adminforth 2.26.2 → 2.27.0-next.2
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/createApp/templates/package.json.hbs +1 -1
- package/dist/modules/restApi.d.ts +1 -0
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +25 -1
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.js +2 -2
- package/dist/modules/styles.js.map +1 -1
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +7 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/package-lock.json +44 -7
- package/dist/spa/package.json +1 -1
- package/dist/spa/pnpm-lock.yaml +301 -299
- package/dist/spa/src/App.vue +1 -1
- package/dist/spa/src/adminforth.ts +17 -29
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/Modal.vue +12 -1
- package/dist/spa/src/afcl/Select.vue +4 -2
- package/dist/spa/src/afcl/Table.vue +27 -13
- package/dist/spa/src/components/AcceptModal.vue +2 -0
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +11 -3
- package/dist/spa/src/components/CustomRangePicker.vue +16 -67
- package/dist/spa/src/components/ListActionsThreeDots.vue +9 -8
- package/dist/spa/src/components/RangePicker.vue +236 -0
- package/dist/spa/src/components/ResourceListTable.vue +45 -70
- package/dist/spa/src/components/Sidebar.vue +1 -1
- package/dist/spa/src/components/ThreeDotsMenu.vue +30 -52
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/stores/core.ts +4 -2
- package/dist/spa/src/types/Back.ts +11 -4
- package/dist/spa/src/types/Common.ts +26 -5
- package/dist/spa/src/types/FrontendAPI.ts +6 -1
- package/dist/spa/src/utils/listUtils.ts +8 -2
- package/dist/spa/src/utils/utils.ts +187 -10
- package/dist/spa/src/views/CreateView.vue +10 -10
- package/dist/spa/src/views/EditView.vue +10 -9
- package/dist/spa/src/views/ListView.vue +122 -18
- package/dist/spa/src/views/LoginView.vue +13 -13
- package/dist/spa/src/views/ShowView.vue +53 -60
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/types/Back.d.ts +8 -5
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +21 -5
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +13 -1
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Express, Request } from 'express';
|
|
1
|
+
import type { Express, Request, Response } from 'express';
|
|
2
2
|
import type { Writable } from 'stream';
|
|
3
3
|
|
|
4
4
|
import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections, AllowedActionsEnum, AdminForthResourcePages,
|
|
@@ -67,6 +67,10 @@ export interface IHttpServer {
|
|
|
67
67
|
headers: {[key: string]: string},
|
|
68
68
|
cookies: {[key: string]: string},
|
|
69
69
|
response: IAdminForthHttpResponse,
|
|
70
|
+
requestUrl: string,
|
|
71
|
+
abortSignal: AbortSignal,
|
|
72
|
+
_raw_express_req: Request,
|
|
73
|
+
_raw_express_res: Response,
|
|
70
74
|
) => void,
|
|
71
75
|
}): void;
|
|
72
76
|
|
|
@@ -1287,11 +1291,14 @@ interface AdminForthInputConfigCustomization {
|
|
|
1287
1291
|
|
|
1288
1292
|
export interface AdminForthActionInput {
|
|
1289
1293
|
name: string;
|
|
1294
|
+
bulkConfirmationMessage?: string;
|
|
1295
|
+
bulkSuccessMessage?: string;
|
|
1290
1296
|
showIn?: {
|
|
1291
1297
|
list?: boolean,
|
|
1292
1298
|
listThreeDotsMenu?: boolean,
|
|
1293
1299
|
showButton?: boolean,
|
|
1294
1300
|
showThreeDotsMenu?: boolean,
|
|
1301
|
+
bulkButton?: boolean,
|
|
1295
1302
|
};
|
|
1296
1303
|
allowed?: (params: {
|
|
1297
1304
|
adminUser: AdminUser;
|
|
@@ -1312,7 +1319,7 @@ export interface AdminForthActionInput {
|
|
|
1312
1319
|
message?: string;
|
|
1313
1320
|
}>;
|
|
1314
1321
|
icon?: string;
|
|
1315
|
-
id
|
|
1322
|
+
id: string;
|
|
1316
1323
|
customComponent?: AdminForthComponentDeclaration;
|
|
1317
1324
|
}
|
|
1318
1325
|
|
|
@@ -1637,7 +1644,7 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
|
|
|
1637
1644
|
|
|
1638
1645
|
loginPageInjections: {
|
|
1639
1646
|
underInputs: Array<AdminForthComponentDeclarationFull>,
|
|
1640
|
-
underLoginButton
|
|
1647
|
+
underLoginButton: Array<AdminForthComponentDeclarationFull>,
|
|
1641
1648
|
panelHeader: Array<AdminForthComponentDeclarationFull>,
|
|
1642
1649
|
},
|
|
1643
1650
|
|
|
@@ -1825,7 +1832,7 @@ export type AllowedActions = {
|
|
|
1825
1832
|
/**
|
|
1826
1833
|
* General options for resource.
|
|
1827
1834
|
*/
|
|
1828
|
-
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions'> {
|
|
1835
|
+
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions' | 'actions'> {
|
|
1829
1836
|
|
|
1830
1837
|
/**
|
|
1831
1838
|
* Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
|
|
@@ -314,6 +314,25 @@ export type FieldGroup = {
|
|
|
314
314
|
noTitle?: boolean;
|
|
315
315
|
};
|
|
316
316
|
|
|
317
|
+
export interface AdminForthActionFront extends Omit<AdminForthActionInput, 'id'> {
|
|
318
|
+
id: string;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export interface AdminForthBulkActionFront extends Omit<AdminForthBulkActionCommon, 'id'> {
|
|
322
|
+
id: string,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
type AdminforthOptionsCommon = NonNullable<AdminForthResourceCommon['options']>;
|
|
326
|
+
|
|
327
|
+
export interface AdminForthOptionsForFrontend extends Omit<AdminforthOptionsCommon, 'actions' | 'bulkActions'> {
|
|
328
|
+
actions?: AdminForthActionFront[],
|
|
329
|
+
bulkActions?: AdminForthBulkActionFront[],
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export interface AdminForthResourceFrontend extends Omit<AdminForthResourceCommon, 'options'> {
|
|
333
|
+
options: AdminForthOptionsForFrontend;
|
|
334
|
+
}
|
|
335
|
+
|
|
317
336
|
/**
|
|
318
337
|
* Resource describes one table or collection in database.
|
|
319
338
|
* AdminForth generates set of pages for 'list', 'show', 'edit', 'create', 'filter' operations for each resource.
|
|
@@ -361,17 +380,17 @@ export interface AdminForthResourceInputCommon {
|
|
|
361
380
|
recordLabel?: (item: any) => string,
|
|
362
381
|
|
|
363
382
|
|
|
364
|
-
/**
|
|
365
|
-
* If true, user will not see warning about unsaved changes when tries to leave edit or create page with unsaved changes.
|
|
366
|
-
* default is false
|
|
367
|
-
*/
|
|
368
|
-
dontShowWarningAboutUnsavedChanges?: boolean,
|
|
369
383
|
|
|
370
384
|
/**
|
|
371
385
|
* General options for resource.
|
|
372
386
|
*/
|
|
373
387
|
options?: {
|
|
374
388
|
|
|
389
|
+
/**
|
|
390
|
+
* If true, user will not see warning about unsaved changes when tries to leave edit or create page with unsaved changes.
|
|
391
|
+
* default is false
|
|
392
|
+
*/
|
|
393
|
+
dontShowWarningAboutUnsavedChanges?: boolean,
|
|
375
394
|
|
|
376
395
|
/**
|
|
377
396
|
* Show quick action icons for base actions (show, edit, delete) in list view.
|
|
@@ -417,6 +436,7 @@ export interface AdminForthResourceInputCommon {
|
|
|
417
436
|
/**
|
|
418
437
|
* Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
|
|
419
438
|
* using checkboxes.
|
|
439
|
+
* @deprecated in favor of defining .
|
|
420
440
|
*/
|
|
421
441
|
bulkActions?: AdminForthBulkActionCommon[],
|
|
422
442
|
|
|
@@ -1172,6 +1192,7 @@ export interface AdminForthConfigForFrontend {
|
|
|
1172
1192
|
loginPageInjections: {
|
|
1173
1193
|
underInputs: Array<AdminForthComponentDeclaration>,
|
|
1174
1194
|
panelHeader: Array<AdminForthComponentDeclaration>,
|
|
1195
|
+
underLoginButton: Array<AdminForthComponentDeclaration>,
|
|
1175
1196
|
},
|
|
1176
1197
|
rememberMeDuration: string,
|
|
1177
1198
|
showBrandNameInSidebar: boolean,
|
|
@@ -144,7 +144,7 @@ export interface FrontendAPIInterface {
|
|
|
144
144
|
/**
|
|
145
145
|
* Run save interceptors for a specific resource or all resources if no resourceId is provided
|
|
146
146
|
*/
|
|
147
|
-
runSaveInterceptors(params: { action: 'create'|'edit'; values: any; resource: any; resourceId: string; }): Promise<{ ok: boolean; error?: string | null; extra?:
|
|
147
|
+
runSaveInterceptors(params: { action: 'create'|'edit'; values: any; resource: any; resourceId: string; }): Promise<{ ok: boolean; error?: string | null; extra?: any; }>;
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
150
|
* Clear save interceptors for a specific resource or all resources if no resourceId is provided
|
|
@@ -152,6 +152,11 @@ export interface FrontendAPIInterface {
|
|
|
152
152
|
* @param resourceId - The resource ID to clear interceptors for
|
|
153
153
|
*/
|
|
154
154
|
clearSaveInterceptors(resourceId?: string): void;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Register a save interceptor for a specific resource
|
|
158
|
+
*/
|
|
159
|
+
registerSaveInterceptor(handler: (ctx: { action: 'create'|'edit'; values: any; resource: any; }) => Promise<{ ok: boolean; error?: string | null; extra?: any; }>): void;
|
|
155
160
|
}
|
|
156
161
|
|
|
157
162
|
export type ConfirmParams = {
|
|
@@ -4,13 +4,18 @@ import { type AdminForthResourceCommon } from '../types/Common';
|
|
|
4
4
|
import { useAdminforth } from '@/adminforth';
|
|
5
5
|
import { showErrorTost } from '@/composables/useFrontendApi'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
let getResourceDataLastAbortController: AbortController | null = null;
|
|
8
8
|
export async function getList(resource: AdminForthResourceCommon, isPageLoaded: boolean, page: number | null , pageSize: number, sort: any, checkboxes:{ value: any[] }, filters: any = [] ) {
|
|
9
9
|
let rows: any[] = [];
|
|
10
10
|
let totalRows: number | null = null;
|
|
11
11
|
if (!isPageLoaded) {
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
+
const abortController = new AbortController();
|
|
15
|
+
if (getResourceDataLastAbortController) {
|
|
16
|
+
getResourceDataLastAbortController.abort();
|
|
17
|
+
}
|
|
18
|
+
getResourceDataLastAbortController = abortController;
|
|
14
19
|
const data = await callAdminForthApi({
|
|
15
20
|
path: '/get_resource_data',
|
|
16
21
|
method: 'POST',
|
|
@@ -21,7 +26,8 @@ export async function getList(resource: AdminForthResourceCommon, isPageLoaded:
|
|
|
21
26
|
offset: ((page || 1) - 1) * pageSize,
|
|
22
27
|
filters: filters,
|
|
23
28
|
sort: sort,
|
|
24
|
-
}
|
|
29
|
+
},
|
|
30
|
+
abortSignal: abortController.signal
|
|
25
31
|
});
|
|
26
32
|
if (data.error) {
|
|
27
33
|
showErrorTost(data.error);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { nextTick, onMounted, ref, resolveComponent } from 'vue';
|
|
2
2
|
import type { CoreConfig } from '../spa_types/core';
|
|
3
|
-
import type { ValidationObject } from '../types/Common.js';
|
|
3
|
+
import type { AdminForthComponentDeclaration, AdminForthComponentDeclarationFull, ValidationObject } from '../types/Common.js';
|
|
4
4
|
import router from "../router";
|
|
5
5
|
import { useCoreStore } from '../stores/core';
|
|
6
6
|
import { useUserStore } from '../stores/user';
|
|
@@ -19,11 +19,12 @@ const LS_LANG_KEY = `afLanguage`;
|
|
|
19
19
|
const MAX_CONSECUTIVE_EMPTY_RESULTS = 2;
|
|
20
20
|
const ITEMS_PER_PAGE_LIMIT = 100;
|
|
21
21
|
|
|
22
|
-
export async function callApi({path, method, body, headers, silentError = false}: {
|
|
22
|
+
export async function callApi({path, method, body, headers, silentError = false, abortSignal}: {
|
|
23
23
|
path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
|
24
24
|
body?: any
|
|
25
25
|
headers?: Record<string, string>
|
|
26
26
|
silentError?: boolean
|
|
27
|
+
abortSignal?: AbortSignal
|
|
27
28
|
}): Promise<any> {
|
|
28
29
|
const t = i18nInstance?.global.t || ((s: string) => s)
|
|
29
30
|
const options = {
|
|
@@ -34,6 +35,7 @@ export async function callApi({path, method, body, headers, silentError = false}
|
|
|
34
35
|
...headers
|
|
35
36
|
},
|
|
36
37
|
body: JSON.stringify(body),
|
|
38
|
+
signal: abortSignal
|
|
37
39
|
};
|
|
38
40
|
const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
|
|
39
41
|
try {
|
|
@@ -68,28 +70,45 @@ export async function callApi({path, method, body, headers, silentError = false}
|
|
|
68
70
|
return null;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
if (!silentError) {
|
|
73
|
+
if (!silentError && !(e instanceof DOMException && e.name === 'AbortError')) {
|
|
72
74
|
adminforth.alert({variant:'danger', message: t('Something went wrong, please try again later'),})
|
|
73
75
|
}
|
|
74
76
|
console.error(`error in callApi ${path}`, e);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
export async function callAdminForthApi(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
export async function callAdminForthApi(
|
|
81
|
+
{
|
|
82
|
+
path,
|
|
83
|
+
method,
|
|
84
|
+
body=undefined,
|
|
85
|
+
headers=undefined,
|
|
86
|
+
silentError = false,
|
|
87
|
+
abortSignal = undefined
|
|
88
|
+
}: {
|
|
89
|
+
path: string,
|
|
90
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
|
|
91
|
+
body?: any,
|
|
92
|
+
headers?: Record<string, string>,
|
|
93
|
+
silentError?: boolean,
|
|
94
|
+
abortSignal?: AbortSignal
|
|
84
95
|
}): Promise<any> {
|
|
85
96
|
try {
|
|
86
|
-
return callApi({path: `/adminapi/v1${path}`, method, body, headers, silentError} );
|
|
97
|
+
return callApi({path: `/adminapi/v1${path}`, method, body, headers, silentError, abortSignal} );
|
|
87
98
|
} catch (e) {
|
|
88
99
|
console.error('error', e);
|
|
89
100
|
return { error: `Unexpected error: ${e}` };
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
export function formatComponent(component: AdminForthComponentDeclaration): AdminForthComponentDeclarationFull {
|
|
105
|
+
if (typeof component === 'string') {
|
|
106
|
+
return { file: component, meta: {} };
|
|
107
|
+
} else {
|
|
108
|
+
return { file: component.file, meta: component.meta };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
93
112
|
export function getCustomComponent({ file, meta }: { file: string, meta?: any }) {
|
|
94
113
|
const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
|
|
95
114
|
return resolveComponent(name);
|
|
@@ -671,4 +690,162 @@ export async function onBeforeRouteLeaveCreateEditViewGuard(initialValues: any,
|
|
|
671
690
|
leaveGuardActive.setActive(false);
|
|
672
691
|
}
|
|
673
692
|
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export async function executeCustomAction({
|
|
696
|
+
actionId,
|
|
697
|
+
resourceId,
|
|
698
|
+
recordId,
|
|
699
|
+
extra = {},
|
|
700
|
+
onSuccess,
|
|
701
|
+
onError,
|
|
702
|
+
setLoadingState,
|
|
703
|
+
}: {
|
|
704
|
+
actionId: string | number,
|
|
705
|
+
resourceId: string,
|
|
706
|
+
recordId: string | number,
|
|
707
|
+
extra?: Record<string, any>,
|
|
708
|
+
onSuccess?: (data: any) => Promise<void>,
|
|
709
|
+
onError?: (error: string) => void,
|
|
710
|
+
setLoadingState?: (loading: boolean) => void,
|
|
711
|
+
}): Promise<any> {
|
|
712
|
+
setLoadingState?.(true);
|
|
713
|
+
|
|
714
|
+
try {
|
|
715
|
+
const data = await callAdminForthApi({
|
|
716
|
+
path: '/start_custom_action',
|
|
717
|
+
method: 'POST',
|
|
718
|
+
body: {
|
|
719
|
+
resourceId,
|
|
720
|
+
actionId,
|
|
721
|
+
recordId,
|
|
722
|
+
extra: extra || {},
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
if (data?.redirectUrl) {
|
|
727
|
+
// Check if the URL should open in a new tab
|
|
728
|
+
if (data.redirectUrl.includes('target=_blank')) {
|
|
729
|
+
window.open(data.redirectUrl.replace('&target=_blank', '').replace('?target=_blank', ''), '_blank');
|
|
730
|
+
} else {
|
|
731
|
+
// Navigate within the app
|
|
732
|
+
if (data.redirectUrl.startsWith('http')) {
|
|
733
|
+
window.location.href = data.redirectUrl;
|
|
734
|
+
} else {
|
|
735
|
+
router.push(data.redirectUrl);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return data;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (data?.ok) {
|
|
742
|
+
if (onSuccess) {
|
|
743
|
+
await onSuccess(data);
|
|
744
|
+
}
|
|
745
|
+
return data;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (data?.error) {
|
|
749
|
+
if (onError) {
|
|
750
|
+
onError(data.error);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return data;
|
|
755
|
+
} finally {
|
|
756
|
+
setLoadingState?.(false);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export async function executeCustomBulkAction({
|
|
761
|
+
actionId,
|
|
762
|
+
resourceId,
|
|
763
|
+
recordIds,
|
|
764
|
+
extra = {},
|
|
765
|
+
onSuccess,
|
|
766
|
+
onError,
|
|
767
|
+
setLoadingState,
|
|
768
|
+
confirmMessage,
|
|
769
|
+
}: {
|
|
770
|
+
actionId: string | number,
|
|
771
|
+
resourceId: string,
|
|
772
|
+
recordIds: (string | number)[],
|
|
773
|
+
extra?: Record<string, any>,
|
|
774
|
+
onSuccess?: (results: any[]) => Promise<void>,
|
|
775
|
+
onError?: (error: string) => void,
|
|
776
|
+
setLoadingState?: (loading: boolean) => void,
|
|
777
|
+
confirmMessage?: string,
|
|
778
|
+
}): Promise<any> {
|
|
779
|
+
if (!recordIds || recordIds.length === 0) {
|
|
780
|
+
if (onError) {
|
|
781
|
+
onError('No records selected');
|
|
782
|
+
}
|
|
783
|
+
return { error: 'No records selected' };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (confirmMessage) {
|
|
787
|
+
const { confirm } = useAdminforth();
|
|
788
|
+
const confirmed = await confirm({
|
|
789
|
+
message: confirmMessage,
|
|
790
|
+
});
|
|
791
|
+
if (!confirmed) {
|
|
792
|
+
return { cancelled: true };
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
setLoadingState?.(true);
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
// Execute action for all records in parallel using Promise.all
|
|
800
|
+
const results = await Promise.all(
|
|
801
|
+
recordIds.map(recordId =>
|
|
802
|
+
callAdminForthApi({
|
|
803
|
+
path: '/start_custom_action',
|
|
804
|
+
method: 'POST',
|
|
805
|
+
body: {
|
|
806
|
+
resourceId,
|
|
807
|
+
actionId,
|
|
808
|
+
recordId,
|
|
809
|
+
extra: extra || {},
|
|
810
|
+
}
|
|
811
|
+
})
|
|
812
|
+
)
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
const lastResult = results[results.length - 1];
|
|
816
|
+
if (lastResult?.redirectUrl) {
|
|
817
|
+
if (lastResult.redirectUrl.includes('target=_blank')) {
|
|
818
|
+
window.open(lastResult.redirectUrl.replace('&target=_blank', '').replace('?target=_blank', ''), '_blank');
|
|
819
|
+
} else {
|
|
820
|
+
if (lastResult.redirectUrl.startsWith('http')) {
|
|
821
|
+
window.location.href = lastResult.redirectUrl;
|
|
822
|
+
} else {
|
|
823
|
+
router.push(lastResult.redirectUrl);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return lastResult;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const allSucceeded = results.every(r => r?.ok);
|
|
830
|
+
const hasErrors = results.some(r => r?.error);
|
|
831
|
+
|
|
832
|
+
if (allSucceeded) {
|
|
833
|
+
if (onSuccess) {
|
|
834
|
+
await onSuccess(results);
|
|
835
|
+
}
|
|
836
|
+
return { ok: true, results };
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (hasErrors) {
|
|
840
|
+
const errorMessages = results.filter(r => r?.error).map(r => r.error).join(', ');
|
|
841
|
+
if (onError) {
|
|
842
|
+
onError(errorMessages);
|
|
843
|
+
}
|
|
844
|
+
return { error: errorMessages, results };
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
return { results };
|
|
848
|
+
} finally {
|
|
849
|
+
setLoadingState?.(false);
|
|
850
|
+
}
|
|
674
851
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<div class="relative w-full">
|
|
3
3
|
|
|
4
4
|
<component
|
|
5
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.beforeBreadcrumbs || []"
|
|
6
|
-
:is="getCustomComponent(c)"
|
|
5
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.beforeBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
6
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
7
7
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
8
8
|
:record="coreStore.record"
|
|
9
9
|
:resource="coreStore.resource"
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
<BreadcrumbsWithButtons>
|
|
14
14
|
<!-- save and cancle -->
|
|
15
15
|
<button @click="() => {cancelButtonClicked = true; $router.back()}"
|
|
16
|
-
class="af-cancel-button flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-lightCreateViewButtonText focus:outline-none bg-lightCreateViewButtonBackground rounded border border-lightCreateViewButtonBorder hover:bg-lightCreateViewButtonBackgroundHover hover:text-lightCreateViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightCreateViewButtonFocusRing dark:focus:ring-darkCreateViewButtonFocusRing dark:bg-darkCreateViewButtonBackground dark:text-darkCreateViewButtonText dark:border-darkCreateViewButtonBorder dark:hover:text-darkCreateViewButtonTextHover dark:hover:bg-darkCreateViewButtonBackgroundHover"
|
|
16
|
+
class="af-cancel-button h-[34px] af-button-shadow flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-lightCreateViewButtonText focus:outline-none bg-lightCreateViewButtonBackground rounded border border-lightCreateViewButtonBorder hover:bg-lightCreateViewButtonBackgroundHover hover:text-lightCreateViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightCreateViewButtonFocusRing dark:focus:ring-darkCreateViewButtonFocusRing dark:bg-darkCreateViewButtonBackground dark:text-darkCreateViewButtonText dark:border-darkCreateViewButtonBorder dark:hover:text-darkCreateViewButtonTextHover dark:hover:bg-darkCreateViewButtonBackgroundHover"
|
|
17
17
|
>
|
|
18
18
|
{{ $t('Cancel') }}
|
|
19
19
|
</button>
|
|
20
20
|
<button
|
|
21
21
|
@click="() => saveRecord()"
|
|
22
|
-
class="af-save-button flex items-center py-1 px-3 text-sm font-medium rounded-default text-lightCreateViewSaveButtonText focus:outline-none bg-lightCreateViewButtonBackground rounded border border-lightCreateViewButtonBorder hover:bg-lightCreateViewButtonBackgroundHover hover:text-lightCreateViewSaveButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightCreateViewButtonFocusRing dark:focus:ring-darkCreateViewButtonFocusRing dark:bg-darkCreateViewButtonBackground dark:text-darkCreateViewSaveButtonText dark:border-darkCreateViewButtonBorder dark:hover:text-darkCreateViewSaveButtonTextHover dark:hover:bg-darkCreateViewButtonBackgroundHover disabled:opacity-50 gap-1"
|
|
22
|
+
class="af-save-button h-[34px] af-button-shadow flex items-center py-1 px-3 text-sm font-medium rounded-default text-lightCreateViewSaveButtonText focus:outline-none bg-lightCreateViewButtonBackground rounded border border-lightCreateViewButtonBorder hover:bg-lightCreateViewButtonBackgroundHover hover:text-lightCreateViewSaveButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightCreateViewButtonFocusRing dark:focus:ring-darkCreateViewButtonFocusRing dark:bg-darkCreateViewButtonBackground dark:text-darkCreateViewSaveButtonText dark:border-darkCreateViewButtonBorder dark:hover:text-darkCreateViewSaveButtonTextHover dark:hover:bg-darkCreateViewButtonBackgroundHover disabled:opacity-50 gap-1"
|
|
23
23
|
:disabled="saving || (validating && !isValid)"
|
|
24
24
|
>
|
|
25
25
|
<svg v-if="saving"
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
</BreadcrumbsWithButtons>
|
|
37
37
|
|
|
38
38
|
<component
|
|
39
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.afterBreadcrumbs || []"
|
|
40
|
-
:is="getCustomComponent(c)"
|
|
39
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.afterBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
40
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
41
41
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
42
42
|
:record="coreStore.record"
|
|
43
43
|
:resource="coreStore.resource"
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
</ResourceForm>
|
|
62
62
|
|
|
63
63
|
<component
|
|
64
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.bottom || []"
|
|
65
|
-
:is="getCustomComponent(c)"
|
|
64
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.bottom as AdminForthComponentDeclaration[] || []"
|
|
65
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
66
66
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
67
67
|
:record="coreStore.record"
|
|
68
68
|
:resource="coreStore.resource"
|
|
@@ -79,7 +79,7 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
|
79
79
|
import ResourceForm from '@/components/ResourceForm.vue';
|
|
80
80
|
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
81
81
|
import { useCoreStore } from '@/stores/core';
|
|
82
|
-
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf, compareOldAndNewRecord, onBeforeRouteLeaveCreateEditViewGuard, leaveGuardActiveClass,
|
|
82
|
+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf, compareOldAndNewRecord, onBeforeRouteLeaveCreateEditViewGuard, leaveGuardActiveClass, formatComponent } from '@/utils';
|
|
83
83
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
84
84
|
import { onMounted, onBeforeMount, onBeforeUnmount, ref, watch, nextTick } from 'vue';
|
|
85
85
|
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
|
|
@@ -88,7 +88,7 @@ import { showErrorTost } from '@/composables/useFrontendApi';
|
|
|
88
88
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
89
89
|
import { useAdminforth } from '@/adminforth';
|
|
90
90
|
import { useI18n } from 'vue-i18n';
|
|
91
|
-
import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
|
|
91
|
+
import { type AdminForthComponentDeclaration, type AdminForthComponentDeclarationFull } from '@/types/Common.js';
|
|
92
92
|
import type { AdminForthResourceColumn } from '@/types/Back';
|
|
93
93
|
|
|
94
94
|
const isValid = ref(false);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative w-full">
|
|
3
3
|
<component
|
|
4
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.beforeBreadcrumbs || []"
|
|
5
|
-
:is="getCustomComponent(c)"
|
|
4
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.beforeBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
5
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
6
6
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
7
7
|
:record="editableRecord"
|
|
8
8
|
:resource="coreStore.resource"
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
<BreadcrumbsWithButtons>
|
|
13
13
|
<!-- save and cancle -->
|
|
14
14
|
<button @click="() => {cancelButtonClicked = true; $router.back()}"
|
|
15
|
-
class="flex items-center py-1 px-3 me-2 text-sm font-medium text-lightEditViewButtonText rounded-default focus:outline-none bg-lightEditViewButtonBackground rounded border border-lightEditViewButtonBorder hover:bg-lightEditViewButtonBackgroundHover hover:text-lightEditViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightEditViewButtonFocusRing dark:focus:ring-darkEditViewButtonFocusRing dark:bg-darkEditViewButtonBackground dark:text-darkEditViewButtonText dark:border-darkEditViewButtonBorder dark:hover:text-darkEditViewButtonTextHover dark:hover:bg-darkEditViewButtonBackgroundHover"
|
|
15
|
+
class="flex items-center h-[34px] af-button-shadow py-1 px-3 me-2 text-sm font-medium text-lightEditViewButtonText rounded-default focus:outline-none bg-lightEditViewButtonBackground rounded border border-lightEditViewButtonBorder hover:bg-lightEditViewButtonBackgroundHover hover:text-lightEditViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightEditViewButtonFocusRing dark:focus:ring-darkEditViewButtonFocusRing dark:bg-darkEditViewButtonBackground dark:text-darkEditViewButtonText dark:border-darkEditViewButtonBorder dark:hover:text-darkEditViewButtonTextHover dark:hover:bg-darkEditViewButtonBackgroundHover"
|
|
16
16
|
>
|
|
17
17
|
{{ $t('Cancel') }}
|
|
18
18
|
</button>
|
|
19
19
|
<button
|
|
20
20
|
@click="() => saveRecord()"
|
|
21
|
-
class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-lightEditViewSaveButtonText focus:outline-none bg-lightEditViewButtonBackground rounded border border-lightEditViewButtonBorder hover:bg-lightEditViewButtonBackgroundHover hover:text-lightEditViewSaveButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightEditViewButtonFocusRing dark:focus:ring-darkEditViewButtonFocusRing dark:bg-darkEditViewButtonBackground dark:text-darkEditViewSaveButtonText dark:border-darkEditViewButtonBorder dark:hover:text-darkEditViewSaveButtonTextHover dark:hover:bg-darkEditViewButtonBackgroundHover disabled:opacity-50 gap-1"
|
|
21
|
+
class="flex items-center h-[34px] af-button-shadow py-1 px-3 text-sm font-medium rounded-default text-lightEditViewSaveButtonText focus:outline-none bg-lightEditViewButtonBackground rounded border border-lightEditViewButtonBorder hover:bg-lightEditViewButtonBackgroundHover hover:text-lightEditViewSaveButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightEditViewButtonFocusRing dark:focus:ring-darkEditViewButtonFocusRing dark:bg-darkEditViewButtonBackground dark:text-darkEditViewSaveButtonText dark:border-darkEditViewButtonBorder dark:hover:text-darkEditViewSaveButtonTextHover dark:hover:bg-darkEditViewButtonBackgroundHover disabled:opacity-50 gap-1"
|
|
22
22
|
:disabled="saving || (validating && !isValid)"
|
|
23
23
|
>
|
|
24
24
|
<IconFloppyDiskSolid class="w-4 h-4" />
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
</BreadcrumbsWithButtons>
|
|
33
33
|
|
|
34
34
|
<component
|
|
35
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.afterBreadcrumbs || []"
|
|
36
|
-
:is="getCustomComponent(c)"
|
|
35
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.afterBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
36
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
37
37
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
38
38
|
:record="coreStore.record"
|
|
39
39
|
:resource="coreStore.resource"
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
</ResourceForm>
|
|
57
57
|
|
|
58
58
|
<component
|
|
59
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.bottom || []"
|
|
60
|
-
:is="getCustomComponent(c)"
|
|
59
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.bottom as AdminForthComponentDeclaration[] || []"
|
|
60
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
61
61
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
62
62
|
:record="coreStore.record"
|
|
63
63
|
:resource="coreStore.resource"
|
|
@@ -82,7 +82,8 @@ import { showErrorTost } from '@/composables/useFrontendApi';
|
|
|
82
82
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
83
83
|
import { useAdminforth } from '@/adminforth';
|
|
84
84
|
import { useI18n } from 'vue-i18n';
|
|
85
|
-
import {
|
|
85
|
+
import { formatComponent } from '@/utils';
|
|
86
|
+
import { type AdminForthComponentDeclaration, type AdminForthComponentDeclarationFull } from '@/types/Common.js';
|
|
86
87
|
import type { AdminForthResourceColumn } from '@/types/Back';
|
|
87
88
|
|
|
88
89
|
const { t } = useI18n();
|