adminforth 2.13.0-next.6 → 2.13.0-next.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +2 -0
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +15 -1
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +23 -3
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +15 -7
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/utils.d.ts +2 -0
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +51 -0
- package/dist/modules/utils.js.map +1 -1
- package/dist/spa/package-lock.json +768 -1412
- package/dist/spa/package.json +33 -32
- package/dist/spa/src/App.vue +45 -91
- package/dist/spa/src/afcl/Dialog.vue +65 -5
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/Select.vue +3 -2
- package/dist/spa/src/components/CallActionWrapper.vue +1 -1
- package/dist/spa/src/components/ResourceListTable.vue +18 -9
- package/dist/spa/src/components/ResourceListTableVirtual.vue +19 -10
- package/dist/spa/src/components/Sidebar.vue +15 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +34 -6
- package/dist/spa/src/components/ValueRenderer.vue +1 -1
- package/dist/spa/src/stores/core.ts +8 -1
- package/dist/spa/src/stores/filters.ts +5 -0
- package/dist/spa/src/types/Back.ts +6 -0
- package/dist/spa/src/types/Common.ts +1 -0
- package/dist/spa/src/types/adapters/OAuth2Adapter.ts +1 -1
- package/dist/spa/src/utils.ts +31 -11
- package/dist/spa/src/views/ListView.vue +6 -0
- package/dist/spa/src/views/LoginView.vue +8 -2
- package/dist/spa/src/views/PageNotFound.vue +2 -2
- package/dist/spa/tsconfig.json +1 -1
- package/dist/types/Back.d.ts +4 -0
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +1 -0
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/adapters/OAuth2Adapter.d.ts +2 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/spa/package.json
CHANGED
|
@@ -13,47 +13,48 @@
|
|
|
13
13
|
"i18n:extract": "echo {} > i18n-empty.json && vue-i18n-extract report --vueFiles \"./src/**/*.{js,vue,ts}\" --output ./i18n-messages.json --languageFiles \"i18n-empty.json\" --add"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@iconify-prerendered/vue-flag": "^0.28.
|
|
16
|
+
"@iconify-prerendered/vue-flag": "^0.28.1748584105",
|
|
17
17
|
"@iconify-prerendered/vue-flowbite": "^0.28.1754899090",
|
|
18
|
-
"@
|
|
19
|
-
"@
|
|
18
|
+
"@iconify-prerendered/vue-humbleicons": "^0.28.1754108846",
|
|
19
|
+
"@unhead/vue": "^1.9.12",
|
|
20
|
+
"@vueuse/core": "^10.10.0",
|
|
20
21
|
"apexcharts": "^4.7.0",
|
|
21
|
-
"dayjs": "^1.11.
|
|
22
|
-
"debounce": "^2.
|
|
23
|
-
"flowbite-datepicker": "^1.
|
|
24
|
-
"javascript-time-ago": "^2.5.
|
|
25
|
-
"pinia": "^2.
|
|
26
|
-
"sanitize-html": "^2.
|
|
27
|
-
"unhead": "^1.
|
|
22
|
+
"dayjs": "^1.11.11",
|
|
23
|
+
"debounce": "^2.1.0",
|
|
24
|
+
"flowbite-datepicker": "^1.2.6",
|
|
25
|
+
"javascript-time-ago": "^2.5.11",
|
|
26
|
+
"pinia": "^2.1.7",
|
|
27
|
+
"sanitize-html": "^2.13.0",
|
|
28
|
+
"unhead": "^1.9.12",
|
|
28
29
|
"uuid": "^10.0.0",
|
|
29
|
-
"vue": "^3.5.
|
|
30
|
+
"vue": "^3.5.12",
|
|
30
31
|
"vue-diff": "^1.2.4",
|
|
31
|
-
"vue-i18n": "^10.0.
|
|
32
|
-
"vue-router": "^4.
|
|
32
|
+
"vue-i18n": "^10.0.5",
|
|
33
|
+
"vue-router": "^4.3.0",
|
|
33
34
|
"vue-slider-component": "^4.1.0-beta.7"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@rushstack/eslint-patch": "^1.
|
|
37
|
-
"@tsconfig/node20": "^20.1.
|
|
38
|
-
"@types/node": "^20.
|
|
39
|
-
"@vitejs/plugin-vue": "^
|
|
37
|
+
"@rushstack/eslint-patch": "^1.8.0",
|
|
38
|
+
"@tsconfig/node20": "^20.1.4",
|
|
39
|
+
"@types/node": "^20.12.5",
|
|
40
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
40
41
|
"@vue/eslint-config-typescript": "^13.0.0",
|
|
41
|
-
"@vue/tsconfig": "^0.
|
|
42
|
-
"autoprefixer": "^10.4.
|
|
43
|
-
"eslint": "^8.57.
|
|
44
|
-
"eslint-plugin-vue": "^9.
|
|
45
|
-
"flag-icons": "^7.
|
|
42
|
+
"@vue/tsconfig": "^0.5.1",
|
|
43
|
+
"autoprefixer": "^10.4.19",
|
|
44
|
+
"eslint": "^8.57.0",
|
|
45
|
+
"eslint-plugin-vue": "^9.23.0",
|
|
46
|
+
"flag-icons": "^7.2.3",
|
|
46
47
|
"flowbite": "^3.1.2",
|
|
47
|
-
"i18n-iso-countries": "^7.
|
|
48
|
-
"npm-run-all2": "^6.2
|
|
49
|
-
"portfinder": "^1.0.
|
|
50
|
-
"postcss": "^8.
|
|
51
|
-
"sass": "^1.
|
|
52
|
-
"tailwindcss": "^3.4.
|
|
53
|
-
"typescript": "~5.
|
|
54
|
-
"vite": "^
|
|
48
|
+
"i18n-iso-countries": "^7.12.0",
|
|
49
|
+
"npm-run-all2": "^6.1.2",
|
|
50
|
+
"portfinder": "^1.0.32",
|
|
51
|
+
"postcss": "^8.4.38",
|
|
52
|
+
"sass": "^1.77.2",
|
|
53
|
+
"tailwindcss": "^3.4.17",
|
|
54
|
+
"typescript": "~5.4.0",
|
|
55
|
+
"vite": "^5.2.13",
|
|
55
56
|
"vue-i18n-extract": "^2.0.7",
|
|
56
|
-
"vue-tsc": "^2.
|
|
57
|
-
"vue3-json-viewer": "^2.
|
|
57
|
+
"vue-tsc": "^2.0.11",
|
|
58
|
+
"vue3-json-viewer": "^2.2.2"
|
|
58
59
|
}
|
|
59
60
|
}
|
package/dist/spa/src/App.vue
CHANGED
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
/>
|
|
25
25
|
|
|
26
26
|
<div class="flex items-center ms-3 ">
|
|
27
|
+
<Tooltip>
|
|
28
|
+
<IconWifiOff v-if="coreStore.isInternetError" class="blinking-icon w-8 h-8 text-red-500" />
|
|
29
|
+
<template #tooltip>
|
|
30
|
+
{{$t('Internet connection lost')}}
|
|
31
|
+
</template>
|
|
32
|
+
</Tooltip>
|
|
27
33
|
<span
|
|
28
34
|
v-if="!coreStore.config?.singleTheme"
|
|
29
35
|
@click="toggleTheme" class="cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-black dark:text-darkSidebarTextHover dark:hover:text-darkSidebarTextActive" role="menuitem">
|
|
@@ -35,12 +41,17 @@
|
|
|
35
41
|
ref="dropdownUserButton"
|
|
36
42
|
type="button" class="flex text-sm bg- rounded-full focus:ring-4 focus:ring-lightSidebarDevider dark:focus:ring-darkSidebarDevider dark:bg-" aria-expanded="false" data-dropdown-toggle="dropdown-user">
|
|
37
43
|
<span class="sr-only">{{ $t('Open user menu') }}</span>
|
|
38
|
-
|
|
44
|
+
<img
|
|
45
|
+
v-if="coreStore.userAvatarUrl"
|
|
46
|
+
class="w-8 h-8 rounded-full object-cover"
|
|
47
|
+
:src="coreStore.userAvatarUrl"
|
|
48
|
+
alt="user photo"
|
|
49
|
+
/>
|
|
50
|
+
<svg v-else class="w-8 h-8 text-lightNavbarIcons dark:text-darkNavbarIcons" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
|
|
39
51
|
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
|
|
40
52
|
</svg>
|
|
41
53
|
</button>
|
|
42
54
|
</div>
|
|
43
|
-
|
|
44
55
|
<div class="z-50 hidden my-4 text-base list-none bg-lightUserMenuBackground divide-y divide-lightUserMenuBorder text-lightUserMenuText rounded shadow dark:shadow-black dark:bg-darkUserMenuBackground dark:divide-darkUserMenuBorder text-darkUserMenuText dark:shadow-black" id="dropdown-user">
|
|
45
56
|
<div class="px-4 py-3" role="none">
|
|
46
57
|
<p class="text-sm text-gray-900 dark:text-darkNavbarText" role="none" v-if="coreStore.userFullname">
|
|
@@ -73,7 +84,7 @@
|
|
|
73
84
|
</nav>
|
|
74
85
|
|
|
75
86
|
<Sidebar
|
|
76
|
-
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout && !headerOnlyLayout"
|
|
87
|
+
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout && !headerOnlyLayout && coreStore.menu.length > 0"
|
|
77
88
|
:sideBarOpen="sideBarOpen"
|
|
78
89
|
:forceIconOnly="route.meta?.sidebarAndHeader === 'preferIconOnly'"
|
|
79
90
|
@hideSidebar="hideSidebar"
|
|
@@ -129,6 +140,19 @@
|
|
|
129
140
|
|
|
130
141
|
<style lang="scss" scoped>
|
|
131
142
|
|
|
143
|
+
@keyframes blink {
|
|
144
|
+
0%, 100% {
|
|
145
|
+
opacity: 1;
|
|
146
|
+
}
|
|
147
|
+
50% {
|
|
148
|
+
opacity: 0.2;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.blinking-icon {
|
|
153
|
+
animation: blink 2s ease-in-out infinite;
|
|
154
|
+
}
|
|
155
|
+
|
|
132
156
|
.fade-leave-active {
|
|
133
157
|
@apply transition-opacity duration-500;
|
|
134
158
|
}
|
|
@@ -163,6 +187,7 @@ import './index.scss'
|
|
|
163
187
|
import { useCoreStore } from '@/stores/core';
|
|
164
188
|
import { useUserStore } from '@/stores/user';
|
|
165
189
|
import { IconMoonSolid, IconSunSolid } from '@iconify-prerendered/vue-flowbite';
|
|
190
|
+
import { IconWifiOff } from '@iconify-prerendered/vue-humbleicons';
|
|
166
191
|
import AcceptModal from './components/AcceptModal.vue';
|
|
167
192
|
import Sidebar from './components/Sidebar.vue';
|
|
168
193
|
import { useRoute, useRouter } from 'vue-router';
|
|
@@ -173,6 +198,7 @@ import {useToastStore} from '@/stores/toast';
|
|
|
173
198
|
import { initFrontedAPI } from '@/adminforth';
|
|
174
199
|
import adminforth from '@/adminforth';
|
|
175
200
|
import UserMenuSettingsButton from './components/UserMenuSettingsButton.vue';
|
|
201
|
+
import { Tooltip } from '@/afcl'
|
|
176
202
|
|
|
177
203
|
const coreStore = useCoreStore();
|
|
178
204
|
const toastStore = useToastStore();
|
|
@@ -182,8 +208,6 @@ initFrontedAPI()
|
|
|
182
208
|
|
|
183
209
|
createHead()
|
|
184
210
|
const sideBarOpen = ref(false);
|
|
185
|
-
const defaultLayout = ref(true);
|
|
186
|
-
const headerOnlyLayout = ref(false);
|
|
187
211
|
const route = useRoute();
|
|
188
212
|
const router = useRouter();
|
|
189
213
|
const publicConfigLoaded = ref(false);
|
|
@@ -197,82 +221,22 @@ const isSidebarIconOnly = ref(localStorage.getItem('afIconOnlySidebar') === 'tru
|
|
|
197
221
|
|
|
198
222
|
const loggedIn = computed(() => !!coreStore?.adminUser);
|
|
199
223
|
|
|
224
|
+
const defaultLayout = computed(() => {
|
|
225
|
+
return route.meta?.sidebarAndHeader !== 'none';
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const headerOnlyLayout = computed(() => {
|
|
229
|
+
return route.meta?.sidebarAndHeader === 'headerOnly';
|
|
230
|
+
});
|
|
231
|
+
|
|
200
232
|
const expandedWidth = computed(() => coreStore.config?.iconOnlySidebar?.expandedSidebarWidth || '16.5rem');
|
|
201
233
|
|
|
202
234
|
const theme = ref('light');
|
|
203
235
|
|
|
204
236
|
const userMenuComponents = computed(() => {
|
|
205
|
-
console.log('🪲🆕 userMenuComponents recomputed', JSON.parse(JSON.stringify(coreStore?.config?.globalInjections?.userMenu)));
|
|
206
237
|
return coreStore?.config?.globalInjections?.userMenu || [];
|
|
207
238
|
})
|
|
208
239
|
|
|
209
|
-
watch(
|
|
210
|
-
() => coreStore.config?.globalInjections?.userMenu,
|
|
211
|
-
(newVal, oldVal) => {
|
|
212
|
-
// Only log when it becomes undefined (you can relax this if needed)
|
|
213
|
-
if (newVal === undefined) {
|
|
214
|
-
const err = new Error('🔍 userMenu changed to undefined');
|
|
215
|
-
console.groupCollapsed(
|
|
216
|
-
'%c[TRACE] userMenu changed to undefined',
|
|
217
|
-
'color: red; font-weight: bold;'
|
|
218
|
-
);
|
|
219
|
-
console.log('old value:', oldVal);
|
|
220
|
-
console.log('new value:', newVal);
|
|
221
|
-
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
|
|
222
|
-
console.log('Stack trace:');
|
|
223
|
-
console.log(err.stack);
|
|
224
|
-
console.groupEnd();
|
|
225
|
-
} else {
|
|
226
|
-
// Optional: log ALL changes for debugging
|
|
227
|
-
console.groupCollapsed(
|
|
228
|
-
'%c[DEBUG] userMenu changed',
|
|
229
|
-
'color: orange; font-weight: bold;'
|
|
230
|
-
);
|
|
231
|
-
console.log('old value:', oldVal);
|
|
232
|
-
console.log('new value:', newVal);
|
|
233
|
-
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
|
|
234
|
-
console.groupEnd();
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
deep: false,
|
|
239
|
-
immediate: false,
|
|
240
|
-
}
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
watch(() => coreStore.config?.globalInjections, (v) => {
|
|
244
|
-
console.log("🔧 globalInjections replaced:", v);
|
|
245
|
-
}, { deep: false });
|
|
246
|
-
|
|
247
|
-
watch(
|
|
248
|
-
() => coreStore.config?.globalInjections?.userMenu,
|
|
249
|
-
(newVal, oldVal) => {
|
|
250
|
-
if (newVal === undefined) {
|
|
251
|
-
const err = new Error('🔍 userMenu changed to undefined');
|
|
252
|
-
console.groupCollapsed(
|
|
253
|
-
'%c[TRACE] userMenu changed to undefined',
|
|
254
|
-
'color: red; font-weight: bold;'
|
|
255
|
-
);
|
|
256
|
-
console.log('old value:', oldVal);
|
|
257
|
-
console.log('new value:', newVal);
|
|
258
|
-
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
|
|
259
|
-
console.log('Stack trace:');
|
|
260
|
-
console.log(err.stack);
|
|
261
|
-
console.groupEnd();
|
|
262
|
-
} else {
|
|
263
|
-
console.groupCollapsed(
|
|
264
|
-
'%c[DEBUG] userMenu changed',
|
|
265
|
-
'color: orange; font-weight: bold;'
|
|
266
|
-
);
|
|
267
|
-
console.log('old value:', oldVal);
|
|
268
|
-
console.log('new value:', newVal);
|
|
269
|
-
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
|
|
270
|
-
console.groupEnd();
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
{ deep: false, immediate: false }
|
|
274
|
-
);
|
|
275
|
-
|
|
276
240
|
function hideSidebar(): void {
|
|
277
241
|
sideBarOpen.value = false;
|
|
278
242
|
}
|
|
@@ -308,19 +272,6 @@ async function loadMenu() {
|
|
|
308
272
|
loginRedirectCheckIsReady.value = true;
|
|
309
273
|
}
|
|
310
274
|
|
|
311
|
-
function handleCustomLayout() {
|
|
312
|
-
if (route.meta?.sidebarAndHeader === 'none') {
|
|
313
|
-
defaultLayout.value = false;
|
|
314
|
-
} else if (route.meta?.sidebarAndHeader === 'preferIconOnly') {
|
|
315
|
-
defaultLayout.value = true;
|
|
316
|
-
isSidebarIconOnly.value = true;
|
|
317
|
-
} else if (route.meta?.sidebarAndHeader === 'headerOnly') {
|
|
318
|
-
headerOnlyLayout.value = true;
|
|
319
|
-
} else {
|
|
320
|
-
defaultLayout.value = true;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
275
|
function humanizeSnake(str: string): string {
|
|
325
276
|
if (!str) {
|
|
326
277
|
return '';
|
|
@@ -345,10 +296,11 @@ watch(title, (title) => {
|
|
|
345
296
|
document.title = title;
|
|
346
297
|
})
|
|
347
298
|
|
|
348
|
-
watch(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
299
|
+
watch(route, () => {
|
|
300
|
+
// Handle preferIconOnly layout
|
|
301
|
+
if (route.meta?.sidebarAndHeader === 'preferIconOnly') {
|
|
302
|
+
isSidebarIconOnly.value = true;
|
|
303
|
+
}
|
|
352
304
|
});
|
|
353
305
|
|
|
354
306
|
|
|
@@ -376,11 +328,13 @@ onMounted(async () => {
|
|
|
376
328
|
loadPublicConfig(); // and this
|
|
377
329
|
// before init flowbite we have to wait router initialized because it affects dom(our v-ifs) and fetch menu
|
|
378
330
|
await initRouter();
|
|
379
|
-
handleCustomLayout();
|
|
380
331
|
|
|
381
332
|
adminforth.menu.refreshMenuBadges = async () => {
|
|
382
333
|
await coreStore.fetchMenuBadges();
|
|
383
334
|
}
|
|
335
|
+
|
|
336
|
+
window.addEventListener('online', () => coreStore.isInternetError = false);
|
|
337
|
+
window.addEventListener('offline', () => coreStore.isInternetError = true);
|
|
384
338
|
})
|
|
385
339
|
|
|
386
340
|
onBeforeMount(()=>{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
v-if="headerCloseButton"
|
|
23
23
|
type="button"
|
|
24
24
|
class="text-lightDialogCloseButton bg-transparent hover:bg-lightDialogCloseButtonHoverBackground hover:text-lightDialogCloseButtonHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkDialogCloseButton dark:hover:bg-darkDialogCloseButtonHoverBackground dark:hover:text-darkDialogCloseButtonHover"
|
|
25
|
-
@click="
|
|
25
|
+
@click="tryToHideModal"
|
|
26
26
|
>
|
|
27
27
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
28
28
|
<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"/>
|
|
@@ -51,13 +51,41 @@
|
|
|
51
51
|
</div>
|
|
52
52
|
</div>
|
|
53
53
|
</div>
|
|
54
|
+
<div>
|
|
55
|
+
<!-- Confirmation Modal -->
|
|
56
|
+
<div
|
|
57
|
+
v-if="showConfirmationOnClose"
|
|
58
|
+
class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-60"
|
|
59
|
+
>
|
|
60
|
+
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg max-w-sm w-full">
|
|
61
|
+
<h2 class="text-lg font-semibold mb-4 text-lightDialogHeaderText dark:text-darkDialogHeaderText">Confirm Close</h2>
|
|
62
|
+
<p class="mb-6 text-lightDialogBodyText dark:text-darkDialogBodyText">{{ props.closeConfirmationText }}</p>
|
|
63
|
+
<div class="flex justify-end">
|
|
64
|
+
<Button
|
|
65
|
+
class="me-3"
|
|
66
|
+
@click="showConfirmationOnClose = false"
|
|
67
|
+
>
|
|
68
|
+
Cancel
|
|
69
|
+
</Button>
|
|
70
|
+
<Button
|
|
71
|
+
@click="
|
|
72
|
+
showConfirmationOnClose = false;
|
|
73
|
+
modal?.hide();
|
|
74
|
+
"
|
|
75
|
+
>
|
|
76
|
+
Confirm
|
|
77
|
+
</Button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
54
82
|
</div>
|
|
55
83
|
</Teleport>
|
|
56
84
|
</template>
|
|
57
85
|
|
|
58
86
|
<script setup lang="ts">
|
|
59
87
|
import Button from "./Button.vue";
|
|
60
|
-
import { ref, onMounted, nextTick, onUnmounted, type Ref } from 'vue';
|
|
88
|
+
import { ref, onMounted, nextTick, onUnmounted, computed, type Ref } from 'vue';
|
|
61
89
|
import { Modal } from 'flowbite';
|
|
62
90
|
|
|
63
91
|
const modalEl = ref(null);
|
|
@@ -77,20 +105,42 @@ interface DialogProps {
|
|
|
77
105
|
beforeCloseFunction?: (() => void | Promise<void>) | null
|
|
78
106
|
beforeOpenFunction?: (() => void | Promise<void>) | null
|
|
79
107
|
closable?: boolean
|
|
108
|
+
askForCloseConfirmation?: boolean
|
|
109
|
+
closeConfirmationText?: string
|
|
80
110
|
}
|
|
81
111
|
|
|
82
112
|
const props = withDefaults(defineProps<DialogProps>(), {
|
|
83
113
|
header: '',
|
|
84
114
|
headerCloseButton: true,
|
|
85
|
-
buttons: () => [
|
|
86
|
-
{ label: 'Close', onclick: (dialog: any) => dialog.hide(), type: '' },
|
|
87
|
-
],
|
|
115
|
+
buttons: () => [],
|
|
88
116
|
clickToCloseOutside: true,
|
|
89
117
|
beforeCloseFunction: null,
|
|
90
118
|
beforeOpenFunction: null,
|
|
91
119
|
closable: true,
|
|
120
|
+
askForCloseConfirmation: false,
|
|
121
|
+
closeConfirmationText: 'Are you sure you want to close this dialog?',
|
|
92
122
|
})
|
|
93
123
|
|
|
124
|
+
const buttons = computed<DialogButton[]>(() => {
|
|
125
|
+
if (props.buttons && props.buttons.length > 0) {
|
|
126
|
+
return props.buttons;
|
|
127
|
+
}
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
130
|
+
label: 'Close',
|
|
131
|
+
onclick: (dialog: any) => {
|
|
132
|
+
if (!props.askForCloseConfirmation) {
|
|
133
|
+
dialog.hide();
|
|
134
|
+
} else {
|
|
135
|
+
showConfirmationOnClose.value = true;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
options: {}
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const showConfirmationOnClose = ref(false);
|
|
94
144
|
onMounted(async () => {
|
|
95
145
|
//await one tick when all is mounted
|
|
96
146
|
await nextTick();
|
|
@@ -129,6 +179,16 @@ function close() {
|
|
|
129
179
|
defineExpose({
|
|
130
180
|
open: open,
|
|
131
181
|
close: close,
|
|
182
|
+
tryToHideModal: tryToHideModal,
|
|
132
183
|
})
|
|
133
184
|
|
|
185
|
+
function tryToHideModal() {
|
|
186
|
+
if (!props.askForCloseConfirmation ) {
|
|
187
|
+
modal.value?.hide();
|
|
188
|
+
} else {
|
|
189
|
+
showConfirmationOnClose.value = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
134
194
|
</script>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
@input="$emit('update:modelValue', type === 'number' ? Number(($event.target as HTMLInputElement)?.value) : ($event.target as HTMLInputElement)?.value)"
|
|
16
16
|
:value="modelValue"
|
|
17
17
|
aria-describedby="helper-text-explanation"
|
|
18
|
-
class="afcl-input inline-flex bg-lightInputBackground border border-lightInputBorder rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
|
|
18
|
+
class="afcl-input inline-flex bg-lightInputBackground text-lightInputText dark:text-darkInputText border border-lightInputBorder rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
|
|
19
19
|
blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkInputBackground dark:border-darkInputBorder placeholder-lightInputPlaceholderText dark:placeholder-darkInputPlaceholderText dark:text-darkInputText translate-y-0"
|
|
20
20
|
:class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth, 'text-base': isIos, 'text-sm': !isIos }"
|
|
21
21
|
:disabled="readonly"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
class="block w-full pl-3 pr-10 py-2.5 border border-lightDropownButtonsBorder rounded-md leading-5 bg-lightDropdownButtonsBackground
|
|
14
14
|
placeholder-lightDropdownButtonsPlaceholderText text-lightDropdownButtonsText sm:text-sm transition duration-150 ease-in-out dark:bg-darkDropdownButtonsBackground dark:border-darkDropdownButtonsBorder dark:placeholder-darkDropdownButtonsPlaceholderText
|
|
15
15
|
dark:text-darkDropdownButtonsText focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
|
|
16
|
+
:class="{'cursor-pointer': searchDisabled}"
|
|
16
17
|
autocomplete="off" data-custom="no-autofill"
|
|
17
18
|
:placeholder="
|
|
18
19
|
selectedItems.length && !multiple ? '' : (showDropdown ? $t('Search') : placeholder || $t('Select...'))
|
|
@@ -265,11 +266,11 @@ onMounted(() => {
|
|
|
265
266
|
|
|
266
267
|
watch(() => props.modelValue, (value) => {
|
|
267
268
|
updateFromProps();
|
|
268
|
-
});
|
|
269
|
+
}, {deep: true});
|
|
269
270
|
|
|
270
271
|
watch(() => props.options, () => {
|
|
271
272
|
updateFromProps();
|
|
272
|
-
});
|
|
273
|
+
}, { deep: true });
|
|
273
274
|
|
|
274
275
|
addClickListener();
|
|
275
276
|
|
|
@@ -83,13 +83,20 @@
|
|
|
83
83
|
</td>
|
|
84
84
|
</tr>
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
<component
|
|
87
|
+
v-else
|
|
88
|
+
v-for="(row, rowI) in rows"
|
|
89
|
+
:is="tableRowReplaceInjection ? getCustomComponent(tableRowReplaceInjection) : 'tr'"
|
|
90
|
+
:key="`row_${row._primaryKeyValue}`"
|
|
91
|
+
:record="row"
|
|
92
|
+
:resource="resource"
|
|
93
|
+
:adminUser="coreStore.adminUser"
|
|
94
|
+
:meta="tableRowReplaceInjection ? tableRowReplaceInjection.meta : undefined"
|
|
95
|
+
@click="onClick($event, row)"
|
|
96
|
+
ref="rowRefs"
|
|
97
|
+
class="list-table-body-row bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
|
|
98
|
+
:class="{'border-b': rowI !== rows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
|
|
99
|
+
>
|
|
93
100
|
<td class="w-4 p-4 cursor-default sticky-column bg-lightListTable dark:bg-darkListTable" @click="(e)=>e.stopPropagation()">
|
|
94
101
|
<Checkbox
|
|
95
102
|
:model-value="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
@@ -210,7 +217,7 @@
|
|
|
210
217
|
</div>
|
|
211
218
|
|
|
212
219
|
</td>
|
|
213
|
-
|
|
220
|
+
</component>
|
|
214
221
|
</tbody>
|
|
215
222
|
</table>
|
|
216
223
|
</div>
|
|
@@ -328,9 +335,10 @@ import {
|
|
|
328
335
|
} from '@iconify-prerendered/vue-flowbite';
|
|
329
336
|
import router from '@/router';
|
|
330
337
|
import { Tooltip } from '@/afcl';
|
|
331
|
-
import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon, AdminForthResourceColumnCommon } from '@/types/Common';
|
|
338
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon, AdminForthResourceColumnCommon, AdminForthComponentDeclaration } from '@/types/Common';
|
|
332
339
|
import adminforth from '@/adminforth';
|
|
333
340
|
import Checkbox from '@/afcl/Checkbox.vue';
|
|
341
|
+
import CallActionWrapper from '@/components/CallActionWrapper.vue'
|
|
334
342
|
|
|
335
343
|
const coreStore = useCoreStore();
|
|
336
344
|
const { t } = useI18n();
|
|
@@ -345,6 +353,7 @@ const props = defineProps<{
|
|
|
345
353
|
noRoundings?: boolean,
|
|
346
354
|
customActionsInjection?: any[],
|
|
347
355
|
tableBodyStartInjection?: any[],
|
|
356
|
+
tableRowReplaceInjection?: AdminForthComponentDeclaration,
|
|
348
357
|
}>();
|
|
349
358
|
|
|
350
359
|
// emits, update page
|
|
@@ -93,14 +93,21 @@
|
|
|
93
93
|
</tr>
|
|
94
94
|
|
|
95
95
|
<!-- Visible rows -->
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
<component
|
|
97
|
+
v-else
|
|
98
|
+
v-for="(row, rowI) in visibleRows"
|
|
99
|
+
:is="tableRowReplaceInjection ? getCustomComponent(tableRowReplaceInjection) : 'tr'"
|
|
100
|
+
:key="`row_${row._primaryKeyValue}`"
|
|
101
|
+
:record="row"
|
|
102
|
+
:resource="resource"
|
|
103
|
+
:adminUser="coreStore.adminUser"
|
|
104
|
+
:meta="tableRowReplaceInjection ? tableRowReplaceInjection.meta : undefined"
|
|
105
|
+
@click="onClick($event, row)"
|
|
106
|
+
ref="rowRefs"
|
|
107
|
+
class="list-table-body-row bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
|
|
108
|
+
:class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
|
|
109
|
+
@mounted="(el: any) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
|
|
110
|
+
>
|
|
104
111
|
<td class="w-4 p-4 cursor-default sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading" @click="(e)=>e.stopPropagation()">
|
|
105
112
|
<Checkbox
|
|
106
113
|
:model-value="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
@@ -224,7 +231,7 @@
|
|
|
224
231
|
</template>
|
|
225
232
|
</div>
|
|
226
233
|
</td>
|
|
227
|
-
</
|
|
234
|
+
</component>
|
|
228
235
|
|
|
229
236
|
<!-- Bottom spacer -->
|
|
230
237
|
<tr v-if="totalHeight > 0">
|
|
@@ -350,9 +357,10 @@ import {
|
|
|
350
357
|
} from '@iconify-prerendered/vue-flowbite';
|
|
351
358
|
import router from '@/router';
|
|
352
359
|
import { Tooltip } from '@/afcl';
|
|
353
|
-
import type { AdminForthResourceCommon, AdminForthResourceColumnCommon } from '@/types/Common';
|
|
360
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnCommon, AdminForthComponentDeclaration } from '@/types/Common';
|
|
354
361
|
import adminforth from '@/adminforth';
|
|
355
362
|
import Checkbox from '@/afcl/Checkbox.vue';
|
|
363
|
+
import CallActionWrapper from '@/components/CallActionWrapper.vue'
|
|
356
364
|
|
|
357
365
|
const coreStore = useCoreStore();
|
|
358
366
|
const { t } = useI18n();
|
|
@@ -370,6 +378,7 @@ const props = defineProps<{
|
|
|
370
378
|
containerHeight?: number,
|
|
371
379
|
itemHeight?: number,
|
|
372
380
|
bufferSize?: number,
|
|
381
|
+
tableRowReplaceInjection?: AdminForthComponentDeclaration
|
|
373
382
|
}>();
|
|
374
383
|
|
|
375
384
|
// emits, update page
|
|
@@ -14,9 +14,21 @@
|
|
|
14
14
|
aria-label="Sidebar"
|
|
15
15
|
>
|
|
16
16
|
<div class="h-full px-3 pb-20 md:pb-4 bg-lightSidebar dark:bg-darkSidebar border-r border-lightSidebarBorder dark:border-darkSidebarBorder pt-4" :class="{'sidebar-scroll':!isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering)}">
|
|
17
|
-
<div
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
<div
|
|
18
|
+
class="af-logo-title-wrapper flex relative transition-all duration-300 ease-in-out h-8 items-center"
|
|
19
|
+
:class="{
|
|
20
|
+
'mb-4': isSidebarIconOnly && !isSidebarHovering, 'mx-4 mb-4': !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering),
|
|
21
|
+
'justify-center': !(coreStore.config?.showBrandLogoInSidebar !== false && (!iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering)))
|
|
22
|
+
}"
|
|
23
|
+
>
|
|
24
|
+
<img
|
|
25
|
+
:src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')"
|
|
26
|
+
:alt="`${ coreStore.config?.brandName } Logo`"
|
|
27
|
+
class="af-logo h-8 me-3"
|
|
28
|
+
:class="{
|
|
29
|
+
'hidden': !(coreStore.config?.showBrandLogoInSidebar !== false && (!iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering))) }"
|
|
30
|
+
/>
|
|
31
|
+
<img :src="loadFile(coreStore.config?.iconOnlySidebar?.logo || '')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-sidebar-icon-only-logo h-8" :class="{ 'hidden': !(coreStore.config?.showBrandLogoInSidebar !== false && coreStore.config?.iconOnlySidebar?.logo && iconOnlySidebarEnabled && isSidebarIconOnly && !isSidebarHovering) }" />
|
|
20
32
|
<span
|
|
21
33
|
v-if="coreStore.config?.showBrandNameInSidebar && (!iconOnlySidebarEnabled || !isSidebarIconOnly || (isSidebarIconOnly && isSidebarHovering))"
|
|
22
34
|
class="af-title self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-darkSidebarText text-lightSidebarText"
|