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.
Files changed (50) hide show
  1. package/commands/createApp/templates/package.json.hbs +1 -1
  2. package/dist/modules/restApi.d.ts +1 -0
  3. package/dist/modules/restApi.d.ts.map +1 -1
  4. package/dist/modules/restApi.js +25 -1
  5. package/dist/modules/restApi.js.map +1 -1
  6. package/dist/modules/styles.js +2 -2
  7. package/dist/modules/styles.js.map +1 -1
  8. package/dist/servers/express.d.ts.map +1 -1
  9. package/dist/servers/express.js +7 -1
  10. package/dist/servers/express.js.map +1 -1
  11. package/dist/spa/package-lock.json +44 -7
  12. package/dist/spa/package.json +1 -1
  13. package/dist/spa/pnpm-lock.yaml +301 -299
  14. package/dist/spa/src/App.vue +1 -1
  15. package/dist/spa/src/adminforth.ts +17 -29
  16. package/dist/spa/src/afcl/Input.vue +1 -1
  17. package/dist/spa/src/afcl/Modal.vue +12 -1
  18. package/dist/spa/src/afcl/Select.vue +4 -2
  19. package/dist/spa/src/afcl/Table.vue +27 -13
  20. package/dist/spa/src/components/AcceptModal.vue +2 -0
  21. package/dist/spa/src/components/ColumnValueInputWrapper.vue +11 -3
  22. package/dist/spa/src/components/CustomRangePicker.vue +16 -67
  23. package/dist/spa/src/components/ListActionsThreeDots.vue +9 -8
  24. package/dist/spa/src/components/RangePicker.vue +236 -0
  25. package/dist/spa/src/components/ResourceListTable.vue +45 -70
  26. package/dist/spa/src/components/Sidebar.vue +1 -1
  27. package/dist/spa/src/components/ThreeDotsMenu.vue +30 -52
  28. package/dist/spa/src/i18n.ts +1 -1
  29. package/dist/spa/src/stores/core.ts +4 -2
  30. package/dist/spa/src/types/Back.ts +11 -4
  31. package/dist/spa/src/types/Common.ts +26 -5
  32. package/dist/spa/src/types/FrontendAPI.ts +6 -1
  33. package/dist/spa/src/utils/listUtils.ts +8 -2
  34. package/dist/spa/src/utils/utils.ts +187 -10
  35. package/dist/spa/src/views/CreateView.vue +10 -10
  36. package/dist/spa/src/views/EditView.vue +10 -9
  37. package/dist/spa/src/views/ListView.vue +122 -18
  38. package/dist/spa/src/views/LoginView.vue +13 -13
  39. package/dist/spa/src/views/ShowView.vue +53 -60
  40. package/dist/spa/tsconfig.app.json +1 -1
  41. package/dist/types/Back.d.ts +8 -5
  42. package/dist/types/Back.d.ts.map +1 -1
  43. package/dist/types/Back.js.map +1 -1
  44. package/dist/types/Common.d.ts +21 -5
  45. package/dist/types/Common.d.ts.map +1 -1
  46. package/dist/types/Common.js.map +1 -1
  47. package/dist/types/FrontendAPI.d.ts +13 -1
  48. package/dist/types/FrontendAPI.d.ts.map +1 -1
  49. package/dist/types/FrontendAPI.js.map +1 -1
  50. 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?: string;
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?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
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?: object; }>;
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({ path, method, body=undefined, headers=undefined, silentError = false }: {
79
- path: string,
80
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
81
- body?: any,
82
- headers?: Record<string, string>,
83
- silentError?: boolean
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, onBeforeRouteLeaveCreateEditView } from '@/utils';
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 { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
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();