adminforth 1.1.29 → 1.1.30
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/dist/index.js +0 -1
- package/dist/plugins/AccessControl/index.js +3 -56
- package/dist/plugins/ForeignInlineListPlugin/custom/InlineList.vue +2 -2
- package/dist/plugins/ForeignInlineListPlugin/index.js +0 -1
- package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +248 -0
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +1 -189
- package/dist/spa/package.json +1 -6
- package/dist/spa/spa/src/components/ResourceListTable.vue +2 -2
- package/dist/spa/spa/src/components/ShowTableItem.vue +14 -0
- package/dist/spa/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/spa/src/views/ListView.vue +2 -2
- package/dist/spa/spa/src/views/ShowView.vue +2 -2
- package/dist/spa/src/App.vue +57 -76
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/src/components/Dropdown.vue +4 -13
- package/dist/spa/src/components/Filters.vue +22 -55
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +99 -157
- package/dist/spa/src/components/ValueRenderer.vue +1 -11
- package/dist/spa/src/router/index.ts +2 -3
- package/dist/spa/src/stores/core.ts +33 -22
- package/dist/spa/src/utils.ts +3 -5
- package/dist/spa/src/views/CreateView.vue +7 -15
- package/dist/spa/src/views/EditView.vue +8 -36
- package/dist/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/src/views/ListView.vue +23 -35
- package/dist/spa/src/views/LoginView.vue +1 -1
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +9 -20
- package/dist/spa/tailwind.config.js +2 -5
- package/index.ts +0 -1
- package/package.json +1 -1
- package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +2 -2
- package/plugins/ForeignInlineListPlugin/index.ts +0 -1
- package/spa/src/components/ResourceListTable.vue +2 -2
- package/spa/src/views/ListView.vue +2 -2
- package/spa/src/views/ShowView.vue +2 -2
- package/dist/spa/src/components/CustomRangePicker.vue +0 -148
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
|
|
4
4
|
<div
|
|
5
|
-
class="relative shadow-
|
|
5
|
+
class="relative shadow-md sm:rounded-lg dark:shadow-2xl"
|
|
6
6
|
>
|
|
7
7
|
<form autocomplete="off" @submit.prevent>
|
|
8
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-
|
|
9
|
+
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
10
10
|
<tr>
|
|
11
11
|
<th scope="col" class="px-6 py-3">
|
|
12
12
|
Field
|
|
@@ -18,116 +18,97 @@
|
|
|
18
18
|
</thead>
|
|
19
19
|
<tbody>
|
|
20
20
|
<tr v-for="column, i in editableColumns" :key="column.name"
|
|
21
|
-
class="bg-
|
|
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
|
-
|
|
28
|
-
:value="currentValues[column.name]"
|
|
29
|
-
@update:value="setCurrentValue(column.name, $event)"
|
|
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'"
|
|
30
28
|
/>
|
|
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
|
-
<button
|
|
115
|
-
v-if="column.masked"
|
|
116
|
-
type="button"
|
|
117
|
-
@click="unmasked[column.name] = !unmasked[column.name]"
|
|
118
|
-
class="h-6 absolute inset-y-2 top-6 right-6 flex items-center pr-2 z-index-100 focus:outline-none"
|
|
119
|
-
>
|
|
120
|
-
<IconEyeSolid class="w-6 h-6 text-gray-400" v-if="!unmasked[column.name]" />
|
|
121
|
-
<IconEyeSlashSolid class="w-6 h-6 text-gray-400" v-else />
|
|
122
|
-
</button>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
|
|
127
|
-
|
|
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>
|
|
129
|
-
</td>
|
|
130
|
-
</template>
|
|
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>
|
|
111
|
+
</td>
|
|
131
112
|
</tr>
|
|
132
113
|
|
|
133
114
|
</tbody>
|
|
@@ -140,24 +121,19 @@
|
|
|
140
121
|
|
|
141
122
|
<script setup>
|
|
142
123
|
|
|
143
|
-
import
|
|
144
|
-
import Dropdown from '@/components/Dropdown.vue';
|
|
124
|
+
import { ref, computed, onMounted, watch } from 'vue';
|
|
145
125
|
import { useCoreStore } from '@/stores/core';
|
|
146
|
-
import
|
|
147
|
-
import { IconExclamationCircleSolid
|
|
148
|
-
import {
|
|
149
|
-
import {
|
|
150
|
-
import
|
|
151
|
-
import { useRouter } from 'vue-router';
|
|
152
|
-
|
|
153
|
-
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";
|
|
154
131
|
|
|
155
132
|
const props = defineProps({
|
|
156
133
|
loading: Boolean,
|
|
157
134
|
resourceColumns: Object,
|
|
158
135
|
record: Object,
|
|
159
136
|
validating: Boolean,
|
|
160
|
-
customComponentsPerColumn: Object,
|
|
161
137
|
});
|
|
162
138
|
|
|
163
139
|
|
|
@@ -169,7 +145,6 @@ const emit = defineEmits(['update:record', 'update:isValid']);
|
|
|
169
145
|
|
|
170
146
|
const currentValues = ref({});
|
|
171
147
|
|
|
172
|
-
|
|
173
148
|
const columnError = (column) => {
|
|
174
149
|
const val = computed(() => {
|
|
175
150
|
if ( column.required[mode.value] && (currentValues.value[column.name] === undefined || currentValues.value[column.name] === null || currentValues.value[column.name] === '') ) {
|
|
@@ -191,24 +166,14 @@ const columnError = (column) => {
|
|
|
191
166
|
return `This field must be less than ${column.maxValue}`;
|
|
192
167
|
}
|
|
193
168
|
}
|
|
194
|
-
if ( column.validation && column.validation.length ){
|
|
195
|
-
const validationArray = column.validation;
|
|
196
|
-
for (let i = 0; i < validationArray.length; i++) {
|
|
197
|
-
if (validationArray[i].regExp) {
|
|
198
|
-
const regExp = new RegExp(validationArray[i].regExp);
|
|
199
|
-
if (!regExp.test(currentValues.value[column.name])) {
|
|
200
|
-
return validationArray[i].message;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
}
|
|
206
169
|
return null;
|
|
207
170
|
});
|
|
208
171
|
return val.value;
|
|
209
172
|
};
|
|
210
173
|
|
|
211
174
|
|
|
175
|
+
|
|
176
|
+
|
|
212
177
|
const setCurrentValue = (key, value) => {
|
|
213
178
|
currentValues.value[key] = value;
|
|
214
179
|
|
|
@@ -220,31 +185,8 @@ onMounted(() => {
|
|
|
220
185
|
currentValues.value[key] = props.record[key];
|
|
221
186
|
});
|
|
222
187
|
initFlowbite();
|
|
223
|
-
|
|
224
|
-
|
|
225
188
|
});
|
|
226
189
|
|
|
227
|
-
const columnOptions = computedAsync(async () => {
|
|
228
|
-
return (await Promise.all(
|
|
229
|
-
Object.values(props.resourceColumns).map(async (column) => {
|
|
230
|
-
if (column.foreignResource) {
|
|
231
|
-
const list = await callAdminForthApi({
|
|
232
|
-
method: 'POST',
|
|
233
|
-
path: `/get_resource_foreign_data`,
|
|
234
|
-
body: {
|
|
235
|
-
resourceId: router.currentRoute.value.params.resourceId,
|
|
236
|
-
column: column.name,
|
|
237
|
-
limit: 1000,
|
|
238
|
-
offset: 0,
|
|
239
|
-
},
|
|
240
|
-
});
|
|
241
|
-
return { [column.name]: list.items };
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
)).reduce((acc, val) => Object.assign(acc, val), {})
|
|
245
|
-
|
|
246
|
-
}, {});
|
|
247
|
-
|
|
248
190
|
const coreStore = useCoreStore();
|
|
249
191
|
|
|
250
192
|
const editableColumns = computed(() => {
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<span v-if="column.
|
|
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'">
|
|
3
|
+
<span v-if="column.type === 'boolean'">
|
|
14
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>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
|
+
import HomeView from '../views/HomeView.vue'
|
|
2
3
|
import ResourceParent from '@/views/ResourceParent.vue'
|
|
3
4
|
import ListView from '@/views/ListView.vue'
|
|
4
5
|
import ShowView from '@/views/ShowView.vue'
|
|
@@ -11,9 +12,7 @@ const router = createRouter({
|
|
|
11
12
|
{
|
|
12
13
|
path: '/',
|
|
13
14
|
name: 'home',
|
|
14
|
-
|
|
15
|
-
/* IMPORTANT:ADMINFORTH REDIRECT */
|
|
16
|
-
|
|
15
|
+
component: HomeView
|
|
17
16
|
},
|
|
18
17
|
{
|
|
19
18
|
path: '/login',
|
|
@@ -1,6 +1,22 @@
|
|
|
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
|
+
}
|
|
4
20
|
|
|
5
21
|
export const useCoreStore = defineStore('core', () => {
|
|
6
22
|
const resourceById = ref([]);
|
|
@@ -24,40 +40,35 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
24
40
|
}, {});
|
|
25
41
|
config.value = resp.config;
|
|
26
42
|
user.value = resp.user;
|
|
27
|
-
console.log('🌍 AdminForth v', resp.version);
|
|
28
43
|
|
|
29
44
|
// find homepage:true in menu recuresively
|
|
30
|
-
|
|
45
|
+
if (import.meta.env.DEV){
|
|
46
|
+
return
|
|
47
|
+
} else {
|
|
48
|
+
const homepage = await findHomepage(menu.value);
|
|
49
|
+
if (homepage) {
|
|
50
|
+
if (homepage.resourceId) {
|
|
51
|
+
// redirect to homepage
|
|
52
|
+
router.push({ name: 'resource-list', params: { resourceId: homepage.resourceId } });
|
|
53
|
+
} else {
|
|
54
|
+
// redirect to path
|
|
55
|
+
router.push(homepage.path);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
31
59
|
}
|
|
32
60
|
|
|
33
61
|
async function fetchRecord({ resourceId, primaryKey }) {
|
|
34
62
|
record.value = null;
|
|
35
|
-
|
|
36
|
-
if (!resourceColumns.value) {
|
|
37
|
-
throw new Error('Columns not fetched yet');
|
|
38
|
-
}
|
|
39
63
|
|
|
40
|
-
|
|
41
|
-
path: '/
|
|
64
|
+
record.value = await callAdminForthApi({
|
|
65
|
+
path: '/get_record',
|
|
42
66
|
method: 'POST',
|
|
43
67
|
body: {
|
|
44
|
-
source: 'show',
|
|
45
68
|
resourceId: resourceId,
|
|
46
|
-
|
|
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
|
|
69
|
+
primaryKey: primaryKey,
|
|
56
70
|
}
|
|
57
71
|
});
|
|
58
|
-
|
|
59
|
-
console.log('📦 record', respData);
|
|
60
|
-
record.value = respData.data[0];
|
|
61
72
|
}
|
|
62
73
|
|
|
63
74
|
async function fetchColumns({ resourceId }) {
|
package/dist/spa/src/utils.ts
CHANGED
|
@@ -29,11 +29,9 @@ export async function callAdminForthApi({ path, method, body=undefined }) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return resolveComponent(name);
|
|
36
|
-
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
37
35
|
|
|
38
36
|
export function getIcon(icon: string) {
|
|
39
37
|
// icon format is "feather:icon-name". We need to get IconName in pascal case
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
@update:record="onUpdateRecord"
|
|
32
32
|
@update:isValid="isValid = $event"
|
|
33
33
|
:validating="validating"
|
|
34
|
-
:customComponentsPerColumn="createComponentsPerColumn"
|
|
35
34
|
>
|
|
36
35
|
</ResourceForm>
|
|
37
36
|
|
|
@@ -41,14 +40,14 @@
|
|
|
41
40
|
|
|
42
41
|
<script setup>
|
|
43
42
|
|
|
44
|
-
import
|
|
45
|
-
import
|
|
46
|
-
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
43
|
+
import { ref, computed, onMounted } from 'vue';
|
|
44
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
47
45
|
import { useCoreStore } from '@/stores/core';
|
|
48
|
-
import { callAdminForthApi
|
|
46
|
+
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
|
|
51
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
50
|
+
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
52
51
|
|
|
53
52
|
const isValid = ref(false);
|
|
54
53
|
const validating = ref(false);
|
|
@@ -60,7 +59,6 @@ const route = useRoute();
|
|
|
60
59
|
const router = useRouter();
|
|
61
60
|
|
|
62
61
|
const record = ref({});
|
|
63
|
-
let createComponentsPerColumn = {};
|
|
64
62
|
|
|
65
63
|
const coreStore = useCoreStore();
|
|
66
64
|
|
|
@@ -71,15 +69,9 @@ async function onUpdateRecord(newRecord) {
|
|
|
71
69
|
|
|
72
70
|
onMounted(async () => {
|
|
73
71
|
loading.value = true;
|
|
74
|
-
|
|
72
|
+
coreStore.fetchColumns({
|
|
75
73
|
resourceId: route.params.resourceId
|
|
76
74
|
});
|
|
77
|
-
createComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
78
|
-
if (column.component?.create) {
|
|
79
|
-
acc[column.name] = getCustomComponent(column.component.create);
|
|
80
|
-
}
|
|
81
|
-
return acc;
|
|
82
|
-
}, {});
|
|
83
75
|
loading.value = false;
|
|
84
76
|
});
|
|
85
77
|
|
|
@@ -23,12 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
<ResourceForm
|
|
25
25
|
v-else
|
|
26
|
-
:record="
|
|
26
|
+
:record="coreStore.record"
|
|
27
27
|
:resourceColumns="coreStore.resourceColumns"
|
|
28
28
|
@update:record="onUpdateRecord"
|
|
29
29
|
@update:isValid="isValid = $event"
|
|
30
30
|
:validating="validating"
|
|
31
|
-
:customComponentsPerColumn="editComponentsPerColumn"
|
|
32
31
|
>
|
|
33
32
|
</ResourceForm>
|
|
34
33
|
</div>
|
|
@@ -37,14 +36,14 @@
|
|
|
37
36
|
|
|
38
37
|
<script setup>
|
|
39
38
|
|
|
40
|
-
import
|
|
41
|
-
import
|
|
42
|
-
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
39
|
+
import { ref, computed, onMounted } from 'vue';
|
|
40
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
43
41
|
import { useCoreStore } from '@/stores/core';
|
|
44
|
-
import
|
|
42
|
+
import ResourceForm from '@/components/ResourceForm.vue';
|
|
43
|
+
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
45
44
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
46
|
-
import
|
|
47
|
-
import {
|
|
45
|
+
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
46
|
+
import { callAdminForthApi } from '@/utils';
|
|
48
47
|
|
|
49
48
|
const coreStore = useCoreStore();
|
|
50
49
|
|
|
@@ -59,24 +58,11 @@ const loading = ref(false);
|
|
|
59
58
|
const saving = ref(false);
|
|
60
59
|
|
|
61
60
|
const record = ref({});
|
|
62
|
-
let editComponentsPerColumn = {};
|
|
63
61
|
|
|
64
62
|
async function onUpdateRecord(newRecord) {
|
|
65
63
|
record.value = newRecord;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
const editableRecord = computed(() => {
|
|
69
|
-
const newRecord = { ...coreStore.record };
|
|
70
|
-
if (!coreStore.resourceColumns) {
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
coreStore.resourceColumns.forEach(column => {
|
|
74
|
-
if (column.foreignResource) {
|
|
75
|
-
newRecord[column.name] = newRecord[column.name]?.pk
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
return newRecord;
|
|
79
|
-
})
|
|
80
66
|
|
|
81
67
|
onMounted(async () => {
|
|
82
68
|
loading.value = true;
|
|
@@ -89,12 +75,6 @@ onMounted(async () => {
|
|
|
89
75
|
primaryKey: route.params.primaryKey,
|
|
90
76
|
});
|
|
91
77
|
|
|
92
|
-
editComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
93
|
-
if (column.component?.edit) {
|
|
94
|
-
acc[column.name] = getCustomComponent(column.component.edit);
|
|
95
|
-
}
|
|
96
|
-
return acc;
|
|
97
|
-
}, {});
|
|
98
78
|
loading.value = false;
|
|
99
79
|
});
|
|
100
80
|
|
|
@@ -107,21 +87,13 @@ async function saveRecord() {
|
|
|
107
87
|
}
|
|
108
88
|
|
|
109
89
|
saving.value = true;
|
|
110
|
-
|
|
111
|
-
const updates = {};
|
|
112
|
-
for (const key in record.value) {
|
|
113
|
-
if (record.value[key] !== coreStore.record[key]) {
|
|
114
|
-
updates[key] = record.value[key];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
90
|
await callAdminForthApi({
|
|
119
91
|
method: 'POST',
|
|
120
92
|
path: `/update_record`,
|
|
121
93
|
body: {
|
|
122
94
|
resourceId: route.params.resourceId,
|
|
123
95
|
recordId: route.params.primaryKey,
|
|
124
|
-
record:
|
|
96
|
+
record: record.value,
|
|
125
97
|
},
|
|
126
98
|
});
|
|
127
99
|
saving.value = false;
|