adminforth 1.0.32 → 1.0.34
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.
- package/dataConnectors/sqlite.ts +1 -0
- package/dist/dataConnectors/sqlite.js +1 -0
- package/dist/index.js +87 -74
- package/dist/modules/utils.js +11 -0
- package/dist/spa/spa/src/App.vue +0 -3
- package/dist/spa/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/spa/src/components/CustomRangePicker.vue +32 -19
- package/dist/spa/spa/src/components/Dropdown.vue +6 -1
- package/dist/spa/spa/src/components/ResourceForm.vue +125 -102
- package/dist/spa/spa/src/components/ValueRenderer.vue +11 -1
- package/dist/spa/spa/src/stores/core.ts +21 -19
- package/dist/spa/spa/src/views/CreateView.vue +16 -6
- package/dist/spa/spa/src/views/EditView.vue +15 -5
- package/dist/spa/spa/src/views/ListView.vue +31 -17
- package/dist/spa/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/spa/src/views/ShowView.vue +21 -6
- package/dist/types/AdminForthConfig.js +1 -0
- package/index.ts +73 -149
- package/modules/utils.ts +16 -0
- package/package.json +1 -1
- package/spa/src/App.vue +0 -3
- package/spa/src/components/CustomDatePicker.vue +3 -3
- package/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/spa/src/components/CustomRangePicker.vue +32 -19
- package/spa/src/components/Dropdown.vue +6 -1
- package/spa/src/components/ResourceForm.vue +125 -102
- package/spa/src/components/ValueRenderer.vue +11 -1
- package/spa/src/stores/core.ts +21 -19
- package/spa/src/views/CreateView.vue +16 -6
- package/spa/src/views/EditView.vue +15 -5
- package/spa/src/views/ListView.vue +31 -17
- package/spa/src/views/ResourceParent.vue +1 -1
- package/spa/src/views/ShowView.vue +21 -6
- package/types/AdminForthConfig.ts +159 -0
|
@@ -20,102 +20,113 @@
|
|
|
20
20
|
<tr v-for="column, i in editableColumns" :key="column.name"
|
|
21
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"
|
|
22
22
|
>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
<!-- if column is in customComponentsPerColumn, use this component. If not, use this code -->
|
|
24
|
+
<template v-if="customComponentsPerColumn[column.name]">
|
|
25
|
+
<component
|
|
26
|
+
:is="customComponentsPerColumn[column.name]"
|
|
27
|
+
:column="column"
|
|
28
|
+
:value="currentValues[column.name]"
|
|
29
|
+
@update:value="setCurrentValue(column.name, $event)"
|
|
28
30
|
/>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
31
|
+
</template>
|
|
32
|
+
<template v-else>
|
|
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
|
+
</td>
|
|
46
|
+
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap relative">
|
|
47
|
+
<Dropdown
|
|
48
|
+
single
|
|
49
|
+
v-if="column.foreignResource"
|
|
50
|
+
:options="columnOptions[column.name] || []"
|
|
51
|
+
:modelValue="currentValues[column.name]"
|
|
52
|
+
@update:modelValue="setCurrentValue(column.name, $event)"
|
|
53
|
+
/>
|
|
54
|
+
<Dropdown
|
|
55
|
+
single
|
|
56
|
+
v-else-if="column.enum"
|
|
57
|
+
:options="column.enum"
|
|
58
|
+
:modelValue="currentValues[column.name]"
|
|
59
|
+
@update:modelValue="setCurrentValue(column.name, $event)"
|
|
60
|
+
/>
|
|
61
|
+
<Dropdown
|
|
62
|
+
single
|
|
63
|
+
v-else-if="column.type === 'boolean'"
|
|
64
|
+
:options="[{ label: 'Yes', value: true }, { label: 'No', value: false }, { label: 'Unset', value: null }]"
|
|
65
|
+
:modelValue="currentValues[column.name]"
|
|
66
|
+
@update:modelValue="setCurrentValue(column.name, $event)"
|
|
67
|
+
/>
|
|
68
|
+
<input
|
|
69
|
+
v-else-if="['integer'].includes(column.type)"
|
|
70
|
+
type="number"
|
|
71
|
+
step="1"
|
|
72
|
+
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"
|
|
73
|
+
placeholder="0"
|
|
74
|
+
:value="currentValues[column.name]"
|
|
75
|
+
@input="setCurrentValue(column.name, $event.target.value)"
|
|
76
|
+
>
|
|
77
|
+
<CustomDatePicker
|
|
78
|
+
v-else-if="['datetime'].includes(column.type)"
|
|
79
|
+
:column="column"
|
|
80
|
+
:valueStart="currentValues[column.name]"
|
|
81
|
+
@update:valueStart="setCurrentValue(column.name, $event)"
|
|
82
|
+
/>
|
|
83
|
+
<input
|
|
84
|
+
v-else-if="['decimal', 'float'].includes(column.type)"
|
|
85
|
+
type="number"
|
|
86
|
+
step="0.1"
|
|
87
|
+
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"
|
|
88
|
+
placeholder="0.0"
|
|
89
|
+
:value="currentValues[column.name]"
|
|
90
|
+
@input="setCurrentValue(column.name, $event.target.value)"
|
|
91
|
+
/>
|
|
92
|
+
<textarea
|
|
93
|
+
v-else-if="['text'].includes(column.type)"
|
|
94
|
+
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"
|
|
95
|
+
placeholder="Text"
|
|
96
|
+
:value="currentValues[column.name]"
|
|
97
|
+
@input="setCurrentValue(column.name, $event.target.value)"
|
|
98
|
+
>
|
|
99
|
+
</textarea>
|
|
100
|
+
<input
|
|
101
|
+
v-else
|
|
102
|
+
:type="!column.masked || unmasked[column.name] ? 'text' : 'password'"
|
|
103
|
+
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"
|
|
104
|
+
placeholder="Text"
|
|
105
|
+
:value="currentValues[column.name]"
|
|
106
|
+
@input="setCurrentValue(column.name, $event.target.value)"
|
|
107
|
+
autocomplete="false"
|
|
108
|
+
data-lpignore="true"
|
|
109
|
+
readonly
|
|
110
|
+
onfocus="this.removeAttribute('readonly');"
|
|
111
|
+
>
|
|
112
|
+
|
|
113
|
+
<button
|
|
114
|
+
v-if="column.masked"
|
|
115
|
+
type="button"
|
|
116
|
+
@click="unmasked[column.name] = !unmasked[column.name]"
|
|
117
|
+
class="h-6 absolute inset-y-2 top-6 right-6 flex items-center pr-2 z-index-100 focus:outline-none"
|
|
118
|
+
>
|
|
119
|
+
<IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
|
|
120
|
+
<IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
|
|
121
|
+
</button>
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
|
|
126
|
+
|
|
127
|
+
<div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
|
|
128
|
+
</td>
|
|
129
|
+
</template>
|
|
119
130
|
</tr>
|
|
120
131
|
|
|
121
132
|
</tbody>
|
|
@@ -128,16 +139,15 @@
|
|
|
128
139
|
|
|
129
140
|
<script setup>
|
|
130
141
|
|
|
131
|
-
import { ref, computed, onMounted, watch } from 'vue';
|
|
132
|
-
import { useCoreStore } from '@/stores/core';
|
|
133
|
-
import Dropdown from '@/components/Dropdown.vue';
|
|
134
|
-
import { IconExclamationCircleSolid } from '@iconify-prerendered/vue-flowbite';
|
|
135
|
-
import { initFlowbite } from 'flowbite'
|
|
136
|
-
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
137
142
|
import CustomDatePicker from "@/components/CustomDatePicker.vue";
|
|
143
|
+
import Dropdown from '@/components/Dropdown.vue';
|
|
144
|
+
import { useCoreStore } from '@/stores/core';
|
|
138
145
|
import { callAdminForthApi } from '@/utils';
|
|
139
|
-
import {
|
|
146
|
+
import { IconExclamationCircleSolid, IconEyeSlashSolid, IconEyeSolid } from '@iconify-prerendered/vue-flowbite';
|
|
140
147
|
import { computedAsync } from '@vueuse/core';
|
|
148
|
+
import { initFlowbite } from 'flowbite';
|
|
149
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
150
|
+
import { useRouter } from 'vue-router';
|
|
141
151
|
|
|
142
152
|
const router = useRouter();
|
|
143
153
|
|
|
@@ -146,6 +156,7 @@ const props = defineProps({
|
|
|
146
156
|
resourceColumns: Object,
|
|
147
157
|
record: Object,
|
|
148
158
|
validating: Boolean,
|
|
159
|
+
customComponentsPerColumn: Object,
|
|
149
160
|
});
|
|
150
161
|
|
|
151
162
|
|
|
@@ -179,6 +190,18 @@ const columnError = (column) => {
|
|
|
179
190
|
return `This field must be less than ${column.maxValue}`;
|
|
180
191
|
}
|
|
181
192
|
}
|
|
193
|
+
if ( column.validation && column.validation.length ){
|
|
194
|
+
const validationArray = column.validation;
|
|
195
|
+
for (let i = 0; i < validationArray.length; i++) {
|
|
196
|
+
if (validationArray[i].regExp) {
|
|
197
|
+
const regExp = new RegExp(validationArray[i].regExp);
|
|
198
|
+
if (!regExp.test(currentValues.value[column.name])) {
|
|
199
|
+
return validationArray[i].message;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
}
|
|
182
205
|
return null;
|
|
183
206
|
});
|
|
184
207
|
return val.value;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<span v-if="column.
|
|
3
|
+
<span v-if="column.foreignResource">
|
|
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 }}
|
|
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'">
|
|
4
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>
|
|
5
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>
|
|
6
16
|
</span>
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import { ref, computed } from 'vue'
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
3
|
import { callAdminForthApi } from '@/utils';
|
|
4
|
-
import router from '@/router';
|
|
5
|
-
|
|
6
|
-
async function findHomepage(menu) {
|
|
7
|
-
for (const item of menu) {
|
|
8
|
-
if (item.homepage) {
|
|
9
|
-
return item;
|
|
10
|
-
}
|
|
11
|
-
if (item.children) {
|
|
12
|
-
const res = findHomepage(item.children);
|
|
13
|
-
if (res) {
|
|
14
|
-
return res;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
4
|
|
|
21
5
|
export const useCoreStore = defineStore('core', () => {
|
|
22
6
|
const resourceById = ref([]);
|
|
@@ -40,6 +24,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
40
24
|
}, {});
|
|
41
25
|
config.value = resp.config;
|
|
42
26
|
user.value = resp.user;
|
|
27
|
+
console.log('🌍 AdminForth version', resp.version);
|
|
43
28
|
|
|
44
29
|
// find homepage:true in menu recuresively
|
|
45
30
|
|
|
@@ -47,15 +32,32 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
47
32
|
|
|
48
33
|
async function fetchRecord({ resourceId, primaryKey }) {
|
|
49
34
|
record.value = null;
|
|
35
|
+
|
|
36
|
+
if (!resourceColumns.value) {
|
|
37
|
+
throw new Error('Columns not fetched yet');
|
|
38
|
+
}
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
path: '/
|
|
40
|
+
const respData = await callAdminForthApi({
|
|
41
|
+
path: '/get_resource_data',
|
|
53
42
|
method: 'POST',
|
|
54
43
|
body: {
|
|
44
|
+
source: 'show',
|
|
55
45
|
resourceId: resourceId,
|
|
56
|
-
|
|
46
|
+
filters: [
|
|
47
|
+
{
|
|
48
|
+
field: resourceColumns.value.find((col) => col.primaryKey).name,
|
|
49
|
+
operator: 'eq',
|
|
50
|
+
value: primaryKey
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
sort: [],
|
|
54
|
+
limit: 1,
|
|
55
|
+
offset: 0
|
|
57
56
|
}
|
|
58
57
|
});
|
|
58
|
+
|
|
59
|
+
console.log('📦 record', respData);
|
|
60
|
+
record.value = respData.data[0];
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
async function fetchColumns({ resourceId }) {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
@update:record="onUpdateRecord"
|
|
32
32
|
@update:isValid="isValid = $event"
|
|
33
33
|
:validating="validating"
|
|
34
|
+
:customComponentsPerColumn="createComponentsPerColumn"
|
|
34
35
|
>
|
|
35
36
|
</ResourceForm>
|
|
36
37
|
|
|
@@ -40,14 +41,14 @@
|
|
|
40
41
|
|
|
41
42
|
<script setup>
|
|
42
43
|
|
|
43
|
-
import
|
|
44
|
-
import
|
|
44
|
+
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
45
|
+
import ResourceForm from '@/components/ResourceForm.vue';
|
|
46
|
+
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
45
47
|
import { useCoreStore } from '@/stores/core';
|
|
46
48
|
import { callAdminForthApi } from '@/utils';
|
|
47
|
-
import ResourceForm from '@/components/ResourceForm.vue';
|
|
48
|
-
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
49
49
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
50
|
-
import
|
|
50
|
+
import { defineAsyncComponent, onMounted, ref } from 'vue';
|
|
51
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
51
52
|
|
|
52
53
|
const isValid = ref(false);
|
|
53
54
|
const validating = ref(false);
|
|
@@ -59,6 +60,7 @@ const route = useRoute();
|
|
|
59
60
|
const router = useRouter();
|
|
60
61
|
|
|
61
62
|
const record = ref({});
|
|
63
|
+
let createComponentsPerColumn = {};
|
|
62
64
|
|
|
63
65
|
const coreStore = useCoreStore();
|
|
64
66
|
|
|
@@ -69,9 +71,17 @@ async function onUpdateRecord(newRecord) {
|
|
|
69
71
|
|
|
70
72
|
onMounted(async () => {
|
|
71
73
|
loading.value = true;
|
|
72
|
-
coreStore.fetchColumns({
|
|
74
|
+
await coreStore.fetchColumns({
|
|
73
75
|
resourceId: route.params.resourceId
|
|
74
76
|
});
|
|
77
|
+
createComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
78
|
+
if (column.component?.create) {
|
|
79
|
+
const path = column.component.create.replace('@@', '../custom');
|
|
80
|
+
let component = defineAsyncComponent(() => import(`${path}`))
|
|
81
|
+
acc[column.name] = component;
|
|
82
|
+
}
|
|
83
|
+
return acc;
|
|
84
|
+
}, {});
|
|
75
85
|
loading.value = false;
|
|
76
86
|
});
|
|
77
87
|
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
@update:record="onUpdateRecord"
|
|
29
29
|
@update:isValid="isValid = $event"
|
|
30
30
|
:validating="validating"
|
|
31
|
+
:customComponentsPerColumn="editComponentsPerColumn"
|
|
31
32
|
>
|
|
32
33
|
</ResourceForm>
|
|
33
34
|
</div>
|
|
@@ -36,14 +37,14 @@
|
|
|
36
37
|
|
|
37
38
|
<script setup>
|
|
38
39
|
|
|
39
|
-
import { ref, computed, onMounted } from 'vue';
|
|
40
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
41
|
-
import { useCoreStore } from '@/stores/core';
|
|
42
|
-
import ResourceForm from '@/components/ResourceForm.vue';
|
|
43
40
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
44
|
-
import
|
|
41
|
+
import ResourceForm from '@/components/ResourceForm.vue';
|
|
45
42
|
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
43
|
+
import { useCoreStore } from '@/stores/core';
|
|
46
44
|
import { callAdminForthApi } from '@/utils';
|
|
45
|
+
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
46
|
+
import { defineAsyncComponent, onMounted, ref } from 'vue';
|
|
47
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
47
48
|
|
|
48
49
|
const coreStore = useCoreStore();
|
|
49
50
|
|
|
@@ -58,6 +59,7 @@ const loading = ref(false);
|
|
|
58
59
|
const saving = ref(false);
|
|
59
60
|
|
|
60
61
|
const record = ref({});
|
|
62
|
+
let editComponentsPerColumn = {};
|
|
61
63
|
|
|
62
64
|
async function onUpdateRecord(newRecord) {
|
|
63
65
|
record.value = newRecord;
|
|
@@ -75,6 +77,14 @@ onMounted(async () => {
|
|
|
75
77
|
primaryKey: route.params.primaryKey,
|
|
76
78
|
});
|
|
77
79
|
|
|
80
|
+
editComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
81
|
+
if (column.component?.edit) {
|
|
82
|
+
const path = column.component.edit.replace('@@', '../custom');
|
|
83
|
+
let component = defineAsyncComponent(() => import(`${path}`))
|
|
84
|
+
acc[column.name] = component;
|
|
85
|
+
}
|
|
86
|
+
return acc;
|
|
87
|
+
}, {});
|
|
78
88
|
loading.value = false;
|
|
79
89
|
});
|
|
80
90
|
|
|
@@ -198,7 +198,12 @@
|
|
|
198
198
|
</div>
|
|
199
199
|
</td>
|
|
200
200
|
<td v-for="c in columnsListed" class="px-6 py-4">
|
|
201
|
-
|
|
201
|
+
<!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
|
|
202
|
+
<component
|
|
203
|
+
:is="listComponentsPerColumn[c.name] || ValueRenderer"
|
|
204
|
+
:column="c"
|
|
205
|
+
:row="row"
|
|
206
|
+
/>
|
|
202
207
|
</td>
|
|
203
208
|
<td class="flex items-center px-6 py-4">
|
|
204
209
|
|
|
@@ -310,29 +315,27 @@
|
|
|
310
315
|
</template>
|
|
311
316
|
|
|
312
317
|
<script setup>
|
|
313
|
-
import {ref, onMounted, watch, computed} from 'vue';
|
|
314
|
-
import {callAdminForthApi, getIcon} from '@/utils';
|
|
315
|
-
import {useRoute} from 'vue-router';
|
|
316
|
-
import {useCoreStore} from '@/stores/core';
|
|
317
|
-
import {useModalStore} from '@/stores/modal';
|
|
318
318
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
319
|
-
import {
|
|
319
|
+
import { useCoreStore } from '@/stores/core';
|
|
320
|
+
import { useModalStore } from '@/stores/modal';
|
|
321
|
+
import { callAdminForthApi, getIcon } from '@/utils';
|
|
322
|
+
import { initFlowbite } from 'flowbite';
|
|
323
|
+
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
|
|
324
|
+
import { useRoute } from 'vue-router';
|
|
320
325
|
|
|
321
326
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
322
327
|
|
|
323
328
|
import {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
IconInboxFullSolid,
|
|
327
|
-
IconInboxOutline,
|
|
328
|
-
IconPlusOutline
|
|
329
|
+
IconInboxOutline,
|
|
330
|
+
IconPlusOutline
|
|
329
331
|
} from '@iconify-prerendered/vue-flowbite';
|
|
330
332
|
|
|
331
333
|
import {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
IconBanOutline,
|
|
335
|
+
IconEyeSolid,
|
|
336
|
+
IconFilterOutline,
|
|
337
|
+
IconPenSolid,
|
|
338
|
+
IconTrashBinSolid
|
|
336
339
|
} from '@iconify-prerendered/vue-flowbite';
|
|
337
340
|
|
|
338
341
|
import Filters from '@/components/Filters.vue';
|
|
@@ -375,6 +378,7 @@ async function selectAll(value) {
|
|
|
375
378
|
|
|
376
379
|
const pageSize = computed(() => coreStore.resourceById[route.params.resourceId]?.pageSize || DEFAULT_PAGE_SIZE);
|
|
377
380
|
const totalPages = computed(() => Math.ceil(totalRows.value / pageSize.value));
|
|
381
|
+
let listComponentsPerColumn = {};
|
|
378
382
|
const allFromThisPageChecked = computed(() => {
|
|
379
383
|
if (!rows.value) return false;
|
|
380
384
|
return rows.value.every((r) => checkboxes.value.includes(r.id));
|
|
@@ -418,6 +422,7 @@ async function getList() {
|
|
|
418
422
|
path: '/get_resource_data',
|
|
419
423
|
method: 'POST',
|
|
420
424
|
body: {
|
|
425
|
+
source: 'list',
|
|
421
426
|
resourceId: route.params.resourceId,
|
|
422
427
|
limit: pageSize.value,
|
|
423
428
|
offset: (page.value - 1) * pageSize.value,
|
|
@@ -425,7 +430,16 @@ async function getList() {
|
|
|
425
430
|
sort: sort.value,
|
|
426
431
|
}
|
|
427
432
|
});
|
|
428
|
-
|
|
433
|
+
listComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
434
|
+
if (column.component?.list) {
|
|
435
|
+
const path = column.component.list.replace('@@', '../custom');
|
|
436
|
+
let component = defineAsyncComponent(() => import(`${path}`))
|
|
437
|
+
acc[column.name] = component;
|
|
438
|
+
}
|
|
439
|
+
return acc;
|
|
440
|
+
}, {});
|
|
441
|
+
|
|
442
|
+
fetchStatus.value.pending = false;
|
|
429
443
|
rows.value = data.data?.map(row => {
|
|
430
444
|
row._primaryKeyValue = row[coreStore.resourceColumns.find(c => c.primaryKey).name];
|
|
431
445
|
return row;
|
|
@@ -48,7 +48,12 @@
|
|
|
48
48
|
{{ column.label }}
|
|
49
49
|
</td>
|
|
50
50
|
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
51
|
-
|
|
51
|
+
<!-- if column.name in showComponentsPerColumn, render it. If not, render ValueRenderer -->
|
|
52
|
+
<component
|
|
53
|
+
:is="showComponentsPerColumn[column.name] || ValueRenderer"
|
|
54
|
+
:column="column"
|
|
55
|
+
:row="coreStore.record"
|
|
56
|
+
/>
|
|
52
57
|
</td>
|
|
53
58
|
</tr>
|
|
54
59
|
|
|
@@ -63,18 +68,18 @@
|
|
|
63
68
|
|
|
64
69
|
<script setup>
|
|
65
70
|
|
|
66
|
-
import { ref, computed, onMounted } from 'vue';
|
|
67
|
-
import { useRoute } from 'vue-router';
|
|
68
|
-
import { callAdminForthApi } from '@/utils';
|
|
69
71
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
70
|
-
import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
|
|
71
|
-
import { useCoreStore } from '@/stores/core';
|
|
72
72
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
73
|
+
import { useCoreStore } from '@/stores/core';
|
|
74
|
+
import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
|
|
75
|
+
import { defineAsyncComponent, onMounted, ref } from 'vue';
|
|
76
|
+
import { useRoute } from 'vue-router';
|
|
73
77
|
|
|
74
78
|
|
|
75
79
|
const item = ref(null);
|
|
76
80
|
const route = useRoute();
|
|
77
81
|
const loading = ref(false);
|
|
82
|
+
let showComponentsPerColumn = {};
|
|
78
83
|
|
|
79
84
|
const coreStore = useCoreStore();
|
|
80
85
|
|
|
@@ -87,6 +92,16 @@ onMounted(async () => {
|
|
|
87
92
|
resourceId: route.params.resourceId,
|
|
88
93
|
primaryKey: route.params.primaryKey,
|
|
89
94
|
});
|
|
95
|
+
showComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
96
|
+
if (column.component?.show) {
|
|
97
|
+
const path = column.component.show.replace('@@', '../custom');
|
|
98
|
+
console.log('path', path);
|
|
99
|
+
let component = defineAsyncComponent(() => import(`${path}`))
|
|
100
|
+
acc[column.name] = component;
|
|
101
|
+
}
|
|
102
|
+
return acc;
|
|
103
|
+
}, {});
|
|
104
|
+
console.log('showComponentsPerColumn', showComponentsPerColumn);
|
|
90
105
|
loading.value = false;
|
|
91
106
|
});
|
|
92
107
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|