adminforth 1.0.67 → 1.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +20 -12
- package/dist/modules/codeInjector.js +8 -3
- package/dist/servers/express.js +1 -1
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +1 -189
- package/dist/spa/package.json +1 -6
- package/dist/spa/spa/index.html +1 -1
- package/dist/spa/spa/package-lock.json +4 -4
- package/dist/spa/spa/package.json +1 -1
- package/dist/spa/spa/public/favicon.png +0 -0
- package/dist/spa/spa/src/App.vue +34 -16
- package/dist/spa/spa/src/assets/logo.svg +19 -1
- package/dist/spa/spa/src/components/ShowTableItem.vue +14 -0
- package/dist/spa/spa/src/router/index.ts +12 -5
- package/dist/spa/spa/src/stores/core.ts +3 -2
- package/dist/spa/spa/src/views/CreateView.vue +1 -1
- package/dist/spa/spa/src/views/EditView.vue +1 -1
- package/dist/spa/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/spa/src/views/ListView.vue +2 -2
- package/dist/spa/spa/src/views/ShowView.vue +1 -1
- package/dist/spa/spa/vite.config.ts +6 -0
- package/dist/spa/src/App.vue +57 -76
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/src/components/Dropdown.vue +4 -13
- package/dist/spa/src/components/Filters.vue +22 -55
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +99 -157
- package/dist/spa/src/components/ValueRenderer.vue +1 -11
- package/dist/spa/src/router/index.ts +2 -3
- package/dist/spa/src/stores/core.ts +33 -22
- package/dist/spa/src/utils.ts +3 -5
- package/dist/spa/src/views/CreateView.vue +7 -15
- package/dist/spa/src/views/EditView.vue +8 -36
- package/dist/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/src/views/ListView.vue +23 -35
- package/dist/spa/src/views/LoginView.vue +1 -1
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +9 -20
- package/dist/spa/tailwind.config.js +2 -5
- package/documentation/docusaurus.config.ts +2 -2
- package/documentation/sidebars.ts +1 -1
- package/index.ts +23 -13
- package/modules/codeInjector.ts +7 -3
- package/package.json +1 -1
- package/servers/express.ts +1 -1
- package/spa/index.html +1 -1
- package/spa/package-lock.json +4 -4
- package/spa/package.json +1 -1
- package/spa/public/favicon.png +0 -0
- package/spa/src/App.vue +34 -16
- package/spa/src/assets/logo.svg +19 -1
- package/spa/src/router/index.ts +12 -5
- package/spa/src/stores/core.ts +3 -2
- package/spa/src/views/CreateView.vue +1 -1
- package/spa/src/views/EditView.vue +1 -1
- package/spa/src/views/ListView.vue +2 -2
- package/spa/src/views/ShowView.vue +1 -1
- package/spa/vite.config.ts +6 -0
- package/types/AdminForthConfig.ts +10 -3
- package/dist/spa/src/components/CustomRangePicker.vue +0 -148
- package/spa/public/favicon.ico +0 -0
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
</BreadcrumbsWithButtons>
|
|
66
66
|
|
|
67
67
|
<!-- table -->
|
|
68
|
-
<div class="relative overflow-x-auto shadow-
|
|
68
|
+
<div class="relative overflow-x-auto shadow-md sm:rounded-lg dark:shadow-2xl">
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
<!-- skelet loader -->
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
</div>
|
|
91
91
|
|
|
92
92
|
<table v-else class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
93
|
-
<thead class="text-xs text-
|
|
93
|
+
<thead class="text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
94
94
|
<tr>
|
|
95
95
|
<th scope="col" class="p-4">
|
|
96
96
|
<div v-if="rows && rows.length" class="flex items-center">
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
</tr>
|
|
128
128
|
</thead>
|
|
129
129
|
<tbody>
|
|
130
|
-
<tr v-if="!rows" class="bg-
|
|
130
|
+
<tr v-if="!rows" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
131
131
|
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
132
132
|
|
|
133
133
|
<div role="status"
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
</div>
|
|
172
172
|
</td>
|
|
173
173
|
</tr>
|
|
174
|
-
<tr v-else-if="rows.length === 0" class="bg-
|
|
174
|
+
<tr v-else-if="rows.length === 0" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
175
175
|
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
176
176
|
|
|
177
177
|
<div id="toast-simple"
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
</tr>
|
|
186
186
|
|
|
187
187
|
<tr v-else v-for="(row, rowI) in rows" :key="row.id"
|
|
188
|
-
class="bg-
|
|
188
|
+
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
|
|
189
189
|
<td class="w-4 p-4">
|
|
190
190
|
<div class="flex items center">
|
|
191
191
|
<input
|
|
@@ -198,12 +198,7 @@
|
|
|
198
198
|
</div>
|
|
199
199
|
</td>
|
|
200
200
|
<td v-for="c in columnsListed" class="px-6 py-4">
|
|
201
|
-
|
|
202
|
-
<component
|
|
203
|
-
:is="listComponentsPerColumn[c.name] || ValueRenderer"
|
|
204
|
-
:column="c"
|
|
205
|
-
:row="row"
|
|
206
|
-
/>
|
|
201
|
+
<ValueRenderer :column="c" :row="row"/>
|
|
207
202
|
</td>
|
|
208
203
|
<td class="flex items-center px-6 py-4">
|
|
209
204
|
|
|
@@ -315,28 +310,29 @@
|
|
|
315
310
|
</template>
|
|
316
311
|
|
|
317
312
|
<script setup>
|
|
313
|
+
import {ref, onMounted, watch, computed} from 'vue';
|
|
314
|
+
import {callAdminForthApi, getIcon} from '@/utils';
|
|
315
|
+
import {useRoute} from 'vue-router';
|
|
316
|
+
import {useCoreStore} from '@/stores/core';
|
|
317
|
+
import {useModalStore} from '@/stores/modal';
|
|
318
318
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
319
|
-
import {
|
|
320
|
-
import { useModalStore } from '@/stores/modal';
|
|
321
|
-
import { callAdminForthApi, getIcon } from '@/utils';
|
|
322
|
-
import { initFlowbite } from 'flowbite';
|
|
323
|
-
import { computed, onMounted, ref, watch } from 'vue';
|
|
324
|
-
import { useRoute } from 'vue-router';
|
|
319
|
+
import {initFlowbite} from 'flowbite'
|
|
325
320
|
|
|
326
321
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
327
|
-
import { getCustomComponent } from '@/utils';
|
|
328
322
|
|
|
329
323
|
import {
|
|
330
|
-
|
|
331
|
-
|
|
324
|
+
IconChevronDoubleLeftOutline,
|
|
325
|
+
IconChevronDoubleRightOutline,
|
|
326
|
+
IconInboxFullSolid,
|
|
327
|
+
IconInboxOutline,
|
|
328
|
+
IconPlusOutline
|
|
332
329
|
} from '@iconify-prerendered/vue-flowbite';
|
|
333
330
|
|
|
334
331
|
import {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
IconTrashBinSolid
|
|
332
|
+
IconEyeSolid,
|
|
333
|
+
IconTrashBinSolid,
|
|
334
|
+
IconPenSolid,
|
|
335
|
+
IconFilterOutline, IconBanOutline
|
|
340
336
|
} from '@iconify-prerendered/vue-flowbite';
|
|
341
337
|
|
|
342
338
|
import Filters from '@/components/Filters.vue';
|
|
@@ -361,6 +357,7 @@ const totalRows = ref(0);
|
|
|
361
357
|
const allCheckedActions = ref([]);
|
|
362
358
|
const allowedActions = ref({});
|
|
363
359
|
|
|
360
|
+
|
|
364
361
|
const DEFAULT_PAGE_SIZE = 10;
|
|
365
362
|
|
|
366
363
|
const columnsListed = computed(() => coreStore.resourceColumns?.filter(c => c.showIn.includes('list')));
|
|
@@ -379,7 +376,6 @@ async function selectAll(value) {
|
|
|
379
376
|
|
|
380
377
|
const pageSize = computed(() => coreStore.resourceById[route.params.resourceId]?.pageSize || DEFAULT_PAGE_SIZE);
|
|
381
378
|
const totalPages = computed(() => Math.ceil(totalRows.value / pageSize.value));
|
|
382
|
-
let listComponentsPerColumn = {};
|
|
383
379
|
const allFromThisPageChecked = computed(() => {
|
|
384
380
|
if (!rows.value) return false;
|
|
385
381
|
return rows.value.every((r) => checkboxes.value.includes(r.id));
|
|
@@ -423,7 +419,6 @@ async function getList() {
|
|
|
423
419
|
path: '/get_resource_data',
|
|
424
420
|
method: 'POST',
|
|
425
421
|
body: {
|
|
426
|
-
source: 'list',
|
|
427
422
|
resourceId: route.params.resourceId,
|
|
428
423
|
limit: pageSize.value,
|
|
429
424
|
offset: (page.value - 1) * pageSize.value,
|
|
@@ -431,14 +426,7 @@ async function getList() {
|
|
|
431
426
|
sort: sort.value,
|
|
432
427
|
}
|
|
433
428
|
});
|
|
434
|
-
|
|
435
|
-
if (column.component?.list) {
|
|
436
|
-
acc[column.name] = getCustomComponent(column.component.list);
|
|
437
|
-
}
|
|
438
|
-
return acc;
|
|
439
|
-
}, {});
|
|
440
|
-
|
|
441
|
-
fetchStatus.value.pending = false;
|
|
429
|
+
fetchStatus.value.pending = false;
|
|
442
430
|
rows.value = data.data?.map(row => {
|
|
443
431
|
row._primaryKeyValue = row[coreStore.resourceColumns.find(c => c.primaryKey).name];
|
|
444
432
|
return row;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
12
12
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
13
13
|
<!-- Modal content -->
|
|
14
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700
|
|
14
|
+
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
15
15
|
<!-- Modal header -->
|
|
16
16
|
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
17
17
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
</div>
|
|
28
28
|
<div
|
|
29
29
|
v-else
|
|
30
|
-
class="relative overflow-x-auto shadow-
|
|
30
|
+
class="relative overflow-x-auto shadow-md sm:rounded-lg"
|
|
31
31
|
>
|
|
32
32
|
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
33
|
-
<thead class="text-xs text-gray-700 uppercase bg-
|
|
33
|
+
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
34
34
|
<tr>
|
|
35
35
|
<th scope="col" class="px-6 py-3">
|
|
36
36
|
Field
|
|
@@ -42,17 +42,13 @@
|
|
|
42
42
|
</thead>
|
|
43
43
|
<tbody>
|
|
44
44
|
<tr v-for="column in coreStore.resourceColumns?.filter(c => c.showIn.includes('show'))" :key="column.name"
|
|
45
|
-
class="bg-
|
|
45
|
+
class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700"
|
|
46
46
|
>
|
|
47
47
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
48
48
|
{{ column.label }}
|
|
49
49
|
</td>
|
|
50
50
|
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
51
|
-
<
|
|
52
|
-
:is="showComponentsPerColumn[column.name] || ValueRenderer"
|
|
53
|
-
:column="column"
|
|
54
|
-
:row="coreStore.record"
|
|
55
|
-
/>
|
|
51
|
+
<ValueRenderer :column="column" :row="coreStore.record" />
|
|
56
52
|
</td>
|
|
57
53
|
</tr>
|
|
58
54
|
|
|
@@ -67,19 +63,18 @@
|
|
|
67
63
|
|
|
68
64
|
<script setup>
|
|
69
65
|
|
|
66
|
+
import { ref, computed, onMounted } from 'vue';
|
|
67
|
+
import { useRoute } from 'vue-router';
|
|
68
|
+
import { callAdminForthApi } from '@/utils';
|
|
70
69
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
71
|
-
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
72
|
-
import { useCoreStore } from '@/stores/core';
|
|
73
|
-
import { getCustomComponent } from '@/utils';
|
|
74
70
|
import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
|
|
75
|
-
import {
|
|
76
|
-
import
|
|
71
|
+
import { useCoreStore } from '@/stores/core';
|
|
72
|
+
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
77
73
|
|
|
78
74
|
|
|
79
75
|
const item = ref(null);
|
|
80
76
|
const route = useRoute();
|
|
81
77
|
const loading = ref(false);
|
|
82
|
-
let showComponentsPerColumn = {};
|
|
83
78
|
|
|
84
79
|
const coreStore = useCoreStore();
|
|
85
80
|
|
|
@@ -92,12 +87,6 @@ onMounted(async () => {
|
|
|
92
87
|
resourceId: route.params.resourceId,
|
|
93
88
|
primaryKey: route.params.primaryKey,
|
|
94
89
|
});
|
|
95
|
-
showComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
96
|
-
if (column.component?.show) {
|
|
97
|
-
acc[column.name] = getCustomComponent(column.component.show);
|
|
98
|
-
}
|
|
99
|
-
return acc;
|
|
100
|
-
}, {});
|
|
101
90
|
loading.value = false;
|
|
102
91
|
});
|
|
103
92
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/** @type {import('tailwindcss').Config} */
|
|
2
2
|
export default {
|
|
3
|
-
content: ["./src/**/*.{vue, js}",
|
|
3
|
+
content: ["./src/**/*.{vue, js}", "./node_modules/flowbite/**/*.js"],
|
|
4
4
|
theme: {
|
|
5
|
-
extend: {
|
|
6
|
-
/* IMPORTANT:ADMINFORTH TAILWIND STYLES */
|
|
7
|
-
}
|
|
5
|
+
extend: {},
|
|
8
6
|
},
|
|
9
|
-
|
|
10
7
|
darkMode: 'class',
|
|
11
8
|
plugins: [
|
|
12
9
|
require('flowbite/plugin'),
|
package/index.ts
CHANGED
|
@@ -110,12 +110,21 @@ class AdminForth {
|
|
|
110
110
|
if (!this.config.baseUrl) {
|
|
111
111
|
this.config.baseUrl = '';
|
|
112
112
|
}
|
|
113
|
-
if (!this.config.brandName) {
|
|
114
|
-
this.config.brandName = 'AdminForth';
|
|
113
|
+
if (!this.config?.customization.brandName) {
|
|
114
|
+
this.config.customization.brandName = 'AdminForth';
|
|
115
115
|
}
|
|
116
|
+
if (this.config.customization.brandLogo) {
|
|
117
|
+
if (!this.config.customization.brandLogo.startsWith('@@/')) {
|
|
118
|
+
errors.push(`Brand logo must start with @@ and be placed in custom directory`);
|
|
119
|
+
}
|
|
120
|
+
// todo check file exist
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
116
124
|
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
|
|
126
|
+
if (!this.config.customization.datesFormat) {
|
|
127
|
+
this.config.customization.datesFormat = 'MMM D, YYYY HH:mm:ss';
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
if (this.config.resources) {
|
|
@@ -469,10 +478,10 @@ class AdminForth {
|
|
|
469
478
|
const usernameColumn = resource.columns.find((col) => col.name === usernameField);
|
|
470
479
|
|
|
471
480
|
return {
|
|
472
|
-
brandName: this.config.brandName,
|
|
481
|
+
brandName: this.config.customization.brandName,
|
|
473
482
|
usernameFieldName: usernameColumn.label,
|
|
474
483
|
loginBackgroundImage: this.config.auth.loginBackgroundImage,
|
|
475
|
-
title: this.config?.title,
|
|
484
|
+
title: this.config.customization?.title,
|
|
476
485
|
};
|
|
477
486
|
},
|
|
478
487
|
});
|
|
@@ -516,12 +525,13 @@ class AdminForth {
|
|
|
516
525
|
})),
|
|
517
526
|
menu: this.config.menu,
|
|
518
527
|
config: {
|
|
519
|
-
brandName: this.config.brandName,
|
|
520
|
-
|
|
528
|
+
brandName: this.config.customization.brandName,
|
|
529
|
+
brandLogo: this.config.customization.brandLogo,
|
|
530
|
+
datesFormat: this.config.customization.datesFormat,
|
|
521
531
|
deleteConfirmation: this.config.deleteConfirmation,
|
|
522
532
|
auth: this.config.auth,
|
|
523
533
|
usernameField: this.config.auth.usernameField,
|
|
524
|
-
title: this.config?.title,
|
|
534
|
+
title: this.config.customization?.title,
|
|
525
535
|
},
|
|
526
536
|
adminUser,
|
|
527
537
|
version: ADMINFORTH_VERSION,
|
|
@@ -643,10 +653,6 @@ class AdminForth {
|
|
|
643
653
|
})
|
|
644
654
|
);
|
|
645
655
|
|
|
646
|
-
data.data.forEach((item) => {
|
|
647
|
-
item._label = resource.itemLabel(item);
|
|
648
|
-
})
|
|
649
|
-
|
|
650
656
|
for (const hook of getFunctionList(resource.hooks?.[source]?.afterDatasourceResponse)) {
|
|
651
657
|
const resp = await hook({ resource, response: data.data, adminUser });
|
|
652
658
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
@@ -667,6 +673,10 @@ class AdminForth {
|
|
|
667
673
|
})
|
|
668
674
|
});
|
|
669
675
|
|
|
676
|
+
data.data.forEach((item) => {
|
|
677
|
+
item._label = resource.itemLabel(item);
|
|
678
|
+
});
|
|
679
|
+
|
|
670
680
|
return {
|
|
671
681
|
...data,
|
|
672
682
|
options: resource?.options,
|
package/modules/codeInjector.ts
CHANGED
|
@@ -107,11 +107,13 @@ class CodeInjector {
|
|
|
107
107
|
path: '${item.path}',
|
|
108
108
|
name: '${item.path}',
|
|
109
109
|
component: () => import('${item.component}'),
|
|
110
|
+
meta: { title: '${item?.meta?.title}'}
|
|
110
111
|
},\n`} else {
|
|
111
112
|
routes += `{
|
|
112
113
|
path: '${item.path}',
|
|
113
114
|
name: '${item.path}',
|
|
114
115
|
component: ${getComponentNameFromPath(item.component)},
|
|
116
|
+
meta: { title: '${item?.meta?.title}'}
|
|
115
117
|
},\n`
|
|
116
118
|
const componentName = `${getComponentNameFromPath(item.component)}`;
|
|
117
119
|
routerComponents += `import ${componentName} from '${item.component}';\n`;
|
|
@@ -122,6 +124,7 @@ class CodeInjector {
|
|
|
122
124
|
path: '${item.path}',
|
|
123
125
|
name: '${item.path}',
|
|
124
126
|
component: ${getComponentNameFromPath(item.component)},
|
|
127
|
+
meta: { title: '${item?.meta?.title}'}
|
|
125
128
|
},\n`
|
|
126
129
|
const componentName = `${getComponentNameFromPath(item.component)}`;
|
|
127
130
|
routerComponents += `import ${componentName} from '${item.component}';\n`;}
|
|
@@ -129,7 +132,8 @@ class CodeInjector {
|
|
|
129
132
|
routes += `{
|
|
130
133
|
path: '${item.path}',
|
|
131
134
|
name: '${item.path}',
|
|
132
|
-
component: () => import('${item.component}'),
|
|
135
|
+
component: () => import('${item.component}'),}
|
|
136
|
+
meta: { title: '${item?.meta?.title}'
|
|
133
137
|
},\n`
|
|
134
138
|
|
|
135
139
|
}
|
|
@@ -239,7 +243,7 @@ class CodeInjector {
|
|
|
239
243
|
this.allComponentNames[filePath] = componentName;
|
|
240
244
|
});
|
|
241
245
|
|
|
242
|
-
console.log('🔧 Injecting code into Vue sources...', this.allComponentNames);
|
|
246
|
+
// console.log('🔧 Injecting code into Vue sources...', this.allComponentNames);
|
|
243
247
|
|
|
244
248
|
let customComponentsImports = '';
|
|
245
249
|
for (const [targetPath, component] of Object.entries(this.allComponentNames)) {
|
|
@@ -299,7 +303,7 @@ class CodeInjector {
|
|
|
299
303
|
// inject title to index.html
|
|
300
304
|
const indexHtmlPath = path.join(CodeInjector.SPA_TMP_PATH, 'index.html');
|
|
301
305
|
let indexHtmlContent = await fs.promises.readFile(indexHtmlPath, 'utf-8');
|
|
302
|
-
indexHtmlContent = indexHtmlContent.replace('/* IMPORTANT:ADMINFORTH TITLE */', `${this.adminforth.config.title || 'AdminForth'}`);
|
|
306
|
+
indexHtmlContent = indexHtmlContent.replace('/* IMPORTANT:ADMINFORTH TITLE */', `${this.adminforth.config.customization.title || 'AdminForth'}`);
|
|
303
307
|
await fs.promises.writeFile(indexHtmlPath, indexHtmlContent);
|
|
304
308
|
|
|
305
309
|
|
package/package.json
CHANGED
package/servers/express.ts
CHANGED
|
@@ -135,7 +135,7 @@ class ExpressServer {
|
|
|
135
135
|
fileExists = false;
|
|
136
136
|
}
|
|
137
137
|
if (!fileExists) {
|
|
138
|
-
res.status(500).send(respondNoServer(`${this.adminforth.config.brandName} is still warming up`, 'Please wait a moment...'));
|
|
138
|
+
res.status(500).send(respondNoServer(`${this.adminforth.config.customization.brandName} is still warming up`, 'Please wait a moment...'));
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
res.sendFile(fullPath, {
|
package/spa/index.html
CHANGED
package/spa/package-lock.json
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"sass": "^1.77.2",
|
|
37
37
|
"tailwindcss": "^3.4.3",
|
|
38
38
|
"typescript": "~5.4.0",
|
|
39
|
-
"vite": "^5.2.
|
|
39
|
+
"vite": "^5.2.13",
|
|
40
40
|
"vue-tsc": "^2.0.11"
|
|
41
41
|
}
|
|
42
42
|
},
|
|
@@ -4033,9 +4033,9 @@
|
|
|
4033
4033
|
"dev": true
|
|
4034
4034
|
},
|
|
4035
4035
|
"node_modules/vite": {
|
|
4036
|
-
"version": "5.2.
|
|
4037
|
-
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.
|
|
4038
|
-
"integrity": "sha512
|
|
4036
|
+
"version": "5.2.13",
|
|
4037
|
+
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz",
|
|
4038
|
+
"integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==",
|
|
4039
4039
|
"dev": true,
|
|
4040
4040
|
"dependencies": {
|
|
4041
4041
|
"esbuild": "^0.20.1",
|
package/spa/package.json
CHANGED
|
Binary file
|
package/spa/src/App.vue
CHANGED
|
@@ -66,11 +66,11 @@
|
|
|
66
66
|
<div class="h-full px-3 pb-4 overflow-y-auto bg-nav-menu-bg dark:bg-gray-800">
|
|
67
67
|
|
|
68
68
|
<div class="flex ms-2 md:me-24 m-4 ">
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
<img :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="h-8 me-3" />
|
|
70
|
+
<span class="self-center text-header-text-size font-semibold sm:text-header-text-size whitespace-nowrap dark:text-header-text text-header-logo-color">
|
|
71
|
+
{{ coreStore.config?.brandName }}
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
74
|
<ul class="space-y-2 font-medium">
|
|
75
75
|
<template v-for="(item, i) in coreStore.menu" :key="`menu-${i}`">
|
|
76
76
|
<div v-if="item.type === 'divider'" class="border-t border-nav-menu-devider dark:border-gray-700"></div>
|
|
@@ -151,6 +151,27 @@ import { createHead } from 'unhead'
|
|
|
151
151
|
const coreStore = useCoreStore();
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
const splitAtLast = (str: string, separator: string) => {
|
|
155
|
+
const index = str.lastIndexOf(separator);
|
|
156
|
+
return [str.slice(0, index), str.slice(index + 1)];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const loadFile = (file: string) => {
|
|
160
|
+
let path;
|
|
161
|
+
let baseUrl = '';
|
|
162
|
+
if (file.startsWith('@/')) {
|
|
163
|
+
path = file.replace('@/', '');
|
|
164
|
+
baseUrl = new URL(`./${path}`, import.meta.url).href;
|
|
165
|
+
} else if (file.startsWith('@@/')) {
|
|
166
|
+
path = file.replace('@@/', '');
|
|
167
|
+
baseUrl = new URL(`./custom/${path}`, import.meta.url).href;
|
|
168
|
+
} else {
|
|
169
|
+
baseUrl = new URL(`./${file}`, import.meta.url).href;
|
|
170
|
+
}
|
|
171
|
+
return baseUrl;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
154
175
|
createHead()
|
|
155
176
|
|
|
156
177
|
|
|
@@ -159,6 +180,13 @@ const sideBarOpen = ref(false);
|
|
|
159
180
|
|
|
160
181
|
|
|
161
182
|
const route = useRoute();
|
|
183
|
+
watch(route, () => {
|
|
184
|
+
title.value = `${coreStore.config?.title || coreStore.config?.brandName || 'Adminforth'} | ${ Object.values(route.params)[0] || route.meta.title || ' '}`;
|
|
185
|
+
useHead({
|
|
186
|
+
title: title.value,
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
|
|
162
190
|
const router = useRouter();
|
|
163
191
|
const title = ref('');
|
|
164
192
|
//create a ref to store the opened menu items with ts type;
|
|
@@ -207,17 +235,7 @@ onMounted(async () => {
|
|
|
207
235
|
}, 100);
|
|
208
236
|
await coreStore.fetchMenuAndResource();
|
|
209
237
|
loginRedirectCheckIsReady.value = true;
|
|
210
|
-
|
|
211
|
-
// useHead({
|
|
212
|
-
// title: title.value,
|
|
213
|
-
// link: [
|
|
214
|
-
// {
|
|
215
|
-
// rel: 'icon',
|
|
216
|
-
// href: coreStore.config.favicon || '/favicon.ico',
|
|
217
|
-
// },
|
|
218
|
-
// ],
|
|
219
|
-
|
|
220
|
-
// })
|
|
238
|
+
|
|
221
239
|
coreStore.menu.forEach((item, i) => {
|
|
222
240
|
if (item.open) {
|
|
223
241
|
opened.value.push(i);
|
package/spa/src/assets/logo.svg
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
<svg
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_1_8)">
|
|
3
|
+
<path d="M8.034 6.006V13H6.097V12.194C5.59433 12.8007 4.86633 13.104 3.913 13.104C3.25433 13.104 2.65633 12.9567 2.119 12.662C1.59033 12.3673 1.17433 11.947 0.871 11.401C0.567667 10.855 0.416 10.2223 0.416 9.503C0.416 8.78367 0.567667 8.151 0.871 7.605C1.17433 7.059 1.59033 6.63867 2.119 6.344C2.65633 6.04933 3.25433 5.902 3.913 5.902C4.80567 5.902 5.50333 6.18367 6.006 6.747V6.006H8.034ZM4.264 11.44C4.77533 11.44 5.2 11.2667 5.538 10.92C5.876 10.5647 6.045 10.0923 6.045 9.503C6.045 8.91367 5.876 8.44567 5.538 8.099C5.2 7.74367 4.77533 7.566 4.264 7.566C3.744 7.566 3.315 7.74367 2.977 8.099C2.639 8.44567 2.47 8.91367 2.47 9.503C2.47 10.0923 2.639 10.5647 2.977 10.92C3.315 11.2667 3.744 11.44 4.264 11.44Z" fill="url(#paint0_linear_1_8)"/>
|
|
4
|
+
<path d="M13.317 5.538C12.589 5.538 12.0387 5.69833 11.666 6.019C11.2933 6.331 11.107 6.80333 11.107 7.436V7.995H14.851V9.685H11.107V13H9.001V7.449C9.001 6.279 9.365 5.369 10.093 4.719C10.8297 4.069 11.8567 3.744 13.174 3.744C13.694 3.744 14.1837 3.80033 14.643 3.913C15.1023 4.017 15.501 4.173 15.839 4.381L15.189 6.045C14.669 5.707 14.045 5.538 13.317 5.538Z" fill="url(#paint1_linear_1_8)"/>
|
|
5
|
+
</g>
|
|
6
|
+
<defs>
|
|
7
|
+
<linearGradient id="paint0_linear_1_8" x1="1.5" y1="6.93333" x2="7.31883" y2="11.8926" gradientUnits="userSpaceOnUse">
|
|
8
|
+
<stop stop-color="#656E7A"/>
|
|
9
|
+
<stop offset="1" stop-color="#99A6B8"/>
|
|
10
|
+
</linearGradient>
|
|
11
|
+
<linearGradient id="paint1_linear_1_8" x1="12.5" y1="3.73333" x2="9.68642" y2="12.7016" gradientUnits="userSpaceOnUse">
|
|
12
|
+
<stop stop-color="#48C5FF"/>
|
|
13
|
+
<stop offset="1" stop-color="#A1E1FF"/>
|
|
14
|
+
</linearGradient>
|
|
15
|
+
<clipPath id="clip0_1_8">
|
|
16
|
+
<rect width="16" height="16" fill="white"/>
|
|
17
|
+
</clipPath>
|
|
18
|
+
</defs>
|
|
19
|
+
</svg>
|
package/spa/src/router/index.ts
CHANGED
|
@@ -8,7 +8,8 @@ const router = createRouter({
|
|
|
8
8
|
{
|
|
9
9
|
path: '/login',
|
|
10
10
|
name: 'login',
|
|
11
|
-
component: () => import('@/views/LoginView.vue')
|
|
11
|
+
component: () => import('@/views/LoginView.vue'),
|
|
12
|
+
meta: { title: 'login' }
|
|
12
13
|
},
|
|
13
14
|
{
|
|
14
15
|
path: '/resource/:resourceId',
|
|
@@ -18,22 +19,28 @@ const router = createRouter({
|
|
|
18
19
|
{
|
|
19
20
|
path: '',
|
|
20
21
|
component: () => import('@/views/ListView.vue'),
|
|
21
|
-
name: 'resource-list'
|
|
22
|
+
name: 'resource-list',
|
|
23
|
+
meta: { title: 'list' }
|
|
22
24
|
},
|
|
23
25
|
{
|
|
24
26
|
path: 'show/:primaryKey',
|
|
25
27
|
component: () => import('@/views/ShowView.vue'),
|
|
26
|
-
name: 'resource-show'
|
|
28
|
+
name: 'resource-show',
|
|
29
|
+
meta: { title: 'show' }
|
|
30
|
+
|
|
27
31
|
},
|
|
28
32
|
{
|
|
29
33
|
path: 'edit/:primaryKey',
|
|
30
34
|
component: () => import('@/views/EditView.vue'),
|
|
31
|
-
name: 'resource-edit'
|
|
35
|
+
name: 'resource-edit',
|
|
36
|
+
meta: { title: 'edit' }
|
|
32
37
|
},
|
|
33
38
|
{
|
|
34
39
|
path: 'create',
|
|
35
40
|
component: () => import('@/views/CreateView.vue'),
|
|
36
|
-
name: 'resource-create'
|
|
41
|
+
name: 'resource-create',
|
|
42
|
+
meta: { title: 'create' }
|
|
43
|
+
|
|
37
44
|
},
|
|
38
45
|
]
|
|
39
46
|
},
|
package/spa/src/stores/core.ts
CHANGED
|
@@ -66,7 +66,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
66
66
|
resourceOptions.value = respData.options;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
async function
|
|
69
|
+
async function fetchResourceFull({ resourceId }) {
|
|
70
70
|
if (resourceColumnsId.value === resourceId && resourceColumns.value) {
|
|
71
71
|
// already fetched
|
|
72
72
|
return;
|
|
@@ -86,6 +86,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
86
86
|
resourceColumnsError.value = res.error;
|
|
87
87
|
} else {
|
|
88
88
|
resourceColumns.value = res.resource.columns;
|
|
89
|
+
resourceById.value[resourceId] = res.resource;
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -126,7 +127,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
126
127
|
fetchRecord,
|
|
127
128
|
record,
|
|
128
129
|
resourceColumns,
|
|
129
|
-
|
|
130
|
+
fetchResourceFull,
|
|
130
131
|
resourceColumnsError,
|
|
131
132
|
flowBitIsInitialised,
|
|
132
133
|
resourceOptions,
|
|
@@ -71,7 +71,7 @@ async function onUpdateRecord(newRecord) {
|
|
|
71
71
|
|
|
72
72
|
onMounted(async () => {
|
|
73
73
|
loading.value = true;
|
|
74
|
-
await coreStore.
|
|
74
|
+
await coreStore.fetchResourceFull({
|
|
75
75
|
resourceId: route.params.resourceId
|
|
76
76
|
});
|
|
77
77
|
createComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|