@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,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
|
+
}
|