@quazardous/qdadm 1.19.3
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/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +101 -0
- package/src/assets/logo.svg +6 -0
- package/src/auth/SessionAuthAdapter.ts +459 -0
- package/src/auth/index.ts +15 -0
- package/src/chain/ActiveStack.ts +154 -0
- package/src/chain/StackHydrator.ts +292 -0
- package/src/chain/index.ts +25 -0
- package/src/chain/useActiveStack.ts +120 -0
- package/src/chain/useStackHydrator.ts +155 -0
- package/src/components/BoolCell.vue +34 -0
- package/src/components/InfoBanner.vue +73 -0
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/SeverityTag.vue +86 -0
- package/src/components/dialogs/BulkStatusDialog.vue +60 -0
- package/src/components/dialogs/MultiStepDialog.vue +331 -0
- package/src/components/dialogs/SimpleDialog.vue +122 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
- package/src/components/display/BannerZone.vue +65 -0
- package/src/components/display/CardsGrid.vue +72 -0
- package/src/components/display/CopyableId.vue +84 -0
- package/src/components/display/EmptyState.vue +62 -0
- package/src/components/display/IntensityBar.vue +190 -0
- package/src/components/display/RichCardsGrid.vue +101 -0
- package/src/components/edit/FormActions.vue +78 -0
- package/src/components/edit/FormField.vue +82 -0
- package/src/components/edit/FormInput.vue +158 -0
- package/src/components/edit/FormPage.vue +260 -0
- package/src/components/edit/FormTab.vue +217 -0
- package/src/components/edit/FormTabs.vue +120 -0
- package/src/components/edit/LookupField.vue +406 -0
- package/src/components/edit/LookupPickerDialog.vue +230 -0
- package/src/components/editors/JsonEditorFoldable.vue +470 -0
- package/src/components/editors/JsonStructuredField.vue +264 -0
- package/src/components/editors/JsonViewer.vue +51 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +259 -0
- package/src/components/editors/PermissionEditor.vue +558 -0
- package/src/components/editors/ScopeEditor.vue +372 -0
- package/src/components/editors/VanillaJsonEditor.vue +183 -0
- package/src/components/index.ts +83 -0
- package/src/components/item/FieldGroups.vue +392 -0
- package/src/components/layout/AppLayout.vue +1032 -0
- package/src/components/layout/BaseLayout.vue +190 -0
- package/src/components/layout/Breadcrumb.vue +55 -0
- package/src/components/layout/DashboardLayout.vue +104 -0
- package/src/components/layout/FormLayout.vue +256 -0
- package/src/components/layout/ListLayout.vue +278 -0
- package/src/components/layout/PageHeader.vue +156 -0
- package/src/components/layout/PageLayout.vue +126 -0
- package/src/components/layout/PageNav.vue +192 -0
- package/src/components/layout/SidebarBox.vue +87 -0
- package/src/components/layout/Zone.vue +182 -0
- package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
- package/src/components/layout/defaults/DefaultFooter.vue +89 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
- package/src/components/layout/defaults/DefaultHeader.vue +78 -0
- package/src/components/layout/defaults/DefaultMenu.vue +188 -0
- package/src/components/layout/defaults/DefaultPagination.vue +75 -0
- package/src/components/layout/defaults/DefaultTable.vue +137 -0
- package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
- package/src/components/layout/defaults/index.ts +16 -0
- package/src/components/lists/ActionButtons.vue +49 -0
- package/src/components/lists/ActionColumn.vue +40 -0
- package/src/components/lists/FilterBar.vue +54 -0
- package/src/components/lists/ListPage.vue +467 -0
- package/src/components/pages/LoginPage.vue +334 -0
- package/src/components/pages/NotFoundPage.vue +98 -0
- package/src/components/show/ShowDisplay.vue +343 -0
- package/src/components/show/ShowField.vue +67 -0
- package/src/components/show/ShowPage.vue +431 -0
- package/src/composables/index.ts +155 -0
- package/src/composables/useApp.ts +67 -0
- package/src/composables/useAuth.ts +98 -0
- package/src/composables/useBareForm.ts +319 -0
- package/src/composables/useBreadcrumb.ts +252 -0
- package/src/composables/useChildPage.ts +113 -0
- package/src/composables/useCurrentEntity.ts +60 -0
- package/src/composables/useDeferred.ts +94 -0
- package/src/composables/useDirtyState.ts +138 -0
- package/src/composables/useEntityItemFormPage.ts +942 -0
- package/src/composables/useEntityItemFormPage.types.ts +451 -0
- package/src/composables/useEntityItemPage.ts +438 -0
- package/src/composables/useEntityItemShowPage.ts +722 -0
- package/src/composables/useFieldManager.ts +692 -0
- package/src/composables/useForm.ts +544 -0
- package/src/composables/useGuardStore.ts +53 -0
- package/src/composables/useHooks.ts +56 -0
- package/src/composables/useInfoBanner.ts +217 -0
- package/src/composables/useJsonSyntax.ts +122 -0
- package/src/composables/useLayoutResolver.ts +329 -0
- package/src/composables/useListPage.ts +1535 -0
- package/src/composables/useListPage.types.ts +437 -0
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavContext.ts +439 -0
- package/src/composables/useNavigation.ts +216 -0
- package/src/composables/useOptionsLookup.ts +323 -0
- package/src/composables/usePageTitle.ts +84 -0
- package/src/composables/useSSEBridge.ts +142 -0
- package/src/composables/useSemanticBreadcrumb.ts +222 -0
- package/src/composables/useSidebarState.ts +24 -0
- package/src/composables/useSignals.ts +52 -0
- package/src/composables/useUnsavedChangesGuard.ts +155 -0
- package/src/composables/useUserImpersonator.ts +317 -0
- package/src/composables/useZoneRegistry.ts +252 -0
- package/src/core/bundles.ts +558 -0
- package/src/core/decorator.ts +420 -0
- package/src/core/extension.ts +470 -0
- package/src/core/index.ts +59 -0
- package/src/deferred/DeferredRegistry.ts +353 -0
- package/src/deferred/index.ts +15 -0
- package/src/editors/index.ts +12 -0
- package/src/entity/EntityManager.cache.ts +530 -0
- package/src/entity/EntityManager.crud.ts +615 -0
- package/src/entity/EntityManager.interface.ts +78 -0
- package/src/entity/EntityManager.query.ts +221 -0
- package/src/entity/EntityManager.relations.ts +151 -0
- package/src/entity/EntityManager.ts +944 -0
- package/src/entity/EntityManager.types.ts +288 -0
- package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
- package/src/entity/auth/EntityAuthAdapter.ts +119 -0
- package/src/entity/auth/PermissiveAdapter.ts +43 -0
- package/src/entity/auth/RoleHierarchy.ts +128 -0
- package/src/entity/auth/SecurityChecker.ts +202 -0
- package/src/entity/auth/factory.ts +203 -0
- package/src/entity/auth/index.ts +47 -0
- package/src/entity/factory.test.js +189 -0
- package/src/entity/factory.ts +146 -0
- package/src/entity/index.ts +49 -0
- package/src/entity/storage/ApiStorage.ts +228 -0
- package/src/entity/storage/IStorage.ts +100 -0
- package/src/entity/storage/LocalStorage.ts +200 -0
- package/src/entity/storage/MemoryStorage.ts +184 -0
- package/src/entity/storage/MockApiStorage.ts +318 -0
- package/src/entity/storage/SdkStorage.ts +377 -0
- package/src/entity/storage/factory.test.js +159 -0
- package/src/entity/storage/factory.ts +209 -0
- package/src/entity/storage/index.ts +75 -0
- package/src/gen/FieldMapper.ts +122 -0
- package/src/gen/StorageProfileFactory.ts +93 -0
- package/src/gen/connectors/BaseConnector.ts +150 -0
- package/src/gen/connectors/ManualConnector.test.js +499 -0
- package/src/gen/connectors/ManualConnector.ts +414 -0
- package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
- package/src/gen/connectors/OpenAPIConnector.ts +693 -0
- package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
- package/src/gen/connectors/index.ts +27 -0
- package/src/gen/createManagers.ts +245 -0
- package/src/gen/decorators.ts +134 -0
- package/src/gen/generateManagers.test.js +540 -0
- package/src/gen/generateManagers.ts +398 -0
- package/src/gen/index.ts +66 -0
- package/src/gen/schema.ts +248 -0
- package/src/gen/vite-plugin.ts +114 -0
- package/src/generated/managers/testManager.js +45 -0
- package/src/generated/managers/testManager.ts +46 -0
- package/src/hooks/HookRegistry.ts +16 -0
- package/src/hooks/index.ts +15 -0
- package/src/i18n/I18n.ts +344 -0
- package/src/i18n/IncrementalDomainProvider.ts +153 -0
- package/src/i18n/InlineTranslationProvider.ts +4 -0
- package/src/i18n/LazyTranslationProvider.ts +102 -0
- package/src/i18n/MessagesRegistry.ts +4 -0
- package/src/i18n/Resolver.ts +4 -0
- package/src/i18n/__tests__/I18n.test.ts +169 -0
- package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
- package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
- package/src/i18n/__tests__/Resolver.test.ts +271 -0
- package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
- package/src/i18n/defaults/core.en.yml +55 -0
- package/src/i18n/defaults/core.fr.yml +55 -0
- package/src/i18n/index.ts +55 -0
- package/src/i18n/loaders/raw-modules.d.ts +15 -0
- package/src/i18n/loaders/yaml.ts +35 -0
- package/src/i18n/strategies.ts +4 -0
- package/src/i18n/types.ts +34 -0
- package/src/i18n/useI18n.ts +34 -0
- package/src/index.ts +453 -0
- package/src/kernel/EventRouter.ts +23 -0
- package/src/kernel/Kernel.api.ts +79 -0
- package/src/kernel/Kernel.auth.ts +120 -0
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +170 -0
- package/src/kernel/Kernel.registries.ts +237 -0
- package/src/kernel/Kernel.routing.ts +334 -0
- package/src/kernel/Kernel.ts +460 -0
- package/src/kernel/Kernel.types.ts +263 -0
- package/src/kernel/Kernel.vue.ts +304 -0
- package/src/kernel/KernelContext.entities.ts +80 -0
- package/src/kernel/KernelContext.events.ts +57 -0
- package/src/kernel/KernelContext.i18n.ts +37 -0
- package/src/kernel/KernelContext.permissions.ts +38 -0
- package/src/kernel/KernelContext.routing.ts +280 -0
- package/src/kernel/KernelContext.ts +252 -0
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/Module.ts +169 -0
- package/src/kernel/ModuleLoader.ts +645 -0
- package/src/kernel/SSEBridge.ts +11 -0
- package/src/kernel/SignalBus.ts +48 -0
- package/src/kernel/index.ts +56 -0
- package/src/module/index.ts +33 -0
- package/src/module/moduleRegistry.ts +492 -0
- package/src/modules/debug/AuthCollector.ts +501 -0
- package/src/modules/debug/Collector.ts +20 -0
- package/src/modules/debug/DebugBridge.ts +14 -0
- package/src/modules/debug/DebugModule.ts +220 -0
- package/src/modules/debug/EntitiesCollector.ts +839 -0
- package/src/modules/debug/ErrorCollector.ts +4 -0
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +7 -0
- package/src/modules/debug/RouterCollector.ts +449 -0
- package/src/modules/debug/SignalCollector.ts +4 -0
- package/src/modules/debug/ToastCollector.ts +4 -0
- package/src/modules/debug/ZonesCollector.ts +447 -0
- package/src/modules/debug/components/DebugBar.vue +31 -0
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +9 -0
- package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
- package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
- package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
- package/src/modules/debug/components/panels/index.ts +16 -0
- package/src/modules/debug/index.ts +58 -0
- package/src/modules/debug/styles.scss +2671 -0
- package/src/notifications/NotificationBadge.vue +35 -0
- package/src/notifications/NotificationListener.vue +71 -0
- package/src/notifications/NotificationModule.ts +76 -0
- package/src/notifications/NotificationPanel.vue +478 -0
- package/src/notifications/NotificationStore.ts +288 -0
- package/src/notifications/index.ts +42 -0
- package/src/notifications/styles.scss +32 -0
- package/src/orchestrator/Orchestrator.ts +427 -0
- package/src/orchestrator/index.ts +8 -0
- package/src/orchestrator/useOrchestrator.ts +80 -0
- package/src/plugin.ts +251 -0
- package/src/query/FilterQuery.ts +306 -0
- package/src/query/QueryExecutor.ts +341 -0
- package/src/query/index.ts +16 -0
- package/src/security/EntityRolesProvider.ts +385 -0
- package/src/security/PermissionMatcher.ts +152 -0
- package/src/security/PermissionRegistry.ts +273 -0
- package/src/security/PersistableRolesProvider.ts +584 -0
- package/src/security/RolesManager.ts +136 -0
- package/src/security/RolesProvider.ts +167 -0
- package/src/security/RolesProviderStorage.ts +211 -0
- package/src/security/SecurityModule.ts +94 -0
- package/src/security/StaticRolesProvider.ts +117 -0
- package/src/security/UsersManager.ts +163 -0
- package/src/security/index.ts +77 -0
- package/src/security/pages/RoleForm.vue +276 -0
- package/src/security/pages/RoleList.vue +123 -0
- package/src/styles/_alerts.scss +95 -0
- package/src/styles/_buttons.scss +391 -0
- package/src/styles/_cards.scss +241 -0
- package/src/styles/_code.scss +78 -0
- package/src/styles/_dialogs.scss +55 -0
- package/src/styles/_filter-bar.scss +70 -0
- package/src/styles/_forms.scss +434 -0
- package/src/styles/_helpers.scss +153 -0
- package/src/styles/_lists.scss +225 -0
- package/src/styles/_main.scss +356 -0
- package/src/styles/_markdown.scss +88 -0
- package/src/styles/_responsive.scss +255 -0
- package/src/styles/_show-pages.scss +186 -0
- package/src/styles/_sidebar.scss +380 -0
- package/src/styles/_states.scss +115 -0
- package/src/styles/_stats.scss +47 -0
- package/src/styles/_variables.scss +224 -0
- package/src/styles/index.scss +38 -0
- package/src/styles/theme/_components.scss +331 -0
- package/src/styles/theme/_index.scss +10 -0
- package/src/styles/theme/_tokens.scss +147 -0
- package/src/styles/theme/_utilities.scss +175 -0
- package/src/toast/ToastBridgeModule.ts +68 -0
- package/src/toast/ToastListener.vue +55 -0
- package/src/toast/index.ts +26 -0
- package/src/toast/useSignalToast.ts +154 -0
- package/src/types/index.ts +285 -0
- package/src/types/pluralize.d.ts +19 -0
- package/src/types/quarkernel.d.ts +50 -0
- package/src/utils/formatters.ts +174 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/transformers.ts +131 -0
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +50 -0
- package/src/zones/ZoneRegistry.ts +934 -0
- package/src/zones/index.ts +29 -0
- package/src/zones/zones.ts +191 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useListPage - Type definitions
|
|
3
|
+
*
|
|
4
|
+
* Extracted from useListPage.ts for maintainability.
|
|
5
|
+
*/
|
|
6
|
+
import type { Ref, ComputedRef } from 'vue'
|
|
7
|
+
import type { Router } from 'vue-router'
|
|
8
|
+
import type { FilterQuery } from '../query/FilterQuery'
|
|
9
|
+
import type { ParentConfig, UseEntityItemPageReturn } from './useEntityItemPage.js'
|
|
10
|
+
import type { EntityManagerRead } from '../entity/EntityManager.interface'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Entity manager interface for list pages (re-export for convenience)
|
|
14
|
+
*/
|
|
15
|
+
export type EntityManager = EntityManagerRead
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* List response from API
|
|
19
|
+
*/
|
|
20
|
+
export interface ListResponse<T = unknown> {
|
|
21
|
+
items: T[]
|
|
22
|
+
total?: number
|
|
23
|
+
fromCache?: boolean
|
|
24
|
+
[key: string]: unknown
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Orchestrator interface
|
|
29
|
+
*/
|
|
30
|
+
export interface Orchestrator {
|
|
31
|
+
get: (entityName: string) => EntityManager
|
|
32
|
+
toast: {
|
|
33
|
+
success: (summary: string, detail?: string, emitter?: unknown) => void
|
|
34
|
+
error: (summary: string, detail?: string, emitter?: unknown) => void
|
|
35
|
+
warn: (summary: string, detail?: string, emitter?: unknown) => void
|
|
36
|
+
info: (summary: string, detail?: string, emitter?: unknown) => void
|
|
37
|
+
[key: string]: ((summary: string, detail?: string, emitter?: unknown) => void) | undefined
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Toast helper interface
|
|
43
|
+
*/
|
|
44
|
+
export interface ToastHelper {
|
|
45
|
+
add: (options: {
|
|
46
|
+
severity: 'success' | 'error' | 'warn' | 'info'
|
|
47
|
+
summary: string
|
|
48
|
+
detail?: string
|
|
49
|
+
emitter?: unknown
|
|
50
|
+
life?: number
|
|
51
|
+
}) => void
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* PrimeVue confirm service interface
|
|
56
|
+
*/
|
|
57
|
+
export interface ConfirmService {
|
|
58
|
+
require: (options: {
|
|
59
|
+
message: string
|
|
60
|
+
header: string
|
|
61
|
+
icon: string
|
|
62
|
+
acceptClass?: string
|
|
63
|
+
accept: () => void
|
|
64
|
+
}) => void
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Column configuration
|
|
69
|
+
*/
|
|
70
|
+
export interface ColumnConfig {
|
|
71
|
+
field: string
|
|
72
|
+
header?: string
|
|
73
|
+
sortable?: boolean
|
|
74
|
+
style?: string
|
|
75
|
+
body?: (row: unknown) => unknown
|
|
76
|
+
[key: string]: unknown
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Filter configuration
|
|
81
|
+
*/
|
|
82
|
+
export interface FilterConfig {
|
|
83
|
+
name: string
|
|
84
|
+
type?: 'select' | 'multiselect' | 'date' | 'checkbox' | 'dropdown' | 'autocomplete'
|
|
85
|
+
placeholder?: string
|
|
86
|
+
options?: Array<{ label: string; value: unknown }>
|
|
87
|
+
default?: unknown
|
|
88
|
+
persist?: boolean
|
|
89
|
+
allLabel?: string
|
|
90
|
+
optionsEntity?: string
|
|
91
|
+
optionsEndpoint?: string | boolean
|
|
92
|
+
optionsFromCache?: string | boolean
|
|
93
|
+
optionLabel?: string
|
|
94
|
+
optionValue?: string
|
|
95
|
+
cacheOptions?: boolean | 'auto'
|
|
96
|
+
component?: 'dropdown' | 'autocomplete'
|
|
97
|
+
reference?: string
|
|
98
|
+
processor?: (options: Array<{ label: string; value: unknown }>) => Array<{ label: string; value: unknown }>
|
|
99
|
+
toQuery?: (value: unknown) => Record<string, unknown>
|
|
100
|
+
local_filter?: (item: unknown, value: unknown) => boolean
|
|
101
|
+
_filterQuery?: FilterQuery | unknown
|
|
102
|
+
_cacheOptions?: boolean
|
|
103
|
+
_optionsLoaded?: boolean
|
|
104
|
+
[key: string]: unknown
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Row action configuration
|
|
109
|
+
*/
|
|
110
|
+
export interface ActionConfig {
|
|
111
|
+
name: string
|
|
112
|
+
icon?: string | ((row: unknown) => string)
|
|
113
|
+
tooltip?: string | ((row: unknown) => string)
|
|
114
|
+
severity?: string | ((row: unknown) => string)
|
|
115
|
+
onClick: (row: unknown) => void | Promise<void>
|
|
116
|
+
visible?: (row: unknown) => boolean
|
|
117
|
+
disabled?: (row: unknown) => boolean
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Resolved row action for rendering
|
|
122
|
+
*/
|
|
123
|
+
export interface ResolvedAction {
|
|
124
|
+
name: string
|
|
125
|
+
icon: string | undefined
|
|
126
|
+
tooltip: string | undefined
|
|
127
|
+
severity: string
|
|
128
|
+
isDisabled: boolean
|
|
129
|
+
handler: () => void | Promise<void>
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Header action state context
|
|
134
|
+
*/
|
|
135
|
+
export interface HeaderActionState {
|
|
136
|
+
hasSelection: boolean
|
|
137
|
+
selectionCount: number
|
|
138
|
+
deleting: boolean
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Header action configuration
|
|
143
|
+
*/
|
|
144
|
+
export interface HeaderActionConfig {
|
|
145
|
+
name: string
|
|
146
|
+
label: string | ((state: HeaderActionState) => string)
|
|
147
|
+
icon?: string
|
|
148
|
+
severity?: string
|
|
149
|
+
onClick: () => void | Promise<void>
|
|
150
|
+
visible?: (state: HeaderActionState) => boolean
|
|
151
|
+
loading?: (state: HeaderActionState) => boolean
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Resolved header action for rendering
|
|
156
|
+
*/
|
|
157
|
+
export interface ResolvedHeaderAction extends HeaderActionConfig {
|
|
158
|
+
isLoading: boolean
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Card configuration
|
|
163
|
+
*/
|
|
164
|
+
export interface CardConfig {
|
|
165
|
+
name: string
|
|
166
|
+
[key: string]: unknown
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Search configuration
|
|
171
|
+
*/
|
|
172
|
+
export interface SearchConfig {
|
|
173
|
+
placeholder?: string
|
|
174
|
+
fields?: string[]
|
|
175
|
+
debounce?: number
|
|
176
|
+
local_search?: (item: unknown, query: string) => boolean
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Entity context for multi-storage routing
|
|
181
|
+
*/
|
|
182
|
+
export interface EntityContext {
|
|
183
|
+
parentChain: Array<{ entity: string; id: string | number }>
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Bulk status action return type
|
|
188
|
+
*/
|
|
189
|
+
export interface BulkStatusActionReturn {
|
|
190
|
+
showDialog: Ref<boolean>
|
|
191
|
+
selectedStatus: Ref<unknown>
|
|
192
|
+
updating: Ref<boolean>
|
|
193
|
+
statusOptions: Ref<Array<{ label: string; value: unknown }>>
|
|
194
|
+
dialogTitle: string
|
|
195
|
+
execute: () => Promise<void>
|
|
196
|
+
cancel: () => void
|
|
197
|
+
setOptions: (options: Array<{ label: string; value: unknown }>) => void
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Axios-like error interface
|
|
202
|
+
*/
|
|
203
|
+
export interface AxiosError {
|
|
204
|
+
response?: {
|
|
205
|
+
data?: {
|
|
206
|
+
detail?: string
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
message?: string
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Options for useListPage
|
|
214
|
+
*/
|
|
215
|
+
export interface UseListPageOptions<T = unknown> {
|
|
216
|
+
/** Entity name for EntityManager */
|
|
217
|
+
entity: string
|
|
218
|
+
/** Custom data key field (default: manager.idField) */
|
|
219
|
+
dataKey?: string
|
|
220
|
+
/** Default sort field */
|
|
221
|
+
defaultSort?: string | null
|
|
222
|
+
/** Default sort order (-1 = desc, 1 = asc) */
|
|
223
|
+
defaultSortOrder?: -1 | 1
|
|
224
|
+
/** Enable server-side pagination/sorting */
|
|
225
|
+
serverSide?: boolean
|
|
226
|
+
/** Default page size */
|
|
227
|
+
pageSize?: number
|
|
228
|
+
/** Auto-load on mount (default: true) */
|
|
229
|
+
loadOnMount?: boolean
|
|
230
|
+
/** Persist filters to sessionStorage (default: true) */
|
|
231
|
+
persistFilters?: boolean
|
|
232
|
+
/** Sync filters to URL query params (default: true) */
|
|
233
|
+
syncUrlParams?: boolean
|
|
234
|
+
/** Filter mode: 'auto' | 'manager' | 'local' */
|
|
235
|
+
filterMode?: 'auto' | 'manager' | 'local'
|
|
236
|
+
/** Auto-filter threshold for 'auto' mode */
|
|
237
|
+
autoFilterThreshold?: number
|
|
238
|
+
/** Auto-load filters from registry (default: true) */
|
|
239
|
+
autoLoadFilters?: boolean
|
|
240
|
+
/** Callback before load */
|
|
241
|
+
onBeforeLoad?: ((params: Record<string, unknown>) => Record<string, unknown> | void) | null
|
|
242
|
+
/** Callback after load */
|
|
243
|
+
onAfterLoad?: ((response: ListResponse<T>, processedData: { items: T[]; total: number }) => void) | null
|
|
244
|
+
/** Transform response */
|
|
245
|
+
transformResponse?: ((response: ListResponse<T>) => { items: T[]; total: number }) | null
|
|
246
|
+
/** Override manager.entityName */
|
|
247
|
+
entityName?: string
|
|
248
|
+
/** Override manager.labelPlural */
|
|
249
|
+
entityNamePlural?: string
|
|
250
|
+
/** Override manager.routePrefix */
|
|
251
|
+
routePrefix?: string
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* List page props for ListPage component
|
|
256
|
+
*/
|
|
257
|
+
export interface ListPageProps {
|
|
258
|
+
title: string | undefined
|
|
259
|
+
headerActions: ResolvedHeaderAction[]
|
|
260
|
+
cards: CardConfig[]
|
|
261
|
+
columns: ColumnConfig[]
|
|
262
|
+
items: unknown[]
|
|
263
|
+
loading: boolean
|
|
264
|
+
dataKey: string
|
|
265
|
+
selected: unknown[]
|
|
266
|
+
selectable: boolean
|
|
267
|
+
lazy: boolean
|
|
268
|
+
totalRecords: number
|
|
269
|
+
rows: number
|
|
270
|
+
rowsPerPageOptions: number[]
|
|
271
|
+
sortField: string | null
|
|
272
|
+
sortOrder: number
|
|
273
|
+
searchQuery: string
|
|
274
|
+
searchPlaceholder: string
|
|
275
|
+
filters: FilterConfig[]
|
|
276
|
+
filterValues: Record<string, unknown>
|
|
277
|
+
isFilterAtDefault: (name: string) => boolean
|
|
278
|
+
getActions: (row: unknown) => ResolvedAction[]
|
|
279
|
+
hasRowTapAction: boolean
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* List page events for ListPage component
|
|
284
|
+
*/
|
|
285
|
+
export interface ListPageEvents {
|
|
286
|
+
'update:selected': (value: unknown[]) => void
|
|
287
|
+
'update:searchQuery': (value: string) => void
|
|
288
|
+
'update:filterValues': (values: Record<string, unknown>) => void
|
|
289
|
+
page: (event: { page: number; rows: number }) => void
|
|
290
|
+
sort: (event: { sortField: string; sortOrder: number }) => void
|
|
291
|
+
'row-click': (item: unknown, event: Event) => void
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Create action options
|
|
296
|
+
*/
|
|
297
|
+
export interface CreateActionOptions {
|
|
298
|
+
label?: string
|
|
299
|
+
routeName?: string
|
|
300
|
+
params?: Record<string, unknown>
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Bulk status action options
|
|
305
|
+
*/
|
|
306
|
+
export interface BulkStatusActionOptions {
|
|
307
|
+
statusField?: string
|
|
308
|
+
idsField?: string
|
|
309
|
+
bulkEndpoint?: string | null
|
|
310
|
+
options?: Array<{ label: string; value: unknown }>
|
|
311
|
+
label?: string
|
|
312
|
+
icon?: string
|
|
313
|
+
dialogTitle?: string
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Return type for useListPage
|
|
318
|
+
*/
|
|
319
|
+
export interface UseListPageReturn<T = unknown> {
|
|
320
|
+
// Manager access
|
|
321
|
+
manager: EntityManager
|
|
322
|
+
|
|
323
|
+
// Parent chain (when list is child of parent entity)
|
|
324
|
+
parentConfig: ComputedRef<ParentConfig | null>
|
|
325
|
+
parentId: ComputedRef<string | number | null>
|
|
326
|
+
parentData: ComputedRef<unknown>
|
|
327
|
+
parentLoading: ComputedRef<boolean>
|
|
328
|
+
parentChain: ComputedRef<Map<number, unknown>>
|
|
329
|
+
parentPage: UseEntityItemPageReturn | null
|
|
330
|
+
|
|
331
|
+
// State
|
|
332
|
+
items: Ref<T[]>
|
|
333
|
+
displayItems: ComputedRef<T[]>
|
|
334
|
+
loading: Ref<boolean>
|
|
335
|
+
selected: Ref<T[]>
|
|
336
|
+
deleting: Ref<boolean>
|
|
337
|
+
|
|
338
|
+
// Pagination
|
|
339
|
+
page: Ref<number>
|
|
340
|
+
pageSize: Ref<number>
|
|
341
|
+
totalRecords: Ref<number>
|
|
342
|
+
rowsPerPageOptions: number[]
|
|
343
|
+
sortField: Ref<string | null>
|
|
344
|
+
sortOrder: Ref<number>
|
|
345
|
+
onPage: (event: { page: number; rows: number }) => void
|
|
346
|
+
onSort: (event: { sortField: string; sortOrder: number }) => void
|
|
347
|
+
|
|
348
|
+
// Search
|
|
349
|
+
searchQuery: Ref<string>
|
|
350
|
+
searchConfig: Ref<SearchConfig>
|
|
351
|
+
setSearch: (config: Partial<SearchConfig>) => void
|
|
352
|
+
|
|
353
|
+
// Columns
|
|
354
|
+
columns: ComputedRef<ColumnConfig[]>
|
|
355
|
+
addColumn: (field: string, config?: Partial<ColumnConfig>) => void
|
|
356
|
+
removeColumn: (field: string) => void
|
|
357
|
+
updateColumn: (field: string, updates: Partial<ColumnConfig>) => void
|
|
358
|
+
|
|
359
|
+
// Header Actions
|
|
360
|
+
headerActions: ComputedRef<ResolvedHeaderAction[]>
|
|
361
|
+
addHeaderAction: (name: string, config: Omit<HeaderActionConfig, 'name'>) => void
|
|
362
|
+
removeHeaderAction: (name: string) => void
|
|
363
|
+
getHeaderActions: () => ResolvedHeaderAction[]
|
|
364
|
+
addCreateAction: (labelOrOptions?: string | CreateActionOptions | null) => void
|
|
365
|
+
addBulkDeleteAction: () => void
|
|
366
|
+
addBulkStatusAction: (options?: BulkStatusActionOptions) => BulkStatusActionReturn
|
|
367
|
+
hasBulkActions: ComputedRef<boolean>
|
|
368
|
+
|
|
369
|
+
// Cards
|
|
370
|
+
cards: ComputedRef<CardConfig[]>
|
|
371
|
+
addCard: (name: string, config: Omit<CardConfig, 'name'>) => void
|
|
372
|
+
updateCard: (name: string, config: Partial<CardConfig>) => void
|
|
373
|
+
removeCard: (name: string) => void
|
|
374
|
+
|
|
375
|
+
// Filters
|
|
376
|
+
filters: ComputedRef<FilterConfig[]>
|
|
377
|
+
filterValues: Ref<Record<string, unknown>>
|
|
378
|
+
filteredItems: ComputedRef<T[]>
|
|
379
|
+
fromCache: Ref<boolean>
|
|
380
|
+
addFilter: (name: string, config: Omit<FilterConfig, 'name'>) => void
|
|
381
|
+
removeFilter: (name: string) => void
|
|
382
|
+
setFilterValue: (name: string, value: unknown) => void
|
|
383
|
+
updateFilters: (values: Record<string, unknown>) => void
|
|
384
|
+
clearFilters: () => void
|
|
385
|
+
isFilterAtDefault: (name: string) => boolean
|
|
386
|
+
hasActiveFilters: ComputedRef<boolean>
|
|
387
|
+
loadFilterOptions: () => Promise<void>
|
|
388
|
+
initFromRegistry: () => void
|
|
389
|
+
restoreFilters: () => void
|
|
390
|
+
|
|
391
|
+
// Actions
|
|
392
|
+
addAction: (name: string, config: Omit<ActionConfig, 'name'>) => void
|
|
393
|
+
removeAction: (name: string) => void
|
|
394
|
+
getActions: (row: unknown) => ResolvedAction[]
|
|
395
|
+
getRowActions: (row: unknown) => ResolvedAction[]
|
|
396
|
+
addViewAction: (options?: Partial<ActionConfig>) => void
|
|
397
|
+
addEditAction: (options?: Partial<ActionConfig>) => void
|
|
398
|
+
addDeleteAction: (options?: Partial<ActionConfig> & { labelField?: string }) => void
|
|
399
|
+
|
|
400
|
+
// Permissions
|
|
401
|
+
canCreate: ComputedRef<boolean>
|
|
402
|
+
canDelete: ComputedRef<boolean>
|
|
403
|
+
canEditRow: (row: unknown) => boolean
|
|
404
|
+
canDeleteRow: (row: unknown) => boolean
|
|
405
|
+
|
|
406
|
+
// Data
|
|
407
|
+
loadItems: (extraParams?: Record<string, unknown>, options?: { force?: boolean }) => Promise<void>
|
|
408
|
+
|
|
409
|
+
// Navigation
|
|
410
|
+
goToCreate: () => void
|
|
411
|
+
goToEdit: (item: unknown) => void
|
|
412
|
+
goToShow: (item: unknown) => void
|
|
413
|
+
|
|
414
|
+
// Mobile row tap
|
|
415
|
+
isMobile: Ref<boolean>
|
|
416
|
+
hasRowTapAction: ComputedRef<boolean>
|
|
417
|
+
getPrimaryRowAction: () => 'edit' | 'show' | null
|
|
418
|
+
onRowClick: (item: unknown, event: Event) => void
|
|
419
|
+
|
|
420
|
+
// Delete
|
|
421
|
+
deleteItem: (item: unknown, labelField?: string) => Promise<void>
|
|
422
|
+
confirmDelete: (item: unknown, labelField?: string) => void
|
|
423
|
+
bulkDelete: () => Promise<void>
|
|
424
|
+
confirmBulkDelete: () => void
|
|
425
|
+
hasSelection: ComputedRef<boolean>
|
|
426
|
+
selectionCount: ComputedRef<number>
|
|
427
|
+
|
|
428
|
+
// Utilities
|
|
429
|
+
formatDate: (dateStr: string | null | undefined, options?: Intl.DateTimeFormatOptions) => string
|
|
430
|
+
toast: ToastHelper
|
|
431
|
+
confirm: ConfirmService
|
|
432
|
+
router: Router
|
|
433
|
+
|
|
434
|
+
// ListPage integration
|
|
435
|
+
props: ComputedRef<ListPageProps>
|
|
436
|
+
events: ListPageEvents
|
|
437
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useListPage helpers — stateless utilities used by the list page composable.
|
|
3
|
+
*
|
|
4
|
+
* Kept module-local so they're trivially importable from the orchestrator
|
|
5
|
+
* file (`useListPage.ts`) and from any future split modules without dragging
|
|
6
|
+
* the full `useListPage` runtime context along.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// Pagination cookie persistence
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
const COOKIE_NAME = 'qdadm_pageSize'
|
|
14
|
+
const COOKIE_EXPIRY_DAYS = 365
|
|
15
|
+
|
|
16
|
+
export function getCookie(name: string): string | null {
|
|
17
|
+
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
|
|
18
|
+
return match?.[2] ?? null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function setCookie(name: string, value: string | number, days: number): void {
|
|
22
|
+
const expires = new Date(Date.now() + days * 864e5).toUTCString()
|
|
23
|
+
document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read the saved page size from cookie. Returns the default when absent or
|
|
28
|
+
* not in the canonical {@link PAGE_SIZE_OPTIONS} list.
|
|
29
|
+
*/
|
|
30
|
+
export function getSavedPageSize(defaultSize: number): number {
|
|
31
|
+
const saved = getCookie(COOKIE_NAME)
|
|
32
|
+
if (saved) {
|
|
33
|
+
const parsed = parseInt(saved, 10)
|
|
34
|
+
if (PAGE_SIZE_OPTIONS.includes(parsed)) return parsed
|
|
35
|
+
}
|
|
36
|
+
return defaultSize
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function persistPageSize(size: number): void {
|
|
40
|
+
setCookie(COOKIE_NAME, size, COOKIE_EXPIRY_DAYS)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
44
|
+
// Default label formatter (snake_case → Title Case)
|
|
45
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
export function snakeToTitle(str: string): string {
|
|
48
|
+
if (!str) return 'Unknown'
|
|
49
|
+
return String(str)
|
|
50
|
+
.split('_')
|
|
51
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
52
|
+
.join(' ')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
56
|
+
// Pagination options
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
/** Canonical page size options offered to the user. */
|
|
60
|
+
export const PAGE_SIZE_OPTIONS = [10, 50, 100]
|
|
61
|
+
|
|
62
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
63
|
+
// Filter session storage
|
|
64
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
const FILTER_SESSION_PREFIX = 'qdadm_filters_'
|
|
67
|
+
|
|
68
|
+
export function getSessionFilters(key: string): Record<string, unknown> | null {
|
|
69
|
+
try {
|
|
70
|
+
const stored = sessionStorage.getItem(FILTER_SESSION_PREFIX + key)
|
|
71
|
+
return stored ? JSON.parse(stored) : null
|
|
72
|
+
} catch {
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function setSessionFilters(key: string, filters: Record<string, unknown>): void {
|
|
78
|
+
try {
|
|
79
|
+
sessionStorage.setItem(FILTER_SESSION_PREFIX + key, JSON.stringify(filters))
|
|
80
|
+
} catch {
|
|
81
|
+
// Ignore storage errors (quota exceeded, disabled by user, …).
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function clearSessionFilters(key: string): void {
|
|
86
|
+
try {
|
|
87
|
+
sessionStorage.removeItem(FILTER_SESSION_PREFIX + key)
|
|
88
|
+
} catch {
|
|
89
|
+
// Ignore storage errors.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
94
|
+
// Smart filter discovery
|
|
95
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Threshold below which the list switches to client-side filtering when
|
|
99
|
+
* `filterMode: 'auto'` is in effect.
|
|
100
|
+
*/
|
|
101
|
+
export const SMART_FILTER_THRESHOLD = 50
|