@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.
Files changed (291) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/package.json +101 -0
  4. package/src/assets/logo.svg +6 -0
  5. package/src/auth/SessionAuthAdapter.ts +459 -0
  6. package/src/auth/index.ts +15 -0
  7. package/src/chain/ActiveStack.ts +154 -0
  8. package/src/chain/StackHydrator.ts +292 -0
  9. package/src/chain/index.ts +25 -0
  10. package/src/chain/useActiveStack.ts +120 -0
  11. package/src/chain/useStackHydrator.ts +155 -0
  12. package/src/components/BoolCell.vue +34 -0
  13. package/src/components/InfoBanner.vue +73 -0
  14. package/src/components/QdadmRoot.vue +52 -0
  15. package/src/components/SeverityTag.vue +86 -0
  16. package/src/components/dialogs/BulkStatusDialog.vue +60 -0
  17. package/src/components/dialogs/MultiStepDialog.vue +331 -0
  18. package/src/components/dialogs/SimpleDialog.vue +122 -0
  19. package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
  20. package/src/components/display/BannerZone.vue +65 -0
  21. package/src/components/display/CardsGrid.vue +72 -0
  22. package/src/components/display/CopyableId.vue +84 -0
  23. package/src/components/display/EmptyState.vue +62 -0
  24. package/src/components/display/IntensityBar.vue +190 -0
  25. package/src/components/display/RichCardsGrid.vue +101 -0
  26. package/src/components/edit/FormActions.vue +78 -0
  27. package/src/components/edit/FormField.vue +82 -0
  28. package/src/components/edit/FormInput.vue +158 -0
  29. package/src/components/edit/FormPage.vue +260 -0
  30. package/src/components/edit/FormTab.vue +217 -0
  31. package/src/components/edit/FormTabs.vue +120 -0
  32. package/src/components/edit/LookupField.vue +406 -0
  33. package/src/components/edit/LookupPickerDialog.vue +230 -0
  34. package/src/components/editors/JsonEditorFoldable.vue +470 -0
  35. package/src/components/editors/JsonStructuredField.vue +264 -0
  36. package/src/components/editors/JsonViewer.vue +51 -0
  37. package/src/components/editors/KeyValueEditor.vue +314 -0
  38. package/src/components/editors/LanguageEditor.vue +259 -0
  39. package/src/components/editors/PermissionEditor.vue +558 -0
  40. package/src/components/editors/ScopeEditor.vue +372 -0
  41. package/src/components/editors/VanillaJsonEditor.vue +183 -0
  42. package/src/components/index.ts +83 -0
  43. package/src/components/item/FieldGroups.vue +392 -0
  44. package/src/components/layout/AppLayout.vue +1032 -0
  45. package/src/components/layout/BaseLayout.vue +190 -0
  46. package/src/components/layout/Breadcrumb.vue +55 -0
  47. package/src/components/layout/DashboardLayout.vue +104 -0
  48. package/src/components/layout/FormLayout.vue +256 -0
  49. package/src/components/layout/ListLayout.vue +278 -0
  50. package/src/components/layout/PageHeader.vue +156 -0
  51. package/src/components/layout/PageLayout.vue +126 -0
  52. package/src/components/layout/PageNav.vue +192 -0
  53. package/src/components/layout/SidebarBox.vue +87 -0
  54. package/src/components/layout/Zone.vue +182 -0
  55. package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
  56. package/src/components/layout/defaults/DefaultFooter.vue +89 -0
  57. package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
  58. package/src/components/layout/defaults/DefaultHeader.vue +78 -0
  59. package/src/components/layout/defaults/DefaultMenu.vue +188 -0
  60. package/src/components/layout/defaults/DefaultPagination.vue +75 -0
  61. package/src/components/layout/defaults/DefaultTable.vue +137 -0
  62. package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
  63. package/src/components/layout/defaults/index.ts +16 -0
  64. package/src/components/lists/ActionButtons.vue +49 -0
  65. package/src/components/lists/ActionColumn.vue +40 -0
  66. package/src/components/lists/FilterBar.vue +54 -0
  67. package/src/components/lists/ListPage.vue +467 -0
  68. package/src/components/pages/LoginPage.vue +334 -0
  69. package/src/components/pages/NotFoundPage.vue +98 -0
  70. package/src/components/show/ShowDisplay.vue +343 -0
  71. package/src/components/show/ShowField.vue +67 -0
  72. package/src/components/show/ShowPage.vue +431 -0
  73. package/src/composables/index.ts +155 -0
  74. package/src/composables/useApp.ts +67 -0
  75. package/src/composables/useAuth.ts +98 -0
  76. package/src/composables/useBareForm.ts +319 -0
  77. package/src/composables/useBreadcrumb.ts +252 -0
  78. package/src/composables/useChildPage.ts +113 -0
  79. package/src/composables/useCurrentEntity.ts +60 -0
  80. package/src/composables/useDeferred.ts +94 -0
  81. package/src/composables/useDirtyState.ts +138 -0
  82. package/src/composables/useEntityItemFormPage.ts +942 -0
  83. package/src/composables/useEntityItemFormPage.types.ts +451 -0
  84. package/src/composables/useEntityItemPage.ts +438 -0
  85. package/src/composables/useEntityItemShowPage.ts +722 -0
  86. package/src/composables/useFieldManager.ts +692 -0
  87. package/src/composables/useForm.ts +544 -0
  88. package/src/composables/useGuardStore.ts +53 -0
  89. package/src/composables/useHooks.ts +56 -0
  90. package/src/composables/useInfoBanner.ts +217 -0
  91. package/src/composables/useJsonSyntax.ts +122 -0
  92. package/src/composables/useLayoutResolver.ts +329 -0
  93. package/src/composables/useListPage.ts +1535 -0
  94. package/src/composables/useListPage.types.ts +437 -0
  95. package/src/composables/useListPage.utils.ts +101 -0
  96. package/src/composables/useNavContext.ts +439 -0
  97. package/src/composables/useNavigation.ts +216 -0
  98. package/src/composables/useOptionsLookup.ts +323 -0
  99. package/src/composables/usePageTitle.ts +84 -0
  100. package/src/composables/useSSEBridge.ts +142 -0
  101. package/src/composables/useSemanticBreadcrumb.ts +222 -0
  102. package/src/composables/useSidebarState.ts +24 -0
  103. package/src/composables/useSignals.ts +52 -0
  104. package/src/composables/useUnsavedChangesGuard.ts +155 -0
  105. package/src/composables/useUserImpersonator.ts +317 -0
  106. package/src/composables/useZoneRegistry.ts +252 -0
  107. package/src/core/bundles.ts +558 -0
  108. package/src/core/decorator.ts +420 -0
  109. package/src/core/extension.ts +470 -0
  110. package/src/core/index.ts +59 -0
  111. package/src/deferred/DeferredRegistry.ts +353 -0
  112. package/src/deferred/index.ts +15 -0
  113. package/src/editors/index.ts +12 -0
  114. package/src/entity/EntityManager.cache.ts +530 -0
  115. package/src/entity/EntityManager.crud.ts +615 -0
  116. package/src/entity/EntityManager.interface.ts +78 -0
  117. package/src/entity/EntityManager.query.ts +221 -0
  118. package/src/entity/EntityManager.relations.ts +151 -0
  119. package/src/entity/EntityManager.ts +944 -0
  120. package/src/entity/EntityManager.types.ts +288 -0
  121. package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
  122. package/src/entity/auth/EntityAuthAdapter.ts +119 -0
  123. package/src/entity/auth/PermissiveAdapter.ts +43 -0
  124. package/src/entity/auth/RoleHierarchy.ts +128 -0
  125. package/src/entity/auth/SecurityChecker.ts +202 -0
  126. package/src/entity/auth/factory.ts +203 -0
  127. package/src/entity/auth/index.ts +47 -0
  128. package/src/entity/factory.test.js +189 -0
  129. package/src/entity/factory.ts +146 -0
  130. package/src/entity/index.ts +49 -0
  131. package/src/entity/storage/ApiStorage.ts +228 -0
  132. package/src/entity/storage/IStorage.ts +100 -0
  133. package/src/entity/storage/LocalStorage.ts +200 -0
  134. package/src/entity/storage/MemoryStorage.ts +184 -0
  135. package/src/entity/storage/MockApiStorage.ts +318 -0
  136. package/src/entity/storage/SdkStorage.ts +377 -0
  137. package/src/entity/storage/factory.test.js +159 -0
  138. package/src/entity/storage/factory.ts +209 -0
  139. package/src/entity/storage/index.ts +75 -0
  140. package/src/gen/FieldMapper.ts +122 -0
  141. package/src/gen/StorageProfileFactory.ts +93 -0
  142. package/src/gen/connectors/BaseConnector.ts +150 -0
  143. package/src/gen/connectors/ManualConnector.test.js +499 -0
  144. package/src/gen/connectors/ManualConnector.ts +414 -0
  145. package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
  146. package/src/gen/connectors/OpenAPIConnector.ts +693 -0
  147. package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
  148. package/src/gen/connectors/index.ts +27 -0
  149. package/src/gen/createManagers.ts +245 -0
  150. package/src/gen/decorators.ts +134 -0
  151. package/src/gen/generateManagers.test.js +540 -0
  152. package/src/gen/generateManagers.ts +398 -0
  153. package/src/gen/index.ts +66 -0
  154. package/src/gen/schema.ts +248 -0
  155. package/src/gen/vite-plugin.ts +114 -0
  156. package/src/generated/managers/testManager.js +45 -0
  157. package/src/generated/managers/testManager.ts +46 -0
  158. package/src/hooks/HookRegistry.ts +16 -0
  159. package/src/hooks/index.ts +15 -0
  160. package/src/i18n/I18n.ts +344 -0
  161. package/src/i18n/IncrementalDomainProvider.ts +153 -0
  162. package/src/i18n/InlineTranslationProvider.ts +4 -0
  163. package/src/i18n/LazyTranslationProvider.ts +102 -0
  164. package/src/i18n/MessagesRegistry.ts +4 -0
  165. package/src/i18n/Resolver.ts +4 -0
  166. package/src/i18n/__tests__/I18n.test.ts +169 -0
  167. package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
  168. package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
  169. package/src/i18n/__tests__/Resolver.test.ts +271 -0
  170. package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
  171. package/src/i18n/defaults/core.en.yml +55 -0
  172. package/src/i18n/defaults/core.fr.yml +55 -0
  173. package/src/i18n/index.ts +55 -0
  174. package/src/i18n/loaders/raw-modules.d.ts +15 -0
  175. package/src/i18n/loaders/yaml.ts +35 -0
  176. package/src/i18n/strategies.ts +4 -0
  177. package/src/i18n/types.ts +34 -0
  178. package/src/i18n/useI18n.ts +34 -0
  179. package/src/index.ts +453 -0
  180. package/src/kernel/EventRouter.ts +23 -0
  181. package/src/kernel/Kernel.api.ts +79 -0
  182. package/src/kernel/Kernel.auth.ts +120 -0
  183. package/src/kernel/Kernel.i18n.ts +29 -0
  184. package/src/kernel/Kernel.modules.ts +170 -0
  185. package/src/kernel/Kernel.registries.ts +237 -0
  186. package/src/kernel/Kernel.routing.ts +334 -0
  187. package/src/kernel/Kernel.ts +460 -0
  188. package/src/kernel/Kernel.types.ts +263 -0
  189. package/src/kernel/Kernel.vue.ts +304 -0
  190. package/src/kernel/KernelContext.entities.ts +80 -0
  191. package/src/kernel/KernelContext.events.ts +57 -0
  192. package/src/kernel/KernelContext.i18n.ts +37 -0
  193. package/src/kernel/KernelContext.permissions.ts +38 -0
  194. package/src/kernel/KernelContext.routing.ts +280 -0
  195. package/src/kernel/KernelContext.ts +252 -0
  196. package/src/kernel/KernelContext.types.ts +173 -0
  197. package/src/kernel/KernelContext.zones.ts +54 -0
  198. package/src/kernel/Module.ts +169 -0
  199. package/src/kernel/ModuleLoader.ts +645 -0
  200. package/src/kernel/SSEBridge.ts +11 -0
  201. package/src/kernel/SignalBus.ts +48 -0
  202. package/src/kernel/index.ts +56 -0
  203. package/src/module/index.ts +33 -0
  204. package/src/module/moduleRegistry.ts +492 -0
  205. package/src/modules/debug/AuthCollector.ts +501 -0
  206. package/src/modules/debug/Collector.ts +20 -0
  207. package/src/modules/debug/DebugBridge.ts +14 -0
  208. package/src/modules/debug/DebugModule.ts +220 -0
  209. package/src/modules/debug/EntitiesCollector.ts +839 -0
  210. package/src/modules/debug/ErrorCollector.ts +4 -0
  211. package/src/modules/debug/I18nCollector.ts +9 -0
  212. package/src/modules/debug/LocalStorageAdapter.ts +7 -0
  213. package/src/modules/debug/RouterCollector.ts +449 -0
  214. package/src/modules/debug/SignalCollector.ts +4 -0
  215. package/src/modules/debug/ToastCollector.ts +4 -0
  216. package/src/modules/debug/ZonesCollector.ts +447 -0
  217. package/src/modules/debug/components/DebugBar.vue +31 -0
  218. package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
  219. package/src/modules/debug/components/index.ts +9 -0
  220. package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
  221. package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
  222. package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
  223. package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
  224. package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
  225. package/src/modules/debug/components/panels/index.ts +16 -0
  226. package/src/modules/debug/index.ts +58 -0
  227. package/src/modules/debug/styles.scss +2671 -0
  228. package/src/notifications/NotificationBadge.vue +35 -0
  229. package/src/notifications/NotificationListener.vue +71 -0
  230. package/src/notifications/NotificationModule.ts +76 -0
  231. package/src/notifications/NotificationPanel.vue +478 -0
  232. package/src/notifications/NotificationStore.ts +288 -0
  233. package/src/notifications/index.ts +42 -0
  234. package/src/notifications/styles.scss +32 -0
  235. package/src/orchestrator/Orchestrator.ts +427 -0
  236. package/src/orchestrator/index.ts +8 -0
  237. package/src/orchestrator/useOrchestrator.ts +80 -0
  238. package/src/plugin.ts +251 -0
  239. package/src/query/FilterQuery.ts +306 -0
  240. package/src/query/QueryExecutor.ts +341 -0
  241. package/src/query/index.ts +16 -0
  242. package/src/security/EntityRolesProvider.ts +385 -0
  243. package/src/security/PermissionMatcher.ts +152 -0
  244. package/src/security/PermissionRegistry.ts +273 -0
  245. package/src/security/PersistableRolesProvider.ts +584 -0
  246. package/src/security/RolesManager.ts +136 -0
  247. package/src/security/RolesProvider.ts +167 -0
  248. package/src/security/RolesProviderStorage.ts +211 -0
  249. package/src/security/SecurityModule.ts +94 -0
  250. package/src/security/StaticRolesProvider.ts +117 -0
  251. package/src/security/UsersManager.ts +163 -0
  252. package/src/security/index.ts +77 -0
  253. package/src/security/pages/RoleForm.vue +276 -0
  254. package/src/security/pages/RoleList.vue +123 -0
  255. package/src/styles/_alerts.scss +95 -0
  256. package/src/styles/_buttons.scss +391 -0
  257. package/src/styles/_cards.scss +241 -0
  258. package/src/styles/_code.scss +78 -0
  259. package/src/styles/_dialogs.scss +55 -0
  260. package/src/styles/_filter-bar.scss +70 -0
  261. package/src/styles/_forms.scss +434 -0
  262. package/src/styles/_helpers.scss +153 -0
  263. package/src/styles/_lists.scss +225 -0
  264. package/src/styles/_main.scss +356 -0
  265. package/src/styles/_markdown.scss +88 -0
  266. package/src/styles/_responsive.scss +255 -0
  267. package/src/styles/_show-pages.scss +186 -0
  268. package/src/styles/_sidebar.scss +380 -0
  269. package/src/styles/_states.scss +115 -0
  270. package/src/styles/_stats.scss +47 -0
  271. package/src/styles/_variables.scss +224 -0
  272. package/src/styles/index.scss +38 -0
  273. package/src/styles/theme/_components.scss +331 -0
  274. package/src/styles/theme/_index.scss +10 -0
  275. package/src/styles/theme/_tokens.scss +147 -0
  276. package/src/styles/theme/_utilities.scss +175 -0
  277. package/src/toast/ToastBridgeModule.ts +68 -0
  278. package/src/toast/ToastListener.vue +55 -0
  279. package/src/toast/index.ts +26 -0
  280. package/src/toast/useSignalToast.ts +154 -0
  281. package/src/types/index.ts +285 -0
  282. package/src/types/pluralize.d.ts +19 -0
  283. package/src/types/quarkernel.d.ts +50 -0
  284. package/src/utils/formatters.ts +174 -0
  285. package/src/utils/index.ts +35 -0
  286. package/src/utils/transformers.ts +131 -0
  287. package/src/vite/qdadmDebugPlugin.ts +401 -0
  288. package/src/vite-env.d.ts +50 -0
  289. package/src/zones/ZoneRegistry.ts +934 -0
  290. package/src/zones/index.ts +29 -0
  291. 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