adminforth 2.4.0-next.31 → 2.4.0-next.310
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 +69 -17
- 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 +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -22
- 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 +202 -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 +172 -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 +59 -174
- package/dist/spa/src/adminforth.ts +42 -18
- 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 +6 -6
- 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 +127 -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 +315 -73
- 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 +16 -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 +90 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +115 -85
- package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
- package/dist/spa/src/components/ShowTable.vue +21 -15
- package/dist/spa/src/components/Sidebar.vue +470 -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 +13 -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 +163 -23
- package/dist/spa/src/types/Common.ts +91 -32
- package/dist/spa/src/types/FrontendAPI.ts +31 -5
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
- 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/index.ts +8 -0
- package/dist/spa/src/utils.ts +291 -11
- package/dist/spa/src/views/CreateView.vue +63 -21
- 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 +146 -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 +106 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +31 -3
- 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 +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/index.d.ts +9 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/index.js +2 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/package.json +4 -2
- package/dist/spa/src/types/adapters/index.js +0 -5
|
@@ -1,29 +1,23 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { useModalStore } from '@/stores/modal';
|
|
3
|
-
|
|
4
|
-
const modalStore = useModalStore();
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
1
|
<template>
|
|
8
2
|
<Teleport to="body">
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<div class="relative bg-
|
|
12
|
-
|
|
3
|
+
<div ref="modalEl" tabindex="-1" aria-hidden="true" class="hidden fixed inset-0 z-[110] w-full h-full overflow-y-auto overflow-x-hidden flex items-center justify-center">
|
|
4
|
+
<div class="relative p-4 w-full max-w-md max-h-full" >
|
|
5
|
+
<div class="afcl-confirmation-container relative bg-lightAcceptModalBackground rounded-lg shadow dark:bg-darkAcceptModalBackground dark:shadow-black">
|
|
6
|
+
<button type="button" @click="modalStore.togleModal" class="absolute top-3 end-2.5 text-lightAcceptModalCloseIcon bg-transparent hover:bg-lightAcceptModalCloseIconHoverBackground hover:text-lightAcceptModalCloseIconHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkAcceptModalCloseIcon dark:hover:bg-darkAcceptModalCloseIconHoverBackground dark:hover:text-darkAcceptModalCloseIconHover" >
|
|
13
7
|
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
14
8
|
<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"/>
|
|
15
9
|
</svg>
|
|
16
10
|
<span class="sr-only">{{ $t('Close modal') }}</span>
|
|
17
11
|
</button>
|
|
18
12
|
<div class="p-4 md:p-5 text-center">
|
|
19
|
-
<svg class="mx-auto mb-4 text-
|
|
13
|
+
<svg class="mx-auto mb-4 text-lightAcceptModalWarningIcon w-12 h-12 dark:text-darkAcceptModalWarningIcon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
|
20
14
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
|
21
15
|
</svg>
|
|
22
|
-
<h3 class="mb-5 text-lg font-normal text-
|
|
23
|
-
<button @click="()=>{ modalStore.onAcceptFunction(true);modalStore.togleModal()}" type="button" class="text-
|
|
16
|
+
<h3 class="afcl-confirmation-title mb-5 text-lg font-normal text-lightAcceptModalText dark:text-darkAcceptModalText">{{ modalStore?.modalContent?.content }}</h3>
|
|
17
|
+
<button @click="()=>{ modalStore.onAcceptFunction(true);modalStore.togleModal()}" type="button" class="afcl-confirmation-accept-button text-lightAcceptModalConfirmButtonText bg-lightAcceptModalConfirmButtonBackground hover:bg-lightAcceptModalConfirmButtonBackgroundHover focus:ring-4 focus:outline-none focus:ring-lightAcceptModalConfirmButtonFocus font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center dark:text-darkAcceptModalConfirmButtonText dark:bg-darkAcceptModalConfirmButtonBackground dark:hover:bg-darkAcceptModalConfirmButtonBackgroundHover dark:focus:ring-darkAcceptModalConfirmButtonFocus">
|
|
24
18
|
{{ modalStore?.modalContent?.acceptText }}
|
|
25
19
|
</button>
|
|
26
|
-
<button @click="()=>{modalStore.onAcceptFunction(false);modalStore.togleModal()}" type="button" class="py-2.5 px-5 ms-3 text-sm font-medium text-
|
|
20
|
+
<button @click="()=>{modalStore.onAcceptFunction(false);modalStore.togleModal()}" type="button" class="afcl-confirmation-cancel-button py-2.5 px-5 ms-3 text-sm font-medium text-lightAcceptModalCancelButtonText focus:outline-none bg-lightAcceptModalCancelButtonBackground rounded-lg border border-lightAcceptModalCancelButtonBorder hover:bg-lightAcceptModalCancelButtonBackgroundHover hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-lightAcceptModalCancelButtonFocus dark:focus:ring-darkAcceptModalCancelButtonFocus dark:bg-darkAcceptModalCancelButtonBackground dark:text-darkAcceptModalCancelButtonText dark:border-darkAcceptModalCancelButtonBorder dark:hover:text-darkAcceptModalCancelButtonTextHover dark:hover:bg-darkAcceptModalCancelButtonBackgroundHover">{{ modalStore?.modalContent?.cancelText }}</button>
|
|
27
21
|
</div>
|
|
28
22
|
</div>
|
|
29
23
|
</div>
|
|
@@ -32,6 +26,46 @@ const modalStore = useModalStore();
|
|
|
32
26
|
</Teleport>
|
|
33
27
|
</template>
|
|
34
28
|
|
|
29
|
+
<script setup lang="ts">
|
|
30
|
+
import { watch, onMounted, nextTick, ref } from 'vue';
|
|
31
|
+
import { useModalStore } from '@/stores/modal';
|
|
32
|
+
import { Modal } from 'flowbite';
|
|
33
|
+
|
|
34
|
+
const modalStore = useModalStore();
|
|
35
|
+
const modalEl = ref(null);
|
|
36
|
+
const modal = ref(null);
|
|
37
|
+
|
|
38
|
+
watch( () => modalStore.isOpened, (newVal) => {
|
|
39
|
+
if (newVal) {
|
|
40
|
+
open();
|
|
41
|
+
} else {
|
|
42
|
+
close();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
onMounted(async () => {
|
|
48
|
+
await nextTick();
|
|
49
|
+
modal.value = new Modal(
|
|
50
|
+
modalEl.value,
|
|
51
|
+
{
|
|
52
|
+
closable: true,
|
|
53
|
+
backdrop: 'static',
|
|
54
|
+
backdropClasses: "bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-[100]"
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
function open() {
|
|
60
|
+
modal.value?.show();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function close() {
|
|
64
|
+
modal.value?.hide();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
</script>
|
|
68
|
+
|
|
35
69
|
<style scoped>
|
|
36
70
|
.modal {
|
|
37
71
|
position: fixed;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<nav class="flex" aria-label="Breadcrumb">
|
|
3
3
|
<ol class="inline-flex items-center space-x-1 md:space-x-2 rtl:space-x-reverse flex-wrap">
|
|
4
4
|
<li class="inline-flex items-center">
|
|
5
|
-
<RouterLink :to="{name: 'home'}" class="inline-flex items-center text-sm font-medium text-
|
|
5
|
+
<RouterLink :to="{name: 'home'}" class="inline-flex items-center text-sm font-medium text-lightBreadcrumbsHomepageText hover:text-lightBreadcrumbsHomepageTextHover dark:text-darkBreadcrumbsHomepageText dark:hover:text-darkBreadcrumbsHomepageTextHover">
|
|
6
6
|
<svg class="w-3 h-3 me-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
7
7
|
<path d="m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z"/>
|
|
8
8
|
</svg>
|
|
@@ -11,19 +11,19 @@
|
|
|
11
11
|
</li>
|
|
12
12
|
<li>
|
|
13
13
|
<div class="flex items-center">
|
|
14
|
-
<svg class="rtl:rotate-180 w-3 h-3 text-
|
|
14
|
+
<svg class="rtl:rotate-180 w-3 h-3 text-lightBreadcrumbsArrowIcon dark:text-darkBreadcrumbsArrowIcon mx-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
|
|
15
15
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
|
|
16
16
|
</svg>
|
|
17
|
-
<RouterLink :to="{name: 'resource-list', params: { resourceId: $route.params.resourceId }}" class="text-sm font-medium text-
|
|
17
|
+
<RouterLink :to="{name: 'resource-list', params: { resourceId: $route.params.resourceId }}" class="text-sm font-medium text-lightBreadcrumbsText md:ms-2 dark:text-darkBreadcrumbsText">{{ coreStore.resourceById[$route.params.resourceId]?.label }}</RouterLink>
|
|
18
18
|
</div>
|
|
19
19
|
</li>
|
|
20
20
|
|
|
21
21
|
<li v-if="$route.params.primaryKey">
|
|
22
22
|
<div class="flex items-center">
|
|
23
|
-
<svg class="rtl:rotate-180 w-3 h-3 text-
|
|
23
|
+
<svg class="rtl:rotate-180 w-3 h-3 text-lightBreadcrumbsArrowIcon dark:text-darkBreadcrumbsArrowIcon mx-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
|
|
24
24
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
|
|
25
25
|
</svg>
|
|
26
|
-
<span class="ms-1 text-sm font-medium text-
|
|
26
|
+
<span class="ms-1 text-sm font-medium text-lightBreadcrumbsText md:ms-2 dark:text-darkBreadcrumbsText
|
|
27
27
|
max-w-80 truncate">
|
|
28
28
|
{{ $route.name === 'resource-edit' ? $t('Edit') : $t('Show') }} {{ coreStore.record?._label }}</span>
|
|
29
29
|
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div @click="onClick">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
const props = defineProps<{ disabled?: boolean, extra?: any }>();
|
|
9
|
+
const emit = defineEmits<{ (e: 'callAction', extra?: any ): void }>();
|
|
10
|
+
|
|
11
|
+
function onClick() {
|
|
12
|
+
if (props.disabled) return;
|
|
13
|
+
emit('callAction', props.extra);
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
@@ -17,14 +17,29 @@
|
|
|
17
17
|
<Select
|
|
18
18
|
v-else-if="column.foreignResource"
|
|
19
19
|
ref="input"
|
|
20
|
+
:key="`select-${column.name}-${source}-${column.foreignResource?.name || column.foreignResource?.table || ''}`"
|
|
20
21
|
class="w-full min-w-24"
|
|
21
22
|
:options="columnOptions[column.name] || []"
|
|
23
|
+
:searchDisabled="!column.foreignResource.searchableFields"
|
|
24
|
+
@scroll-near-end="loadMoreOptions && loadMoreOptions(column.name)"
|
|
25
|
+
@search="(searchTerm) => {
|
|
26
|
+
if (column.foreignResource.searchableFields && onSearchInput && onSearchInput[column.name]) {
|
|
27
|
+
onSearchInput[column.name](searchTerm);
|
|
28
|
+
}
|
|
29
|
+
}"
|
|
22
30
|
teleportToBody
|
|
23
31
|
:placeholder = "columnOptions[column.name]?.length ?$t('Select...'): $t('There are no options available')"
|
|
24
32
|
:modelValue="value"
|
|
25
33
|
:readonly="(column.editReadonly && source === 'edit') || readonly"
|
|
26
34
|
@update:modelValue="$emit('update:modelValue', $event)"
|
|
27
|
-
|
|
35
|
+
>
|
|
36
|
+
<template #extra-item v-if="columnLoadingState && columnLoadingState[column.name]?.loading">
|
|
37
|
+
<div class="text-center text-gray-400 dark:text-gray-300 py-2 flex items-center justify-center gap-2">
|
|
38
|
+
<Spinner class="w-4 h-4" />
|
|
39
|
+
{{ $t('Loading...') }}
|
|
40
|
+
</div>
|
|
41
|
+
</template>
|
|
42
|
+
</Select>
|
|
28
43
|
<Select
|
|
29
44
|
v-else-if="column.enum"
|
|
30
45
|
ref="input"
|
|
@@ -52,6 +67,7 @@
|
|
|
52
67
|
step="1"
|
|
53
68
|
class="w-40"
|
|
54
69
|
placeholder="0"
|
|
70
|
+
:fullWidth="true"
|
|
55
71
|
:min="![undefined, null].includes(column.minValue) ? column.minValue : ''"
|
|
56
72
|
:max="![undefined, null].includes(column.maxValue) ? column.maxValue : ''"
|
|
57
73
|
:prefix="column.inputPrefix"
|
|
@@ -60,7 +76,7 @@
|
|
|
60
76
|
:modelValue="value"
|
|
61
77
|
@update:modelValue="$emit('update:modelValue', $event)"
|
|
62
78
|
/>
|
|
63
|
-
<
|
|
79
|
+
<DatePicker
|
|
64
80
|
v-else-if="['datetime', 'date', 'time'].includes(type || column.type)"
|
|
65
81
|
ref="input"
|
|
66
82
|
:column="column"
|
|
@@ -76,6 +92,7 @@
|
|
|
76
92
|
step="0.1"
|
|
77
93
|
class="w-40"
|
|
78
94
|
placeholder="0.0"
|
|
95
|
+
:fullWidth="true"
|
|
79
96
|
:min="![undefined, null].includes(column.minValue) ? column.minValue : ''"
|
|
80
97
|
:max="![undefined, null].includes(column.maxValue) ? column.maxValue : ''"
|
|
81
98
|
:prefix="column.inputPrefix"
|
|
@@ -84,28 +101,25 @@
|
|
|
84
101
|
@update:modelValue="$emit('update:modelValue', $event)"
|
|
85
102
|
:readonly="(column.editReadonly && source === 'edit') || readonly"
|
|
86
103
|
/>
|
|
87
|
-
<
|
|
104
|
+
<Textarea
|
|
88
105
|
v-else-if="['text', 'richtext'].includes(type || column.type)"
|
|
89
|
-
ref="input"
|
|
90
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
|
|
91
106
|
:placeholder="$t('Text')"
|
|
92
|
-
:
|
|
93
|
-
@
|
|
107
|
+
:modelValue="value"
|
|
108
|
+
@update:modelValue="$emit('update:modelValue', $event)"
|
|
94
109
|
:readonly="(column.editReadonly && source === 'edit') || readonly"
|
|
95
110
|
/>
|
|
96
|
-
<
|
|
111
|
+
<Textarea
|
|
97
112
|
v-else-if="['json'].includes(type || column.type)"
|
|
98
|
-
ref="input"
|
|
99
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
|
|
100
113
|
:placeholder="$t('Text')"
|
|
101
|
-
:
|
|
102
|
-
@
|
|
114
|
+
:modelValue="value"
|
|
115
|
+
@update:modelValue="$emit('update:modelValue', $event)"
|
|
103
116
|
/>
|
|
104
117
|
<Input
|
|
105
118
|
v-else
|
|
106
119
|
ref="input"
|
|
107
120
|
:type="!column.masked || unmasked[column.name] ? 'text' : 'password'"
|
|
108
121
|
class="w-full"
|
|
122
|
+
:fullWidth="true"
|
|
109
123
|
:placeholder="$t('Text')"
|
|
110
124
|
:prefix="column.inputPrefix"
|
|
111
125
|
:suffix="column.inputSuffix"
|
|
@@ -123,7 +137,7 @@
|
|
|
123
137
|
class="h-6 inset-y-2 right-0 flex items-center px-2 pt-4 z-index-100 focus:outline-none"
|
|
124
138
|
@click="$emit('delete')"
|
|
125
139
|
>
|
|
126
|
-
<IconTrashBinSolid class="w-6 h-6 text-
|
|
140
|
+
<IconTrashBinSolid class="w-6 h-6 text-lightInputIcons dark:text-darkInputIcons"/>
|
|
127
141
|
</button>
|
|
128
142
|
<button
|
|
129
143
|
v-else-if="column.masked"
|
|
@@ -131,18 +145,20 @@
|
|
|
131
145
|
@click="$emit('update:unmasked')"
|
|
132
146
|
class="h-6 inset-y-2 right-0 flex items-center px-2 pt-4 z-index-100 focus:outline-none"
|
|
133
147
|
>
|
|
134
|
-
<IconEyeSolid class="w-6 h-6 text-
|
|
135
|
-
<IconEyeSlashSolid class="w-6 h-6 text-
|
|
148
|
+
<IconEyeSolid class="w-6 h-6 text-lightInputIcons dark:text-darkInputIcons" v-if="!unmasked[column.name]"/>
|
|
149
|
+
<IconEyeSlashSolid class="w-6 h-6 text-lightInputIcons dark:text-darkInputIcons" v-else />
|
|
136
150
|
</button>
|
|
137
151
|
</div>
|
|
138
152
|
</template>
|
|
139
153
|
|
|
140
154
|
<script setup lang="ts">
|
|
141
155
|
import { IconEyeSlashSolid, IconEyeSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
|
|
142
|
-
import
|
|
156
|
+
import DatePicker from "@/afcl/DatePicker.vue";
|
|
143
157
|
import Select from '@/afcl/Select.vue';
|
|
144
158
|
import Input from '@/afcl/Input.vue';
|
|
145
|
-
import
|
|
159
|
+
import Spinner from '@/afcl/Spinner.vue';
|
|
160
|
+
import Textarea from '@/afcl/Textarea.vue';
|
|
161
|
+
import { ref, inject } from 'vue';
|
|
146
162
|
import { getCustomComponent } from '@/utils';
|
|
147
163
|
import { useI18n } from 'vue-i18n';
|
|
148
164
|
import { useCoreStore } from '@/stores/core';
|
|
@@ -171,7 +187,11 @@
|
|
|
171
187
|
}
|
|
172
188
|
);
|
|
173
189
|
|
|
174
|
-
const
|
|
190
|
+
const columnLoadingState = inject('columnLoadingState', {} as any);
|
|
191
|
+
const onSearchInput = inject('onSearchInput', {} as any);
|
|
192
|
+
const loadMoreOptions = inject('loadMoreOptions', (() => {}) as any);
|
|
193
|
+
|
|
194
|
+
const input = ref<HTMLInputElement | null>(null);
|
|
175
195
|
|
|
176
196
|
const getBooleanOptions = (column: any) => {
|
|
177
197
|
const options: Array<{ label: string; value: boolean | null }> = [
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
:currentValues="currentValues"
|
|
14
14
|
:mode="mode"
|
|
15
15
|
:columnOptions="columnOptions"
|
|
16
|
+
:unmasked="unmasked"
|
|
16
17
|
:deletable="!column.editReadonly"
|
|
17
18
|
@update:modelValue="setCurrentValue(column.name, $event, arrayItemIndex)"
|
|
18
19
|
@update:unmasked="$emit('update:unmasked', column.name)"
|
|
19
20
|
@update:inValidity="$emit('update:inValidity', { name: column.name, value: $event })"
|
|
20
21
|
@update:emptiness="$emit('update:emptiness', { name: column.name, value: $event })"
|
|
21
|
-
@delete="setCurrentValue(column.name, currentValues[column.name].filter((_, index) => index !== arrayItemIndex))"
|
|
22
|
+
@delete="setCurrentValue(column.name, currentValues[column.name].filter((_: any, index: any) => index !== arrayItemIndex))"
|
|
22
23
|
/>
|
|
23
24
|
</div>
|
|
24
25
|
<div class="flex items-center">
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
v-if="!column.editReadonly"
|
|
27
28
|
type="button"
|
|
28
29
|
@click="addArrayItem"
|
|
29
|
-
class="flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-
|
|
30
|
+
class="flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-lightInputText focus:outline-none bg-lightInputBackground rounded border border-lightInputBorder hover:bg-lightInputBackgroundHover hover:text-lightInputTextHover hover:border-lightInputBorderHover focus:z-10 focus:ring-4 focus:ring-lightInputFocusRing dark:focus:ring-darkInputFocusRing dark:bg-darkInputBackground dark:text-darkInputText dark:border-darkInputBorder dark:hover:darkInputTextHover dark:hover:bg-darkInputHover"
|
|
30
31
|
:class="{'mt-2': currentValues[column.name].length}"
|
|
31
32
|
>
|
|
32
33
|
<IconPlusOutline class="w-4 h-4 me-2"/>
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
|
|
71
72
|
const emit = defineEmits(['update:unmasked', 'update:inValidity', 'update:emptiness', 'focus-last-input']);
|
|
72
73
|
|
|
73
|
-
const arrayItemRefs = ref([]);
|
|
74
|
+
const arrayItemRefs = ref<HTMLInputElement[]>([]);
|
|
74
75
|
|
|
75
76
|
async function addArrayItem() {
|
|
76
77
|
props.setCurrentValue(props.column.name, props.currentValues[props.column.name], props.currentValues[props.column.name].length);
|
|
@@ -3,21 +3,21 @@
|
|
|
3
3
|
<div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: column.type === 'time'}">
|
|
4
4
|
<div class="relative">
|
|
5
5
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
6
|
-
<IconCalendar class="w-4 h-4 text-
|
|
6
|
+
<IconCalendar class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon"/>
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
9
|
<input ref="datepickerStartEl" type="text"
|
|
10
|
-
class="bg-
|
|
10
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
11
11
|
:placeholder="$t('From')">
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
<div class="relative">
|
|
15
15
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
16
|
-
<IconCalendar class="w-4 h-4 text-
|
|
16
|
+
<IconCalendar class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon"/>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
19
|
<input ref="datepickerEndEl" type="text"
|
|
20
|
-
class="bg-
|
|
20
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
21
21
|
:placeholder="$t('To')">
|
|
22
22
|
</div>
|
|
23
23
|
</div>
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
<div>
|
|
28
28
|
<div class="relative">
|
|
29
29
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
30
|
-
<IconTime class="w-4 h-4 text-
|
|
30
|
+
<IconTime class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon bg-lightDatePickerButtonBackground dark:bg-darkDatePickerButtonBackground"/>
|
|
31
31
|
</div>
|
|
32
32
|
|
|
33
33
|
<input v-model="startTime" type="time" id="start-time"
|
|
34
|
-
class="bg-
|
|
34
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
35
35
|
value="00:00" required/>
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
<div>
|
|
40
40
|
<div class="relative">
|
|
41
41
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
42
|
-
<IconTime class="w-4 h-4 text-
|
|
42
|
+
<IconTime class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon bg-lightDatePickerButtonBackground dark:bg-darkDatePickerButtonBackground"/>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
45
|
<input v-model="endTime" type="time" id="end-time"
|
|
46
|
-
class="bg-
|
|
46
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
47
47
|
value="00:00" required/>
|
|
48
48
|
</div>
|
|
49
49
|
</div>
|
|
@@ -172,6 +172,7 @@ function updateFromProps() {
|
|
|
172
172
|
if (!props.valueEnd) {
|
|
173
173
|
datepickerEndEl.value.value = '';
|
|
174
174
|
endTime.value = '';
|
|
175
|
+
endDate.value = '';
|
|
175
176
|
} else if (props.column.type === 'time') {
|
|
176
177
|
endTime.value = props.valueEnd;
|
|
177
178
|
} else {
|
|
@@ -4,27 +4,20 @@
|
|
|
4
4
|
:min="minFormatted"
|
|
5
5
|
:max="maxFormatted"
|
|
6
6
|
type="number" aria-describedby="helper-text-explanation"
|
|
7
|
-
class="bg-
|
|
7
|
+
class="flex-1 bg-lightRangePickerInputBackground border border-lightRangePickerInputBorder text-lightRangePickerInputText placeholder-lightRangePickerInputPlaceholder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkRangePickerInputBackground dark:border-darkRangePickerInputBorder dark:placeholder-darkRangePickerInputPlaceholder dark:text-darkRangePickerInputText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
8
8
|
:placeholder="$t('From')"
|
|
9
9
|
v-model="start"
|
|
10
10
|
>
|
|
11
|
-
|
|
11
|
+
<p>_</p>
|
|
12
12
|
<input
|
|
13
13
|
:min="minFormatted"
|
|
14
14
|
:max="maxFormatted"
|
|
15
15
|
type="number" aria-describedby="helper-text-explanation"
|
|
16
|
-
class="bg-
|
|
16
|
+
class="flex-1 bg-lightRangePickerInputBackground border border-lightRangePickerInputBorder text-lightRangePickerInputText placeholder-lightRangePickerInputPlaceholder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkRangePickerInputBackground dark:border-darkRangePickerInputBorder dark:placeholder-darkRangePickerInputPlaceholder dark:text-darkRangePickerInputText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
17
17
|
:placeholder="$t('To')"
|
|
18
18
|
v-model="end"
|
|
19
19
|
>
|
|
20
20
|
|
|
21
|
-
<button
|
|
22
|
-
v-if="isChanged"
|
|
23
|
-
type="button"
|
|
24
|
-
class="flex items-center p-0.5 ml-auto px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded 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 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
25
|
-
@click="clear">Clear
|
|
26
|
-
</button>
|
|
27
|
-
|
|
28
21
|
<div v-if="min && max" class="w-full px-2.5">
|
|
29
22
|
<vue-slider
|
|
30
23
|
class="custom-slider"
|
|
@@ -57,15 +50,15 @@ const props = defineProps({
|
|
|
57
50
|
|
|
58
51
|
const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
|
|
59
52
|
|
|
60
|
-
const minFormatted = computed(() => Math.floor(props.min));
|
|
61
|
-
const maxFormatted = computed(() => Math.ceil(props.max));
|
|
53
|
+
const minFormatted = computed(() => Math.floor(<number>props.min));
|
|
54
|
+
const maxFormatted = computed(() => Math.ceil(<number>props.max));
|
|
62
55
|
|
|
63
56
|
const isChanged = computed(() => {
|
|
64
57
|
return start.value && start.value !== minFormatted.value || end.value && end.value !== maxFormatted.value;
|
|
65
58
|
});
|
|
66
59
|
|
|
67
|
-
const start = ref(props.valueStart);
|
|
68
|
-
const end = ref(props.valueEnd);
|
|
60
|
+
const start = ref<string | number>(props.valueStart);
|
|
61
|
+
const end = ref<string | number>(props.valueEnd);
|
|
69
62
|
|
|
70
63
|
const sliderValue = ref([minFormatted.value, maxFormatted.value]);
|
|
71
64
|
|
|
@@ -112,13 +105,7 @@ watch([minFormatted,maxFormatted], () => {
|
|
|
112
105
|
setSliderValues(minFormatted.value, maxFormatted.value)
|
|
113
106
|
})
|
|
114
107
|
|
|
115
|
-
|
|
116
|
-
start.value = ''
|
|
117
|
-
end.value = ''
|
|
118
|
-
setSliderValues('', '')
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function setSliderValues(start, end) {
|
|
108
|
+
function setSliderValues(start: any, end: any) {
|
|
122
109
|
sliderValue.value = [start || minFormatted.value, end || maxFormatted.value];
|
|
123
110
|
}
|
|
124
111
|
</script>
|
|
@@ -153,4 +140,33 @@ function setSliderValues(start, end) {
|
|
|
153
140
|
@apply bg-lightPrimaryOpacity;
|
|
154
141
|
}
|
|
155
142
|
}
|
|
143
|
+
|
|
144
|
+
.dark .custom-slider {
|
|
145
|
+
&:deep(.vue-slider-rail) {
|
|
146
|
+
background-color: rgb(55 65 81); // gray-700
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
&:deep(.vue-slider-dot-handle) {
|
|
150
|
+
@apply bg-darkPrimary;
|
|
151
|
+
border: none;
|
|
152
|
+
box-shadow: none;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
&:deep(.vue-slider-dot-handle:hover) {
|
|
156
|
+
@apply bg-darkPrimary;
|
|
157
|
+
filter: brightness(1.1);
|
|
158
|
+
border: none;
|
|
159
|
+
box-shadow: none;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
&:deep(.vue-slider-process) {
|
|
163
|
+
@apply bg-darkPrimaryOpacity;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&:deep(.vue-slider-process:hover) {
|
|
167
|
+
filter: brightness(1.1);
|
|
168
|
+
@apply bg-darkPrimaryOpacity;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
156
172
|
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="error" class="af-login-modal-error flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
|
|
3
|
+
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
4
|
+
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
5
|
+
</svg>
|
|
6
|
+
<span class="sr-only">{{ $t('Info') }}</span>
|
|
7
|
+
<div>
|
|
8
|
+
{{ error }}
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
|
|
15
|
+
defineProps({
|
|
16
|
+
error: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: null
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
</script>
|