adminforth 1.1.47 → 1.1.48

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 (65) hide show
  1. package/dist/modules/codeInjector.js +13 -22
  2. package/dist/modules/styleGenerator.js +39 -0
  3. package/dist/modules/styles.js +84 -53
  4. package/dist/modules/utils.js +44 -0
  5. package/dist/plugins/AccessControl/index.js +66 -0
  6. package/dist/plugins/AccessControl/types.js +1 -0
  7. package/dist/spa/index.html +2 -2
  8. package/dist/spa/package-lock.json +4 -17
  9. package/dist/spa/package.json +1 -2
  10. package/dist/spa/public/favicon.ico +0 -0
  11. package/dist/spa/spa/index.html +1 -1
  12. package/dist/spa/spa/public/favicon.ico +0 -0
  13. package/dist/spa/spa/src/App.vue +16 -16
  14. package/dist/spa/spa/src/components/MenuLink.vue +3 -3
  15. package/dist/spa/spa/src/components/ResourceForm.vue +3 -3
  16. package/dist/spa/spa/src/components/ResourceListTable.vue +7 -64
  17. package/dist/spa/spa/src/components/SkeleteLoader.vue +23 -0
  18. package/dist/spa/spa/src/views/ListView.vue +1 -1
  19. package/dist/spa/spa/src/views/ShowView.vue +3 -3
  20. package/dist/spa/src/App.vue +45 -125
  21. package/dist/spa/src/assets/logo.svg +1 -19
  22. package/dist/spa/src/components/AcceptModal.vue +9 -2
  23. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  24. package/dist/spa/src/components/CustomDatePicker.vue +3 -16
  25. package/dist/spa/src/components/CustomDateRangePicker.vue +2 -11
  26. package/dist/spa/src/components/Dropdown.vue +1 -6
  27. package/dist/spa/src/components/Filters.vue +19 -40
  28. package/dist/spa/src/components/ResourceForm.vue +24 -26
  29. package/dist/spa/src/components/ValueRenderer.vue +8 -14
  30. package/dist/spa/src/main.ts +1 -1
  31. package/dist/spa/src/router/index.ts +20 -30
  32. package/dist/spa/src/stores/core.ts +31 -48
  33. package/dist/spa/src/stores/modal.ts +3 -13
  34. package/dist/spa/src/utils.ts +7 -62
  35. package/dist/spa/src/views/CreateView.vue +15 -67
  36. package/dist/spa/src/views/EditView.vue +11 -45
  37. package/dist/spa/src/views/ListView.vue +366 -82
  38. package/dist/spa/src/views/LoginView.vue +4 -16
  39. package/dist/spa/src/views/ShowView.vue +21 -105
  40. package/dist/spa/tailwind.config.js +1 -1
  41. package/dist/spa/vite.config.ts +0 -6
  42. package/dist/types.js +30 -0
  43. package/modules/codeInjector.ts +10 -18
  44. package/modules/styleGenerator.ts +45 -0
  45. package/modules/styles.ts +95 -48
  46. package/modules/utils.ts +48 -0
  47. package/package.json +1 -1
  48. package/spa/index.html +1 -1
  49. package/spa/src/App.vue +16 -16
  50. package/spa/src/components/MenuLink.vue +3 -3
  51. package/spa/src/components/ResourceForm.vue +3 -3
  52. package/spa/src/components/ResourceListTable.vue +7 -64
  53. package/spa/src/components/SkeleteLoader.vue +23 -0
  54. package/spa/src/views/ListView.vue +1 -1
  55. package/spa/src/views/ShowView.vue +3 -3
  56. package/types/AdminForthConfig.ts +6 -4
  57. package/dist/spa/src/components/ResourceListTable.vue +0 -419
  58. package/dist/spa/src/components/Toast.vue +0 -66
  59. package/dist/spa/src/composables/useFrontendApi.ts +0 -26
  60. package/dist/spa/src/composables/useStores.ts +0 -113
  61. package/dist/spa/src/spa_types/core.ts +0 -51
  62. package/dist/spa/src/stores/filters.ts +0 -22
  63. package/dist/spa/src/stores/toast.ts +0 -15
  64. package/dist/spa/src/stores/user.ts +0 -54
  65. /package/dist/spa/{public/assets → spa/public}/favicon.png +0 -0
@@ -2,11 +2,11 @@
2
2
  <!-- drawer component -->
3
3
  <div id="drawer-navigation"
4
4
 
5
- class="fixed right-0 z-50 p-4 overflow-y-auto transition-transform translate-x-full bg-white w-80 dark:bg-gray-800 shadow-xl dark:shadow-black"
5
+ class="fixed top-14 right-0 z-40 p-4 overflow-y-auto transition-transform translate-x-full bg-white w-80 dark:bg-gray-800 shadow-xl dark:shadow-black"
6
6
 
7
7
  :class="show ? 'top-0 transform-none' : ''"
8
8
  tabindex="-1" aria-labelledby="drawer-navigation-label"
9
- :style="{ height: `calc(100vh ` }"
9
+ :style="{ height: `calc(100vh - 3.5rem)` }"
10
10
  >
11
11
  <h5 id="drawer-navigation-label" class="text-base font-semibold text-gray-500 uppercase dark:text-gray-400">
12
12
  Filters
@@ -26,13 +26,13 @@
26
26
  v-if="c.foreignResource"
27
27
  :options="columnOptions[c.name] || []"
28
28
  @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event })"
29
- :modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
29
+ :modelValue="filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
30
30
  />
31
31
  <Dropdown
32
32
  v-else-if="c.type === 'boolean'"
33
33
  :options="[{ label: 'Yes', value: true }, { label: 'No', value: false }, { label: 'Unset', value: null }]"
34
34
  @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event })"
35
- :modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
35
+ :modelValue="filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
36
36
  />
37
37
 
38
38
  <Dropdown
@@ -40,7 +40,7 @@
40
40
  :options="c.enum"
41
41
  :allowCustom="c.allowCustom"
42
42
  @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event })"
43
- :modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
43
+ :modelValue="filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
44
44
  />
45
45
 
46
46
  <input
@@ -54,9 +54,9 @@
54
54
  <CustomDateRangePicker
55
55
  v-else-if="['datetime'].includes(c.type)"
56
56
  :column="c"
57
- :valueStart="filtersStore.filters.find(f => f.field === c.name && f.operator === 'gte')?.value || undefined"
57
+ :valueStart="filters.find(f => f.field === c.name && f.operator === 'gte')?.value || undefined"
58
58
  @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
59
- :valueEnd="filtersStore.filters.find(f => f.field === c.name && f.operator === 'lte')?.value || undefined"
59
+ :valueEnd="filters.find(f => f.field === c.name && f.operator === 'lte')?.value || undefined"
60
60
  @update:valueEnd="setFilterItem({ column: c, operator: 'lte', value: $event || undefined })"
61
61
  />
62
62
 
@@ -69,7 +69,7 @@
69
69
  >
70
70
 
71
71
  <CustomRangePicker
72
- v-else-if="['integer', 'decimal', 'float'].includes(c.type) && c.allowMinMaxQuery"
72
+ v-else-if="['integer', 'decimal', 'float'].includes(c.type)"
73
73
  :min="c.min"
74
74
  :max="c.max"
75
75
  :valueStart="getFilterItem({ column: c, operator: 'gte' })"
@@ -77,23 +77,6 @@
77
77
  :valueEnd="getFilterItem({ column: c, operator: 'lte' })"
78
78
  @update:valueEnd="setFilterItem({ column: c, operator: 'lte', value: $event || undefined })"
79
79
  />
80
-
81
- <div v-else-if="['integer', 'decimal', 'float'].includes(c.type)" class="flex gap-2">
82
- <input
83
- type="number" aria-describedby="helper-text-explanation"
84
- 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-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
85
- placeholder="From"
86
- @input="setFilterItem({ column: c, operator: 'gte', value: $event.target.value || undefined })"
87
- :value="getFilterItem({ column: c, operator: 'gte' })"
88
- >
89
- <input
90
- type="number" aria-describedby="helper-text-explanation"
91
- 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-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
92
- placeholder="To"
93
- @input="setFilterItem({ column: c, operator: 'lte', value: $event.target.value || undefined})"
94
- :value="getFilterItem({ column: c, operator: 'lte' })"
95
- >
96
- </div>
97
80
 
98
81
  </li>
99
82
  </ul>
@@ -101,15 +84,15 @@
101
84
 
102
85
  <div class="flex justify-end gap-2">
103
86
  <button
104
- :disabled="!filtersStore.filters.length"
87
+ :disabled="!filters.length"
105
88
  type="button"
106
- class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
89
+ class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
107
90
  @click="clear">Clear all</button>
108
91
 
109
92
  </div>
110
93
  </div>
111
94
 
112
- <div v-if="show" class="bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-30"
95
+ <div v-if="show" drawer-backdrop="" class="bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-50"
113
96
  @click="$emit('hide')">
114
97
  </div>
115
98
  </template>
@@ -122,10 +105,6 @@ import { callAdminForthApi } from '@/utils';
122
105
  import { useRouter } from 'vue-router';
123
106
  import { computedAsync } from '@vueuse/core'
124
107
  import CustomRangePicker from "@/components/CustomRangePicker.vue";
125
- import { useFiltersStore } from '@/stores/filters';
126
-
127
- const filtersStore = useFiltersStore();
128
-
129
108
 
130
109
  // props: columns
131
110
  // add support for v-model:filers
@@ -184,28 +163,28 @@ watch(() => props.show, (show) => {
184
163
 
185
164
  function setFilterItem({ column, operator, value }) {
186
165
 
187
- const index = filtersStore.filters.findIndex(f => f.field === column.name && f.operator === operator);
166
+ const index = props.filters.findIndex(f => f.field === column.name && f.operator === operator);
188
167
  if (value === undefined) {
189
168
  if (index !== -1) {
190
- filtersStore.filters.splice(index, 1);
169
+ props.filters.splice(index, 1);
191
170
  }
192
- emits('update:filters', [...filtersStore.filters]);
171
+ emits('update:filters', [...props.filters]);
193
172
  return;
194
173
  } else {
195
174
  if (index === -1) {
196
- filtersStore.setFilter({ field: column.name, value, operator });
175
+ props.filters.push({ field: column.name, value, operator });
197
176
  } else {
198
- filtersStore.setFilters([...filtersStore.filters.slice(0, index), { field: column.name, value, operator }, ...filtersStore.filters.slice(index + 1)])
177
+ props.filters[index].value = value;
199
178
  }
200
179
  }
201
- emits('update:filters', [...filtersStore.filters]);
180
+ emits('update:filters', [...props.filters]);
202
181
  }
203
182
 
204
183
  function getFilterItem({ column, operator }) {
205
- return filtersStore.filters.find(f => f.field === column.name && f.operator === operator)?.value || '';
184
+ return props.filters.find(f => f.field === column.name && f.operator === operator)?.value || '';
206
185
  }
207
186
 
208
187
  async function clear() {
209
- filtersStore.clearFilters();
188
+ emits('update:filters', []);
210
189
  }
211
190
  </script>
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <div class="rounded-default">
2
+ <div>
3
3
 
4
4
  <div
5
- class="relative shadow-resourse-form-shadow sm:rounded-lg dark:shadow-2xl dark:shadow-black rounded-default"
5
+ class="relative shadow-resourse-form-shadow sm:rounded-lg dark:shadow-2xl dark:shadow-black"
6
6
  >
7
7
  <form autocomplete="off" @submit.prevent>
8
- <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
+ <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
9
9
  <thead class="text-xs text-gray-700 uppercase bg-form-view-heading dark:bg-gray-700 dark:text-gray-400">
10
10
  <tr>
11
11
  <th scope="col" class="px-6 py-3">
@@ -30,28 +30,28 @@
30
30
  />
31
31
  </template>
32
32
  <template v-else>
33
- <td class="px-6 py-4 whitespace-nowrap "> <!--align-top-->
34
- {{ column.label }}
35
- <span :data-tooltip-target="`tooltip-show-${i}`" class="relative inline-block">
36
- <IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
37
- :class="(columnError(column) && validating) ? 'text-red-500 dark:text-red-400' : 'text-gray-400 dark:text-gray-500'"
38
- />
39
- </span>
40
- <div :id="`tooltip-show-${i}`"
41
- role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
42
- Required field
43
- <div class="tooltip-arrow" data-popper-arrow></div>
44
- </div>
33
+ <td class="px-6 py-4 whitespace-nowrap">
34
+ {{ column.label }}
35
+ <span :data-tooltip-target="`tooltip-show-${i}`" class="relative inline-block">
36
+ <IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
37
+ :class="(columnError(column) && validating) ? 'text-red-500 dark:text-red-400' : 'text-gray-400 dark:text-gray-500'"
38
+ />
39
+ </span>
40
+ <div :id="`tooltip-show-${i}`"
41
+ role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
42
+ Required field
43
+ <div class="tooltip-arrow" data-popper-arrow></div>
44
+ </div>
45
45
  </td>
46
46
  <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap relative">
47
+
47
48
  <Dropdown
48
49
  single
49
50
  v-if="column.foreignResource"
50
51
  :options="columnOptions[column.name] || []"
51
- :placeholder = "columnOptions[column.name]?.length || 'There are no options available'"
52
52
  :modelValue="currentValues[column.name]"
53
53
  @update:modelValue="setCurrentValue(column.name, $event)"
54
- ></Dropdown>
54
+ />
55
55
  <Dropdown
56
56
  single
57
57
  v-else-if="column.enum"
@@ -79,7 +79,6 @@
79
79
  v-else-if="['datetime'].includes(column.type)"
80
80
  :column="column"
81
81
  :valueStart="currentValues[column.name]"
82
- auto-hide
83
82
  @update:valueStart="setCurrentValue(column.name, $event)"
84
83
  />
85
84
  <input
@@ -121,6 +120,9 @@
121
120
  <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
122
121
  <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
123
122
  </button>
123
+
124
+
125
+
124
126
  <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
125
127
 
126
128
  <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
@@ -152,7 +154,7 @@ const router = useRouter();
152
154
 
153
155
  const props = defineProps({
154
156
  loading: Boolean,
155
- resource: Object,
157
+ resourceColumns: Object,
156
158
  record: Object,
157
159
  validating: Boolean,
158
160
  customComponentsPerColumn: Object,
@@ -194,11 +196,7 @@ const columnError = (column) => {
194
196
  for (let i = 0; i < validationArray.length; i++) {
195
197
  if (validationArray[i].regExp) {
196
198
  const regExp = new RegExp(validationArray[i].regExp);
197
- let value = currentValues.value[column.name];
198
- if (value === undefined || value === null) {
199
- value = '';
200
- }
201
- if (!regExp.test(value)) {
199
+ if (!regExp.test(currentValues.value[column.name])) {
202
200
  return validationArray[i].message;
203
201
  }
204
202
  }
@@ -228,7 +226,7 @@ onMounted(() => {
228
226
 
229
227
  const columnOptions = computedAsync(async () => {
230
228
  return (await Promise.all(
231
- Object.values(props.resource.columns).map(async (column) => {
229
+ Object.values(props.resourceColumns).map(async (column) => {
232
230
  if (column.foreignResource) {
233
231
  const list = await callAdminForthApi({
234
232
  method: 'POST',
@@ -251,7 +249,7 @@ const coreStore = useCoreStore();
251
249
 
252
250
  const editableColumns = computed(() => {
253
251
  const mode = props.record ? 'edit' : 'create';
254
- return props.resource?.columns?.filter(column => column.showIn.includes(mode));
252
+ return props.resourceColumns?.filter(column => column.showIn.includes(mode));
255
253
  });
256
254
 
257
255
  const isValid = computed(() => {
@@ -1,10 +1,9 @@
1
1
  <template>
2
2
  <div>
3
-
4
3
  <span v-if="column.foreignResource">
5
- <RouterLink v-if="record[column.name]" class="font-medium text-blue-600 dark:text-blue-500 hover:brightness-110"
6
- :to="{ name: 'resource-show', params: { resourceId: column.foreignResource.resourceId, primaryKey: record[column.name].pk } }">
7
- {{ record[column.name].label }}
4
+ <RouterLink v-if="row[column.name]" class="font-medium text-blue-600 dark:text-blue-500 hover:brightness-110"
5
+ :to="{ name: 'resource-show', params: { resourceId: column.foreignResource.resourceId, primaryKey: row[column.name].pk } }">
6
+ {{ row[column.name].label }}
8
7
  </RouterLink>
9
8
  <div v-else>
10
9
  <span class="text-gray-400">-</span>
@@ -12,18 +11,18 @@
12
11
  </span>
13
12
 
14
13
  <span v-else-if="column.type === 'boolean'">
15
- <span v-if="record[column.name]" class="bg-green-100 text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">Yes</span>
14
+ <span v-if="row[column.name]" class="bg-green-100 text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">Yes</span>
16
15
  <span v-else class="bg-red-100 text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">No</span>
17
16
  </span>
18
17
  <span v-else-if="column.enum">
19
- {{ checkEmptyValues(column.enum.find(e => e.value === record[column.name])?.label,route.meta.type) }}
18
+ {{ column.enum.find(e => e.value === row[column.name])?.label }}
20
19
  </span>
21
20
  <span v-else-if="column.type === 'datetime'">
22
- {{ checkEmptyValues(formatDate(record[column.name]),route.meta.type) }}
21
+ {{ formatDate(row[column.name]) }}
23
22
 
24
23
  </span>
25
24
  <span v-else>
26
- {{ checkEmptyValues(record[column.name],route.meta.type) }}
25
+ {{ row[column.name] }}
27
26
  </span>
28
27
  </div>
29
28
  </template>
@@ -34,22 +33,17 @@
34
33
  import dayjs from 'dayjs';
35
34
  import utc from 'dayjs/plugin/utc';
36
35
  import timezone from 'dayjs/plugin/timezone';
37
- import {checkEmptyValues} from '@/utils';
38
- import { useRoute, useRouter } from 'vue-router';
39
-
40
36
 
41
37
  import { useCoreStore } from '@/stores/core';
42
38
 
43
39
  const coreStore = useCoreStore();
44
- const route = useRoute();
45
-
46
40
 
47
41
  dayjs.extend(utc);
48
42
  dayjs.extend(timezone);
49
43
 
50
44
  const props = defineProps({
51
45
  column: Object,
52
- record: Object
46
+ row: Object
53
47
  });
54
48
 
55
49
 
@@ -9,7 +9,7 @@ import router from './router'
9
9
  const app = createApp(App)
10
10
  /* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */
11
11
 
12
-
12
+
13
13
  app.use(createPinia())
14
14
  app.use(router)
15
15
 
@@ -1,24 +1,24 @@
1
1
  import { createRouter, createWebHistory } from 'vue-router'
2
2
  import ResourceParent from '@/views/ResourceParent.vue'
3
- import { useUserStore } from '@/stores/user'
4
- /* IMPORTANT:ADMINFORTH ROUTES IMPORTS */
3
+ import ListView from '@/views/ListView.vue'
4
+ import ShowView from '@/views/ShowView.vue'
5
+ import EditView from '@/views/EditView.vue'
6
+ import CreateView from '@/views/CreateView.vue'
5
7
 
6
8
  const router = createRouter({
7
9
  history: createWebHistory(import.meta.env.BASE_URL),
8
10
  routes: [
11
+ {
12
+ path: '/',
13
+ name: 'home',
14
+ //redirect to login
15
+ /* IMPORTANT:ADMINFORTH REDIRECT */
16
+
17
+ },
9
18
  {
10
19
  path: '/login',
11
20
  name: 'login',
12
- component: () => import('@/views/LoginView.vue'),
13
- meta: { title: 'login' },
14
- beforeEnter: async (to, from, next) => {
15
- const userStore = useUserStore()
16
- if(localStorage.getItem('isAuthorized') === 'true'){
17
- next({name: 'home'})
18
- } else {
19
- next()
20
- }
21
- }
21
+ component: () => import('@/views/LoginView.vue')
22
22
  },
23
23
  {
24
24
  path: '/resource/:resourceId',
@@ -27,29 +27,23 @@ const router = createRouter({
27
27
  children: [
28
28
  {
29
29
  path: '',
30
- component: () => import('@/views/ListView.vue'),
31
- name: 'resource-list',
32
- meta: { title: 'list',type: 'list' }
30
+ component: ListView,
31
+ name: 'resource-list'
33
32
  },
34
33
  {
35
34
  path: 'show/:primaryKey',
36
- component: () => import('@/views/ShowView.vue'),
37
- name: 'resource-show',
38
- meta: { title: 'show', type: 'show'}
39
-
35
+ component: ShowView,
36
+ name: 'resource-show'
40
37
  },
41
38
  {
42
39
  path: 'edit/:primaryKey',
43
- component: () => import('@/views/EditView.vue'),
44
- name: 'resource-edit',
45
- meta: { title: 'edit', type: 'edit'}
40
+ component: EditView,
41
+ name: 'resource-edit'
46
42
  },
47
43
  {
48
44
  path: 'create',
49
- component: () => import('@/views/CreateView.vue'),
50
- name: 'resource-create',
51
- meta: { title: 'create', type: 'create'}
52
-
45
+ component: CreateView,
46
+ name: 'resource-create'
53
47
  },
54
48
  ]
55
49
  },
@@ -57,8 +51,4 @@ const router = createRouter({
57
51
  ]
58
52
  })
59
53
 
60
-
61
-
62
-
63
-
64
54
  export default router
@@ -1,50 +1,39 @@
1
1
  import { ref, computed } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
3
  import { callAdminForthApi } from '@/utils';
4
- import type { AdminForthResource, AdminForthResourceColumn } from '@/types/AdminForthConfig';
5
- import type { Ref } from 'vue'
6
4
 
7
5
  export const useCoreStore = defineStore('core', () => {
8
- const resourceById: Ref<Object> = ref({});
6
+ const resourceById = ref([]);
9
7
  const menu = ref([]);
10
8
  const config = ref({});
11
- const record: Ref<any | null> = ref({});
12
- const resource: Ref<AdminForthResource | null> = ref(null);
13
-
14
- const resourceColumnsWithFilters = computed(() => {
15
- if (!resource.value) {
16
- return [];
17
- }
18
- return resource.value.columns.filter((col: AdminForthResourceColumn) => col.showIn?.includes('filter'));
19
- })
20
-
21
- const resourceOptions = ref(null);
9
+ const record = ref({});
10
+ const resourceColumns = ref(null);
22
11
  const resourceColumnsError = ref('');
23
12
  const resourceColumnsId = ref(null);
24
- const adminUser = ref(null);
13
+ const user = ref(null);
25
14
 
26
15
  async function fetchMenuAndResource() {
27
16
  const resp = await callAdminForthApi({
28
17
  path: '/get_base_config',
29
18
  method: 'GET',
30
19
  });
31
- if(!resp){
32
- return
33
- }
34
20
  menu.value = resp.menu;
35
- resourceById.value = resp.resources.reduce((acc: Object, resource: AdminForthResource) => {
21
+ resourceById.value = resp.resources.reduce((acc, resource) => {
36
22
  acc[resource.resourceId] = resource;
37
23
  return acc;
38
24
  }, {});
39
25
  config.value = resp.config;
40
- adminUser.value = resp.user;
26
+ user.value = resp.user;
41
27
  console.log('🌍 AdminForth v', resp.version);
28
+
29
+ // find homepage:true in menu recuresively
30
+
42
31
  }
43
32
 
44
33
  async function fetchRecord({ resourceId, primaryKey }) {
45
34
  record.value = null;
46
35
 
47
- if (!resource.value) {
36
+ if (!resourceColumns.value) {
48
37
  throw new Error('Columns not fetched yet');
49
38
  }
50
39
 
@@ -56,7 +45,7 @@ export const useCoreStore = defineStore('core', () => {
56
45
  resourceId: resourceId,
57
46
  filters: [
58
47
  {
59
- field: resource.value.columns.find((col: AdminForthResourceColumn) => col.primaryKey).name,
48
+ field: resourceColumns.value.find((col) => col.primaryKey).name,
60
49
  operator: 'eq',
61
50
  value: primaryKey
62
51
  }
@@ -67,39 +56,30 @@ export const useCoreStore = defineStore('core', () => {
67
56
  }
68
57
  });
69
58
 
70
- if (respData.error) {
71
- window.adminforth.alert({
72
- message: respData.error,
73
- variant: 'danger',
74
- timeout: 'unlimited'
75
- });
76
- record.value = {};
77
- } else {
78
- record.value = respData.data[0];
79
- }
80
-
59
+ console.log('📦 record', respData);
60
+ record.value = respData.data[0];
81
61
  }
82
62
 
83
- async function fetchResourceFull({ resourceId }: { resourceId: string }) {
84
- if (resourceColumnsId.value === resourceId && resource.value) {
63
+ async function fetchColumns({ resourceId }) {
64
+ if (resourceColumnsId.value === resourceId && resourceColumns.value) {
85
65
  // already fetched
86
66
  return;
87
67
  }
88
68
  resourceColumnsId.value = resourceId;
69
+ resourceColumns.value = null;
89
70
  resourceColumnsError.value = '';
90
71
  const res = await callAdminForthApi({
91
- path: '/get_resource',
72
+ path: '/get_resource_columns',
92
73
  method: 'POST',
93
74
  body: {
94
75
  resourceId,
95
76
  }
96
- });
77
+ }
78
+ );
97
79
  if (res.error) {
98
80
  resourceColumnsError.value = res.error;
99
81
  } else {
100
- resourceById.value[resourceId] = res.resource;
101
- resource.value = res.resource;
102
- resourceOptions.value = res.resource.options;
82
+ resourceColumns.value = res.resource.columns;
103
83
  }
104
84
  }
105
85
 
@@ -111,16 +91,21 @@ export const useCoreStore = defineStore('core', () => {
111
91
  config.value = {...config.value, ...res};
112
92
  }
113
93
 
114
-
94
+ async function logout() {
95
+ await callAdminForthApi({
96
+ path: '/logout',
97
+ method: 'POST',
98
+ });
99
+ }
115
100
 
116
101
  const username = computed(() => {
117
102
  const usernameField = config.value.usernameField;
118
- return adminUser.value && adminUser.value[usernameField];
103
+ return user.value && user.value[usernameField];
119
104
  });
120
105
 
121
106
  const userFullname = computed(() => {
122
107
  const userFullnameField = config.value.userFullnameField;
123
- return adminUser.value && adminUser.value[userFullnameField];
108
+ return user.value && user.value[userFullnameField];
124
109
  })
125
110
 
126
111
 
@@ -134,11 +119,9 @@ export const useCoreStore = defineStore('core', () => {
134
119
  fetchMenuAndResource,
135
120
  fetchRecord,
136
121
  record,
137
- fetchResourceFull,
122
+ resourceColumns,
123
+ fetchColumns,
138
124
  resourceColumnsError,
139
- resourceOptions,
140
- resource,
141
- adminUser,
142
- resourceColumnsWithFilters
125
+ logout
143
126
  }
144
127
  })
@@ -1,12 +1,6 @@
1
1
  import { ref } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
-
4
- type ModalContentType = {
5
- title?: string;
6
- content?: string;
7
- acceptText?: string;
8
- cancelText?: string;
9
- }
3
+ import { callAdminForthApi } from '@/utils';
10
4
 
11
5
 
12
6
  export const useModalStore = defineStore('modal', () => {
@@ -18,17 +12,13 @@ export const useModalStore = defineStore('modal', () => {
18
12
  });
19
13
  const isOpened = ref(false);
20
14
  const onAcceptFunction: any = ref(()=>{});
21
- const onCancelFunction: any = ref(()=>{});
22
15
  function togleModal() {
23
16
  isOpened.value = !isOpened.value;
24
17
  }
25
18
  function setOnAcceptFunction(func: Function) {
26
19
  onAcceptFunction.value = func;
27
20
  }
28
- function setOnCancelFunction(func: Function) {
29
- onCancelFunction.value = func;
30
- }
31
- function setModalContent(content: ModalContentType) {
21
+ function setModalContent(content: string) {
32
22
  modalContent.value = content;
33
23
  }
34
24
  function resetmodalState() {
@@ -43,6 +33,6 @@ export const useModalStore = defineStore('modal', () => {
43
33
 
44
34
  }
45
35
 
46
- return {isOpened, setModalContent,onCancelFunction, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState,setOnCancelFunction}
36
+ return {isOpened, setModalContent, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState}
47
37
 
48
38
  })