adminforth 1.21.0-next.9 → 1.22.0
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/commands/createCustomComponent/configUpdater.js +279 -0
- package/commands/createCustomComponent/fileGenerator.js +83 -7
- package/commands/createCustomComponent/main.js +248 -65
- package/commands/createCustomComponent/templates/customCrud/afterBreadcrumbs.vue.hbs +30 -0
- package/commands/createCustomComponent/templates/customCrud/beforeBreadcrumbs.vue.hbs +30 -0
- package/commands/createCustomComponent/templates/customCrud/bottom.vue.hbs +61 -0
- package/commands/createCustomComponent/templates/customCrud/threeDotsDropdownItems.vue.hbs +55 -0
- package/commands/createCustomComponent/templates/global/everyPageBottom.vue.hbs +11 -0
- package/commands/createCustomComponent/templates/global/header.vue.hbs +11 -0
- package/commands/createCustomComponent/templates/global/sidebar.vue.hbs +11 -0
- package/commands/createCustomComponent/templates/global/userMenu.vue.hbs +11 -0
- package/commands/createCustomComponent/templates/login/afterLogin.vue.hbs +12 -0
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +12 -3
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/spa/package-lock.json +171 -83
- package/dist/spa/package.json +2 -2
- package/dist/spa/src/afcl/ProgressBar.vue +2 -2
- package/dist/spa/src/types/Adapters.ts +118 -2
- package/dist/spa/src/utils.ts +2 -2
- package/dist/types/Adapters.d.ts +102 -1
- package/dist/types/Adapters.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { select,
|
|
2
|
-
import chalk from 'chalk'
|
|
3
|
-
import path from 'path';
|
|
1
|
+
import { select, Separator, search, input } from '@inquirer/prompts';
|
|
2
|
+
import chalk from 'chalk';// Import path
|
|
3
|
+
import path from 'path';
|
|
4
4
|
import { loadAdminForthConfig } from './configLoader.js'; // Helper to load config
|
|
5
|
-
import { generateComponentFile } from './fileGenerator.js'; // Helper to create the .vue file
|
|
6
|
-
import { updateResourceConfig } from './configUpdater.js'; // Helper to modify resource .ts file
|
|
7
|
-
|
|
5
|
+
import { generateComponentFile, generateLoginOrGlobalComponentFile, generateCrudInjectionComponent } from './fileGenerator.js'; // Helper to create the .vue file
|
|
6
|
+
import { updateResourceConfig, injectLoginComponent, injectGlobalComponent, updateCrudInjectionConfig } from './configUpdater.js'; // Helper to modify resource .ts file
|
|
7
|
+
|
|
8
|
+
function sanitizeLabel(input){
|
|
9
|
+
return input
|
|
10
|
+
.replace(/[^a-zA-Z0-9\s]/g, '')
|
|
11
|
+
.split(' ')
|
|
12
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
13
|
+
.join('');
|
|
14
|
+
}
|
|
8
15
|
|
|
9
16
|
export default async function createComponent(args) {
|
|
10
17
|
console.log('This command will help you to generate boilerplate for component.\n');
|
|
@@ -64,13 +71,23 @@ async function handleFieldComponentCreation(config, resources) {
|
|
|
64
71
|
const selectedResource = resources.find(r => r.resourceId === resourceId);
|
|
65
72
|
console.log(chalk.grey(`Selected ❯ 🔤 Custom fields ❯ ${fieldType} ❯ ${selectedResource.label}`));
|
|
66
73
|
|
|
67
|
-
const columnName = await
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
const columnName = await search({
|
|
75
|
+
message: 'Select column for which you want to create component:',
|
|
76
|
+
source: async (input) => {
|
|
77
|
+
const searchTerm = input ? input.toLowerCase() : '';
|
|
78
|
+
|
|
79
|
+
const filteredColumns = selectedResource.columns.filter(c => {
|
|
80
|
+
const label = c.label || '';
|
|
81
|
+
const name = c.name || '';
|
|
82
|
+
return label.toLowerCase().includes(searchTerm) || name.toLowerCase().includes(searchTerm);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return [
|
|
86
|
+
...filteredColumns.map(c => ({ name: `${c.label} ${chalk.grey(`${c.name}`)}`, value: c.name })),
|
|
87
|
+
new Separator(),
|
|
88
|
+
{ name: '🔙 BACK', value: '__BACK__' },
|
|
89
|
+
];
|
|
90
|
+
},
|
|
74
91
|
});
|
|
75
92
|
if (columnName === '__BACK__') return handleFieldComponentCreation(config, resources); // Pass config back
|
|
76
93
|
|
|
@@ -79,60 +96,226 @@ async function handleFieldComponentCreation(config, resources) {
|
|
|
79
96
|
console.log(chalk.dim(`One-line alternative: |adminforth component fields.${fieldType}.${resourceId}.${columnName}|`));
|
|
80
97
|
|
|
81
98
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
const safeResourceLabel = sanitizeLabel(selectedResource.label)
|
|
100
|
+
const safeColumnLabel = sanitizeLabel(selectedColumn.label)
|
|
101
|
+
const componentFileName = `${safeResourceLabel}${safeColumnLabel}${fieldType.charAt(0).toUpperCase() + fieldType.slice(1)}.vue`; // e.g., UserEmailShow.vue
|
|
102
|
+
const componentPathForConfig = `@@/${componentFileName}`; // Path relative to custom dir for config
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const { alreadyExists, path: absoluteComponentPath } = await generateComponentFile(
|
|
107
|
+
componentFileName,
|
|
108
|
+
fieldType,
|
|
109
|
+
{ resource: selectedResource, column: selectedColumn },
|
|
110
|
+
config
|
|
111
|
+
);
|
|
112
|
+
if (!alreadyExists) {
|
|
113
|
+
console.log(chalk.dim(`Component generation successful: ${absoluteComponentPath}`));
|
|
114
|
+
|
|
115
|
+
await updateResourceConfig(selectedResource.resourceId, columnName, fieldType, componentPathForConfig);
|
|
116
|
+
console.log(
|
|
117
|
+
chalk.bold.greenBright('You can now open the component in your IDE:'),
|
|
118
|
+
chalk.underline.cyanBright(absoluteComponentPath)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}catch (error) {
|
|
123
|
+
console.error(error);
|
|
124
|
+
console.error(chalk.red('\n❌ Component creation failed. Please check the errors above.'));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function handleCrudPageInjectionCreation(config, resources) {
|
|
130
|
+
console.log(chalk.grey('Selected ❯ 📄 CRUD Page Injection'));
|
|
131
|
+
|
|
132
|
+
const crudType = await select({
|
|
133
|
+
message: 'What view do you want to inject a custom component into?',
|
|
134
|
+
choices: [
|
|
135
|
+
{ name: '🔸 list', value: 'list' },
|
|
136
|
+
{ name: '📃 show', value: 'show' },
|
|
137
|
+
{ name: '✏️ edit', value: 'edit' },
|
|
138
|
+
{ name: '➕ create', value: 'create' },
|
|
139
|
+
new Separator(),
|
|
140
|
+
{ name: '🔙 BACK', value: '__BACK__' },
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
if (crudType === '__BACK__') return createComponent([]);
|
|
144
|
+
|
|
145
|
+
console.log(chalk.grey(`Selected ❯ 📄 CRUD Page Injection ❯ ${crudType}`));
|
|
146
|
+
|
|
147
|
+
const resourceId = await select({
|
|
148
|
+
message: 'Select resource for which you want to inject the component:',
|
|
149
|
+
choices: [
|
|
150
|
+
...resources.map(r => ({ name: `${r.label} ${chalk.grey(`${r.resourceId}`)}`, value: r.resourceId })),
|
|
151
|
+
new Separator(),
|
|
152
|
+
{ name: '🔙 BACK', value: '__BACK__' },
|
|
153
|
+
],
|
|
154
|
+
});
|
|
155
|
+
if (resourceId === '__BACK__') return handleCrudPageInjectionCreation(config, resources);
|
|
156
|
+
|
|
157
|
+
const selectedResource = resources.find(r => r.resourceId === resourceId);
|
|
158
|
+
console.log(chalk.grey(`Selected ❯ 📄 CRUD Page Injection ❯ ${crudType} ❯ ${selectedResource.label}`));
|
|
159
|
+
|
|
160
|
+
const injectionPosition = await select({
|
|
161
|
+
message: 'Where exactly do you want to inject the component?',
|
|
162
|
+
choices: [
|
|
163
|
+
{ name: '⬆️ Before Breadcrumbs', value: 'beforeBreadcrumbs' },
|
|
164
|
+
{ name: '⬇️ After Breadcrumbs', value: 'afterBreadcrumbs' },
|
|
165
|
+
{ name: '📄 After Page', value: 'bottom' },
|
|
166
|
+
{ name: '⋯ threeDotsDropdownItems', value: 'threeDotsDropdownItems' },
|
|
167
|
+
new Separator(),
|
|
168
|
+
{ name: '🔙 BACK', value: '__BACK__' },
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
if (injectionPosition === '__BACK__') return handleCrudPageInjectionCreation(config, resources);
|
|
172
|
+
|
|
173
|
+
const isThin = await select({
|
|
174
|
+
message: 'Will this component be thin enough to fit on the same page with list (so list will still shrink)?',
|
|
175
|
+
choices: [
|
|
176
|
+
{ name: 'Yes', value: true },
|
|
177
|
+
{ name: 'No', value: false },
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const safeResourceLabel = sanitizeLabel(selectedResource.label)
|
|
182
|
+
const componentFileName = `${safeResourceLabel}${crudType.charAt(0).toUpperCase() + crudType.slice(1)}${injectionPosition.charAt(0).toUpperCase() + injectionPosition.slice(1)}.vue`;
|
|
183
|
+
const componentPathForConfig = `@@/${componentFileName}`;
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const { alreadyExists, path: absoluteComponentPath } = await generateCrudInjectionComponent(
|
|
187
|
+
componentFileName,
|
|
188
|
+
injectionPosition,
|
|
189
|
+
{ resource: selectedResource },
|
|
190
|
+
config
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (!alreadyExists) {
|
|
194
|
+
console.log(chalk.dim(`Component generation successful: ${absoluteComponentPath}`));
|
|
195
|
+
|
|
196
|
+
await updateCrudInjectionConfig(
|
|
197
|
+
selectedResource.resourceId,
|
|
198
|
+
crudType,
|
|
199
|
+
injectionPosition,
|
|
200
|
+
componentPathForConfig,
|
|
201
|
+
isThin
|
|
202
|
+
);
|
|
203
|
+
console.log(
|
|
204
|
+
chalk.bold.greenBright('You can now open the component in your IDE:'),
|
|
205
|
+
chalk.underline.cyanBright(absoluteComponentPath)
|
|
206
|
+
);
|
|
100
207
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
208
|
+
process.exit(0);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(error);
|
|
211
|
+
console.error(chalk.red('\n❌ Component creation failed. Please check the errors above.'));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
async function handleLoginPageInjectionCreation(config) {
|
|
218
|
+
console.log('Selected ❯ 🔐 Login page injections');
|
|
219
|
+
const injectionType = await select({
|
|
220
|
+
message: 'Select injection type:',
|
|
221
|
+
choices: [
|
|
222
|
+
{ name: 'After Login and password inputs', value: 'afterLogin' },
|
|
223
|
+
{ name: '🔙 BACK', value: '__BACK__' },
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
if (injectionType === '__BACK__') return createComponent([]);
|
|
227
|
+
|
|
228
|
+
console.log(chalk.grey(`Selected ❯ 🔐 Login page injections ❯ ${injectionType}`));
|
|
229
|
+
|
|
230
|
+
const reason = await input({
|
|
231
|
+
message: 'What will you need component for? (enter name)',
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
console.log(chalk.grey(`Selected ❯ 🔐 Login page injections ❯ ${injectionType} ❯ ${reason}`));
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const safeName = sanitizeLabel(reason)
|
|
239
|
+
const componentFileName = `CustomLogin${safeName}.vue`;
|
|
240
|
+
|
|
241
|
+
const context = { reason };
|
|
242
|
+
|
|
243
|
+
const { alreadyExists, path: absoluteComponentPath } = await generateLoginOrGlobalComponentFile(
|
|
244
|
+
componentFileName,
|
|
245
|
+
injectionType,
|
|
246
|
+
context
|
|
247
|
+
);
|
|
248
|
+
if (!alreadyExists) {
|
|
249
|
+
console.log(chalk.dim(`Component generation successful: ${absoluteComponentPath}`));
|
|
250
|
+
const configFilePath = path.resolve(process.cwd(), 'index.ts');
|
|
251
|
+
console.log(chalk.dim(`Injecting component: ${configFilePath}, ${componentFileName}`));
|
|
252
|
+
await injectLoginComponent(configFilePath, `@@/${componentFileName}`);
|
|
253
|
+
|
|
254
|
+
console.log(
|
|
255
|
+
chalk.bold.greenBright('You can now open the component in your IDE:'),
|
|
256
|
+
chalk.underline.cyanBright(absoluteComponentPath)
|
|
257
|
+
);
|
|
131
258
|
}
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}catch (error) {
|
|
261
|
+
console.error(error);
|
|
262
|
+
console.error(chalk.red('\n❌ Component creation failed. Please check the errors above.'));
|
|
263
|
+
process.exit(1);
|
|
132
264
|
}
|
|
265
|
+
|
|
133
266
|
}
|
|
134
267
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
268
|
+
async function handleGlobalInjectionCreation(config) {
|
|
269
|
+
console.log('Selected ❯ 🌍 Global page injections');
|
|
270
|
+
|
|
271
|
+
const injectionType = await select({
|
|
272
|
+
message: 'Select global injection type:',
|
|
273
|
+
choices: [
|
|
274
|
+
{ name: 'User Menu', value: 'userMenu' },
|
|
275
|
+
{ name: 'Header', value: 'header' },
|
|
276
|
+
{ name: 'Sidebar', value: 'sidebar' },
|
|
277
|
+
{ name: 'Every Page Bottom', value: 'everyPageBottom' },
|
|
278
|
+
{ name: '🔙 BACK', value: '__BACK__' },
|
|
279
|
+
],
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (injectionType === '__BACK__') return createComponent([]);
|
|
283
|
+
|
|
284
|
+
console.log(chalk.grey(`Selected ❯ 🌍 Global page injections ❯ ${injectionType}`));
|
|
285
|
+
|
|
286
|
+
const reason = await input({
|
|
287
|
+
message: 'What will you need the component for? (enter name)',
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
console.log(chalk.grey(`Selected ❯ 🌍 Global page injections ❯ ${injectionType} ❯ ${reason}`));
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const safeName = sanitizeLabel(reason)
|
|
294
|
+
const componentFileName = `CustomGlobal${safeName}.vue`;
|
|
295
|
+
|
|
296
|
+
const context = { reason };
|
|
297
|
+
|
|
298
|
+
const { alreadyExists, path: absoluteComponentPath } = await generateLoginOrGlobalComponentFile(
|
|
299
|
+
componentFileName,
|
|
300
|
+
injectionType,
|
|
301
|
+
context
|
|
302
|
+
);
|
|
303
|
+
if (!alreadyExists) {
|
|
304
|
+
console.log(chalk.dim(`Component generation successful: ${absoluteComponentPath}`));
|
|
305
|
+
|
|
306
|
+
const configFilePath = path.resolve(process.cwd(), 'index.ts');
|
|
307
|
+
|
|
308
|
+
await injectGlobalComponent(configFilePath, injectionType, `@@/${componentFileName}`);
|
|
309
|
+
|
|
310
|
+
console.log(
|
|
311
|
+
chalk.bold.greenBright('You can now open the component in your IDE:'),
|
|
312
|
+
chalk.underline.cyanBright(absoluteComponentPath)
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
process.exit(0);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.error(error);
|
|
318
|
+
console.error(chalk.red('\n❌ Component creation failed. Please check the errors above.'));
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
|
|
3
|
+
<button
|
|
4
|
+
type="button"
|
|
5
|
+
@click="handleClick"
|
|
6
|
+
class="text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-500 dark:hover:bg-blue-600 dark:focus:ring-blue-800"
|
|
7
|
+
>
|
|
8
|
+
Example
|
|
9
|
+
</button>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { onMounted } from 'vue';
|
|
15
|
+
import { useI18n } from 'vue-i18n';
|
|
16
|
+
import adminforth from '@/adminforth';
|
|
17
|
+
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
|
|
20
|
+
onMounted(() => {
|
|
21
|
+
// Logic on mount if needed
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function handleClick() {
|
|
25
|
+
adminforth.alert({
|
|
26
|
+
message: t('Confirmed'),
|
|
27
|
+
variant: 'success',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
|
|
3
|
+
<button
|
|
4
|
+
type="button"
|
|
5
|
+
@click="handleClick"
|
|
6
|
+
class="text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-500 dark:hover:bg-blue-600 dark:focus:ring-blue-800"
|
|
7
|
+
>
|
|
8
|
+
Example
|
|
9
|
+
</button>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { onMounted } from 'vue';
|
|
15
|
+
import { useI18n } from 'vue-i18n';
|
|
16
|
+
import adminforth from '@/adminforth';
|
|
17
|
+
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
|
|
20
|
+
onMounted(() => {
|
|
21
|
+
// Logic on mount if needed
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function handleClick() {
|
|
25
|
+
adminforth.alert({
|
|
26
|
+
message: t('Confirmed'),
|
|
27
|
+
variant: 'success',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="overflow-auto p-4">
|
|
3
|
+
<table class="min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
|
4
|
+
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
5
|
+
<tr>
|
|
6
|
+
<th scope="col" class="px-6 py-3" v-for="header in headers" :key="header">
|
|
7
|
+
{{ header }}
|
|
8
|
+
</th>
|
|
9
|
+
</tr>
|
|
10
|
+
</thead>
|
|
11
|
+
<tbody>
|
|
12
|
+
<tr
|
|
13
|
+
v-for="row in rows"
|
|
14
|
+
:key="row.id"
|
|
15
|
+
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600"
|
|
16
|
+
>
|
|
17
|
+
<td class="px-6 py-4" v-for="cell in row.cells" :key="cell">
|
|
18
|
+
{{ cell }}
|
|
19
|
+
</td>
|
|
20
|
+
</tr>
|
|
21
|
+
</tbody>
|
|
22
|
+
</table>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { onMounted, ref } from 'vue';
|
|
28
|
+
|
|
29
|
+
const headers = [
|
|
30
|
+
'ID',
|
|
31
|
+
'Name',
|
|
32
|
+
'Email',
|
|
33
|
+
'Phone',
|
|
34
|
+
'Address',
|
|
35
|
+
'City',
|
|
36
|
+
'Country',
|
|
37
|
+
'Company',
|
|
38
|
+
'Position',
|
|
39
|
+
'Status',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const rows = Array.from({ length: 50 }, (_, i) => ({
|
|
43
|
+
id: i + 1,
|
|
44
|
+
cells: [
|
|
45
|
+
i + 1,
|
|
46
|
+
`Name ${i + 1}`,
|
|
47
|
+
`user${i + 1}@example.com`,
|
|
48
|
+
`+1-555-010${i.toString().padStart(2, '0')}`,
|
|
49
|
+
`Address ${i + 1}`,
|
|
50
|
+
`City ${i + 1}`,
|
|
51
|
+
`Country ${i + 1}`,
|
|
52
|
+
`Company ${i + 1}`,
|
|
53
|
+
`Position ${i + 1}`,
|
|
54
|
+
i % 2 === 0 ? 'Active' : 'Inactive',
|
|
55
|
+
],
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
onMounted(() => {
|
|
59
|
+
console.log('Table mounted. Rows:', rows.length);
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col p-4 space-y-2">
|
|
3
|
+
<button
|
|
4
|
+
v-for="item in menuItems"
|
|
5
|
+
:key="item.label"
|
|
6
|
+
@click="item.action"
|
|
7
|
+
class="w-full text-left text-sm px-4 py-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white"
|
|
8
|
+
>
|
|
9
|
+
{{ t(item.label) }}
|
|
10
|
+
</button>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { useI18n } from 'vue-i18n';
|
|
16
|
+
import adminforth from '@/adminforth';
|
|
17
|
+
import { onMounted } from 'vue';
|
|
18
|
+
|
|
19
|
+
const { t } = useI18n();
|
|
20
|
+
|
|
21
|
+
const menuItems = [
|
|
22
|
+
{
|
|
23
|
+
label: 'Show Success Alert',
|
|
24
|
+
action: () => {
|
|
25
|
+
adminforth.alert({
|
|
26
|
+
message: t('Success Alert'),
|
|
27
|
+
variant: 'success',
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: 'Show Warning Alert',
|
|
33
|
+
action: () => {
|
|
34
|
+
adminforth.alert({
|
|
35
|
+
message: t('Warning Alert'),
|
|
36
|
+
variant: 'warning',
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: 'Show Error Alert',
|
|
42
|
+
action: () => {
|
|
43
|
+
adminforth.alert({
|
|
44
|
+
message: t('Error Alert'),
|
|
45
|
+
variant: 'danger',
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
onMounted(() => {
|
|
52
|
+
// Logic on mount if needed
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center justify-center text-gray-900 dark:text-gray-400 p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
|
|
3
|
+
{{reason}}
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
defineProps({
|
|
9
|
+
reason: String
|
|
10
|
+
});
|
|
11
|
+
</script>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseConnector.d.ts","sourceRoot":"","sources":["../../dataConnectors/baseConnector.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAAE,kCAAkC,EACtD,wBAAwB,EACxB,eAAe,EAAE,uBAAuB,EAAE,sBAAsB,EACjE,MAAM,kBAAkB,CAAC;AAK1B,OAAO,
|
|
1
|
+
{"version":3,"file":"baseConnector.d.ts","sourceRoot":"","sources":["../../dataConnectors/baseConnector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { suggestIfTypo } from "../modules/utils.js";
|
|
2
|
-
import { AdminForthFilterOperators } from "../types/Common.js";
|
|
2
|
+
import { AdminForthDataTypes, AdminForthFilterOperators } from "../types/Common.js";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
3
4
|
export default class AdminForthBaseConnector {
|
|
4
5
|
get db() {
|
|
5
6
|
console.warn('.db is deprecated, use .client instead');
|
|
@@ -45,6 +46,7 @@ export default class AdminForthBaseConnector {
|
|
|
45
46
|
return { operator: AdminForthFilterOperators.AND, subFilters: [filter] };
|
|
46
47
|
}
|
|
47
48
|
validateAndNormalizeFilters(filters, resource) {
|
|
49
|
+
var _a;
|
|
48
50
|
if (Array.isArray(filters)) {
|
|
49
51
|
// go through all filters in array and call validation+normalization for each
|
|
50
52
|
// as soon as error is encountered, there is no point in calling validation for other filters
|
|
@@ -99,8 +101,15 @@ export default class AdminForthBaseConnector {
|
|
|
99
101
|
return { ok: false, error: `Value for operator '${filters.operator}' should be an array, in filter object: ${JSON.stringify(filters)}` };
|
|
100
102
|
}
|
|
101
103
|
if (filters.value.length === 0) {
|
|
102
|
-
// nonsense
|
|
103
|
-
|
|
104
|
+
// nonsense, and some databases might not accept IN []
|
|
105
|
+
const colType = (_a = resource.dataSourceColumns.find((col) => col.name == filters.field)) === null || _a === void 0 ? void 0 : _a.type;
|
|
106
|
+
if (colType === AdminForthDataTypes.STRING || colType === AdminForthDataTypes.TEXT) {
|
|
107
|
+
filters.value = [randomUUID()];
|
|
108
|
+
return { ok: true, error: `` };
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
return { ok: false, error: `Value for operator '${filters.operator}' should not be empty array, in filter object: ${JSON.stringify(filters)}` };
|
|
112
|
+
}
|
|
104
113
|
}
|
|
105
114
|
filters.value = filters.value.map((val) => this.setFieldValue(fieldObj, val));
|
|
106
115
|
}
|