adminforth 2.26.0-next.3 → 2.26.0-next.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/commands/createApp/templates/package.json.hbs +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +3 -0
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- 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/qdrant.d.ts +57 -0
- package/dist/dataConnectors/qdrant.d.ts.map +1 -0
- package/dist/dataConnectors/qdrant.js +469 -0
- package/dist/dataConnectors/qdrant.js.map +1 -0
- 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/index.d.ts.map +1 -1
- package/dist/index.js +22 -33
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +59 -50
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +3 -2
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts +1 -0
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +33 -16
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/utils.d.ts +6 -0
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +13 -0
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +7 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/package-lock.json +57 -0
- package/dist/spa/package.json +2 -0
- package/dist/spa/pnpm-lock.yaml +32 -0
- package/dist/spa/src/adminforth.ts +17 -29
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/Modal.vue +18 -3
- package/dist/spa/src/afcl/Select.vue +4 -2
- package/dist/spa/src/afcl/Table.vue +27 -13
- package/dist/spa/src/components/AcceptModal.vue +33 -53
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +4 -5
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +11 -3
- package/dist/spa/src/components/ListActionsThreeDots.vue +10 -9
- package/dist/spa/src/components/ResourceListTable.vue +291 -144
- package/dist/spa/src/components/Sidebar.vue +6 -2
- package/dist/spa/src/components/ThreeDotsMenu.vue +10 -9
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/renderers/CountryFlag.vue +2 -2
- package/dist/spa/src/stores/core.ts +4 -2
- package/dist/spa/src/types/Back.ts +24 -5
- package/dist/spa/src/types/Common.ts +45 -5
- package/dist/spa/src/types/FrontendAPI.ts +6 -1
- package/dist/spa/src/utils/listUtils.ts +8 -2
- package/dist/spa/src/utils/utils.ts +29 -10
- package/dist/spa/src/views/CreateView.vue +8 -8
- package/dist/spa/src/views/EditView.vue +8 -7
- package/dist/spa/src/views/ListView.vue +14 -48
- package/dist/spa/src/views/LoginView.vue +13 -13
- package/dist/spa/src/views/PageNotFound.vue +5 -1
- package/dist/spa/src/views/ShowView.vue +6 -6
- package/dist/types/Back.d.ts +23 -6
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +25 -5
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +13 -1
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/package.json +2 -1
- package/dist/spa/src/components/ResourceListTableVirtual.vue +0 -794
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))
|
|
@@ -78,6 +81,9 @@ importers:
|
|
|
78
81
|
'@types/node':
|
|
79
82
|
specifier: ^20.12.5
|
|
80
83
|
version: 20.19.37
|
|
84
|
+
'@types/sanitize-html':
|
|
85
|
+
specifier: ^2.16.1
|
|
86
|
+
version: 2.16.1
|
|
81
87
|
'@vitejs/plugin-vue':
|
|
82
88
|
specifier: ^5.0.4
|
|
83
89
|
version: 5.2.4(vite@5.4.21(@types/node@20.19.37)(sass@1.97.3))(vue@3.5.29(typescript@5.4.5))
|
|
@@ -335,6 +341,11 @@ packages:
|
|
|
335
341
|
peerDependencies:
|
|
336
342
|
vue: ^3.0.0
|
|
337
343
|
|
|
344
|
+
'@iconify-prerendered/vue-heroicons@0.28.1721921294':
|
|
345
|
+
resolution: {integrity: sha512-L/Xe9HeunSvUA2+KtlaRehFpKjdZPpzPWsynX1To4QmCPAF3YD0lwusV9fZrtkdi0Y5NYS8WZF1SDypkL65+dQ==}
|
|
346
|
+
peerDependencies:
|
|
347
|
+
vue: ^3.0.0
|
|
348
|
+
|
|
338
349
|
'@iconify-prerendered/vue-humbleicons@0.28.1754108846':
|
|
339
350
|
resolution: {integrity: sha512-O7X7bHh4QhR9ui098FK8Sh/fPA7JRg2rVrLxUpv8Xh0LpahI9u01YKy5flhkzcwwQ+A6zN2uuKI16rytrVVcaA==}
|
|
340
351
|
peerDependencies:
|
|
@@ -669,6 +680,9 @@ packages:
|
|
|
669
680
|
'@types/resolve@1.20.2':
|
|
670
681
|
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
|
671
682
|
|
|
683
|
+
'@types/sanitize-html@2.16.1':
|
|
684
|
+
resolution: {integrity: sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==}
|
|
685
|
+
|
|
672
686
|
'@types/web-bluetooth@0.0.14':
|
|
673
687
|
resolution: {integrity: sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A==}
|
|
674
688
|
|
|
@@ -1265,6 +1279,9 @@ packages:
|
|
|
1265
1279
|
hookable@5.5.3:
|
|
1266
1280
|
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
|
1267
1281
|
|
|
1282
|
+
htmlparser2@10.1.0:
|
|
1283
|
+
resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
|
|
1284
|
+
|
|
1268
1285
|
htmlparser2@8.0.2:
|
|
1269
1286
|
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
|
1270
1287
|
|
|
@@ -2019,6 +2036,10 @@ snapshots:
|
|
|
2019
2036
|
dependencies:
|
|
2020
2037
|
vue: 3.5.29(typescript@5.4.5)
|
|
2021
2038
|
|
|
2039
|
+
'@iconify-prerendered/vue-heroicons@0.28.1721921294(vue@3.5.29(typescript@5.4.5))':
|
|
2040
|
+
dependencies:
|
|
2041
|
+
vue: 3.5.29(typescript@5.4.5)
|
|
2042
|
+
|
|
2022
2043
|
'@iconify-prerendered/vue-humbleicons@0.28.1754108846(vue@3.5.29(typescript@5.4.5))':
|
|
2023
2044
|
dependencies:
|
|
2024
2045
|
vue: 3.5.29(typescript@5.4.5)
|
|
@@ -2253,6 +2274,10 @@ snapshots:
|
|
|
2253
2274
|
|
|
2254
2275
|
'@types/resolve@1.20.2': {}
|
|
2255
2276
|
|
|
2277
|
+
'@types/sanitize-html@2.16.1':
|
|
2278
|
+
dependencies:
|
|
2279
|
+
htmlparser2: 10.1.0
|
|
2280
|
+
|
|
2256
2281
|
'@types/web-bluetooth@0.0.14': {}
|
|
2257
2282
|
|
|
2258
2283
|
'@types/web-bluetooth@0.0.20': {}
|
|
@@ -2960,6 +2985,13 @@ snapshots:
|
|
|
2960
2985
|
|
|
2961
2986
|
hookable@5.5.3: {}
|
|
2962
2987
|
|
|
2988
|
+
htmlparser2@10.1.0:
|
|
2989
|
+
dependencies:
|
|
2990
|
+
domelementtype: 2.3.0
|
|
2991
|
+
domhandler: 5.0.3
|
|
2992
|
+
domutils: 3.2.2
|
|
2993
|
+
entities: 7.0.1
|
|
2994
|
+
|
|
2963
2995
|
htmlparser2@8.0.2:
|
|
2964
2996
|
dependencies:
|
|
2965
2997
|
domelementtype: 2.3.0
|
|
@@ -19,26 +19,13 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
19
19
|
public modalStore:any
|
|
20
20
|
public filtersStore:any
|
|
21
21
|
public coreStore:any
|
|
22
|
-
private saveInterceptors: Record<string, Array<
|
|
23
|
-
|
|
24
|
-
public list: {
|
|
25
|
-
refresh(): Promise<{ error? : string }>;
|
|
26
|
-
silentRefresh(): Promise<{ error? : string }>;
|
|
27
|
-
silentRefreshRow(pk: any): Promise<{ error? : string }>;
|
|
28
|
-
closeThreeDotsDropdown(): Promise<{ error? : string }>;
|
|
29
|
-
closeUserMenuDropdown: () => void;
|
|
30
|
-
setFilter: (filter: FilterParams) => void;
|
|
31
|
-
updateFilter: (filter: FilterParams) => void;
|
|
32
|
-
clearFilters: () => void;
|
|
33
|
-
}
|
|
22
|
+
private saveInterceptors: Record<string, Array<Parameters<FrontendAPIInterface['registerSaveInterceptor']>[0]>> = {};
|
|
34
23
|
|
|
35
|
-
public
|
|
36
|
-
refreshMenuBadges: () => void;
|
|
37
|
-
}
|
|
24
|
+
public list: FrontendAPIInterface['list'];
|
|
38
25
|
|
|
39
|
-
public
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
public menu: FrontendAPIInterface['menu'];
|
|
27
|
+
|
|
28
|
+
public show: FrontendAPIInterface['show'];
|
|
42
29
|
|
|
43
30
|
closeUserMenuDropdown(): void {
|
|
44
31
|
console.log('closeUserMenuDropdown')
|
|
@@ -70,9 +57,6 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
70
57
|
console.log('closeThreeDotsDropdown')
|
|
71
58
|
return { error: 'Not implemented' }
|
|
72
59
|
},
|
|
73
|
-
closeUserMenuDropdown: () => {
|
|
74
|
-
console.log('closeUserMenuDropdown')
|
|
75
|
-
},
|
|
76
60
|
setFilter: this.setListFilter.bind(this),
|
|
77
61
|
updateFilter: this.updateListFilter.bind(this),
|
|
78
62
|
clearFilters: this.clearListFilters.bind(this),
|
|
@@ -83,11 +67,15 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
83
67
|
console.log('show.refresh')
|
|
84
68
|
}
|
|
85
69
|
}
|
|
70
|
+
|
|
71
|
+
this.closeUserMenuDropdown = () => {
|
|
72
|
+
console.log('closeUserMenuDropdown')
|
|
73
|
+
};
|
|
86
74
|
}
|
|
87
75
|
|
|
88
76
|
registerSaveInterceptor(
|
|
89
|
-
handler:
|
|
90
|
-
):
|
|
77
|
+
handler: Parameters<FrontendAPIInterface['registerSaveInterceptor']>[0]
|
|
78
|
+
): ReturnType<FrontendAPIInterface['registerSaveInterceptor']> {
|
|
91
79
|
const rid = router.currentRoute.value?.params?.resourceId as string;
|
|
92
80
|
if (!rid) {
|
|
93
81
|
return;
|
|
@@ -98,7 +86,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
98
86
|
this.saveInterceptors[rid].push(handler);
|
|
99
87
|
}
|
|
100
88
|
|
|
101
|
-
async runSaveInterceptors(params:
|
|
89
|
+
async runSaveInterceptors(params: Parameters<FrontendAPIInterface['runSaveInterceptors']>[0]): ReturnType<FrontendAPIInterface['runSaveInterceptors']> {
|
|
102
90
|
const list = this.saveInterceptors[params.resourceId] || [];
|
|
103
91
|
const aggregatedExtra: Record<string, any> = {};
|
|
104
92
|
for (const fn of list) {
|
|
@@ -120,7 +108,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
120
108
|
return { ok: true, extra: aggregatedExtra };
|
|
121
109
|
}
|
|
122
110
|
|
|
123
|
-
clearSaveInterceptors(resourceId?:
|
|
111
|
+
clearSaveInterceptors(resourceId?: Parameters<FrontendAPIInterface['clearSaveInterceptors']>[0]): ReturnType<FrontendAPIInterface['clearSaveInterceptors']> {
|
|
124
112
|
if (resourceId) {
|
|
125
113
|
delete this.saveInterceptors[resourceId];
|
|
126
114
|
} else {
|
|
@@ -128,7 +116,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
128
116
|
}
|
|
129
117
|
}
|
|
130
118
|
|
|
131
|
-
confirm(params:
|
|
119
|
+
confirm(params: Parameters<FrontendAPIInterface['confirm']>[0]): ReturnType<FrontendAPIInterface['confirm']> {
|
|
132
120
|
return new Promise((resolve, reject) => {
|
|
133
121
|
this.modalStore.setModalContent({
|
|
134
122
|
content: params.message,
|
|
@@ -142,7 +130,7 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
142
130
|
})
|
|
143
131
|
}
|
|
144
132
|
|
|
145
|
-
alert(params:
|
|
133
|
+
alert(params: Parameters<FrontendAPIInterface['alert']>[0]): ReturnType<FrontendAPIInterface['alert']> {
|
|
146
134
|
const toats = {
|
|
147
135
|
message: params.message,
|
|
148
136
|
messageHtml: params.messageHtml,
|
|
@@ -162,14 +150,14 @@ class FrontendAPI implements FrontendAPIInterface {
|
|
|
162
150
|
}
|
|
163
151
|
}
|
|
164
152
|
|
|
165
|
-
listFilterValidation(filter:
|
|
153
|
+
listFilterValidation(filter: Parameters<FrontendAPIInterface['list']['setFilter']>[0]): boolean {
|
|
166
154
|
if(router.currentRoute.value.meta.type !== 'list'){
|
|
167
155
|
throw new Error(`Cannot use ${this.setListFilter.name} filter on a list page`)
|
|
168
156
|
}
|
|
169
157
|
return true
|
|
170
158
|
}
|
|
171
159
|
|
|
172
|
-
setListFilter(filter:
|
|
160
|
+
setListFilter(filter: Parameters<FrontendAPIInterface['list']['setFilter']>[0]): ReturnType<FrontendAPIInterface['list']['setFilter']> {
|
|
173
161
|
if(this.listFilterValidation(filter)){
|
|
174
162
|
const existingFilterIndex = this.filtersStore.filters.findIndex((f: any) => {
|
|
175
163
|
return f.field === filter.field && f.operator === filter.operator
|
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
<slot name="trigger"></slot>
|
|
7
7
|
</div>
|
|
8
8
|
<Teleport to="body">
|
|
9
|
-
<div
|
|
9
|
+
<div
|
|
10
|
+
v-show="isModalOpen"
|
|
11
|
+
v-if="!removeFromDom"
|
|
12
|
+
@click="backdropClick"
|
|
13
|
+
class="bg-black/50 overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full h-full md:inset-0 h-1rem max-h-full flex"
|
|
14
|
+
:class="props.backgroundCustomClasses"
|
|
15
|
+
>
|
|
10
16
|
<!-- Modal content -->
|
|
11
17
|
<div v-bind="$attrs" class="relative bg-lightDialogBackgorund rounded-lg shadow-sm dark:bg-darkDialogBackgorund">
|
|
12
18
|
|
|
@@ -19,6 +25,7 @@
|
|
|
19
25
|
<div
|
|
20
26
|
v-if="showConfirmationOnClose"
|
|
21
27
|
class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-[60]"
|
|
28
|
+
:class="props.modalCustomClasses"
|
|
22
29
|
>
|
|
23
30
|
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg max-w-sm w-full">
|
|
24
31
|
<h2 class="text-lg font-semibold mb-4 text-lightDialogHeaderText dark:text-darkDialogHeaderText">Confirm Close</h2>
|
|
@@ -64,6 +71,8 @@ interface DialogProps {
|
|
|
64
71
|
askForCloseConfirmation?: boolean
|
|
65
72
|
closeConfirmationText?: string
|
|
66
73
|
removeFromDomOnClose?: boolean
|
|
74
|
+
backgroundCustomClasses?: string
|
|
75
|
+
modalCustomClasses?: string
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
const props = withDefaults(defineProps<DialogProps>(), {
|
|
@@ -74,18 +83,24 @@ const props = withDefaults(defineProps<DialogProps>(), {
|
|
|
74
83
|
askForCloseConfirmation: false,
|
|
75
84
|
closeConfirmationText: 'Are you sure you want to close this dialog?',
|
|
76
85
|
removeFromDomOnClose: false,
|
|
86
|
+
backgroundCustomClasses: '',
|
|
87
|
+
modalCustomClasses: '',
|
|
77
88
|
})
|
|
78
89
|
|
|
79
90
|
const showConfirmationOnClose = ref(false);
|
|
80
91
|
|
|
81
92
|
|
|
82
93
|
async function open() {
|
|
83
|
-
|
|
94
|
+
if (props.beforeOpenFunction) {
|
|
95
|
+
await props.beforeOpenFunction?.();
|
|
96
|
+
}
|
|
84
97
|
isModalOpen.value = true;
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
async function close() {
|
|
88
|
-
|
|
101
|
+
if (props.beforeCloseFunction) {
|
|
102
|
+
await props.beforeCloseFunction?.();
|
|
103
|
+
}
|
|
89
104
|
isModalOpen.value = false;
|
|
90
105
|
}
|
|
91
106
|
|
|
@@ -119,10 +119,12 @@ import { ref, computed, onMounted, onUnmounted, watch, nextTick,type PropType, t
|
|
|
119
119
|
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
120
120
|
import { useElementSize } from '@vueuse/core'
|
|
121
121
|
|
|
122
|
+
type ISingleSelectModelValue = string | number;
|
|
123
|
+
|
|
122
124
|
const props = defineProps({
|
|
123
125
|
options: Array,
|
|
124
126
|
modelValue: {
|
|
125
|
-
type: Array as PropType<(
|
|
127
|
+
type: Array as PropType<(ISingleSelectModelValue)[] | ISingleSelectModelValue>,
|
|
126
128
|
default: () => [],
|
|
127
129
|
},
|
|
128
130
|
multiple: {
|
|
@@ -201,7 +203,7 @@ function updateFromProps() {
|
|
|
201
203
|
selectedItems.value = [];
|
|
202
204
|
}
|
|
203
205
|
} else {
|
|
204
|
-
selectedItems.value = props.options?.filter((item: any) => props.modelValue?.includes(item.value)) || [];
|
|
206
|
+
selectedItems.value = props.options?.filter((item: any) => (props.modelValue as (ISingleSelectModelValue)[])?.includes(item.value)) || [];
|
|
205
207
|
}
|
|
206
208
|
}
|
|
207
209
|
}
|
|
@@ -91,12 +91,12 @@
|
|
|
91
91
|
<template #total><span class="font-semibold text-lightTablePaginationNumeration dark:text-darkTablePaginationNumeration">{{ dataResult.total }}</span></template>
|
|
92
92
|
</i18n-t>
|
|
93
93
|
<div class="af-pagination-container flex flex-row items-center xs:flex-row xs:justify-between xs:items-center gap-3">
|
|
94
|
-
<div class="inline-flex" :class="
|
|
94
|
+
<div class="inline-flex" :class="blockPagination ? 'pointer-events-none select-none opacity-50' : ''">
|
|
95
95
|
<!-- Buttons -->
|
|
96
96
|
<button
|
|
97
97
|
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightActivePaginationButtonText bg-lightActivePaginationButtonBackground border-r-0 rounded-s hover:opacity-90 dark:bg-darkActivePaginationButtonBackground dark:text-darkActivePaginationButtonText disabled:opacity-50"
|
|
98
98
|
@click="currentPage--; pageInput = currentPage.toString();"
|
|
99
|
-
:disabled="currentPage <= 1 ||
|
|
99
|
+
:disabled="currentPage <= 1 || blockPagination">
|
|
100
100
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
101
101
|
viewBox="0 0 14 10">
|
|
102
102
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
<button
|
|
107
107
|
class="flex items-center py-1 px-3 text-sm font-medium text-lightUnactivePaginationButtonText focus:outline-none bg-lightUnactivePaginationButtonBackground border-r-0 border border-lightUnactivePaginationButtonBorder hover:bg-lightUnactivePaginationButtonHoverBackground hover:text-lightUnactivePaginationButtonHoverText dark:bg-darkUnactivePaginationButtonBackground dark:text-darkUnactivePaginationButtonText dark:border-darkUnactivePaginationButtonBorder dark:hover:text-darkUnactivePaginationButtonHoverText dark:hover:bg-darkUnactivePaginationButtonHoverBackground disabled:opacity-50"
|
|
108
108
|
@click="switchPage(1); pageInput = currentPage.toString();"
|
|
109
|
-
:disabled="currentPage <= 1 ||
|
|
109
|
+
:disabled="currentPage <= 1 || blockPagination">
|
|
110
110
|
<!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
|
|
111
111
|
1
|
|
112
112
|
</button>
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
<button
|
|
124
124
|
class="flex items-center py-1 px-3 text-sm font-medium text-lightUnactivePaginationButtonText focus:outline-none bg-lightUnactivePaginationButtonBackground border-l-0 border border-lightUnactivePaginationButtonBorder hover:bg-lightUnactivePaginationButtonHoverBackground hover:text-lightUnactivePaginationButtonHoverText dark:bg-darkUnactivePaginationButtonBackground dark:text-darkUnactivePaginationButtonText dark:border-darkUnactivePaginationButtonBorder dark:hover:text-darkUnactivePaginationButtonHoverText dark:hover:bg-darkUnactivePaginationButtonHoverBackground disabled:opacity-50"
|
|
125
125
|
@click="currentPage = totalPages; pageInput = currentPage.toString();"
|
|
126
|
-
:disabled="currentPage >= totalPages ||
|
|
126
|
+
:disabled="currentPage >= totalPages || blockPagination"
|
|
127
127
|
>
|
|
128
128
|
{{ totalPages }}
|
|
129
129
|
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
<button
|
|
132
132
|
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightActivePaginationButtonText focus:outline-none bg-lightActivePaginationButtonBackground border-l-0 rounded-e hover:opacity-90 dark:bg-darkActivePaginationButtonBackground dark:text-darkActivePaginationButtonText disabled:opacity-50"
|
|
133
133
|
@click="currentPage++; pageInput = currentPage.toString();"
|
|
134
|
-
:disabled="currentPage >= totalPages ||
|
|
134
|
+
:disabled="currentPage >= totalPages || blockPagination"
|
|
135
135
|
>
|
|
136
136
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
137
137
|
viewBox="0 0 14 10">
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
}[],
|
|
164
164
|
data: {
|
|
165
165
|
[key: string]: any,
|
|
166
|
-
}[] | ((params: { offset: number, limit: number, sortField?: string, sortDirection?: 'asc' | 'desc' }) => Promise<{data: {[key: string]: any}[], total: number}>),
|
|
166
|
+
}[] | ((params: { offset: number, limit: number, sortField?: string, sortDirection?: 'asc' | 'desc' }, abortSignal?: AbortSignal) => Promise<{data: {[key: string]: any}[], total: number}>),
|
|
167
167
|
evenHighlights?: boolean,
|
|
168
168
|
pageSize?: number,
|
|
169
169
|
isLoading?: boolean,
|
|
@@ -171,9 +171,11 @@
|
|
|
171
171
|
defaultSortDirection?: 'asc' | 'desc',
|
|
172
172
|
makeHeaderSticky?: boolean,
|
|
173
173
|
makePaginationSticky?: boolean,
|
|
174
|
+
blockPaginationOnLoading?: boolean,
|
|
174
175
|
}>(), {
|
|
175
176
|
evenHighlights: true,
|
|
176
177
|
pageSize: 5,
|
|
178
|
+
blockPaginationOnLoading: true,
|
|
177
179
|
}
|
|
178
180
|
);
|
|
179
181
|
|
|
@@ -188,6 +190,9 @@
|
|
|
188
190
|
const isAtLeastOneLoading = ref<boolean[]>([false]);
|
|
189
191
|
const currentSortField = ref<string | undefined>(props.defaultSortField);
|
|
190
192
|
const currentSortDirection = ref<'asc' | 'desc'>(props.defaultSortDirection ?? 'asc');
|
|
193
|
+
const oldAbortController = ref<AbortController | null>(null);
|
|
194
|
+
|
|
195
|
+
const blockPagination = computed(() => (isLoading.value || props.isLoading) && props.blockPaginationOnLoading);
|
|
191
196
|
|
|
192
197
|
onMounted(() => {
|
|
193
198
|
// If defaultSortField points to a non-sortable column, ignore it
|
|
@@ -277,16 +282,25 @@
|
|
|
277
282
|
isLoading.value = true;
|
|
278
283
|
const currentLoadingIndex = currentPage.value;
|
|
279
284
|
isAtLeastOneLoading.value[currentLoadingIndex] = true;
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
const abortController = new AbortController();
|
|
286
|
+
if (oldAbortController.value) {
|
|
287
|
+
oldAbortController.value.abort();
|
|
288
|
+
}
|
|
289
|
+
oldAbortController.value = abortController;
|
|
290
|
+
const result = await props.data(
|
|
291
|
+
{
|
|
292
|
+
offset: (currentLoadingIndex - 1) * props.pageSize,
|
|
293
|
+
limit: props.pageSize,
|
|
294
|
+
sortField: currentSortField.value,
|
|
295
|
+
...(currentSortField.value ? { sortDirection: currentSortDirection.value } : {}),
|
|
296
|
+
},
|
|
297
|
+
abortController.signal
|
|
298
|
+
);
|
|
286
299
|
isAtLeastOneLoading.value[currentLoadingIndex] = false;
|
|
287
300
|
if (isAtLeastOneLoading.value.every(v => v === false)) {
|
|
288
301
|
isLoading.value = false;
|
|
289
302
|
}
|
|
303
|
+
if(abortController.signal.aborted) return;
|
|
290
304
|
dataResult.value = result;
|
|
291
305
|
} else if (typeof props.data === 'object' && Array.isArray(props.data)) {
|
|
292
306
|
const start = (currentPage.value - 1) * props.pageSize;
|
|
@@ -350,7 +364,7 @@ function sortArrayData(data:any[], sortField?:string, dir:'asc'|'desc'='asc') {
|
|
|
350
364
|
});
|
|
351
365
|
}
|
|
352
366
|
|
|
353
|
-
function tableRowClick(row) {
|
|
367
|
+
function tableRowClick(row: any) {
|
|
354
368
|
emit("clickTableRow", row)
|
|
355
369
|
}
|
|
356
370
|
</script>
|
|
@@ -1,41 +1,42 @@
|
|
|
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
|
+
backgroundCustomClasses="z-[998]"
|
|
7
|
+
modalCustomClasses="z-[999]"
|
|
8
|
+
>
|
|
9
|
+
<div class="relative p-4 w-full max-w-md max-h-full" >
|
|
10
|
+
<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" >
|
|
11
|
+
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
12
|
+
<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"/>
|
|
13
|
+
</svg>
|
|
14
|
+
<span class="sr-only">{{ $t('Close modal') }}</span>
|
|
15
|
+
</button>
|
|
16
|
+
<div class="p-4 md:p-5 text-center">
|
|
17
|
+
<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">
|
|
18
|
+
<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"/>
|
|
19
|
+
</svg>
|
|
20
|
+
<h3 class="afcl-confirmation-title mb-5 text-lg font-normal text-lightAcceptModalText dark:text-darkAcceptModalText">{{ modalStore?.modalContent?.content }}</h3>
|
|
21
|
+
<h3 class=" afcl-confirmation-title mb-5 text-lg font-normal text-lightAcceptModalText dark:text-darkAcceptModalText" v-html="modalStore?.modalContent?.contentHTML"></h3>
|
|
22
|
+
|
|
23
|
+
<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">
|
|
24
|
+
{{ modalStore?.modalContent?.acceptText }}
|
|
25
|
+
</button>
|
|
26
|
+
<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
27
|
</div>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</Teleport>
|
|
28
|
+
</div>
|
|
29
|
+
</Modal>
|
|
30
|
+
</Teleport>
|
|
29
31
|
</template>
|
|
30
32
|
|
|
31
33
|
<script setup lang="ts">
|
|
32
34
|
import { watch, onMounted, nextTick, ref } from 'vue';
|
|
33
35
|
import { useModalStore } from '@/stores/modal';
|
|
34
|
-
import { Modal } from '
|
|
36
|
+
import { Modal } from '@/afcl';
|
|
35
37
|
|
|
38
|
+
const modalRef = ref();
|
|
36
39
|
const modalStore = useModalStore();
|
|
37
|
-
const modalEl = ref(null);
|
|
38
|
-
const modal = ref(null);
|
|
39
40
|
|
|
40
41
|
watch( () => modalStore.isOpened, (newVal) => {
|
|
41
42
|
if (newVal) {
|
|
@@ -46,35 +47,14 @@ watch( () => modalStore.isOpened, (newVal) => {
|
|
|
46
47
|
}
|
|
47
48
|
);
|
|
48
49
|
|
|
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
|
-
})
|
|
50
|
+
|
|
60
51
|
|
|
61
52
|
function open() {
|
|
62
|
-
|
|
53
|
+
modalRef.value.open();
|
|
63
54
|
}
|
|
64
55
|
|
|
65
56
|
function close() {
|
|
66
|
-
|
|
57
|
+
modalRef.value.close();
|
|
67
58
|
}
|
|
68
59
|
|
|
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>
|
|
60
|
+
</script>
|
|
@@ -6,17 +6,16 @@
|
|
|
6
6
|
<slot></slot>
|
|
7
7
|
</div>
|
|
8
8
|
</div>
|
|
9
|
-
<div
|
|
10
|
-
<
|
|
11
|
-
<span class="font-medium">{{ $t('Error!')}}</span> {{ coreStore.resourceColumnsError }}
|
|
12
|
-
</div>
|
|
9
|
+
<div v-if="coreStore.resourceColumnsError" >
|
|
10
|
+
<PageNotFound :errorMessage="coreStore.resourceColumnsError" />
|
|
13
11
|
</div>
|
|
14
12
|
</div>
|
|
15
13
|
|
|
16
14
|
</template>
|
|
17
15
|
|
|
18
16
|
<script setup>
|
|
19
|
-
import Breadcrumbs from '@/components/Breadcrumbs.vue';
|
|
17
|
+
import Breadcrumbs from '@/components/Breadcrumbs.vue';
|
|
18
|
+
import PageNotFound from '@/views/PageNotFound.vue';
|
|
20
19
|
|
|
21
20
|
import { useCoreStore } from '@/stores/core';
|
|
22
21
|
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
:unmasked="unmasked"
|
|
17
17
|
:deletable="!column.editReadonly"
|
|
18
18
|
@update:modelValue="setCurrentValue(column.name, $event, arrayItemIndex)"
|
|
19
|
-
@update:recordFieldValue="
|
|
19
|
+
@update:recordFieldValue="recordFieldValueUpdate"
|
|
20
20
|
@update:unmasked="$emit('update:unmasked', column.name)"
|
|
21
21
|
@update:inValidity="$emit('update:inValidity', { name: column.name, value: $event })"
|
|
22
22
|
@update:emptiness="$emit('update:emptiness', { name: column.name, value: $event })"
|
|
23
|
-
@delete="
|
|
23
|
+
@delete="deleteHandler(arrayItemIndex)"
|
|
24
24
|
/>
|
|
25
25
|
</div>
|
|
26
26
|
<div class="flex items-center">
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
:columnOptions="columnOptions"
|
|
49
49
|
:unmasked="unmasked"
|
|
50
50
|
@update:modelValue="setCurrentValue(column.name, $event)"
|
|
51
|
-
@update:recordFieldValue="
|
|
51
|
+
@update:recordFieldValue="recordFieldValueUpdate"
|
|
52
52
|
@update:unmasked="$emit('update:unmasked', column.name)"
|
|
53
53
|
@update:inValidity="$emit('update:inValidity', { name: column.name, value: $event })"
|
|
54
54
|
@update:emptiness="$emit('update:emptiness', { name: column.name, value: $event })"
|
|
@@ -80,4 +80,12 @@
|
|
|
80
80
|
await nextTick();
|
|
81
81
|
arrayItemRefs.value[arrayItemRefs.value.length - 1].focus();
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
function recordFieldValueUpdate({ fieldName, fieldValue }: { fieldName: string; fieldValue: any }) {
|
|
85
|
+
props.setCurrentValue(fieldName, fieldValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function deleteHandler(arrayItemIndex: number | string) {
|
|
89
|
+
props.setCurrentValue(props.column.name, props.currentValues[props.column.name].filter((_: any, index: any) => index !== arrayItemIndex));
|
|
90
|
+
}
|
|
83
91
|
</script>
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<div
|
|
12
12
|
v-if="showMenu"
|
|
13
13
|
ref="menuRef"
|
|
14
|
-
class="z-
|
|
14
|
+
class="z-40 bg-white dark:bg-gray-900 rounded-md shadow-lg border dark:border-gray-700 py-1"
|
|
15
15
|
:style="menuStyles"
|
|
16
16
|
>
|
|
17
17
|
<template v-if="!resourceOptions?.baseActionsAsQuickIcons || (resourceOptions?.baseActionsAsQuickIcons && !resourceOptions?.baseActionsAsQuickIcons.includes('show'))">
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
{{ $t('Delete item') }}
|
|
58
58
|
</button>
|
|
59
59
|
</template>
|
|
60
|
-
<div v-for="action in (resourceOptions.actions ?? []).filter(a => a.showIn?.listThreeDotsMenu)" :key="action.id" >
|
|
60
|
+
<div v-for="action in (resourceOptions.actions ?? []).filter((a: AdminForthActionInput) => a.showIn?.listThreeDotsMenu)" :key="action.id" >
|
|
61
61
|
<button class="flex text-nowrap p-1 hover:bg-gray-100 dark:hover:bg-gray-800 w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-300" @click="() => { startCustomAction(action.id, record); showMenu = false; }">
|
|
62
62
|
<component
|
|
63
63
|
:is="action.customComponent ? getCustomComponent(action.customComponent) : CallActionWrapper"
|
|
64
64
|
:meta="action.customComponent?.meta"
|
|
65
65
|
:row="record"
|
|
66
|
-
:resource="resource"
|
|
67
|
-
:adminUser="adminUser"
|
|
66
|
+
:resource="coreStore.resource"
|
|
67
|
+
:adminUser="coreStore.adminUser"
|
|
68
68
|
@callAction="(payload? : Object) => startCustomAction(action.id, record, payload)"
|
|
69
69
|
>
|
|
70
70
|
<component
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
<template v-if="customActionIconsThreeDotsMenuItems">
|
|
80
80
|
<component
|
|
81
81
|
v-for="c in customActionIconsThreeDotsMenuItems"
|
|
82
|
-
:is="getCustomComponent(c)"
|
|
83
|
-
:meta="c.meta"
|
|
82
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
83
|
+
:meta="formatComponent(c).meta"
|
|
84
84
|
:resource="coreStore.resource"
|
|
85
85
|
:adminUser="coreStore.adminUser"
|
|
86
86
|
:record="record"
|
|
@@ -100,9 +100,10 @@ import {
|
|
|
100
100
|
IconDotsHorizontalOutline
|
|
101
101
|
} from '@iconify-prerendered/vue-flowbite';
|
|
102
102
|
import { onMounted, onBeforeUnmount, ref, nextTick, watch } from 'vue';
|
|
103
|
-
import { getIcon, getCustomComponent } from '@/utils';
|
|
103
|
+
import { getIcon, getCustomComponent, formatComponent } from '@/utils';
|
|
104
104
|
import { useCoreStore } from '@/stores/core';
|
|
105
105
|
import CallActionWrapper from '@/components/CallActionWrapper.vue'
|
|
106
|
+
import { type AdminForthActionInput, type AdminForthComponentDeclaration, type AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
106
107
|
|
|
107
108
|
const coreStore = useCoreStore();
|
|
108
109
|
const showMenu = ref(false);
|
|
@@ -113,11 +114,11 @@ const menuStyles = ref<Record<string, string>>({});
|
|
|
113
114
|
const props = defineProps<{
|
|
114
115
|
resourceOptions: any;
|
|
115
116
|
record: any;
|
|
116
|
-
customActionIconsThreeDotsMenuItems:
|
|
117
|
+
customActionIconsThreeDotsMenuItems: AdminForthComponentDeclaration[];
|
|
117
118
|
resourceId: string;
|
|
118
119
|
deleteRecord: (record: any) => void;
|
|
119
120
|
updateRecords: () => void;
|
|
120
|
-
startCustomAction: (actionId: string,
|
|
121
|
+
startCustomAction: (actionId: string, row: any, extraData?: Record<string, any>) => void;
|
|
121
122
|
}>();
|
|
122
123
|
|
|
123
124
|
onMounted(() => {
|