adminforth 2.26.0-next.1 → 2.26.0-next.11
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/dataConnectors/clickhouse.d.ts +4 -0
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +14 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts +4 -0
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +9 -0
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts +4 -0
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts +4 -0
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +11 -0
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts +4 -0
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +60 -51
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/spa/package-lock.json +13 -0
- package/dist/spa/package.json +1 -0
- package/dist/spa/pnpm-lock.yaml +12 -0
- package/dist/spa/src/afcl/Modal.vue +6 -2
- package/dist/spa/src/components/AcceptModal.vue +31 -53
- package/dist/spa/src/components/MenuLink.vue +18 -4
- package/dist/spa/src/components/ResourceListTable.vue +11 -11
- package/dist/spa/src/components/ResourceListTableVirtual.vue +21 -26
- package/dist/spa/src/components/Sidebar.vue +5 -1
- package/dist/spa/src/types/Back.ts +2 -1
- package/dist/spa/src/types/Common.ts +10 -0
- package/dist/types/Back.d.ts +1 -1
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +6 -0
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/package.json +2 -2
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@iconify-prerendered/vue-flag": "^0.28.1748584105",
|
|
12
12
|
"@iconify-prerendered/vue-flowbite": "^0.28.1754899090",
|
|
13
|
+
"@iconify-prerendered/vue-heroicons": "^0.28.1721921294",
|
|
13
14
|
"@iconify-prerendered/vue-humbleicons": "^0.28.1754108846",
|
|
14
15
|
"@unhead/vue": "^1.9.12",
|
|
15
16
|
"@vueuse/core": "^10.10.0",
|
|
@@ -676,6 +677,18 @@
|
|
|
676
677
|
"vue": "^3.0.0"
|
|
677
678
|
}
|
|
678
679
|
},
|
|
680
|
+
"node_modules/@iconify-prerendered/vue-heroicons": {
|
|
681
|
+
"version": "0.28.1721921294",
|
|
682
|
+
"resolved": "https://registry.npmjs.org/@iconify-prerendered/vue-heroicons/-/vue-heroicons-0.28.1721921294.tgz",
|
|
683
|
+
"integrity": "sha512-L/Xe9HeunSvUA2+KtlaRehFpKjdZPpzPWsynX1To4QmCPAF3YD0lwusV9fZrtkdi0Y5NYS8WZF1SDypkL65+dQ==",
|
|
684
|
+
"license": "MIT",
|
|
685
|
+
"funding": {
|
|
686
|
+
"url": "https://www.buymeacoffee.com/kozack/"
|
|
687
|
+
},
|
|
688
|
+
"peerDependencies": {
|
|
689
|
+
"vue": "^3.0.0"
|
|
690
|
+
}
|
|
691
|
+
},
|
|
679
692
|
"node_modules/@iconify-prerendered/vue-humbleicons": {
|
|
680
693
|
"version": "0.28.1754108846",
|
|
681
694
|
"resolved": "https://registry.npmjs.org/@iconify-prerendered/vue-humbleicons/-/vue-humbleicons-0.28.1754108846.tgz",
|
package/dist/spa/package.json
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@iconify-prerendered/vue-flag": "^0.28.1748584105",
|
|
17
17
|
"@iconify-prerendered/vue-flowbite": "^0.28.1754899090",
|
|
18
|
+
"@iconify-prerendered/vue-heroicons": "^0.28.1721921294",
|
|
18
19
|
"@iconify-prerendered/vue-humbleicons": "^0.28.1754108846",
|
|
19
20
|
"@unhead/vue": "^1.9.12",
|
|
20
21
|
"@vueuse/core": "^10.10.0",
|
package/dist/spa/pnpm-lock.yaml
CHANGED
|
@@ -14,6 +14,9 @@ importers:
|
|
|
14
14
|
'@iconify-prerendered/vue-flowbite':
|
|
15
15
|
specifier: ^0.28.1754899090
|
|
16
16
|
version: 0.28.1754899090(vue@3.5.29(typescript@5.4.5))
|
|
17
|
+
'@iconify-prerendered/vue-heroicons':
|
|
18
|
+
specifier: ^0.28.1721921294
|
|
19
|
+
version: 0.28.1721921294(vue@3.5.29(typescript@5.4.5))
|
|
17
20
|
'@iconify-prerendered/vue-humbleicons':
|
|
18
21
|
specifier: ^0.28.1754108846
|
|
19
22
|
version: 0.28.1754108846(vue@3.5.29(typescript@5.4.5))
|
|
@@ -335,6 +338,11 @@ packages:
|
|
|
335
338
|
peerDependencies:
|
|
336
339
|
vue: ^3.0.0
|
|
337
340
|
|
|
341
|
+
'@iconify-prerendered/vue-heroicons@0.28.1721921294':
|
|
342
|
+
resolution: {integrity: sha512-L/Xe9HeunSvUA2+KtlaRehFpKjdZPpzPWsynX1To4QmCPAF3YD0lwusV9fZrtkdi0Y5NYS8WZF1SDypkL65+dQ==}
|
|
343
|
+
peerDependencies:
|
|
344
|
+
vue: ^3.0.0
|
|
345
|
+
|
|
338
346
|
'@iconify-prerendered/vue-humbleicons@0.28.1754108846':
|
|
339
347
|
resolution: {integrity: sha512-O7X7bHh4QhR9ui098FK8Sh/fPA7JRg2rVrLxUpv8Xh0LpahI9u01YKy5flhkzcwwQ+A6zN2uuKI16rytrVVcaA==}
|
|
340
348
|
peerDependencies:
|
|
@@ -2019,6 +2027,10 @@ snapshots:
|
|
|
2019
2027
|
dependencies:
|
|
2020
2028
|
vue: 3.5.29(typescript@5.4.5)
|
|
2021
2029
|
|
|
2030
|
+
'@iconify-prerendered/vue-heroicons@0.28.1721921294(vue@3.5.29(typescript@5.4.5))':
|
|
2031
|
+
dependencies:
|
|
2032
|
+
vue: 3.5.29(typescript@5.4.5)
|
|
2033
|
+
|
|
2022
2034
|
'@iconify-prerendered/vue-humbleicons@0.28.1754108846(vue@3.5.29(typescript@5.4.5))':
|
|
2023
2035
|
dependencies:
|
|
2024
2036
|
vue: 3.5.29(typescript@5.4.5)
|
|
@@ -80,12 +80,16 @@ const showConfirmationOnClose = ref(false);
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
async function open() {
|
|
83
|
-
|
|
83
|
+
if (props.beforeOpenFunction) {
|
|
84
|
+
await props.beforeOpenFunction?.();
|
|
85
|
+
}
|
|
84
86
|
isModalOpen.value = true;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
async function close() {
|
|
88
|
-
|
|
90
|
+
if (props.beforeCloseFunction) {
|
|
91
|
+
await props.beforeCloseFunction?.();
|
|
92
|
+
}
|
|
89
93
|
isModalOpen.value = false;
|
|
90
94
|
}
|
|
91
95
|
|
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="body">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
3
|
+
<Modal
|
|
4
|
+
ref="modalRef"
|
|
5
|
+
:beforeCloseFunction="()=>{modalStore.onAcceptFunction(false);modalStore.isOpened=false}"
|
|
6
|
+
>
|
|
7
|
+
<div class="relative p-4 w-full max-w-md max-h-full" >
|
|
8
|
+
<button type="button" @click="modalStore.togleModal()" class="absolute top-3 end-2.5 text-lightAcceptModalCloseIcon bg-transparent hover:bg-lightAcceptModalCloseIconHoverBackground hover:text-lightAcceptModalCloseIconHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkAcceptModalCloseIcon dark:hover:bg-darkAcceptModalCloseIconHoverBackground dark:hover:text-darkAcceptModalCloseIconHover" >
|
|
9
|
+
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
10
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
11
|
+
</svg>
|
|
12
|
+
<span class="sr-only">{{ $t('Close modal') }}</span>
|
|
13
|
+
</button>
|
|
14
|
+
<div class="p-4 md:p-5 text-center">
|
|
15
|
+
<svg class="mx-auto mb-4 text-lightAcceptModalWarningIcon w-12 h-12 dark:text-darkAcceptModalWarningIcon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
|
16
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
|
17
|
+
</svg>
|
|
18
|
+
<h3 class="afcl-confirmation-title mb-5 text-lg font-normal text-lightAcceptModalText dark:text-darkAcceptModalText">{{ modalStore?.modalContent?.content }}</h3>
|
|
19
|
+
<h3 class=" afcl-confirmation-title mb-5 text-lg font-normal text-lightAcceptModalText dark:text-darkAcceptModalText" v-html="modalStore?.modalContent?.contentHTML"></h3>
|
|
20
|
+
|
|
21
|
+
<button @click="()=>{ modalStore.onAcceptFunction(true);modalStore.togleModal()}" type="button" class="afcl-confirmation-accept-button text-lightAcceptModalConfirmButtonText bg-lightAcceptModalConfirmButtonBackground hover:bg-lightAcceptModalConfirmButtonBackgroundHover focus:ring-4 focus:outline-none focus:ring-lightAcceptModalConfirmButtonFocus font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center dark:text-darkAcceptModalConfirmButtonText dark:bg-darkAcceptModalConfirmButtonBackground dark:hover:bg-darkAcceptModalConfirmButtonBackgroundHover dark:focus:ring-darkAcceptModalConfirmButtonFocus">
|
|
22
|
+
{{ modalStore?.modalContent?.acceptText }}
|
|
23
|
+
</button>
|
|
24
|
+
<button @click="()=>{modalStore.onAcceptFunction(false);modalStore.togleModal()}" type="button" class="afcl-confirmation-cancel-button py-2.5 px-5 ms-3 text-sm font-medium text-lightAcceptModalCancelButtonText focus:outline-none bg-lightAcceptModalCancelButtonBackground rounded-lg border border-lightAcceptModalCancelButtonBorder hover:bg-lightAcceptModalCancelButtonBackgroundHover hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-lightAcceptModalCancelButtonFocus dark:focus:ring-darkAcceptModalCancelButtonFocus dark:bg-darkAcceptModalCancelButtonBackground dark:text-darkAcceptModalCancelButtonText dark:border-darkAcceptModalCancelButtonBorder dark:hover:text-darkAcceptModalCancelButtonTextHover dark:hover:bg-darkAcceptModalCancelButtonBackgroundHover">{{ modalStore?.modalContent?.cancelText }}</button>
|
|
25
25
|
</div>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</Teleport>
|
|
26
|
+
</div>
|
|
27
|
+
</Modal>
|
|
28
|
+
</Teleport>
|
|
29
29
|
</template>
|
|
30
30
|
|
|
31
31
|
<script setup lang="ts">
|
|
32
32
|
import { watch, onMounted, nextTick, ref } from 'vue';
|
|
33
33
|
import { useModalStore } from '@/stores/modal';
|
|
34
|
-
import { Modal } from '
|
|
34
|
+
import { Modal } from '@/afcl';
|
|
35
35
|
|
|
36
|
+
const modalRef = ref();
|
|
36
37
|
const modalStore = useModalStore();
|
|
37
|
-
const modalEl = ref(null);
|
|
38
|
-
const modal = ref(null);
|
|
39
38
|
|
|
40
39
|
watch( () => modalStore.isOpened, (newVal) => {
|
|
41
40
|
if (newVal) {
|
|
@@ -46,35 +45,14 @@ watch( () => modalStore.isOpened, (newVal) => {
|
|
|
46
45
|
}
|
|
47
46
|
);
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
await nextTick();
|
|
51
|
-
modal.value = new Modal(
|
|
52
|
-
modalEl.value,
|
|
53
|
-
{
|
|
54
|
-
closable: true,
|
|
55
|
-
backdrop: 'static',
|
|
56
|
-
backdropClasses: "bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-[100]"
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
})
|
|
48
|
+
|
|
60
49
|
|
|
61
50
|
function open() {
|
|
62
|
-
|
|
51
|
+
modalRef.value.open();
|
|
63
52
|
}
|
|
64
53
|
|
|
65
54
|
function close() {
|
|
66
|
-
|
|
55
|
+
modalRef.value.close();
|
|
67
56
|
}
|
|
68
57
|
|
|
69
|
-
</script>
|
|
70
|
-
|
|
71
|
-
<style scoped>
|
|
72
|
-
.modal {
|
|
73
|
-
position: fixed;
|
|
74
|
-
z-index: 999;
|
|
75
|
-
top: 20%;
|
|
76
|
-
left: 50%;
|
|
77
|
-
width: 300px;
|
|
78
|
-
margin-left: -150px;
|
|
79
|
-
}
|
|
80
|
-
</style>
|
|
58
|
+
</script>
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<RouterLink
|
|
3
|
-
:to="{name: item.resourceId ? 'resource-list' : item.path, params: item.resourceId ? { resourceId: item.resourceId }: {}}"
|
|
3
|
+
:to="item.url || { name: item.resourceId ? 'resource-list' : item.path, params: item.resourceId ? { resourceId: item.resourceId }: {}}"
|
|
4
|
+
:target="item.isOpenInNewTab ? '_blank' : '_self'"
|
|
4
5
|
class="af-menu-link flex group relative items-center w-full py-2 text-lightSidebarText dark:text-darkSidebarText rounded-default transition-all duration-200 ease-in-out"
|
|
5
6
|
:class="{
|
|
6
7
|
'hover:bg-lightSidebarItemHover hover:text-lightSidebarTextHover dark:hover:bg-darkSidebarItemHover dark:hover:text-darkSidebarTextHover active:bg-lightSidebarActive dark:active:bg-darkSidebarHover': !['divider', 'gap', 'heading'].includes(item.type),
|
|
7
8
|
'pl-6 pr-3.5': (isChild && !isSidebarIconOnly && !isSidebarHovering) || (isChild && isSidebarIconOnly && isSidebarHovering),
|
|
8
9
|
'px-3.5 ': !isChild || (isSidebarIconOnly && !isSidebarHovering),
|
|
9
10
|
'max-w-12': isSidebarIconOnly && !isSidebarHovering,
|
|
10
|
-
'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': item
|
|
11
|
-
($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
|
|
12
|
-
($route.name === item.path)
|
|
11
|
+
'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': isItemActive(item)
|
|
13
12
|
}"
|
|
14
13
|
>
|
|
15
14
|
<component v-if="item.icon" :is="getIcon(item.icon)"
|
|
@@ -61,6 +60,21 @@ import { Tooltip } from '@/afcl';
|
|
|
61
60
|
import { ref, watch, computed } from 'vue';
|
|
62
61
|
import { useCoreStore } from '@/stores/core';
|
|
63
62
|
import { IconFileImageOutline } from '@iconify-prerendered/vue-flowbite';
|
|
63
|
+
import { useRoute } from 'vue-router';
|
|
64
|
+
|
|
65
|
+
const route = useRoute();
|
|
66
|
+
|
|
67
|
+
const isItemActive = (item: any) => {
|
|
68
|
+
if (item.url) {
|
|
69
|
+
return route.fullPath === item.url;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (item.resourceId) {
|
|
73
|
+
return route.params.resourceId === item.resourceId && route.name === 'resource-list';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return route.name === item.path;
|
|
77
|
+
};
|
|
64
78
|
|
|
65
79
|
const props = defineProps(['item', 'isChild', 'isSidebarIconOnly', 'isSidebarHovering']);
|
|
66
80
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<div class="h-2 bg-lightListSkeletLoader rounded-full dark:bg-darkListSkeletLoader max-w-[360px]"></div>
|
|
11
11
|
</div>
|
|
12
12
|
</div>
|
|
13
|
-
<table v-else class="
|
|
13
|
+
<table v-else class="w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
|
|
14
14
|
|
|
15
15
|
<tbody>
|
|
16
16
|
<!-- table header -->
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
<!-- table header end -->
|
|
65
65
|
<SkeleteLoader
|
|
66
66
|
v-if="!rows"
|
|
67
|
-
:columns="resource?.columns.filter((c:
|
|
67
|
+
:columns="resource?.columns.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list).length + 2"
|
|
68
68
|
:rows="rowHeights.length || 3"
|
|
69
69
|
:row-heights="rowHeights"
|
|
70
70
|
:column-widths="columnWidths"
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
|
|
183
183
|
<template v-if="resource.options?.actions">
|
|
184
184
|
<Tooltip
|
|
185
|
-
v-for="action in resource.options.actions.filter(a => a.showIn?.list
|
|
185
|
+
v-for="action in resource.options.actions.filter(a => a.showIn?.list)"
|
|
186
186
|
:key="action.id"
|
|
187
187
|
>
|
|
188
188
|
<component
|
|
@@ -339,7 +339,7 @@ import {
|
|
|
339
339
|
} from '@iconify-prerendered/vue-flowbite';
|
|
340
340
|
import router from '@/router';
|
|
341
341
|
import { Tooltip } from '@/afcl';
|
|
342
|
-
import type { AdminForthResourceCommon,
|
|
342
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnCommon, AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
343
343
|
import { useAdminforth } from '@/adminforth';
|
|
344
344
|
import Checkbox from '@/afcl/Checkbox.vue';
|
|
345
345
|
import ListActionsThreeDots from '@/components/ListActionsThreeDots.vue';
|
|
@@ -360,7 +360,7 @@ const props = defineProps<{
|
|
|
360
360
|
customActionsInjection?: any[],
|
|
361
361
|
tableBodyStartInjection?: any[],
|
|
362
362
|
customActionIconsThreeDotsMenuItems?: any[]
|
|
363
|
-
tableRowReplaceInjection?:
|
|
363
|
+
tableRowReplaceInjection?: AdminForthComponentDeclarationFull,
|
|
364
364
|
}>();
|
|
365
365
|
|
|
366
366
|
// emits, update page
|
|
@@ -436,7 +436,7 @@ watch(() => props.rows, (newRows) => {
|
|
|
436
436
|
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
|
|
437
437
|
});
|
|
438
438
|
|
|
439
|
-
function addToCheckedValues(id: string) {
|
|
439
|
+
function addToCheckedValues(id: string | number) {
|
|
440
440
|
if (checkboxesInternal.value.includes(id)) {
|
|
441
441
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
|
|
442
442
|
} else {
|
|
@@ -468,7 +468,7 @@ const allFromThisPageChecked = computed(() => {
|
|
|
468
468
|
if (!props.rows || !props.rows.length) return false;
|
|
469
469
|
return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
|
|
470
470
|
});
|
|
471
|
-
const ascArr = computed(() => sort.value.filter((s:any) => s.direction === 'asc').map((s: any) => s.field));
|
|
471
|
+
const ascArr = computed(() => sort.value.filter((s: any) => s.direction === 'asc').map((s: any) => s.field));
|
|
472
472
|
const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
|
|
473
473
|
|
|
474
474
|
|
|
@@ -487,9 +487,9 @@ function onSortButtonClick(event: any, field: string) {
|
|
|
487
487
|
} else {
|
|
488
488
|
const sortField = sort.value[sortIndex];
|
|
489
489
|
if (sortField.direction === 'asc') {
|
|
490
|
-
sort.value = sort.value.map((s
|
|
490
|
+
sort.value = sort.value.map((s) => s.field === field ? {field, direction: 'desc'} : s);
|
|
491
491
|
} else {
|
|
492
|
-
sort.value = sort.value.filter((s
|
|
492
|
+
sort.value = sort.value.filter((s) => s.field !== field);
|
|
493
493
|
}
|
|
494
494
|
}
|
|
495
495
|
}
|
|
@@ -576,7 +576,7 @@ async function deleteRecord(row: any) {
|
|
|
576
576
|
const actionLoadingStates = ref<Record<string | number, boolean>>({});
|
|
577
577
|
|
|
578
578
|
async function startCustomAction(actionId: string, row: any, extraData: Record<string, any> = {}) {
|
|
579
|
-
|
|
579
|
+
|
|
580
580
|
actionLoadingStates.value[actionId] = true;
|
|
581
581
|
|
|
582
582
|
const data = await callAdminForthApi({
|
|
@@ -586,7 +586,7 @@ async function startCustomAction(actionId: string, row: any, extraData: Record<s
|
|
|
586
586
|
resourceId: props.resource?.resourceId,
|
|
587
587
|
actionId: actionId,
|
|
588
588
|
recordId: row._primaryKeyValue,
|
|
589
|
-
extra: extraData
|
|
589
|
+
extra: extraData
|
|
590
590
|
}
|
|
591
591
|
});
|
|
592
592
|
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
<tbody>
|
|
20
20
|
<!-- table header -->
|
|
21
|
-
<tr class="t-header sticky z-20 top-0 text-xs
|
|
22
|
-
<td scope="col" class="list-table-header-cell
|
|
21
|
+
<tr class="t-header sticky z-20 top-0 text-xs text-lightListTableHeadingText bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-darkListTableHeadingText">
|
|
22
|
+
<td scope="col" class="list-table-header-cell p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
|
|
23
23
|
<Checkbox
|
|
24
24
|
:modelValue="allFromThisPageChecked"
|
|
25
25
|
:disabled="!rows || !rows.length"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
</Checkbox>
|
|
30
30
|
</td>
|
|
31
31
|
|
|
32
|
-
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="list-table-header-cell
|
|
32
|
+
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="list-table-header-cell px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
|
|
33
33
|
|
|
34
34
|
<div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
|
|
35
35
|
class="flex items-center " :class="{'cursor-pointer':c.sortable}">
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<SkeleteLoader
|
|
70
70
|
v-if="!rows"
|
|
71
71
|
:columns="resource?.columns.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list).length + 2"
|
|
72
|
-
:rows="rowHeights.length ||
|
|
72
|
+
:rows="rowHeights.length || 3"
|
|
73
73
|
:row-heights="rowHeights"
|
|
74
74
|
:column-widths="columnWidths"
|
|
75
75
|
/>
|
|
@@ -94,7 +94,6 @@
|
|
|
94
94
|
|
|
95
95
|
<!-- Visible rows -->
|
|
96
96
|
<component
|
|
97
|
-
v-else
|
|
98
97
|
v-for="(row, rowI) in visibleRows"
|
|
99
98
|
:is="tableRowReplaceInjection ? getCustomComponent(tableRowReplaceInjection) : 'tr'"
|
|
100
99
|
:key="`row_${row._primaryKeyValue}`"
|
|
@@ -108,7 +107,7 @@
|
|
|
108
107
|
:class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
|
|
109
108
|
@mounted="(el: any) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
|
|
110
109
|
>
|
|
111
|
-
<td class="w-4 p-4 cursor-default sticky-column bg-
|
|
110
|
+
<td class="w-4 p-4 cursor-default sticky-column bg-lightListTable dark:bg-darkListTable" @click="(e)=>e.stopPropagation()">
|
|
112
111
|
<Checkbox
|
|
113
112
|
:model-value="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
114
113
|
@change="(e: any)=>{addToCheckedValues(row._primaryKeyValue)}"
|
|
@@ -116,7 +115,7 @@
|
|
|
116
115
|
>
|
|
117
116
|
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
118
117
|
</Checkbox>
|
|
119
|
-
|
|
118
|
+
</td>
|
|
120
119
|
<td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4" :class="{'sticky-column bg-lightListTable dark:bg-darkListTable': c.listSticky}">
|
|
121
120
|
<!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
|
|
122
121
|
<component
|
|
@@ -177,7 +176,7 @@
|
|
|
177
176
|
<template v-slot:tooltip>
|
|
178
177
|
{{ $t('Delete item') }}
|
|
179
178
|
</template>
|
|
180
|
-
</Tooltip>
|
|
179
|
+
</Tooltip>
|
|
181
180
|
<template v-if="customActionsInjection">
|
|
182
181
|
<component
|
|
183
182
|
v-for="c in customActionsInjection"
|
|
@@ -191,7 +190,7 @@
|
|
|
191
190
|
</template>
|
|
192
191
|
<template v-if="resource.options?.actions">
|
|
193
192
|
<Tooltip
|
|
194
|
-
v-for="action in resource.options.actions.filter(a => a.showIn?.list
|
|
193
|
+
v-for="action in resource.options.actions.filter(a => a.showIn?.list)"
|
|
195
194
|
:key="action.id"
|
|
196
195
|
>
|
|
197
196
|
<CallActionWrapper
|
|
@@ -251,9 +250,9 @@
|
|
|
251
250
|
<!-- pagination
|
|
252
251
|
totalRows in v-if is used to not hide page input during loading when user puts cursor into it and edit directly (rows gets null there during edit)
|
|
253
252
|
-->
|
|
254
|
-
<div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
|
|
253
|
+
<div class="af-pagination-container flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
|
|
255
254
|
|
|
256
|
-
<div class="inline-flex "
|
|
255
|
+
<div class="af-pagination-buttons-container inline-flex "
|
|
257
256
|
v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
|
|
258
257
|
>
|
|
259
258
|
<!-- Buttons -->
|
|
@@ -279,11 +278,10 @@
|
|
|
279
278
|
type="text"
|
|
280
279
|
v-model="pageInput"
|
|
281
280
|
:style="{ width: `${Math.max(1, pageInput.length+4)}ch` }"
|
|
282
|
-
class="af-pagination-input min-w-10 outline-none inline-block
|
|
281
|
+
class="af-pagination-input min-w-10 outline-none inline-block py-1.5 px-3 text-sm text-center text-lightListTablePaginationCurrentPageText border border-lightListTablePaginationBorder dark:border-darkListTablePaginationBorder dark:text-darkListTablePaginationCurrentPageText dark:bg-darkListTablePaginationBackgoround z-10"
|
|
283
282
|
@keydown="onPageKeydown($event)"
|
|
284
283
|
@blur="validatePageInput()"
|
|
285
|
-
|
|
286
|
-
</input>
|
|
284
|
+
/>
|
|
287
285
|
|
|
288
286
|
<button
|
|
289
287
|
class="af-pagination-last-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
@@ -309,7 +307,7 @@
|
|
|
309
307
|
<span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
|
|
310
308
|
<template v-else-if="resource && totalRows > 0">
|
|
311
309
|
|
|
312
|
-
<span class="hidden sm:inline">
|
|
310
|
+
<span class="af-pagination-info hidden sm:inline">
|
|
313
311
|
<i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
|
|
314
312
|
<template v-slot:from>
|
|
315
313
|
<strong>{{ from }}</strong>
|
|
@@ -343,7 +341,7 @@
|
|
|
343
341
|
<script setup lang="ts">
|
|
344
342
|
|
|
345
343
|
|
|
346
|
-
import { computed, onMounted, ref, watch, useTemplateRef, nextTick, type Ref
|
|
344
|
+
import { computed, onMounted, ref, watch, useTemplateRef, nextTick, type Ref } from 'vue';
|
|
347
345
|
import { callAdminForthApi } from '@/utils';
|
|
348
346
|
import { useI18n } from 'vue-i18n';
|
|
349
347
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
@@ -352,18 +350,15 @@ import { useCoreStore } from '@/stores/core';
|
|
|
352
350
|
import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
|
|
353
351
|
import SkeleteLoader from '@/components/SkeleteLoader.vue';
|
|
354
352
|
import { getIcon } from '@/utils';
|
|
355
|
-
import {
|
|
356
|
-
IconInboxOutline,
|
|
357
|
-
} from '@iconify-prerendered/vue-flowbite';
|
|
358
|
-
|
|
359
353
|
import {
|
|
360
354
|
IconEyeSolid,
|
|
361
355
|
IconPenSolid,
|
|
362
|
-
IconTrashBinSolid
|
|
356
|
+
IconTrashBinSolid,
|
|
357
|
+
IconInboxOutline
|
|
363
358
|
} from '@iconify-prerendered/vue-flowbite';
|
|
364
359
|
import router from '@/router';
|
|
365
360
|
import { Tooltip } from '@/afcl';
|
|
366
|
-
import type { AdminForthResourceCommon, AdminForthResourceColumnCommon,
|
|
361
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnCommon, AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
367
362
|
import { useAdminforth } from '@/adminforth';
|
|
368
363
|
import Checkbox from '@/afcl/Checkbox.vue';
|
|
369
364
|
import ListActionsThreeDots from '@/components/ListActionsThreeDots.vue';
|
|
@@ -387,7 +382,7 @@ const props = defineProps<{
|
|
|
387
382
|
itemHeight?: number,
|
|
388
383
|
bufferSize?: number,
|
|
389
384
|
customActionIconsThreeDotsMenuItems?: any[]
|
|
390
|
-
tableRowReplaceInjection?:
|
|
385
|
+
tableRowReplaceInjection?: AdminForthComponentDeclarationFull
|
|
391
386
|
}>();
|
|
392
387
|
|
|
393
388
|
// emits, update page
|
|
@@ -463,7 +458,7 @@ watch(() => props.rows, (newRows) => {
|
|
|
463
458
|
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
|
|
464
459
|
});
|
|
465
460
|
|
|
466
|
-
function addToCheckedValues(id:
|
|
461
|
+
function addToCheckedValues(id: string | number) {
|
|
467
462
|
if (checkboxesInternal.value.includes(id)) {
|
|
468
463
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
|
|
469
464
|
} else {
|
|
@@ -499,7 +494,7 @@ const ascArr = computed(() => sort.value.filter((s: any) => s.direction === 'asc
|
|
|
499
494
|
const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
|
|
500
495
|
|
|
501
496
|
|
|
502
|
-
function onSortButtonClick(event: any, field:
|
|
497
|
+
function onSortButtonClick(event: any, field: string) {
|
|
503
498
|
// if ctrl key is pressed, add to sort otherwise sort by this field
|
|
504
499
|
// in any case if field is already in sort, toggle direction
|
|
505
500
|
|
|
@@ -524,7 +519,7 @@ function onSortButtonClick(event: any, field: any) {
|
|
|
524
519
|
|
|
525
520
|
const clickTarget = ref(null);
|
|
526
521
|
|
|
527
|
-
async function onClick(e: any,row: any) {
|
|
522
|
+
async function onClick(e: any, row: any) {
|
|
528
523
|
if(clickTarget.value === e.target) return;
|
|
529
524
|
clickTarget.value = e.target;
|
|
530
525
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
'-translate-x-full': !sideBarOpen,
|
|
10
10
|
'transform-none': sideBarOpen,
|
|
11
11
|
'sidebar-collapsed': iconOnlySidebarEnabled && isSidebarIconOnly && !isSidebarHovering,
|
|
12
|
-
'sidebar-expanded': !iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering)
|
|
12
|
+
'sidebar-expanded': !iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering),
|
|
13
|
+
'sidebar-floating': isSidebarIconOnly && isSidebarHovering
|
|
13
14
|
}"
|
|
14
15
|
aria-label="Sidebar"
|
|
15
16
|
>
|
|
@@ -217,6 +218,9 @@
|
|
|
217
218
|
|
|
218
219
|
.sidebar-expanded {
|
|
219
220
|
width: v-bind(expandedWidth); /* Expanded width (w-64) */
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.sidebar-floating {
|
|
220
224
|
box-shadow: 3px 0px 12px -2px rgba(0, 0, 0, 0.15);
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -1760,6 +1760,8 @@ export interface IOperationalResource {
|
|
|
1760
1760
|
update: (primaryKey: any, record: any) => Promise<any>;
|
|
1761
1761
|
|
|
1762
1762
|
delete: (primaryKey: any) => Promise<boolean>;
|
|
1763
|
+
|
|
1764
|
+
deleteMany?(recordIds: any[]): Promise<number>;
|
|
1763
1765
|
|
|
1764
1766
|
dataConnector: IAdminForthDataSourceConnectorBase;
|
|
1765
1767
|
}
|
|
@@ -1811,7 +1813,6 @@ export type AllowedActions = {
|
|
|
1811
1813
|
*/
|
|
1812
1814
|
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions'> {
|
|
1813
1815
|
|
|
1814
|
-
baseActionsAsQuickIcons?: ('show' | 'edit' | 'delete')[],
|
|
1815
1816
|
/**
|
|
1816
1817
|
* Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
|
|
1817
1818
|
* using checkboxes.
|
|
@@ -371,6 +371,14 @@ export interface AdminForthResourceInputCommon {
|
|
|
371
371
|
* General options for resource.
|
|
372
372
|
*/
|
|
373
373
|
options?: {
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Show quick action icons for base actions (show, edit, delete) in list view.
|
|
378
|
+
* By default, they are inside three dots dropdown menu.
|
|
379
|
+
*/
|
|
380
|
+
baseActionsAsQuickIcons?: ('show' | 'edit' | 'delete')[],
|
|
381
|
+
|
|
374
382
|
|
|
375
383
|
/**
|
|
376
384
|
* Default sort for list view.
|
|
@@ -1122,6 +1130,8 @@ export interface AdminForthConfigMenuItem {
|
|
|
1122
1130
|
* Item id will be automatically generated from hashed resourceId+Path+label
|
|
1123
1131
|
*/
|
|
1124
1132
|
itemId?: string, // todo move to runtime type
|
|
1133
|
+
|
|
1134
|
+
url?: string
|
|
1125
1135
|
}
|
|
1126
1136
|
|
|
1127
1137
|
|
package/dist/types/Back.d.ts
CHANGED
|
@@ -1535,6 +1535,7 @@ export interface IOperationalResource {
|
|
|
1535
1535
|
}>;
|
|
1536
1536
|
update: (primaryKey: any, record: any) => Promise<any>;
|
|
1537
1537
|
delete: (primaryKey: any) => Promise<boolean>;
|
|
1538
|
+
deleteMany?(recordIds: any[]): Promise<number>;
|
|
1538
1539
|
dataConnector: IAdminForthDataSourceConnectorBase;
|
|
1539
1540
|
}
|
|
1540
1541
|
/**
|
|
@@ -1573,7 +1574,6 @@ export type AllowedActions = {
|
|
|
1573
1574
|
* General options for resource.
|
|
1574
1575
|
*/
|
|
1575
1576
|
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions'> {
|
|
1576
|
-
baseActionsAsQuickIcons?: ('show' | 'edit' | 'delete')[];
|
|
1577
1577
|
/**
|
|
1578
1578
|
* Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
|
|
1579
1579
|
* using checkboxes.
|