@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,190 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * BaseLayout - Root of the 3-level layout inheritance chain
4
+ *
5
+ * Defines all standard zones for the admin application:
6
+ * - header: Top bar with branding
7
+ * - menu: Navigation sidebar
8
+ * - breadcrumb: Breadcrumb trail
9
+ * - sidebar: Optional secondary sidebar (empty by default)
10
+ * - main: Primary content area (slot for child layouts)
11
+ * - footer: Footer with optional branding
12
+ * - toaster: Toast notifications overlay
13
+ * - user-info: User info in sidebar (separate from footer)
14
+ *
15
+ * Each zone renders blocks from ZoneRegistry, with defaults
16
+ * provided when no blocks are registered.
17
+ *
18
+ * Inheritance pattern (Twig-style):
19
+ * BaseLayout renders Zone components for each area
20
+ * -> Child layouts (List, Form, Dashboard) extend by providing content to main zone
21
+ * -> Entity pages extend child layouts with entity-specific customizations
22
+ *
23
+ * Usage:
24
+ * <BaseLayout>
25
+ * <template #main>
26
+ * <!-- Page content here -->
27
+ * </template>
28
+ * </BaseLayout>
29
+ *
30
+ * Or with RouterView for nested routes:
31
+ * <BaseLayout />
32
+ */
33
+ import { useSlots, provide, ref } from 'vue'
34
+ import { RouterView } from 'vue-router'
35
+ import ConfirmDialog from 'primevue/confirmdialog'
36
+ import Zone from './Zone.vue'
37
+ import { LAYOUT_ZONES } from '../../zones/zones'
38
+ import { useGuardDialog } from '../../composables/useGuardStore'
39
+ import UnsavedChangesDialog from '../dialogs/UnsavedChangesDialog.vue'
40
+ import type { BreadcrumbItem, NavLinkItem } from '../../composables/useNavContext'
41
+
42
+ // Default components for zones
43
+ import DefaultHeader from './defaults/DefaultHeader.vue'
44
+ import DefaultMenu from './defaults/DefaultMenu.vue'
45
+ import DefaultFooter from './defaults/DefaultFooter.vue'
46
+ import DefaultUserInfo from './defaults/DefaultUserInfo.vue'
47
+ import DefaultBreadcrumb from './defaults/DefaultBreadcrumb.vue'
48
+ // DefaultToaster removed - Toast is now provided by Kernel at root level
49
+
50
+ const slots = useSlots()
51
+ const hasMainSlot: boolean = !!slots.main
52
+
53
+ // Guard dialog from shared store (registered by useBareForm/useForm when a form is active)
54
+ const guardDialog = useGuardDialog()
55
+
56
+ // Provide breadcrumb/navlinks override mechanism for child pages
57
+ const breadcrumbOverride = ref<BreadcrumbItem[] | null>(null)
58
+ const navlinksOverride = ref<NavLinkItem[] | null>(null)
59
+ provide('qdadmBreadcrumbOverride', breadcrumbOverride)
60
+ provide('qdadmNavlinksOverride', navlinksOverride)
61
+ </script>
62
+
63
+ <template>
64
+ <div class="base-layout">
65
+ <!-- Sidebar (contains header, menu, user-info, footer) -->
66
+ <aside class="sidebar">
67
+ <!-- Header zone: branding, logo -->
68
+ <Zone
69
+ :name="LAYOUT_ZONES.HEADER"
70
+ :default-component="DefaultHeader"
71
+ />
72
+
73
+ <!-- Menu zone: navigation -->
74
+ <Zone
75
+ :name="LAYOUT_ZONES.MENU"
76
+ :default-component="DefaultMenu"
77
+ />
78
+
79
+ <!-- User info (special zone between menu and footer) -->
80
+ <DefaultUserInfo />
81
+
82
+ <!-- Footer zone: powered by, etc. -->
83
+ <Zone
84
+ :name="LAYOUT_ZONES.FOOTER"
85
+ :default-component="DefaultFooter"
86
+ />
87
+
88
+ <!-- Always-visible notification status (hidden when collapsed) -->
89
+ <Zone name="_app:notification-status" class="sidebar-notification-status" />
90
+ </aside>
91
+
92
+ <!-- Main content area -->
93
+ <div class="main-area">
94
+ <!-- Breadcrumb zone -->
95
+ <Zone
96
+ :name="LAYOUT_ZONES.BREADCRUMB"
97
+ :default-component="DefaultBreadcrumb"
98
+ />
99
+
100
+ <!-- Sidebar zone (optional secondary sidebar) -->
101
+ <div class="content-with-sidebar">
102
+ <Zone :name="LAYOUT_ZONES.SIDEBAR" />
103
+
104
+ <!-- Main zone: primary content -->
105
+ <main class="main-content">
106
+ <Zone :name="LAYOUT_ZONES.MAIN">
107
+ <!-- Allow slot override or RouterView for nested routes -->
108
+ <template v-if="hasMainSlot">
109
+ <slot name="main" />
110
+ </template>
111
+ <RouterView v-else />
112
+ </Zone>
113
+ </main>
114
+ </div>
115
+ </div>
116
+
117
+ <!-- Toast is now provided by Kernel at root level -->
118
+
119
+ <!-- Notification panel (rendered outside sidebar, follows sidebar position) -->
120
+ <Zone name="_app:notifications" />
121
+
122
+ <!-- Confirm dialog (global) -->
123
+ <ConfirmDialog />
124
+
125
+ <!-- Unsaved Changes Dialog (auto-rendered when a form registers guardDialog) -->
126
+ <UnsavedChangesDialog
127
+ v-if="guardDialog"
128
+ :visible="guardDialog.visible.value"
129
+ :saving="guardDialog.saving?.value ?? false"
130
+ :message="guardDialog.message"
131
+ :hasOnSave="guardDialog.hasOnSave"
132
+ @saveAndLeave="guardDialog.onSaveAndLeave"
133
+ @leave="guardDialog.onLeave"
134
+ @stay="guardDialog.onStay"
135
+ />
136
+ </div>
137
+ </template>
138
+
139
+ <style scoped>
140
+ /*
141
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
142
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
143
+ */
144
+ .base-layout {
145
+ display: flex;
146
+ min-height: 100vh;
147
+ }
148
+
149
+ .sidebar {
150
+ width: var(--fad-sidebar-width, 18rem);
151
+ background: var(--p-surface-800, #1e293b);
152
+ color: var(--p-surface-0, white);
153
+ display: flex;
154
+ flex-direction: column;
155
+ position: fixed;
156
+ top: 0;
157
+ left: 0;
158
+ bottom: 0;
159
+ z-index: 100;
160
+ }
161
+
162
+ .main-area {
163
+ flex: 1;
164
+ margin-left: var(--fad-sidebar-width, 18rem);
165
+ background: var(--p-surface-50, #f8fafc);
166
+ min-height: 100vh;
167
+ display: flex;
168
+ flex-direction: column;
169
+ }
170
+
171
+ .content-with-sidebar {
172
+ display: flex;
173
+ flex: 1;
174
+ }
175
+
176
+ .main-content {
177
+ flex: 1;
178
+ padding: 1.5rem;
179
+ overflow-y: auto;
180
+ }
181
+
182
+ /* Dark mode support */
183
+ .dark-mode .sidebar {
184
+ background: var(--p-surface-900);
185
+ }
186
+
187
+ .dark-mode .main-area {
188
+ background: var(--p-surface-900);
189
+ }
190
+ </style>
@@ -0,0 +1,55 @@
1
+ <!-- eslint-disable vue/multi-word-component-names -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Breadcrumb - Navigation breadcrumb component
5
+ *
6
+ * Props:
7
+ * - items: Array of { label, to?, icon? }
8
+ *
9
+ * Last item is always rendered as current page (no link)
10
+ */
11
+ import { RouterLink, type RouteLocationRaw } from 'vue-router'
12
+
13
+ interface BreadcrumbItem {
14
+ label: string
15
+ to?: RouteLocationRaw
16
+ icon?: string
17
+ }
18
+
19
+ withDefaults(defineProps<{
20
+ items?: BreadcrumbItem[]
21
+ }>(), {
22
+ items: () => []
23
+ })
24
+ </script>
25
+
26
+ <template>
27
+ <nav class="breadcrumb" aria-label="Breadcrumb">
28
+ <ol class="breadcrumb-list">
29
+ <li
30
+ v-for="(item, index) in items"
31
+ :key="index"
32
+ class="breadcrumb-item"
33
+ :class="{ 'is-current': index === items.length - 1 }"
34
+ >
35
+ <template v-if="index > 0">
36
+ <i class="pi pi-chevron-right breadcrumb-separator" ></i>
37
+ </template>
38
+
39
+ <RouterLink
40
+ v-if="item.to && index < items.length - 1"
41
+ :to="item.to"
42
+ class="breadcrumb-link"
43
+ >
44
+ <i v-if="item.icon" :class="item.icon" ></i>
45
+ <span>{{ item.label }}</span>
46
+ </RouterLink>
47
+
48
+ <span v-else class="breadcrumb-text">
49
+ <i v-if="item.icon" :class="item.icon" ></i>
50
+ <span>{{ item.label }}</span>
51
+ </span>
52
+ </li>
53
+ </ol>
54
+ </nav>
55
+ </template>
@@ -0,0 +1,104 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DashboardLayout - Second level of layout inheritance for dashboard pages
4
+ *
5
+ * Extends BaseLayout by providing content to its `main` zone and defining
6
+ * dashboard-specific zones:
7
+ * - stats: Top area for KPI cards (CardsGrid component)
8
+ * - widgets: Main dashboard widgets area
9
+ * - recent-activity: Recent changes and activity log
10
+ *
11
+ * Each zone renders blocks from ZoneRegistry. Empty zones render nothing
12
+ * (no default widgets). Applications provide dashboard content by
13
+ * registering blocks to these zones.
14
+ *
15
+ * Inheritance pattern:
16
+ * BaseLayout renders Zone components for each area
17
+ * -> DashboardLayout fills main zone with dashboard structure
18
+ * -> Dashboard pages extend with entity-specific customizations
19
+ *
20
+ * Usage:
21
+ * <DashboardLayout>
22
+ * <!-- Optional: Override specific zones via slots -->
23
+ * </DashboardLayout>
24
+ *
25
+ * Or register blocks via ZoneRegistry:
26
+ * registry.registerBlock(DASHBOARD_ZONES.STATS, {
27
+ * component: StatsWidget,
28
+ * id: 'kpi-cards',
29
+ * weight: 10
30
+ * })
31
+ */
32
+ import { useSlots } from 'vue'
33
+ import BaseLayout from './BaseLayout.vue'
34
+ import Zone from './Zone.vue'
35
+ import { DASHBOARD_ZONES } from '../../zones/zones'
36
+
37
+ const slots = useSlots()
38
+ </script>
39
+
40
+ <template>
41
+ <BaseLayout>
42
+ <template #main>
43
+ <div class="dashboard-layout">
44
+ <!-- Stats zone: KPI cards, metrics -->
45
+ <section v-if="slots.stats" class="dashboard-stats">
46
+ <slot name="stats" />
47
+ </section>
48
+ <section v-else class="dashboard-stats">
49
+ <Zone :name="DASHBOARD_ZONES.STATS" />
50
+ </section>
51
+
52
+ <!-- Widgets zone: main dashboard widgets -->
53
+ <section v-if="slots.widgets" class="dashboard-widgets">
54
+ <slot name="widgets" />
55
+ </section>
56
+ <section v-else class="dashboard-widgets">
57
+ <Zone :name="DASHBOARD_ZONES.WIDGETS" />
58
+ </section>
59
+
60
+ <!-- Recent activity zone: activity log, recent changes -->
61
+ <section v-if="slots['recent-activity']" class="dashboard-recent-activity">
62
+ <slot name="recent-activity" />
63
+ </section>
64
+ <section v-else class="dashboard-recent-activity">
65
+ <Zone :name="DASHBOARD_ZONES.RECENT_ACTIVITY" />
66
+ </section>
67
+ </div>
68
+ </template>
69
+ </BaseLayout>
70
+ </template>
71
+
72
+ <style scoped>
73
+ /*
74
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
75
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
76
+ */
77
+ .dashboard-layout {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: 1.5rem;
81
+ }
82
+
83
+ .dashboard-stats {
84
+ /* Stats area typically uses CardsGrid */
85
+ }
86
+
87
+ .dashboard-widgets {
88
+ /* Main widgets area */
89
+ display: flex;
90
+ flex-direction: column;
91
+ gap: 1rem;
92
+ }
93
+
94
+ .dashboard-recent-activity {
95
+ /* Recent activity area */
96
+ }
97
+
98
+ /* Empty zones should collapse - Zone renders nothing when no blocks */
99
+ .dashboard-stats:empty,
100
+ .dashboard-widgets:empty,
101
+ .dashboard-recent-activity:empty {
102
+ display: none;
103
+ }
104
+ </style>
@@ -0,0 +1,256 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * FormLayout - Second level of the 3-level layout inheritance chain
4
+ *
5
+ * Extends BaseLayout by providing content to the `main` zone, and defines
6
+ * form-specific zones within that main area:
7
+ * - form-header: Title, back button, status indicators
8
+ * - form-fields: Main form fields area
9
+ * - form-tabs: Optional tabbed sections
10
+ * - actions: Save, cancel, delete buttons
11
+ *
12
+ * Inheritance pattern (Twig-style):
13
+ * BaseLayout renders all standard zones (header, menu, footer, main...)
14
+ * -> FormLayout fills the main zone with form structure
15
+ * -> Entity pages (BooksEditPage.vue) fill form zones with entity fields
16
+ *
17
+ * Usage:
18
+ * <FormLayout>
19
+ * <template #form-fields>
20
+ * <FormField v-model="data.title" name="title" />
21
+ * </template>
22
+ * <template #actions>
23
+ * <Button @click="save">Save</Button>
24
+ * </template>
25
+ * </FormLayout>
26
+ *
27
+ * Or using Zone API for extension points:
28
+ * <FormLayout /> <!-- Renders zones from registry -->
29
+ */
30
+ import { useSlots, provide, computed } from 'vue'
31
+ import BaseLayout from './BaseLayout.vue'
32
+ import Zone from './Zone.vue'
33
+ import { FORM_ZONES } from '../../zones/zones'
34
+
35
+ // Default components for form zones
36
+ import DefaultFormActions from './defaults/DefaultFormActions.vue'
37
+
38
+ interface Props {
39
+ /** Form loading state (displays spinner instead of fields) */
40
+ loading?: boolean
41
+ /** Form saving state (passed to actions) */
42
+ saving?: boolean
43
+ /** Form has unsaved changes (passed to actions) */
44
+ dirty?: boolean
45
+ /** Edit mode (changes action button labels) */
46
+ isEdit?: boolean
47
+ /** Whether to wrap form in a card component */
48
+ cardWrapper?: boolean
49
+ /** Whether to show the actions zone */
50
+ showActions?: boolean
51
+ }
52
+
53
+ interface FormEmit {
54
+ save: () => void
55
+ saveAndClose: () => void
56
+ cancel: () => void
57
+ delete: () => void
58
+ }
59
+
60
+ const props = withDefaults(defineProps<Props>(), {
61
+ loading: false,
62
+ saving: false,
63
+ dirty: false,
64
+ isEdit: false,
65
+ cardWrapper: true,
66
+ showActions: true
67
+ })
68
+
69
+ const emit = defineEmits<{
70
+ 'save': []
71
+ 'saveAndClose': []
72
+ 'cancel': []
73
+ 'delete': []
74
+ }>()
75
+
76
+ const slots = useSlots()
77
+
78
+ // Check which slots are provided by the parent
79
+ const hasFormHeaderSlot = computed((): boolean => !!slots['form-header'])
80
+ const hasFormFieldsSlot = computed((): boolean => !!slots['form-fields'])
81
+ const hasFormTabsSlot = computed((): boolean => !!slots['form-tabs'])
82
+ const hasActionsSlot = computed((): boolean => !!slots.actions)
83
+
84
+ // Provide form state to child zones (DefaultFormActions needs these)
85
+ provide('qdadmFormState', computed(() => ({
86
+ loading: props.loading,
87
+ saving: props.saving,
88
+ dirty: props.dirty,
89
+ isEdit: props.isEdit
90
+ })))
91
+
92
+ // Provide form events for child zones to emit
93
+ provide<FormEmit>('qdadmFormEmit', {
94
+ save: () => emit('save'),
95
+ saveAndClose: () => emit('saveAndClose'),
96
+ cancel: () => emit('cancel'),
97
+ delete: () => emit('delete')
98
+ })
99
+ </script>
100
+
101
+ <template>
102
+ <BaseLayout>
103
+ <template #main>
104
+ <div class="form-layout" :class="{ loading }">
105
+ <!-- Form Header Zone: title, back button, status indicators -->
106
+ <div class="form-header-zone">
107
+ <Zone :name="FORM_ZONES.FORM_HEADER">
108
+ <template v-if="hasFormHeaderSlot">
109
+ <slot name="form-header" />
110
+ </template>
111
+ </Zone>
112
+ </div>
113
+
114
+ <!-- Loading State -->
115
+ <div v-if="loading" class="form-loading-state">
116
+ <i class="pi pi-spin pi-spinner loading-spinner"></i>
117
+ </div>
118
+
119
+ <!-- Form Content -->
120
+ <template v-else>
121
+ <!-- Card wrapper or direct content -->
122
+ <div v-if="cardWrapper" class="form-card">
123
+ <!-- Form Fields Zone: main form fields area -->
124
+ <div class="form-fields-zone">
125
+ <Zone :name="FORM_ZONES.FORM_FIELDS">
126
+ <template v-if="hasFormFieldsSlot">
127
+ <slot name="form-fields" />
128
+ </template>
129
+ </Zone>
130
+ </div>
131
+
132
+ <!-- Form Tabs Zone: optional tabbed sections -->
133
+ <div v-if="hasFormTabsSlot" class="form-tabs-zone">
134
+ <Zone :name="FORM_ZONES.FORM_TABS">
135
+ <slot name="form-tabs" />
136
+ </Zone>
137
+ </div>
138
+
139
+ <!-- Actions Zone: save, cancel, delete buttons -->
140
+ <div v-if="showActions" class="form-actions-zone">
141
+ <!-- Slot override takes priority over Zone -->
142
+ <template v-if="hasActionsSlot">
143
+ <slot name="actions" />
144
+ </template>
145
+ <!-- Otherwise use Zone with default component -->
146
+ <Zone
147
+ v-else
148
+ :name="FORM_ZONES.ACTIONS"
149
+ :default-component="DefaultFormActions"
150
+ />
151
+ </div>
152
+ </div>
153
+
154
+ <!-- Without card wrapper -->
155
+ <template v-else>
156
+ <!-- Form Fields Zone -->
157
+ <div class="form-fields-zone">
158
+ <Zone :name="FORM_ZONES.FORM_FIELDS">
159
+ <template v-if="hasFormFieldsSlot">
160
+ <slot name="form-fields" />
161
+ </template>
162
+ </Zone>
163
+ </div>
164
+
165
+ <!-- Form Tabs Zone -->
166
+ <div v-if="hasFormTabsSlot" class="form-tabs-zone">
167
+ <Zone :name="FORM_ZONES.FORM_TABS">
168
+ <slot name="form-tabs" />
169
+ </Zone>
170
+ </div>
171
+
172
+ <!-- Actions Zone -->
173
+ <div v-if="showActions" class="form-actions-zone">
174
+ <!-- Slot override takes priority over Zone -->
175
+ <template v-if="hasActionsSlot">
176
+ <slot name="actions" />
177
+ </template>
178
+ <!-- Otherwise use Zone with default component -->
179
+ <Zone
180
+ v-else
181
+ :name="FORM_ZONES.ACTIONS"
182
+ :default-component="DefaultFormActions"
183
+ />
184
+ </div>
185
+ </template>
186
+ </template>
187
+ </div>
188
+ </template>
189
+ </BaseLayout>
190
+ </template>
191
+
192
+ <style scoped>
193
+ /*
194
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
195
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
196
+ */
197
+ .form-layout {
198
+ display: flex;
199
+ flex-direction: column;
200
+ gap: 1rem;
201
+ }
202
+
203
+ .form-layout.loading {
204
+ min-height: 300px;
205
+ }
206
+
207
+ .form-loading-state {
208
+ display: flex;
209
+ justify-content: center;
210
+ align-items: center;
211
+ padding: 3rem;
212
+ flex: 1;
213
+ }
214
+
215
+ .loading-spinner {
216
+ font-size: 2rem;
217
+ color: var(--p-primary-500);
218
+ }
219
+
220
+ .form-card {
221
+ background: var(--p-surface-0);
222
+ border-radius: var(--p-border-radius);
223
+ padding: 1.5rem;
224
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
225
+ }
226
+
227
+ .form-header-zone {
228
+ /* Form header inherits page header styles */
229
+ }
230
+
231
+ .form-fields-zone {
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: 1rem;
235
+ }
236
+
237
+ .form-tabs-zone {
238
+ margin-top: 1rem;
239
+ }
240
+
241
+ .form-actions-zone {
242
+ margin-top: 1.5rem;
243
+ padding-top: 1.5rem;
244
+ border-top: 1px solid var(--p-surface-200);
245
+ }
246
+
247
+ /* Dark mode support */
248
+ .dark-mode .form-card {
249
+ background: var(--p-surface-800);
250
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
251
+ }
252
+
253
+ .dark-mode .form-actions-zone {
254
+ border-top-color: var(--p-surface-700);
255
+ }
256
+ </style>