@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,440 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * RouterPanel - Debug panel for Vue Router state
4
+ *
5
+ * Displays:
6
+ * - Current route (name, path, params, query, meta)
7
+ * - Computed breadcrumb
8
+ * - Navigation history (recent route changes)
9
+ * - All registered routes
10
+ */
11
+ import { ref, computed, watch } from 'vue'
12
+ import { ObjectTree } from '@quazardous/qddebug'
13
+
14
+ interface RouteLocation {
15
+ name?: string
16
+ path: string
17
+ fullPath: string
18
+ params?: Record<string, string>
19
+ query?: Record<string, string>
20
+ hash?: string
21
+ meta?: Record<string, unknown>
22
+ matched?: MatchedRoute[]
23
+ }
24
+
25
+ interface MatchedRoute {
26
+ name?: string
27
+ path: string
28
+ meta?: Record<string, unknown>
29
+ }
30
+
31
+ interface NavigationEntry {
32
+ id: string | number
33
+ timestamp: number
34
+ from?: RouteLocation
35
+ to: RouteLocation
36
+ seen?: boolean
37
+ }
38
+
39
+ interface RouteEntry {
40
+ name: string
41
+ path: string
42
+ meta?: Record<string, unknown>
43
+ hasComponent?: boolean
44
+ }
45
+
46
+ interface StackLevel {
47
+ entity: string
48
+ param: string
49
+ id?: string
50
+ foreignKey?: string
51
+ loading?: boolean
52
+ hydrated?: boolean
53
+ hasData?: boolean
54
+ label?: string
55
+ }
56
+
57
+ interface ActiveStack {
58
+ depth: number
59
+ levels: StackLevel[]
60
+ }
61
+
62
+ interface BreadcrumbItem {
63
+ kind: 'route' | 'entity'
64
+ route?: string
65
+ entity?: string
66
+ id?: string
67
+ }
68
+
69
+ interface Tab {
70
+ id: string
71
+ label: string
72
+ icon: string
73
+ }
74
+
75
+ interface RouterCollector {
76
+ navigate: (path: string) => Promise<void>
77
+ getActiveStack: () => ActiveStack | null
78
+ getCurrentRoute: () => RouteLocation | null
79
+ getBreadcrumb: () => BreadcrumbItem[]
80
+ getRoutes: () => RouteEntry[]
81
+ markSeen: () => void
82
+ getBadge: () => number
83
+ [key: string]: unknown
84
+ }
85
+
86
+ const props = defineProps<{
87
+ collector: RouterCollector
88
+ entries: NavigationEntry[]
89
+ }>()
90
+
91
+ // Active tab
92
+ const activeTab = ref<string>('current')
93
+ const tabs: Tab[] = [
94
+ { id: 'current', label: 'Current', icon: 'pi-map-marker' },
95
+ { id: 'history', label: 'History', icon: 'pi-history' },
96
+ { id: 'routes', label: 'Routes', icon: 'pi-sitemap' }
97
+ ]
98
+
99
+ // Route filter for routes tab
100
+ const routeFilter = ref<string>('')
101
+
102
+ // Address bar for navigation
103
+ const addressInput = ref<string>('')
104
+
105
+ async function navigateTo(): Promise<void> {
106
+ const path = addressInput.value.trim()
107
+ if (!path) return
108
+ await props.collector.navigate(path)
109
+ addressInput.value = ''
110
+ }
111
+
112
+ // ActiveStack state
113
+ const activeStack = computed<ActiveStack | null>(() => {
114
+ void props.entries.length // trigger reactivity
115
+ return props.collector.getActiveStack()
116
+ })
117
+
118
+ // Max history - persisted in localStorage
119
+ const STORAGE_KEY_MAX = 'qdadm-router-max'
120
+ const maxHistory = ref<number>(parseInt(localStorage.getItem(STORAGE_KEY_MAX) || '20') || 20)
121
+
122
+ watch(maxHistory, (val) => {
123
+ localStorage.setItem(STORAGE_KEY_MAX, String(val))
124
+ })
125
+
126
+ // Current route (reactive via entries change)
127
+ const currentRoute = computed<RouteLocation | null>(() => {
128
+ // Trigger reactivity from entries
129
+ void props.entries.length
130
+ return props.collector.getCurrentRoute()
131
+ })
132
+
133
+ // Breadcrumb
134
+ const breadcrumb = computed<BreadcrumbItem[]>(() => {
135
+ void props.entries.length
136
+ return props.collector.getBreadcrumb()
137
+ })
138
+
139
+ // Matched routes with their individual metas
140
+ const matchedRoutes = computed<MatchedRoute[]>(() => {
141
+ void props.entries.length
142
+ return currentRoute.value?.matched ?? []
143
+ })
144
+
145
+ // Navigation history (already newest-first from collector, apply max limit)
146
+ const history = computed<NavigationEntry[]>(() => {
147
+ let result: NavigationEntry[] = props.entries
148
+ if (maxHistory.value > 0 && result.length > maxHistory.value) {
149
+ result = result.slice(0, maxHistory.value)
150
+ }
151
+ return result
152
+ })
153
+
154
+ // All routes filtered
155
+ const routes = computed<RouteEntry[]>(() => {
156
+ void props.entries.length // trigger
157
+ const all = props.collector.getRoutes()
158
+ if (!routeFilter.value) return all
159
+ const filter = routeFilter.value.toLowerCase()
160
+ return all.filter((r: RouteEntry) =>
161
+ r.name.toLowerCase().includes(filter) ||
162
+ r.path.toLowerCase().includes(filter)
163
+ )
164
+ })
165
+
166
+ // Mark history as seen when viewing history tab
167
+ watch(activeTab, (tab: string) => {
168
+ if (tab === 'history') {
169
+ props.collector.markSeen()
170
+ }
171
+ })
172
+
173
+ function formatTime(ts: number): string {
174
+ const d = new Date(ts)
175
+ return d.toLocaleTimeString('en-US', { hour12: false }) + '.' + String(d.getMilliseconds()).padStart(3, '0')
176
+ }
177
+
178
+ function hasParams(obj: Record<string, unknown> | undefined): boolean {
179
+ return obj !== undefined && obj !== null && Object.keys(obj).length > 0
180
+ }
181
+
182
+ // Hydration state helpers
183
+ function getHydrationIcon(level: StackLevel): string {
184
+ if (level.loading) return '⏳'
185
+ if (level.hydrated) return level.hasData ? '✓' : '○'
186
+ return '…'
187
+ }
188
+
189
+ function getHydrationClass(level: StackLevel): string {
190
+ if (level.loading) return 'hydration-badge-loading'
191
+ if (level.hydrated) return level.hasData ? 'hydration-badge-data' : 'hydration-badge-ready'
192
+ return 'hydration-badge-pending'
193
+ }
194
+
195
+ function getHydrationTitle(level: StackLevel): string {
196
+ if (level.loading) return 'Loading entity data...'
197
+ if (level.hydrated && level.hasData) return 'Hydrated with data'
198
+ if (level.hydrated) return 'Hydrated (no data to fetch)'
199
+ return 'Pending hydration'
200
+ }
201
+ </script>
202
+
203
+ <template>
204
+ <div class="router-panel">
205
+ <!-- Address Bar -->
206
+ <div class="address-bar">
207
+ <i class="pi pi-compass" />
208
+ <input
209
+ v-model="addressInput"
210
+ type="text"
211
+ placeholder="Navigate to path..."
212
+ class="address-input"
213
+ @keyup.enter="navigateTo"
214
+ />
215
+ <button class="address-go" @click="navigateTo" :disabled="!addressInput.trim()">
216
+ <i class="pi pi-arrow-right" />
217
+ </button>
218
+ </div>
219
+
220
+ <!-- Tabs -->
221
+ <div class="router-tabs">
222
+ <button
223
+ v-for="tab in tabs"
224
+ :key="tab.id"
225
+ class="router-tab"
226
+ :class="{ 'router-tab-active': activeTab === tab.id }"
227
+ @click="activeTab = tab.id"
228
+ >
229
+ <i :class="['pi', tab.icon]" />
230
+ {{ tab.label }}
231
+ <span v-if="tab.id === 'history' && collector.getBadge() > 0" class="tab-badge">
232
+ {{ collector.getBadge() }}
233
+ </span>
234
+ <span v-if="tab.id === 'routes'" class="tab-count">
235
+ {{ routes.length }}
236
+ </span>
237
+ </button>
238
+ </div>
239
+
240
+ <!-- Current Route -->
241
+ <div v-if="activeTab === 'current'" class="router-content">
242
+ <div v-if="currentRoute" class="route-current">
243
+ <!-- Route name and path -->
244
+ <div class="route-header">
245
+ <span class="route-name">{{ currentRoute.name }}</span>
246
+ <span class="route-path">{{ currentRoute.fullPath }}</span>
247
+ </div>
248
+
249
+ <!-- ActiveStack -->
250
+ <div v-if="activeStack" class="route-section">
251
+ <div class="section-label">Active Stack (depth: {{ activeStack.depth }})</div>
252
+ <div v-if="activeStack.levels.length === 0" class="stack-empty">
253
+ <i class="pi pi-inbox" />
254
+ <span>Stack empty (no entity with ID in route)</span>
255
+ </div>
256
+ <div v-else class="stack-list">
257
+ <div
258
+ v-for="(level, idx) in activeStack.levels"
259
+ :key="idx"
260
+ class="stack-entry"
261
+ :class="{
262
+ 'stack-current': idx === activeStack.levels.length - 1,
263
+ 'stack-loading': level.loading
264
+ }"
265
+ >
266
+ <div class="stack-entry-header">
267
+ <span class="stack-index">{{ idx }}</span>
268
+ <span class="stack-entity">{{ level.entity }}</span>
269
+ <span class="stack-hydration-badge" :class="getHydrationClass(level)" :title="getHydrationTitle(level)">
270
+ {{ getHydrationIcon(level) }}
271
+ </span>
272
+ </div>
273
+ <div class="stack-entry-props">
274
+ <span class="stack-prop">param: <code>{{ level.param }}</code></span>
275
+ <span class="stack-prop">id: <code>{{ level.id || '—' }}</code></span>
276
+ <span v-if="level.foreignKey" class="stack-prop">fk: <code>{{ level.foreignKey }}</code></span>
277
+ </div>
278
+ <div class="stack-entry-hydration">
279
+ <span v-if="level.loading" class="hydration-loading">⏳ loading...</span>
280
+ <span v-else-if="level.hydrated" class="hydration-ready">
281
+ <span class="hydration-label">{{ level.label || '(no label)' }}</span>
282
+ <span v-if="level.hasData" class="hydration-data">📦</span>
283
+ </span>
284
+ <span v-else class="hydration-pending">⏸ pending</span>
285
+ </div>
286
+ </div>
287
+ </div>
288
+ </div>
289
+
290
+ <!-- Breadcrumb -->
291
+ <div v-if="breadcrumb.length > 0" class="route-section">
292
+ <div class="section-label">Breadcrumb</div>
293
+ <div class="breadcrumb-list">
294
+ <div v-for="(item, idx) in breadcrumb" :key="idx" class="breadcrumb-entry" :class="'breadcrumb-' + item.kind">
295
+ <div class="breadcrumb-entry-header">
296
+ <span class="breadcrumb-index">{{ idx + 1 }}</span>
297
+ <span class="breadcrumb-kind">{{ item.kind }}</span>
298
+ </div>
299
+ <div class="breadcrumb-entry-props">
300
+ <template v-if="item.kind === 'route'">
301
+ <span class="breadcrumb-prop">route: <code>{{ item.route }}</code></span>
302
+ </template>
303
+ <template v-else>
304
+ <span class="breadcrumb-prop">entity: <code>{{ item.entity }}</code></span>
305
+ <span v-if="item.id" class="breadcrumb-prop">id: <code>{{ item.id }}</code></span>
306
+ </template>
307
+ </div>
308
+ </div>
309
+ </div>
310
+ </div>
311
+
312
+ <!-- Params -->
313
+ <div v-if="hasParams(currentRoute.params)" class="route-section">
314
+ <div class="section-label">Params</div>
315
+ <ObjectTree :data="currentRoute.params" :expanded="true" />
316
+ </div>
317
+
318
+ <!-- Query -->
319
+ <div v-if="hasParams(currentRoute.query)" class="route-section">
320
+ <div class="section-label">Query</div>
321
+ <ObjectTree :data="currentRoute.query" :expanded="true" />
322
+ </div>
323
+
324
+ <!-- Matched Routes -->
325
+ <div v-if="matchedRoutes.length > 0" class="route-section">
326
+ <div class="section-label">Matched Routes ({{ matchedRoutes.length }})</div>
327
+ <div class="matched-list">
328
+ <div v-for="(match, idx) in matchedRoutes" :key="idx" class="matched-entry">
329
+ <div class="matched-header">
330
+ <span class="matched-index">{{ idx }}</span>
331
+ <span class="matched-name">{{ match.name || '(unnamed)' }}</span>
332
+ </div>
333
+ <div class="matched-path">{{ match.path }}</div>
334
+ <div v-if="hasParams(match.meta)" class="matched-meta">
335
+ <ObjectTree :data="match.meta" :expanded="false" />
336
+ </div>
337
+ </div>
338
+ </div>
339
+ </div>
340
+
341
+ <!-- Hash -->
342
+ <div v-if="currentRoute.hash" class="route-section">
343
+ <div class="section-label">Hash</div>
344
+ <code class="route-hash">{{ currentRoute.hash }}</code>
345
+ </div>
346
+ </div>
347
+ <div v-else class="router-empty">
348
+ <i class="pi pi-compass" />
349
+ <span>No route active</span>
350
+ </div>
351
+ </div>
352
+
353
+ <!-- History -->
354
+ <div v-if="activeTab === 'history'" class="router-content">
355
+ <!-- Max selector -->
356
+ <div class="history-header-bar">
357
+ <span class="history-stats">{{ history.length }}/{{ entries.length }}</span>
358
+ <div class="history-max">
359
+ <label>Max:</label>
360
+ <select v-model.number="maxHistory" class="max-select">
361
+ <option :value="10">10</option>
362
+ <option :value="20">20</option>
363
+ <option :value="50">50</option>
364
+ <option :value="100">100</option>
365
+ </select>
366
+ </div>
367
+ </div>
368
+ <div v-if="history.length === 0" class="router-empty">
369
+ <i class="pi pi-history" />
370
+ <span>No navigation history</span>
371
+ </div>
372
+ <div v-else class="history-list">
373
+ <div
374
+ v-for="nav in history"
375
+ :key="nav.id"
376
+ class="history-entry"
377
+ :class="{ 'history-new': !nav.seen }"
378
+ >
379
+ <div class="history-header">
380
+ <span v-if="!nav.seen" class="history-dot" title="New" />
381
+ <span class="history-time">{{ formatTime(nav.timestamp) }}</span>
382
+ <span class="history-arrow">
383
+ <template v-if="nav.from">
384
+ {{ nav.from.name }}
385
+ <i class="pi pi-arrow-right" />
386
+ </template>
387
+ {{ nav.to.name }}
388
+ </span>
389
+ <button
390
+ class="history-nav-btn"
391
+ title="Navigate to this route"
392
+ @click="collector.navigate(nav.to.fullPath)"
393
+ >
394
+ <i class="pi pi-external-link" />
395
+ </button>
396
+ </div>
397
+ <div class="history-path">{{ nav.to.fullPath }}</div>
398
+ </div>
399
+ </div>
400
+ </div>
401
+
402
+ <!-- Routes -->
403
+ <div v-if="activeTab === 'routes'" class="router-content">
404
+ <div class="routes-filter">
405
+ <i class="pi pi-search" />
406
+ <input
407
+ v-model="routeFilter"
408
+ type="text"
409
+ placeholder="Filter routes..."
410
+ class="filter-input"
411
+ />
412
+ </div>
413
+ <div class="routes-list">
414
+ <div
415
+ v-for="route in routes"
416
+ :key="route.path"
417
+ class="route-entry"
418
+ :class="{
419
+ 'route-current-entry': currentRoute?.name === route.name,
420
+ 'route-internal': route.name?.startsWith('_')
421
+ }"
422
+ >
423
+ <div class="route-entry-header">
424
+ <span class="route-entry-name">{{ route.name }}</span>
425
+ <span v-if="!route.hasComponent" class="route-no-component" title="No component">
426
+ <i class="pi pi-exclamation-triangle" />
427
+ </span>
428
+ </div>
429
+ <div class="route-entry-path">{{ route.path }}</div>
430
+ <div v-if="hasParams(route.meta)" class="route-entry-meta">
431
+ <span v-if="route.meta?.public" class="meta-tag meta-public">public</span>
432
+ <span v-if="route.meta?.entity" class="meta-tag meta-entity">{{ route.meta.entity }}</span>
433
+ <span v-if="route.meta?.permission" class="meta-tag meta-permission">{{ route.meta.permission }}</span>
434
+ </div>
435
+ </div>
436
+ </div>
437
+ </div>
438
+ </div>
439
+ </template>
440
+
@@ -0,0 +1,133 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * ZonesPanel - Zones collector display
4
+ */
5
+
6
+ interface ZoneBlock {
7
+ id: string
8
+ component: string
9
+ weight: number
10
+ hasWrappers?: boolean
11
+ wrappersCount?: number
12
+ }
13
+
14
+ interface ZoneEntry {
15
+ name: string
16
+ blocksCount: number
17
+ blocks: ZoneBlock[]
18
+ hasDefault?: boolean
19
+ defaultName?: string
20
+ }
21
+
22
+ interface ZonesCollector {
23
+ toggleFilter?: () => void
24
+ toggleInternalFilter?: () => void
25
+ toggleHighlight?: (zoneName: string) => void
26
+ getHighlightedZone?: () => string | null
27
+ showCurrentPageOnly?: boolean
28
+ showInternalZones?: boolean
29
+ }
30
+
31
+ interface Props {
32
+ collector: ZonesCollector
33
+ entries: ZoneEntry[]
34
+ }
35
+
36
+ const props = defineProps<Props>()
37
+
38
+ const emit = defineEmits<{
39
+ update: []
40
+ }>()
41
+
42
+ function toggleFilter(): void {
43
+ if (props.collector.toggleFilter) {
44
+ props.collector.toggleFilter()
45
+ emit('update')
46
+ }
47
+ }
48
+
49
+ function isFilterActive(): boolean {
50
+ return props.collector.showCurrentPageOnly ?? true
51
+ }
52
+
53
+ function toggleInternalFilter(): void {
54
+ if (props.collector.toggleInternalFilter) {
55
+ props.collector.toggleInternalFilter()
56
+ emit('update')
57
+ }
58
+ }
59
+
60
+ function showInternalZones(): boolean {
61
+ return props.collector.showInternalZones ?? false
62
+ }
63
+
64
+ function highlightZone(zoneName: string): void {
65
+ if (props.collector.toggleHighlight) {
66
+ props.collector.toggleHighlight(zoneName)
67
+ emit('update')
68
+ }
69
+ }
70
+
71
+ function isHighlighted(zoneName: string): boolean {
72
+ return props.collector.getHighlightedZone?.() === zoneName
73
+ }
74
+ </script>
75
+
76
+ <template>
77
+ <div class="zones-panel">
78
+ <div class="debug-panel-toolbar">
79
+ <button
80
+ class="debug-toolbar-btn"
81
+ :class="{ 'debug-toolbar-btn--active': !isFilterActive() }"
82
+ :title="isFilterActive() ? 'Click to show all zones' : 'Click to show current page only'"
83
+ @click="toggleFilter"
84
+ >
85
+ <i :class="['pi', isFilterActive() ? 'pi-filter-slash' : 'pi-filter']" />
86
+ All Pages
87
+ </button>
88
+ <button
89
+ class="debug-toolbar-btn"
90
+ :class="{ 'debug-toolbar-btn--active': showInternalZones() }"
91
+ :title="showInternalZones() ? 'Click to hide internal zones' : 'Click to show internal zones (prefixed with _)'"
92
+ @click="toggleInternalFilter"
93
+ >
94
+ <i :class="['pi', showInternalZones() ? 'pi-lock-open' : 'pi-lock']" />
95
+ Internal
96
+ </button>
97
+ </div>
98
+ <div class="zones-panel-content">
99
+ <div v-if="entries.length === 0" class="zones-empty">
100
+ <i class="pi pi-inbox" />
101
+ <span>No zones</span>
102
+ </div>
103
+ <div v-for="zone in entries" :key="zone.name" class="zone-item">
104
+ <div class="zone-header">
105
+ <button
106
+ class="zone-highlight"
107
+ :class="{ 'zone-highlight-active': isHighlighted(zone.name) }"
108
+ :title="isHighlighted(zone.name) ? 'Hide highlight' : 'Highlight zone'"
109
+ @click="highlightZone(zone.name)"
110
+ >
111
+ <i class="pi pi-eye" />
112
+ </button>
113
+ <span class="zone-name">{{ zone.name }}</span>
114
+ <span class="zone-count">{{ zone.blocksCount }} block(s)</span>
115
+ </div>
116
+ <div v-if="zone.blocks.length > 0" class="zone-blocks">
117
+ <div v-for="block in zone.blocks" :key="block.id" class="zone-block">
118
+ <span class="block-id">{{ block.id }}</span>
119
+ <span class="block-component">{{ block.component }}</span>
120
+ <span class="block-weight">w:{{ block.weight }}</span>
121
+ <span v-if="block.hasWrappers" class="block-wrappers">
122
+ <i class="pi pi-sitemap" :title="`${block.wrappersCount} wrapper(s)`" />
123
+ </span>
124
+ </div>
125
+ </div>
126
+ <div v-else-if="zone.hasDefault" class="zone-default">
127
+ Default: {{ zone.defaultName || '(component)' }}
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </template>
133
+
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Debug Panel Components — qdadm.
3
+ *
4
+ * Admin-specific panels live here; generic panels (Entries, Signals, Toasts)
5
+ * are re-exported from `@quazardous/qddebug` to preserve qdadm's public API.
6
+ */
7
+
8
+ // Admin-specific panels
9
+ export { default as ZonesPanel } from './ZonesPanel.vue'
10
+ export { default as AuthPanel } from './AuthPanel.vue'
11
+ export { default as EntitiesPanel } from './EntitiesPanel.vue'
12
+ export { default as RouterPanel } from './RouterPanel.vue'
13
+ export { default as I18nPanel } from './I18nPanel.vue'
14
+
15
+ // Generic panels — re-exported from qddebug (single source of truth)
16
+ export { EntriesPanel, SignalsPanel, ToastsPanel } from '@quazardous/qddebug'
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Debug Module Exports
3
+ *
4
+ * Provides base classes and utilities for debug collectors
5
+ * used by the debug panel UI.
6
+ */
7
+
8
+ export { Collector, type CollectorEntry, type CollectorContext, type CollectorOptions } from './Collector'
9
+ export { SignalCollector, type SignalEntry } from './SignalCollector'
10
+ export { DebugBridge, createDebugBridge, type DebugBridgeOptions } from './DebugBridge'
11
+ export { ErrorCollector, type ErrorEntry } from './ErrorCollector'
12
+ export { ToastCollector, type ToastEntry } from './ToastCollector'
13
+ export { ZonesCollector, type ZoneEntry } from './ZonesCollector'
14
+ export {
15
+ AuthCollector,
16
+ type AuthEntry,
17
+ type AuthEvent,
18
+ type AuthEventType,
19
+ type AuthCollectorOptions
20
+ } from './AuthCollector'
21
+ export {
22
+ EntitiesCollector,
23
+ type EntityEntry,
24
+ type StorageInfo,
25
+ type CacheInfo,
26
+ type PermissionsInfo,
27
+ type StatsInfo
28
+ } from './EntitiesCollector'
29
+ export { RouterCollector, type NavigationEntry, type RouterCollectorOptions } from './RouterCollector'
30
+ export {
31
+ I18nCollector,
32
+ type MissingKeyEntry,
33
+ type LocaleChange,
34
+ type I18nCollectorOptions
35
+ } from './I18nCollector'
36
+ export { LocalStorageAdapter, createLocalStorageAdapter } from './LocalStorageAdapter'
37
+
38
+ // Module System v2 integration
39
+ export { DebugModule, DEBUG_BRIDGE_KEY, DEBUG_ZONE, QdadmDebugBar, type DebugModuleOptions } from './DebugModule'
40
+
41
+ // Components
42
+ export { DebugBar } from './components/index'
43
+
44
+ // Convenience export for Kernel debugBar option
45
+ import { DebugModule as _DebugModule, QdadmDebugBar as _QdadmDebugBar } from './DebugModule'
46
+ export const debugBar = {
47
+ module: _DebugModule,
48
+ component: _QdadmDebugBar
49
+ }
50
+
51
+ // Admin-specific panels + tab metadata, exposed for hosts that own a
52
+ // shared bridge and render a single `<DebugBar />` covering both
53
+ // qdadm and host-side (e.g. qdcms) collectors. Merge with your own
54
+ // panels and pass to `@quazardous/qddebug`'s `<DebugBar />`.
55
+ export {
56
+ adminPanels,
57
+ adminPanelsMeta,
58
+ } from './components/adminPanelsConfig'