adminforth 2.17.0-next.6 → 2.17.0-next.61

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 (114) hide show
  1. package/commands/callTsProxy.js +2 -1
  2. package/commands/createApp/templates/adminuser.ts.hbs +2 -1
  3. package/commands/createApp/templates/index.ts.hbs +3 -2
  4. package/commands/createCustomComponent/main.js +0 -3
  5. package/commands/createCustomComponent/templates/customCrud/afterBreadcrumbs.vue.hbs +4 -2
  6. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +3 -2
  7. package/commands/createCustomComponent/templates/customCrud/beforeBreadcrumbs.vue.hbs +4 -2
  8. package/commands/createCustomComponent/templates/customCrud/bottom.vue.hbs +4 -2
  9. package/commands/createCustomComponent/templates/customCrud/threeDotsDropdownItems.vue.hbs +4 -2
  10. package/commands/createPlugin/templates/index.ts.hbs +4 -0
  11. package/dist/auth.d.ts +2 -2
  12. package/dist/auth.d.ts.map +1 -1
  13. package/dist/auth.js +17 -10
  14. package/dist/auth.js.map +1 -1
  15. package/dist/basePlugin.d.ts +1 -0
  16. package/dist/basePlugin.d.ts.map +1 -1
  17. package/dist/basePlugin.js +4 -2
  18. package/dist/basePlugin.js.map +1 -1
  19. package/dist/dataConnectors/baseConnector.d.ts +4 -0
  20. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  21. package/dist/dataConnectors/baseConnector.js +103 -14
  22. package/dist/dataConnectors/baseConnector.js.map +1 -1
  23. package/dist/dataConnectors/clickhouse.d.ts +2 -0
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -4
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts +8 -1
  28. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  29. package/dist/dataConnectors/mongo.js +72 -28
  30. package/dist/dataConnectors/mongo.js.map +1 -1
  31. package/dist/dataConnectors/mysql.d.ts +2 -0
  32. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  33. package/dist/dataConnectors/mysql.js +22 -23
  34. package/dist/dataConnectors/mysql.js.map +1 -1
  35. package/dist/dataConnectors/postgres.d.ts +2 -0
  36. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  37. package/dist/dataConnectors/postgres.js +23 -26
  38. package/dist/dataConnectors/postgres.js.map +1 -1
  39. package/dist/dataConnectors/sqlite.d.ts +2 -0
  40. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  41. package/dist/dataConnectors/sqlite.js +19 -19
  42. package/dist/dataConnectors/sqlite.js.map +1 -1
  43. package/dist/index.d.ts +21 -40
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +77 -54
  46. package/dist/index.js.map +1 -1
  47. package/dist/modules/codeInjector.d.ts.map +1 -1
  48. package/dist/modules/codeInjector.js +45 -63
  49. package/dist/modules/codeInjector.js.map +1 -1
  50. package/dist/modules/configValidator.d.ts.map +1 -1
  51. package/dist/modules/configValidator.js +14 -9
  52. package/dist/modules/configValidator.js.map +1 -1
  53. package/dist/modules/logger.d.ts +5 -0
  54. package/dist/modules/logger.d.ts.map +1 -0
  55. package/dist/modules/logger.js +16 -0
  56. package/dist/modules/logger.js.map +1 -0
  57. package/dist/modules/restApi.d.ts.map +1 -1
  58. package/dist/modules/restApi.js +31 -24
  59. package/dist/modules/restApi.js.map +1 -1
  60. package/dist/modules/socketBroker.d.ts.map +1 -1
  61. package/dist/modules/socketBroker.js +6 -5
  62. package/dist/modules/socketBroker.js.map +1 -1
  63. package/dist/modules/styles.js +1 -1
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +11 -11
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/package-lock.json +0 -13
  68. package/dist/spa/package.json +0 -1
  69. package/dist/spa/src/App.vue +6 -3
  70. package/dist/spa/src/adminforth.ts +60 -1
  71. package/dist/spa/src/afcl/DatePicker.vue +0 -1
  72. package/dist/spa/src/afcl/Dialog.vue +2 -2
  73. package/dist/spa/src/afcl/Dropzone.vue +6 -4
  74. package/dist/spa/src/afcl/Select.vue +1 -1
  75. package/dist/spa/src/afcl/Table.vue +7 -5
  76. package/dist/spa/src/afcl/Tooltip.vue +38 -4
  77. package/dist/spa/src/components/ColumnValueInput.vue +14 -1
  78. package/dist/spa/src/components/CustomDateRangePicker.vue +0 -2
  79. package/dist/spa/src/components/CustomRangePicker.vue +9 -6
  80. package/dist/spa/src/components/Filters.vue +4 -4
  81. package/dist/spa/src/components/ListActionsThreeDots.vue +235 -0
  82. package/dist/spa/src/components/ResourceForm.vue +4 -4
  83. package/dist/spa/src/components/ResourceListTable.vue +30 -16
  84. package/dist/spa/src/components/ResourceListTableVirtual.vue +34 -18
  85. package/dist/spa/src/components/Sidebar.vue +4 -2
  86. package/dist/spa/src/components/ThreeDotsMenu.vue +35 -20
  87. package/dist/spa/src/composables/useFrontendApi.ts +8 -4
  88. package/dist/spa/src/renderers/CompactField.vue +3 -2
  89. package/dist/spa/src/renderers/CompactUUID.vue +3 -2
  90. package/dist/spa/src/renderers/RichText.vue +15 -0
  91. package/dist/spa/src/stores/core.ts +3 -2
  92. package/dist/spa/src/stores/filters.ts +1 -1
  93. package/dist/spa/src/stores/toast.ts +1 -2
  94. package/dist/spa/src/types/Back.ts +419 -18
  95. package/dist/spa/src/types/Common.ts +7 -14
  96. package/dist/spa/src/types/FrontendAPI.ts +27 -11
  97. package/dist/spa/src/views/CreateView.vue +23 -31
  98. package/dist/spa/src/views/EditView.vue +27 -31
  99. package/dist/spa/src/views/ListView.vue +20 -10
  100. package/dist/spa/src/views/SettingsView.vue +3 -2
  101. package/dist/spa/src/views/ShowView.vue +7 -6
  102. package/dist/types/Back.d.ts +371 -44
  103. package/dist/types/Back.d.ts.map +1 -1
  104. package/dist/types/Back.js +6 -0
  105. package/dist/types/Back.js.map +1 -1
  106. package/dist/types/Common.d.ts +8 -15
  107. package/dist/types/Common.d.ts.map +1 -1
  108. package/dist/types/Common.js +2 -0
  109. package/dist/types/Common.js.map +1 -1
  110. package/dist/types/FrontendAPI.d.ts +34 -11
  111. package/dist/types/FrontendAPI.d.ts.map +1 -1
  112. package/dist/types/FrontendAPI.js.map +1 -1
  113. package/package.json +4 -1
  114. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +0 -28
@@ -10,9 +10,10 @@ export interface FrontendAPIInterface {
10
10
  * Example:
11
11
  *
12
12
  * ```ts
13
- * import adminforth from '@/adminforth'
13
+ *import { useAdminforth } from '@/adminforth';
14
14
  *
15
- * const isConfirmed = await adminforth.confirm({message: 'Are you sure?', yes: 'Yes', no: 'No'})
15
+ * const { confirm } = useAdminforth();
16
+ * const isConfirmed = await confirm({message: 'Are you sure?', yes: 'Yes', no: 'No'})
16
17
  * if (isConfirmed) {
17
18
  * your code...
18
19
  * }
@@ -31,9 +32,10 @@ export interface FrontendAPIInterface {
31
32
  * Example:
32
33
  *
33
34
  * ```ts
34
- * import adminforth from '@/adminforth'
35
+ * import { useAdminforth } from '@/adminforth';
36
+ * const { alert } = useAdminforth();
35
37
  *
36
- * adminforth.alert({message: 'Hello', variant: 'success'})
38
+ * alert({message: 'Hello', variant: 'success'})
37
39
  * ```
38
40
  *
39
41
  * @param params - The parameters of the alert
@@ -76,13 +78,14 @@ export interface FrontendAPIInterface {
76
78
  * Example:
77
79
  *
78
80
  * ```ts
79
- * import adminforth from '@/adminforth'
81
+ * import { useAdminforth } from '@/adminforth';
80
82
  *
83
+ * const { list } = useAdminforth();
81
84
  * // Regular filter (will show in badge if column.showIn.filter !== false)
82
- * adminforth.list.setFilter({field: 'name', operator: 'ilike', value: 'john'})
85
+ * list.setFilter({field: 'name', operator: 'ilike', value: 'john'})
83
86
  *
84
87
  * // Hidden filter (won't show in badge if column.showIn.filter === false)
85
- * adminforth.list.setFilter({field: 'internal_status', operator: 'eq', value: 'active'})
88
+ * list.setFilter({field: 'internal_status', operator: 'eq', value: 'active'})
86
89
  * ```
87
90
  *
88
91
  * Please note that you can set/update filter even for fields which have showIn.filter=false in resource configuration.
@@ -93,7 +96,8 @@ export interface FrontendAPIInterface {
93
96
  setFilter(filter: FilterParams): void;
94
97
 
95
98
  /**
96
- * DEPRECATED: does the same as setFilter, kept for backward compatibility
99
+ * @deprecated does the same as setFilter, kept for backward compatibility, will be removed in 2.0.0
100
+ *
97
101
  * Update a filter in the list
98
102
  *
99
103
  * Filters visibility in badge is automatically determined by column configuration:
@@ -102,9 +106,9 @@ export interface FrontendAPIInterface {
102
106
  * Example:
103
107
  *
104
108
  * ```ts
105
- * import adminforth from '@/adminforth';
106
- *
107
- * adminforth.list.updateFilter({field: 'name', operator: 'ilike', value: 'john'})
109
+ * import { useAdminforth } from '@/adminforth';
110
+ * const { list } = useAdminforth();
111
+ * list.updateFilter({field: 'name', operator: 'ilike', value: 'john'})
108
112
  * ```
109
113
  *
110
114
  * @param filter - The filter to update
@@ -136,6 +140,18 @@ export interface FrontendAPIInterface {
136
140
  * Close the user menu dropdown
137
141
  */
138
142
  closeUserMenuDropdown(): void;
143
+
144
+ /**
145
+ * Run save interceptors for a specific resource or all resources if no resourceId is provided
146
+ */
147
+ runSaveInterceptors(params: { action: 'create'|'edit'; values: any; resource: any; resourceId: string; }): Promise<{ ok: boolean; error?: string | null; extra?: object; }>;
148
+
149
+ /**
150
+ * Clear save interceptors for a specific resource or all resources if no resourceId is provided
151
+ *
152
+ * @param resourceId - The resource ID to clear interceptors for
153
+ */
154
+ clearSaveInterceptors(resourceId?: string): void;
139
155
  }
140
156
 
141
157
  export type ConfirmParams = {
@@ -17,25 +17,7 @@
17
17
  >
18
18
  {{ $t('Cancel') }}
19
19
  </button>
20
-
21
- <!-- Custom Save Button injection -->
22
- <component
23
- v-if="createSaveButtonInjection"
24
- :is="getCustomComponent(createSaveButtonInjection)"
25
- :meta="createSaveButtonInjection.meta"
26
- :record="record"
27
- :resource="coreStore.resource"
28
- :adminUser="coreStore.adminUser"
29
- :saving="saving"
30
- :validating="validating"
31
- :isValid="isValid"
32
- :disabled="saving || (validating && !isValid)"
33
- :saveRecord="saveRecord"
34
- />
35
-
36
- <!-- Default Save Button fallback -->
37
20
  <button
38
- v-else
39
21
  @click="() => saveRecord()"
40
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"
41
23
  :disabled="saving || (validating && !isValid)"
@@ -99,12 +81,12 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
99
81
  import { useCoreStore } from '@/stores/core';
100
82
  import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf } from '@/utils';
101
83
  import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
102
- import { onMounted, ref, watch, nextTick } from 'vue';
84
+ import { onMounted, onBeforeMount, ref, watch, nextTick } from 'vue';
103
85
  import { useRoute, useRouter } from 'vue-router';
104
86
  import { computed } from 'vue';
105
87
  import { showErrorTost } from '@/composables/useFrontendApi';
106
88
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
107
- import adminforth from '@/adminforth';
89
+ import { useAdminforth } from '@/adminforth';
108
90
  import { useI18n } from 'vue-i18n';
109
91
  import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
110
92
  import type { AdminForthResourceColumn } from '@/types/Back';
@@ -121,18 +103,12 @@ const router = useRouter();
121
103
  const record = ref({});
122
104
 
123
105
  const coreStore = useCoreStore();
106
+ const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
124
107
 
125
108
  const { t } = useI18n();
126
109
 
127
110
  const resourceFormRef = ref<InstanceType<typeof ResourceForm> | null>(null);
128
111
 
129
- const createSaveButtonInjection = computed<AdminForthComponentDeclarationFull | null>(() => {
130
- const raw: any = coreStore.resourceOptions?.pageInjections?.create?.saveButton as any;
131
- if (!raw) return null;
132
- const item = Array.isArray(raw) ? raw[0] : raw;
133
- return item as AdminForthComponentDeclarationFull;
134
- });
135
-
136
112
  const initialValues = ref({});
137
113
 
138
114
  const readonlyColumns = ref([]);
@@ -142,6 +118,10 @@ async function onUpdateRecord(newRecord: any) {
142
118
  record.value = newRecord;
143
119
  }
144
120
 
121
+ onBeforeMount(() => {
122
+ clearSaveInterceptors(route.params.resourceId as string);
123
+ });
124
+
145
125
  onMounted(async () => {
146
126
  loading.value = true;
147
127
  await coreStore.fetchResourceFull({
@@ -182,7 +162,7 @@ onMounted(async () => {
182
162
  initThreeDotsDropdown();
183
163
  });
184
164
 
185
- async function saveRecord(opts?: { confirmationResult?: any }) {
165
+ async function saveRecord() {
186
166
  if (!isValid.value) {
187
167
  validating.value = true;
188
168
  await nextTick();
@@ -194,6 +174,18 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
194
174
  const requiredColumns = coreStore.resource?.columns.filter(c => c.required?.create === true) || [];
195
175
  const requiredColumnsToSkip = requiredColumns.filter(c => checkShowIf(c, record.value, coreStore.resource?.columns || []) === false);
196
176
  saving.value = true;
177
+ const interceptorsResult = await runSaveInterceptors({
178
+ action: 'create',
179
+ values: record.value,
180
+ resource: coreStore.resource,
181
+ resourceId: route.params.resourceId as string,
182
+ });
183
+ if (!interceptorsResult.ok) {
184
+ saving.value = false;
185
+ if (interceptorsResult.error) showErrorTost(interceptorsResult.error);
186
+ return;
187
+ }
188
+ const interceptorConfirmationResult = (interceptorsResult.extra as Record<string, any>)?.confirmationResult;
197
189
  const response = await callAdminForthApi({
198
190
  method: 'POST',
199
191
  path: `/create_record`,
@@ -202,7 +194,7 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
202
194
  record: record.value,
203
195
  requiredColumnsToSkip,
204
196
  meta: {
205
- ...(opts?.confirmationResult ? { confirmationResult: opts.confirmationResult } : {}),
197
+ ...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
206
198
  },
207
199
  },
208
200
  });
@@ -220,7 +212,7 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
220
212
  primaryKey: response.newRecordId
221
213
  }
222
214
  });
223
- adminforth.alert({
215
+ alert({
224
216
  message: t('Record created successfully!'),
225
217
  variant: 'success'
226
218
  });
@@ -236,7 +228,7 @@ function scrollToInvalidField() {
236
228
  }
237
229
  }
238
230
  const errorMessage = t('Failed to save. Please fix errors for the following fields:') + '<ul class="mt-2 list-disc list-inside">' + columnsWithErrors.map(c => `<li><strong>${c.column.label || c.column.name}</strong>: ${c.error}</li>`).join('') + '</ul>';
239
- adminforth.alert({
231
+ alert({
240
232
  messageHtml: errorMessage,
241
233
  variant: 'danger'
242
234
  });
@@ -16,25 +16,7 @@
16
16
  >
17
17
  {{ $t('Cancel') }}
18
18
  </button>
19
-
20
- <!-- Custom Save Button injection -->
21
- <component
22
- v-if="editSaveButtonInjection"
23
- :is="getCustomComponent(editSaveButtonInjection)"
24
- :meta="editSaveButtonInjection.meta"
25
- :record="editableRecord"
26
- :resource="coreStore.resource"
27
- :adminUser="coreStore.adminUser"
28
- :saving="saving"
29
- :validating="validating"
30
- :isValid="isValid"
31
- :disabled="saving || (validating && !isValid)"
32
- :saveRecord="saveRecord"
33
- />
34
-
35
- <!-- Default Save Button fallback -->
36
19
  <button
37
- v-else
38
20
  @click="() => saveRecord()"
39
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"
40
22
  :disabled="saving || (validating && !isValid)"
@@ -94,17 +76,18 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
94
76
  import { useCoreStore } from '@/stores/core';
95
77
  import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
96
78
  import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
97
- import { computed, onMounted, ref, type Ref, nextTick } from 'vue';
79
+ import { computed, onMounted, onBeforeMount, ref, type Ref, nextTick, watch } from 'vue';
98
80
  import { useRoute, useRouter } from 'vue-router';
99
81
  import { showErrorTost } from '@/composables/useFrontendApi';
100
82
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
101
- import adminforth from '@/adminforth';
83
+ import { useAdminforth } from '@/adminforth';
102
84
  import { useI18n } from 'vue-i18n';
103
85
  import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
104
86
  import type { AdminForthResourceColumn } from '@/types/Back';
105
87
 
106
88
  const { t } = useI18n();
107
89
  const coreStore = useCoreStore();
90
+ const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
108
91
 
109
92
  const isValid = ref(false);
110
93
  const validating = ref(false);
@@ -118,14 +101,11 @@ const saving = ref(false);
118
101
 
119
102
  const record: Ref<Record<string, any>> = ref({});
120
103
 
121
- const resourceFormRef = ref<InstanceType<typeof ResourceForm> | null>(null);
104
+ watch(record, (newVal) => {
105
+ console.log('Record updated:', newVal);
106
+ }, { deep: true });
122
107
 
123
- const editSaveButtonInjection = computed<AdminForthComponentDeclarationFull | null>(() => {
124
- const raw: any = coreStore.resourceOptions?.pageInjections?.edit?.saveButton as any;
125
- if (!raw) return null;
126
- const item = Array.isArray(raw) ? raw[0] : raw;
127
- return item as AdminForthComponentDeclarationFull;
128
- });
108
+ const resourceFormRef = ref<InstanceType<typeof ResourceForm> | null>(null);
129
109
 
130
110
  async function onUpdateRecord(newRecord: Record<string, any>) {
131
111
  record.value = newRecord;
@@ -148,6 +128,10 @@ const editableRecord = computed(() => {
148
128
  return newRecord;
149
129
  })
150
130
 
131
+ onBeforeMount(() => {
132
+ clearSaveInterceptors(route.params.resourceId as string);
133
+ });
134
+
151
135
  onMounted(async () => {
152
136
  loading.value = true;
153
137
 
@@ -169,7 +153,7 @@ onMounted(async () => {
169
153
  loading.value = false;
170
154
  });
171
155
 
172
- async function saveRecord(opts?: { confirmationResult?: any }) {
156
+ async function saveRecord() {
173
157
  if (!isValid.value) {
174
158
  validating.value = true;
175
159
  await nextTick();
@@ -180,6 +164,18 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
180
164
  }
181
165
 
182
166
  saving.value = true;
167
+ const interceptorsResult = await runSaveInterceptors({
168
+ action: 'edit',
169
+ values: record.value,
170
+ resource: coreStore.resource,
171
+ resourceId: route.params.resourceId as string,
172
+ });
173
+ if (!interceptorsResult.ok) {
174
+ saving.value = false;
175
+ if (interceptorsResult.error) showErrorTost(interceptorsResult.error);
176
+ return;
177
+ }
178
+ const interceptorConfirmationResult = interceptorsResult.extra?.confirmationResult;
183
179
  const updates: Record<string, any> = {};
184
180
  for (const key in record.value) {
185
181
  let columnIsUpdated = false;
@@ -212,14 +208,14 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
212
208
  recordId: route.params.primaryKey,
213
209
  record: updates,
214
210
  meta: {
215
- ...(opts?.confirmationResult ? { confirmationResult: opts.confirmationResult } : {}),
211
+ ...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
216
212
  },
217
213
  },
218
214
  });
219
215
  if (resp.error && resp.error !== 'Operation aborted by hook') {
220
216
  showErrorTost(resp.error);
221
217
  } else {
222
- adminforth.alert({
218
+ alert({
223
219
  message: t('Record updated successfully'),
224
220
  variant: 'success',
225
221
  timeout: 400000
@@ -238,7 +234,7 @@ function scrollToInvalidField() {
238
234
  }
239
235
  }
240
236
  const errorMessage = t('Failed to save. Please fix errors for the following fields:') + '<ul class="mt-2 list-disc list-inside">' + columnsWithErrors.map(c => `<li><strong>${c.column.label || c.column.name}</strong>: ${c.error}</li>`).join('') + '</ul>';
241
- adminforth.alert({
237
+ alert({
242
238
  messageHtml: errorMessage,
243
239
  variant: 'danger'
244
240
  });
@@ -119,7 +119,7 @@
119
119
  :adminUser="coreStore.adminUser"
120
120
  />
121
121
  <ResourceListTableVirtual
122
- v-if="isVirtualScrollEnabled"
122
+ v-if="isVirtualScrollEnabled && !coreStore.isResourceFetching"
123
123
  :resource="coreStore.resource"
124
124
  :rows="rows"
125
125
  :page="page"
@@ -143,6 +143,11 @@
143
143
  ? [coreStore.resourceOptions.pageInjections.list.tableBodyStart]
144
144
  : []
145
145
  "
146
+ :customActionIconsThreeDotsMenuItems="Array.isArray(coreStore.resourceOptions?.pageInjections?.list?.customActionIconsThreeDotsMenuItems)
147
+ ? coreStore.resourceOptions.pageInjections.list.customActionIconsThreeDotsMenuItems
148
+ : coreStore.resourceOptions?.pageInjections?.list?.customActionIconsThreeDotsMenuItems
149
+ ? [coreStore.resourceOptions.pageInjections.list.customActionIconsThreeDotsMenuItems]
150
+ : []"
146
151
  :tableRowReplaceInjection="Array.isArray(coreStore.resourceOptions?.pageInjections?.list?.tableRowReplace)
147
152
  ? coreStore.resourceOptions.pageInjections.list.tableRowReplace[0]
148
153
  : coreStore.resourceOptions?.pageInjections?.list?.tableRowReplace || undefined"
@@ -152,7 +157,7 @@
152
157
  />
153
158
 
154
159
  <ResourceListTable
155
- v-else
160
+ v-else-if="!coreStore.isResourceFetching"
156
161
  :resource="coreStore.resource"
157
162
  :rows="rows"
158
163
  :page="page"
@@ -176,6 +181,11 @@
176
181
  ? [coreStore.resourceOptions.pageInjections.list.tableBodyStart]
177
182
  : []
178
183
  "
184
+ :customActionIconsThreeDotsMenuItems="Array.isArray(coreStore.resourceOptions?.pageInjections?.list?.customActionIconsThreeDotsMenuItems)
185
+ ? coreStore.resourceOptions.pageInjections.list.customActionIconsThreeDotsMenuItems
186
+ : coreStore.resourceOptions?.pageInjections?.list?.customActionIconsThreeDotsMenuItems
187
+ ? [coreStore.resourceOptions.pageInjections.list.customActionIconsThreeDotsMenuItems]
188
+ : []"
179
189
  :tableRowReplaceInjection="Array.isArray(coreStore.resourceOptions?.pageInjections?.list?.tableRowReplace)
180
190
  ? coreStore.resourceOptions.pageInjections.list.tableRowReplace[0]
181
191
  : coreStore.resourceOptions?.pageInjections?.list?.tableRowReplace || undefined"
@@ -215,10 +225,10 @@ import {
215
225
  } from '@iconify-prerendered/vue-flowbite';
216
226
 
217
227
  import Filters from '@/components/Filters.vue';
218
- import adminforth from '@/adminforth';
228
+ import { useAdminforth } from '@/adminforth';
219
229
 
220
230
  const filtersShow = ref(false);
221
-
231
+ const { confirm, alert, list } = useAdminforth();
222
232
  const coreStore = useCoreStore();
223
233
  const filtersStore = useFiltersStore();
224
234
 
@@ -345,7 +355,7 @@ async function refreshExistingList(pk?: any) {
345
355
  async function startBulkAction(actionId: string) {
346
356
  const action = coreStore.resource?.options?.bulkActions?.find(a => a.id === actionId);
347
357
  if (action?.confirm) {
348
- const confirmed = await adminforth.confirm({
358
+ const confirmed = await confirm({
349
359
  message: action.confirm,
350
360
  });
351
361
  if (!confirmed) {
@@ -370,7 +380,7 @@ async function startBulkAction(actionId: string) {
370
380
  await getList();
371
381
 
372
382
  if (data.successMessage) {
373
- adminforth.alert({
383
+ alert({
374
384
  message: data.successMessage,
375
385
  variant: 'success'
376
386
  });
@@ -454,7 +464,7 @@ async function init() {
454
464
  clearAutoRefresher();
455
465
  if (coreStore.resource!.options?.listRowsAutoRefreshSeconds) {
456
466
  listAutorefresher = setInterval(async () => {
457
- await adminforth.list.silentRefresh();
467
+ await list.silentRefresh();
458
468
  }, coreStore.resource!.options.listRowsAutoRefreshSeconds * 1000);
459
469
  }
460
470
  }
@@ -464,7 +474,7 @@ watch([page, sort, () => filtersStore.filters], async () => {
464
474
  await getList();
465
475
  }, { deep: true });
466
476
 
467
- adminforth.list.refresh = async () => {
477
+ list.refresh = async () => {
468
478
  const result = await getList();
469
479
 
470
480
  if (!result) {
@@ -478,11 +488,11 @@ adminforth.list.refresh = async () => {
478
488
  return {};
479
489
  };
480
490
 
481
- adminforth.list.silentRefresh = async () => {
491
+ list.silentRefresh = async () => {
482
492
  return await refreshExistingList();
483
493
  }
484
494
 
485
- adminforth.list.silentRefreshRow = async (pk: any) => {
495
+ list.silentRefreshRow = async (pk: any) => {
486
496
  return await refreshExistingList(pk);
487
497
  }
488
498
 
@@ -28,7 +28,7 @@ import { useRouter } from 'vue-router';
28
28
  import { useCoreStore } from '@/stores/core';
29
29
  import { getCustomComponent, getIcon } from '@/utils';
30
30
  import { Dropdown } from 'flowbite';
31
- import adminforth from '@/adminforth';
31
+ import { useAdminforth } from '@/adminforth';
32
32
  import { VerticalTabs } from '@/afcl'
33
33
  import { useRoute } from 'vue-router'
34
34
 
@@ -36,6 +36,7 @@ const route = useRoute()
36
36
  const coreStore = useCoreStore();
37
37
  const router = useRouter();
38
38
 
39
+ let { closeUserMenuDropdown } = useAdminforth();
39
40
  const routerIsReady = ref(false);
40
41
  const loginRedirectCheckIsReady = ref(false);
41
42
  const dropdownUserButton = ref<HTMLElement | null>(null);
@@ -58,7 +59,7 @@ watch(dropdownUserButton, (el) => {
58
59
  document.querySelector('#dropdown-user') as HTMLElement,
59
60
  document.querySelector('[data-dropdown-toggle="dropdown-user"]') as HTMLElement,
60
61
  );
61
- adminforth.closeUserMenuDropdown = () => dd.hide();
62
+ closeUserMenuDropdown = () => dd.hide();
62
63
  }
63
64
  });
64
65
 
@@ -144,16 +144,17 @@ import {callAdminForthApi} from '@/utils';
144
144
  import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
145
145
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
146
146
  import ShowTable from '@/components/ShowTable.vue';
147
- import adminforth from "@/adminforth";
147
+ import { useAdminforth } from '@/adminforth';
148
148
  import { useI18n } from 'vue-i18n';
149
149
  import { getIcon } from '@/utils';
150
- import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
150
+ import { type AdminForthComponentDeclarationFull, type AdminForthResourceColumnCommon, type FieldGroup } from '@/types/Common.js';
151
151
  import CallActionWrapper from '@/components/CallActionWrapper.vue'
152
152
 
153
153
  const route = useRoute();
154
154
  const router = useRouter();
155
155
  const loading = ref(true);
156
156
  const { t } = useI18n();
157
+ const { confirm, alert, show } = useAdminforth();
157
158
  const coreStore = useCoreStore();
158
159
 
159
160
  const actionLoadingStates = ref<Record<string, boolean>>({});
@@ -206,7 +207,7 @@ const allColumns = computed(() => {
206
207
 
207
208
  const otherColumns = computed(() => {
208
209
  const groupedColumnNames = new Set(
209
- groups.value.flatMap(group => group.columns.map((col: AdminForthResourceColumnCommon) => col.name))
210
+ groups.value.flatMap(group => group.columns?.map((col: AdminForthResourceColumnCommon) => col.name))
210
211
  );
211
212
 
212
213
  return coreStore.resource?.columns.filter(
@@ -215,7 +216,7 @@ const otherColumns = computed(() => {
215
216
  });
216
217
 
217
218
  async function deleteRecord() {
218
- const data = await adminforth.confirm({
219
+ const data = await confirm({
219
220
  message: t('Are you sure you want to delete this item?'),
220
221
  yes: t('Delete'),
221
222
  no: t('Cancel'),
@@ -282,7 +283,7 @@ async function startCustomAction(actionId: string, extra: any) {
282
283
  });
283
284
 
284
285
  if (data.successMessage) {
285
- adminforth.alert({
286
+ alert({
286
287
  message: data.successMessage,
287
288
  variant: 'success'
288
289
  });
@@ -294,7 +295,7 @@ async function startCustomAction(actionId: string, extra: any) {
294
295
  }
295
296
  }
296
297
 
297
- adminforth.show.refresh = () => {
298
+ show.refresh = () => {
298
299
  (async () => {
299
300
  try {
300
301
  loading.value = true;