adminforth 3.31.19 → 3.31.21
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.
|
@@ -6,21 +6,25 @@
|
|
|
6
6
|
@scroll="handleScroll"
|
|
7
7
|
ref="containerRef"
|
|
8
8
|
>
|
|
9
|
-
<!-- skelet loader -->
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
<!-- skelet loader: resource is not loaded yet, so its columns are unknown -->
|
|
10
|
+
<table v-if="!resource" :class="TABLE_CLASSES">
|
|
11
|
+
<tbody>
|
|
12
|
+
<tr :class="HEADER_ROW_CLASSES">
|
|
13
|
+
<td v-for="i in SKELETON_COLUMNS_COUNT" :key="i" class="list-table-header-cell px-2 md:px-3 lg:px-6 py-3">
|
|
14
|
+
<div role="status" class="h-4 flex items-center animate-pulse">
|
|
15
|
+
<div class="h-2 w-full max-w-[120px] bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor"></div>
|
|
16
|
+
</div>
|
|
17
|
+
</td>
|
|
18
|
+
</tr>
|
|
19
|
+
<SkeleteLoader :columns="SKELETON_COLUMNS_COUNT" :rows="3" />
|
|
20
|
+
</tbody>
|
|
21
|
+
</table>
|
|
18
22
|
|
|
19
|
-
<table v-else class="
|
|
23
|
+
<table v-else :class="TABLE_CLASSES">
|
|
20
24
|
|
|
21
25
|
<tbody>
|
|
22
26
|
<!-- table header -->
|
|
23
|
-
<tr class="
|
|
27
|
+
<tr :class="HEADER_ROW_CLASSES">
|
|
24
28
|
<td scope="col" class="list-table-header-cell p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
|
|
25
29
|
<Checkbox
|
|
26
30
|
:modelValue="allFromThisPageChecked"
|
|
@@ -402,6 +406,10 @@ import ListActionsThreeDots from '@/components/ListActionsThreeDots.vue';
|
|
|
402
406
|
import CallActionWrapper from '@/components/CallActionWrapper.vue'
|
|
403
407
|
import { Select } from '@/afcl';
|
|
404
408
|
|
|
409
|
+
const TABLE_CLASSES = 'w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default';
|
|
410
|
+
const HEADER_ROW_CLASSES = 'border-b dark:border-gray-700 t-header sticky z-20 top-0 text-xs text-lightListTableHeadingText bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-darkListTableHeadingText';
|
|
411
|
+
const SKELETON_COLUMNS_COUNT = 5;
|
|
412
|
+
|
|
405
413
|
const coreStore = useCoreStore();
|
|
406
414
|
const { t } = useI18n();
|
|
407
415
|
const { alert, confirm } = useAdminforth();
|
|
@@ -53,14 +53,17 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
53
53
|
const resourceColumnsError: Ref<string> = ref('');
|
|
54
54
|
const resourceColumnsId: Ref<string | null> = ref(null);
|
|
55
55
|
const adminUser: Ref<null | AdminUser> = ref(null);
|
|
56
|
+
// /get_resource responses, valid until config is refetched or user logs out
|
|
57
|
+
const fullResourceCache = new Map<string, AdminForthResourceFrontend>();
|
|
58
|
+
|
|
56
59
|
|
|
57
|
-
|
|
58
60
|
async function resetAdminUser() {
|
|
59
61
|
adminUser.value = null;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
async function resetResource() {
|
|
63
65
|
resource.value = null;
|
|
66
|
+
fullResourceCache.clear();
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
async function toggleTheme() {
|
|
@@ -99,6 +102,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
menu.value = resp.menu;
|
|
105
|
+
fullResourceCache.clear();
|
|
102
106
|
resourceById.value = resp.resources.reduce((acc: Record<string, ResourceVeryShort>, resource: ResourceVeryShort) => {
|
|
103
107
|
acc[resource.resourceId] = resource;
|
|
104
108
|
return acc;
|
|
@@ -237,14 +241,23 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
237
241
|
|
|
238
242
|
}
|
|
239
243
|
|
|
244
|
+
function applyResource(fullResource: AdminForthResourceFrontend) {
|
|
245
|
+
resource.value = fullResource;
|
|
246
|
+
resourceOptions.value = fullResource.options;
|
|
247
|
+
}
|
|
248
|
+
|
|
240
249
|
async function fetchResourceFull({ resourceId, forceFetch }: { resourceId: string, forceFetch?: boolean }) {
|
|
241
|
-
|
|
242
|
-
|
|
250
|
+
resourceColumnsId.value = resourceId;
|
|
251
|
+
resourceColumnsError.value = '';
|
|
252
|
+
const cached = fullResourceCache.get(resourceId);
|
|
253
|
+
if (cached && !forceFetch) {
|
|
254
|
+
// applied synchronously so the page renders real columns on the first frame
|
|
255
|
+
// flag is reset because a request for previously opened resource may still be in flight
|
|
256
|
+
isResourceFetching.value = false;
|
|
257
|
+
applyResource(cached);
|
|
243
258
|
return;
|
|
244
259
|
}
|
|
245
260
|
isResourceFetching.value = true;
|
|
246
|
-
resourceColumnsId.value = resourceId;
|
|
247
|
-
resourceColumnsError.value = '';
|
|
248
261
|
const res = await callAdminForthApi({
|
|
249
262
|
path: '/get_resource',
|
|
250
263
|
method: 'POST',
|
|
@@ -252,18 +265,23 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
252
265
|
resourceId,
|
|
253
266
|
}
|
|
254
267
|
});
|
|
268
|
+
if (res && !res.error) {
|
|
269
|
+
fullResourceCache.set(resourceId, res.resource);
|
|
270
|
+
resourceById.value[resourceId] = res.resource;
|
|
271
|
+
}
|
|
272
|
+
if (resourceColumnsId.value !== resourceId) {
|
|
273
|
+
// user switched to another resource while this request was in flight, that fetch owns the state now
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
isResourceFetching.value = false;
|
|
255
277
|
if (!res) {
|
|
256
|
-
isResourceFetching.value = false;
|
|
257
278
|
return;
|
|
258
279
|
}
|
|
259
280
|
if (res.error) {
|
|
260
281
|
resourceColumnsError.value = res.error;
|
|
261
282
|
} else {
|
|
262
|
-
|
|
263
|
-
resource.value = res.resource;
|
|
264
|
-
resourceOptions.value = res.resource.options;
|
|
283
|
+
applyResource(res.resource);
|
|
265
284
|
}
|
|
266
|
-
isResourceFetching.value = false;
|
|
267
285
|
}
|
|
268
286
|
|
|
269
287
|
async function getLoginFormConfig() {
|
|
@@ -147,8 +147,8 @@
|
|
|
147
147
|
:adminUser="coreStore.adminUser"
|
|
148
148
|
/>
|
|
149
149
|
<ResourceListTable
|
|
150
|
-
v-if="!coreStore.
|
|
151
|
-
:resource="
|
|
150
|
+
v-if="!coreStore.resourceColumnsError"
|
|
151
|
+
:resource="listResource"
|
|
152
152
|
:rows="rows"
|
|
153
153
|
:page="page"
|
|
154
154
|
@update:page="page = $event"
|
|
@@ -268,6 +268,9 @@ const syncPageSize = () => {
|
|
|
268
268
|
}
|
|
269
269
|
};
|
|
270
270
|
|
|
271
|
+
// store keeps previous resource until /get_resource for this route resolves, table shows skeleton meanwhile
|
|
272
|
+
const listResource = computed(() => coreStore.resource?.resourceId === route.params.resourceId ? coreStore.resource : null);
|
|
273
|
+
|
|
271
274
|
const isVirtualScrollEnabled = computed(() => coreStore.resource?.options?.listVirtualScrollEnabled || false);
|
|
272
275
|
const listBufferSize = computed(() => coreStore.resource?.options?.listBufferSize || 30);
|
|
273
276
|
|
package/dist/spa/vite.config.ts
CHANGED
|
@@ -37,7 +37,22 @@ export default defineConfig({
|
|
|
37
37
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
38
38
|
'@@': fileURLToPath(new URL('./src/custom', import.meta.url)),
|
|
39
39
|
}
|
|
40
|
-
},
|
|
40
|
+
},
|
|
41
|
+
build: {
|
|
42
|
+
rollupOptions: {
|
|
43
|
+
output: {
|
|
44
|
+
manualChunks(id) {
|
|
45
|
+
// reduce the size of the vendor chunk
|
|
46
|
+
// to only include the package name
|
|
47
|
+
// helps to reduce consumption of memory
|
|
48
|
+
if (id.includes('node_modules/')) {
|
|
49
|
+
// take the last node_modules segment: pnpm nests packages as node_modules/.pnpm/<pkg>@<version>/node_modules/<pkg>
|
|
50
|
+
return id.split('node_modules/').pop()!.split('/')[0];
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
41
56
|
css: {
|
|
42
57
|
preprocessorOptions: {
|
|
43
58
|
scss: {
|