adminforth 2.4.0-next.31 → 2.4.0-next.310

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 (176) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/createApp/templates/api.ts.hbs +10 -0
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +12 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
  7. package/commands/createApp/templates/schema.prisma.hbs +0 -1
  8. package/commands/createApp/utils.js +10 -0
  9. package/commands/createCustomComponent/configLoader.js +17 -4
  10. package/commands/createCustomComponent/main.js +13 -7
  11. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  12. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
  13. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  14. package/commands/createPlugin/templates/package.json.hbs +1 -1
  15. package/commands/generateModels.js +30 -22
  16. package/dist/auth.d.ts +9 -1
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +21 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/dataConnectors/baseConnector.d.ts +1 -1
  21. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  22. package/dist/dataConnectors/baseConnector.js +69 -17
  23. package/dist/dataConnectors/baseConnector.js.map +1 -1
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -0
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  28. package/dist/dataConnectors/mongo.js +50 -15
  29. package/dist/dataConnectors/mongo.js.map +1 -1
  30. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  31. package/dist/dataConnectors/mysql.js +11 -0
  32. package/dist/dataConnectors/mysql.js.map +1 -1
  33. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  34. package/dist/dataConnectors/postgres.js +43 -14
  35. package/dist/dataConnectors/postgres.js.map +1 -1
  36. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  37. package/dist/dataConnectors/sqlite.js +11 -0
  38. package/dist/dataConnectors/sqlite.js.map +1 -1
  39. package/dist/index.d.ts +12 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +45 -22
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/codeInjector.d.ts +2 -0
  44. package/dist/modules/codeInjector.d.ts.map +1 -1
  45. package/dist/modules/codeInjector.js +62 -6
  46. package/dist/modules/codeInjector.js.map +1 -1
  47. package/dist/modules/configValidator.d.ts +6 -0
  48. package/dist/modules/configValidator.d.ts.map +1 -1
  49. package/dist/modules/configValidator.js +202 -25
  50. package/dist/modules/configValidator.js.map +1 -1
  51. package/dist/modules/restApi.d.ts +1 -1
  52. package/dist/modules/restApi.d.ts.map +1 -1
  53. package/dist/modules/restApi.js +172 -31
  54. package/dist/modules/restApi.js.map +1 -1
  55. package/dist/modules/styles.d.ts +499 -13
  56. package/dist/modules/styles.d.ts.map +1 -1
  57. package/dist/modules/styles.js +555 -31
  58. package/dist/modules/styles.js.map +1 -1
  59. package/dist/modules/utils.d.ts +7 -15
  60. package/dist/modules/utils.d.ts.map +1 -1
  61. package/dist/modules/utils.js +45 -68
  62. package/dist/modules/utils.js.map +1 -1
  63. package/dist/servers/express.d.ts +5 -0
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +40 -1
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/index.html +1 -1
  68. package/dist/spa/package-lock.json +1208 -708
  69. package/dist/spa/package.json +34 -34
  70. package/dist/spa/src/App.vue +59 -174
  71. package/dist/spa/src/adminforth.ts +42 -18
  72. package/dist/spa/src/afcl/AreaChart.vue +0 -1
  73. package/dist/spa/src/afcl/BarChart.vue +2 -2
  74. package/dist/spa/src/afcl/Button.vue +6 -6
  75. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  76. package/dist/spa/src/afcl/Card.vue +25 -0
  77. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  78. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  79. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  80. package/dist/spa/src/afcl/Dialog.vue +47 -27
  81. package/dist/spa/src/afcl/Dropzone.vue +127 -48
  82. package/dist/spa/src/afcl/Input.vue +14 -6
  83. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  84. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  85. package/dist/spa/src/afcl/PieChart.vue +5 -5
  86. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  87. package/dist/spa/src/afcl/Select.vue +82 -34
  88. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  89. package/dist/spa/src/afcl/Table.vue +315 -73
  90. package/dist/spa/src/afcl/Textarea.vue +31 -0
  91. package/dist/spa/src/afcl/Toggle.vue +32 -0
  92. package/dist/spa/src/afcl/Tooltip.vue +28 -18
  93. package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
  94. package/dist/spa/src/afcl/index.ts +6 -3
  95. package/dist/spa/src/components/AcceptModal.vue +48 -14
  96. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  97. package/dist/spa/src/components/CallActionWrapper.vue +15 -0
  98. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  99. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  100. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  101. package/dist/spa/src/components/CustomRangePicker.vue +37 -21
  102. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  103. package/dist/spa/src/components/Filters.vue +195 -132
  104. package/dist/spa/src/components/GroupsTable.vue +9 -8
  105. package/dist/spa/src/components/MenuLink.vue +90 -23
  106. package/dist/spa/src/components/ResourceForm.vue +94 -51
  107. package/dist/spa/src/components/ResourceListTable.vue +115 -85
  108. package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
  109. package/dist/spa/src/components/ShowTable.vue +21 -15
  110. package/dist/spa/src/components/Sidebar.vue +470 -0
  111. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  112. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  113. package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
  114. package/dist/spa/src/components/Toast.vue +40 -29
  115. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  116. package/dist/spa/src/components/ValueRenderer.vue +44 -17
  117. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  118. package/dist/spa/src/i18n.ts +5 -3
  119. package/dist/spa/src/main.ts +1 -1
  120. package/dist/spa/src/renderers/CompactField.vue +1 -1
  121. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  122. package/dist/spa/src/router/index.ts +8 -0
  123. package/dist/spa/src/shims-vue.d.ts +5 -0
  124. package/dist/spa/src/spa_types/core.ts +13 -1
  125. package/dist/spa/src/stores/core.ts +13 -1
  126. package/dist/spa/src/stores/filters.ts +33 -2
  127. package/dist/spa/src/stores/modal.ts +6 -1
  128. package/dist/spa/src/stores/toast.ts +22 -3
  129. package/dist/spa/src/types/Back.ts +163 -23
  130. package/dist/spa/src/types/Common.ts +91 -32
  131. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  132. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  133. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
  134. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  135. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  136. package/dist/spa/src/types/adapters/index.ts +8 -0
  137. package/dist/spa/src/utils.ts +291 -11
  138. package/dist/spa/src/views/CreateView.vue +63 -21
  139. package/dist/spa/src/views/EditView.vue +55 -22
  140. package/dist/spa/src/views/ListView.vue +144 -87
  141. package/dist/spa/src/views/LoginView.vue +26 -35
  142. package/dist/spa/src/views/ResourceParent.vue +2 -2
  143. package/dist/spa/src/views/SettingsView.vue +121 -0
  144. package/dist/spa/src/views/ShowView.vue +83 -53
  145. package/dist/spa/src/websocket.ts +6 -1
  146. package/dist/spa/tsconfig.app.json +1 -1
  147. package/dist/spa/vite.config.ts +45 -2
  148. package/dist/types/Back.d.ts +146 -14
  149. package/dist/types/Back.d.ts.map +1 -1
  150. package/dist/types/Back.js +15 -0
  151. package/dist/types/Back.js.map +1 -1
  152. package/dist/types/Common.d.ts +106 -29
  153. package/dist/types/Common.d.ts.map +1 -1
  154. package/dist/types/Common.js.map +1 -1
  155. package/dist/types/FrontendAPI.d.ts +31 -3
  156. package/dist/types/FrontendAPI.d.ts.map +1 -1
  157. package/dist/types/FrontendAPI.js.map +1 -1
  158. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  159. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  160. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  161. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  162. package/dist/types/adapters/EmailAdapter.d.ts +1 -1
  163. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  164. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  165. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  166. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  167. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  168. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  169. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  170. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  171. package/dist/types/adapters/index.d.ts +9 -0
  172. package/dist/types/adapters/index.d.ts.map +1 -0
  173. package/dist/types/adapters/index.js +2 -0
  174. package/dist/types/adapters/index.js.map +1 -0
  175. package/package.json +4 -2
  176. package/dist/spa/src/types/adapters/index.js +0 -5
@@ -63,14 +63,14 @@
63
63
 
64
64
  <script setup lang="ts">
65
65
 
66
- import { applyRegexValidation, callAdminForthApi} from '@/utils';
66
+ import { applyRegexValidation, callAdminForthApi, loadMoreForeignOptions, searchForeignOptions, createSearchInputHandlers, checkShowIf } from '@/utils';
67
67
  import { computedAsync } from '@vueuse/core';
68
- import { computed, onMounted, ref, watch } from 'vue';
68
+ import { computed, onMounted, reactive, ref, watch, provide, type Ref } from 'vue';
69
69
  import { useRouter, useRoute } from 'vue-router';
70
70
  import { useCoreStore } from "@/stores/core";
71
71
  import GroupsTable from '@/components/GroupsTable.vue';
72
72
  import { useI18n } from 'vue-i18n';
73
- import { type AdminForthResourceCommon } from '@/types/Common';
73
+ import { type AdminForthResourceColumnCommon, type AdminForthResourceCommon } from '@/types/Common';
74
74
 
75
75
  const { t } = useI18n();
76
76
 
@@ -91,11 +91,16 @@ const mode = computed(() => route.name === 'resource-create' ? 'create' : 'edit'
91
91
 
92
92
  const emit = defineEmits(['update:record', 'update:isValid']);
93
93
 
94
- const currentValues = ref(null);
95
- const customComponentsInValidity = ref({});
96
- const customComponentsEmptiness = ref({});
94
+ const currentValues = ref<any | any[] | null>(null);
95
+ const customComponentsInValidity: Ref<Record<string, AdminForthResourceColumnCommon>> = ref({});
96
+ const customComponentsEmptiness: Ref<Record<string, AdminForthResourceColumnCommon>> = ref({});
97
97
 
98
- const columnError = (column) => {
98
+ const columnOptions = ref<Record<string, any[]>>({});
99
+ const columnLoadingState = reactive<Record<string, { loading: boolean; hasMore: boolean }>>({});
100
+ const columnOffsets = reactive<Record<string, number>>({});
101
+ const columnEmptyResultsCount = reactive<Record<string, number>>({});
102
+
103
+ const columnError = (column: AdminForthResourceColumnCommon) => {
99
104
  const val = computed(() => {
100
105
  if (!currentValues.value) {
101
106
  return null;
@@ -104,7 +109,7 @@ const columnError = (column) => {
104
109
  return customComponentsInValidity.value?.[column.name];
105
110
  }
106
111
 
107
- if ( column.required[mode.value] ) {
112
+ if ( column.required?.[mode.value] ) {
108
113
  const naturalEmptiness = currentValues.value[column.name] === undefined ||
109
114
  currentValues.value[column.name] === null ||
110
115
  currentValues.value[column.name] === '' ||
@@ -131,17 +136,23 @@ const columnError = (column) => {
131
136
  }
132
137
  } else if (column.isArray?.enabled) {
133
138
  if (!column.isArray.allowDuplicateItems) {
134
- if (currentValues.value[column.name].filter((value, index, self) => self.indexOf(value) !== index).length > 0) {
139
+ if (currentValues.value[column.name].filter((value: any, index: any, self: any) => self.indexOf(value) !== index).length > 0) {
135
140
  return t('Array cannot contain duplicate items');
136
141
  }
137
142
  }
138
143
 
139
- return currentValues.value[column.name] && currentValues.value[column.name].reduce((error, item) => {
140
- return error || validateValue(column.isArray.itemType, item, column) ||
141
- (item === null || !item.toString() ? t('Array cannot contain empty items') : null);
144
+ return currentValues.value[column.name] && currentValues.value[column.name].reduce((error: any, item: any) => {
145
+ if (column.isArray) {
146
+ return error || validateValue(column.isArray.itemType, item, column) ||
147
+ (item === null || !item.toString() ? t('Array cannot contain empty items') : null);
148
+ } else {
149
+ return error;
150
+ }
142
151
  }, null);
143
152
  } else {
144
- return validateValue(column.type, currentValues.value[column.name], column);
153
+ if (column.type) {
154
+ return validateValue(column.type, currentValues.value[column.name], column);
155
+ }
145
156
  }
146
157
 
147
158
  return null;
@@ -149,7 +160,7 @@ const columnError = (column) => {
149
160
  return val.value;
150
161
  };
151
162
 
152
- const validateValue = (type, value, column) => {
163
+ const validateValue = (type: string, value: any, column: AdminForthResourceColumnCommon) => {
153
164
  if (type === 'string' || type === 'text') {
154
165
  if (column.maxLength && value?.length > column.maxLength) {
155
166
  return t('This field must be shorter than {maxLength} characters', { maxLength: column.maxLength });
@@ -157,7 +168,7 @@ const validateValue = (type, value, column) => {
157
168
 
158
169
  if (column.minLength && value?.length < column.minLength) {
159
170
  // if column.required[mode.value] is false, then we check if the field is empty
160
- let needToCheckEmpty = column.required[mode.value] || value?.length > 0;
171
+ let needToCheckEmpty = column.required?.[mode.value] || value?.length > 0;
161
172
  if (!needToCheckEmpty) {
162
173
  return null;
163
174
  }
@@ -186,10 +197,10 @@ const validateValue = (type, value, column) => {
186
197
  };
187
198
 
188
199
 
189
- const setCurrentValue = (key, value, index=null) => {
200
+ const setCurrentValue = (key: any, value: any, index = null) => {
190
201
  const col = props.resource.columns.find((column) => column.name === key);
191
202
  // if field is an array, we need to update the array or individual element
192
- if (col.type === 'json' && col.isArray?.enabled) {
203
+ if (((col?.type && col.type === 'json') && (col?.type && col.isArray?.enabled))) {
193
204
  if (index === null) {
194
205
  currentValues.value[key] = value;
195
206
  } else if (index === currentValues.value[key].length) {
@@ -204,12 +215,12 @@ const setCurrentValue = (key, value, index=null) => {
204
215
  } else {
205
216
  currentValues.value[key][index] = value;
206
217
  }
207
- if (['text', 'richtext', 'string'].includes(col.isArray.itemType) && col.enforceLowerCase) {
218
+ if (col?.isArray && ['text', 'richtext', 'string'].includes(col.isArray.itemType) && col.enforceLowerCase) {
208
219
  currentValues.value[key][index] = currentValues.value[key][index].toLowerCase();
209
220
  }
210
221
  }
211
222
  } else {
212
- if (['integer', 'float', 'decimal'].includes(col.type)) {
223
+ if (col?.type && ['integer', 'float', 'decimal'].includes(col.type)) {
213
224
  if (value || value === 0) {
214
225
  currentValues.value[key] = +value;
215
226
  } else {
@@ -218,7 +229,7 @@ const setCurrentValue = (key, value, index=null) => {
218
229
  } else {
219
230
  currentValues.value[key] = value;
220
231
  }
221
- if (['text', 'richtext', 'string'].includes(col.type) && col.enforceLowerCase) {
232
+ if (col?.type && ['text', 'richtext', 'string'].includes(col?.type) && col.enforceLowerCase) {
222
233
  currentValues.value[key] = currentValues.value[key].toLowerCase();
223
234
  }
224
235
  }
@@ -239,11 +250,28 @@ const setCurrentValue = (key, value, index=null) => {
239
250
  emit('update:record', up);
240
251
  };
241
252
 
253
+ watch(() => props.resource.columns, async (newColumns) => {
254
+ if (!newColumns) return;
255
+
256
+ for (const column of newColumns) {
257
+ if (column.foreignResource) {
258
+ if (!columnOptions.value[column.name]) {
259
+ columnOptions.value[column.name] = [];
260
+ columnLoadingState[column.name] = { loading: false, hasMore: true };
261
+ columnOffsets[column.name] = 0;
262
+ columnEmptyResultsCount[column.name] = 0;
263
+
264
+ await loadMoreOptions(column.name);
265
+ }
266
+ }
267
+ }
268
+ }, { immediate: true });
269
+
242
270
  onMounted(() => {
243
271
  currentValues.value = Object.assign({}, props.record);
244
272
  // json values should transform to string
245
273
  props.resource.columns.forEach((column) => {
246
- if (column.type === 'json') {
274
+ if (column.type === 'json' && currentValues.value) {
247
275
  if (column.isArray?.enabled) {
248
276
  // if value is null or undefined, we should set it to empty array
249
277
  if (!currentValues.value[column.name]) {
@@ -266,33 +294,35 @@ onMounted(() => {
266
294
  emit('update:isValid', isValid.value);
267
295
  });
268
296
 
269
- const columnOptions = computedAsync(async () => {
270
- return (await Promise.all(
271
- Object.values(props.resource.columns).map(async (column) => {
272
- if (column.foreignResource) {
273
- const list = await callAdminForthApi({
274
- method: 'POST',
275
- path: `/get_resource_foreign_data`,
276
- body: {
277
- resourceId: router.currentRoute.value.params.resourceId,
278
- column: column.name,
279
- limit: 1000,
280
- offset: 0,
281
- },
282
- });
283
-
284
- if (!column.required[props.source] && !column.isArray?.enabled) list.items.push({ value: null, label: column.foreignResource.unsetLabel });
285
-
286
- return { [column.name]: list.items };
287
- }
288
- })
289
- )).reduce((acc, val) => Object.assign(acc, val), {})
290
-
291
- }, {});
297
+ async function loadMoreOptions(columnName: string, searchTerm = '') {
298
+ return loadMoreForeignOptions({
299
+ columnName,
300
+ searchTerm,
301
+ columns: props.resource.columns,
302
+ resourceId: router.currentRoute.value.params.resourceId as string,
303
+ columnOptions,
304
+ columnLoadingState,
305
+ columnOffsets,
306
+ columnEmptyResultsCount
307
+ });
308
+ }
309
+
310
+ async function searchOptions(columnName: string, searchTerm: string) {
311
+ return searchForeignOptions({
312
+ columnName,
313
+ searchTerm,
314
+ columns: props.resource.columns,
315
+ resourceId: router.currentRoute.value.params.resourceId as string,
316
+ columnOptions,
317
+ columnLoadingState,
318
+ columnOffsets,
319
+ columnEmptyResultsCount
320
+ });
321
+ }
292
322
 
293
323
 
294
324
  const editableColumns = computed(() => {
295
- return props.resource?.columns?.filter(column => column.showIn[mode.value]);
325
+ return props.resource?.columns?.filter(column => column.showIn?.[mode.value] && (currentValues.value ? checkShowIf(column, currentValues.value) : true));
296
326
  });
297
327
 
298
328
  const isValid = computed(() => {
@@ -302,12 +332,14 @@ const isValid = computed(() => {
302
332
 
303
333
  const groups = computed(() => {
304
334
  let fieldGroupType;
305
- if (mode.value === 'edit' && coreStore.resource.options?.editFieldGroups !== undefined) {
306
- fieldGroupType = coreStore.resource.options.editFieldGroups;
307
- } else if (mode.value === 'create' && coreStore.resource.options?.createFieldGroups !== undefined) {
308
- fieldGroupType = coreStore.resource.options.createFieldGroups;
309
- } else {
310
- fieldGroupType = coreStore.resource.options?.fieldGroups;
335
+ if(coreStore.resource){
336
+ if (mode.value === 'edit' && coreStore.resource.options?.editFieldGroups !== undefined) {
337
+ fieldGroupType = coreStore.resource.options.editFieldGroups;
338
+ } else if (mode.value === 'create' && coreStore.resource.options?.createFieldGroups !== undefined) {
339
+ fieldGroupType = coreStore.resource.options.createFieldGroups;
340
+ } else {
341
+ fieldGroupType = coreStore.resource.options?.fieldGroups;
342
+ }
311
343
  }
312
344
  return fieldGroupType ?? [];
313
345
  });
@@ -330,6 +362,17 @@ const getOtherColumns = () => {
330
362
 
331
363
  const otherColumns = getOtherColumns();
332
364
 
365
+ const onSearchInput = computed(() => {
366
+ return createSearchInputHandlers(
367
+ props.resource.columns,
368
+ searchOptions
369
+ );
370
+ });
371
+
372
+ provide('columnLoadingState', columnLoadingState);
373
+ provide('onSearchInput', onSearchInput);
374
+ provide('loadMoreOptions', loadMoreOptions);
375
+
333
376
  watch(() => isValid.value, (value) => {
334
377
  emit('update:isValid', value);
335
378
  });