adminforth 1.5.6-next.8 → 1.5.6

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 (47) hide show
  1. package/dist/adapters/completion-adapter-open-ai-chat-gpt/index.d.ts +11 -0
  2. package/dist/adapters/completion-adapter-open-ai-chat-gpt/index.d.ts.map +1 -0
  3. package/dist/adapters/completion-adapter-open-ai-chat-gpt/index.js +36 -0
  4. package/dist/adapters/completion-adapter-open-ai-chat-gpt/index.js.map +1 -0
  5. package/dist/adapters/completion-adapter-open-ai-chat-gpt/types.d.ts +23 -0
  6. package/dist/adapters/completion-adapter-open-ai-chat-gpt/types.d.ts.map +1 -0
  7. package/dist/adapters/completion-adapter-open-ai-chat-gpt/types.js +2 -0
  8. package/dist/adapters/completion-adapter-open-ai-chat-gpt/types.js.map +1 -0
  9. package/dist/adapters/email-adapter-aws-ses/index.d.ts +11 -0
  10. package/dist/adapters/email-adapter-aws-ses/index.d.ts.map +1 -0
  11. package/dist/adapters/email-adapter-aws-ses/index.js +64 -0
  12. package/dist/adapters/email-adapter-aws-ses/index.js.map +1 -0
  13. package/dist/adapters/email-adapter-aws-ses/types.d.ts +14 -0
  14. package/dist/adapters/email-adapter-aws-ses/types.d.ts.map +1 -0
  15. package/dist/adapters/email-adapter-aws-ses/types.js +2 -0
  16. package/dist/adapters/email-adapter-aws-ses/types.js.map +1 -0
  17. package/dist/index.d.ts +8 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +18 -10
  20. package/dist/index.js.map +1 -1
  21. package/dist/modules/restApi.d.ts +1 -1
  22. package/dist/modules/restApi.d.ts.map +1 -1
  23. package/dist/modules/restApi.js.map +1 -1
  24. package/dist/spa/package-lock.json +65 -0
  25. package/dist/spa/package.json +1 -0
  26. package/dist/spa/src/components/GroupsTable.vue +16 -7
  27. package/dist/spa/src/components/ResourceForm.vue +15 -6
  28. package/dist/spa/src/components/ShowTable.vue +66 -0
  29. package/dist/spa/src/main.ts +5 -1
  30. package/dist/spa/src/types/Adapters.ts +17 -0
  31. package/dist/spa/src/types/Back.ts +1 -2
  32. package/dist/spa/src/types/Common.ts +14 -2
  33. package/dist/spa/src/views/LoginView.vue +8 -9
  34. package/dist/spa/src/views/ShowView.vue +62 -50
  35. package/dist/spa/tailwind.config.js +3 -1
  36. package/dist/spa/vite.config.ts +15 -1
  37. package/dist/types/Adapters.d.ts +10 -0
  38. package/dist/types/Adapters.d.ts.map +1 -0
  39. package/dist/types/Adapters.js +2 -0
  40. package/dist/types/Adapters.js.map +1 -0
  41. package/dist/types/Back.d.ts +1 -1
  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 +14 -2
  45. package/dist/types/Common.d.ts.map +1 -1
  46. package/dist/types/Common.js.map +1 -1
  47. package/package.json +1 -1
@@ -22,6 +22,7 @@
22
22
  "uuid": "^10.0.0",
23
23
  "vue": "^3.5.12",
24
24
  "vue-diff": "^1.2.4",
25
+ "vue-i18n": "^10.0.5",
25
26
  "vue-router": "^4.3.0",
26
27
  "vue-slider-component": "^4.1.0-beta.7"
27
28
  },
@@ -618,6 +619,50 @@
618
619
  "vue": "^3.0.0"
619
620
  }
620
621
  },
622
+ "node_modules/@intlify/core-base": {
623
+ "version": "10.0.5",
624
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz",
625
+ "integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==",
626
+ "license": "MIT",
627
+ "dependencies": {
628
+ "@intlify/message-compiler": "10.0.5",
629
+ "@intlify/shared": "10.0.5"
630
+ },
631
+ "engines": {
632
+ "node": ">= 16"
633
+ },
634
+ "funding": {
635
+ "url": "https://github.com/sponsors/kazupon"
636
+ }
637
+ },
638
+ "node_modules/@intlify/message-compiler": {
639
+ "version": "10.0.5",
640
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz",
641
+ "integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==",
642
+ "license": "MIT",
643
+ "dependencies": {
644
+ "@intlify/shared": "10.0.5",
645
+ "source-map-js": "^1.0.2"
646
+ },
647
+ "engines": {
648
+ "node": ">= 16"
649
+ },
650
+ "funding": {
651
+ "url": "https://github.com/sponsors/kazupon"
652
+ }
653
+ },
654
+ "node_modules/@intlify/shared": {
655
+ "version": "10.0.5",
656
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz",
657
+ "integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==",
658
+ "license": "MIT",
659
+ "engines": {
660
+ "node": ">= 16"
661
+ },
662
+ "funding": {
663
+ "url": "https://github.com/sponsors/kazupon"
664
+ }
665
+ },
621
666
  "node_modules/@isaacs/cliui": {
622
667
  "version": "8.0.2",
623
668
  "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -4560,6 +4605,26 @@
4560
4605
  "eslint": ">=6.0.0"
4561
4606
  }
4562
4607
  },
4608
+ "node_modules/vue-i18n": {
4609
+ "version": "10.0.5",
4610
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz",
4611
+ "integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==",
4612
+ "license": "MIT",
4613
+ "dependencies": {
4614
+ "@intlify/core-base": "10.0.5",
4615
+ "@intlify/shared": "10.0.5",
4616
+ "@vue/devtools-api": "^6.5.0"
4617
+ },
4618
+ "engines": {
4619
+ "node": ">= 16"
4620
+ },
4621
+ "funding": {
4622
+ "url": "https://github.com/sponsors/kazupon"
4623
+ },
4624
+ "peerDependencies": {
4625
+ "vue": "^3.0.0"
4626
+ }
4627
+ },
4563
4628
  "node_modules/vue-router": {
4564
4629
  "version": "4.3.2",
4565
4630
  "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.2.tgz",
@@ -26,6 +26,7 @@
26
26
  "uuid": "^10.0.0",
27
27
  "vue": "^3.5.12",
28
28
  "vue-diff": "^1.2.4",
29
+ "vue-i18n": "^10.0.5",
29
30
  "vue-router": "^4.3.0",
30
31
  "vue-slider-component": "^4.1.0-beta.7"
31
32
  },
@@ -1,15 +1,15 @@
1
1
  <template>
2
- <div class="shadow-resourseFormShadow dark:shadow-darkResourseFormShadow dark:shadow-2xl">
2
+ <div class="rounded-lg shadow-resourseFormShadow dark:shadow-darkResourseFormShadow dark:shadow-2xl">
3
3
  <div v-if="group.groupName" class="text-md font-semibold px-6 py-3 flex flex-1 items-center dark:border-gray-600 text-gray-700 bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400 rounded-t-lg">
4
4
  {{ group.groupName }}
5
5
  </div>
6
6
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
7
7
  <thead class="text-xs text-gray-700 uppercase dark:text-gray-400 bg-lightFormHeading dark:bg-gray-700 block md:table-row-group ">
8
8
  <tr>
9
- <th scope="col" class="px-6 py-3 hidden md:w-52 md:table-cell">
9
+ <th scope="col" :class="{'rounded-tl-lg': !group.groupName}" class="px-6 py-3 hidden md:w-52 md:table-cell">
10
10
  Field
11
11
  </th>
12
- <th scope="col" class="px-6 py-3 hidden md:table-cell">
12
+ <th scope="col" :class="{'rounded-tr-lg': !group.groupName}" class="px-6 py-3 hidden md:table-cell">
13
13
  Value
14
14
  </th>
15
15
  </tr>
@@ -37,8 +37,6 @@
37
37
  </template>
38
38
  </Tooltip>
39
39
  </span>
40
-
41
-
42
40
  </td>
43
41
  <td class="px-6 py-4 whitespace-pre-wrap relative block md:table-cell rounded-br-lg "
44
42
  :class="{'rounded-br-lg': i === group.columns.length - 1}">
@@ -73,7 +71,7 @@
73
71
  <Select
74
72
  class="w-full"
75
73
  v-else-if="column.type === 'boolean'"
76
- :options="[{ label: 'Yes', value: true }, { label: 'No', value: false }, { label: 'Unset', value: null }]"
74
+ :options="getBooleanOptions(column)"
77
75
  :modelValue="currentValues[column.name]"
78
76
  @update:modelValue="setCurrentValue(column.name, $event)"
79
77
  ></Select>
@@ -167,9 +165,20 @@
167
165
  unmasked: any,
168
166
  columnError: (column: any) => string,
169
167
  setCurrentValue: (columnName: string, value: any) => void,
170
- columnOptions: (column: any) => any,
168
+ columnOptions: any,
171
169
  }>();
172
170
 
171
+ const getBooleanOptions = (column: any) => {
172
+ const options: Array<{ label: string; value: boolean | null }> = [
173
+ { label: 'Yes', value: true },
174
+ { label: 'No', value: false },
175
+ ];
176
+ if (!column.required[props.mode]) {
177
+ options.push({ label: 'Unset', value: null });
178
+ }
179
+ return options;
180
+ };
181
+
173
182
  const emit = defineEmits(['update:customComponentsInValidity', 'update:customComponentsEmptiness']);
174
183
 
175
184
 
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="rounded-default">
3
3
  <form autocomplete="off" @submit.prevent>
4
- <div v-if="!coreStore.resource.options.createEditGroups || coreStore.resource.options.createEditGroups.length === 0">
4
+ <div v-if="!groups || groups.length === 0">
5
5
  <GroupsTable
6
6
  :source="source"
7
7
  :group="{groupName: '', columns: editableColumns}"
@@ -178,7 +178,6 @@ const setCurrentValue = (key, value) => {
178
178
  };
179
179
 
180
180
  onMounted(() => {
181
-
182
181
  currentValues.value = Object.assign({}, props.record);
183
182
  // json values should transform to string
184
183
  props.resource.columns.forEach((column) => {
@@ -220,19 +219,29 @@ const isValid = computed(() => {
220
219
  });
221
220
 
222
221
 
223
- const groups = coreStore.resource.options.createEditGroups;
222
+ const groups = computed(() => {
223
+ let fieldGroupType;
224
+ if (mode.value === 'edit' && coreStore.resource.options?.editFieldGroups !== undefined) {
225
+ fieldGroupType = coreStore.resource.options.editFieldGroups;
226
+ } else if (mode.value === 'create' && coreStore.resource.options?.createFieldGroups !== undefined) {
227
+ fieldGroupType = coreStore.resource.options.createFieldGroups;
228
+ } else {
229
+ fieldGroupType = coreStore.resource.options?.fieldGroups;
230
+ }
231
+ return fieldGroupType ?? [];
232
+ });
224
233
 
225
234
  const groupedColumns = computed(() => {
226
- if (!groups || groups.length === 0) return [];
235
+ if (!groups.value || groups.value.length === 0) return [];
227
236
 
228
- return groups.map(group => ({
237
+ return groups.value.map(group => ({
229
238
  ...group,
230
239
  columns: props.resource.columns.filter(col => group.columns.includes(col.name) && editableColumns.value.includes(col))
231
240
  }));
232
241
  });
233
242
 
234
243
  const getOtherColumns = () => {
235
- if (!groups || groups.length === 0) return;
244
+ if (!groups.value || groups.value.length === 0) return;
236
245
 
237
246
  const groupedColumnNames = new Set(groupedColumns.value.flatMap(group => group.columns.map(col => col.name)));
238
247
  return editableColumns.value.filter(col => !groupedColumnNames.has(col.name));
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <div class="overflow-x-auto rounded-default shadow-resourseFormShadow dark:shadow-darkResourseFormShadow">
3
+ <div v-if="groupName" class="text-md font-semibold px-6 py-3 flex flex-1 items-center dark:border-gray-600 text-gray-700 bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400 rounded-t-lg">
4
+ {{ groupName }}
5
+ </div>
6
+ <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 table-fixed mb-4">
7
+ <thead class="text-gray-700 dark:text-gray-400 bg-lightFormHeading dark:bg-gray-700 block md:table-row-group">
8
+ <tr>
9
+ <th scope="col" class="px-6 py-3 text-xs uppercase hidden md:w-52 md:table-cell">
10
+ Field
11
+ </th>
12
+ <th scope="col" class="px-6 py-3 text-xs uppercase hidden md:table-cell">
13
+ Value
14
+ </th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ <tr
19
+ v-for="column in columns"
20
+ :key="column.name"
21
+ class="bg-lightForm bg-darkForm odd:dark:bg-gray-900 even:dark:bg-gray-800 dark:border-gray-700 block md:table-row"
22
+ >
23
+ <td class="px-6 py-4 relative block md:table-cell font-bold md:font-normal pb-0 md:pb-4">
24
+ {{ column.label }}
25
+ </td>
26
+ <td class="px-6 py-4 whitespace-pre-wrap" :data-af-column="column.name">
27
+ <component
28
+ v-if="column?.components?.show"
29
+ :is="getCustomComponent(column?.components?.show)"
30
+ :resource="resource"
31
+ :meta="column.components.show.meta"
32
+ :column="column"
33
+ :record="record"
34
+ />
35
+ <ValueRenderer
36
+ v-else
37
+ :column="column"
38
+ :record="record"
39
+ />
40
+ </td>
41
+ </tr>
42
+ </tbody>
43
+ </table>
44
+ </div>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import { defineProps } from 'vue';
49
+ import ValueRenderer from '@/components/ValueRenderer.vue';
50
+ import { getCustomComponent } from '@/utils';
51
+
52
+ defineProps<{
53
+ columns: Array<{
54
+ name: string;
55
+ label: string;
56
+ components?: {
57
+ show?: {
58
+ meta: Record<string, any>;
59
+ };
60
+ };
61
+ }>;
62
+ groupName?: string | null;
63
+ resource: Record<string, any>;
64
+ record: Record<string, any>;
65
+ }>();
66
+ </script>
@@ -1,4 +1,5 @@
1
1
  import { createApp } from 'vue'
2
+ import { createI18n } from 'vue-i18n'
2
3
  import { createPinia } from 'pinia'
3
4
  /* IMPORTANT:ADMINFORTH IMPORTS */
4
5
 
@@ -9,9 +10,12 @@ import router from './router'
9
10
  export const app = createApp(App)
10
11
  /* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */
11
12
 
12
-
13
+ const i18n = createI18n({
14
+ // something vue-i18n options here ...
15
+ })
13
16
  app.use(createPinia())
14
17
  app.use(router)
18
+ app.use(i18n)
15
19
 
16
20
  /* IMPORTANT:ADMINFORTH CUSTOM USES */
17
21
 
@@ -0,0 +1,17 @@
1
+ export interface EmailAdapter {
2
+ sendEmail(
3
+ from: string,
4
+ to: string,
5
+ text: string,
6
+ html: string,
7
+ subject: string
8
+ );
9
+ }
10
+
11
+ export interface CompletionAdapter {
12
+ complete(
13
+ content: string,
14
+ stop: string[],
15
+ maxTokens: number
16
+ ): Promise<{ content: string; finishReason: string }>;
17
+ }
@@ -204,7 +204,6 @@ export interface IAdminForthDataSourceConnector {
204
204
  */
205
205
  updateRecordOriginalValues({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<void>;
206
206
 
207
-
208
207
  /**
209
208
  * Used to delete record in database.
210
209
  */
@@ -278,7 +277,7 @@ export interface IAdminForthRestAPI {
278
277
  * @param toReturn - this is an object which will get status of login process. If at least one callback returns error or redirectTo, login process will be stopped (future callbacks will not be called).
279
278
  * @param response - http response object
280
279
  */
281
- processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin: boolean, error?: string }, response: any): Promise<void>;
280
+ processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin: boolean, error?: string }, response: any, extra: HttpExtra): Promise<void>;
282
281
  }
283
282
 
284
283
  export interface IAdminForth {
@@ -338,9 +338,21 @@ export interface AdminForthResourceInputCommon {
338
338
  allowedActions?: AllowedActionsResolved,
339
339
 
340
340
  /**
341
- * Allows to make groups of columns in create/edit resource pages.
341
+ * Allows to make groups of columns in show, create and edit resource pages.
342
342
  */
343
- createEditGroups?: {
343
+ fieldGroups?: {
344
+ groupName: string;
345
+ columns: string[];
346
+ }[];
347
+ createFieldGroups?: {
348
+ groupName: string;
349
+ columns: string[];
350
+ }[];
351
+ editFieldGroups?: {
352
+ groupName: string;
353
+ columns: string[];
354
+ }[];
355
+ showFieldGroups?: {
344
356
  groupName: string;
345
357
  columns: string[];
346
358
  }[];
@@ -31,14 +31,14 @@
31
31
  <!-- Modal header -->
32
32
  <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
33
33
  <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
34
- Sign in to {{ coreStore.config?.brandName }}
34
+ {{ $t('Sign in to') }} {{ coreStore.config?.brandName }}
35
35
  </h3>
36
36
  </div>
37
37
  <!-- Modal body -->
38
38
  <div class="p-4 md:p-5">
39
39
  <form class="space-y-4" @submit.prevent>
40
40
  <div>
41
- <label for="username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your {{ coreStore.config?.usernameFieldName?.toLowerCase() }}</label>
41
+ <label for="username" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ $t('Your') }} {{ coreStore.config?.usernameFieldName?.toLowerCase() }}</label>
42
42
  <input
43
43
  autocomplete="username"
44
44
  type="username"
@@ -49,7 +49,7 @@
49
49
  class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white" placeholder="name@company.com" required />
50
50
  </div>
51
51
  <div class="relative">
52
- <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your password</label>
52
+ <label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ $t('Your password') }}</label>
53
53
  <input
54
54
  ref="passwordInput"
55
55
  autocomplete="current-password"
@@ -66,7 +66,7 @@
66
66
  :title="`Stay logged in for ${coreStore.config.rememberMeDays} days`"
67
67
  >
68
68
  <Checkbox v-model="rememberMeValue" class="mr-2">
69
- Remember me
69
+ {{ $t('Remember me') }}
70
70
  </Checkbox>
71
71
 
72
72
  </div>
@@ -81,9 +81,9 @@
81
81
  <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
82
82
  <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
83
83
  </svg>
84
- <span class="sr-only">Info</span>
84
+ <span class="sr-only">{{ $t('Info') }}</span>
85
85
  <div>
86
- {{ error }}
86
+ {{ error }}
87
87
  </div>
88
88
  </div>
89
89
 
@@ -93,11 +93,11 @@
93
93
  <svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
94
94
  <path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
95
95
  </svg>
96
- <span class="sr-only">Info</span>
96
+ <span class="sr-only">{{ $t('Info') }}</span>
97
97
  <div v-html="coreStore.config?.loginPromptHTML"></div>
98
98
  </div>
99
99
  <Button @click="login" :loader="inProgress" :disabled="inProgress" class="w-full">
100
- Login to your account
100
+ {{ $t('Login to your account') }}
101
101
  </Button>
102
102
  </form>
103
103
 
@@ -141,7 +141,6 @@ const backgroundPosition = computed(() => {
141
141
 
142
142
  onMounted(async () => {
143
143
  if (coreStore.config?.demoCredentials) {
144
- console.log('Setting demo credentials');
145
144
  const [username, password] = coreStore.config.demoCredentials.split(':');
146
145
  usernameInput.value.value = username;
147
146
  passwordInput.value.value = password;
@@ -57,55 +57,33 @@
57
57
  </div>
58
58
  <div
59
59
  v-else-if="coreStore.record"
60
- class="relative w-full overflow-x-auto rounded-default shadow-resourseFormShadow"
60
+ class="relative w-full flex flex-col gap-4"
61
61
  >
62
- <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 table-fixed">
63
- <!-- table fixed layour used to prevent double scroll in inline list plugins -->
64
- <thead class="text-xs text-gray-700 uppercase dark:text-gray-400 bg-lightFormHeading dark:bg-gray-700 block md:table-row-group ">
65
- <tr>
66
- <th scope="col" class="px-6 py-3 hidden md:w-52 md:table-cell">
67
- Field
68
- </th>
69
- <th scope="col" class="px-6 py-3 hidden md:table-cell">
70
- Value
71
- </th>
72
- </tr>
73
- </thead>
74
- <tbody>
75
- <tr v-for="column,i in coreStore.resource?.columns.filter(c => c.showIn.includes('show'))" :key="column.name"
76
- class="bg-lightForm bg-darkForm odd:dark:bg-gray-900 even:dark:bg-gray-800 dark:border-gray-700 block md:table-row"
77
- :class="{ 'border-b': i !== coreStore.resource.columns.filter(c => c.showIn.includes('show')).length - 1 }"
78
- >
79
- <component
80
- v-if="column.components?.showRow"
81
- :is="getCustomComponent(column.components.showRow)"
82
- :meta="column.components.showRow.meta"
83
- :column="column"
84
- :resource="coreStore.resource"
85
- :record="coreStore.record"
86
- />
87
- <template v-else>
88
- <td class="px-6 py-4 relative block md:table-cell font-bold md:font-normal pb-0 md:pb-4"> <!--align-top-->
89
- {{ column.label }}
90
- </td>
91
- <td class="px-6 py-4 whitespace-pre-wrap" :data-af-column="column.name">
92
- <component
93
- v-if="column?.components?.show"
94
- :is="getCustomComponent(column?.components?.show)"
95
- :resource="coreStore.resource"
96
- :meta="column.components.show.meta"
97
- :column="column"
98
- :record="coreStore.record"
99
- />
100
- <ValueRenderer v-else
101
- :column="column"
102
- :record="coreStore.record"
103
- />
104
- </td>
105
- </template>
106
- </tr>
107
- </tbody>
108
- </table>
62
+ <div v-if="!groups.length && allColumns.length">
63
+ <ShowTable
64
+ :columns="allColumns"
65
+ :resource="coreStore.resource"
66
+ :record="coreStore.record"
67
+ />
68
+ </div>
69
+ <template v-else>
70
+ <template v-for="group in groups" :key="group.groupName">
71
+ <ShowTable
72
+ :columns="group.columns"
73
+ :groupName="group.groupName"
74
+ :resource="coreStore.resource"
75
+ :record="coreStore.record"
76
+ />
77
+ </template>
78
+ <template v-if="otherColumns.length > 0">
79
+ <ShowTable
80
+ :columns="otherColumns"
81
+ groupName="Other Fields"
82
+ :resource="coreStore.resource"
83
+ :record="coreStore.record"
84
+ />
85
+ </template>
86
+ </template>
109
87
  </div>
110
88
 
111
89
  <div v-else class="text-center text-gray-500 dark:text-gray-400 mt-10">
@@ -132,15 +110,15 @@
132
110
 
133
111
  import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
134
112
 
135
- import ValueRenderer from '@/components/ValueRenderer.vue';
136
113
  import { useCoreStore } from '@/stores/core';
137
114
  import { getCustomComponent, checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
138
115
  import { IconPenSolid, IconTrashBinSolid, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
139
- import { onMounted, ref } from 'vue';
116
+ import { onMounted, ref, computed } from 'vue';
140
117
  import { useRoute,useRouter } from 'vue-router';
141
118
  import {callAdminForthApi} from '@/utils';
142
119
  import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
143
120
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
121
+ import ShowTable from '@/components/ShowTable.vue';
144
122
 
145
123
 
146
124
  const route = useRoute();
@@ -164,6 +142,40 @@ onMounted(async () => {
164
142
  loading.value = false;
165
143
  });
166
144
 
145
+ const groups = computed(() => {
146
+ let fieldGroupType;
147
+ if (coreStore.resource.options?.showFieldGroups) {
148
+ fieldGroupType = coreStore.resource.options.showFieldGroups;
149
+ } else if (coreStore.resource.options?.showFieldGroups === null) {
150
+ fieldGroupType = [];
151
+ } else {
152
+ fieldGroupType = coreStore.resource.options?.fieldGroups;
153
+ }
154
+
155
+ const activeGroups = fieldGroupType ?? [];
156
+
157
+ return activeGroups.map(group => ({
158
+ ...group,
159
+ columns: coreStore.resource.columns.filter(
160
+ col => group.columns.includes(col.name) && col.showIn.includes('show')
161
+ ),
162
+ }));
163
+ });
164
+
165
+ const allColumns = computed(() => {
166
+ return coreStore.resource.columns.filter(col => col.showIn.includes('show'));
167
+ });
168
+
169
+ const otherColumns = computed(() => {
170
+ const groupedColumnNames = new Set(
171
+ groups.value.flatMap(group => group.columns.map(col => col.name))
172
+ );
173
+
174
+ return coreStore.resource.columns.filter(
175
+ col => !groupedColumnNames.has(col.name) && col.showIn.includes('show')
176
+ );
177
+ });
178
+
167
179
  async function deleteRecord(row) {
168
180
  const data = await window.adminforth.confirm({
169
181
  message: 'Are you sure you want to delete this item?',
@@ -1,4 +1,6 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
+ import flowbitePlugin from 'flowbite/plugin';
3
+
2
4
  export default {
3
5
  content: ["./src/**/*.{vue, js, ts, tsx}","./src/*.{vue, js, ts, tsx}", "./index.html", "./node_modules/flowbite/**/*.js"],
4
6
  theme: {
@@ -9,7 +11,7 @@ export default {
9
11
 
10
12
  darkMode: 'class',
11
13
  plugins: [
12
- require('flowbite/plugin')({
14
+ flowbitePlugin({
13
15
  charts: true,
14
16
  }),
15
17
  ],
@@ -34,5 +34,19 @@ export default defineConfig({
34
34
  '@': fileURLToPath(new URL('./src', import.meta.url)),
35
35
  '@@': fileURLToPath(new URL('./src/custom', import.meta.url)),
36
36
  }
37
- }
37
+ },
38
+ build: {
39
+ rollupOptions: {
40
+ output: {
41
+ manualChunks(id) {
42
+ // reduce the size of the vendor chunk
43
+ // to only include the package name
44
+ // helps to reduce consumption of memory
45
+ if (id.includes('node_modules')) {
46
+ return id.toString().split('node_modules/')[1].split('/')[0].toString();
47
+ }
48
+ },
49
+ },
50
+ },
51
+ },
38
52
  })
@@ -0,0 +1,10 @@
1
+ export interface EmailAdapter {
2
+ sendEmail(from: string, to: string, text: string, html: string, subject: string): any;
3
+ }
4
+ export interface CompletionAdapter {
5
+ complete(content: string, stop: string[], maxTokens: number): Promise<{
6
+ content: string;
7
+ finishReason: string;
8
+ }>;
9
+ }
10
+ //# sourceMappingURL=Adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Adapters.d.ts","sourceRoot":"","sources":["../../types/Adapters.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,OACf;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CACN,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Adapters.js","sourceRoot":"","sources":["../../types/Adapters.ts"],"names":[],"mappings":""}
@@ -266,7 +266,7 @@ export interface IAdminForthRestAPI {
266
266
  redirectTo?: string;
267
267
  allowedLogin: boolean;
268
268
  error?: string;
269
- }, response: any): Promise<void>;
269
+ }, response: any, extra: HttpExtra): Promise<void>;
270
270
  }
271
271
  export interface IAdminForth {
272
272
  config: AdminForthConfig;