adminforth 1.0.57 → 1.0.58
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 +12 -1
- package/dist/plugins/ForeignInlineListPlugin/index.js +22 -0
- package/dist/plugins/base.js +8 -0
- package/dist/servers/express.js +17 -2
- package/dist/spa/spa/src/App.vue +20 -4
- package/index.ts +15 -2
- package/package.json +1 -1
- package/plugins/ForeignInlineListPlugin/index.ts +35 -0
- package/plugins/base.ts +16 -0
- package/servers/express.ts +19 -2
- package/spa/src/App.vue +20 -4
- package/types/AdminForthConfig.ts +32 -31
package/dist/index.js
CHANGED
|
@@ -33,6 +33,8 @@ class AdminForth {
|
|
|
33
33
|
});
|
|
34
34
|
this.config = Object.assign(Object.assign({}, __classPrivateFieldGet(this, _AdminForth_defaultConfig, "f")), config);
|
|
35
35
|
this.validateConfig();
|
|
36
|
+
this.activatePlugins();
|
|
37
|
+
this.validateConfig(); // revalidate after plugins
|
|
36
38
|
this.express = new ExpressServer(this);
|
|
37
39
|
this.auth = new Auth();
|
|
38
40
|
this.codeInjector = new CodeInjector(this);
|
|
@@ -40,6 +42,14 @@ class AdminForth {
|
|
|
40
42
|
this.statuses = {};
|
|
41
43
|
console.log(`🚀 AdminForth v${ADMINFORTH_VERSION} starting up`);
|
|
42
44
|
}
|
|
45
|
+
activatePlugins() {
|
|
46
|
+
for (let resource of this.config.resources) {
|
|
47
|
+
for (let pluginInstance of resource.plugins || []) {
|
|
48
|
+
pluginInstance.modifyResourceConfig(this, resource);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
;
|
|
52
|
+
}
|
|
43
53
|
validateConfig() {
|
|
44
54
|
if (this.config.rootUser) {
|
|
45
55
|
if (!this.config.rootUser.username) {
|
|
@@ -456,7 +466,8 @@ class AdminForth {
|
|
|
456
466
|
if (!resource) {
|
|
457
467
|
return { error: `Resource ${resourceId} not found` };
|
|
458
468
|
}
|
|
459
|
-
|
|
469
|
+
// exclude "plugins" key
|
|
470
|
+
return { resource: Object.assign(Object.assign({}, resource), { plugins: undefined }) };
|
|
460
471
|
}),
|
|
461
472
|
});
|
|
462
473
|
server.endpoint({
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import AdminForthPlugin from "../base.js";
|
|
2
|
+
export default class ForeignInlineListPlugin extends AdminForthPlugin {
|
|
3
|
+
constructor(options) {
|
|
4
|
+
super(options);
|
|
5
|
+
this.options = options;
|
|
6
|
+
console.log('ForeignInlineListPlugin', this);
|
|
7
|
+
}
|
|
8
|
+
modifyResourceConfig(adminforth, resourceConfig) {
|
|
9
|
+
super.modifyResourceConfig(adminforth, resourceConfig);
|
|
10
|
+
// get resource with foreignResourceId
|
|
11
|
+
this.foreignResource = adminforth.config.resources.find((resource) => resource.resourceId === this.options.foreignResourceId);
|
|
12
|
+
if (!this.foreignResource) {
|
|
13
|
+
throw new Error(`ForeignInlineListPlugin: Resource with ID "${this.options.foreignResourceId}" not found`);
|
|
14
|
+
}
|
|
15
|
+
resourceConfig.columns.push({
|
|
16
|
+
name: `foreignInlineList_${this.foreignResource.resourceId}`,
|
|
17
|
+
label: 'Foreign Inline List',
|
|
18
|
+
virtual: true,
|
|
19
|
+
showIn: ['show'],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/servers/express.js
CHANGED
|
@@ -107,7 +107,14 @@ class ExpressServer {
|
|
|
107
107
|
}
|
|
108
108
|
else {
|
|
109
109
|
this.expressApp.get(`${slashedPrefix}assets/*`, (req, res) => {
|
|
110
|
-
res.sendFile(path.join(CodeInjector.SPA_TMP_PATH, 'dist', replaceAtStart(req.url, prefix))
|
|
110
|
+
res.sendFile(path.join(CodeInjector.SPA_TMP_PATH, 'dist', replaceAtStart(req.url, prefix)), {
|
|
111
|
+
cacheControl: false,
|
|
112
|
+
// store for a year
|
|
113
|
+
headers: {
|
|
114
|
+
'Cache-Control': 'public, max-age=31536000',
|
|
115
|
+
'Pragma': 'public',
|
|
116
|
+
}
|
|
117
|
+
});
|
|
111
118
|
});
|
|
112
119
|
this.expressApp.get(`${prefix}*`, (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
113
120
|
const fullPath = path.join(CodeInjector.SPA_TMP_PATH, 'dist', 'index.html');
|
|
@@ -122,7 +129,15 @@ class ExpressServer {
|
|
|
122
129
|
res.status(500).send(respondNoServer(`${this.adminforth.config.brandName} is still warming up`, 'Please wait a moment...'));
|
|
123
130
|
return;
|
|
124
131
|
}
|
|
125
|
-
res.sendFile(fullPath
|
|
132
|
+
res.sendFile(fullPath, {
|
|
133
|
+
cacheControl: false,
|
|
134
|
+
headers: {
|
|
135
|
+
'Content-Type': 'text/html',
|
|
136
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
137
|
+
'Pragma': 'no-cache',
|
|
138
|
+
'Expires': '0'
|
|
139
|
+
}
|
|
140
|
+
});
|
|
126
141
|
}));
|
|
127
142
|
}
|
|
128
143
|
}
|
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -55,10 +55,12 @@
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
<aside
|
|
58
|
+
|
|
58
59
|
v-if="loggedIn"
|
|
59
60
|
|
|
60
61
|
id="logo-sidebar" class="fixed border-none top-0 left-0 z-40 w-64 h-screen transition-transform -translate-x-full bg-nav-menu-bg border-r border- sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700" aria-label="Sidebar">
|
|
61
62
|
<div class="h-full px-3 pb-4 overflow-y-auto bg-nav-menu-bg dark:bg-gray-800">
|
|
63
|
+
|
|
62
64
|
<div class="flex ms-2 md:me-24 m-4 ">
|
|
63
65
|
<img src="https://flowbite.com/docs/images/logo.svg" class="h-8 me-3" alt="FlowBite Logo" />
|
|
64
66
|
<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,17 +73,15 @@
|
|
|
71
73
|
<div v-else-if="item.type === 'gap'" class="flex items-center justify-center h-8"></div>
|
|
72
74
|
<div v-else-if="item.type === 'heading'" class="flex items-center justify-left pl-2 h-8 text-gray-400 dark:text-gray-400
|
|
73
75
|
">{{ item.label }}</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
76
|
<li v-else-if="item.children">
|
|
77
|
-
<button
|
|
77
|
+
<button @click="clickOnMenuItem(i)" type="button" class="flex items-center w-full p-2 text-base text-nav-menu-text rounded-default transition duration-75 group hover:bg-nav-menu-bg-hover dark:text-white dark:hover:bg-gray-700"
|
|
78
78
|
:aria-controls="`dropdown-example${i}`"
|
|
79
79
|
:data-collapse-toggle="`dropdown-example${i}`"
|
|
80
80
|
>
|
|
81
81
|
<component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 dark:group-hover:text-white" ></component>
|
|
82
82
|
|
|
83
83
|
<span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}</span>
|
|
84
|
-
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
|
84
|
+
<svg :class="{'rotate-180': opened.includes(i) }" class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
|
85
85
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
|
86
86
|
</svg>
|
|
87
87
|
</button>
|
|
@@ -144,6 +144,8 @@ createHead()
|
|
|
144
144
|
const route = useRoute();
|
|
145
145
|
const router = useRouter();
|
|
146
146
|
const title = ref('');
|
|
147
|
+
//create a ref to store the opened menu items with ts type;
|
|
148
|
+
const opened = ref<string[]>([]);
|
|
147
149
|
|
|
148
150
|
|
|
149
151
|
const routerIsReady = ref(false);
|
|
@@ -157,6 +159,15 @@ function toggleTheme() {
|
|
|
157
159
|
document.documentElement.classList.toggle('dark');
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
function clickOnMenuItem (label: string) {
|
|
163
|
+
if (opened.value.includes(label)) {
|
|
164
|
+
opened.value = opened.value.filter((item) => item !== label);
|
|
165
|
+
} else {
|
|
166
|
+
opened.value.push(label);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
160
171
|
async function logout() {
|
|
161
172
|
await coreStore.logout();
|
|
162
173
|
router.push({ name: 'login' });
|
|
@@ -186,6 +197,11 @@ onMounted(async () => {
|
|
|
186
197
|
],
|
|
187
198
|
|
|
188
199
|
})
|
|
200
|
+
coreStore.menu.forEach((item, i) => {
|
|
201
|
+
if (item.open) {
|
|
202
|
+
opened.value.push(i);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
189
205
|
})
|
|
190
206
|
|
|
191
207
|
</script>
|
package/index.ts
CHANGED
|
@@ -51,15 +51,27 @@ class AdminForth {
|
|
|
51
51
|
|
|
52
52
|
constructor(config: AdminForthConfig) {
|
|
53
53
|
this.config = {...this.#defaultConfig,...config};
|
|
54
|
+
|
|
54
55
|
this.validateConfig();
|
|
56
|
+
this.activatePlugins();
|
|
57
|
+
this.validateConfig(); // revalidate after plugins
|
|
58
|
+
|
|
55
59
|
this.express = new ExpressServer(this);
|
|
56
60
|
this.auth = new Auth();
|
|
57
61
|
this.codeInjector = new CodeInjector(this);
|
|
58
62
|
this.connectors = {};
|
|
59
|
-
this.statuses = {}
|
|
63
|
+
this.statuses = {};
|
|
60
64
|
console.log(`🚀 AdminForth v${ADMINFORTH_VERSION} starting up`)
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
activatePlugins() {
|
|
68
|
+
for (let resource of this.config.resources) {
|
|
69
|
+
for (let pluginInstance of resource.plugins || []) {
|
|
70
|
+
pluginInstance.modifyResourceConfig(this, resource);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
validateConfig() {
|
|
64
76
|
if (this.config.rootUser) {
|
|
65
77
|
if (!this.config.rootUser.username) {
|
|
@@ -527,7 +539,8 @@ class AdminForth {
|
|
|
527
539
|
if (!resource) {
|
|
528
540
|
return { error: `Resource ${resourceId} not found` };
|
|
529
541
|
}
|
|
530
|
-
|
|
542
|
+
// exclude "plugins" key
|
|
543
|
+
return { resource: { ...resource, plugins: undefined } };
|
|
531
544
|
},
|
|
532
545
|
});
|
|
533
546
|
server.endpoint({
|
package/package.json
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AdminForthResource } from "../../types/AdminForthConfig.js";
|
|
2
|
+
import AdminForthPlugin from "../base.js";
|
|
3
|
+
import AdminForth from "../../index.js";
|
|
4
|
+
|
|
5
|
+
type PluginOptions = {
|
|
6
|
+
foreignResourceId: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default class ForeignInlineListPlugin extends AdminForthPlugin {
|
|
10
|
+
foreignResource: AdminForthResource;
|
|
11
|
+
options: PluginOptions;
|
|
12
|
+
|
|
13
|
+
constructor(options: PluginOptions) {
|
|
14
|
+
super(options);
|
|
15
|
+
this.options = options;
|
|
16
|
+
console.log('ForeignInlineListPlugin', this);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
modifyResourceConfig(adminforth: AdminForth, resourceConfig: AdminForthResource) {
|
|
20
|
+
super.modifyResourceConfig(adminforth, resourceConfig);
|
|
21
|
+
|
|
22
|
+
// get resource with foreignResourceId
|
|
23
|
+
this.foreignResource = adminforth.config.resources.find((resource) => resource.resourceId === this.options.foreignResourceId);
|
|
24
|
+
if (!this.foreignResource) {
|
|
25
|
+
throw new Error(`ForeignInlineListPlugin: Resource with ID "${this.options.foreignResourceId}" not found`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resourceConfig.columns.push({
|
|
29
|
+
name: `foreignInlineList_${this.foreignResource.resourceId}`,
|
|
30
|
+
label: 'Foreign Inline List',
|
|
31
|
+
virtual: true,
|
|
32
|
+
showIn: ['show'],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
package/plugins/base.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AdminForthResource } from '../types/AdminForthConfig.js';
|
|
2
|
+
import AdminForth from '../index.js';
|
|
3
|
+
|
|
4
|
+
export default class AdminForthPlugin {
|
|
5
|
+
|
|
6
|
+
adminforth: AdminForth;
|
|
7
|
+
|
|
8
|
+
constructor(pluginOptions: any) {
|
|
9
|
+
// set up plugin here
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
modifyResourceConfig(adminforth: AdminForth, resourceConfig: AdminForthResource) {
|
|
13
|
+
this.adminforth = adminforth;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
}
|
package/servers/express.ts
CHANGED
|
@@ -112,7 +112,17 @@ class ExpressServer {
|
|
|
112
112
|
|
|
113
113
|
} else {
|
|
114
114
|
this.expressApp.get(`${slashedPrefix}assets/*`, (req, res) => {
|
|
115
|
-
res.sendFile(
|
|
115
|
+
res.sendFile(
|
|
116
|
+
path.join(CodeInjector.SPA_TMP_PATH, 'dist', replaceAtStart(req.url, prefix)),
|
|
117
|
+
{
|
|
118
|
+
cacheControl: false,
|
|
119
|
+
// store for a year
|
|
120
|
+
headers: {
|
|
121
|
+
'Cache-Control': 'public, max-age=31536000',
|
|
122
|
+
'Pragma': 'public',
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
)
|
|
116
126
|
})
|
|
117
127
|
|
|
118
128
|
this.expressApp.get(`${prefix}*`, async (req, res) => {
|
|
@@ -128,7 +138,14 @@ class ExpressServer {
|
|
|
128
138
|
res.status(500).send(respondNoServer(`${this.adminforth.config.brandName} is still warming up`, 'Please wait a moment...'));
|
|
129
139
|
return;
|
|
130
140
|
}
|
|
131
|
-
res.sendFile(fullPath
|
|
141
|
+
res.sendFile(fullPath, {
|
|
142
|
+
cacheControl: false,
|
|
143
|
+
headers: {
|
|
144
|
+
'Content-Type': 'text/html',
|
|
145
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
146
|
+
'Pragma': 'no-cache',
|
|
147
|
+
'Expires': '0'
|
|
148
|
+
} });
|
|
132
149
|
});
|
|
133
150
|
}
|
|
134
151
|
}
|
package/spa/src/App.vue
CHANGED
|
@@ -55,10 +55,12 @@
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
<aside
|
|
58
|
+
|
|
58
59
|
v-if="loggedIn"
|
|
59
60
|
|
|
60
61
|
id="logo-sidebar" class="fixed border-none top-0 left-0 z-40 w-64 h-screen transition-transform -translate-x-full bg-nav-menu-bg border-r border- sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700" aria-label="Sidebar">
|
|
61
62
|
<div class="h-full px-3 pb-4 overflow-y-auto bg-nav-menu-bg dark:bg-gray-800">
|
|
63
|
+
|
|
62
64
|
<div class="flex ms-2 md:me-24 m-4 ">
|
|
63
65
|
<img src="https://flowbite.com/docs/images/logo.svg" class="h-8 me-3" alt="FlowBite Logo" />
|
|
64
66
|
<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,17 +73,15 @@
|
|
|
71
73
|
<div v-else-if="item.type === 'gap'" class="flex items-center justify-center h-8"></div>
|
|
72
74
|
<div v-else-if="item.type === 'heading'" class="flex items-center justify-left pl-2 h-8 text-gray-400 dark:text-gray-400
|
|
73
75
|
">{{ item.label }}</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
76
|
<li v-else-if="item.children">
|
|
77
|
-
<button
|
|
77
|
+
<button @click="clickOnMenuItem(i)" type="button" class="flex items-center w-full p-2 text-base text-nav-menu-text rounded-default transition duration-75 group hover:bg-nav-menu-bg-hover dark:text-white dark:hover:bg-gray-700"
|
|
78
78
|
:aria-controls="`dropdown-example${i}`"
|
|
79
79
|
:data-collapse-toggle="`dropdown-example${i}`"
|
|
80
80
|
>
|
|
81
81
|
<component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 dark:group-hover:text-white" ></component>
|
|
82
82
|
|
|
83
83
|
<span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}</span>
|
|
84
|
-
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
|
84
|
+
<svg :class="{'rotate-180': opened.includes(i) }" class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
|
85
85
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
|
86
86
|
</svg>
|
|
87
87
|
</button>
|
|
@@ -144,6 +144,8 @@ createHead()
|
|
|
144
144
|
const route = useRoute();
|
|
145
145
|
const router = useRouter();
|
|
146
146
|
const title = ref('');
|
|
147
|
+
//create a ref to store the opened menu items with ts type;
|
|
148
|
+
const opened = ref<string[]>([]);
|
|
147
149
|
|
|
148
150
|
|
|
149
151
|
const routerIsReady = ref(false);
|
|
@@ -157,6 +159,15 @@ function toggleTheme() {
|
|
|
157
159
|
document.documentElement.classList.toggle('dark');
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
function clickOnMenuItem (label: string) {
|
|
163
|
+
if (opened.value.includes(label)) {
|
|
164
|
+
opened.value = opened.value.filter((item) => item !== label);
|
|
165
|
+
} else {
|
|
166
|
+
opened.value.push(label);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
}
|
|
170
|
+
|
|
160
171
|
async function logout() {
|
|
161
172
|
await coreStore.logout();
|
|
162
173
|
router.push({ name: 'login' });
|
|
@@ -186,6 +197,11 @@ onMounted(async () => {
|
|
|
186
197
|
],
|
|
187
198
|
|
|
188
199
|
})
|
|
200
|
+
coreStore.menu.forEach((item, i) => {
|
|
201
|
+
if (item.open) {
|
|
202
|
+
opened.value.push(i);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
189
205
|
})
|
|
190
206
|
|
|
191
207
|
</script>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import AdminForthPlugin from '../plugins/base.js';
|
|
2
2
|
|
|
3
3
|
export type AdminForthConfigMenuItem = {
|
|
4
4
|
type?: 'heading' | 'group' | 'resource' | 'page' | 'gap' | 'divider',
|
|
@@ -8,6 +8,7 @@ export type AdminForthConfigMenuItem = {
|
|
|
8
8
|
component?: string,
|
|
9
9
|
resourceId?: string,
|
|
10
10
|
homepage?: boolean,
|
|
11
|
+
open?: boolean,
|
|
11
12
|
children?: Array<AdminForthConfigMenuItem>,
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -44,39 +45,39 @@ export type AdminForthResource = {
|
|
|
44
45
|
dataSource: string,
|
|
45
46
|
columns: Array<AdminForthResourceColumn>,
|
|
46
47
|
itemLabel?: Function,
|
|
48
|
+
plugins?: Array<AdminForthPlugin>,
|
|
47
49
|
hooks?: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
show?: {
|
|
51
|
+
beforeDatasourceRequest?: Function,
|
|
52
|
+
afterDatasourceResponse?: Function,
|
|
53
|
+
},
|
|
54
|
+
list?: {
|
|
55
|
+
beforeDatasourceRequest?: Function,
|
|
56
|
+
afterDatasourceResponse?: Function,
|
|
57
|
+
},
|
|
58
|
+
create?: {
|
|
59
|
+
beforeSave?: Function,
|
|
60
|
+
afterSave?: Function,
|
|
61
|
+
},
|
|
62
|
+
edit?: {
|
|
63
|
+
beforeSave?: Function,
|
|
64
|
+
afterSave?: Function,
|
|
65
|
+
},
|
|
66
|
+
delete?: {
|
|
67
|
+
beforeSave?: Function,
|
|
68
|
+
afterSave?: Function,
|
|
69
|
+
},
|
|
68
70
|
},
|
|
69
71
|
options?: {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
bulkActions?: Array<{
|
|
73
|
+
label: string,
|
|
74
|
+
state: string,
|
|
75
|
+
icon: string,
|
|
76
|
+
action: Function,
|
|
77
|
+
id?: string,
|
|
78
|
+
}>,
|
|
79
|
+
allowedActions?: AllowedActions,
|
|
80
|
+
allowDelete?: boolean,
|
|
80
81
|
},
|
|
81
82
|
}
|
|
82
83
|
|