@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,288 @@
1
+ /**
2
+ * NotificationStore - Reactive store for notification panel
3
+ *
4
+ * Manages captured toast notifications and custom status items.
5
+ * Uses Vue provide/inject pattern (same as useInfoBanner).
6
+ *
7
+ * @example
8
+ * // In app root (Kernel handles this automatically)
9
+ * const store = provideNotificationStore()
10
+ *
11
+ * // In any component
12
+ * const { notifications, unreadCount, addNotification } = useNotifications()
13
+ *
14
+ * // Register a custom status item from a module
15
+ * const { registerStatus } = useNotifications()
16
+ * registerStatus({ id: 'overdue', label: '3 books overdue', severity: 'warn', count: 3 })
17
+ */
18
+ import {
19
+ inject,
20
+ provide,
21
+ reactive,
22
+ computed,
23
+ ref,
24
+ type ComputedRef,
25
+ type Ref,
26
+ type InjectionKey,
27
+ } from 'vue'
28
+
29
+ const NOTIFICATION_KEY: InjectionKey<NotificationStore> = Symbol('qdadm-notifications')
30
+
31
+ // ─────────────────────────────────────────────────────────────────────────────
32
+ // Types
33
+ // ─────────────────────────────────────────────────────────────────────────────
34
+
35
+ export type NotificationSeverity = 'success' | 'info' | 'warn' | 'error'
36
+
37
+ export interface Notification {
38
+ id: string
39
+ severity: NotificationSeverity
40
+ summary: string
41
+ detail?: string
42
+ emitter?: string
43
+ timestamp: number
44
+ read: boolean
45
+ }
46
+
47
+ export interface StatusItem {
48
+ id: string
49
+ label: string
50
+ severity: 'nominal' | 'warn' | 'error'
51
+ count?: number
52
+ icon?: string
53
+ /** Route location for navigation (vue-router LocationAsRelativeRaw) */
54
+ to?: { name: string; params?: Record<string, unknown>; query?: Record<string, unknown> }
55
+ }
56
+
57
+ export interface NotificationStoreConfig {
58
+ maxNotifications?: number
59
+ }
60
+
61
+ export interface NotificationStore {
62
+ // Notifications (captured toasts)
63
+ notifications: ComputedRef<Notification[]>
64
+ unreadCount: ComputedRef<number>
65
+ hasAlert: ComputedRef<boolean>
66
+ addNotification(n: Omit<Notification, 'id' | 'timestamp' | 'read'>): string
67
+ markRead(id: string): void
68
+ markAllRead(): void
69
+ removeNotification(id: string): void
70
+ clearNotifications(): void
71
+
72
+ // Status items (custom module items)
73
+ statusItems: ComputedRef<StatusItem[]>
74
+ registerStatus(item: StatusItem): void
75
+ updateStatus(id: string, updates: Partial<StatusItem>): void
76
+ removeStatus(id: string): void
77
+
78
+ // Panel state
79
+ isOpen: Ref<boolean>
80
+ open(): void
81
+ close(): void
82
+ toggle(): void
83
+ }
84
+
85
+ // ─────────────────────────────────────────────────────────────────────────────
86
+ // Internal state
87
+ // ─────────────────────────────────────────────────────────────────────────────
88
+
89
+ interface NotificationState {
90
+ notifications: Notification[]
91
+ statusItems: StatusItem[]
92
+ }
93
+
94
+ let _idCounter = 0
95
+
96
+ // ─────────────────────────────────────────────────────────────────────────────
97
+ // Factory
98
+ // ─────────────────────────────────────────────────────────────────────────────
99
+
100
+ /**
101
+ * Create a notification store instance
102
+ */
103
+ export function createNotificationStore(config: NotificationStoreConfig = {}): NotificationStore {
104
+ const maxNotifications = config.maxNotifications ?? 50
105
+
106
+ const state = reactive<NotificationState>({
107
+ notifications: [],
108
+ statusItems: [],
109
+ })
110
+
111
+ const isOpen = ref(false)
112
+
113
+ // ── Notifications ──────────────────────────────────────────────────────
114
+
115
+ function addNotification(n: Omit<Notification, 'id' | 'timestamp' | 'read'>): string {
116
+ const id = `notif-${++_idCounter}-${Date.now()}`
117
+ const notification: Notification = {
118
+ ...n,
119
+ id,
120
+ timestamp: Date.now(),
121
+ read: false,
122
+ }
123
+
124
+ // Add to front (most recent first)
125
+ state.notifications.unshift(notification)
126
+
127
+ // Trim to max
128
+ if (state.notifications.length > maxNotifications) {
129
+ state.notifications.length = maxNotifications
130
+ }
131
+
132
+ return id
133
+ }
134
+
135
+ function markRead(id: string): void {
136
+ const notif = state.notifications.find((n) => n.id === id)
137
+ if (notif) {
138
+ notif.read = true
139
+ }
140
+ }
141
+
142
+ function markAllRead(): void {
143
+ for (const notif of state.notifications) {
144
+ notif.read = true
145
+ }
146
+ }
147
+
148
+ function removeNotification(id: string): void {
149
+ const index = state.notifications.findIndex((n) => n.id === id)
150
+ if (index !== -1) {
151
+ state.notifications.splice(index, 1)
152
+ }
153
+ }
154
+
155
+ function clearNotifications(): void {
156
+ state.notifications.length = 0
157
+ }
158
+
159
+ // ── Status items ───────────────────────────────────────────────────────
160
+
161
+ function registerStatus(item: StatusItem): void {
162
+ const existing = state.statusItems.findIndex((s) => s.id === item.id)
163
+ if (existing !== -1) {
164
+ state.statusItems.splice(existing, 1, item)
165
+ } else {
166
+ state.statusItems.push(item)
167
+ }
168
+ }
169
+
170
+ function updateStatus(id: string, updates: Partial<StatusItem>): void {
171
+ const item = state.statusItems.find((s) => s.id === id)
172
+ if (item) {
173
+ Object.assign(item, updates)
174
+ }
175
+ }
176
+
177
+ function removeStatus(id: string): void {
178
+ const index = state.statusItems.findIndex((s) => s.id === id)
179
+ if (index !== -1) {
180
+ state.statusItems.splice(index, 1)
181
+ }
182
+ }
183
+
184
+ // ── Panel state ────────────────────────────────────────────────────────
185
+
186
+ function open(): void {
187
+ isOpen.value = true
188
+ }
189
+
190
+ function close(): void {
191
+ isOpen.value = false
192
+ }
193
+
194
+ function toggle(): void {
195
+ isOpen.value = !isOpen.value
196
+ }
197
+
198
+ // ── Computed ───────────────────────────────────────────────────────────
199
+
200
+ const notifications = computed(() => state.notifications)
201
+ const statusItems = computed(() => state.statusItems)
202
+
203
+ const unreadCount = computed(() =>
204
+ state.notifications.filter((n) => !n.read).length
205
+ )
206
+
207
+ const hasAlert = computed(() => {
208
+ // Unread error/warn notifications
209
+ const hasUnreadAlert = state.notifications.some(
210
+ (n) => !n.read && (n.severity === 'error' || n.severity === 'warn')
211
+ )
212
+ // Status items with alert severity
213
+ const hasStatusAlert = state.statusItems.some(
214
+ (s) => s.severity === 'error' || s.severity === 'warn'
215
+ )
216
+ return hasUnreadAlert || hasStatusAlert
217
+ })
218
+
219
+ return {
220
+ notifications,
221
+ unreadCount,
222
+ hasAlert,
223
+ addNotification,
224
+ markRead,
225
+ markAllRead,
226
+ removeNotification,
227
+ clearNotifications,
228
+ statusItems,
229
+ registerStatus,
230
+ updateStatus,
231
+ removeStatus,
232
+ isOpen,
233
+ open,
234
+ close,
235
+ toggle,
236
+ }
237
+ }
238
+
239
+ // ─────────────────────────────────────────────────────────────────────────────
240
+ // Provide / Inject
241
+ // ─────────────────────────────────────────────────────────────────────────────
242
+
243
+ /**
244
+ * Provide the notification store to descendant components.
245
+ * Call this in your app root or layout component.
246
+ */
247
+ export function provideNotificationStore(config: NotificationStoreConfig = {}): NotificationStore {
248
+ const store = createNotificationStore(config)
249
+ provide(NOTIFICATION_KEY, store)
250
+ return store
251
+ }
252
+
253
+ /**
254
+ * Use the notification store in a component.
255
+ * Returns a no-op store if not provided (graceful fallback).
256
+ */
257
+ export function useNotifications(): NotificationStore {
258
+ const store = inject(NOTIFICATION_KEY)
259
+
260
+ if (!store) {
261
+ // Return a no-op store to prevent crashes
262
+ return {
263
+ notifications: computed(() => []),
264
+ unreadCount: computed(() => 0),
265
+ hasAlert: computed(() => false),
266
+ addNotification: () => '',
267
+ markRead: () => {},
268
+ markAllRead: () => {},
269
+ removeNotification: () => {},
270
+ clearNotifications: () => {},
271
+ statusItems: computed(() => []),
272
+ registerStatus: () => {},
273
+ updateStatus: () => {},
274
+ removeStatus: () => {},
275
+ isOpen: ref(false),
276
+ open: () => {},
277
+ close: () => {},
278
+ toggle: () => {},
279
+ }
280
+ }
281
+
282
+ return store
283
+ }
284
+
285
+ /**
286
+ * Injection key for direct use with provide/inject
287
+ */
288
+ export { NOTIFICATION_KEY }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Notifications Module - Optional notification panel system
3
+ *
4
+ * Provides toast capture, notification history, custom status items,
5
+ * and a panel UI anchored to the sidebar.
6
+ *
7
+ * Usage:
8
+ * ```ts
9
+ * import { NotificationModule, useNotifications } from '@quazardous/qdadm'
10
+ *
11
+ * // Enable via kernel config
12
+ * const kernel = new Kernel({
13
+ * notifications: { enabled: true }
14
+ * })
15
+ *
16
+ * // Use in components
17
+ * const { notifications, unreadCount, registerStatus } = useNotifications()
18
+ * ```
19
+ */
20
+
21
+ export {
22
+ NotificationModule,
23
+ NOTIFICATION_ZONE,
24
+ NOTIFICATION_BADGE_ZONE,
25
+ NOTIFICATION_STATUS_ZONE,
26
+ } from './NotificationModule'
27
+
28
+ export {
29
+ useNotifications,
30
+ createNotificationStore,
31
+ provideNotificationStore,
32
+ NOTIFICATION_KEY,
33
+ type Notification,
34
+ type NotificationSeverity,
35
+ type StatusItem,
36
+ type NotificationStore,
37
+ type NotificationStoreConfig,
38
+ } from './NotificationStore'
39
+
40
+ export { default as NotificationListener } from './NotificationListener.vue'
41
+ export { default as NotificationPanel } from './NotificationPanel.vue'
42
+ export { default as NotificationBadge } from './NotificationBadge.vue'
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Notification Module Styles
3
+ *
4
+ * Styles for the always-visible notification status zone in the sidebar.
5
+ * Panel and badge styles are scoped in their respective Vue components.
6
+ */
7
+
8
+ /* Timing values match _sidebar.scss sidebar-box-content-* variables */
9
+ $_content-delay: 0.2s;
10
+ $_content-duration: 0.1s;
11
+
12
+ /* =============================================================================
13
+ Notification Status Zone (always visible below footer in sidebar)
14
+ ============================================================================= */
15
+
16
+ .sidebar-notification-status {
17
+ opacity: 1;
18
+ transition: opacity $_content-duration ease $_content-delay;
19
+ }
20
+
21
+ /* Status label: delayed fade-in on expand, instant hide on collapse.
22
+ Matches SidebarBox content behavior (.sidebar-box-content). */
23
+ .sidebar-notification-status .status-label {
24
+ opacity: 1;
25
+ transition: opacity $_content-duration ease $_content-delay;
26
+ }
27
+
28
+ .sidebar--collapsed .sidebar-notification-status .status-label {
29
+ opacity: 0;
30
+ visibility: hidden;
31
+ transition: opacity 0s, visibility 0s;
32
+ }