adminforth 2.27.0-next.7 → 2.27.0-next.71
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 +10 -5
- package/commands/createApp/templates/api.ts.hbs +28 -9
- package/commands/createApp/templates/package.json.hbs +2 -1
- package/commands/proxy.ts +18 -10
- package/dist/basePlugin.js +1 -1
- package/dist/basePlugin.js.map +1 -1
- package/dist/commands/proxy.js +14 -10
- package/dist/commands/proxy.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts +10 -4
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +76 -54
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts +5 -2
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +76 -10
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +3 -1
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +3 -1
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +3 -1
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +4 -2
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +16 -9
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +598 -15
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.js +1 -1
- package/dist/modules/utils.d.ts +1 -1
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +3 -5
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts +18 -7
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +141 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/servers/openapi.d.ts +25 -0
- package/dist/servers/openapi.d.ts.map +1 -0
- package/dist/servers/openapi.js +92 -0
- package/dist/servers/openapi.js.map +1 -0
- package/dist/servers/openapiDocument.d.ts +12 -0
- package/dist/servers/openapiDocument.d.ts.map +1 -0
- package/dist/servers/openapiDocument.js +313 -0
- package/dist/servers/openapiDocument.js.map +1 -0
- package/dist/spa/package-lock.json +41 -0
- package/dist/spa/package.json +4 -0
- package/dist/spa/pnpm-lock.yaml +384 -310
- package/dist/spa/pnpm-workspace.yaml +4 -0
- package/dist/spa/src/App.vue +78 -76
- package/dist/spa/src/afcl/Button.vue +2 -3
- package/dist/spa/src/afcl/Dialog.vue +1 -1
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/Select.vue +8 -2
- package/dist/spa/src/afcl/Skeleton.vue +5 -0
- package/dist/spa/src/afcl/Spinner.vue +1 -1
- package/dist/spa/src/components/CallActionWrapper.vue +1 -1
- package/dist/spa/src/components/ColumnValueInput.vue +16 -3
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +25 -2
- package/dist/spa/src/components/CustomRangePicker.vue +10 -14
- package/dist/spa/src/components/Filters.vue +95 -63
- package/dist/spa/src/components/GroupsTable.vue +9 -6
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +103 -9
- package/dist/spa/src/components/ResourceListTable.vue +16 -10
- package/dist/spa/src/components/ShowTable.vue +3 -3
- package/dist/spa/src/components/Sidebar.vue +29 -8
- package/dist/spa/src/components/ThreeDotsMenu.vue +25 -9
- package/dist/spa/src/components/ValueRenderer.vue +1 -0
- package/dist/spa/src/controls/BoolToggle.vue +2 -2
- package/dist/spa/src/renderers/RichText.vue +2 -2
- package/dist/spa/src/renderers/ZeroStylesRichText.vue +2 -2
- package/dist/spa/src/spa_types/core.ts +32 -0
- package/dist/spa/src/stores/core.ts +16 -2
- package/dist/spa/src/stores/filters.ts +16 -12
- package/dist/spa/src/types/Back.ts +137 -26
- package/dist/spa/src/types/Common.ts +25 -6
- package/dist/spa/src/types/adapters/CompletionAdapter.ts +27 -5
- package/dist/spa/src/types/adapters/index.ts +2 -2
- package/dist/spa/src/utils/createEditUtils.ts +65 -0
- package/dist/spa/src/utils/index.ts +2 -1
- package/dist/spa/src/utils/listUtils.ts +3 -3
- package/dist/spa/src/utils/utils.ts +42 -7
- package/dist/spa/src/utils.ts +2 -1
- package/dist/spa/src/views/CreateEditSkeleton.vue +74 -0
- package/dist/spa/src/views/CreateView.vue +24 -50
- package/dist/spa/src/views/EditView.vue +23 -40
- package/dist/spa/src/views/ListView.vue +22 -32
- package/dist/spa/src/views/ShowView.vue +66 -24
- package/dist/types/Back.d.ts +140 -32
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +32 -6
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/adapters/CompletionAdapter.d.ts +18 -3
- package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -1
- package/dist/types/adapters/index.d.ts +1 -1
- package/dist/types/adapters/index.d.ts.map +1 -1
- package/package.json +11 -6
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
:key="action.id"
|
|
201
201
|
>
|
|
202
202
|
<component
|
|
203
|
-
v-if="action
|
|
203
|
+
v-if="action"
|
|
204
204
|
:is="action.customComponent ? getCustomComponent(formatComponent(action.customComponent)) : CallActionWrapper"
|
|
205
205
|
:meta="formatComponent(action.customComponent).meta"
|
|
206
206
|
:row="row"
|
|
@@ -211,12 +211,18 @@
|
|
|
211
211
|
<button
|
|
212
212
|
type="button"
|
|
213
213
|
class="border border-gray-300 dark:border-gray-700 dark:border-opacity-0 border-opacity-0 hover:border-opacity-100 dark:hover:border-opacity-100 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
|
|
214
|
+
:disabled="!!actionLoadingStates[`${action.id}_${row._primaryKeyValue}`]"
|
|
214
215
|
>
|
|
215
216
|
<component
|
|
216
|
-
v-if="action.icon"
|
|
217
|
+
v-if="action.icon && !actionLoadingStates[`${action.id}_${row._primaryKeyValue}`]"
|
|
217
218
|
:is="getIcon(action.icon)"
|
|
218
219
|
class="w-6 h-6 text-lightPrimary dark:text-darkPrimary"
|
|
219
220
|
/>
|
|
221
|
+
<Spinner
|
|
222
|
+
v-if="actionLoadingStates[`${action.id}_${row._primaryKeyValue}`]"
|
|
223
|
+
class="w-5 h-5 text-gray-200 dark:text-gray-500 fill-gray-500 dark:fill-gray-300"
|
|
224
|
+
/>
|
|
225
|
+
<span v-if="actionLoadingStates[`${action.id}_${row._primaryKeyValue}`]" class="sr-only">Loading...</span>
|
|
220
226
|
</button>
|
|
221
227
|
</component>
|
|
222
228
|
|
|
@@ -356,8 +362,8 @@ import {
|
|
|
356
362
|
IconInboxOutline
|
|
357
363
|
} from '@iconify-prerendered/vue-flowbite';
|
|
358
364
|
import router from '@/router';
|
|
359
|
-
import { Tooltip } from '@/afcl';
|
|
360
|
-
import type {
|
|
365
|
+
import { Tooltip, Spinner } from '@/afcl';
|
|
366
|
+
import type { AdminForthResourceFrontend, AdminForthResourceColumnCommon, AdminForthComponentDeclarationFull, AdminForthComponentDeclaration } from '@/types/Common';
|
|
361
367
|
import { useAdminforth } from '@/adminforth';
|
|
362
368
|
import Checkbox from '@/afcl/Checkbox.vue';
|
|
363
369
|
import ListActionsThreeDots from '@/components/ListActionsThreeDots.vue';
|
|
@@ -368,7 +374,7 @@ const { t } = useI18n();
|
|
|
368
374
|
const { alert, confirm } = useAdminforth();
|
|
369
375
|
const props = defineProps<{
|
|
370
376
|
page: number,
|
|
371
|
-
resource:
|
|
377
|
+
resource: AdminForthResourceFrontend | null,
|
|
372
378
|
rows: any[] | null,
|
|
373
379
|
totalRows: number,
|
|
374
380
|
pageSize: number,
|
|
@@ -613,7 +619,7 @@ async function startCustomAction(actionId: string | number, row: any, extraData:
|
|
|
613
619
|
recordId: row._primaryKeyValue,
|
|
614
620
|
extra: extraData,
|
|
615
621
|
setLoadingState: (loading: boolean) => {
|
|
616
|
-
actionLoadingStates.value[actionId] = loading;
|
|
622
|
+
actionLoadingStates.value[`${actionId}_${row._primaryKeyValue}`] = loading;
|
|
617
623
|
},
|
|
618
624
|
onSuccess: async (data: any) => {
|
|
619
625
|
emits('update:records', true);
|
|
@@ -638,10 +644,10 @@ function validatePageInput() {
|
|
|
638
644
|
pageInput.value = validPage.toString();
|
|
639
645
|
}
|
|
640
646
|
/*
|
|
641
|
-
*___________________________________________________________________
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
647
|
+
* ___________________________________________________________________
|
|
648
|
+
*| |
|
|
649
|
+
*| Virtual Scroll Implementation |
|
|
650
|
+
*|___________________________________________________________________|
|
|
645
651
|
*/
|
|
646
652
|
// Add throttle utility
|
|
647
653
|
const throttle = (fn: Function, delay: number) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="overflow-x-auto shadow-resourseFormShadow dark:shadow-darkResourseFormShadow"
|
|
2
|
+
<div class="overflow-x-auto shadow-resourseFormShadow dark:shadow-darkResourseFormShadow border dark:border-gray-700"
|
|
3
3
|
:class="{'rounded-default' : isRounded}"
|
|
4
4
|
>
|
|
5
5
|
<div v-if="groupName && !noTitle" class="text-md font-semibold px-6 py-3 flex flex-1 items-center text-lightShowTableHeadingText bg-lightShowTableHeadingBackground dark:bg-darkShowTableHeadingBackground dark:text-darkShowTableHeadingText rounded-t-lg">
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
import { getCustomComponent, checkShowIf } from '@/utils';
|
|
65
65
|
import { useCoreStore } from '@/stores/core';
|
|
66
66
|
import { computed } from 'vue';
|
|
67
|
-
import type {
|
|
67
|
+
import type { AdminForthResourceFrontend, AdminForthResourceColumnInputCommon } from '@/types/Common';
|
|
68
68
|
const props = withDefaults(defineProps<{
|
|
69
69
|
columns: Array<{
|
|
70
70
|
name: string;
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
}>;
|
|
84
84
|
groupName?: string | null;
|
|
85
85
|
noTitle?: boolean;
|
|
86
|
-
resource:
|
|
86
|
+
resource: AdminForthResourceFrontend | null;
|
|
87
87
|
record: Record<string, any>;
|
|
88
88
|
isRounded?: boolean;
|
|
89
89
|
}>(), {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
>
|
|
72
72
|
<component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
|
|
73
73
|
<span class="overflow-hidden flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}
|
|
74
|
-
<span v-if="item.badge" class="inline-flex items-center justify-center w-3 h-3 p-3 ms-3 text-sm font-medium rounded-full bg-lightAnnouncementBG dark:bg-darkAnnouncementBG
|
|
74
|
+
<span v-if="item.badge || item.badge === 0" class="inline-flex items-center justify-center w-3 h-3 p-3 ms-3 text-sm font-medium rounded-full bg-lightAnnouncementBG dark:bg-darkAnnouncementBG
|
|
75
75
|
fill-lightAnnouncementText dark:fill-darkAccent text-lightAnnouncementText dark:text-darkAccent">
|
|
76
76
|
<Tooltip v-if="item.badgeTooltip">
|
|
77
77
|
{{ item.badge }}
|
|
@@ -90,13 +90,15 @@
|
|
|
90
90
|
</svg>
|
|
91
91
|
</button>
|
|
92
92
|
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
<
|
|
96
|
-
|
|
93
|
+
<transition name="slow-drop">
|
|
94
|
+
<ul v-show="opened.includes(i)" :id="`dropdown-example${i}`" role="none" class="af-sidebar-dropdown pt-1 space-y-1 overflow-hidden">
|
|
95
|
+
<template v-for="(child, j) in item.children" :key="`menu-${i}-${j}`">
|
|
96
|
+
<li class="af-sidebar-menu-link">
|
|
97
|
+
<MenuLink :item="child" isChild="true" @click="$emit('hideSidebar')"/>
|
|
97
98
|
</li>
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
</template>
|
|
100
|
+
</ul>
|
|
101
|
+
</transition>
|
|
100
102
|
</li>
|
|
101
103
|
<li v-else class="af-sidebar-menu-link">
|
|
102
104
|
<MenuLink :item="item" @click="$emit('hideSidebar')"/>
|
|
@@ -129,7 +131,7 @@
|
|
|
129
131
|
} : {}"
|
|
130
132
|
>{{ item.label }}
|
|
131
133
|
|
|
132
|
-
<span v-if="item.badge" class="inline-flex items-center justify-center w-3 h-3 p-3 ms-3 text-sm font-medium rounded-full bg-lightAnnouncementBG dark:bg-darkAnnouncementBG
|
|
134
|
+
<span v-if="item.badge || item.badge === 0" class="inline-flex items-center justify-center w-3 h-3 p-3 ms-3 text-sm font-medium rounded-full bg-lightAnnouncementBG dark:bg-darkAnnouncementBG
|
|
133
135
|
fill-lightAnnouncementText dark:fill-darkAccent text-lightAnnouncementText dark:text-darkAccent">
|
|
134
136
|
<Tooltip v-if="item.badgeTooltip">
|
|
135
137
|
{{ item.badge }}
|
|
@@ -292,6 +294,25 @@
|
|
|
292
294
|
background-color: rgba(75, 85, 99, 0.4);
|
|
293
295
|
}
|
|
294
296
|
|
|
297
|
+
/* Custom animation for dropdown */
|
|
298
|
+
.slow-drop-enter-active,
|
|
299
|
+
.slow-drop-leave-active {
|
|
300
|
+
overflow: hidden;
|
|
301
|
+
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.slow-drop-enter-from,
|
|
305
|
+
.slow-drop-leave-to {
|
|
306
|
+
opacity: 0;
|
|
307
|
+
transform: translateY(-4px);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.slow-drop-enter-to,
|
|
311
|
+
.slow-drop-leave-from {
|
|
312
|
+
opacity: 1;
|
|
313
|
+
transform: translateY(0);
|
|
314
|
+
}
|
|
315
|
+
|
|
295
316
|
/* For browsers that support overlay scrollbars natively */
|
|
296
317
|
@supports (overflow: overlay) {
|
|
297
318
|
.sidebar-scroll {
|
|
@@ -43,20 +43,28 @@
|
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
45
45
|
</li>
|
|
46
|
-
<li v-for="action in customActions" :key="action.id">
|
|
47
|
-
<div
|
|
46
|
+
<li v-for="(action, i) in customActions" :key="action.id">
|
|
47
|
+
<div
|
|
48
|
+
class="wrapper"
|
|
49
|
+
@click="injectedComponentClick(threeDotsDropdownItems ? threeDotsDropdownItems.length + i : i)"
|
|
50
|
+
>
|
|
48
51
|
<component
|
|
52
|
+
:ref="(el: any) => setComponentRef(el, threeDotsDropdownItems ? threeDotsDropdownItems.length + i : i)"
|
|
49
53
|
:is="(action.customComponent && getCustomComponent(formatComponent(action.customComponent))) || CallActionWrapper"
|
|
50
|
-
:meta="formatComponent(action.customComponent
|
|
54
|
+
:meta="formatComponent(action.customComponent).meta"
|
|
51
55
|
@callAction="(payload? : Object) => handleActionClick(action, payload)"
|
|
52
56
|
>
|
|
53
|
-
<a @click.prevent class="block
|
|
54
|
-
<div class="flex items-center gap-2">
|
|
57
|
+
<a @click.prevent class="block">
|
|
58
|
+
<div class="flex items-center gap-2 hover:text-lightThreeDotsMenuBodyTextHover hover:bg-lightThreeDotsMenuBodyBackgroundHover dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover">
|
|
55
59
|
<component
|
|
56
|
-
v-if="action.icon"
|
|
60
|
+
v-if="action.icon && !actionLoadingStates[action.id!]"
|
|
57
61
|
:is="getIcon(action.icon)"
|
|
58
62
|
class="w-4 h-4 text-lightPrimary dark:text-darkPrimary"
|
|
59
63
|
/>
|
|
64
|
+
<Spinner
|
|
65
|
+
v-if="actionLoadingStates[action.id!]"
|
|
66
|
+
class="w-5 h-5 text-gray-200 dark:text-gray-500 fill-gray-500 dark:fill-gray-300"
|
|
67
|
+
/>
|
|
60
68
|
{{ action.name }}
|
|
61
69
|
</div>
|
|
62
70
|
</a>
|
|
@@ -91,12 +99,12 @@
|
|
|
91
99
|
import { getCustomComponent, getIcon, formatComponent, executeCustomAction } from '@/utils';
|
|
92
100
|
import { useCoreStore } from '@/stores/core';
|
|
93
101
|
import { useAdminforth } from '@/adminforth';
|
|
94
|
-
import { callAdminForthApi } from '@/utils';
|
|
95
102
|
import { useRoute, useRouter } from 'vue-router';
|
|
96
103
|
import CallActionWrapper from '@/components/CallActionWrapper.vue'
|
|
97
104
|
import { ref, type ComponentPublicInstance, onMounted, onUnmounted } from 'vue';
|
|
98
105
|
import type { AdminForthActionFront, AdminForthBulkActionFront, AdminForthComponentDeclarationFull } from '@/types/Common';
|
|
99
106
|
import type { AdminForthActionInput } from '@/types/Back';
|
|
107
|
+
import { Spinner } from '@/afcl';
|
|
100
108
|
|
|
101
109
|
const { list, alert} = useAdminforth();
|
|
102
110
|
const route = useRoute();
|
|
@@ -104,6 +112,7 @@ const coreStore = useCoreStore();
|
|
|
104
112
|
const router = useRouter();
|
|
105
113
|
const threeDotsDropdownItemsRefs = ref<Array<ComponentPublicInstance | null>>([]);
|
|
106
114
|
const showDropdown = ref(false);
|
|
115
|
+
const actionLoadingStates = ref<Record<string, boolean>>({});
|
|
107
116
|
const dropdownRef = ref<HTMLElement | null>(null);
|
|
108
117
|
const buttonTriggerRef = ref<HTMLElement | null>(null);
|
|
109
118
|
|
|
@@ -135,6 +144,9 @@ async function handleActionClick(action: AdminForthActionInput, payload: any) {
|
|
|
135
144
|
resourceId: route.params.resourceId as string,
|
|
136
145
|
recordId: route.params.primaryKey as string,
|
|
137
146
|
extra: payload || {},
|
|
147
|
+
setLoadingState: (loading: boolean) => {
|
|
148
|
+
actionLoadingStates.value[action.id!] = loading;
|
|
149
|
+
},
|
|
138
150
|
onSuccess: async (data: any) => {
|
|
139
151
|
await coreStore.fetchRecord({
|
|
140
152
|
resourceId: route.params.resourceId as string,
|
|
@@ -165,6 +177,7 @@ function startBulkAction(actionId: string) {
|
|
|
165
177
|
}
|
|
166
178
|
|
|
167
179
|
async function injectedComponentClick(index: number) {
|
|
180
|
+
console.log('Injected component click triggered for index:', index);
|
|
168
181
|
const componentRef = threeDotsDropdownItemsRefs.value[index];
|
|
169
182
|
if (componentRef && 'click' in componentRef) {
|
|
170
183
|
(componentRef as any).click?.();
|
|
@@ -195,8 +208,11 @@ onUnmounted(() => {
|
|
|
195
208
|
</script>
|
|
196
209
|
|
|
197
210
|
<style lang="scss" scoped>
|
|
198
|
-
.wrapper
|
|
199
|
-
@apply px-4 py-2
|
|
211
|
+
.wrapper {
|
|
212
|
+
@apply px-4 py-2
|
|
213
|
+
hover:text-lightThreeDotsMenuBodyTextHover hover:bg-lightThreeDotsMenuBodyBackgroundHover
|
|
214
|
+
dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover
|
|
215
|
+
cursor-pointer;
|
|
200
216
|
}
|
|
201
217
|
</style>
|
|
202
218
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
class="rounded-md m-0.5 bg-lightAnnouncementBG dark:bg-darkAnnouncementBG text-lightAnnouncementText dark:text-darkAnnouncementText py-0.5 px-2.5 text-sm"
|
|
12
12
|
>
|
|
13
13
|
<RouterLink
|
|
14
|
+
v-if="foreignResource && foreignResource?.pk"
|
|
14
15
|
class="font-medium text-lightSidebarText dark:text-darkSidebarText hover:brightness-110 whitespace-nowrap"
|
|
15
16
|
:to="{
|
|
16
17
|
name: 'resource-show',
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import Toggle from '@/afcl/Toggle.vue';
|
|
13
13
|
import type {
|
|
14
14
|
AdminForthResourceColumnCommon,
|
|
15
|
-
|
|
15
|
+
AdminForthResourceFrontend,
|
|
16
16
|
AdminUser,
|
|
17
17
|
} from "@/types/Common";
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@ import type {
|
|
|
22
22
|
column: AdminForthResourceColumnCommon,
|
|
23
23
|
record: any,
|
|
24
24
|
meta: any,
|
|
25
|
-
resource:
|
|
25
|
+
resource: AdminForthResourceFrontend,
|
|
26
26
|
adminUser: AdminUser,
|
|
27
27
|
readonly: boolean
|
|
28
28
|
}>();
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
|
-
import type { AdminForthResourceColumnCommon,
|
|
6
|
+
import type { AdminForthResourceColumnCommon, AdminForthResourceFrontend, AdminUser } from '@/types/Common'
|
|
7
7
|
import { protectAgainstXSS } from '@/utils'
|
|
8
8
|
|
|
9
9
|
const props = defineProps<{
|
|
10
10
|
column: AdminForthResourceColumnCommon
|
|
11
11
|
record: any
|
|
12
12
|
meta: any
|
|
13
|
-
resource:
|
|
13
|
+
resource: AdminForthResourceFrontend
|
|
14
14
|
adminUser: AdminUser
|
|
15
15
|
}>()
|
|
16
16
|
const htmlContent = protectAgainstXSS(props.record[props.column.name])
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { nextTick, onMounted, ref, watch } from 'vue'
|
|
7
|
-
import type { AdminForthResourceColumnCommon,
|
|
7
|
+
import type { AdminForthResourceColumnCommon, AdminForthResourceFrontend, AdminUser } from '@/types/Common'
|
|
8
8
|
import { protectAgainstXSS } from '@/utils'
|
|
9
9
|
|
|
10
10
|
const props = defineProps<{
|
|
11
11
|
column: AdminForthResourceColumnCommon
|
|
12
12
|
record: any
|
|
13
13
|
meta: any
|
|
14
|
-
resource:
|
|
14
|
+
resource: AdminForthResourceFrontend
|
|
15
15
|
adminUser: AdminUser
|
|
16
16
|
}>()
|
|
17
17
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { AdminForthResource, AdminForthResourceColumn } from '../types/Back.js';
|
|
2
|
+
import type { FilterParams } from '@/types/Common';
|
|
3
|
+
import type { Ref, ComputedRef } from 'vue';
|
|
2
4
|
|
|
3
5
|
export type resourceById = {
|
|
4
6
|
[key: string]: AdminForthResource;
|
|
@@ -61,3 +63,33 @@ export type AllowedActions = {
|
|
|
61
63
|
delete: boolean,
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
|
|
67
|
+
export type sortType = {
|
|
68
|
+
field: string,
|
|
69
|
+
direction: 'ask' | 'desc'
|
|
70
|
+
} | null;
|
|
71
|
+
|
|
72
|
+
export type AdminforthFilterStore = {
|
|
73
|
+
filters: Ref<FilterParams[]>,
|
|
74
|
+
|
|
75
|
+
setSort: (sort: sortType) => void,
|
|
76
|
+
getSort: () => sortType,
|
|
77
|
+
|
|
78
|
+
setFilter: (filters: FilterParams) => void,
|
|
79
|
+
setFilters: (filters: FilterParams[]) => void,
|
|
80
|
+
|
|
81
|
+
getFilters: () => FilterParams[],
|
|
82
|
+
|
|
83
|
+
clearFilter: (fieldName: string) => void,
|
|
84
|
+
clearFilters: () => void,
|
|
85
|
+
|
|
86
|
+
shouldFilterBeHidden: (fieldName: string) => boolean,
|
|
87
|
+
|
|
88
|
+
visibleFiltersCount: ComputedRef<number>,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AdminforthFilterStoreUnwrapped extends Omit<AdminforthFilterStore, 'filters' | 'visibleFiltersCount'> {
|
|
92
|
+
filters: FilterParams[],
|
|
93
|
+
visibleFiltersCount: number,
|
|
94
|
+
}
|
|
95
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ref, computed } from 'vue'
|
|
1
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
3
|
import { callAdminForthApi } from '@/utils';
|
|
4
4
|
import websocket from '@/websocket';
|
|
@@ -21,6 +21,19 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
21
21
|
const userData: Ref<UserData | null> = ref(null);
|
|
22
22
|
const isResourceFetching = ref(false);
|
|
23
23
|
const isInternetError = ref(false);
|
|
24
|
+
const screenWidth = ref(window.innerWidth);
|
|
25
|
+
|
|
26
|
+
onMounted(() => {
|
|
27
|
+
window.addEventListener('resize', updateWidth);
|
|
28
|
+
});
|
|
29
|
+
onUnmounted(() => {
|
|
30
|
+
window.removeEventListener('resize', updateWidth);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const isMobile = computed(() => screenWidth.value <= 768);
|
|
34
|
+
const updateWidth = () => {
|
|
35
|
+
screenWidth.value = window.innerWidth
|
|
36
|
+
}
|
|
24
37
|
|
|
25
38
|
const resourceColumnsWithFilters = computed(() => {
|
|
26
39
|
if (!resource.value) {
|
|
@@ -29,7 +42,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
29
42
|
return resource.value.columns.filter((col: AdminForthResourceColumnCommon) => col.showIn?.filter);
|
|
30
43
|
})
|
|
31
44
|
|
|
32
|
-
const resourceOptions: Ref<
|
|
45
|
+
const resourceOptions: Ref<AdminForthResourceFrontend['options'] | null> = ref(null);
|
|
33
46
|
const resourceColumnsError: Ref<string> = ref('');
|
|
34
47
|
const resourceColumnsId: Ref<string | null> = ref(null);
|
|
35
48
|
const adminUser: Ref<null | AdminUser> = ref(null);
|
|
@@ -261,5 +274,6 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
261
274
|
isResourceFetching,
|
|
262
275
|
isIos,
|
|
263
276
|
isInternetError,
|
|
277
|
+
isMobile,
|
|
264
278
|
}
|
|
265
279
|
})
|
|
@@ -1,40 +1,42 @@
|
|
|
1
1
|
import { ref, computed, type Ref } from 'vue';
|
|
2
2
|
import { defineStore } from 'pinia';
|
|
3
3
|
import { useCoreStore } from './core';
|
|
4
|
+
import type { FilterParams } from '@/types/Common';
|
|
5
|
+
import type { AdminforthFilterStore, sortType } from '../spa_types/core';
|
|
4
6
|
|
|
5
7
|
export const useFiltersStore = defineStore('filters', () => {
|
|
6
|
-
const filters: Ref<
|
|
7
|
-
const sort: Ref<
|
|
8
|
+
const filters: Ref<FilterParams[]> = ref([]);
|
|
9
|
+
const sort: Ref<sortType> = ref(null);
|
|
8
10
|
const coreStore = useCoreStore();
|
|
9
11
|
|
|
10
|
-
const setSort = (s:
|
|
12
|
+
const setSort = (s: sortType): void => {
|
|
11
13
|
sort.value = s;
|
|
12
14
|
}
|
|
13
|
-
const getSort = () => {
|
|
15
|
+
const getSort = (): sortType => {
|
|
14
16
|
return sort.value;
|
|
15
17
|
}
|
|
16
|
-
const setFilter = (filter:
|
|
18
|
+
const setFilter = (filter: FilterParams) => {
|
|
17
19
|
const index = filters.value.findIndex(f => f.field === filter.field);
|
|
18
|
-
if (filters.value[index] && filters.value[index].operator === filter.
|
|
20
|
+
if (filters.value[index] && filters.value[index].operator === filter.operator) {
|
|
19
21
|
filters.value[index] = filter;
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
22
24
|
filters.value.push(filter);
|
|
23
25
|
}
|
|
24
|
-
const setFilters = (f:
|
|
26
|
+
const setFilters = (f: FilterParams[]) => {
|
|
25
27
|
filters.value = f;
|
|
26
28
|
}
|
|
27
|
-
const getFilters = () => {
|
|
29
|
+
const getFilters = (): FilterParams[] => {
|
|
28
30
|
return filters.value;
|
|
29
31
|
}
|
|
30
|
-
const clearFilter = (fieldName: string) => {
|
|
32
|
+
const clearFilter = (fieldName: string): void => {
|
|
31
33
|
filters.value = filters.value.filter(f => f.field !== fieldName);
|
|
32
34
|
}
|
|
33
|
-
const clearFilters = () => {
|
|
35
|
+
const clearFilters = (): void => {
|
|
34
36
|
filters.value = [];
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const shouldFilterBeHidden = (fieldName: string) => {
|
|
39
|
+
const shouldFilterBeHidden = (fieldName: string): boolean => {
|
|
38
40
|
if (coreStore.resource?.columns) {
|
|
39
41
|
const column = coreStore.resource.columns.find((col: any) => col.name === fieldName);
|
|
40
42
|
if (column?.showIn?.filter !== true) {
|
|
@@ -48,7 +50,7 @@ export const useFiltersStore = defineStore('filters', () => {
|
|
|
48
50
|
return filters.value.filter(f => !shouldFilterBeHidden(f.field)).length;
|
|
49
51
|
});
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
const store = {
|
|
52
54
|
setFilter,
|
|
53
55
|
getFilters,
|
|
54
56
|
clearFilters,
|
|
@@ -60,4 +62,6 @@ export const useFiltersStore = defineStore('filters', () => {
|
|
|
60
62
|
shouldFilterBeHidden,
|
|
61
63
|
clearFilter
|
|
62
64
|
}
|
|
65
|
+
|
|
66
|
+
return store as AdminforthFilterStore;
|
|
63
67
|
})
|