adminforth 1.2.87 → 1.2.89

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 (101) hide show
  1. package/dist/modules/restApi.js +9 -0
  2. package/dist/spa/.vscode/extensions.json +6 -0
  3. package/dist/spa/index.html +4 -4
  4. package/dist/spa/package-lock.json +6 -421
  5. package/dist/spa/package.json +2 -10
  6. package/dist/spa/public/favicon.ico +0 -0
  7. package/dist/spa/spa/.eslintrc.cjs +14 -0
  8. package/dist/spa/spa/.vscode/extensions.json +6 -0
  9. package/dist/spa/spa/README.md +39 -0
  10. package/dist/spa/spa/env.d.ts +1 -0
  11. package/dist/spa/spa/index.html +23 -0
  12. package/dist/spa/spa/package-lock.json +4573 -0
  13. package/dist/spa/spa/package.json +49 -0
  14. package/dist/spa/spa/postcss.config.js +6 -0
  15. package/dist/spa/spa/public/favicon.ico +0 -0
  16. package/dist/spa/spa/src/App.vue +314 -0
  17. package/dist/spa/spa/src/assets/base.css +0 -0
  18. package/dist/spa/spa/src/assets/logo.svg +19 -0
  19. package/dist/spa/spa/src/components/AcceptModal.vue +45 -0
  20. package/dist/spa/spa/src/components/Breadcrumbs.vue +40 -0
  21. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +26 -0
  22. package/dist/spa/spa/src/components/CustomDatePicker.vue +174 -0
  23. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +218 -0
  24. package/dist/spa/spa/src/components/Dropdown.vue +168 -0
  25. package/dist/spa/spa/src/components/Filters.vue +222 -0
  26. package/dist/spa/spa/src/components/HelloWorld.vue +17 -0
  27. package/dist/spa/spa/src/components/MenuLink.vue +24 -0
  28. package/dist/spa/spa/src/components/ResourceForm.vue +289 -0
  29. package/dist/spa/{src → spa/src}/components/ResourceListTable.vue +3 -6
  30. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +13 -0
  31. package/dist/spa/{src → spa/src}/components/Toast.vue +3 -9
  32. package/dist/spa/spa/src/components/ValueRenderer.vue +66 -0
  33. package/dist/spa/spa/src/components/icons/IconCalendar.vue +5 -0
  34. package/dist/spa/spa/src/components/icons/IconCommunity.vue +7 -0
  35. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +7 -0
  36. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +7 -0
  37. package/dist/spa/spa/src/components/icons/IconSupport.vue +7 -0
  38. package/dist/spa/spa/src/components/icons/IconTime.vue +5 -0
  39. package/dist/spa/spa/src/components/icons/IconTooling.vue +19 -0
  40. package/dist/spa/spa/src/index.scss +27 -0
  41. package/dist/spa/spa/src/main.ts +18 -0
  42. package/dist/spa/spa/src/router/index.ts +63 -0
  43. package/dist/spa/spa/src/stores/core.ts +144 -0
  44. package/dist/spa/spa/src/stores/modal.ts +48 -0
  45. package/dist/spa/spa/src/utils.ts +103 -0
  46. package/dist/spa/spa/src/views/CreateView.vue +156 -0
  47. package/dist/spa/spa/src/views/EditView.vue +157 -0
  48. package/dist/spa/spa/src/views/HomeView.vue +8 -0
  49. package/dist/spa/spa/src/views/ListView.vue +258 -0
  50. package/dist/spa/spa/src/views/LoginView.vue +143 -0
  51. package/dist/spa/spa/src/views/ResourceParent.vue +17 -0
  52. package/dist/spa/spa/src/views/ShowView.vue +184 -0
  53. package/dist/spa/spa/tailwind.config.js +17 -0
  54. package/dist/spa/spa/tsconfig.app.json +14 -0
  55. package/dist/spa/spa/tsconfig.json +11 -0
  56. package/dist/spa/spa/tsconfig.node.json +19 -0
  57. package/dist/spa/spa/vite.config.ts +56 -0
  58. package/dist/spa/src/App.vue +94 -236
  59. package/dist/spa/src/assets/base.css +0 -2
  60. package/dist/spa/src/assets/logo.svg +1 -19
  61. package/dist/spa/src/components/AcceptModal.vue +10 -3
  62. package/dist/spa/src/components/Breadcrumbs.vue +2 -3
  63. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  64. package/dist/spa/src/components/CustomDatePicker.vue +10 -25
  65. package/dist/spa/src/components/CustomDateRangePicker.vue +5 -14
  66. package/dist/spa/src/components/Dropdown.vue +5 -19
  67. package/dist/spa/src/components/Filters.vue +38 -103
  68. package/dist/spa/src/components/MenuLink.vue +3 -3
  69. package/dist/spa/src/components/ResourceForm.vue +116 -205
  70. package/dist/spa/src/components/ValueRenderer.vue +8 -30
  71. package/dist/spa/src/index.scss +1 -2
  72. package/dist/spa/src/main.ts +2 -2
  73. package/dist/spa/src/router/index.ts +19 -29
  74. package/dist/spa/src/stores/core.ts +59 -65
  75. package/dist/spa/src/stores/modal.ts +3 -13
  76. package/dist/spa/src/utils.ts +10 -69
  77. package/dist/spa/src/views/CreateView.vue +16 -69
  78. package/dist/spa/src/views/EditView.vue +14 -68
  79. package/dist/spa/src/views/HomeView.vue +8 -0
  80. package/dist/spa/src/views/ListView.vue +371 -100
  81. package/dist/spa/src/views/LoginView.vue +29 -67
  82. package/dist/spa/src/views/ResourceParent.vue +2 -1
  83. package/dist/spa/src/views/ShowView.vue +25 -116
  84. package/dist/spa/tailwind.config.js +3 -8
  85. package/dist/spa/vite.config.ts +0 -13
  86. package/dist/types.js +30 -0
  87. package/modules/restApi.ts +14 -1
  88. package/package.json +1 -1
  89. package/spa/src/components/MenuLink.vue +3 -0
  90. package/types/AdminForthConfig.ts +6 -0
  91. package/dist/spa/src/types/AdminForthConfig.ts +0 -1477
  92. package/dist/spa/src/types/FrontendAPI.ts +0 -121
  93. /package/dist/spa/{public → spa/public}/assets/favicon.png +0 -0
  94. /package/dist/spa/{src → spa/src}/components/CustomRangePicker.vue +0 -0
  95. /package/dist/spa/{src → spa/src}/components/SkeleteLoader.vue +0 -0
  96. /package/dist/spa/{src → spa/src}/composables/useFrontendApi.ts +0 -0
  97. /package/dist/spa/{src → spa/src}/composables/useStores.ts +0 -0
  98. /package/dist/spa/{src → spa/src}/spa_types/core.ts +0 -0
  99. /package/dist/spa/{src → spa/src}/stores/filters.ts +0 -0
  100. /package/dist/spa/{src → spa/src}/stores/toast.ts +0 -0
  101. /package/dist/spa/{src → spa/src}/stores/user.ts +0 -0
@@ -1,137 +1,113 @@
1
1
  <template>
2
- <div class="rounded-default">
2
+ <div>
3
+
3
4
  <div
4
- class="relative shadow-resourseFormShadow dark:shadow-darkResourseFormShadow sm:rounded-lg dark:shadow-2xl rounded-default"
5
+ class="relative shadow-md sm:rounded-lg dark:shadow-2xl"
5
6
  >
6
7
  <form autocomplete="off" @submit.prevent>
7
- <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
- <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400 block md:table-row-group">
8
+ <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
9
+ <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
9
10
  <tr>
10
- <th scope="col" class="px-6 py-3 hidden md:table-cell">
11
+ <th scope="col" class="px-6 py-3">
11
12
  Field
12
13
  </th>
13
- <th scope="col" class="px-6 py-3 w-5/6 hidden md:table-cell">
14
+ <th scope="col" class="px-6 py-3 w-4/6">
14
15
  Value
15
16
  </th>
16
-
17
-
18
17
  </tr>
19
18
  </thead>
20
19
  <tbody>
21
20
  <tr v-for="column, i in editableColumns" :key="column.name"
22
- v-if="currentValues !== null"
23
- class="bg-ligftForm dark:bg-gray-800 border-b dark:border-gray-700 block md:table-row"
21
+ class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700"
24
22
  >
25
- <td class="px-6 py-4 sm:pb-0 whitespace-nowrap flex items-center block md:table-cell"> <!--align-top-->
26
- {{ column.label }}
27
- <span :data-tooltip-target="`tooltip-show-${i}`" class="ml-1 relative inline-block">
28
- <IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
29
- :class="(columnError(column) && validating) ? 'text-red-500 dark:text-red-400' : 'text-gray-400 dark:text-gray-500'"
30
- />
31
- </span>
32
- <div :id="`tooltip-show-${i}`"
33
- role="tooltip"
34
- class="ml-1 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">
35
- Required field
36
- <div class="tooltip-arrow" data-popper-arrow></div>
37
- </div>
38
- </td>
39
- <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap relative block md:table-cell">
40
- <template v-if="column?.components?.[props.source]?.file">
41
- <component
42
- :is="getCustomComponent(column.components[props.source])"
43
- :column="column"
44
- :value="currentValues[column.name]"
45
- @update:value="setCurrentValue(column.name, $event)"
46
- :meta="column.components[props.source].meta"
47
- :record="currentValues"
48
- @update:inValidity="customComponentsInValidity[column.name] = $event"
49
- @update:emptiness="customComponentsEmptiness[column.name] = $event"
50
- />
51
- </template>
52
- <template v-else>
53
- <Dropdown
54
- single
55
- v-if="column.foreignResource"
56
- :options="columnOptions[column.name] || []"
57
- :placeholder = "columnOptions[column.name]?.length ?'Select...': 'There are no options available'"
58
- :modelValue="currentValues[column.name]"
59
- @update:modelValue="setCurrentValue(column.name, $event)"
60
- ></Dropdown>
61
- <Dropdown
62
- single
63
- v-else-if="column.enum"
64
- :options="column.enum"
65
- :modelValue="currentValues[column.name]"
66
- @update:modelValue="setCurrentValue(column.name, $event)"
67
- />
68
- <Dropdown
69
- single
70
- v-else-if="column.type === 'boolean'"
71
- :options="[{ label: 'Yes', value: true }, { label: 'No', value: false }, { label: 'Unset', value: null }]"
72
- :modelValue="currentValues[column.name]"
73
- @update:modelValue="setCurrentValue(column.name, $event)"
74
- />
75
- <input
76
- v-else-if="['integer'].includes(column.type)"
77
- type="number"
78
- step="1"
79
- 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-40 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"
80
- placeholder="0"
81
- :value="currentValues[column.name]"
82
- @input="setCurrentValue(column.name, $event.target.value)"
83
- >
84
- <CustomDatePicker
85
- v-else-if="['datetime'].includes(column.type)"
86
- :column="column"
87
- :valueStart="currentValues[column.name]"
88
- auto-hide
89
- @update:valueStart="setCurrentValue(column.name, $event)"
90
- />
91
- <input
92
- v-else-if="['decimal', 'float'].includes(column.type)"
93
- type="number"
94
- step="0.1"
95
- 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-40 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"
96
- placeholder="0.0"
97
- :value="currentValues[column.name]"
98
- @input="setCurrentValue(column.name, $event.target.value)"
99
- />
100
- <textarea
101
- v-else-if="['text', 'richtext'].includes(column.type)"
102
- 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-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
103
- placeholder="Text"
104
- :value="currentValues[column.name]"
105
- @input="setCurrentValue(column.name, $event.target.value)"
106
- >
107
- </textarea>
108
- <input
109
- v-else
110
- :type="!column.masked || unmasked[column.name] ? 'text' : 'password'"
111
- 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-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
112
- placeholder="Text"
113
- :value="currentValues[column.name]"
114
- @input="setCurrentValue(column.name, $event.target.value)"
115
- autocomplete="false"
116
- data-lpignore="true"
117
- readonly
118
- onfocus="this.removeAttribute('readonly');"
119
- >
120
-
121
- <button
122
- v-if="column.masked"
123
- type="button"
124
- @click="unmasked[column.name] = !unmasked[column.name]"
125
- class="h-6 absolute inset-y-2 top-6 right-6 flex items-center pr-2 z-index-100 focus:outline-none"
126
- >
127
- <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
128
- <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
129
- </button>
130
- </template>
131
- <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
132
-
133
- <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
134
-
23
+ <td class="px-6 py-4 whitespace-nowrap">
24
+ {{ column.label }}
25
+ <span :data-tooltip-target="`tooltip-show-${i}`" class="relative inline-block">
26
+ <IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
27
+ :class="(columnError(column) && validating) ? 'text-red-500 dark:text-red-400' : 'text-gray-400 dark:text-gray-500'"
28
+ />
29
+ </span>
30
+ <div :id="`tooltip-show-${i}`"
31
+ 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">
32
+ Required field
33
+ <div class="tooltip-arrow" data-popper-arrow></div>
34
+ </div>
35
+ </td>
36
+ <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap relative">
37
+ <Dropdown
38
+ single
39
+ v-if="column.enum"
40
+ :options="column.enum"
41
+ :modelValue="currentValues[column.name]"
42
+ @update:modelValue="setCurrentValue(column.name, $event)"
43
+ />
44
+ <Dropdown
45
+ single
46
+ v-else-if="column.type === 'boolean'"
47
+ :options="[{ label: 'Yes', value: true }, { label: 'No', value: false }, { label: 'Unset', value: null }]"
48
+ :modelValue="currentValues[column.name]"
49
+ @update:modelValue="setCurrentValue(column.name, $event)"
50
+ />
51
+ <input
52
+ v-else-if="['integer'].includes(column.type)"
53
+ type="number"
54
+ step="1"
55
+ 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-40 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"
56
+ placeholder="0"
57
+ :value="currentValues[column.name]"
58
+ @input="setCurrentValue(column.name, $event.target.value)"
59
+ >
60
+ <CustomDatePicker
61
+ v-else-if="['datetime'].includes(column.type)"
62
+ :column="column"
63
+ :valueStart="currentValues[column.name]"
64
+ @update:valueStart="setCurrentValue(column.name, $event)"
65
+ />
66
+ <input
67
+ v-else-if="['decimal', 'float'].includes(column.type)"
68
+ type="number"
69
+ step="0.1"
70
+ 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-40 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"
71
+ placeholder="0.0"
72
+ :value="currentValues[column.name]"
73
+ @input="setCurrentValue(column.name, $event.target.value)"
74
+ />
75
+ <textarea
76
+ v-else-if="['text'].includes(column.type)"
77
+ 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-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
78
+ placeholder="Text"
79
+ :value="currentValues[column.name]"
80
+ @input="setCurrentValue(column.name, $event.target.value)"
81
+ >
82
+ </textarea>
83
+ <input
84
+ v-else
85
+ :type="!column.masked || unmasked[column.name] ? 'text' : 'password'"
86
+ 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-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
87
+ placeholder="Text"
88
+ :value="currentValues[column.name]"
89
+ @input="setCurrentValue(column.name, $event.target.value)"
90
+ autocomplete="false"
91
+ data-lpignore="true"
92
+ readonly
93
+ onfocus="this.removeAttribute('readonly');"
94
+ >
95
+
96
+ <button
97
+ v-if="column.masked"
98
+ type="button"
99
+ @click="unmasked[column.name] = !unmasked[column.name]"
100
+ class="h-6 absolute inset-y-2 top-6 right-6 flex items-center pr-2 z-index-100 focus:outline-none"
101
+ >
102
+ <IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
103
+ <IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
104
+ </button>
105
+
106
+
107
+
108
+ <div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
109
+
110
+ <div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
135
111
  </td>
136
112
  </tr>
137
113
 
@@ -145,55 +121,33 @@
145
121
 
146
122
  <script setup>
147
123
 
148
- import CustomDatePicker from "@/components/CustomDatePicker.vue";
149
- import Dropdown from '@/components/Dropdown.vue';
124
+ import { ref, computed, onMounted, watch } from 'vue';
150
125
  import { useCoreStore } from '@/stores/core';
151
- import { callAdminForthApi, getCustomComponent } from '@/utils';
152
- import { IconExclamationCircleSolid, IconEyeSlashSolid, IconEyeSolid } from '@iconify-prerendered/vue-flowbite';
153
- import { computedAsync } from '@vueuse/core';
154
- import { initFlowbite } from 'flowbite';
155
- import { computed, onMounted, ref, watch } from 'vue';
156
- import { useRouter } from 'vue-router';
157
-
158
- const router = useRouter();
126
+ import Dropdown from '@/components/Dropdown.vue';
127
+ import { IconExclamationCircleSolid } from '@iconify-prerendered/vue-flowbite';
128
+ import { initFlowbite } from 'flowbite'
129
+ import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
130
+ import CustomDatePicker from "@/components/CustomDatePicker.vue";
159
131
 
160
132
  const props = defineProps({
161
133
  loading: Boolean,
162
- resource: Object,
134
+ resourceColumns: Object,
163
135
  record: Object,
164
136
  validating: Boolean,
165
- source: String,
166
137
  });
167
138
 
139
+
168
140
  const unmasked = ref({});
169
141
 
170
142
  const mode = computed(() => props.record && Object.keys(props.record).length ? 'edit' : 'create');
171
143
 
172
144
  const emit = defineEmits(['update:record', 'update:isValid']);
173
145
 
174
- const currentValues = ref(null);
175
-
176
- const customComponentsInValidity = ref({});
177
- const customComponentsEmptiness = ref({});
178
-
146
+ const currentValues = ref({});
179
147
 
180
148
  const columnError = (column) => {
181
149
  const val = computed(() => {
182
- if (!currentValues.value) {
183
- return null;
184
- }
185
- if (customComponentsInValidity.value[column.name]) {
186
- return customComponentsInValidity.value[column.name];
187
- }
188
-
189
- if (
190
- column.required[mode.value] &&
191
- (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') &&
192
- // if component is custum it might tell other criteria for emptiness by emitting 'update:emptiness'
193
- // components which do not emit 'update:emptiness' will have undefined value in customComponentsEmptiness
194
- (customComponentsEmptiness.value[column.name] !== false)
195
-
196
- ) {
150
+ if ( column.required[mode.value] && (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') ) {
197
151
  return 'This field is required';
198
152
  }
199
153
  if ( column.type === 'string' || column.type === 'text' ) {
@@ -205,82 +159,39 @@ const columnError = (column) => {
205
159
  }
206
160
  }
207
161
  if ( ['integer', 'decimal', 'float'].includes(column.type) ) {
208
- if ( column.minValue !== undefined
209
- && currentValues.value[column.name] !== null
210
- && currentValues.value[column.name] < column.minValue
211
- ) {
162
+ if ( column.minValue !== undefined && currentValues.value[column.name] < column.minValue ) {
212
163
  return `This field must be greater than ${column.minValue}`;
213
164
  }
214
165
  if ( column.maxValue !== undefined && currentValues.value[column.name] > column.maxValue ) {
215
166
  return `This field must be less than ${column.maxValue}`;
216
167
  }
217
168
  }
218
- if ( column.validation && column.validation.length ) {
219
- const validationArray = column.validation;
220
- for (let i = 0; i < validationArray.length; i++) {
221
- if (validationArray[i].regExp) {
222
- const regExp = new RegExp(validationArray[i].regExp);
223
- let value = currentValues.value[column.name];
224
- if (value === undefined || value === null) {
225
- value = '';
226
- }
227
- if (!regExp.test(value)) {
228
- return validationArray[i].message;
229
- }
230
- }
231
- }
232
-
233
- }
234
169
  return null;
235
170
  });
236
171
  return val.value;
237
172
  };
238
173
 
239
174
 
175
+
176
+
240
177
  const setCurrentValue = (key, value) => {
241
- const col = props.resource.columns.find((column) => column.name === key);
242
- if (['integer', 'float'].includes(col.type) && (value || value === 0)) {
243
- currentValues.value[key] = +value;
244
- } else {
245
- currentValues.value[key] = value;
246
- }
247
- currentValues.value = { ...currentValues.value };
248
- console.log('3️⃣ setCurrentValue', key, value);
178
+ currentValues.value[key] = value;
179
+
249
180
  emit('update:record', currentValues.value);
250
181
  };
251
182
 
252
183
  onMounted(() => {
253
- currentValues.value = Object.assign({}, props.record);
254
- console.log('2️⃣ currentValues', JSON.stringify(currentValues.value));
184
+ Object.keys(props.record).forEach((key) => {
185
+ currentValues.value[key] = props.record[key];
186
+ });
255
187
  initFlowbite();
256
- emit('update:isValid', isValid.value);
257
188
  });
258
189
 
259
- const columnOptions = computedAsync(async () => {
260
- return (await Promise.all(
261
- Object.values(props.resource.columns).map(async (column) => {
262
- if (column.foreignResource) {
263
- const list = await callAdminForthApi({
264
- method: 'POST',
265
- path: `/get_resource_foreign_data`,
266
- body: {
267
- resourceId: router.currentRoute.value.params.resourceId,
268
- column: column.name,
269
- limit: 1000,
270
- offset: 0,
271
- },
272
- });
273
- return { [column.name]: list.items };
274
- }
275
- })
276
- )).reduce((acc, val) => Object.assign(acc, val), {})
277
-
278
- }, {});
279
-
190
+ const coreStore = useCoreStore();
280
191
 
281
192
  const editableColumns = computed(() => {
282
193
  const mode = props.record ? 'edit' : 'create';
283
- return props.resource?.columns?.filter(column => column.showIn.includes(mode));
194
+ return props.resourceColumns?.filter(column => column.showIn.includes(mode));
284
195
  });
285
196
 
286
197
  const isValid = computed(() => {
@@ -1,30 +1,18 @@
1
1
  <template>
2
2
  <div>
3
- <span @click="(e)=>{e.stopPropagation()}" v-if="column.foreignResource">
4
- <RouterLink v-if="record[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: record[column.name].pk } }">
6
- {{ record[column.name].label }}
7
- </RouterLink>
8
- <div v-else>
9
- <span class="text-gray-400">-</span>
10
- </div>
11
- </span>
12
-
13
- <span v-else-if="column.type === 'boolean'">
14
- <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>
3
+ <span v-if="column.type === 'boolean'">
4
+ <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>
15
5
  <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>
16
6
  </span>
17
7
  <span v-else-if="column.enum">
18
- {{ checkEmptyValues(column.enum.find(e => e.value === record[column.name])?.label,route.meta.type) }}
19
- </span>
20
- <span v-else-if="column.type === 'datetime'" class="whitespace-nowrap">
21
- {{ checkEmptyValues(formatDate(record[column.name]),route.meta.type) }}
8
+ {{ column.enum.find(e => e.value === row[column.name])?.label }}
22
9
  </span>
23
- <span v-else-if="column.type === 'richtext'">
24
- <div v-html="protectAgainstXSS(record[column.name])"></div>
10
+ <span v-else-if="column.type === 'datetime'">
11
+ {{ formatDate(row[column.name]) }}
12
+
25
13
  </span>
26
14
  <span v-else>
27
- {{ checkEmptyValues(record[column.name],route.meta.type) }}
15
+ {{ row[column.name] }}
28
16
  </span>
29
17
  </div>
30
18
  </template>
@@ -35,29 +23,19 @@
35
23
  import dayjs from 'dayjs';
36
24
  import utc from 'dayjs/plugin/utc';
37
25
  import timezone from 'dayjs/plugin/timezone';
38
- import {checkEmptyValues} from '@/utils';
39
- import { useRoute, useRouter } from 'vue-router';
40
- import sanitizeHtml from 'sanitize-html';
41
-
42
26
 
43
27
  import { useCoreStore } from '@/stores/core';
44
28
 
45
29
  const coreStore = useCoreStore();
46
- const route = useRoute();
47
-
48
30
 
49
31
  dayjs.extend(utc);
50
32
  dayjs.extend(timezone);
51
33
 
52
34
  const props = defineProps({
53
35
  column: Object,
54
- record: Object
36
+ row: Object
55
37
  });
56
38
 
57
- function protectAgainstXSS(value) {
58
- return sanitizeHtml(value);
59
- }
60
-
61
39
 
62
40
  function formatDate(date) {
63
41
  if (!date) return '';
@@ -23,5 +23,4 @@
23
23
  // ::-webkit-scrollbar-thumb:hover {
24
24
  // @apply bg-gray-700
25
25
  // }
26
- // }
27
-
26
+ // }
@@ -6,10 +6,10 @@ import { createPinia } from 'pinia'
6
6
  import App from './App.vue'
7
7
  import router from './router'
8
8
 
9
- export const app = createApp(App)
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,23 +1,23 @@
1
1
  import { createRouter, createWebHistory } from 'vue-router'
2
+ import HomeView from '../views/HomeView.vue'
2
3
  import ResourceParent from '@/views/ResourceParent.vue'
3
- import { useUserStore } from '@/stores/user'
4
- /* IMPORTANT:ADMINFORTH ROUTES IMPORTS */
4
+ import ListView from '@/views/ListView.vue'
5
+ import ShowView from '@/views/ShowView.vue'
6
+ import EditView from '@/views/EditView.vue'
7
+ import CreateView from '@/views/CreateView.vue'
5
8
 
6
9
  const router = createRouter({
7
10
  history: createWebHistory(import.meta.env.BASE_URL),
8
11
  routes: [
12
+ {
13
+ path: '/',
14
+ name: 'home',
15
+ component: HomeView
16
+ },
9
17
  {
10
18
  path: '/login',
11
19
  name: 'login',
12
- component: () => import('@/views/LoginView.vue'),
13
- meta: { title: 'login' },
14
- beforeEnter: async (to, from, next) => {
15
- if(localStorage.getItem('isAuthorized') === 'true'){
16
- next({name: 'home'})
17
- } else {
18
- next()
19
- }
20
- }
20
+ component: () => import('@/views/LoginView.vue')
21
21
  },
22
22
  {
23
23
  path: '/resource/:resourceId',
@@ -26,29 +26,23 @@ const router = createRouter({
26
26
  children: [
27
27
  {
28
28
  path: '',
29
- component: () => import('@/views/ListView.vue'),
30
- name: 'resource-list',
31
- meta: { title: 'list',type: 'list' }
29
+ component: ListView,
30
+ name: 'resource-list'
32
31
  },
33
32
  {
34
33
  path: 'show/:primaryKey',
35
- component: () => import('@/views/ShowView.vue'),
36
- name: 'resource-show',
37
- meta: { title: 'show', type: 'show'}
38
-
34
+ component: ShowView,
35
+ name: 'resource-show'
39
36
  },
40
37
  {
41
38
  path: 'edit/:primaryKey',
42
- component: () => import('@/views/EditView.vue'),
43
- name: 'resource-edit',
44
- meta: { title: 'edit', type: 'edit'}
39
+ component: EditView,
40
+ name: 'resource-edit'
45
41
  },
46
42
  {
47
43
  path: 'create',
48
- component: () => import('@/views/CreateView.vue'),
49
- name: 'resource-create',
50
- meta: { title: 'create', type: 'create'}
51
-
44
+ component: CreateView,
45
+ name: 'resource-create'
52
46
  },
53
47
  ]
54
48
  },
@@ -56,8 +50,4 @@ const router = createRouter({
56
50
  ]
57
51
  })
58
52
 
59
-
60
-
61
-
62
-
63
53
  export default router