adminforth 2.4.0-next.33 → 2.4.0-next.331
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/callTsProxy.js +14 -4
- package/commands/createApp/templates/api.ts.hbs +10 -0
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +12 -1
- package/commands/createApp/templates/package.json.hbs +1 -1
- package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
- package/commands/createApp/templates/schema.prisma.hbs +0 -1
- package/commands/createApp/utils.js +10 -0
- package/commands/createCustomComponent/configLoader.js +17 -4
- package/commands/createCustomComponent/main.js +13 -7
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/commands/createPlugin/templates/package.json.hbs +1 -1
- package/commands/generateModels.js +30 -22
- package/dist/auth.d.ts +9 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +21 -2
- package/dist/auth.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +70 -18
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +50 -15
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +43 -14
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -21
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +62 -6
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts +6 -0
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +209 -25
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +199 -31
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +499 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +555 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +7 -15
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +45 -68
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts +5 -0
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +40 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/package-lock.json +1208 -708
- package/dist/spa/package.json +34 -34
- package/dist/spa/src/App.vue +132 -174
- package/dist/spa/src/adminforth.ts +41 -17
- package/dist/spa/src/afcl/AreaChart.vue +0 -1
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +3 -3
- package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
- package/dist/spa/src/afcl/Card.vue +25 -0
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +4 -1
- package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
- package/dist/spa/src/afcl/Dialog.vue +47 -27
- package/dist/spa/src/afcl/Dropzone.vue +145 -48
- package/dist/spa/src/afcl/Input.vue +14 -6
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/LinkButton.vue +3 -3
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +82 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +313 -75
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +28 -18
- package/dist/spa/src/afcl/VerticalTabs.vue +21 -7
- package/dist/spa/src/afcl/index.ts +6 -3
- package/dist/spa/src/components/AcceptModal.vue +48 -14
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/CallActionWrapper.vue +15 -0
- package/dist/spa/src/components/ColumnValueInput.vue +38 -18
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +37 -21
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +195 -132
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +95 -23
- package/dist/spa/src/components/ResourceForm.vue +99 -51
- package/dist/spa/src/components/ResourceListTable.vue +121 -95
- package/dist/spa/src/components/ResourceListTableVirtual.vue +119 -88
- package/dist/spa/src/components/ShowTable.vue +21 -15
- package/dist/spa/src/components/Sidebar.vue +472 -0
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +3 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
- package/dist/spa/src/components/Toast.vue +40 -29
- package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
- package/dist/spa/src/components/ValueRenderer.vue +44 -17
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +5 -3
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/renderers/CompactField.vue +1 -1
- package/dist/spa/src/renderers/CompactUUID.vue +1 -1
- package/dist/spa/src/router/index.ts +8 -0
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +13 -1
- package/dist/spa/src/stores/core.ts +15 -1
- package/dist/spa/src/stores/filters.ts +33 -2
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/stores/toast.ts +22 -3
- package/dist/spa/src/types/Back.ts +168 -23
- package/dist/spa/src/types/Common.ts +109 -32
- package/dist/spa/src/types/FrontendAPI.ts +32 -23
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
- package/dist/spa/src/types/adapters/StorageAdapter.ts +4 -2
- package/dist/spa/src/types/adapters/index.ts +3 -0
- package/dist/spa/src/utils.ts +291 -11
- package/dist/spa/src/views/CreateView.vue +88 -22
- package/dist/spa/src/views/EditView.vue +55 -22
- package/dist/spa/src/views/ListView.vue +144 -87
- package/dist/spa/src/views/LoginView.vue +26 -35
- package/dist/spa/src/views/ResourceParent.vue +2 -2
- package/dist/spa/src/views/SettingsView.vue +121 -0
- package/dist/spa/src/views/ShowView.vue +83 -53
- package/dist/spa/src/websocket.ts +6 -1
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/spa/vite.config.ts +45 -2
- package/dist/types/Back.d.ts +151 -14
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +15 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +123 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +32 -18
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
- package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CaptchaAdapter.js +5 -0
- package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +2 -3
- package/dist/types/adapters/EmailAdapter.d.ts.map +1 -1
- package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.js +2 -0
- package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.js +2 -0
- package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
- package/dist/types/adapters/StorageAdapter.d.ts +2 -0
- package/dist/types/adapters/StorageAdapter.d.ts.map +1 -1
- package/dist/types/adapters/index.d.ts +3 -0
- package/dist/types/adapters/index.d.ts.map +1 -1
- package/package.json +4 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template >
|
|
2
|
-
<template v-if="threeDotsDropdownItems?.length || customActions?.length">
|
|
2
|
+
<template v-if="threeDotsDropdownItems?.length || customActions?.length || (bulkActions?.some((action: AdminForthBulkActionCommon) => action.showInThreeDotsDropdown))">
|
|
3
3
|
<button
|
|
4
4
|
data-dropdown-toggle="listThreeDotsDropdown"
|
|
5
|
-
class="flex items-center py-2 px-2 text-sm font-medium text-
|
|
5
|
+
class="flex items-center py-2 px-2 text-sm font-medium text-lightThreeDotsMenuIconDots focus:outline-none bg-lightThreeDotsMenuIconBackground rounded border border-lightThreeDotsMenuIconBackgroundBorder hover:bg-lightThreeDotsMenuIconBackgroundHover hover:text-lightThreeDotsMenuIconDotsHover focus:z-10 focus:ring-4 focus:ring-lightThreeDotsMenuIconFocus dark:focus:ring-darkThreeDotsMenuIconFocus dark:bg-darkThreeDotsMenuIconBackground dark:text-darkThreeDotsMenuIconDots dark:border-darkThreeDotsMenuIconBackgroundBorder dark:hover:text-darkThreeDotsMenuIconDotsHover dark:hover:bg-darkThreeDotsMenuIconBackgroundHover rounded-default"
|
|
6
6
|
>
|
|
7
7
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 4 15">
|
|
8
8
|
<path d="M3.5 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 6.041a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 5.959a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"/>
|
|
@@ -12,26 +12,60 @@
|
|
|
12
12
|
<!-- Dropdown menu -->
|
|
13
13
|
<div
|
|
14
14
|
id="listThreeDotsDropdown"
|
|
15
|
-
class="z-
|
|
16
|
-
<ul class="py-2 text-sm text-
|
|
17
|
-
<li v-for="item in threeDotsDropdownItems" :key="`dropdown-item-${
|
|
18
|
-
<a
|
|
19
|
-
|
|
15
|
+
class="z-30 hidden bg-lightThreeDotsMenuBodyBackground divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-darkThreeDotsMenuBodyBackground dark:divide-gray-600">
|
|
16
|
+
<ul class="py-2 text-sm text-lightThreeDotsMenuBodyText dark:text-darkThreeDotsMenuBodyText" aria-labelledby="dropdownMenuIconButton">
|
|
17
|
+
<li v-for="(item, i) in threeDotsDropdownItems" :key="`dropdown-item-${i}`">
|
|
18
|
+
<a href="#"
|
|
19
|
+
class="block px-4 py-2 hover:bg-lightThreeDotsMenuBodyBackgroundHover hover:text-lightThreeDotsMenuBodyTextHover dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover"
|
|
20
|
+
:class="{
|
|
21
|
+
'pointer-events-none': checkboxes && checkboxes.length === 0 && item.meta?.disabledWhenNoCheckboxes,
|
|
22
|
+
'opacity-50': checkboxes && checkboxes.length === 0 && item.meta?.disabledWhenNoCheckboxes,
|
|
23
|
+
'cursor-not-allowed': checkboxes && checkboxes.length === 0 && item.meta?.disabledWhenNoCheckboxes,
|
|
24
|
+
}"
|
|
25
|
+
@click="injectedComponentClick(i)">
|
|
26
|
+
<component :ref="(el: any) => setComponentRef(el, i)" :is="getCustomComponent(item)"
|
|
20
27
|
:meta="item.meta"
|
|
21
28
|
:resource="coreStore.resource"
|
|
22
29
|
:adminUser="coreStore.adminUser"
|
|
30
|
+
:checkboxes="checkboxes"
|
|
31
|
+
:updateList="props.updateList"
|
|
32
|
+
:clearCheckboxes="clearCheckboxes"
|
|
23
33
|
/>
|
|
24
34
|
</a>
|
|
25
35
|
</li>
|
|
26
36
|
<li v-for="action in customActions" :key="action.id">
|
|
27
|
-
<
|
|
37
|
+
<component
|
|
38
|
+
:is="(action.customComponent && getCustomComponent(action.customComponent)) || CallActionWrapper"
|
|
39
|
+
:meta="action.customComponent?.meta"
|
|
40
|
+
@callAction="(payload? : Object) => handleActionClick(action, payload)"
|
|
41
|
+
>
|
|
42
|
+
<a href="#" @click.prevent class="block px-4 py-2 hover:text-lightThreeDotsMenuBodyTextHover hover:bg-lightThreeDotsMenuBodyBackgroundHover dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover">
|
|
43
|
+
<div class="flex items-center gap-2">
|
|
44
|
+
<component
|
|
45
|
+
v-if="action.icon"
|
|
46
|
+
:is="getIcon(action.icon)"
|
|
47
|
+
class="w-4 h-4 text-lightPrimary dark:text-darkPrimary"
|
|
48
|
+
/>
|
|
49
|
+
{{ action.name }}
|
|
50
|
+
</div>
|
|
51
|
+
</a>
|
|
52
|
+
</component>
|
|
53
|
+
</li>
|
|
54
|
+
<li v-for="action in (bulkActions ?? []).filter(a => a.showInThreeDotsDropdown)" :key="action.id">
|
|
55
|
+
<a href="#" @click.prevent="startBulkAction(action.id)"
|
|
56
|
+
class="block px-4 py-2 hover:text-lightThreeDotsMenuBodyTextHover hover:bg-lightThreeDotsMenuBodyBackgroundHover dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover"
|
|
57
|
+
:class="{
|
|
58
|
+
'pointer-events-none': checkboxes && checkboxes.length === 0,
|
|
59
|
+
'opacity-50': checkboxes && checkboxes.length === 0,
|
|
60
|
+
'cursor-not-allowed': checkboxes && checkboxes.length === 0
|
|
61
|
+
}">
|
|
28
62
|
<div class="flex items-center gap-2">
|
|
29
63
|
<component
|
|
30
64
|
v-if="action.icon"
|
|
31
65
|
:is="getIcon(action.icon)"
|
|
32
66
|
class="w-4 h-4 text-lightPrimary dark:text-darkPrimary"
|
|
33
67
|
/>
|
|
34
|
-
{{ action.
|
|
68
|
+
{{ action.label }}
|
|
35
69
|
</div>
|
|
36
70
|
</a>
|
|
37
71
|
</li>
|
|
@@ -47,17 +81,39 @@ import { useCoreStore } from '@/stores/core';
|
|
|
47
81
|
import adminforth from '@/adminforth';
|
|
48
82
|
import { callAdminForthApi } from '@/utils';
|
|
49
83
|
import { useRoute, useRouter } from 'vue-router';
|
|
84
|
+
import CallActionWrapper from '@/components/CallActionWrapper.vue'
|
|
85
|
+
import { ref, type ComponentPublicInstance } from 'vue';
|
|
86
|
+
import type { AdminForthBulkActionCommon, AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
87
|
+
import type { AdminForthActionInput } from '@/types/Back';
|
|
88
|
+
|
|
50
89
|
|
|
51
90
|
const route = useRoute();
|
|
52
91
|
const coreStore = useCoreStore();
|
|
53
92
|
const router = useRouter();
|
|
93
|
+
const threeDotsDropdownItemsRefs = ref<Array<ComponentPublicInstance | null>>([]);
|
|
54
94
|
|
|
55
95
|
const props = defineProps({
|
|
56
|
-
threeDotsDropdownItems: Array
|
|
57
|
-
customActions: Array
|
|
96
|
+
threeDotsDropdownItems: Array<AdminForthComponentDeclarationFull>,
|
|
97
|
+
customActions: Array<AdminForthActionInput>,
|
|
98
|
+
bulkActions: Array<AdminForthBulkActionCommon>,
|
|
99
|
+
checkboxes: Array,
|
|
100
|
+
updateList: {
|
|
101
|
+
type: Function,
|
|
102
|
+
},
|
|
103
|
+
clearCheckboxes: {
|
|
104
|
+
type: Function
|
|
105
|
+
}
|
|
58
106
|
});
|
|
59
107
|
|
|
60
|
-
|
|
108
|
+
const emit = defineEmits(['startBulkAction']);
|
|
109
|
+
|
|
110
|
+
function setComponentRef(el: ComponentPublicInstance | null, index: number) {
|
|
111
|
+
if (el) {
|
|
112
|
+
threeDotsDropdownItemsRefs.value[index] = el;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function handleActionClick(action: AdminForthActionInput, payload: any) {
|
|
61
117
|
adminforth.list.closeThreeDotsDropdown();
|
|
62
118
|
|
|
63
119
|
const actionId = action.id;
|
|
@@ -67,7 +123,8 @@ async function handleActionClick(action) {
|
|
|
67
123
|
body: {
|
|
68
124
|
resourceId: route.params.resourceId,
|
|
69
125
|
actionId: actionId,
|
|
70
|
-
recordId: route.params.primaryKey
|
|
126
|
+
recordId: route.params.primaryKey,
|
|
127
|
+
extra: payload || {},
|
|
71
128
|
}
|
|
72
129
|
});
|
|
73
130
|
|
|
@@ -88,8 +145,8 @@ async function handleActionClick(action) {
|
|
|
88
145
|
|
|
89
146
|
if (data?.ok) {
|
|
90
147
|
await coreStore.fetchRecord({
|
|
91
|
-
resourceId: route.params.resourceId,
|
|
92
|
-
primaryKey: route.params.primaryKey,
|
|
148
|
+
resourceId: route.params.resourceId as string,
|
|
149
|
+
primaryKey: route.params.primaryKey as string,
|
|
93
150
|
source: 'show',
|
|
94
151
|
});
|
|
95
152
|
|
|
@@ -108,4 +165,16 @@ async function handleActionClick(action) {
|
|
|
108
165
|
});
|
|
109
166
|
}
|
|
110
167
|
}
|
|
168
|
+
|
|
169
|
+
function startBulkAction(actionId: string) {
|
|
170
|
+
adminforth.list.closeThreeDotsDropdown();
|
|
171
|
+
emit('startBulkAction', actionId);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function injectedComponentClick(index: number) {
|
|
175
|
+
const componentRef = threeDotsDropdownItemsRefs.value[index];
|
|
176
|
+
if (componentRef && 'click' in componentRef) {
|
|
177
|
+
(componentRef as any).click?.();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
111
180
|
</script>
|
|
@@ -1,46 +1,43 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
<div class="flex items-center w-full p-4
|
|
4
|
+
<div class="afcl-toast flex items-center w-full p-4 rounded-lg shadow-lg dark:text-darkToastText dark:bg-darkToastBackground bg-lightToastBackground text-lightToastText border-l-4"
|
|
5
|
+
:class="toast.variant == 'info' ? 'border-lightPrimary dark:border-darkPrimary' : toast.variant == 'danger' ? 'border-red-500 dark:border-red-800' : toast.variant == 'warning' ? 'border-orange-500 dark:border-orange-700' : 'border-green-500 dark:border-green-800'"
|
|
5
6
|
role="alert"
|
|
6
|
-
:class="
|
|
7
|
-
{
|
|
8
|
-
'danger': 'bg-red-100',
|
|
9
|
-
}[toast.variant]
|
|
10
|
-
"
|
|
11
7
|
>
|
|
12
|
-
<div v-if="toast.variant == 'info'" class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-lightPrimary dark:text-darkPrimary bg-lightPrimaryOpacity rounded-lg dark:bg-
|
|
13
|
-
<
|
|
14
|
-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.147 15.085a7.159 7.159 0 0 1-6.189 3.307A6.713 6.713 0 0 1 3.1 15.444c-2.679-4.513.287-8.737.888-9.548A4.373 4.373 0 0 0 5 1.608c1.287.953 6.445 3.218 5.537 10.5 1.5-1.122 2.706-3.01 2.853-6.14 1.433 1.049 3.993 5.395 1.757 9.117Z"/>
|
|
15
|
-
</svg>
|
|
16
|
-
<span class="sr-only">{{ $t('Fire icon') }}</span>
|
|
8
|
+
<div v-if="toast.variant == 'info'" class="af-toast-icon inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-lightPrimary dark:text-darkPrimary bg-lightPrimaryOpacity rounded-lg dark:bg-darkPrimary dark:!text-blue-100">
|
|
9
|
+
<IconInfoCircleSolid class="w-5 h-5" aria-hidden="true" />
|
|
17
10
|
</div>
|
|
18
|
-
<div v-else-if="toast.variant == 'danger'" class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200">
|
|
19
|
-
<
|
|
20
|
-
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 11.793a1 1 0 1 1-1.414 1.414L10 11.414l-2.293 2.293a1 1 0 0 1-1.414-1.414L8.586 10 6.293 7.707a1 1 0 0 1 1.414-1.414L10 8.586l2.293-2.293a1 1 0 0 1 1.414 1.414L11.414 10l2.293 2.293Z"/>
|
|
21
|
-
</svg>
|
|
22
|
-
<span class="sr-only">{{ $t('Error icon') }}</span>
|
|
11
|
+
<div v-else-if="toast.variant == 'danger'" class="af-toast-icon inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200">
|
|
12
|
+
<IconCloseCircleSolid class="w-5 h-5" aria-hidden="true" />
|
|
23
13
|
</div>
|
|
24
|
-
<div v-else-if="toast.variant == 'warning'"class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-orange-500 bg-orange-100 rounded-lg dark:bg-orange-700 dark:text-orange-200">
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
</svg>
|
|
28
|
-
<span class="sr-only">{{ $t('Warning icon') }}</span>
|
|
14
|
+
<div v-else-if="toast.variant == 'warning'" class="af-toast-icon inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-orange-500 bg-orange-100 rounded-lg dark:bg-orange-700 dark:text-orange-200">
|
|
15
|
+
<IconExclamationCircleSolid class="w-5 h-5" aria-hidden="true" />
|
|
16
|
+
|
|
29
17
|
</div>
|
|
30
|
-
<div v-else class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
|
|
31
|
-
<
|
|
32
|
-
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z"/>
|
|
33
|
-
</svg>
|
|
34
|
-
<span class="sr-only">{{ $t('Check icon') }}</span>
|
|
18
|
+
<div v-else class="af-toast-icon inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
|
|
19
|
+
<IconCheckCircleSolid class="w-5 h-5" aria-hidden="true" />
|
|
35
20
|
</div>
|
|
36
21
|
|
|
37
22
|
<div class="ms-3 text-sm font-normal max-w-xs pr-2" v-if="toast.messageHtml" v-html="toast.messageHtml"></div>
|
|
38
|
-
<div class="ms-3 text-sm font-normal max-w-xs pr-2" v-else>
|
|
39
|
-
|
|
23
|
+
<div class="ms-3 text-sm font-normal max-w-xs pr-2" v-else>
|
|
24
|
+
<div class="flex flex-col items-center justify-center">
|
|
25
|
+
{{toast.message}}
|
|
26
|
+
<div v-if="toast.buttons" class="flex justify-center mt-2 gap-2">
|
|
27
|
+
<div v-for="button in toast.buttons" class="af-toast-button rounded-md bg-lightButtonsBackground hover:bg-lightButtonsHover text-lightButtonsText dark:bg-darkPrimary dark:hover:bg-darkButtonsBackground dark:text-darkButtonsText">
|
|
28
|
+
<button @click="onButtonClick(button.value)" class="px-2 py-1 rounded hover:bg-black/5 dark:hover:bg-white/10">
|
|
29
|
+
{{ button.label }}
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<button @click="closeToast" type="button" class="ms-auto -mx-1.5 -my-1.5 bg-lightToastCloseIconBackground text-lightToastCloseIcon hover:text-lightToastCloseIconHover rounded-lg focus:ring-2 focus:ring-lightToastCloseIconFocusRing p-1.5 hover:bg-lightToastCloseIconBackgroundHover inline-flex items-center justify-center h-8 w-8 dark:text-darkToastCloseIcon dark:hover:text-darkToastCloseIconHover dark:bg-darkToastCloseIconBackground dark:hover:bg-darkToastCloseIconBackgroundHover dark:focus:ring-darkToastCloseIconFocusRing" >
|
|
40
36
|
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
41
37
|
<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"/>
|
|
42
38
|
</svg>
|
|
43
39
|
</button>
|
|
40
|
+
<!-- <div class="h-full ml-3 w-1 rounded-r-lg" :class="toast.variant == 'info' ? 'bg-lightPrimary dark:bg-darkPrimary' : toast.variant == 'danger' ? 'bg-red-500 dark:bg-red-800' : toast.variant == 'warning' ? 'bg-orange-500 dark:bg-orange-700' : 'bg-green-500 dark:bg-green-800'"></div> -->
|
|
44
41
|
</div>
|
|
45
42
|
|
|
46
43
|
|
|
@@ -49,6 +46,8 @@
|
|
|
49
46
|
<script setup lang="ts">
|
|
50
47
|
import { onMounted } from 'vue';
|
|
51
48
|
import { useToastStore } from '@/stores/toast';
|
|
49
|
+
import { IconInfoCircleSolid, IconCloseCircleSolid, IconExclamationCircleSolid, IconCheckCircleSolid } from '@iconify-prerendered/vue-flowbite';
|
|
50
|
+
|
|
52
51
|
const toastStore = useToastStore();
|
|
53
52
|
const emit = defineEmits(['close']);
|
|
54
53
|
const props = defineProps<{
|
|
@@ -58,16 +57,28 @@ const props = defineProps<{
|
|
|
58
57
|
variant: string;
|
|
59
58
|
id: string;
|
|
60
59
|
timeout?: number|'unlimited';
|
|
60
|
+
buttons?: { value: any; label: string }[];
|
|
61
61
|
}
|
|
62
62
|
}>();
|
|
63
63
|
function closeToast() {
|
|
64
|
+
// resolve with undefined on close (X button)
|
|
65
|
+
toastStore.resolveToast(props.toast.id);
|
|
66
|
+
emit('close');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function onButtonClick(value: any) {
|
|
70
|
+
toastStore.resolveToast(props.toast.id, value);
|
|
64
71
|
emit('close');
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
onMounted(() => {
|
|
68
75
|
if (props.toast.timeout === 'unlimited') return;
|
|
69
76
|
else {
|
|
70
|
-
setTimeout(() => {
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
// resolve with undefined on auto-timeout
|
|
79
|
+
toastStore.resolveToast(props.toast.id);
|
|
80
|
+
emit('close');
|
|
81
|
+
}, (props.toast.timeout || 10) * 1e3 );
|
|
71
82
|
}
|
|
72
83
|
});
|
|
73
84
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="options && options.length" class="min-w-40">
|
|
3
|
+
<div class="cursor-pointer flex items-center justify-between gap-1 block px-4 py-2 text-sm
|
|
4
|
+
bg-lightUserMenuItemBackground hover:bg-lightUserMenuItemBackgroundHover text-lightUserMenuItemText
|
|
5
|
+
hover:text-lightUserMenuItemText dark:bg-darkUserMenuItemBackground dark:hover:bg-darkUserMenuItemBackgroundHover
|
|
6
|
+
dark:text-darkUserMenuItemText dark:hover:darkUserMenuItemTextHover
|
|
7
|
+
w-full select-none "
|
|
8
|
+
@click="showDropdown = !showDropdown"
|
|
9
|
+
>
|
|
10
|
+
<span>{{ $t('Settings') }}</span>
|
|
11
|
+
<IconCaretDownSolid class="h-5 w-5 text-lightPrimary dark:text-gray-400 opacity-50 transition duration-150 ease-in"
|
|
12
|
+
:class="{ 'transform rotate-180': showDropdown }"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div v-if="showDropdown" >
|
|
17
|
+
|
|
18
|
+
<router-link class="cursor-pointer flex items-center gap-1 block px-4 py-1 text-sm
|
|
19
|
+
bg-lightUserMenuItemBackground hover:bg-lightUserMenuItemBackgroundHover text-lightUserMenuItemText
|
|
20
|
+
hover:text-lightUserMenuItemText dark:bg-darkUserMenuItemBackground dark:hover:bg-darkUserMenuItemBackgroundHover
|
|
21
|
+
dark:text-darkUserMenuItemText dark:hover:darkUserMenuItemTextHover
|
|
22
|
+
w-full text-select-none pl-5 select-none"
|
|
23
|
+
v-for="option in options"
|
|
24
|
+
:to="getRoute(option)"
|
|
25
|
+
>
|
|
26
|
+
<span class="mr-1">
|
|
27
|
+
<component v-if="option.icon" :is="getIcon(option.icon)" class="w-5 h-5 transition duration-75" ></component>
|
|
28
|
+
</span>
|
|
29
|
+
<span>{{ option.pageLabel }}</span>
|
|
30
|
+
</router-link>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
|
|
39
|
+
import { computed, ref, onMounted, watch } from 'vue';
|
|
40
|
+
import { useCoreStore } from '@/stores/core';
|
|
41
|
+
import { getIcon } from '@/utils';
|
|
42
|
+
import { useRouter } from 'vue-router';
|
|
43
|
+
import { useI18n } from 'vue-i18n';
|
|
44
|
+
|
|
45
|
+
const router = useRouter();
|
|
46
|
+
const coreStore = useCoreStore();
|
|
47
|
+
const { t } = useI18n();
|
|
48
|
+
|
|
49
|
+
const showDropdown = ref(false);
|
|
50
|
+
const props = defineProps(['meta', 'resource']);
|
|
51
|
+
|
|
52
|
+
const options = computed(() => {
|
|
53
|
+
return coreStore.config?.settingPages?.filter(page => page.isVisible).map((page) => {
|
|
54
|
+
return {
|
|
55
|
+
pageLabel: page.pageLabel,
|
|
56
|
+
slug: page.slug || null,
|
|
57
|
+
icon: page.icon || null,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function getRoute(option: { slug?: string | null, pageLabel: string }) {
|
|
63
|
+
return {
|
|
64
|
+
name: 'settings',
|
|
65
|
+
params: { page: option.slug }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
</script>
|
|
@@ -12,13 +12,40 @@
|
|
|
12
12
|
>
|
|
13
13
|
<RouterLink
|
|
14
14
|
class="font-medium text-lightSidebarText dark:text-darkSidebarText hover:brightness-110 whitespace-nowrap"
|
|
15
|
-
:to="{
|
|
15
|
+
:to="{
|
|
16
|
+
name: 'resource-show',
|
|
17
|
+
params: {
|
|
18
|
+
primaryKey: foreignResource.pk,
|
|
19
|
+
resourceId: column.foreignResource
|
|
20
|
+
? (
|
|
21
|
+
column.foreignResource.resourceId
|
|
22
|
+
|| column.foreignResource.polymorphicResources?.find(
|
|
23
|
+
(pr: any) => pr.whenValue === record[column.foreignResource?.polymorphicOn!]
|
|
24
|
+
)?.resourceId
|
|
25
|
+
)
|
|
26
|
+
: undefined
|
|
27
|
+
}
|
|
28
|
+
}"
|
|
16
29
|
>
|
|
17
30
|
{{ foreignResource.label }}
|
|
18
31
|
</RouterLink>
|
|
19
32
|
</span>
|
|
20
33
|
<RouterLink v-else-if="record[column.name]" class="font-medium text-lightPrimary dark:text-darkPrimary hover:brightness-110 whitespace-nowrap"
|
|
21
|
-
|
|
34
|
+
:to="{
|
|
35
|
+
name: 'resource-show',
|
|
36
|
+
params: {
|
|
37
|
+
primaryKey: record[column.name].pk,
|
|
38
|
+
resourceId: column.foreignResource
|
|
39
|
+
? (
|
|
40
|
+
column.foreignResource.resourceId
|
|
41
|
+
|| column.foreignResource.polymorphicResources?.find(
|
|
42
|
+
(pr: any) => pr.whenValue === record[column.foreignResource?.polymorphicOn!]
|
|
43
|
+
)?.resourceId
|
|
44
|
+
)
|
|
45
|
+
: undefined
|
|
46
|
+
}
|
|
47
|
+
}"
|
|
48
|
+
>
|
|
22
49
|
{{ record[column.name].label }}
|
|
23
50
|
</RouterLink>
|
|
24
51
|
<div v-else>
|
|
@@ -27,8 +54,8 @@
|
|
|
27
54
|
</span>
|
|
28
55
|
|
|
29
56
|
<span v-else-if="column.type === 'boolean'">
|
|
30
|
-
<span v-if="record[column.name] === true" class="bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">{{ $t('Yes') }}</span>
|
|
31
|
-
<span v-else-if="record[column.name] === false" class="bg-red-100 whitespace-nowrap text-red-
|
|
57
|
+
<span v-if="record[column.name] === true" class="af-true-value-icon bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">{{ $t('Yes') }}</span>
|
|
58
|
+
<span v-else-if="record[column.name] === false" class="af-false-value-icon bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">{{ $t('No') }}</span>
|
|
32
59
|
<span v-else class="bg-gray-100 whitespace-nowrap text-gray-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-gray-400 border border-gray-400">{{ $t('Unset') }}</span>
|
|
33
60
|
</span>
|
|
34
61
|
<span
|
|
@@ -38,14 +65,14 @@
|
|
|
38
65
|
<template v-for="(arrayItem, arrayItemIndex) in record[column.name]">
|
|
39
66
|
<span
|
|
40
67
|
v-if="column.isArray.itemType === 'boolean' && arrayItem"
|
|
41
|
-
:key="`${column.name}-${arrayItemIndex}`"
|
|
42
|
-
class="bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">
|
|
68
|
+
:key="`${column.name}-${arrayItemIndex}-true`"
|
|
69
|
+
class="af-true-value-icon bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">
|
|
43
70
|
{{ $t('Yes') }}
|
|
44
71
|
</span>
|
|
45
72
|
<span
|
|
46
73
|
v-else-if="column.isArray.itemType === 'boolean'"
|
|
47
|
-
:key="`${column.name}-${arrayItemIndex}`"
|
|
48
|
-
class="bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">
|
|
74
|
+
:key="`${column.name}-${arrayItemIndex}-false`"
|
|
75
|
+
class="af-false-value-icon bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">
|
|
49
76
|
{{ $t('No') }}
|
|
50
77
|
</span>
|
|
51
78
|
<span
|
|
@@ -53,30 +80,30 @@
|
|
|
53
80
|
:key="`${column.name}-${arrayItemIndex}`"
|
|
54
81
|
class="rounded-md m-0.5 bg-lightAnnouncementBG dark:bg-darkAnnouncementBG text-lightAnnouncementText dark:text-darkAnnouncementText py-0.5 px-2.5 text-sm"
|
|
55
82
|
>
|
|
56
|
-
{{ checkEmptyValues(getArrayItemDisplayValue(arrayItem, column), route.meta.type) }}
|
|
83
|
+
{{ checkEmptyValues(getArrayItemDisplayValue(arrayItem, column), route.meta.type as "show" | "list") }}
|
|
57
84
|
</span>
|
|
58
85
|
</template>
|
|
59
86
|
</span>
|
|
60
87
|
<span v-else-if="column.enum">
|
|
61
|
-
{{ checkEmptyValues(column.enum.find(e => e.value === record[column.name])?.label || record[column.name], route.meta.type) }}
|
|
88
|
+
{{ checkEmptyValues(column.enum.find(e => e.value === record[column.name])?.label || record[column.name], route.meta.type as "show" | "list") }}
|
|
62
89
|
</span>
|
|
63
90
|
<span v-else-if="column.type === 'datetime'" class="whitespace-nowrap">
|
|
64
|
-
{{ checkEmptyValues(formatDateTime(record[column.name]), route.meta.type) }}
|
|
91
|
+
{{ checkEmptyValues(formatDateTime(record[column.name]), route.meta.type as "show" | "list") }}
|
|
65
92
|
</span>
|
|
66
93
|
<span v-else-if="column.type === 'date'" class="whitespace-nowrap">
|
|
67
|
-
{{ checkEmptyValues(formatDate(record[column.name]), route.meta.type) }}
|
|
94
|
+
{{ checkEmptyValues(formatDate(record[column.name]), route.meta.type as "show" | "list") }}
|
|
68
95
|
</span>
|
|
69
96
|
<span v-else-if="column.type === 'time'" class="whitespace-nowrap">
|
|
70
|
-
{{ checkEmptyValues(formatTime(record[column.name]), route.meta.type) }}
|
|
97
|
+
{{ checkEmptyValues(formatTime(record[column.name]), route.meta.type as "show" | "list") }}
|
|
71
98
|
</span>
|
|
72
99
|
<span v-else-if="column.type === 'decimal'">
|
|
73
|
-
{{ checkEmptyValues(record[column.name] && parseFloat(record[column.name]), route.meta.type) }}
|
|
100
|
+
{{ checkEmptyValues(record[column.name] && parseFloat(record[column.name]), route.meta.type as "show" | "list") }}
|
|
74
101
|
</span>
|
|
75
102
|
<span v-else-if="column.type === 'json'">
|
|
76
103
|
<JsonViewer class="min-w-[6rem]" :value="record[column.name]" :expandDepth="column.extra?.jsonCollapsedLevel" copyable sort :theme="coreStore.theme"/>
|
|
77
104
|
</span>
|
|
78
105
|
<span v-else>
|
|
79
|
-
{{ checkEmptyValues(record[column.name],route.meta.type) }}
|
|
106
|
+
{{ checkEmptyValues(record[column.name], route.meta.type as "show" | "list") }}
|
|
80
107
|
</span>
|
|
81
108
|
</div>
|
|
82
109
|
</template>
|
|
@@ -90,7 +117,7 @@ import timezone from 'dayjs/plugin/timezone';
|
|
|
90
117
|
import {checkEmptyValues} from '@/utils';
|
|
91
118
|
import { useRoute, useRouter } from 'vue-router';
|
|
92
119
|
import { JsonViewer } from "vue3-json-viewer";
|
|
93
|
-
import "vue3-json-viewer/dist/
|
|
120
|
+
import "vue3-json-viewer/dist/vue3-json-viewer.css";
|
|
94
121
|
import type { AdminForthResourceColumnCommon } from '@/types/Common';
|
|
95
122
|
|
|
96
123
|
import { useCoreStore } from '@/stores/core';
|
|
@@ -122,7 +149,7 @@ function formatTime(time: string) {
|
|
|
122
149
|
return dayjs(`0000-00-00 ${time}`).format(coreStore.config?.timeFormat || 'HH:mm:ss');
|
|
123
150
|
}
|
|
124
151
|
|
|
125
|
-
function getArrayItemDisplayValue(value, column) {
|
|
152
|
+
function getArrayItemDisplayValue(value: any, column: AdminForthResourceColumnCommon) {
|
|
126
153
|
if (column.isArray?.itemType === 'datetime') {
|
|
127
154
|
return formatDateTime(value);
|
|
128
155
|
} else if (column.isArray?.itemType === 'date') {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Toggle
|
|
3
|
+
:disabled="readonly"
|
|
4
|
+
@update:modelValue="$emit('update:value', $event)"
|
|
5
|
+
:modelValue="valueFromRecord"
|
|
6
|
+
>
|
|
7
|
+
<p>{{text}}</p>
|
|
8
|
+
</Toggle>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import Toggle from '@/afcl/Toggle.vue';
|
|
13
|
+
import type {
|
|
14
|
+
AdminForthResourceColumnCommon,
|
|
15
|
+
AdminForthResourceCommon,
|
|
16
|
+
AdminUser,
|
|
17
|
+
} from "@/types/Common";
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
value: boolean,
|
|
21
|
+
text: string,
|
|
22
|
+
column: AdminForthResourceColumnCommon,
|
|
23
|
+
record: any,
|
|
24
|
+
meta: any,
|
|
25
|
+
resource: AdminForthResourceCommon,
|
|
26
|
+
adminUser: AdminUser,
|
|
27
|
+
readonly: boolean
|
|
28
|
+
}>();
|
|
29
|
+
console.log(JSON.stringify(props));
|
|
30
|
+
console.log("Current mode:", props.meta?.mode)
|
|
31
|
+
defineEmits(['update:value']);
|
|
32
|
+
const valueFromRecord = props.record[props.column.name]
|
|
33
|
+
const editReadOnly = props.column.editReadonly;
|
|
34
|
+
</script>
|
package/dist/spa/src/i18n.ts
CHANGED
|
@@ -3,11 +3,11 @@ import { createApp } from 'vue';
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
// taken from here https://vue-i18n.intlify.dev/guide/essentials/pluralization.html#custom-pluralization
|
|
6
|
-
function slavicPluralRule(choice, choicesLength, orgRule) {
|
|
6
|
+
function slavicPluralRule(choice: number, choicesLength: number, orgRule: any) {
|
|
7
7
|
if (choice === 0) {
|
|
8
8
|
return 0
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
const teen = choice > 10 && choice < 20
|
|
12
12
|
const endsWithOne = choice % 10 === 1
|
|
13
13
|
|
|
@@ -21,6 +21,8 @@ function slavicPluralRule(choice, choicesLength, orgRule) {
|
|
|
21
21
|
return choicesLength < 4 ? 2 : 3
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export let i18nInstance: ReturnType<typeof createI18n> | null = null
|
|
25
|
+
|
|
24
26
|
export function initI18n(app: ReturnType<typeof createApp>) {
|
|
25
27
|
const i18n = createI18n({
|
|
26
28
|
legacy: false,
|
|
@@ -48,7 +50,7 @@ export function initI18n(app: ReturnType<typeof createApp>) {
|
|
|
48
50
|
return key + ' ';
|
|
49
51
|
},
|
|
50
52
|
});
|
|
51
|
-
|
|
52
53
|
app.use(i18n);
|
|
54
|
+
i18nInstance = i18n
|
|
53
55
|
return i18n
|
|
54
56
|
}
|
package/dist/spa/src/main.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Tooltip>
|
|
3
3
|
<span class="flex items-center">
|
|
4
|
-
{{ visualValue }} <IconFileCopyAltSolid @click.stop="copyToCB" class="w-5 h-5 text-lightPrimary dark:text-darkPrimary" v-if="visualValue"/>
|
|
4
|
+
{{ visualValue }} <IconFileCopyAltSolid @click.stop="copyToCB" class="min-w-5 min-h-5 text-lightPrimary dark:text-darkPrimary" v-if="visualValue"/>
|
|
5
5
|
</span>
|
|
6
6
|
<template #tooltip v-if="visualValue">
|
|
7
7
|
{{ props.record[props.column.name] }}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Tooltip>
|
|
3
3
|
<span class="flex items-center">
|
|
4
|
-
{{ visualValue }} <IconFileCopyAltSolid @click.stop="copyToCB" class="w-5 h-5 text-lightPrimary dark:text-darkPrimary" v-if="visualValue"/>
|
|
4
|
+
{{ visualValue }} <IconFileCopyAltSolid @click.stop="copyToCB" class="min-w-5 min-h-5 text-lightPrimary dark:text-darkPrimary" v-if="visualValue"/>
|
|
5
5
|
</span>
|
|
6
6
|
<template #tooltip v-if="visualValue">
|
|
7
7
|
{{ props.record[props.column.name] }}
|
|
@@ -62,6 +62,14 @@ const router = createRouter({
|
|
|
62
62
|
},
|
|
63
63
|
]
|
|
64
64
|
},
|
|
65
|
+
{
|
|
66
|
+
path: '/settings/:page?',
|
|
67
|
+
name: 'settings',
|
|
68
|
+
component: () => import('@/views/SettingsView.vue'),
|
|
69
|
+
meta: {
|
|
70
|
+
title: 'Settings',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
65
73
|
/* IMPORTANT:ADMINFORTH ROUTES */
|
|
66
74
|
{ path: "/:pathMatch(.*)*", component: PageNotFound },
|
|
67
75
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdminForthResource, AdminForthResourceColumn } from '../types/
|
|
1
|
+
import type { AdminForthResource, AdminForthResourceColumn } from '../types/Back.js';
|
|
2
2
|
|
|
3
3
|
export type resourceById = {
|
|
4
4
|
[key: string]: AdminForthResource;
|
|
@@ -21,7 +21,12 @@ export type ResourceColumns = {
|
|
|
21
21
|
|
|
22
22
|
export type CoreConfig = {
|
|
23
23
|
brandName: string,
|
|
24
|
+
singleTheme?: 'light' | 'dark',
|
|
24
25
|
brandLogo: string,
|
|
26
|
+
iconOnlySidebar: {
|
|
27
|
+
logo?: string,
|
|
28
|
+
enabled?: boolean,
|
|
29
|
+
},
|
|
25
30
|
title: string,
|
|
26
31
|
datesFormat: string,
|
|
27
32
|
timeFormat: string,
|
|
@@ -33,12 +38,19 @@ export type CoreConfig = {
|
|
|
33
38
|
passwordHashField: string,
|
|
34
39
|
loginBackgroundImage: string,
|
|
35
40
|
loginBackgroundPosition: string,
|
|
41
|
+
removeBackgroundBlendMode: boolean,
|
|
36
42
|
userFullnameField: string,
|
|
37
43
|
},
|
|
38
44
|
emptyFieldPlaceholder?: {
|
|
39
45
|
show?: string,
|
|
40
46
|
list?: string,
|
|
41
47
|
} | string,
|
|
48
|
+
|
|
49
|
+
customHeadItems?: {
|
|
50
|
+
tagName: string;
|
|
51
|
+
attributes: { [key: string]: string | boolean };
|
|
52
|
+
innerCode?: string;
|
|
53
|
+
}[],
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
|