adminforth 2.26.4 → 2.27.0-next.10
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 +65 -3
- 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 +85 -7
- package/dist/spa/package.json +4 -1
- package/dist/spa/pnpm-lock.yaml +339 -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 +35 -4
- package/dist/spa/src/components/CustomRangePicker.vue +22 -67
- package/dist/spa/src/components/GroupsTable.vue +7 -4
- package/dist/spa/src/components/ListActionsThreeDots.vue +9 -8
- package/dist/spa/src/components/RangePicker.vue +236 -0
- package/dist/spa/src/components/ResourceForm.vue +100 -6
- 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 +54 -57
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/stores/core.ts +4 -2
- package/dist/spa/src/types/Back.ts +10 -3
- package/dist/spa/src/types/Common.ts +43 -8
- package/dist/spa/src/types/FrontendAPI.ts +6 -1
- package/dist/spa/src/types/adapters/StorageAdapter.ts +12 -0
- package/dist/spa/src/utils/createEditUtils.ts +65 -0
- package/dist/spa/src/utils/index.ts +2 -1
- package/dist/spa/src/utils/listUtils.ts +8 -2
- package/dist/spa/src/utils/utils.ts +192 -12
- package/dist/spa/src/utils.ts +2 -1
- package/dist/spa/src/views/CreateView.vue +32 -59
- package/dist/spa/src/views/EditView.vue +30 -47
- package/dist/spa/src/views/ListView.vue +119 -18
- package/dist/spa/src/views/LoginView.vue +13 -13
- package/dist/spa/src/views/ShowView.vue +67 -61
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/types/Back.d.ts +7 -4
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +43 -8
- 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/dist/types/adapters/StorageAdapter.d.ts +11 -0
- package/dist/types/adapters/StorageAdapter.d.ts.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,70 @@
|
|
|
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
|
|
102
|
+
focus:outline-none bg-lightListViewButtonBackground rounded-default border h-[34px]
|
|
103
|
+
border-lightListViewButtonBorder hover:bg-lightListViewButtonBackgroundHover
|
|
104
|
+
hover:text-lightListViewButtonTextHover focus:z-10 focus:ring-4 af-button-shadow
|
|
105
|
+
focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
|
|
106
|
+
dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder
|
|
107
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover"
|
|
108
|
+
>
|
|
109
|
+
<component
|
|
110
|
+
v-if="action.icon && !customActionLoadingStates[action.id!]"
|
|
111
|
+
:is="getIcon(action.icon)"
|
|
112
|
+
class="w-5 h-5 transition duration-75 group-hover:text-gray-900 dark:group-hover:text-white"></component>
|
|
113
|
+
<div v-if="customActionLoadingStates[action.id!]">
|
|
114
|
+
<svg
|
|
115
|
+
aria-hidden="true"
|
|
116
|
+
class="w-5 h-5 animate-spin text-gray-200 dark:text-gray-500 fill-gray-500 dark:fill-gray-300"
|
|
117
|
+
viewBox="0 0 100 101"
|
|
118
|
+
fill="none"
|
|
119
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
120
|
+
>
|
|
121
|
+
<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"/>
|
|
122
|
+
<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"/>
|
|
123
|
+
</svg>
|
|
124
|
+
<span class="sr-only">Loading...</span>
|
|
125
|
+
</div>
|
|
126
|
+
{{ `${action.name} (${checkboxes.length})` }}
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
81
129
|
|
|
82
130
|
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
83
131
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
84
|
-
class="af-create-button flex items-center py-1 px-3 text-sm
|
|
132
|
+
class="af-create-button flex items-center py-1 h-[34px] px-3 text-sm af-button-shadow
|
|
133
|
+
font-medium text-lightPrimaryContrast transition-all focus:outline-none
|
|
134
|
+
bg-lightPrimary hover:bg-lightPrimary/80 dark:bg-darkPrimary dark:hover:bg-darkPrimary/80
|
|
135
|
+
rounded border border-lightPrimary/90 focus:z-10 focus:ring-4 focus:ring-lightListViewButtonFocusRing
|
|
136
|
+
dark:focus:ring-darkListViewButtonFocusRing dark:text-darkPrimaryContrast dark:border-darkPrimary/80
|
|
137
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover rounded-default gap-1"
|
|
85
138
|
>
|
|
86
139
|
<IconPlusOutline class="w-4 h-4"/>
|
|
87
140
|
{{ $t('Create') }}
|
|
88
141
|
</RouterLink>
|
|
89
142
|
|
|
90
143
|
<button
|
|
91
|
-
class="af-filter-button flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium
|
|
144
|
+
class="af-filter-button flex gap-1 items-center py-1 h-[34px] px-3 me-2 af-button-shadow text-sm font-medium
|
|
145
|
+
text-lightListViewButtonText transition-all focus:outline-none bg-lightListViewButtonBackground rounded border
|
|
146
|
+
border-lightListViewButtonBorder hover:bg-lightListViewButtonBackgroundHover hover:text-lightListViewButtonTextHover
|
|
147
|
+
focus:z-10 focus:ring-4 focus:ring-lightListViewButtonFocusRing dark:focus:ring-darkListViewButtonFocusRing
|
|
148
|
+
dark:bg-darkListViewButtonBackground dark:text-darkListViewButtonText dark:border-darkListViewButtonBorder
|
|
149
|
+
dark:hover:text-darkListViewButtonTextHover dark:hover:bg-darkListViewButtonBackgroundHover rounded-default"
|
|
92
150
|
@click="()=>{filtersShow = !filtersShow}"
|
|
93
151
|
v-if="coreStore.resource?.options?.allowedActions?.filter"
|
|
94
152
|
>
|
|
@@ -114,8 +172,8 @@
|
|
|
114
172
|
|
|
115
173
|
<component
|
|
116
174
|
v-if="!coreStore.isResourceFetching && !initInProcess"
|
|
117
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.afterBreadcrumbs || []"
|
|
118
|
-
:is="getCustomComponent(c)"
|
|
175
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.afterBreadcrumbs as AdminForthComponentDeclaration[] || []"
|
|
176
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
119
177
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
120
178
|
:resource="coreStore.resource"
|
|
121
179
|
:adminUser="coreStore.adminUser"
|
|
@@ -161,8 +219,8 @@
|
|
|
161
219
|
/>
|
|
162
220
|
|
|
163
221
|
<component
|
|
164
|
-
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.bottom || []"
|
|
165
|
-
:is="getCustomComponent(c)"
|
|
222
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.bottom as AdminForthComponentDeclaration[] || []"
|
|
223
|
+
:is="getCustomComponent(formatComponent(c))"
|
|
166
224
|
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
167
225
|
:resource="coreStore.resource"
|
|
168
226
|
:adminUser="coreStore.adminUser"
|
|
@@ -176,13 +234,13 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
|
176
234
|
import ResourceListTable from '@/components/ResourceListTable.vue';
|
|
177
235
|
import { useCoreStore } from '@/stores/core';
|
|
178
236
|
import { useFiltersStore } from '@/stores/filters';
|
|
179
|
-
import { callAdminForthApi, currentQuery, getIcon, setQuery } from '@/utils';
|
|
237
|
+
import { callAdminForthApi, currentQuery, getIcon, setQuery, formatComponent, executeCustomBulkAction } from '@/utils';
|
|
180
238
|
import { computed, onMounted, onUnmounted, ref, watch, type Ref } from 'vue';
|
|
181
239
|
import { useRoute } from 'vue-router';
|
|
182
240
|
import { getCustomComponent, initThreeDotsDropdown, getList, startBulkAction } from '@/utils';
|
|
183
241
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
184
242
|
import { Tooltip } from '@/afcl'
|
|
185
|
-
import type { AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
243
|
+
import type { AdminForthComponentDeclaration, AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
186
244
|
|
|
187
245
|
|
|
188
246
|
import {
|
|
@@ -195,7 +253,7 @@ import Filters from '@/components/Filters.vue';
|
|
|
195
253
|
import { useAdminforth } from '@/adminforth';
|
|
196
254
|
|
|
197
255
|
const filtersShow = ref(false);
|
|
198
|
-
const { list } = useAdminforth();
|
|
256
|
+
const { list, alert } = useAdminforth();
|
|
199
257
|
const coreStore = useCoreStore();
|
|
200
258
|
const filtersStore = useFiltersStore();
|
|
201
259
|
|
|
@@ -214,6 +272,7 @@ const rows: Ref<any[]|null> = ref(null);
|
|
|
214
272
|
const totalRows = ref(0);
|
|
215
273
|
const checkboxes = ref([]);
|
|
216
274
|
const bulkActionLoadingStates = ref<{[key: string]: boolean}>({});
|
|
275
|
+
const customActionLoadingStates = ref<{[key: string]: boolean}>({});
|
|
217
276
|
|
|
218
277
|
const DEFAULT_PAGE_SIZE = 10;
|
|
219
278
|
|
|
@@ -283,6 +342,38 @@ async function startBulkActionInner(actionId: string) {
|
|
|
283
342
|
await startBulkAction(actionId, coreStore.resource!, checkboxes, bulkActionLoadingStates, getListInner);
|
|
284
343
|
}
|
|
285
344
|
|
|
345
|
+
async function startCustomBulkActionInner(actionId: string | number) {
|
|
346
|
+
const action = coreStore.resource?.options?.actions?.find(a => a.id === actionId);
|
|
347
|
+
|
|
348
|
+
await executeCustomBulkAction({
|
|
349
|
+
actionId,
|
|
350
|
+
resourceId: route.params.resourceId as string,
|
|
351
|
+
recordIds: checkboxes.value,
|
|
352
|
+
confirmMessage: action?.bulkConfirmationMessage,
|
|
353
|
+
setLoadingState: (loading: boolean) => {
|
|
354
|
+
customActionLoadingStates.value[actionId] = loading;
|
|
355
|
+
},
|
|
356
|
+
onSuccess: async (results: any[]) => {
|
|
357
|
+
checkboxes.value = [];
|
|
358
|
+
await getListInner();
|
|
359
|
+
|
|
360
|
+
const successResults = results.filter(r => r?.successMessage);
|
|
361
|
+
if (successResults.length > 0) {
|
|
362
|
+
alert({
|
|
363
|
+
message: action?.bulkSuccessMessage || `${successResults.length} out of ${results.length} items processed successfully`,
|
|
364
|
+
variant: 'success'
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
onError: (error: string) => {
|
|
369
|
+
alert({
|
|
370
|
+
message: error,
|
|
371
|
+
variant: 'danger'
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
286
377
|
async function getListInner() {
|
|
287
378
|
rows.value = null; // to show loading state
|
|
288
379
|
const result = await getList(coreStore.resource!, isPageLoaded.value, page.value, pageSize.value, sort.value, checkboxes, filtersStore.filters);
|
|
@@ -445,4 +536,14 @@ watch([sort], async () => {
|
|
|
445
536
|
setQuery({ sort: SortQuerySerializer.serialize(sort.value) });
|
|
446
537
|
});
|
|
447
538
|
|
|
448
|
-
</script>
|
|
539
|
+
</script>
|
|
540
|
+
|
|
541
|
+
<style lang="scss">
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
.af-button-shadow {
|
|
545
|
+
box-shadow: -0px 6px 6px rgb(0, 0, 0, 0.1);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
</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,21 +14,34 @@
|
|
|
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
|
-
v-if="action.icon"
|
|
28
|
+
v-if="action.icon && !actionLoadingStates[action.id!]"
|
|
29
29
|
:is="getIcon(action.icon)"
|
|
30
30
|
class="w-4 h-4 me-2 text-lightPrimary dark:text-darkPrimary"
|
|
31
31
|
/>
|
|
32
|
+
<div v-if="actionLoadingStates[action.id!]" class="me-2">
|
|
33
|
+
<svg
|
|
34
|
+
aria-hidden="true"
|
|
35
|
+
class="w-4 h-4 animate-spin text-gray-200 dark:text-gray-500 fill-gray-500 dark:fill-gray-300"
|
|
36
|
+
viewBox="0 0 100 101"
|
|
37
|
+
fill="none"
|
|
38
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
39
|
+
>
|
|
40
|
+
<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"/>
|
|
41
|
+
<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"/>
|
|
42
|
+
</svg>
|
|
43
|
+
<span class="sr-only">Loading...</span>
|
|
44
|
+
</div>
|
|
32
45
|
{{ action.name }}
|
|
33
46
|
</button>
|
|
34
47
|
</component>
|
|
@@ -36,21 +49,21 @@
|
|
|
36
49
|
</template>
|
|
37
50
|
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
38
51
|
: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"
|
|
52
|
+
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
53
|
>
|
|
41
54
|
<IconPlusOutline class="w-4 h-4"/>
|
|
42
55
|
{{ $t('Add new') }}
|
|
43
56
|
</RouterLink>
|
|
44
57
|
|
|
45
58
|
<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"
|
|
59
|
+
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
60
|
>
|
|
48
61
|
<IconPenSolid class="w-4 h-4" />
|
|
49
62
|
{{ $t('Edit') }}
|
|
50
63
|
</RouterLink>
|
|
51
64
|
|
|
52
65
|
<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"
|
|
66
|
+
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
67
|
>
|
|
55
68
|
<IconTrashBinSolid class="w-4 h-4" />
|
|
56
69
|
{{ $t('Delete') }}
|
|
@@ -65,8 +78,8 @@
|
|
|
65
78
|
<component
|
|
66
79
|
v-if="!loading"
|
|
67
80
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.afterBreadcrumbs || []"
|
|
68
|
-
:is="getCustomComponent(c)"
|
|
69
|
-
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
81
|
+
:is="getCustomComponent(formatComponent(c as AdminForthComponentDeclarationFull))"
|
|
82
|
+
:meta="formatComponent(c as AdminForthComponentDeclarationFull).meta"
|
|
70
83
|
:record="coreStore.record"
|
|
71
84
|
:resource="coreStore.resource"
|
|
72
85
|
:adminUser="coreStore.adminUser"
|
|
@@ -95,7 +108,7 @@
|
|
|
95
108
|
<template v-else>
|
|
96
109
|
<template v-for="group in groups" :key="group.groupName">
|
|
97
110
|
<ShowTable
|
|
98
|
-
:columns="group.columns"
|
|
111
|
+
:columns="group.columns as any"
|
|
99
112
|
:groupName="group.groupName"
|
|
100
113
|
:noTitle="group.noTitle"
|
|
101
114
|
:resource="coreStore.resource"
|
|
@@ -120,8 +133,8 @@
|
|
|
120
133
|
<component
|
|
121
134
|
v-if="!loading"
|
|
122
135
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.bottom || []"
|
|
123
|
-
:is="getCustomComponent(c)"
|
|
124
|
-
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
136
|
+
:is="getCustomComponent(formatComponent(c as AdminForthComponentDeclarationFull))"
|
|
137
|
+
:meta="formatComponent(c as AdminForthComponentDeclarationFull).meta"
|
|
125
138
|
:record="coreStore.record"
|
|
126
139
|
:resource="coreStore.resource"
|
|
127
140
|
:adminUser="coreStore.adminUser"
|
|
@@ -137,7 +150,7 @@
|
|
|
137
150
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
138
151
|
|
|
139
152
|
import { useCoreStore } from '@/stores/core';
|
|
140
|
-
import { getCustomComponent, checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
|
|
153
|
+
import { getCustomComponent, checkAcessByAllowedActions, initThreeDotsDropdown, formatComponent, executeCustomAction } from '@/utils';
|
|
141
154
|
import { IconPenSolid, IconTrashBinSolid, IconPlusOutline } from '@iconify-prerendered/vue-flowbite';
|
|
142
155
|
import { onMounted, ref, computed } from 'vue';
|
|
143
156
|
import { useRoute,useRouter } from 'vue-router';
|
|
@@ -245,55 +258,48 @@ async function deleteRecord() {
|
|
|
245
258
|
|
|
246
259
|
}
|
|
247
260
|
|
|
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);
|
|
261
|
+
async function startCustomAction(actionId: string, extra?: any) {
|
|
262
|
+
await executeCustomAction({
|
|
263
|
+
actionId,
|
|
264
|
+
resourceId: route.params.resourceId as string,
|
|
265
|
+
recordId: route.params.primaryKey as string,
|
|
266
|
+
extra,
|
|
267
|
+
setLoadingState: (loading: boolean) => {
|
|
268
|
+
actionLoadingStates.value[actionId] = loading;
|
|
269
|
+
},
|
|
270
|
+
onSuccess: async (data: any) => {
|
|
271
|
+
if (data?.redirectUrl) {
|
|
272
|
+
// Check if the URL should open in a new tab
|
|
273
|
+
if (data.redirectUrl.includes('target=_blank')) {
|
|
274
|
+
window.open(data.redirectUrl.replace('&target=_blank', '').replace('?target=_blank', ''), '_blank');
|
|
275
|
+
} else {
|
|
276
|
+
// Navigate within the app
|
|
277
|
+
if (data.redirectUrl.startsWith('http')) {
|
|
278
|
+
window.location.href = data.redirectUrl;
|
|
279
|
+
} else {
|
|
280
|
+
router.push(data.redirectUrl);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
274
284
|
}
|
|
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
285
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
286
|
+
await coreStore.fetchRecord({
|
|
287
|
+
resourceId: route.params.resourceId as string,
|
|
288
|
+
primaryKey: route.params.primaryKey as string,
|
|
289
|
+
source: 'show',
|
|
290
290
|
});
|
|
291
|
+
|
|
292
|
+
if (data.successMessage) {
|
|
293
|
+
alert({
|
|
294
|
+
message: data.successMessage,
|
|
295
|
+
variant: 'success'
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
onError: (error: string) => {
|
|
300
|
+
showErrorTost(error);
|
|
291
301
|
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (data?.error) {
|
|
295
|
-
showErrorTost(data.error);
|
|
296
|
-
}
|
|
302
|
+
});
|
|
297
303
|
}
|
|
298
304
|
|
|
299
305
|
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;
|
|
@@ -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.
|