adminforth 1.0.61 → 1.0.63
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 +41 -38
- package/dist/modules/codeInjector.js +1 -0
- package/dist/modules/utils.js +14 -1
- package/dist/plugins/ForeignInlineListPlugin/index.js +1 -1
- package/dist/spa/spa/src/App.vue +6 -3
- package/dist/spa/spa/src/views/ListView.vue +1 -4
- package/dist/spa/spa/src/views/ShowView.vue +28 -2
- package/dist/spa/spa/tailwind.config.js +1 -1
- package/index.ts +68 -65
- package/modules/codeInjector.ts +2 -0
- package/modules/utils.ts +13 -2
- package/package.json +4 -2
- package/plugins/ForeignInlineListPlugin/index.ts +1 -1
- package/spa/src/App.vue +6 -3
- package/spa/src/views/ListView.vue +1 -4
- package/spa/src/views/ShowView.vue +28 -2
- package/spa/tailwind.config.js +1 -1
- package/types/AdminForthConfig.ts +13 -11
- package/spa/src/components/ShowTableItem.vue +0 -14
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { v1 as uuid } from 'uuid';
|
|
|
24
24
|
import fs from 'fs';
|
|
25
25
|
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
26
26
|
import { AdminForthFilterOperators, AdminForthTypes } from './types.js';
|
|
27
|
+
import { getFunctionList } from './modules/utils.js';
|
|
27
28
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
28
29
|
const DEFAULT_ALLOWED_ACTIONS = { create: true, edit: true, show: true, delete: true };
|
|
29
30
|
class AdminForth {
|
|
@@ -159,7 +160,7 @@ class AdminForth {
|
|
|
159
160
|
errors.push(`Resource "${res.resourceId}" bulkActions must be an array`);
|
|
160
161
|
bulkActions = [];
|
|
161
162
|
}
|
|
162
|
-
if ((_c = res.options) === null || _c === void 0 ? void 0 : _c.allowDelete) {
|
|
163
|
+
if (((_c = res.options) === null || _c === void 0 ? void 0 : _c.allowDelete) && !bulkActions.find((action) => action.label === 'Delete checked')) {
|
|
163
164
|
bulkActions.push({
|
|
164
165
|
label: `Delete checked`,
|
|
165
166
|
state: 'danger',
|
|
@@ -389,6 +390,7 @@ class AdminForth {
|
|
|
389
390
|
method: 'GET',
|
|
390
391
|
path: '/get_public_config',
|
|
391
392
|
handler: (_d) => __awaiter(this, [_d], void 0, function* ({ body }) {
|
|
393
|
+
var _e;
|
|
392
394
|
// find resource
|
|
393
395
|
if (!this.config.auth) {
|
|
394
396
|
throw new Error('No config.auth defined');
|
|
@@ -400,14 +402,15 @@ class AdminForth {
|
|
|
400
402
|
brandName: this.config.brandName,
|
|
401
403
|
usernameFieldName: usernameColumn.label,
|
|
402
404
|
loginBackgroundImage: this.config.auth.loginBackgroundImage,
|
|
405
|
+
title: (_e = this.config) === null || _e === void 0 ? void 0 : _e.title,
|
|
403
406
|
};
|
|
404
407
|
}),
|
|
405
408
|
});
|
|
406
409
|
server.endpoint({
|
|
407
410
|
method: 'GET',
|
|
408
411
|
path: '/get_base_config',
|
|
409
|
-
handler: (
|
|
410
|
-
var
|
|
412
|
+
handler: (_f) => __awaiter(this, [_f], void 0, function* ({ input, adminUser, cookies }) {
|
|
413
|
+
var _g;
|
|
411
414
|
const cookieParsed = this.auth.verify(cookies['adminforth_jwt']);
|
|
412
415
|
let username = '';
|
|
413
416
|
let userFullName = '';
|
|
@@ -448,7 +451,7 @@ class AdminForth {
|
|
|
448
451
|
deleteConfirmation: this.config.deleteConfirmation,
|
|
449
452
|
auth: this.config.auth,
|
|
450
453
|
usernameField: this.config.auth.usernameField,
|
|
451
|
-
title: (
|
|
454
|
+
title: (_g = this.config) === null || _g === void 0 ? void 0 : _g.title,
|
|
452
455
|
},
|
|
453
456
|
adminUser,
|
|
454
457
|
version: ADMINFORTH_VERSION,
|
|
@@ -458,7 +461,7 @@ class AdminForth {
|
|
|
458
461
|
server.endpoint({
|
|
459
462
|
method: 'POST',
|
|
460
463
|
path: '/get_resource_columns',
|
|
461
|
-
handler: (
|
|
464
|
+
handler: (_h) => __awaiter(this, [_h], void 0, function* ({ body }) {
|
|
462
465
|
const { resourceId } = body;
|
|
463
466
|
if (!this.statuses.dbDiscover) {
|
|
464
467
|
return { error: 'Database discovery not started' };
|
|
@@ -477,8 +480,8 @@ class AdminForth {
|
|
|
477
480
|
server.endpoint({
|
|
478
481
|
method: 'POST',
|
|
479
482
|
path: '/get_resource_data',
|
|
480
|
-
handler: (
|
|
481
|
-
var
|
|
483
|
+
handler: (_j) => __awaiter(this, [_j], void 0, function* ({ body, adminUser }) {
|
|
484
|
+
var _k, _l, _m, _o;
|
|
482
485
|
const { resourceId, source } = body;
|
|
483
486
|
if (['show', 'list'].includes(source) === false) {
|
|
484
487
|
return { error: 'Invalid source, should be list or show' };
|
|
@@ -493,8 +496,8 @@ class AdminForth {
|
|
|
493
496
|
if (!resource) {
|
|
494
497
|
return { error: `Resource ${resourceId} not found` };
|
|
495
498
|
}
|
|
496
|
-
|
|
497
|
-
const resp = yield (
|
|
499
|
+
for (const hook of getFunctionList((_l = (_k = resource.hooks) === null || _k === void 0 ? void 0 : _k[source]) === null || _l === void 0 ? void 0 : _l.beforeDatasourceRequest)) {
|
|
500
|
+
const resp = yield hook({ resource, query: body, adminUser });
|
|
498
501
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
499
502
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
500
503
|
}
|
|
@@ -563,8 +566,8 @@ class AdminForth {
|
|
|
563
566
|
data.data.forEach((item) => {
|
|
564
567
|
item._label = resource.itemLabel(item);
|
|
565
568
|
});
|
|
566
|
-
|
|
567
|
-
const resp = yield (
|
|
569
|
+
for (const hook of getFunctionList((_o = (_m = resource.hooks) === null || _m === void 0 ? void 0 : _m[source]) === null || _o === void 0 ? void 0 : _o.afterDatasourceResponse)) {
|
|
570
|
+
const resp = yield hook({ resource, response: data.data, adminUser });
|
|
568
571
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
569
572
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
570
573
|
}
|
|
@@ -586,8 +589,8 @@ class AdminForth {
|
|
|
586
589
|
server.endpoint({
|
|
587
590
|
method: 'POST',
|
|
588
591
|
path: '/get_resource_foreign_data',
|
|
589
|
-
handler: (
|
|
590
|
-
var
|
|
592
|
+
handler: (_p) => __awaiter(this, [_p], void 0, function* ({ body, adminUser }) {
|
|
593
|
+
var _q, _r;
|
|
591
594
|
const { resourceId, column } = body;
|
|
592
595
|
if (!this.statuses.dbDiscover) {
|
|
593
596
|
return { error: 'Database discovery not started' };
|
|
@@ -608,8 +611,8 @@ class AdminForth {
|
|
|
608
611
|
}
|
|
609
612
|
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
610
613
|
const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
611
|
-
|
|
612
|
-
const resp = yield (
|
|
614
|
+
for (const hook of getFunctionList((_q = columnConfig.foreignResource.hooks) === null || _q === void 0 ? void 0 : _q.beforeDatasourceRequest)) {
|
|
615
|
+
const resp = yield hook({ query: body, adminUser });
|
|
613
616
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
614
617
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
615
618
|
}
|
|
@@ -637,8 +640,8 @@ class AdminForth {
|
|
|
637
640
|
const response = {
|
|
638
641
|
items
|
|
639
642
|
};
|
|
640
|
-
|
|
641
|
-
const resp = yield (
|
|
643
|
+
for (const hook of getFunctionList((_r = columnConfig.foreignResource.hooks) === null || _r === void 0 ? void 0 : _r.afterDatasourceResponse)) {
|
|
644
|
+
const resp = yield hook({ response, adminUser });
|
|
642
645
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
643
646
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
644
647
|
}
|
|
@@ -652,7 +655,7 @@ class AdminForth {
|
|
|
652
655
|
server.endpoint({
|
|
653
656
|
method: 'POST',
|
|
654
657
|
path: '/get_min_max_for_columns',
|
|
655
|
-
handler: (
|
|
658
|
+
handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body }) {
|
|
656
659
|
const { resourceId } = body;
|
|
657
660
|
if (!this.statuses.dbDiscover) {
|
|
658
661
|
return { error: 'Database discovery not started' };
|
|
@@ -681,8 +684,8 @@ class AdminForth {
|
|
|
681
684
|
server.endpoint({
|
|
682
685
|
method: 'POST',
|
|
683
686
|
path: '/create_record',
|
|
684
|
-
handler: (
|
|
685
|
-
var
|
|
687
|
+
handler: (_t) => __awaiter(this, [_t], void 0, function* ({ body, adminUser }) {
|
|
688
|
+
var _u, _v, _w, _x, _y;
|
|
686
689
|
console.log('create_record', body, this.config.resources);
|
|
687
690
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
688
691
|
if (!resource) {
|
|
@@ -690,8 +693,8 @@ class AdminForth {
|
|
|
690
693
|
}
|
|
691
694
|
const record = body['record'];
|
|
692
695
|
// execute hook if needed
|
|
693
|
-
|
|
694
|
-
const resp = yield (
|
|
696
|
+
for (const hook of getFunctionList((_v = (_u = resource.hooks) === null || _u === void 0 ? void 0 : _u.create) === null || _v === void 0 ? void 0 : _v.beforeSave)) {
|
|
697
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
695
698
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
696
699
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
697
700
|
}
|
|
@@ -707,7 +710,7 @@ class AdminForth {
|
|
|
707
710
|
});
|
|
708
711
|
}
|
|
709
712
|
}
|
|
710
|
-
if (((
|
|
713
|
+
if (((_w = column.required) === null || _w === void 0 ? void 0 : _w.create) && body['record'][column.name] === undefined) {
|
|
711
714
|
return { error: `Column '${column.name}' is required` };
|
|
712
715
|
}
|
|
713
716
|
if (column.isUnique) {
|
|
@@ -732,8 +735,8 @@ class AdminForth {
|
|
|
732
735
|
const connector = this.connectors[resource.dataSource];
|
|
733
736
|
yield connector.createRecord({ resource, record });
|
|
734
737
|
// execute hook if needed
|
|
735
|
-
|
|
736
|
-
const resp = yield (
|
|
738
|
+
for (const hook of getFunctionList((_y = (_x = resource.hooks) === null || _x === void 0 ? void 0 : _x.create) === null || _y === void 0 ? void 0 : _y.afterSave)) {
|
|
739
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
737
740
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
738
741
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
739
742
|
}
|
|
@@ -749,8 +752,8 @@ class AdminForth {
|
|
|
749
752
|
server.endpoint({
|
|
750
753
|
method: 'POST',
|
|
751
754
|
path: '/update_record',
|
|
752
|
-
handler: (
|
|
753
|
-
var
|
|
755
|
+
handler: (_z) => __awaiter(this, [_z], void 0, function* ({ body, adminUser }) {
|
|
756
|
+
var _0, _1, _2, _3;
|
|
754
757
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
755
758
|
if (!resource) {
|
|
756
759
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -764,8 +767,8 @@ class AdminForth {
|
|
|
764
767
|
}
|
|
765
768
|
const record = body['record'];
|
|
766
769
|
// execute hook if needed
|
|
767
|
-
|
|
768
|
-
const resp = yield (
|
|
770
|
+
for (const hook of getFunctionList((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.edit) === null || _1 === void 0 ? void 0 : _1.beforeSave)) {
|
|
771
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
769
772
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
770
773
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
771
774
|
}
|
|
@@ -790,8 +793,8 @@ class AdminForth {
|
|
|
790
793
|
yield connector.updateRecord({ resource, recordId, record, newValues });
|
|
791
794
|
}
|
|
792
795
|
// execute hook if needed
|
|
793
|
-
|
|
794
|
-
const resp = yield (
|
|
796
|
+
for (const hook of getFunctionList((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.edit) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
|
|
797
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
795
798
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
796
799
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
797
800
|
}
|
|
@@ -807,16 +810,16 @@ class AdminForth {
|
|
|
807
810
|
server.endpoint({
|
|
808
811
|
method: 'POST',
|
|
809
812
|
path: '/delete_record',
|
|
810
|
-
handler: (
|
|
811
|
-
var
|
|
813
|
+
handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
|
|
814
|
+
var _5, _6, _7, _8;
|
|
812
815
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
813
816
|
const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
814
817
|
if (!resource) {
|
|
815
818
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
816
819
|
}
|
|
817
820
|
// execute hook if needed
|
|
818
|
-
|
|
819
|
-
const resp = yield (
|
|
821
|
+
for (const hook of getFunctionList((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.delete) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
|
|
822
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
820
823
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
821
824
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
822
825
|
}
|
|
@@ -827,8 +830,8 @@ class AdminForth {
|
|
|
827
830
|
const connector = this.connectors[resource.dataSource];
|
|
828
831
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
829
832
|
// execute hook if needed
|
|
830
|
-
|
|
831
|
-
const resp = yield (
|
|
833
|
+
for (const hook of getFunctionList((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.delete) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
|
|
834
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
832
835
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
833
836
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
834
837
|
}
|
|
@@ -844,7 +847,7 @@ class AdminForth {
|
|
|
844
847
|
server.endpoint({
|
|
845
848
|
method: 'POST',
|
|
846
849
|
path: '/start_bulk_action',
|
|
847
|
-
handler: (
|
|
850
|
+
handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body }) {
|
|
848
851
|
const { resourceId, actionId, recordIds } = body;
|
|
849
852
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
850
853
|
if (!resource) {
|
|
@@ -214,6 +214,7 @@ class CodeInjector {
|
|
|
214
214
|
const componentName = getComponentNameFromPath(filePath);
|
|
215
215
|
this.allComponentNames[filePath] = componentName;
|
|
216
216
|
});
|
|
217
|
+
console.log('🔧 Injecting code into Vue sources...', this.allComponentNames);
|
|
217
218
|
let customComponentsImports = '';
|
|
218
219
|
for (const [targetPath, component] of Object.entries(this.allComponentNames)) {
|
|
219
220
|
customComponentsImports += `import ${component} from '${targetPath}';\n`;
|
package/dist/modules/utils.js
CHANGED
|
@@ -26,5 +26,18 @@ export const ADMIN_FORTH_ABSOLUTE_PATH = path.join(currentFileDir(import.meta.ur
|
|
|
26
26
|
const package_json = JSON.parse(fs.readFileSync(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'package.json'), 'utf8'));
|
|
27
27
|
export const ADMINFORTH_VERSION = package_json.version;
|
|
28
28
|
export function getComponentNameFromPath(filePath) {
|
|
29
|
-
return filePath.replace(/@/g, '').replace(
|
|
29
|
+
return filePath.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
|
|
30
|
+
}
|
|
31
|
+
export function getFunctionList(param) {
|
|
32
|
+
if (param) {
|
|
33
|
+
if (Array.isArray(param)) {
|
|
34
|
+
return param;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return [param];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
30
43
|
}
|
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -113,7 +113,8 @@
|
|
|
113
113
|
</div>
|
|
114
114
|
<div v-else class="flex items-center justify-center h-screen">
|
|
115
115
|
<div class="text-3xl text-gray-400 animate-bounce">
|
|
116
|
-
|
|
116
|
+
<svg aria-hidden="true" class="w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/><path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/></svg>
|
|
117
|
+
<span class="sr-only">Loading...</span>
|
|
117
118
|
</div>
|
|
118
119
|
</div>
|
|
119
120
|
<AcceptModal />
|
|
@@ -191,10 +192,12 @@ async function initRouter() {
|
|
|
191
192
|
// initialize components based on data attribute selectors
|
|
192
193
|
onMounted(async () => {
|
|
193
194
|
initRouter();
|
|
194
|
-
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
initFlowbite();
|
|
197
|
+
}, 100);
|
|
195
198
|
await coreStore.fetchMenuAndResource();
|
|
196
199
|
loginRedirectCheckIsReady.value = true;
|
|
197
|
-
title.value = coreStore.config
|
|
200
|
+
title.value = coreStore.config?.title || 'AdminForth';
|
|
198
201
|
useHead({
|
|
199
202
|
title: title.value,
|
|
200
203
|
link: [
|
|
@@ -45,10 +45,28 @@
|
|
|
45
45
|
class="bg-form-view-bg odd:dark:bg-gray-900 even:dark:bg-gray-800 border-b dark:border-gray-700"
|
|
46
46
|
>
|
|
47
47
|
<component
|
|
48
|
-
|
|
48
|
+
v-if="showRowComponentsPerColumn[column.name]"
|
|
49
|
+
:is="showRowComponentsPerColumn[column.name]"
|
|
49
50
|
:column="column"
|
|
50
51
|
:row="coreStore.record"
|
|
51
52
|
/>
|
|
53
|
+
<template v-else>
|
|
54
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
|
55
|
+
{{ column.label }}
|
|
56
|
+
</td>
|
|
57
|
+
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
58
|
+
<component
|
|
59
|
+
v-if="showComponentsPerColumn[column.name]"
|
|
60
|
+
:is="showComponentsPerColumn[column.name]"
|
|
61
|
+
:column="column"
|
|
62
|
+
:row="coreStore.record"
|
|
63
|
+
/>
|
|
64
|
+
<ValueRenderer v-else
|
|
65
|
+
:column="column"
|
|
66
|
+
:row="coreStore.record"
|
|
67
|
+
/>
|
|
68
|
+
</td>
|
|
69
|
+
</template>
|
|
52
70
|
</tr>
|
|
53
71
|
</tbody>
|
|
54
72
|
</table>
|
|
@@ -76,6 +94,7 @@ const item = ref(null);
|
|
|
76
94
|
const route = useRoute();
|
|
77
95
|
const loading = ref(false);
|
|
78
96
|
let showComponentsPerColumn = {};
|
|
97
|
+
let showRowComponentsPerColumn = {};
|
|
79
98
|
|
|
80
99
|
const coreStore = useCoreStore();
|
|
81
100
|
|
|
@@ -93,7 +112,14 @@ onMounted(async () => {
|
|
|
93
112
|
acc[column.name] = getCustomComponent(column.component.show);
|
|
94
113
|
}
|
|
95
114
|
return acc;
|
|
96
|
-
|
|
115
|
+
}, {});
|
|
116
|
+
showRowComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
117
|
+
if (column.component?.showRow) {
|
|
118
|
+
acc[column.name] = getCustomComponent(column.component.showRow);
|
|
119
|
+
}
|
|
120
|
+
return acc;
|
|
121
|
+
}, {});
|
|
122
|
+
|
|
97
123
|
loading.value = false;
|
|
98
124
|
});
|
|
99
125
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @type {import('tailwindcss').Config} */
|
|
2
2
|
export default {
|
|
3
|
-
content: ["./src/**/*.{vue, js}","./src/*.{vue, js}", "./index.html", "./node_modules/flowbite/**/*.js"],
|
|
3
|
+
content: ["./src/**/*.{vue, js, ts, tsx}","./src/*.{vue, js, ts, tsx}", "./index.html", "./node_modules/flowbite/**/*.js"],
|
|
4
4
|
theme: {
|
|
5
5
|
extend: {
|
|
6
6
|
/* IMPORTANT:ADMINFORTH TAILWIND STYLES */
|
package/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import fs from 'fs';
|
|
|
11
11
|
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
12
12
|
import { AdminForthFilterOperators, AdminForthTypes, AdminForthTypesValues } from './types.js';
|
|
13
13
|
import { AdminForthConfig } from './types/AdminForthConfig.js';
|
|
14
|
+
import { getFunctionList } from './modules/utils.js';
|
|
14
15
|
|
|
15
16
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
16
17
|
const DEFAULT_ALLOWED_ACTIONS = {create: true, edit: true, show: true, delete: true};
|
|
@@ -196,7 +197,7 @@ class AdminForth {
|
|
|
196
197
|
errors.push(`Resource "${res.resourceId}" bulkActions must be an array`);
|
|
197
198
|
bulkActions = [];
|
|
198
199
|
}
|
|
199
|
-
if(res.options?.allowDelete){
|
|
200
|
+
if(res.options?.allowDelete && !bulkActions.find((action) => action.label === 'Delete checked')){
|
|
200
201
|
bulkActions.push({
|
|
201
202
|
label: `Delete checked`,
|
|
202
203
|
state: 'danger',
|
|
@@ -471,6 +472,7 @@ class AdminForth {
|
|
|
471
472
|
brandName: this.config.brandName,
|
|
472
473
|
usernameFieldName: usernameColumn.label,
|
|
473
474
|
loginBackgroundImage: this.config.auth.loginBackgroundImage,
|
|
475
|
+
title: this.config?.title,
|
|
474
476
|
};
|
|
475
477
|
},
|
|
476
478
|
});
|
|
@@ -565,8 +567,8 @@ class AdminForth {
|
|
|
565
567
|
return { error: `Resource ${resourceId} not found` };
|
|
566
568
|
}
|
|
567
569
|
|
|
568
|
-
|
|
569
|
-
const resp = await
|
|
570
|
+
for (const hook of getFunctionList(resource.hooks?.[source]?.beforeDatasourceRequest)) {
|
|
571
|
+
const resp = await hook({ resource, query: body, adminUser });
|
|
570
572
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
571
573
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
572
574
|
}
|
|
@@ -645,8 +647,8 @@ class AdminForth {
|
|
|
645
647
|
item._label = resource.itemLabel(item);
|
|
646
648
|
})
|
|
647
649
|
|
|
648
|
-
|
|
649
|
-
const resp = await
|
|
650
|
+
for (const hook of getFunctionList(resource.hooks?.[source]?.afterDatasourceResponse)) {
|
|
651
|
+
const resp = await hook({ resource, response: data.data, adminUser });
|
|
650
652
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
651
653
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
652
654
|
}
|
|
@@ -695,8 +697,9 @@ class AdminForth {
|
|
|
695
697
|
}
|
|
696
698
|
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
697
699
|
const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
698
|
-
|
|
699
|
-
|
|
700
|
+
|
|
701
|
+
for (const hook of getFunctionList(columnConfig.foreignResource.hooks?.beforeDatasourceRequest)) {
|
|
702
|
+
const resp = await hook({ query: body, adminUser });
|
|
700
703
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
701
704
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
702
705
|
}
|
|
@@ -725,8 +728,9 @@ class AdminForth {
|
|
|
725
728
|
const response = {
|
|
726
729
|
items
|
|
727
730
|
};
|
|
728
|
-
|
|
729
|
-
|
|
731
|
+
|
|
732
|
+
for (const hook of getFunctionList(columnConfig.foreignResource.hooks?.afterDatasourceResponse)) {
|
|
733
|
+
const resp = await hook({ response, adminUser });
|
|
730
734
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
731
735
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
732
736
|
}
|
|
@@ -735,7 +739,7 @@ class AdminForth {
|
|
|
735
739
|
return { error: resp.error };
|
|
736
740
|
}
|
|
737
741
|
}
|
|
738
|
-
|
|
742
|
+
|
|
739
743
|
return response;
|
|
740
744
|
},
|
|
741
745
|
});
|
|
@@ -782,15 +786,15 @@ class AdminForth {
|
|
|
782
786
|
|
|
783
787
|
const record = body['record'];
|
|
784
788
|
// execute hook if needed
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
789
|
+
for (const hook of getFunctionList(resource.hooks?.create?.beforeSave)) {
|
|
790
|
+
const resp = await hook({ resource, record, adminUser });
|
|
791
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
792
|
+
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (resp.error) {
|
|
796
|
+
return { error: resp.error };
|
|
797
|
+
}
|
|
794
798
|
}
|
|
795
799
|
|
|
796
800
|
for (const column of resource.columns) {
|
|
@@ -828,15 +832,15 @@ class AdminForth {
|
|
|
828
832
|
const connector = this.connectors[resource.dataSource];
|
|
829
833
|
await connector.createRecord({ resource, record });
|
|
830
834
|
// execute hook if needed
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
835
|
+
for (const hook of getFunctionList(resource.hooks?.create?.afterSave)) {
|
|
836
|
+
const resp = await hook({ resource, record, adminUser });
|
|
837
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
838
|
+
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (resp.error) {
|
|
842
|
+
return { error: resp.error };
|
|
843
|
+
}
|
|
840
844
|
}
|
|
841
845
|
|
|
842
846
|
return {
|
|
@@ -863,17 +867,16 @@ class AdminForth {
|
|
|
863
867
|
const record = body['record'];
|
|
864
868
|
|
|
865
869
|
// execute hook if needed
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
if (resp.error) {
|
|
873
|
-
return { error: resp.error };
|
|
874
|
-
}
|
|
875
|
-
}
|
|
870
|
+
for (const hook of getFunctionList(resource.hooks?.edit?.beforeSave)) {
|
|
871
|
+
const resp = await hook({ resource, record, adminUser });
|
|
872
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
873
|
+
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
874
|
+
}
|
|
876
875
|
|
|
876
|
+
if (resp.error) {
|
|
877
|
+
return { error: resp.error };
|
|
878
|
+
}
|
|
879
|
+
}
|
|
877
880
|
const newValues = {};
|
|
878
881
|
|
|
879
882
|
for (const recordField in record) {
|
|
@@ -893,15 +896,15 @@ class AdminForth {
|
|
|
893
896
|
}
|
|
894
897
|
|
|
895
898
|
// execute hook if needed
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
899
|
+
for (const hook of getFunctionList(resource.hooks?.edit?.afterSave)) {
|
|
900
|
+
const resp = await hook({ resource, record, adminUser });
|
|
901
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
902
|
+
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (resp.error) {
|
|
906
|
+
return { error: resp.error };
|
|
907
|
+
}
|
|
905
908
|
}
|
|
906
909
|
|
|
907
910
|
return {
|
|
@@ -920,30 +923,30 @@ class AdminForth {
|
|
|
920
923
|
}
|
|
921
924
|
|
|
922
925
|
// execute hook if needed
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
926
|
+
for (const hook of getFunctionList(resource.hooks?.delete?.beforeSave)) {
|
|
927
|
+
const resp = await hook({ resource, record, adminUser });
|
|
928
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
929
|
+
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (resp.error) {
|
|
933
|
+
return { error: resp.error };
|
|
934
|
+
}
|
|
932
935
|
}
|
|
933
936
|
|
|
934
937
|
const connector = this.connectors[resource.dataSource];
|
|
935
938
|
await connector.deleteRecord({ resource, recordId: body['primaryKey']});
|
|
936
939
|
|
|
937
940
|
// execute hook if needed
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
941
|
+
for (const hook of getFunctionList(resource.hooks?.delete?.afterSave)) {
|
|
942
|
+
const resp = await hook({ resource, record, adminUser });
|
|
943
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
944
|
+
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
if (resp.error) {
|
|
948
|
+
return { error: resp.error };
|
|
949
|
+
}
|
|
947
950
|
}
|
|
948
951
|
return {
|
|
949
952
|
recordId: body['primaryKey']
|
package/modules/codeInjector.ts
CHANGED
|
@@ -238,6 +238,8 @@ class CodeInjector {
|
|
|
238
238
|
const componentName = getComponentNameFromPath(filePath);
|
|
239
239
|
this.allComponentNames[filePath] = componentName;
|
|
240
240
|
});
|
|
241
|
+
|
|
242
|
+
console.log('🔧 Injecting code into Vue sources...', this.allComponentNames);
|
|
241
243
|
|
|
242
244
|
let customComponentsImports = '';
|
|
243
245
|
for (const [targetPath, component] of Object.entries(this.allComponentNames)) {
|
package/modules/utils.ts
CHANGED
|
@@ -14,7 +14,6 @@ export function guessLabelFromName(name) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
export const currentFileDir = (metaUrl) => {
|
|
19
18
|
const __filename = fileURLToPath(metaUrl);
|
|
20
19
|
let __dirname = path.dirname(__filename);
|
|
@@ -32,5 +31,17 @@ const package_json = JSON.parse(fs.readFileSync(path.join(ADMIN_FORTH_ABSOLUTE_P
|
|
|
32
31
|
export const ADMINFORTH_VERSION: string = package_json.version;
|
|
33
32
|
|
|
34
33
|
export function getComponentNameFromPath(filePath) {
|
|
35
|
-
return filePath.replace(/@/g, '').replace(
|
|
34
|
+
return filePath.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getFunctionList(param?: Function | Array<Function>) {
|
|
38
|
+
if (param) {
|
|
39
|
+
if (Array.isArray(param)) {
|
|
40
|
+
return param;
|
|
41
|
+
} else {
|
|
42
|
+
return [param];
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
36
47
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminforth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.63",
|
|
4
4
|
"description": "OpenSource Vue3 powered forth-generation admin panel",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
9
|
"build": "tsc",
|
|
10
|
-
"rollout": "tsc && cp -r spa dist/spa && npm version patch && npm publish"
|
|
10
|
+
"rollout": "tsc && cp -r spa dist/spa && npm version patch && npm publish",
|
|
11
|
+
"docs": "typedoc --out docs index.ts"
|
|
11
12
|
},
|
|
12
13
|
"author": "devforth.io",
|
|
13
14
|
"license": "ISC",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@types/node": "^20.14.2",
|
|
30
|
+
"typedoc": "^0.25.13",
|
|
29
31
|
"typescript": "^5.4.5"
|
|
30
32
|
}
|
|
31
33
|
}
|
package/spa/src/App.vue
CHANGED
|
@@ -113,7 +113,8 @@
|
|
|
113
113
|
</div>
|
|
114
114
|
<div v-else class="flex items-center justify-center h-screen">
|
|
115
115
|
<div class="text-3xl text-gray-400 animate-bounce">
|
|
116
|
-
|
|
116
|
+
<svg aria-hidden="true" class="w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/><path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/></svg>
|
|
117
|
+
<span class="sr-only">Loading...</span>
|
|
117
118
|
</div>
|
|
118
119
|
</div>
|
|
119
120
|
<AcceptModal />
|
|
@@ -191,10 +192,12 @@ async function initRouter() {
|
|
|
191
192
|
// initialize components based on data attribute selectors
|
|
192
193
|
onMounted(async () => {
|
|
193
194
|
initRouter();
|
|
194
|
-
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
initFlowbite();
|
|
197
|
+
}, 100);
|
|
195
198
|
await coreStore.fetchMenuAndResource();
|
|
196
199
|
loginRedirectCheckIsReady.value = true;
|
|
197
|
-
title.value = coreStore.config
|
|
200
|
+
title.value = coreStore.config?.title || 'AdminForth';
|
|
198
201
|
useHead({
|
|
199
202
|
title: title.value,
|
|
200
203
|
link: [
|
|
@@ -45,10 +45,28 @@
|
|
|
45
45
|
class="bg-form-view-bg odd:dark:bg-gray-900 even:dark:bg-gray-800 border-b dark:border-gray-700"
|
|
46
46
|
>
|
|
47
47
|
<component
|
|
48
|
-
|
|
48
|
+
v-if="showRowComponentsPerColumn[column.name]"
|
|
49
|
+
:is="showRowComponentsPerColumn[column.name]"
|
|
49
50
|
:column="column"
|
|
50
51
|
:row="coreStore.record"
|
|
51
52
|
/>
|
|
53
|
+
<template v-else>
|
|
54
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
|
55
|
+
{{ column.label }}
|
|
56
|
+
</td>
|
|
57
|
+
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
58
|
+
<component
|
|
59
|
+
v-if="showComponentsPerColumn[column.name]"
|
|
60
|
+
:is="showComponentsPerColumn[column.name]"
|
|
61
|
+
:column="column"
|
|
62
|
+
:row="coreStore.record"
|
|
63
|
+
/>
|
|
64
|
+
<ValueRenderer v-else
|
|
65
|
+
:column="column"
|
|
66
|
+
:row="coreStore.record"
|
|
67
|
+
/>
|
|
68
|
+
</td>
|
|
69
|
+
</template>
|
|
52
70
|
</tr>
|
|
53
71
|
</tbody>
|
|
54
72
|
</table>
|
|
@@ -76,6 +94,7 @@ const item = ref(null);
|
|
|
76
94
|
const route = useRoute();
|
|
77
95
|
const loading = ref(false);
|
|
78
96
|
let showComponentsPerColumn = {};
|
|
97
|
+
let showRowComponentsPerColumn = {};
|
|
79
98
|
|
|
80
99
|
const coreStore = useCoreStore();
|
|
81
100
|
|
|
@@ -93,7 +112,14 @@ onMounted(async () => {
|
|
|
93
112
|
acc[column.name] = getCustomComponent(column.component.show);
|
|
94
113
|
}
|
|
95
114
|
return acc;
|
|
96
|
-
|
|
115
|
+
}, {});
|
|
116
|
+
showRowComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
117
|
+
if (column.component?.showRow) {
|
|
118
|
+
acc[column.name] = getCustomComponent(column.component.showRow);
|
|
119
|
+
}
|
|
120
|
+
return acc;
|
|
121
|
+
}, {});
|
|
122
|
+
|
|
97
123
|
loading.value = false;
|
|
98
124
|
});
|
|
99
125
|
|
package/spa/tailwind.config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @type {import('tailwindcss').Config} */
|
|
2
2
|
export default {
|
|
3
|
-
content: ["./src/**/*.{vue, js}","./src/*.{vue, js}", "./index.html", "./node_modules/flowbite/**/*.js"],
|
|
3
|
+
content: ["./src/**/*.{vue, js, ts, tsx}","./src/*.{vue, js, ts, tsx}", "./index.html", "./node_modules/flowbite/**/*.js"],
|
|
4
4
|
theme: {
|
|
5
5
|
extend: {
|
|
6
6
|
/* IMPORTANT:ADMINFORTH TAILWIND STYLES */
|
|
@@ -49,24 +49,24 @@ export type AdminForthResource = {
|
|
|
49
49
|
plugins?: Array<AdminForthPlugin>,
|
|
50
50
|
hooks?: {
|
|
51
51
|
show?: {
|
|
52
|
-
beforeDatasourceRequest?: Function
|
|
53
|
-
afterDatasourceResponse?: Function
|
|
52
|
+
beforeDatasourceRequest?: Function | Array<Function>,
|
|
53
|
+
afterDatasourceResponse?: Function | Array<Function>,
|
|
54
54
|
},
|
|
55
55
|
list?: {
|
|
56
|
-
beforeDatasourceRequest?: Function
|
|
57
|
-
afterDatasourceResponse?: Function
|
|
56
|
+
beforeDatasourceRequest?: Function | Array<Function>,
|
|
57
|
+
afterDatasourceResponse?: Function | Array<Function>,
|
|
58
58
|
},
|
|
59
59
|
create?: {
|
|
60
|
-
beforeSave?: Function
|
|
61
|
-
afterSave?: Function
|
|
60
|
+
beforeSave?: Function | Array<Function>,
|
|
61
|
+
afterSave?: Function | Array<Function>,
|
|
62
62
|
},
|
|
63
63
|
edit?: {
|
|
64
|
-
beforeSave?: Function
|
|
65
|
-
afterSave?: Function
|
|
64
|
+
beforeSave?: Function | Array<Function>,
|
|
65
|
+
afterSave?: Function | Array<Function>,
|
|
66
66
|
},
|
|
67
67
|
delete?: {
|
|
68
|
-
beforeSave?: Function
|
|
69
|
-
afterSave?: Function
|
|
68
|
+
beforeSave?: Function | Array<Function>,
|
|
69
|
+
afterSave?: Function | Array<Function>,
|
|
70
70
|
},
|
|
71
71
|
},
|
|
72
72
|
options?: {
|
|
@@ -134,9 +134,11 @@ export type DataSource = {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
export type AdminForthResourceColumnComponent = {
|
|
137
|
-
show?: string,
|
|
137
|
+
show?: string, // rewrite value in show
|
|
138
|
+
showRow?: string, // rewrite full view table row (both <td> tags)
|
|
138
139
|
create?: string,
|
|
139
140
|
edit?: string,
|
|
141
|
+
list?: string,
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<td class="px-6 py-4 whitespace-nowrap">
|
|
3
|
-
{{ column.label }}
|
|
4
|
-
</td>
|
|
5
|
-
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
6
|
-
<ValueRenderer :column="column" :row="row" />
|
|
7
|
-
</td>
|
|
8
|
-
</template>
|
|
9
|
-
|
|
10
|
-
<script setup>
|
|
11
|
-
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
12
|
-
|
|
13
|
-
defineProps(['column', 'row']);
|
|
14
|
-
</script>
|