@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,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useChildPage - Composable for non-entity child pages on entity items
|
|
3
|
+
*
|
|
4
|
+
* Provides parent entity context for custom child pages registered via ctx.childPage().
|
|
5
|
+
* The parent data is loaded automatically via the StackHydrator (triggered by the
|
|
6
|
+
* Kernel's activeStack rebuild from route.meta.parent).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // In a child page component (e.g., BookInfo.vue)
|
|
10
|
+
* const { parentData, parentLoading, parentManager } = useChildPage()
|
|
11
|
+
*
|
|
12
|
+
* // parentData is reactive - null while loading, then the parent entity record
|
|
13
|
+
* // parentManager gives access to the parent EntityManager (labels, fields, etc.)
|
|
14
|
+
*/
|
|
15
|
+
import { computed, inject, type ComputedRef } from 'vue'
|
|
16
|
+
import { useRoute } from 'vue-router'
|
|
17
|
+
import { useStackHydrator, type StackHydratorReturn } from '../chain/useStackHydrator'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parent config from route meta
|
|
21
|
+
*/
|
|
22
|
+
interface ParentConfig {
|
|
23
|
+
entity: string
|
|
24
|
+
param: string
|
|
25
|
+
foreignKey?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Entity manager interface (subset used by child pages)
|
|
30
|
+
*/
|
|
31
|
+
interface EntityManager {
|
|
32
|
+
idField: string
|
|
33
|
+
label?: string
|
|
34
|
+
labelPlural?: string
|
|
35
|
+
routePrefix?: string
|
|
36
|
+
getEntityLabel: (data: unknown) => string
|
|
37
|
+
getEntityBadges?: (data: unknown) => Array<{ label: string; severity?: string }>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Orchestrator interface
|
|
42
|
+
*/
|
|
43
|
+
interface Orchestrator {
|
|
44
|
+
get: (entityName: string) => EntityManager | null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Return type for useChildPage
|
|
49
|
+
*/
|
|
50
|
+
export interface UseChildPageReturn {
|
|
51
|
+
/** Parent config from route.meta.parent */
|
|
52
|
+
parentConfig: ComputedRef<ParentConfig | null>
|
|
53
|
+
/** Parent entity name */
|
|
54
|
+
parentEntity: ComputedRef<string | null>
|
|
55
|
+
/** Parent entity ID from route params */
|
|
56
|
+
parentId: ComputedRef<string | null>
|
|
57
|
+
/** Hydrated parent entity data (null while loading) */
|
|
58
|
+
parentData: ComputedRef<unknown | null>
|
|
59
|
+
/** Whether parent data is still loading */
|
|
60
|
+
parentLoading: ComputedRef<boolean>
|
|
61
|
+
/** Parent entity manager */
|
|
62
|
+
parentManager: ComputedRef<EntityManager | null>
|
|
63
|
+
/** Stack hydrator for advanced usage */
|
|
64
|
+
hydrator: StackHydratorReturn
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function useChildPage(): UseChildPageReturn {
|
|
68
|
+
const route = useRoute()
|
|
69
|
+
const hydrator = useStackHydrator()
|
|
70
|
+
const orchestrator = inject<Orchestrator | null>('qdadmOrchestrator', null)
|
|
71
|
+
|
|
72
|
+
const parentConfig = computed<ParentConfig | null>(
|
|
73
|
+
() => (route.meta?.parent as ParentConfig) || null
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const parentEntity = computed(() => parentConfig.value?.entity || null)
|
|
77
|
+
|
|
78
|
+
const parentId = computed(() => {
|
|
79
|
+
if (!parentConfig.value) return null
|
|
80
|
+
return (route.params[parentConfig.value.param] as string) || null
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const parentData = computed(() => {
|
|
84
|
+
if (!parentEntity.value || !parentId.value) return null
|
|
85
|
+
const level = hydrator.levels.value.find(
|
|
86
|
+
(l) => l.entity === parentEntity.value && String(l.id) === String(parentId.value)
|
|
87
|
+
)
|
|
88
|
+
return level?.data || null
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
const parentLoading = computed(() => {
|
|
92
|
+
if (!parentEntity.value || !parentId.value) return false
|
|
93
|
+
const level = hydrator.levels.value.find(
|
|
94
|
+
(l) => l.entity === parentEntity.value && String(l.id) === String(parentId.value)
|
|
95
|
+
)
|
|
96
|
+
return level ? !level.hydrated : true
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const parentManager = computed(() => {
|
|
100
|
+
if (!parentEntity.value) return null
|
|
101
|
+
return orchestrator?.get(parentEntity.value) || null
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
parentConfig,
|
|
106
|
+
parentEntity,
|
|
107
|
+
parentId,
|
|
108
|
+
parentData,
|
|
109
|
+
parentLoading,
|
|
110
|
+
parentManager,
|
|
111
|
+
hydrator,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Use useStackHydrator() instead
|
|
3
|
+
*
|
|
4
|
+
* Legacy composable for setting breadcrumb entity data.
|
|
5
|
+
* This has been replaced by the activeStack + stackHydrator system.
|
|
6
|
+
*
|
|
7
|
+
* Migration:
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Before (deprecated)
|
|
10
|
+
* const { setBreadcrumbEntity } = useCurrentEntity()
|
|
11
|
+
* setBreadcrumbEntity(data)
|
|
12
|
+
*
|
|
13
|
+
* // After
|
|
14
|
+
* const hydrator = useStackHydrator()
|
|
15
|
+
* hydrator.setCurrentData(data)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { useStackHydrator } from '../chain/useStackHydrator'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Return type for useCurrentEntity
|
|
22
|
+
*/
|
|
23
|
+
export interface UseCurrentEntityReturn {
|
|
24
|
+
/** @deprecated Use hydrator.setCurrentData() instead */
|
|
25
|
+
setBreadcrumbEntity: (data: unknown, level?: number) => void
|
|
26
|
+
/** @deprecated Use hydrator.setCurrentData() instead */
|
|
27
|
+
setCurrentEntity: (data: unknown) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Use useStackHydrator() instead
|
|
32
|
+
*/
|
|
33
|
+
export function useCurrentEntity(): UseCurrentEntityReturn {
|
|
34
|
+
console.warn(
|
|
35
|
+
'[qdadm] useCurrentEntity is deprecated. Use useStackHydrator() instead.'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const hydrator = useStackHydrator()
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated Use hydrator.setCurrentData() instead
|
|
42
|
+
*/
|
|
43
|
+
function setBreadcrumbEntity(data: unknown, level = 1): void {
|
|
44
|
+
if (level === 1) {
|
|
45
|
+
hydrator.setCurrentData(data)
|
|
46
|
+
} else {
|
|
47
|
+
// For parent levels, use setEntityData with entity name
|
|
48
|
+
// But we don't have entity name here - just set current
|
|
49
|
+
hydrator.setCurrentData(data)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Backwards compat alias
|
|
54
|
+
const setCurrentEntity = (data: unknown): void => setBreadcrumbEntity(data, 1)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
setBreadcrumbEntity,
|
|
58
|
+
setCurrentEntity,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useDeferred - Access the DeferredRegistry from components
|
|
3
|
+
*
|
|
4
|
+
* Provides loose async coupling between services and components.
|
|
5
|
+
* Components can await dependencies without knowing when they're loaded.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { useDeferred } from '@quazardous/qdadm/composables'
|
|
10
|
+
*
|
|
11
|
+
* // In a component
|
|
12
|
+
* const deferred = useDeferred()
|
|
13
|
+
*
|
|
14
|
+
* // Await a single dependency
|
|
15
|
+
* const config = await deferred.await('config')
|
|
16
|
+
*
|
|
17
|
+
* // Await multiple dependencies
|
|
18
|
+
* const [users, settings] = await Promise.all([
|
|
19
|
+
* deferred.await('users-service'),
|
|
20
|
+
* deferred.await('settings')
|
|
21
|
+
* ])
|
|
22
|
+
*
|
|
23
|
+
* // Check status without waiting
|
|
24
|
+
* if (deferred.isSettled('config')) {
|
|
25
|
+
* const config = deferred.value('config')
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { inject, ref, type Ref } from 'vue'
|
|
31
|
+
import type { DeferredRegistry } from '../deferred/DeferredRegistry'
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Injection key for DeferredRegistry
|
|
35
|
+
*/
|
|
36
|
+
export const DEFERRED_INJECTION_KEY = 'qdadmDeferred'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the DeferredRegistry instance
|
|
40
|
+
* @returns The DeferredRegistry instance
|
|
41
|
+
*/
|
|
42
|
+
export function useDeferred(): DeferredRegistry {
|
|
43
|
+
const deferred = inject<DeferredRegistry | null>(DEFERRED_INJECTION_KEY)
|
|
44
|
+
|
|
45
|
+
if (!deferred) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'[useDeferred] DeferredRegistry not found. ' +
|
|
48
|
+
'Make sure you are using the Kernel to bootstrap your app.'
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return deferred
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Return type for useDeferredValue
|
|
57
|
+
*/
|
|
58
|
+
export interface UseDeferredValueReturn<T> {
|
|
59
|
+
data: Ref<T | null>
|
|
60
|
+
loading: Ref<boolean>
|
|
61
|
+
error: Ref<Error | null>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Helper: await a deferred value in setup
|
|
66
|
+
* Returns a ref that updates when the deferred resolves
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const { data: config, loading, error } = useDeferredValue('config')
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @param key - Deferred key
|
|
74
|
+
* @returns Object with data, loading, and error refs
|
|
75
|
+
*/
|
|
76
|
+
export function useDeferredValue<T = unknown>(key: string): UseDeferredValueReturn<T> {
|
|
77
|
+
const deferred = useDeferred()
|
|
78
|
+
const data = ref<T | null>(null) as Ref<T | null>
|
|
79
|
+
const loading = ref(true)
|
|
80
|
+
const error = ref<Error | null>(null)
|
|
81
|
+
|
|
82
|
+
deferred
|
|
83
|
+
.await<T>(key)
|
|
84
|
+
.then((value) => {
|
|
85
|
+
data.value = value
|
|
86
|
+
loading.value = false
|
|
87
|
+
})
|
|
88
|
+
.catch((err: Error) => {
|
|
89
|
+
error.value = err
|
|
90
|
+
loading.value = false
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return { data, loading, error }
|
|
94
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable for tracking form dirty state
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* const { dirty, dirtyFields, isFieldDirty, takeSnapshot, checkDirty } = useDirtyState(() => ({
|
|
6
|
+
* form: formData.value
|
|
7
|
+
* }))
|
|
8
|
+
*
|
|
9
|
+
* // Take snapshot after loading data
|
|
10
|
+
* takeSnapshot()
|
|
11
|
+
*
|
|
12
|
+
* // Watch for changes
|
|
13
|
+
* watch(formData, checkDirty, { deep: true })
|
|
14
|
+
*
|
|
15
|
+
* // Check if specific field is dirty
|
|
16
|
+
* isFieldDirty('username') // true/false
|
|
17
|
+
*
|
|
18
|
+
* // Get all dirty field names
|
|
19
|
+
* dirtyFields.value // ['username', 'email']
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { ref, nextTick, type Ref } from 'vue'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* State getter function type
|
|
26
|
+
*/
|
|
27
|
+
export type StateGetter<T = Record<string, unknown>> = () => T
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Form state type with optional form/formData keys
|
|
31
|
+
*/
|
|
32
|
+
export interface FormState {
|
|
33
|
+
form?: Record<string, unknown>
|
|
34
|
+
formData?: Record<string, unknown>
|
|
35
|
+
[key: string]: unknown
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Return type for useDirtyState
|
|
40
|
+
*/
|
|
41
|
+
export interface UseDirtyStateReturn {
|
|
42
|
+
dirty: Ref<boolean>
|
|
43
|
+
dirtyFields: Ref<string[]>
|
|
44
|
+
ready: Ref<boolean>
|
|
45
|
+
takeSnapshot: () => void
|
|
46
|
+
checkDirty: () => void
|
|
47
|
+
isFieldDirty: (fieldName: string) => boolean
|
|
48
|
+
reset: () => void
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function useDirtyState<T extends FormState = FormState>(
|
|
52
|
+
getState: StateGetter<T>
|
|
53
|
+
): UseDirtyStateReturn {
|
|
54
|
+
const dirty = ref(false)
|
|
55
|
+
const dirtyFields = ref<string[]>([])
|
|
56
|
+
const initialSnapshot = ref<string | null>(null)
|
|
57
|
+
const initialState = ref<T | null>(null)
|
|
58
|
+
const ready = ref(false)
|
|
59
|
+
|
|
60
|
+
function getFormSnapshot(): string {
|
|
61
|
+
return JSON.stringify(getState())
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function takeSnapshot(): void {
|
|
65
|
+
// Disable dirty checking temporarily to prevent watchers from re-setting dirty
|
|
66
|
+
ready.value = false
|
|
67
|
+
dirty.value = false
|
|
68
|
+
dirtyFields.value = []
|
|
69
|
+
|
|
70
|
+
// Use nextTick to ensure all reactive updates have settled for snapshot
|
|
71
|
+
nextTick(() => {
|
|
72
|
+
const state = getState()
|
|
73
|
+
initialSnapshot.value = JSON.stringify(state)
|
|
74
|
+
// Store raw state for field-level comparison
|
|
75
|
+
initialState.value = JSON.parse(JSON.stringify(state)) as T
|
|
76
|
+
ready.value = true
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function checkDirty(): void {
|
|
81
|
+
// Don't check until snapshot is taken and ready
|
|
82
|
+
if (!ready.value || !initialSnapshot.value) return
|
|
83
|
+
|
|
84
|
+
const currentSnapshot = getFormSnapshot()
|
|
85
|
+
dirty.value = currentSnapshot !== initialSnapshot.value
|
|
86
|
+
|
|
87
|
+
// Calculate dirty fields
|
|
88
|
+
if (dirty.value && initialState.value) {
|
|
89
|
+
const current = getState()
|
|
90
|
+
const changed: string[] = []
|
|
91
|
+
|
|
92
|
+
// Compare form fields if state has a 'form' or 'formData' key (common patterns)
|
|
93
|
+
const initial =
|
|
94
|
+
(initialState.value.form as Record<string, unknown>) ||
|
|
95
|
+
(initialState.value.formData as Record<string, unknown>) ||
|
|
96
|
+
(initialState.value as Record<string, unknown>)
|
|
97
|
+
const currentForm =
|
|
98
|
+
(current.form as Record<string, unknown>) ||
|
|
99
|
+
(current.formData as Record<string, unknown>) ||
|
|
100
|
+
(current as Record<string, unknown>)
|
|
101
|
+
|
|
102
|
+
for (const key in currentForm) {
|
|
103
|
+
const initialVal = JSON.stringify(initial[key])
|
|
104
|
+
const currentVal = JSON.stringify(currentForm[key])
|
|
105
|
+
if (initialVal !== currentVal) {
|
|
106
|
+
changed.push(key)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
dirtyFields.value = changed
|
|
110
|
+
} else {
|
|
111
|
+
dirtyFields.value = []
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isFieldDirty(fieldName: string): boolean {
|
|
116
|
+
return dirtyFields.value.includes(fieldName)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function reset(): void {
|
|
120
|
+
dirty.value = false
|
|
121
|
+
dirtyFields.value = []
|
|
122
|
+
initialSnapshot.value = null
|
|
123
|
+
initialState.value = null
|
|
124
|
+
ready.value = false
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
dirty,
|
|
129
|
+
dirtyFields,
|
|
130
|
+
ready,
|
|
131
|
+
takeSnapshot,
|
|
132
|
+
checkDirty,
|
|
133
|
+
isFieldDirty,
|
|
134
|
+
reset,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export default useDirtyState
|