adminforth 2.7.18 → 2.8.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.
Files changed (107) hide show
  1. package/commands/createApp/templates/index.ts.hbs +2 -1
  2. package/commands/createCustomComponent/main.js +1 -0
  3. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  4. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  5. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  6. package/dist/dataConnectors/baseConnector.js +33 -15
  7. package/dist/dataConnectors/baseConnector.js.map +1 -1
  8. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  9. package/dist/dataConnectors/clickhouse.js +15 -0
  10. package/dist/dataConnectors/clickhouse.js.map +1 -1
  11. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  12. package/dist/dataConnectors/mongo.js +30 -1
  13. package/dist/dataConnectors/mongo.js.map +1 -1
  14. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  15. package/dist/dataConnectors/mysql.js +11 -0
  16. package/dist/dataConnectors/mysql.js.map +1 -1
  17. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  18. package/dist/dataConnectors/postgres.js +11 -0
  19. package/dist/dataConnectors/postgres.js.map +1 -1
  20. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  21. package/dist/dataConnectors/sqlite.js +11 -0
  22. package/dist/dataConnectors/sqlite.js.map +1 -1
  23. package/dist/modules/codeInjector.d.ts +1 -0
  24. package/dist/modules/codeInjector.d.ts.map +1 -1
  25. package/dist/modules/codeInjector.js +4 -0
  26. package/dist/modules/codeInjector.js.map +1 -1
  27. package/dist/modules/configValidator.d.ts.map +1 -1
  28. package/dist/modules/configValidator.js +6 -2
  29. package/dist/modules/configValidator.js.map +1 -1
  30. package/dist/modules/restApi.d.ts.map +1 -1
  31. package/dist/modules/restApi.js +2 -0
  32. package/dist/modules/restApi.js.map +1 -1
  33. package/dist/modules/styles.d.ts +8 -1
  34. package/dist/modules/styles.d.ts.map +1 -1
  35. package/dist/modules/styles.js +9 -2
  36. package/dist/modules/styles.js.map +1 -1
  37. package/dist/spa/src/App.vue +12 -4
  38. package/dist/spa/src/adminforth.ts +31 -11
  39. package/dist/spa/src/afcl/BarChart.vue +2 -2
  40. package/dist/spa/src/afcl/Checkbox.vue +2 -2
  41. package/dist/spa/src/afcl/Dialog.vue +38 -21
  42. package/dist/spa/src/afcl/Dropzone.vue +2 -2
  43. package/dist/spa/src/afcl/Input.vue +1 -1
  44. package/dist/spa/src/afcl/LinkButton.vue +1 -1
  45. package/dist/spa/src/afcl/PieChart.vue +5 -5
  46. package/dist/spa/src/afcl/Select.vue +17 -12
  47. package/dist/spa/src/afcl/Table.vue +202 -72
  48. package/dist/spa/src/afcl/Textarea.vue +31 -0
  49. package/dist/spa/src/afcl/Toggle.vue +2 -2
  50. package/dist/spa/src/afcl/Tooltip.vue +0 -1
  51. package/dist/spa/src/afcl/index.ts +1 -1
  52. package/dist/spa/src/components/AcceptModal.vue +1 -1
  53. package/dist/spa/src/components/ColumnValueInput.vue +11 -11
  54. package/dist/spa/src/components/ColumnValueInputWrapper.vue +2 -2
  55. package/dist/spa/src/components/CustomRangePicker.vue +5 -5
  56. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  57. package/dist/spa/src/components/Filters.vue +7 -6
  58. package/dist/spa/src/components/GroupsTable.vue +2 -1
  59. package/dist/spa/src/components/MenuLink.vue +3 -3
  60. package/dist/spa/src/components/ResourceForm.vue +35 -27
  61. package/dist/spa/src/components/ResourceListTable.vue +42 -42
  62. package/dist/spa/src/components/ResourceListTableVirtual.vue +41 -41
  63. package/dist/spa/src/components/ShowTable.vue +3 -3
  64. package/dist/spa/src/components/SkeleteLoader.vue +2 -2
  65. package/dist/spa/src/components/ThreeDotsMenu.vue +69 -10
  66. package/dist/spa/src/components/Toast.vue +25 -2
  67. package/dist/spa/src/components/ValueRenderer.vue +39 -12
  68. package/dist/spa/src/i18n.ts +1 -1
  69. package/dist/spa/src/shims-vue.d.ts +5 -0
  70. package/dist/spa/src/spa_types/core.ts +1 -1
  71. package/dist/spa/src/stores/modal.ts +6 -1
  72. package/dist/spa/src/stores/toast.ts +22 -3
  73. package/dist/spa/src/types/Back.ts +31 -6
  74. package/dist/spa/src/types/Common.ts +33 -24
  75. package/dist/spa/src/types/FrontendAPI.ts +21 -5
  76. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
  77. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  78. package/dist/spa/src/types/adapters/index.ts +1 -0
  79. package/dist/spa/src/utils.ts +8 -7
  80. package/dist/spa/src/views/CreateView.vue +15 -16
  81. package/dist/spa/src/views/EditView.vue +23 -17
  82. package/dist/spa/src/views/ListView.vue +116 -66
  83. package/dist/spa/src/views/LoginView.vue +2 -9
  84. package/dist/spa/src/views/ResourceParent.vue +1 -1
  85. package/dist/spa/src/views/ShowView.vue +59 -39
  86. package/dist/spa/src/websocket.ts +6 -1
  87. package/dist/spa/tsconfig.app.json +1 -1
  88. package/dist/spa/vite.config.ts +45 -2
  89. package/dist/types/Back.d.ts +16 -1
  90. package/dist/types/Back.d.ts.map +1 -1
  91. package/dist/types/Back.js +15 -0
  92. package/dist/types/Back.js.map +1 -1
  93. package/dist/types/Common.d.ts +27 -22
  94. package/dist/types/Common.d.ts.map +1 -1
  95. package/dist/types/Common.js.map +1 -1
  96. package/dist/types/FrontendAPI.d.ts +21 -3
  97. package/dist/types/FrontendAPI.d.ts.map +1 -1
  98. package/dist/types/FrontendAPI.js.map +1 -1
  99. package/dist/types/adapters/EmailAdapter.d.ts +2 -3
  100. package/dist/types/adapters/EmailAdapter.d.ts.map +1 -1
  101. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  102. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  103. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  104. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  105. package/dist/types/adapters/index.d.ts +1 -0
  106. package/dist/types/adapters/index.d.ts.map +1 -1
  107. package/package.json +2 -1
@@ -8,12 +8,12 @@ import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections,
8
8
  type AdminForthBulkActionCommon,
9
9
  type AdminForthForeignResourceCommon,
10
10
  type AdminForthResourceColumnCommon,
11
- AdminForthResourceInputCommon,
12
- AdminForthComponentDeclarationFull,
13
- AdminForthConfigMenuItem,
14
- AnnouncementBadgeResponse,
11
+ type AdminForthResourceInputCommon,
12
+ type AdminForthComponentDeclarationFull,
13
+ type AdminForthConfigMenuItem,
14
+ type AnnouncementBadgeResponse,
15
15
  AdminForthResourcePages,
16
- AdminForthResourceColumnInputCommon,
16
+ type AdminForthResourceColumnInputCommon,
17
17
  } from './Common.js';
18
18
 
19
19
  export interface ICodeInjector {
@@ -22,7 +22,7 @@ export interface ICodeInjector {
22
22
  devServerPort: number;
23
23
 
24
24
  getServeDir(): string;
25
-
25
+ registerCustomComponent(filePath: string): void;
26
26
  spaTmpPath(): string;
27
27
  }
28
28
 
@@ -130,7 +130,9 @@ export interface IAdminForthSingleFilter {
130
130
  | AdminForthFilterOperators.LTE | AdminForthFilterOperators.LIKE | AdminForthFilterOperators.ILIKE
131
131
  | AdminForthFilterOperators.IN | AdminForthFilterOperators.NIN;
132
132
  value?: any;
133
+ rightField?: string;
133
134
  insecureRawSQL?: string;
135
+ insecureRawNoSQL?: any;
134
136
  }
135
137
  export interface IAdminForthAndOrFilter {
136
138
  operator: AdminForthFilterOperators.AND | AdminForthFilterOperators.OR;
@@ -647,6 +649,12 @@ interface AdminForthInputConfigCustomization {
647
649
  */
648
650
  showBrandNameInSidebar?: boolean,
649
651
 
652
+ /**
653
+ * Whether to show brand logo in sidebar
654
+ * default is true
655
+ */
656
+ showBrandLogoInSidebar?: boolean,
657
+
650
658
  /**
651
659
  * Path to your app logo
652
660
  *
@@ -779,6 +787,7 @@ interface AdminForthInputConfigCustomization {
779
787
  userMenu?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
780
788
  header?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
781
789
  sidebar?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
790
+ sidebarTop?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
782
791
  everyPageBottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
783
792
  }
784
793
 
@@ -1101,6 +1110,7 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
1101
1110
  userMenu: Array<AdminForthComponentDeclarationFull>,
1102
1111
  header: Array<AdminForthComponentDeclarationFull>,
1103
1112
  sidebar: Array<AdminForthComponentDeclarationFull>,
1113
+ sidebarTop: Array<AdminForthComponentDeclarationFull>,
1104
1114
  everyPageBottom: Array<AdminForthComponentDeclarationFull>,
1105
1115
  },
1106
1116
 
@@ -1156,6 +1166,21 @@ export class Filters {
1156
1166
  static LIKE(field: string, value: any): IAdminForthSingleFilter {
1157
1167
  return { field, operator: AdminForthFilterOperators.LIKE, value };
1158
1168
  }
1169
+ static ILIKE(field: string, value: any): IAdminForthSingleFilter {
1170
+ return { field, operator: AdminForthFilterOperators.ILIKE, value };
1171
+ }
1172
+ static GT_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1173
+ return { field: leftField, operator: AdminForthFilterOperators.GT, rightField };
1174
+ }
1175
+ static GTE_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1176
+ return { field: leftField, operator: AdminForthFilterOperators.GTE, rightField };
1177
+ }
1178
+ static LT_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1179
+ return { field: leftField, operator: AdminForthFilterOperators.LT, rightField };
1180
+ }
1181
+ static LTE_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1182
+ return { field: leftField, operator: AdminForthFilterOperators.LTE, rightField };
1183
+ }
1159
1184
  static AND(
1160
1185
  ...args: (IAdminForthSingleFilter | IAdminForthAndOrFilter | Array<IAdminForthSingleFilter | IAdminForthAndOrFilter>)[]
1161
1186
  ): IAdminForthAndOrFilter {
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * Types that are common for both frontend side (SPA) and backend side (server).
4
3
  */
@@ -119,6 +118,10 @@ export interface AdminForthBulkActionCommon {
119
118
  */
120
119
  successMessage?: string,
121
120
 
121
+ /**
122
+ * Show in three dots dropdown menu in list view.
123
+ */
124
+ showInThreeDotsDropdown?: boolean,
122
125
  }
123
126
 
124
127
  export interface AdminForthFieldComponents {
@@ -260,9 +263,17 @@ export interface AdminForthComponentDeclarationFull {
260
263
  */
261
264
  meta?: any,
262
265
  }
266
+ import { type AdminForthActionInput } from './Back.js'
267
+ export { type AdminForthActionInput } from './Back.js'
263
268
 
264
269
  export type AdminForthComponentDeclaration = AdminForthComponentDeclarationFull | string;
265
270
 
271
+ export type FieldGroup = {
272
+ groupName: string;
273
+ columns: string[];
274
+ noTitle?: boolean;
275
+ };
276
+
266
277
  /**
267
278
  * Resource describes one table or collection in database.
268
279
  * AdminForth generates set of pages for 'list', 'show', 'edit', 'create', 'filter' operations for each resource.
@@ -344,6 +355,11 @@ export interface AdminForthResourceInputCommon {
344
355
  direction: AdminForthSortDirections | string,
345
356
  }
346
357
 
358
+ /*
359
+ * Custom actions list. Actions available in show, edit and create views.
360
+ */
361
+ actions?: AdminForthActionInput[],
362
+
347
363
  /**
348
364
  * Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
349
365
  * using checkboxes.
@@ -371,26 +387,10 @@ export interface AdminForthResourceInputCommon {
371
387
  /**
372
388
  * Allows to make groups of columns in show, create and edit resource pages.
373
389
  */
374
- fieldGroups?: {
375
- groupName: string;
376
- columns: string[];
377
- noTitle?: boolean;
378
- }[];
379
- createFieldGroups?: {
380
- groupName: string;
381
- columns: string[];
382
- noTitle?: boolean;
383
- }[];
384
- editFieldGroups?: {
385
- groupName: string;
386
- columns: string[];
387
- noTitle?: boolean;
388
- }[];
389
- showFieldGroups?: {
390
- groupName: string;
391
- columns: string[];
392
- noTitle?: boolean;
393
- }[];
390
+ fieldGroups?: FieldGroup[];
391
+ createFieldGroups?: FieldGroup[];
392
+ editFieldGroups?: FieldGroup[];
393
+ showFieldGroups?: FieldGroup[];
394
394
 
395
395
  /**
396
396
  * Page size for list view
@@ -468,6 +468,7 @@ export interface AdminForthResourceInputCommon {
468
468
  list?: {
469
469
  beforeBreadcrumbs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
470
470
  afterBreadcrumbs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
471
+ beforeActionButtons?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
471
472
  bottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
472
473
  threeDotsDropdownItems?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
473
474
  customActionIcons?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
@@ -809,9 +810,6 @@ export interface AdminForthResourceColumnInputCommon {
809
810
  */
810
811
  minLength?: number,
811
812
 
812
- min?: number,
813
- max?: number,
814
-
815
813
  /**
816
814
  * Minimum value that can be entered in this field.
817
815
  */
@@ -877,6 +875,15 @@ export interface AdminForthResourceColumnCommon extends AdminForthResourceColumn
877
875
 
878
876
  editingNote?: { create?: string, edit?: string },
879
877
 
878
+ /**
879
+ * Minimal value stored in this field.
880
+ */
881
+ min?: number,
882
+
883
+ /**
884
+ * Maximum value stored in this field.
885
+ */
886
+ max?: number,
880
887
  }
881
888
 
882
889
  export enum AdminForthMenuTypes {
@@ -1064,6 +1071,7 @@ export interface AdminForthConfigForFrontend {
1064
1071
  },
1065
1072
  rememberMeDays: number,
1066
1073
  showBrandNameInSidebar: boolean,
1074
+ showBrandLogoInSidebar: boolean,
1067
1075
  brandLogo?: string,
1068
1076
  singleTheme?: 'light' | 'dark',
1069
1077
  datesFormat: string,
@@ -1080,6 +1088,7 @@ export interface AdminForthConfigForFrontend {
1080
1088
  userMenu: Array<AdminForthComponentDeclarationFull>,
1081
1089
  header: Array<AdminForthComponentDeclarationFull>,
1082
1090
  sidebar: Array<AdminForthComponentDeclarationFull>,
1091
+ sidebarTop: Array<AdminForthComponentDeclarationFull>,
1083
1092
  everyPageBottom: Array<AdminForthComponentDeclarationFull>,
1084
1093
  },
1085
1094
  customHeadItems?: {
@@ -55,7 +55,7 @@ export interface FrontendAPIInterface {
55
55
  *
56
56
  * @param params - The parameters of the alert
57
57
  */
58
- alert(params:AlertParams): void;
58
+ alert(params:AlertParams): void | Promise<string> | string;
59
59
 
60
60
 
61
61
  list: {
@@ -82,10 +82,9 @@ export interface FrontendAPIInterface {
82
82
  */
83
83
  closeThreeDotsDropdown(): void;
84
84
 
85
-
86
85
  /**
87
- * Set a filter in the list
88
- * Works only when user located on the list page.
86
+ * Set a filter in the list.
87
+ * Works only when user located on the list page. If filter already exists, it will be replaced with the new one.
89
88
  * Can be used to set filter from charts or other components in pageInjections.
90
89
  *
91
90
  * Example:
@@ -96,11 +95,15 @@ export interface FrontendAPIInterface {
96
95
  * adminforth.list.setFilter({field: 'name', operator: 'ilike', value: 'john'})
97
96
  * ```
98
97
  *
98
+ * Please note that you can set/update filter even for fields which have showIn.filter=false in resource configuration.
99
+ * Also you can set filter for virtual columns. For example Universal search plugin calls updateFilter for virtual column which has showIn.filter=false (because we dont want to show this column in filter dropdown, plugin renders its own filter UI)
100
+ *
99
101
  * @param filter - The filter to set
100
102
  */
101
103
  setFilter(filter: FilterParams): void;
102
104
 
103
105
  /**
106
+ * DEPRECATED: does the same as setFilter, kept for backward compatibility
104
107
  * Update a filter in the list
105
108
  *
106
109
  * Example:
@@ -121,6 +124,14 @@ export interface FrontendAPIInterface {
121
124
  clearFilters(): void;
122
125
  }
123
126
 
127
+ show: {
128
+ /**
129
+ * Full refresh the current record on the show page. Loader may be shown during fetching.
130
+ * Fire-and-forget; you don't need to await it.
131
+ */
132
+ refresh(): void;
133
+ }
134
+
124
135
  menu: {
125
136
  /**
126
137
  * Refreshes the badges in the menu, by recalling the badge function for each menu item
@@ -171,7 +182,12 @@ export type AlertParams = {
171
182
  * Default is 10 seconds;
172
183
  */
173
184
  timeout?: number | 'unlimited';
174
-
185
+
186
+ /**
187
+ * Optional buttons to display in the alert
188
+ */
189
+ buttons?: {value: any, label: string}[];
190
+
175
191
  }
176
192
 
177
193
 
@@ -1,8 +1,8 @@
1
- interface EmailAdapter {
1
+ export interface EmailAdapter {
2
2
 
3
3
  /**
4
4
  * This method is called to validate the configuration of the adapter
5
- * and should throw a clear user-readbale error if the configuration is invalid.
5
+ * and should throw a clear user-readable error if the configuration is invalid.
6
6
  */
7
7
  validate(): Promise<void>;
8
8
 
@@ -25,5 +25,3 @@ interface EmailAdapter {
25
25
  ok?: boolean;
26
26
  }>;
27
27
  }
28
-
29
- export { EmailAdapter };
@@ -0,0 +1,30 @@
1
+ export interface ImageVisionAdapter {
2
+
3
+ /**
4
+ * This method is called to validate the configuration of the adapter
5
+ * and should throw a clear user-readable error if the configuration is invalid.
6
+ */
7
+ validate(): void;
8
+
9
+ /**
10
+ * Input file extension supported
11
+ */
12
+ inputFileExtensionSupported(): string[];
13
+
14
+ /**
15
+ * This method should generate an image based on the provided prompt and input files.
16
+ * @param prompt - The prompt to generate the image
17
+ * @param inputFileUrls - An array of input file paths (optional)
18
+ * @returns A promise that resolves to an object containing the generated image and any error message
19
+ */
20
+ generate({
21
+ prompt,
22
+ inputFileUrls,
23
+ }: {
24
+ prompt: string,
25
+ inputFileUrls: string[],
26
+ }): Promise<{
27
+ response: string;
28
+ error?: string;
29
+ }>;
30
+ }
@@ -1,5 +1,6 @@
1
1
  export type { EmailAdapter } from './EmailAdapter.js';
2
2
  export type { CompletionAdapter } from './CompletionAdapter.js';
3
3
  export type { ImageGenerationAdapter } from './ImageGenerationAdapter.js';
4
+ export type { ImageVisionAdapter } from './ImageVisionAdapter.js';
4
5
  export type { OAuth2Adapter } from './OAuth2Adapter.js';
5
6
  export type { StorageAdapter } from './StorageAdapter.js';
@@ -1,6 +1,6 @@
1
1
  import { onMounted, ref, resolveComponent } from 'vue';
2
2
  import type { CoreConfig } from './spa_types/core';
3
- import type { ValidationObject } from './types/AdminForthConfig';
3
+ import type { 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';
@@ -95,13 +95,14 @@ export const loadFile = (file: string) => {
95
95
  }
96
96
 
97
97
  export function checkEmptyValues(value: any, viewType: 'show' | 'list' ) {
98
- const config: CoreConfig | {} = useCoreStore().config;
98
+ const config: CoreConfig | {} | null = useCoreStore().config;
99
99
  let emptyFieldPlaceholder = '';
100
- if (config.emptyFieldPlaceholder) {
101
- if(typeof config.emptyFieldPlaceholder === 'string') {
102
- emptyFieldPlaceholder = config.emptyFieldPlaceholder;
100
+ if (config && 'emptyFieldPlaceholder' in config) {
101
+ const efp = (config as CoreConfig).emptyFieldPlaceholder;
102
+ if(typeof efp === 'string') {
103
+ emptyFieldPlaceholder = efp;
103
104
  } else {
104
- emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '';
105
+ emptyFieldPlaceholder = efp?.[viewType] || '';
105
106
  }
106
107
  if (value === null || value === undefined || value === '') {
107
108
  return emptyFieldPlaceholder;
@@ -182,7 +183,7 @@ export function verySimpleHash(str: string): string {
182
183
  return `${str.split('').reduce((a, b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)}`;
183
184
  }
184
185
 
185
- export function humanifySize(size) {
186
+ export function humanifySize(size: number) {
186
187
  if (!size) {
187
188
  return '';
188
189
  }
@@ -4,7 +4,7 @@
4
4
  <component
5
5
  v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.beforeBreadcrumbs || []"
6
6
  :is="getCustomComponent(c)"
7
- :meta="c.meta"
7
+ :meta="(c as AdminForthComponentDeclarationFull).meta"
8
8
  :record="coreStore.record"
9
9
  :resource="coreStore.resource"
10
10
  :adminUser="coreStore.adminUser"
@@ -20,18 +20,18 @@
20
20
 
21
21
  <button
22
22
  @click="saveRecord"
23
- 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"
23
+ 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"
24
24
  :disabled="saving || (validating && !isValid)"
25
25
  >
26
26
  <svg v-if="saving"
27
27
  aria-hidden="true" class="w-4 h-4 mr-1 text-gray-200 animate-spin dark:text-gray-600 fill-lightCreateViewSaveButtonText" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/><path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/></svg>
28
28
 
29
- <IconFloppyDiskSolid v-else class="w-4 h-4 mr-1" />
29
+ <IconFloppyDiskSolid v-else class="w-4 h-4" />
30
30
  {{ $t('Save') }}
31
31
  </button>
32
32
 
33
33
  <ThreeDotsMenu
34
- :threeDotsDropdownItems="coreStore.resourceOptions?.pageInjections?.create?.threeDotsDropdownItems"
34
+ :threeDotsDropdownItems="(coreStore.resourceOptions?.pageInjections?.create?.threeDotsDropdownItems as [])"
35
35
  ></ThreeDotsMenu>
36
36
 
37
37
  </BreadcrumbsWithButtons>
@@ -39,7 +39,7 @@
39
39
  <component
40
40
  v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.afterBreadcrumbs || []"
41
41
  :is="getCustomComponent(c)"
42
- :meta="c.meta"
42
+ :meta="(c as AdminForthComponentDeclarationFull).meta"
43
43
  :record="coreStore.record"
44
44
  :resource="coreStore.resource"
45
45
  :adminUser="coreStore.adminUser"
@@ -51,7 +51,7 @@
51
51
  <ResourceForm
52
52
  v-else
53
53
  :record="record"
54
- :resource="coreStore.resource"
54
+ :resource="coreStore.resource!"
55
55
  @update:record="onUpdateRecord"
56
56
  @update:isValid="isValid = $event"
57
57
  :validating="validating"
@@ -63,7 +63,7 @@
63
63
  <component
64
64
  v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.bottom || []"
65
65
  :is="getCustomComponent(c)"
66
- :meta="c.meta"
66
+ :meta="(c as AdminForthComponentDeclarationFull).meta"
67
67
  :record="coreStore.record"
68
68
  :resource="coreStore.resource"
69
69
  :adminUser="coreStore.adminUser"
@@ -88,6 +88,7 @@ import { showErrorTost } from '@/composables/useFrontendApi';
88
88
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
89
89
  import adminforth from '@/adminforth';
90
90
  import { useI18n } from 'vue-i18n';
91
+ import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
91
92
 
92
93
  const isValid = ref(false);
93
94
  const validating = ref(false);
@@ -109,36 +110,34 @@ const initialValues = ref({});
109
110
  const readonlyColumns = ref([]);
110
111
 
111
112
 
112
- async function onUpdateRecord(newRecord) {
113
- console.log('newRecord', newRecord);
113
+ async function onUpdateRecord(newRecord: any) {
114
114
  record.value = newRecord;
115
115
  }
116
116
 
117
117
  onMounted(async () => {
118
118
  loading.value = true;
119
119
  await coreStore.fetchResourceFull({
120
- resourceId: route.params.resourceId
120
+ resourceId: route.params.resourceId as string
121
121
  });
122
- initialValues.value = (coreStore.resource?.columns || []).reduce((acc, column) => {
122
+ initialValues.value = (coreStore.resource?.columns || []).reduce<Record<string, unknown>>((acc, column) => {
123
123
  if (column.suggestOnCreate !== undefined) {
124
124
  acc[column.name] = column.suggestOnCreate;
125
125
  }
126
126
  return acc;
127
127
  }, {});
128
128
  if (route.query.values) {
129
- initialValues.value = { ...initialValues.value, ...JSON.parse(decodeURIComponent(route.query.values)) };
129
+ initialValues.value = { ...initialValues.value, ...JSON.parse(decodeURIComponent(route.query.values as string)) };
130
130
  }
131
131
  if (route.query.readonlyColumns) {
132
- readonlyColumns.value = JSON.parse(decodeURIComponent(route.query.readonlyColumns));
132
+ readonlyColumns.value = JSON.parse(decodeURIComponent(route.query.readonlyColumns as string));
133
133
  }
134
134
  record.value = initialValues.value;
135
135
  loading.value = false;
136
- checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');
136
+ checkAcessByAllowedActions(coreStore.resourceOptions!.allowedActions,'create');
137
137
  initThreeDotsDropdown();
138
138
  });
139
139
 
140
140
  async function saveRecord() {
141
- console.log('saveRecord isValid', isValid.value);
142
141
  if (!isValid.value) {
143
142
  validating.value = true;
144
143
  return;
@@ -159,7 +158,7 @@ async function saveRecord() {
159
158
  }
160
159
  saving.value = false;
161
160
  if (route.query.returnTo) {
162
- router.push(route.query.returnTo);
161
+ router.push(<string>route.query.returnTo);
163
162
  } else {
164
163
  router.push({
165
164
  name: 'resource-show',
@@ -3,7 +3,7 @@
3
3
  <component
4
4
  v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.beforeBreadcrumbs || []"
5
5
  :is="getCustomComponent(c)"
6
- :meta="c.meta"
6
+ :meta="(c as AdminForthComponentDeclarationFull).meta"
7
7
  :record="editableRecord"
8
8
  :resource="coreStore.resource"
9
9
  :adminUser="coreStore.adminUser"
@@ -19,7 +19,7 @@
19
19
 
20
20
  <button
21
21
  @click="saveRecord"
22
- 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"
22
+ 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"
23
23
  :disabled="saving || (validating && !isValid)"
24
24
  >
25
25
  <IconFloppyDiskSolid class="w-4 h-4" />
@@ -27,7 +27,7 @@
27
27
  </button>
28
28
 
29
29
  <ThreeDotsMenu
30
- :threeDotsDropdownItems="coreStore.resourceOptions?.pageInjections?.edit?.threeDotsDropdownItems"
30
+ :threeDotsDropdownItems="(coreStore.resourceOptions?.pageInjections?.edit?.threeDotsDropdownItems as [])"
31
31
  ></ThreeDotsMenu>
32
32
 
33
33
  </BreadcrumbsWithButtons>
@@ -35,16 +35,16 @@
35
35
  <component
36
36
  v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.afterBreadcrumbs || []"
37
37
  :is="getCustomComponent(c)"
38
- :meta="c.meta"
38
+ :meta="(c as AdminForthComponentDeclarationFull).meta"
39
39
  :record="coreStore.record"
40
40
  :resource="coreStore.resource"
41
41
  :adminUser="coreStore.adminUser"
42
42
  />
43
43
 
44
44
  <SingleSkeletLoader v-if="loading"></SingleSkeletLoader>
45
-
45
+
46
46
  <ResourceForm
47
- v-else
47
+ v-else-if="coreStore.resource"
48
48
  :record="editableRecord"
49
49
  :resource="coreStore.resource"
50
50
  :adminUser="coreStore.adminUser"
@@ -58,7 +58,7 @@
58
58
  <component
59
59
  v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.bottom || []"
60
60
  :is="getCustomComponent(c)"
61
- :meta="c.meta"
61
+ :meta="(c as AdminForthComponentDeclarationFull).meta"
62
62
  :record="coreStore.record"
63
63
  :resource="coreStore.resource"
64
64
  :adminUser="coreStore.adminUser"
@@ -76,12 +76,13 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
76
76
  import { useCoreStore } from '@/stores/core';
77
77
  import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
78
78
  import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
79
- import { computed, onMounted, ref } from 'vue';
79
+ import { computed, onMounted, ref, type Ref } from 'vue';
80
80
  import { useRoute, useRouter } from 'vue-router';
81
81
  import { showErrorTost } from '@/composables/useFrontendApi';
82
82
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
83
83
  import adminforth from '@/adminforth';
84
84
  import { useI18n } from 'vue-i18n';
85
+ import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
85
86
 
86
87
  const { t } = useI18n();
87
88
  const coreStore = useCoreStore();
@@ -96,9 +97,9 @@ const loading = ref(true);
96
97
 
97
98
  const saving = ref(false);
98
99
 
99
- const record = ref({});
100
+ const record: Ref<Record<string, any>> = ref({});
100
101
 
101
- async function onUpdateRecord(newRecord) {
102
+ async function onUpdateRecord(newRecord: Record<string, any>) {
102
103
  record.value = newRecord;
103
104
  }
104
105
 
@@ -110,7 +111,7 @@ const editableRecord = computed(() => {
110
111
  coreStore.resource.columns.forEach(column => {
111
112
  if (column.foreignResource) {
112
113
  if (column.isArray?.enabled) {
113
- newRecord[column.name] = newRecord[column.name]?.map(fr => fr.pk);
114
+ newRecord[column.name] = newRecord[column.name]?.map((fr: { pk: any }) => fr.pk);
114
115
  } else {
115
116
  newRecord[column.name] = newRecord[column.name]?.pk;
116
117
  }
@@ -123,16 +124,20 @@ onMounted(async () => {
123
124
  loading.value = true;
124
125
 
125
126
  await coreStore.fetchResourceFull({
126
- resourceId: route.params.resourceId
127
+ resourceId: route.params.resourceId as string
127
128
  });
128
129
  initThreeDotsDropdown();
129
130
 
130
131
  await coreStore.fetchRecord({
131
- resourceId: route.params.resourceId,
132
- primaryKey: route.params.primaryKey,
132
+ resourceId: route.params.resourceId as string,
133
+ primaryKey: route.params.primaryKey as string,
133
134
  source: 'edit',
134
135
  });
135
- checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'edit');
136
+
137
+ if (coreStore.resourceOptions) {
138
+ checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'edit');
139
+ }
140
+
136
141
  loading.value = false;
137
142
  });
138
143
 
@@ -145,7 +150,7 @@ async function saveRecord() {
145
150
  }
146
151
 
147
152
  saving.value = true;
148
- const updates = {};
153
+ const updates: Record<string, any> = {};
149
154
  for (const key in record.value) {
150
155
  let columnIsUpdated = false;
151
156
 
@@ -157,7 +162,8 @@ async function saveRecord() {
157
162
  columnIsUpdated = record.value[key] !== coreStore.record[key];
158
163
  }
159
164
 
160
- const column = coreStore.resource.columns.find((c) => c.name === key);
165
+ if (!coreStore.resource) return;
166
+ const column = coreStore.resource.columns.find((c) => c.name === key);
161
167
 
162
168
  if (column?.foreignResource) {
163
169
  columnIsUpdated = record.value[key] !== coreStore.record[key]?.pk;