@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,217 @@
1
+ /**
2
+ * useInfoBanner - Programmatic banner management
3
+ *
4
+ * Manages persistent info banners that appear at the top of page content.
5
+ * Unlike toasts, banners stay visible until explicitly dismissed.
6
+ *
7
+ * @example
8
+ * // In a component
9
+ * const { showBanner, hideBanner, clearBanners } = useInfoBanner()
10
+ *
11
+ * // Show a warning banner
12
+ * showBanner({
13
+ * id: 'unsaved',
14
+ * severity: 'warn',
15
+ * message: 'You have unsaved changes',
16
+ * closable: true
17
+ * })
18
+ *
19
+ * // Hide a specific banner
20
+ * hideBanner('unsaved')
21
+ *
22
+ * // Clear all banners
23
+ * clearBanners()
24
+ *
25
+ * @example
26
+ * // With custom icon
27
+ * showBanner({
28
+ * id: 'offline',
29
+ * severity: 'error',
30
+ * icon: 'pi-wifi',
31
+ * message: 'You are offline. Changes will be saved when connection is restored.'
32
+ * })
33
+ *
34
+ * @example
35
+ * // In layout - get banners to render
36
+ * const { banners } = useInfoBanner()
37
+ *
38
+ * <InfoBanner
39
+ * v-for="banner in banners"
40
+ * :key="banner.id"
41
+ * :severity="banner.severity"
42
+ * :icon="banner.icon"
43
+ * :closable="banner.closable"
44
+ * @close="hideBanner(banner.id)"
45
+ * >
46
+ * {{ banner.message }}
47
+ * </InfoBanner>
48
+ */
49
+ import { inject, provide, reactive, computed, type ComputedRef, type InjectionKey } from 'vue'
50
+
51
+ const BANNER_KEY: InjectionKey<BannerStore> = Symbol('qdadm-banners')
52
+
53
+ /**
54
+ * Banner severity types
55
+ */
56
+ export type BannerSeverity = 'info' | 'success' | 'warn' | 'error'
57
+
58
+ /**
59
+ * Banner configuration options
60
+ */
61
+ export interface BannerOptions {
62
+ /** Unique identifier (replaces existing with same id) */
63
+ id?: string
64
+ /** Banner severity: info, success, warn, error */
65
+ severity?: BannerSeverity
66
+ /** Banner message (can include HTML) */
67
+ message?: string
68
+ /** Custom icon or false to hide */
69
+ icon?: string | boolean
70
+ /** Allow user to dismiss (default: true) */
71
+ closable?: boolean
72
+ /** Higher priority banners appear first */
73
+ priority?: number
74
+ }
75
+
76
+ /**
77
+ * Internal banner state
78
+ */
79
+ export interface Banner {
80
+ id: string
81
+ severity: BannerSeverity
82
+ message: string
83
+ icon?: string | boolean
84
+ closable: boolean
85
+ priority: number
86
+ createdAt: number
87
+ }
88
+
89
+ /**
90
+ * Banner store state
91
+ */
92
+ interface BannerState {
93
+ banners: Banner[]
94
+ }
95
+
96
+ /**
97
+ * Banner store interface
98
+ */
99
+ export interface BannerStore {
100
+ banners: ComputedRef<Banner[]>
101
+ showBanner: (options: BannerOptions) => string
102
+ hideBanner: (id: string) => void
103
+ clearBanners: () => void
104
+ hasBanner: (id: string) => boolean
105
+ }
106
+
107
+ /**
108
+ * Create the banner store (call once in app root)
109
+ */
110
+ export function createBannerStore(): BannerStore {
111
+ const state = reactive<BannerState>({
112
+ banners: [],
113
+ })
114
+
115
+ /**
116
+ * Show a banner
117
+ * @param options - Banner options
118
+ */
119
+ function showBanner(options: BannerOptions): string {
120
+ const banner: Banner = {
121
+ id: options.id || `banner-${Date.now()}`,
122
+ severity: options.severity || 'info',
123
+ message: options.message || '',
124
+ icon: options.icon,
125
+ closable: options.closable !== false,
126
+ priority: options.priority || 0,
127
+ createdAt: Date.now(),
128
+ }
129
+
130
+ // Remove existing banner with same id
131
+ const existingIndex = state.banners.findIndex((b) => b.id === banner.id)
132
+ if (existingIndex !== -1) {
133
+ state.banners.splice(existingIndex, 1)
134
+ }
135
+
136
+ // Insert by priority (higher first)
137
+ const insertIndex = state.banners.findIndex((b) => b.priority < banner.priority)
138
+ if (insertIndex === -1) {
139
+ state.banners.push(banner)
140
+ } else {
141
+ state.banners.splice(insertIndex, 0, banner)
142
+ }
143
+
144
+ return banner.id
145
+ }
146
+
147
+ /**
148
+ * Hide a specific banner
149
+ * @param id - Banner id to remove
150
+ */
151
+ function hideBanner(id: string): void {
152
+ const index = state.banners.findIndex((b) => b.id === id)
153
+ if (index !== -1) {
154
+ state.banners.splice(index, 1)
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Clear all banners
160
+ */
161
+ function clearBanners(): void {
162
+ state.banners.length = 0
163
+ }
164
+
165
+ /**
166
+ * Check if a banner exists
167
+ * @param id - Banner id
168
+ * @returns True if banner exists
169
+ */
170
+ function hasBanner(id: string): boolean {
171
+ return state.banners.some((b) => b.id === id)
172
+ }
173
+
174
+ const banners = computed(() => state.banners)
175
+
176
+ return {
177
+ banners,
178
+ showBanner,
179
+ hideBanner,
180
+ clearBanners,
181
+ hasBanner,
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Provide the banner store to descendant components
187
+ * Call this in your app root or layout component
188
+ */
189
+ export function provideBannerStore(): BannerStore {
190
+ const store = createBannerStore()
191
+ provide(BANNER_KEY, store)
192
+ return store
193
+ }
194
+
195
+ /**
196
+ * Use the banner system in a component
197
+ * @returns Banner API
198
+ */
199
+ export function useInfoBanner(): BannerStore {
200
+ const store = inject(BANNER_KEY)
201
+
202
+ if (!store) {
203
+ console.warn(
204
+ '[qdadm] useInfoBanner: banner store not provided. Call provideBannerStore() in app root.'
205
+ )
206
+ // Return a no-op store to prevent crashes
207
+ return {
208
+ banners: computed(() => []),
209
+ showBanner: () => '',
210
+ hideBanner: () => {},
211
+ clearBanners: () => {},
212
+ hasBanner: () => false,
213
+ }
214
+ }
215
+
216
+ return store
217
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * useJsonSyntax - Shared JSON syntax highlighting utilities
3
+ *
4
+ * Provides consistent JSON formatting and syntax highlighting
5
+ * across JsonEditor, JsonViewer, and JsonEditorFoldable components.
6
+ */
7
+
8
+ /**
9
+ * JSON value type enumeration
10
+ */
11
+ export type JsonValueType = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object'
12
+
13
+ /**
14
+ * Result of safe JSON parsing
15
+ */
16
+ export interface SafeJsonParseResult<T = unknown> {
17
+ value: T | null
18
+ error: string | null
19
+ }
20
+
21
+ /**
22
+ * Apply syntax highlighting to JSON string
23
+ * @param jsonText - Raw JSON text
24
+ * @returns HTML with syntax highlighting spans
25
+ */
26
+ export function highlightJson(jsonText: string): string {
27
+ if (!jsonText) {
28
+ return ''
29
+ }
30
+
31
+ return (
32
+ jsonText
33
+ .replace(/&/g, '&amp;')
34
+ .replace(/</g, '&lt;')
35
+ .replace(/>/g, '&gt;')
36
+ // Strings (values only - after colon or in arrays)
37
+ .replace(/("(?:[^"\\]|\\.)*")(?=\s*[,\]}]|\s*$)/gm, '<span class="json-string">$1</span>')
38
+ // Keys (before colon)
39
+ .replace(/("(?:[^"\\]|\\.)*")(\s*:)/g, '<span class="json-key">$1</span>$2')
40
+ // Numbers
41
+ .replace(/\b(-?\d+\.?\d*)\b/g, '<span class="json-number">$1</span>')
42
+ // Booleans
43
+ .replace(/\b(true|false)\b/g, '<span class="json-boolean">$1</span>')
44
+ // Null
45
+ .replace(/\bnull\b/g, '<span class="json-null">null</span>')
46
+ )
47
+ }
48
+
49
+ /**
50
+ * Get the type of a JSON value for display
51
+ * @param value - The value to check
52
+ * @returns Type name: 'string', 'number', 'boolean', 'null', 'array', 'object'
53
+ */
54
+ export function getJsonValueType(value: unknown): JsonValueType {
55
+ if (value === null) return 'null'
56
+ if (Array.isArray(value)) return 'array'
57
+ return typeof value as JsonValueType
58
+ }
59
+
60
+ /**
61
+ * Get a short preview of a JSON value
62
+ * @param value - The value to preview
63
+ * @param maxLength - Maximum length for string previews
64
+ * @returns Short preview text
65
+ */
66
+ export function getJsonPreview(value: unknown, maxLength = 50): string {
67
+ const type = getJsonValueType(value)
68
+
69
+ switch (type) {
70
+ case 'string': {
71
+ const strValue = value as string
72
+ const preview = strValue.substring(0, maxLength)
73
+ return preview + (strValue.length > maxLength ? '...' : '')
74
+ }
75
+ case 'array': {
76
+ const arrValue = value as unknown[]
77
+ return `[${arrValue.length} item${arrValue.length !== 1 ? 's' : ''}]`
78
+ }
79
+ case 'object': {
80
+ const objValue = value as Record<string, unknown>
81
+ const keyCount = Object.keys(objValue).length
82
+ return `{${keyCount} key${keyCount !== 1 ? 's' : ''}}`
83
+ }
84
+ case 'null':
85
+ return 'null'
86
+ case 'boolean':
87
+ return String(value)
88
+ case 'number':
89
+ return String(value)
90
+ default:
91
+ return String(value)
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Safely parse JSON with error handling
97
+ * @param text - JSON text to parse
98
+ * @returns Parse result with value and error
99
+ */
100
+ export function safeJsonParse<T = unknown>(text: string): SafeJsonParseResult<T> {
101
+ if (!text || !text.trim()) {
102
+ return { value: null, error: null }
103
+ }
104
+ try {
105
+ return { value: JSON.parse(text) as T, error: null }
106
+ } catch (e) {
107
+ return { value: null, error: (e as Error).message }
108
+ }
109
+ }
110
+
111
+ /**
112
+ * CSS class names for JSON syntax highlighting
113
+ * Import these in your component's style section
114
+ */
115
+ export const JSON_SYNTAX_CLASSES = `
116
+ .json-key { color: #881391; }
117
+ .json-string { color: #1a1aa6; }
118
+ .json-number { color: #1c00cf; }
119
+ .json-boolean { color: #0d22aa; font-weight: 500; }
120
+ .json-null { color: #808080; font-style: italic; }
121
+ .json-placeholder { color: var(--p-surface-400); }
122
+ `
@@ -0,0 +1,329 @@
1
+ /**
2
+ * useLayoutResolver - Automatic layout resolution for entity pages
3
+ *
4
+ * Resolves the appropriate layout component based on:
5
+ * 1. Route meta: `{ meta: { layout: 'list' } }` (highest priority)
6
+ * 2. Page name convention: `*ListPage` -> ListLayout, `*EditPage` -> FormLayout
7
+ * 3. Explicit prop: `layout="list"` passed to component
8
+ *
9
+ * Layout types:
10
+ * - 'list' -> ListLayout (entity list pages)
11
+ * - 'form' -> FormLayout (create/edit pages)
12
+ * - 'dashboard' -> DashboardLayout (dashboard pages)
13
+ * - 'base' -> BaseLayout (generic pages)
14
+ *
15
+ * Usage:
16
+ * ```ts
17
+ * const { layoutComponent, layoutType, resolveLayout } = useLayoutResolver()
18
+ *
19
+ * // In template:
20
+ * <component :is="layoutComponent">
21
+ * <template #main>...</template>
22
+ * </component>
23
+ * ```
24
+ *
25
+ * Or with explicit layout prop:
26
+ * ```ts
27
+ * const { layoutComponent } = useLayoutResolver({ layout: 'list' })
28
+ * ```
29
+ */
30
+ import { computed, inject, getCurrentInstance, type ComputedRef, type Component } from 'vue'
31
+ import { useRoute } from 'vue-router'
32
+
33
+ // Layout type constants
34
+ export const LAYOUT_TYPES = {
35
+ LIST: 'list',
36
+ FORM: 'form',
37
+ DASHBOARD: 'dashboard',
38
+ BASE: 'base',
39
+ } as const
40
+
41
+ export type LayoutType = (typeof LAYOUT_TYPES)[keyof typeof LAYOUT_TYPES]
42
+
43
+ /**
44
+ * Layout components map
45
+ */
46
+ export interface LayoutComponentsMap {
47
+ list?: Component | null
48
+ form?: Component | null
49
+ dashboard?: Component | null
50
+ base?: Component | null
51
+ [key: string]: Component | null | undefined
52
+ }
53
+
54
+ /**
55
+ * Page name pattern for layout detection
56
+ */
57
+ interface PageNamePattern {
58
+ pattern: RegExp
59
+ layout: LayoutType
60
+ }
61
+
62
+ // Page name patterns for auto-detection
63
+ const PAGE_NAME_PATTERNS: PageNamePattern[] = [
64
+ { pattern: /List(Page)?$/i, layout: LAYOUT_TYPES.LIST },
65
+ { pattern: /(Edit|Create|Form)(Page)?$/i, layout: LAYOUT_TYPES.FORM },
66
+ { pattern: /Dashboard(Page)?$/i, layout: LAYOUT_TYPES.DASHBOARD },
67
+ ]
68
+
69
+ // Route name patterns for auto-detection
70
+ const ROUTE_NAME_PATTERNS: PageNamePattern[] = [
71
+ { pattern: /-list$/i, layout: LAYOUT_TYPES.LIST },
72
+ { pattern: /-(edit|create)$/i, layout: LAYOUT_TYPES.FORM },
73
+ { pattern: /-dashboard$/i, layout: LAYOUT_TYPES.DASHBOARD },
74
+ // Common patterns without suffix
75
+ { pattern: /^dashboard$/i, layout: LAYOUT_TYPES.DASHBOARD },
76
+ ]
77
+
78
+ /**
79
+ * Resolve layout type from component name
80
+ * @param componentName - Vue component name
81
+ * @returns Layout type or null
82
+ */
83
+ function resolveFromComponentName(componentName: string | null | undefined): LayoutType | null {
84
+ if (!componentName) return null
85
+
86
+ for (const { pattern, layout } of PAGE_NAME_PATTERNS) {
87
+ if (pattern.test(componentName)) {
88
+ return layout
89
+ }
90
+ }
91
+ return null
92
+ }
93
+
94
+ /**
95
+ * Resolve layout type from route name
96
+ * @param routeName - Route name
97
+ * @returns Layout type or null
98
+ */
99
+ function resolveFromRouteName(routeName: string | null | undefined): LayoutType | null {
100
+ if (!routeName) return null
101
+
102
+ for (const { pattern, layout } of ROUTE_NAME_PATTERNS) {
103
+ if (pattern.test(routeName)) {
104
+ return layout
105
+ }
106
+ }
107
+ return null
108
+ }
109
+
110
+ /**
111
+ * Options for useLayoutResolver
112
+ */
113
+ export interface UseLayoutResolverOptions {
114
+ /** Explicit layout type override */
115
+ layout?: LayoutType
116
+ /** Default layout if none detected (default: 'base') */
117
+ default?: LayoutType
118
+ }
119
+
120
+ /**
121
+ * Resolution context for resolveLayout
122
+ */
123
+ export interface LayoutResolutionContext {
124
+ routeMeta?: { layout?: LayoutType }
125
+ componentName?: string
126
+ routeName?: string
127
+ explicit?: LayoutType
128
+ }
129
+
130
+ /**
131
+ * Return type for useLayoutResolver
132
+ */
133
+ export interface UseLayoutResolverReturn {
134
+ /** Current resolved layout type */
135
+ layoutType: ComputedRef<LayoutType>
136
+ /** Layout component for current type (if injected) */
137
+ layoutComponent: ComputedRef<Component | null>
138
+ /** Check if specific layout type is active */
139
+ isLayout: (type: LayoutType) => boolean
140
+ /** Manually resolve layout for given context */
141
+ resolveLayout: (context?: LayoutResolutionContext) => LayoutType
142
+ /** Layout type constants */
143
+ LAYOUT_TYPES: typeof LAYOUT_TYPES
144
+ }
145
+
146
+ /**
147
+ * useLayoutResolver composable
148
+ *
149
+ * @param options - Configuration options
150
+ * @returns Layout resolution utilities
151
+ */
152
+ export function useLayoutResolver(options: UseLayoutResolverOptions = {}): UseLayoutResolverReturn {
153
+ const route = useRoute()
154
+ const instance = getCurrentInstance()
155
+
156
+ // Get layout components from injection (provided by Kernel)
157
+ const layoutComponents = inject<LayoutComponentsMap | null>('qdadmLayoutComponents', null)
158
+
159
+ /**
160
+ * Resolve the layout type based on priority:
161
+ * 1. Explicit option.layout
162
+ * 2. Route meta.layout
163
+ * 3. Component name pattern
164
+ * 4. Route name pattern
165
+ * 5. Default
166
+ */
167
+ const layoutType = computed((): LayoutType => {
168
+ // Priority 1: Explicit layout prop/option
169
+ if (options.layout) {
170
+ return options.layout
171
+ }
172
+
173
+ // Priority 2: Route meta.layout
174
+ if (route.meta?.layout) {
175
+ return route.meta.layout as LayoutType
176
+ }
177
+
178
+ // Priority 3: Component name pattern
179
+ const componentName =
180
+ (instance?.type as { name?: string; __name?: string })?.name ||
181
+ (instance?.type as { name?: string; __name?: string })?.__name
182
+ const fromComponentName = resolveFromComponentName(componentName)
183
+ if (fromComponentName) {
184
+ return fromComponentName
185
+ }
186
+
187
+ // Priority 4: Route name pattern
188
+ const fromRouteName = resolveFromRouteName(route.name as string | undefined)
189
+ if (fromRouteName) {
190
+ return fromRouteName
191
+ }
192
+
193
+ // Priority 5: Default
194
+ return options.default || LAYOUT_TYPES.BASE
195
+ })
196
+
197
+ /**
198
+ * Get the layout component for the resolved type
199
+ */
200
+ const layoutComponent = computed((): Component | null => {
201
+ if (!layoutComponents) {
202
+ // No components injected, return null (caller handles fallback)
203
+ return null
204
+ }
205
+
206
+ const type = layoutType.value
207
+ return layoutComponents[type] || layoutComponents.base || null
208
+ })
209
+
210
+ /**
211
+ * Check if a specific layout type is active
212
+ * @param type - Layout type to check
213
+ * @returns True if layout type matches
214
+ */
215
+ function isLayout(type: LayoutType): boolean {
216
+ return layoutType.value === type
217
+ }
218
+
219
+ /**
220
+ * Manually resolve layout for a given context
221
+ * Useful for programmatic layout determination
222
+ *
223
+ * @param context - Resolution context
224
+ * @returns Resolved layout type
225
+ */
226
+ function resolveLayout(context: LayoutResolutionContext = {}): LayoutType {
227
+ // Priority 1: Explicit
228
+ if (context.explicit) {
229
+ return context.explicit
230
+ }
231
+
232
+ // Priority 2: Route meta
233
+ if (context.routeMeta?.layout) {
234
+ return context.routeMeta.layout
235
+ }
236
+
237
+ // Priority 3: Component name
238
+ const fromComponent = resolveFromComponentName(context.componentName)
239
+ if (fromComponent) {
240
+ return fromComponent
241
+ }
242
+
243
+ // Priority 4: Route name
244
+ const fromRoute = resolveFromRouteName(context.routeName)
245
+ if (fromRoute) {
246
+ return fromRoute
247
+ }
248
+
249
+ // Default
250
+ return options.default || LAYOUT_TYPES.BASE
251
+ }
252
+
253
+ return {
254
+ layoutType,
255
+ layoutComponent,
256
+ isLayout,
257
+ resolveLayout,
258
+ LAYOUT_TYPES,
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Layout components input for createLayoutComponents
264
+ */
265
+ export interface LayoutComponentsInput {
266
+ list?: Component
267
+ form?: Component
268
+ dashboard?: Component
269
+ base?: Component
270
+ ListLayout?: Component
271
+ FormLayout?: Component
272
+ DashboardLayout?: Component
273
+ BaseLayout?: Component
274
+ }
275
+
276
+ /**
277
+ * Create layout components map for Kernel injection
278
+ *
279
+ * @param components - Layout component imports
280
+ * @returns Layout components map
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * import ListLayout from './components/layout/ListLayout.vue'
285
+ * import FormLayout from './components/layout/FormLayout.vue'
286
+ *
287
+ * const layouts = createLayoutComponents({
288
+ * list: ListLayout,
289
+ * form: FormLayout
290
+ * })
291
+ * ```
292
+ */
293
+ export function createLayoutComponents(components: LayoutComponentsInput = {}): LayoutComponentsMap {
294
+ return {
295
+ [LAYOUT_TYPES.LIST]: components.list || components.ListLayout || null,
296
+ [LAYOUT_TYPES.FORM]: components.form || components.FormLayout || null,
297
+ [LAYOUT_TYPES.DASHBOARD]: components.dashboard || components.DashboardLayout || null,
298
+ [LAYOUT_TYPES.BASE]: components.base || components.BaseLayout || null,
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Route meta object with layout
304
+ */
305
+ export interface LayoutMeta {
306
+ layout: LayoutType
307
+ }
308
+
309
+ /**
310
+ * Get route meta configuration for layout
311
+ *
312
+ * Helper to add layout meta to route definitions
313
+ *
314
+ * @param layoutType - Layout type
315
+ * @returns Route meta object
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * {
320
+ * path: 'books',
321
+ * name: 'book-list',
322
+ * component: BookList,
323
+ * meta: layoutMeta('list')
324
+ * }
325
+ * ```
326
+ */
327
+ export function layoutMeta(layoutType: LayoutType): LayoutMeta {
328
+ return { layout: layoutType }
329
+ }