adminforth 2.26.2 → 2.27.0-next.2
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/modules/restApi.d.ts +1 -0
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +25 -1
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.js +2 -2
- package/dist/modules/styles.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 +44 -7
- package/dist/spa/package.json +1 -1
- package/dist/spa/pnpm-lock.yaml +301 -299
- package/dist/spa/src/App.vue +1 -1
- package/dist/spa/src/adminforth.ts +17 -29
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/Modal.vue +12 -1
- 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 +2 -0
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +11 -3
- package/dist/spa/src/components/CustomRangePicker.vue +16 -67
- package/dist/spa/src/components/ListActionsThreeDots.vue +9 -8
- package/dist/spa/src/components/RangePicker.vue +236 -0
- package/dist/spa/src/components/ResourceListTable.vue +45 -70
- package/dist/spa/src/components/Sidebar.vue +1 -1
- package/dist/spa/src/components/ThreeDotsMenu.vue +30 -52
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/stores/core.ts +4 -2
- package/dist/spa/src/types/Back.ts +11 -4
- package/dist/spa/src/types/Common.ts +26 -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 +187 -10
- package/dist/spa/src/views/CreateView.vue +10 -10
- package/dist/spa/src/views/EditView.vue +10 -9
- package/dist/spa/src/views/ListView.vue +122 -18
- package/dist/spa/src/views/LoginView.vue +13 -13
- package/dist/spa/src/views/ShowView.vue +53 -60
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/types/Back.d.ts +8 -5
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +21 -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 +1 -1
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
<component
|
|
13
13
|
v-if="!coreStore.isResourceFetching && !initInProcess"
|
|
14
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.beforeBreadcrumbs || []"
|
|
15
|
-
:is="getCustomComponent(c)"
|
|
14
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.beforeBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
15
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
16
16
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
17
17
|
:resource="coreStore.resource"
|
|
18
18
|
:adminUser="coreStore.adminUser"
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
<BreadcrumbsWithButtons>
|
|
22
22
|
<component
|
|
23
23
|
v-if="!coreStore.isResourceFetching && !initInProcess"
|
|
24
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.beforeActionButtons || []"
|
|
25
|
-
:is="getCustomComponent(c)"
|
|
24
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.beforeActionButtons as AdminForthComponentDeclaration[] || []"
|
|
25
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
26
26
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
27
27
|
:resource="coreStore.resource"
|
|
28
28
|
:adminUser="coreStore.adminUser"
|
|
@@ -33,7 +33,12 @@
|
|
|
33
33
|
@click="()=>{checkboxes = []}"
|
|
34
34
|
v-if="checkboxes.length"
|
|
35
35
|
data-tooltip-target="tooltip-remove-all"
|
|
36
|
-
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-lightListViewButtonText
|
|
36
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-lightListViewButtonText af-button-shadow
|
|
37
|
+
focus:outline-none bg-lightListViewButtonBackground rounded border border-lightListViewButtonBorder h-[34px]
|
|
38
|
+
hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover focus:z-10 focus:ring-4
|
|
39
|
+
focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
|
|
40
|
+
dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder
|
|
41
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover rounded-default"
|
|
37
42
|
>
|
|
38
43
|
<Tooltip>
|
|
39
44
|
<IconBanOutline class="w-5 h-5 "/>
|
|
@@ -43,7 +48,7 @@
|
|
|
43
48
|
</Tooltip>
|
|
44
49
|
</button>
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
<div
|
|
47
52
|
v-if="checkboxes.length"
|
|
48
53
|
v-for="(action,i) in coreStore.resource?.options?.bulkActions"
|
|
49
54
|
>
|
|
@@ -51,7 +56,13 @@
|
|
|
51
56
|
v-if="!action.showInThreeDotsDropdown"
|
|
52
57
|
:key="action.id"
|
|
53
58
|
@click="startBulkActionInner(action.id!)"
|
|
54
|
-
class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-lightListViewButtonText
|
|
59
|
+
class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-lightListViewButtonText
|
|
60
|
+
focus:outline-none bg-lightListViewButtonBackground rounded-default border h-[34px]
|
|
61
|
+
border-lightListViewButtonBorder hover:bg-lightListViewButtonBackgroundHover
|
|
62
|
+
hover:text-lightListViewButtonTextHover focus:z-10 focus:ring-4 af-button-shadow
|
|
63
|
+
focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
|
|
64
|
+
dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder
|
|
65
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover"
|
|
55
66
|
:class="action.buttonCustomCssClass || ''"
|
|
56
67
|
>
|
|
57
68
|
<component
|
|
@@ -72,23 +83,64 @@
|
|
|
72
83
|
<span class="sr-only">Loading...</span>
|
|
73
84
|
</div>
|
|
74
85
|
{{ `${action.label} (${checkboxes.length})` }}
|
|
75
|
-
<div v-if="action.badge" class="text-white bg-gradient-to-r from-purple-500
|
|
86
|
+
<div v-if="action.badge" class="text-white bg-gradient-to-r from-purple-500
|
|
87
|
+
via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none
|
|
88
|
+
focus:ring-purple-300 dark:focus:ring-purple-800
|
|
76
89
|
font-medium rounded-sm text-xs px-1 ml-1 text-center ">
|
|
77
90
|
{{ action.badge }}
|
|
78
91
|
</div>
|
|
79
92
|
</button>
|
|
80
93
|
</div>
|
|
94
|
+
<div
|
|
95
|
+
v-if="checkboxes.length"
|
|
96
|
+
v-for="(action,i) in coreStore.resource?.options?.actions?.filter(a => a.showIn?.bulkButton)"
|
|
97
|
+
>
|
|
98
|
+
<button
|
|
99
|
+
:key="action.id"
|
|
100
|
+
@click="startCustomBulkActionInner(action.id!)"
|
|
101
|
+
class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-lightListViewButtonText focus:outline-none bg-lightListViewButtonBackground rounded-default border border-lightListViewButtonBorder hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover"
|
|
102
|
+
>
|
|
103
|
+
<component
|
|
104
|
+
v-if="action.icon && !customActionLoadingStates[action.id!]"
|
|
105
|
+
:is="getIcon(action.icon)"
|
|
106
|
+
class="w-5 h-5 transition duration-75 group-hover:text-gray-900 dark:group-hover:text-white"></component>
|
|
107
|
+
<div v-if="customActionLoadingStates[action.id!]">
|
|
108
|
+
<svg
|
|
109
|
+
aria-hidden="true"
|
|
110
|
+
class="w-5 h-5 animate-spin text-gray-200 dark:text-gray-500 fill-gray-500 dark:fill-gray-300"
|
|
111
|
+
viewBox="0 0 100 101"
|
|
112
|
+
fill="none"
|
|
113
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
114
|
+
>
|
|
115
|
+
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
|
116
|
+
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
|
117
|
+
</svg>
|
|
118
|
+
<span class="sr-only">Loading...</span>
|
|
119
|
+
</div>
|
|
120
|
+
{{ `${action.name} (${checkboxes.length})` }}
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
81
123
|
|
|
82
124
|
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
83
125
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
84
|
-
class="af-create-button flex items-center py-1 px-3 text-sm
|
|
126
|
+
class="af-create-button flex items-center py-1 h-[34px] px-3 text-sm af-button-shadow
|
|
127
|
+
font-medium text-lightPrimaryContrast transition-all focus:outline-none
|
|
128
|
+
bg-lightPrimary hover:bg-lightPrimary/80 dark:bg-darkPrimary dark:hover:bg-darkPrimary/80
|
|
129
|
+
rounded border border-lightPrimary/90 focus:z-10 focus:ring-4 focus:ring-lightListViewButtonFocusRing
|
|
130
|
+
dark:focus:ring-darkListViewButtonFocusRing dark:text-darkPrimaryContrast dark:border-darkPrimary/80
|
|
131
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover rounded-default gap-1"
|
|
85
132
|
>
|
|
86
133
|
<IconPlusOutline class="w-4 h-4"/>
|
|
87
134
|
{{ $t('Create') }}
|
|
88
135
|
</RouterLink>
|
|
89
136
|
|
|
90
137
|
<button
|
|
91
|
-
class="af-filter-button flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium
|
|
138
|
+
class="af-filter-button flex gap-1 items-center py-1 h-[34px] px-3 me-2 af-button-shadow text-sm font-medium
|
|
139
|
+
text-lightListViewButtonText transition-all focus:outline-none bg-lightListViewButtonBackground rounded border
|
|
140
|
+
border-lightListViewButtonBorder hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover
|
|
141
|
+
focus:z-10 focus:ring-4 focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
|
|
142
|
+
dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder
|
|
143
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover rounded-default"
|
|
92
144
|
@click="()=>{filtersShow = !filtersShow}"
|
|
93
145
|
v-if="coreStore.resource?.options?.allowedActions?.filter"
|
|
94
146
|
>
|
|
@@ -114,8 +166,8 @@
|
|
|
114
166
|
|
|
115
167
|
<component
|
|
116
168
|
v-if="!coreStore.isResourceFetching && !initInProcess"
|
|
117
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.afterBreadcrumbs || []"
|
|
118
|
-
:is="getCustomComponent(c)"
|
|
169
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.afterBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
170
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
119
171
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
120
172
|
:resource="coreStore.resource"
|
|
121
173
|
:adminUser="coreStore.adminUser"
|
|
@@ -161,8 +213,8 @@
|
|
|
161
213
|
/>
|
|
162
214
|
|
|
163
215
|
<component
|
|
164
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.bottom || []"
|
|
165
|
-
:is="getCustomComponent(c)"
|
|
216
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.bottom as AdminForthComponentDeclaration[] || []"
|
|
217
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
166
218
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
167
219
|
:resource="coreStore.resource"
|
|
168
220
|
:adminUser="coreStore.adminUser"
|
|
@@ -176,13 +228,13 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
|
176
228
|
import ResourceListTable from '@/components/ResourceListTable.vue';
|
|
177
229
|
import { useCoreStore } from '@/stores/core';
|
|
178
230
|
import { useFiltersStore } from '@/stores/filters';
|
|
179
|
-
import { callAdminForthApi, currentQuery, getIcon, setQuery } from '@/utils';
|
|
231
|
+
import { callAdminForthApi, currentQuery, getIcon, setQuery, formatComponent, executeCustomBulkAction } from '@/utils';
|
|
180
232
|
import { computed, onMounted, onUnmounted, ref, watch, type Ref } from 'vue';
|
|
181
233
|
import { useRoute } from 'vue-router';
|
|
182
234
|
import { getCustomComponent, initThreeDotsDropdown, getList, startBulkAction } from '@/utils';
|
|
183
235
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
184
236
|
import { Tooltip } from '@/afcl'
|
|
185
|
-
import type { AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
237
|
+
import type { AdminForthComponentDeclaration, AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
186
238
|
|
|
187
239
|
|
|
188
240
|
import {
|
|
@@ -195,7 +247,7 @@ import Filters from '@/components/Filters.vue';
|
|
|
195
247
|
import { useAdminforth } from '@/adminforth';
|
|
196
248
|
|
|
197
249
|
const filtersShow = ref(false);
|
|
198
|
-
const { list } = useAdminforth();
|
|
250
|
+
const { list, alert } = useAdminforth();
|
|
199
251
|
const coreStore = useCoreStore();
|
|
200
252
|
const filtersStore = useFiltersStore();
|
|
201
253
|
|
|
@@ -214,6 +266,7 @@ const rows: Ref<any[]|null> = ref(null);
|
|
|
214
266
|
const totalRows = ref(0);
|
|
215
267
|
const checkboxes = ref([]);
|
|
216
268
|
const bulkActionLoadingStates = ref<{[key: string]: boolean}>({});
|
|
269
|
+
const customActionLoadingStates = ref<{[key: string]: boolean}>({});
|
|
217
270
|
|
|
218
271
|
const DEFAULT_PAGE_SIZE = 10;
|
|
219
272
|
|
|
@@ -283,6 +336,38 @@ async function startBulkActionInner(actionId: string) {
|
|
|
283
336
|
await startBulkAction(actionId, coreStore.resource!, checkboxes, bulkActionLoadingStates, getListInner);
|
|
284
337
|
}
|
|
285
338
|
|
|
339
|
+
async function startCustomBulkActionInner(actionId: string | number) {
|
|
340
|
+
const action = coreStore.resource?.options?.actions?.find(a => a.id === actionId);
|
|
341
|
+
|
|
342
|
+
await executeCustomBulkAction({
|
|
343
|
+
actionId,
|
|
344
|
+
resourceId: route.params.resourceId as string,
|
|
345
|
+
recordIds: checkboxes.value,
|
|
346
|
+
confirmMessage: action?.bulkConfirmationMessage,
|
|
347
|
+
setLoadingState: (loading: boolean) => {
|
|
348
|
+
customActionLoadingStates.value[actionId] = loading;
|
|
349
|
+
},
|
|
350
|
+
onSuccess: async (results: any[]) => {
|
|
351
|
+
checkboxes.value = [];
|
|
352
|
+
await getListInner();
|
|
353
|
+
|
|
354
|
+
const successResults = results.filter(r => r?.successMessage);
|
|
355
|
+
if (successResults.length > 0) {
|
|
356
|
+
alert({
|
|
357
|
+
message: action?.bulkSuccessMessage || `${successResults.length} out of ${results.length} items processed successfully`,
|
|
358
|
+
variant: 'success'
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
onError: (error: string) => {
|
|
363
|
+
alert({
|
|
364
|
+
message: error,
|
|
365
|
+
variant: 'danger'
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
286
371
|
async function getListInner() {
|
|
287
372
|
rows.value = null; // to show loading state
|
|
288
373
|
const result = await getList(coreStore.resource!, isPageLoaded.value, page.value, pageSize.value, sort.value, checkboxes, filtersStore.filters);
|
|
@@ -445,4 +530,23 @@ watch([sort], async () => {
|
|
|
445
530
|
setQuery({ sort: SortQuerySerializer.serialize(sort.value) });
|
|
446
531
|
});
|
|
447
532
|
|
|
448
|
-
</script>
|
|
533
|
+
</script>
|
|
534
|
+
|
|
535
|
+
<style lang="scss">
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
.af-button-shadow {
|
|
539
|
+
position: relative;
|
|
540
|
+
&::after {
|
|
541
|
+
content: '';
|
|
542
|
+
position: absolute;
|
|
543
|
+
left: 0;
|
|
544
|
+
right: 0;
|
|
545
|
+
height: 100%;
|
|
546
|
+
box-shadow: -0px 6px 6px rgb(0, 0, 0, 0.1);
|
|
547
|
+
border-radius: inherit;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
</style>
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
<!-- Modal header -->
|
|
32
32
|
<div class="af-login-modal-header flex items-center justify-between flex-col p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
33
33
|
|
|
34
|
-
<template v-if="coreStore?.config?.loginPageInjections?.panelHeader.length > 0">
|
|
34
|
+
<template v-if="coreStore?.config?.loginPageInjections?.panelHeader.length && coreStore?.config?.loginPageInjections?.panelHeader.length > 0">
|
|
35
35
|
<component
|
|
36
36
|
v-for="(c, index) in coreStore?.config?.loginPageInjections?.panelHeader || []"
|
|
37
37
|
:key="index"
|
|
38
|
-
:is="getCustomComponent(c)"
|
|
39
|
-
:meta="c.meta"
|
|
38
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
39
|
+
:meta="formatComponent(c).meta"
|
|
40
40
|
/>
|
|
41
41
|
</template>
|
|
42
42
|
<h3 v-else class="text-xl font-semibold text-lightLoginViewText dark:text-darkLoginViewTextColor">
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
name="username"
|
|
56
56
|
id="username"
|
|
57
57
|
ref="usernameInput"
|
|
58
|
-
@keydown.enter="passwordInput
|
|
58
|
+
@keydown.enter="passwordInput?.focus()"
|
|
59
59
|
class="w-full"
|
|
60
60
|
placeholder="name@company.com" required />
|
|
61
61
|
</div>
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
</Input>
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
|
-
<div v-if="coreStore
|
|
79
|
+
<div v-if="coreStore?.config?.rememberMeDuration"
|
|
80
80
|
class="flex items-start mb-5"
|
|
81
81
|
:title="$t(`Stay logged in for {days}`, {days: coreStore.config.rememberMeDuration})"
|
|
82
82
|
>
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
|
|
89
89
|
<component
|
|
90
90
|
v-for="c in coreStore?.config?.loginPageInjections?.underInputs || []"
|
|
91
|
-
:is="getCustomComponent(c)"
|
|
92
|
-
:meta="c.meta"
|
|
91
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
92
|
+
:meta="formatComponent(c).meta"
|
|
93
93
|
@update:disableLoginButton="setDisableLoginButton($event)"
|
|
94
94
|
/>
|
|
95
95
|
|
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
</Button>
|
|
108
108
|
<component
|
|
109
109
|
v-for="c in coreStore?.config?.loginPageInjections?.underLoginButton || []"
|
|
110
|
-
:is="getCustomComponent(c)"
|
|
111
|
-
:meta="c.meta"
|
|
110
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
111
|
+
:meta="formatComponent(c).meta"
|
|
112
112
|
@update:disableLoginButton="setDisableLoginButton($event)"
|
|
113
113
|
/>
|
|
114
114
|
</form>
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
|
|
125
125
|
<script setup lang="ts">
|
|
126
126
|
|
|
127
|
-
import { getCustomComponent } from '@/utils';
|
|
127
|
+
import { getCustomComponent, formatComponent } from '@/utils';
|
|
128
128
|
import { onBeforeMount, onMounted, ref, computed } from 'vue';
|
|
129
129
|
import { useCoreStore } from '@/stores/core';
|
|
130
130
|
import { useUserStore } from '@/stores/user';
|
|
@@ -137,8 +137,8 @@ import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
|
137
137
|
|
|
138
138
|
const { t } = useI18n();
|
|
139
139
|
|
|
140
|
-
const passwordInput = ref(null);
|
|
141
|
-
const usernameInput = ref(null);
|
|
140
|
+
const passwordInput = ref<InstanceType<typeof Input> | null>(null);
|
|
141
|
+
const usernameInput = ref<InstanceType<typeof Input> | null>(null);
|
|
142
142
|
const rememberMeValue= ref(false);
|
|
143
143
|
const username = ref('');
|
|
144
144
|
const password = ref('');
|
|
@@ -179,7 +179,7 @@ onMounted(async () => {
|
|
|
179
179
|
username.value = demoUsername;
|
|
180
180
|
password.value = demoPassword;
|
|
181
181
|
}
|
|
182
|
-
usernameInput.value
|
|
182
|
+
usernameInput.value?.focus();
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<component
|
|
4
4
|
v-if="!loading"
|
|
5
5
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.beforeBreadcrumbs || []"
|
|
6
|
-
:is="getCustomComponent(c)"
|
|
7
|
-
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
6
|
+
:is="getCustomComponent(formatComponent(c as AdminForthComponentDeclarationFull))"
|
|
7
|
+
:meta="formatComponent(c as AdminForthComponentDeclarationFull).meta"
|
|
8
8
|
:record="coreStore.record"
|
|
9
9
|
:resource="coreStore.resource"
|
|
10
10
|
:adminUser="coreStore.adminUser"
|
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
|
|
15
15
|
<template v-for="action in coreStore.resource.options.actions.filter(a => a.showIn?.showButton)" :key="action.id">
|
|
16
16
|
<component
|
|
17
|
-
:is="action?.customComponent ? getCustomComponent(action.customComponent) : CallActionWrapper"
|
|
18
|
-
:meta="action.customComponent
|
|
19
|
-
@callAction="(payload
|
|
17
|
+
:is="action?.customComponent ? getCustomComponent(formatComponent(action.customComponent)) : CallActionWrapper"
|
|
18
|
+
:meta="action.customComponent ? formatComponent(action.customComponent).meta : undefined"
|
|
19
|
+
@callAction="(payload?: any) => startCustomAction(action.id, payload)"
|
|
20
20
|
:disabled="actionLoadingStates[action.id]"
|
|
21
21
|
>
|
|
22
22
|
<button
|
|
23
23
|
:key="action.id"
|
|
24
24
|
:disabled="actionLoadingStates[action.id!]"
|
|
25
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
25
|
+
class="flex items-center af-button-shadow h-[34px] py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
26
26
|
>
|
|
27
27
|
<component
|
|
28
28
|
v-if="action.icon"
|
|
@@ -36,21 +36,21 @@
|
|
|
36
36
|
</template>
|
|
37
37
|
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
38
38
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
39
|
-
class="af-add-new-button flex items-center py-1 px-3 text-sm font-medium text-lightShowViewButtonText focus:outline-none bg-lightShowViewButtonBackground rounded border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-lightShowViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-darkShowViewButtonText dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover rounded-default gap-1"
|
|
39
|
+
class="af-add-new-button af-button-shadow h-[34px] flex items-center py-1 px-3 text-sm font-medium text-lightShowViewButtonText focus:outline-none bg-lightShowViewButtonBackground rounded border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-lightShowViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-darkShowViewButtonText dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover rounded-default gap-1"
|
|
40
40
|
>
|
|
41
41
|
<IconPlusOutline class="w-4 h-4"/>
|
|
42
42
|
{{ $t('Add new') }}
|
|
43
43
|
</RouterLink>
|
|
44
44
|
|
|
45
45
|
<RouterLink v-if="coreStore?.resourceOptions?.allowedActions?.edit" :to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: $route.params.primaryKey } }"
|
|
46
|
-
class="flex items-center af-edit-button py-1 px-3 text-sm font-medium text-lightShowViewButtonText focus:outline-none bg-lightShowViewButtonBackground rounded-default border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-lightShowViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-darkShowViewButtonText dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover gap-1"
|
|
46
|
+
class="flex items-center h-[34px] af-button-shadow af-edit-button py-1 px-3 text-sm font-medium text-lightShowViewButtonText focus:outline-none bg-lightShowViewButtonBackground rounded-default border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-lightShowViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-darkShowViewButtonText dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover gap-1"
|
|
47
47
|
>
|
|
48
48
|
<IconPenSolid class="w-4 h-4" />
|
|
49
49
|
{{ $t('Edit') }}
|
|
50
50
|
</RouterLink>
|
|
51
51
|
|
|
52
52
|
<button v-if="coreStore?.resourceOptions?.allowedActions?.delete" @click="deleteRecord"
|
|
53
|
-
class="flex items-center af-delete-button py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-lightShowViewButtonBackground border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-red-500 dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover gap-1"
|
|
53
|
+
class="flex items-center h-[34px] af-button-shadow af-delete-button py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-lightShowViewButtonBackground border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-red-500 dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover gap-1"
|
|
54
54
|
>
|
|
55
55
|
<IconTrashBinSolid class="w-4 h-4" />
|
|
56
56
|
{{ $t('Delete') }}
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
<component
|
|
66
66
|
v-if="!loading"
|
|
67
67
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.afterBreadcrumbs || []"
|
|
68
|
-
:is="getCustomComponent(c)"
|
|
69
|
-
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
68
|
+
:is="getCustomComponent(formatComponent(c as AdminForthComponentDeclarationFull))"
|
|
69
|
+
:meta="formatComponent(c as AdminForthComponentDeclarationFull).meta"
|
|
70
70
|
:record="coreStore.record"
|
|
71
71
|
:resource="coreStore.resource"
|
|
72
72
|
:adminUser="coreStore.adminUser"
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
<template v-else>
|
|
96
96
|
<template v-for="group in groups" :key="group.groupName">
|
|
97
97
|
<ShowTable
|
|
98
|
-
:columns="group.columns"
|
|
98
|
+
:columns="group.columns as any"
|
|
99
99
|
:groupName="group.groupName"
|
|
100
100
|
:noTitle="group.noTitle"
|
|
101
101
|
:resource="coreStore.resource"
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
<component
|
|
121
121
|
v-if="!loading"
|
|
122
122
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.bottom || []"
|
|
123
|
-
:is="getCustomComponent(c)"
|
|
124
|
-
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
123
|
+
:is="getCustomComponent(formatComponent(c as AdminForthComponentDeclarationFull))"
|
|
124
|
+
:meta="formatComponent(c as AdminForthComponentDeclarationFull).meta"
|
|
125
125
|
:record="coreStore.record"
|
|
126
126
|
:resource="coreStore.resource"
|
|
127
127
|
:adminUser="coreStore.adminUser"
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
138
138
|
|
|
139
139
|
import { useCoreStore } from '@/stores/core';
|
|
140
|
-
import { getCustomComponent, checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
|
|
140
|
+
import { getCustomComponent, checkAcessByAllowedActions, initThreeDotsDropdown, formatComponent, executeCustomAction } from '@/utils';
|
|
141
141
|
import { IconPenSolid, IconTrashBinSolid, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
|
|
142
142
|
import { onMounted, ref, computed } from 'vue';
|
|
143
143
|
import { useRoute,useRouter } from 'vue-router';
|
|
@@ -245,55 +245,48 @@ async function deleteRecord() {
|
|
|
245
245
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
async function startCustomAction(actionId: string, extra
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
window.location.href = data.redirectUrl;
|
|
272
|
-
} else {
|
|
273
|
-
router.push(data.redirectUrl);
|
|
248
|
+
async function startCustomAction(actionId: string, extra?: any) {
|
|
249
|
+
await executeCustomAction({
|
|
250
|
+
actionId,
|
|
251
|
+
resourceId: route.params.resourceId as string,
|
|
252
|
+
recordId: route.params.primaryKey as string,
|
|
253
|
+
extra,
|
|
254
|
+
setLoadingState: (loading: boolean) => {
|
|
255
|
+
actionLoadingStates.value[actionId] = loading;
|
|
256
|
+
},
|
|
257
|
+
onSuccess: async (data: any) => {
|
|
258
|
+
if (data?.redirectUrl) {
|
|
259
|
+
// Check if the URL should open in a new tab
|
|
260
|
+
if (data.redirectUrl.includes('target=_blank')) {
|
|
261
|
+
window.open(data.redirectUrl.replace('&target=_blank', '').replace('?target=_blank', ''), '_blank');
|
|
262
|
+
} else {
|
|
263
|
+
// Navigate within the app
|
|
264
|
+
if (data.redirectUrl.startsWith('http')) {
|
|
265
|
+
window.location.href = data.redirectUrl;
|
|
266
|
+
} else {
|
|
267
|
+
router.push(data.redirectUrl);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
274
271
|
}
|
|
275
|
-
}
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (data?.ok) {
|
|
280
|
-
await coreStore.fetchRecord({
|
|
281
|
-
resourceId: route.params.resourceId as string,
|
|
282
|
-
primaryKey: route.params.primaryKey as string,
|
|
283
|
-
source: 'show',
|
|
284
|
-
});
|
|
285
272
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
273
|
+
await coreStore.fetchRecord({
|
|
274
|
+
resourceId: route.params.resourceId as string,
|
|
275
|
+
primaryKey: route.params.primaryKey as string,
|
|
276
|
+
source: 'show',
|
|
290
277
|
});
|
|
278
|
+
|
|
279
|
+
if (data.successMessage) {
|
|
280
|
+
alert({
|
|
281
|
+
message: data.successMessage,
|
|
282
|
+
variant: 'success'
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
onError: (error: string) => {
|
|
287
|
+
showErrorTost(error);
|
|
291
288
|
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (data?.error) {
|
|
295
|
-
showErrorTost(data.error);
|
|
296
|
-
}
|
|
289
|
+
});
|
|
297
290
|
}
|
|
298
291
|
|
|
299
292
|
show.refresh = () => {
|
package/dist/types/Back.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Express, Request } from 'express';
|
|
1
|
+
import type { Express, Request, Response } from 'express';
|
|
2
2
|
import type { Writable } from 'stream';
|
|
3
3
|
import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections, AllowedActionsEnum, AdminForthResourcePages, type AdminForthComponentDeclaration, type AdminUser, type AllowedActionsResolved, type AdminForthBulkActionCommon, type AdminForthForeignResourceCommon, type AdminForthResourceColumnCommon, type AdminForthResourceInputCommon, type AdminForthComponentDeclarationFull, type AdminForthConfigMenuItem, type AnnouncementBadgeResponse, type AdminForthResourceColumnInputCommon } from './Common.js';
|
|
4
4
|
export interface ICodeInjector {
|
|
@@ -44,7 +44,7 @@ export interface IHttpServer {
|
|
|
44
44
|
[key: string]: string;
|
|
45
45
|
}, cookies: {
|
|
46
46
|
[key: string]: string;
|
|
47
|
-
}, response: IAdminForthHttpResponse) => void;
|
|
47
|
+
}, response: IAdminForthHttpResponse, requestUrl: string, abortSignal: AbortSignal, _raw_express_req: Request, _raw_express_res: Response) => void;
|
|
48
48
|
}): void;
|
|
49
49
|
}
|
|
50
50
|
export interface IExpressHttpServer extends IHttpServer {
|
|
@@ -1191,11 +1191,14 @@ interface AdminForthInputConfigCustomization {
|
|
|
1191
1191
|
}
|
|
1192
1192
|
export interface AdminForthActionInput {
|
|
1193
1193
|
name: string;
|
|
1194
|
+
bulkConfirmationMessage?: string;
|
|
1195
|
+
bulkSuccessMessage?: string;
|
|
1194
1196
|
showIn?: {
|
|
1195
1197
|
list?: boolean;
|
|
1196
1198
|
listThreeDotsMenu?: boolean;
|
|
1197
1199
|
showButton?: boolean;
|
|
1198
1200
|
showThreeDotsMenu?: boolean;
|
|
1201
|
+
bulkButton?: boolean;
|
|
1199
1202
|
};
|
|
1200
1203
|
allowed?: (params: {
|
|
1201
1204
|
adminUser: AdminUser;
|
|
@@ -1216,7 +1219,7 @@ export interface AdminForthActionInput {
|
|
|
1216
1219
|
message?: string;
|
|
1217
1220
|
}>;
|
|
1218
1221
|
icon?: string;
|
|
1219
|
-
id
|
|
1222
|
+
id: string;
|
|
1220
1223
|
customComponent?: AdminForthComponentDeclaration;
|
|
1221
1224
|
}
|
|
1222
1225
|
export interface AdminForthResourceInput extends Omit<NonNullable<AdminForthResourceInputCommon>, 'columns' | 'hooks' | 'options'> {
|
|
@@ -1494,7 +1497,7 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
|
|
|
1494
1497
|
customPages: Array<AdminForthPageDeclaration>;
|
|
1495
1498
|
loginPageInjections: {
|
|
1496
1499
|
underInputs: Array<AdminForthComponentDeclarationFull>;
|
|
1497
|
-
underLoginButton
|
|
1500
|
+
underLoginButton: Array<AdminForthComponentDeclarationFull>;
|
|
1498
1501
|
panelHeader: Array<AdminForthComponentDeclarationFull>;
|
|
1499
1502
|
};
|
|
1500
1503
|
globalInjections: {
|
|
@@ -1590,7 +1593,7 @@ export type AllowedActions = {
|
|
|
1590
1593
|
/**
|
|
1591
1594
|
* General options for resource.
|
|
1592
1595
|
*/
|
|
1593
|
-
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions'> {
|
|
1596
|
+
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions' | 'actions'> {
|
|
1594
1597
|
/**
|
|
1595
1598
|
* Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
|
|
1596
1599
|
* using checkboxes.
|