@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,67 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DefaultFormActions - Default component for the form actions zone
4
+ *
5
+ * Renders save/cancel buttons with loading states, using the form state
6
+ * provided by FormLayout via Vue injection.
7
+ *
8
+ * This component is rendered by Zone when no blocks are registered
9
+ * in the 'actions' zone and no slot override is provided.
10
+ *
11
+ * Uses FormActions component for consistent button rendering.
12
+ *
13
+ * Inject:
14
+ * - qdadmFormState: { loading, saving, dirty, isEdit }
15
+ * - qdadmFormEmit: { save, saveAndClose, cancel, delete }
16
+ */
17
+ import { inject, computed, type Ref } from 'vue'
18
+ import FormActions from '../../edit/FormActions.vue'
19
+
20
+ interface FormState {
21
+ isEdit?: boolean
22
+ saving?: boolean
23
+ dirty?: boolean
24
+ loading?: boolean
25
+ }
26
+
27
+ interface FormEmit {
28
+ save: () => void
29
+ saveAndClose: () => void
30
+ cancel: () => void
31
+ delete?: () => void
32
+ }
33
+
34
+ // Inject form state from FormLayout
35
+ const formState = inject<Ref<FormState> | null>('qdadmFormState', null)
36
+ const formEmit = inject<FormEmit | null>('qdadmFormEmit', null)
37
+
38
+ // Fallback values if not injected (for standalone testing)
39
+ const isEdit = computed(() => formState?.value?.isEdit ?? false)
40
+ const saving = computed(() => formState?.value?.saving ?? false)
41
+ const dirty = computed(() => formState?.value?.dirty ?? true)
42
+
43
+ // Event handlers
44
+ function onSave(): void {
45
+ formEmit?.save()
46
+ }
47
+
48
+ function onSaveAndClose(): void {
49
+ formEmit?.saveAndClose()
50
+ }
51
+
52
+ function onCancel(): void {
53
+ formEmit?.cancel()
54
+ }
55
+ </script>
56
+
57
+ <template>
58
+ <FormActions
59
+ :isEdit="isEdit"
60
+ :saving="saving"
61
+ :dirty="dirty"
62
+ :showSaveAndClose="true"
63
+ @save="onSave"
64
+ @saveAndClose="onSaveAndClose"
65
+ @cancel="onCancel"
66
+ />
67
+ </template>
@@ -0,0 +1,78 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DefaultHeader - Default header component for BaseLayout
4
+ *
5
+ * Displays app branding (logo or name) and version badge.
6
+ * Extracted from AppLayout for zone-based architecture.
7
+ *
8
+ * This is the default component rendered in the "header" zone
9
+ * when no blocks are registered.
10
+ */
11
+ import { inject } from 'vue'
12
+
13
+ interface AppConfig {
14
+ name: string
15
+ shortName: string
16
+ version: string | null
17
+ logo: string | null
18
+ }
19
+
20
+ // App config from useApp (injected by createQdadm plugin)
21
+ const app = inject<AppConfig>('qdadmApp', {
22
+ name: 'Admin',
23
+ shortName: 'adm',
24
+ version: null,
25
+ logo: null
26
+ })
27
+ </script>
28
+
29
+ <template>
30
+ <div class="default-header">
31
+ <div class="header-branding">
32
+ <img v-if="app.logo" :src="app.logo" :alt="app.name" class="header-logo" />
33
+ <h1 v-else class="header-title">{{ app.name }}</h1>
34
+ </div>
35
+ <span v-if="app.version" class="header-version">v{{ app.version }}</span>
36
+ </div>
37
+ </template>
38
+
39
+ <style scoped>
40
+ /*
41
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
42
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
43
+ */
44
+ .default-header {
45
+ display: flex;
46
+ flex-direction: column;
47
+ align-items: flex-start;
48
+ gap: 0.5rem;
49
+ padding: 1.5rem;
50
+ border-bottom: 1px solid var(--p-surface-700, #334155);
51
+ }
52
+
53
+ .header-branding {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 0.5rem;
57
+ }
58
+
59
+ .header-title {
60
+ font-size: 1.25rem;
61
+ font-weight: 600;
62
+ margin: 0;
63
+ color: var(--p-surface-0, white);
64
+ }
65
+
66
+ .header-logo {
67
+ max-height: 32px;
68
+ max-width: 160px;
69
+ }
70
+
71
+ .header-version {
72
+ font-size: 0.625rem;
73
+ color: var(--p-surface-400, #94a3b8);
74
+ background: var(--p-surface-700, #334155);
75
+ padding: 0.125rem 0.375rem;
76
+ border-radius: 0.25rem;
77
+ }
78
+ </style>
@@ -0,0 +1,188 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DefaultMenu - Default navigation menu for BaseLayout
4
+ *
5
+ * Displays sidebar navigation with collapsible sections.
6
+ * Navigation items are auto-built from moduleRegistry.
7
+ * Extracted from AppLayout for zone-based architecture.
8
+ *
9
+ * This is the default component rendered in the "menu" zone
10
+ * when no blocks are registered.
11
+ */
12
+ import { ref, watch, onMounted, computed, inject } from 'vue'
13
+ import { RouterLink, useRoute } from 'vue-router'
14
+ import { useNavigation, type NavSection } from '../../../composables/useNavigation'
15
+
16
+ interface AppConfig {
17
+ shortName: string
18
+ [key: string]: unknown
19
+ }
20
+
21
+ const route = useRoute()
22
+ const { navSections, isNavActive, sectionHasActiveItem, handleNavClick } = useNavigation()
23
+
24
+ // App config for storage key namespacing
25
+ const app = inject<AppConfig>('qdadmApp', { shortName: 'qdadm' })
26
+
27
+ // LocalStorage key for collapsed sections state
28
+ const STORAGE_KEY = computed<string>(() => `${app.shortName.toLowerCase()}_nav_collapsed`)
29
+
30
+ // Collapsed sections state (section title -> boolean)
31
+ const collapsedSections = ref<Record<string, boolean>>({})
32
+
33
+ /**
34
+ * Load collapsed state from localStorage
35
+ */
36
+ function loadCollapsedState(): void {
37
+ try {
38
+ const stored = localStorage.getItem(STORAGE_KEY.value)
39
+ if (stored) {
40
+ collapsedSections.value = JSON.parse(stored) as Record<string, boolean>
41
+ }
42
+ } catch (e) {
43
+ console.warn('Failed to load nav state:', e)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Save collapsed state to localStorage
49
+ */
50
+ function saveCollapsedState(): void {
51
+ try {
52
+ localStorage.setItem(STORAGE_KEY.value, JSON.stringify(collapsedSections.value))
53
+ } catch (e) {
54
+ console.warn('Failed to save nav state:', e)
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Toggle section collapsed state
60
+ */
61
+ function toggleSection(sectionTitle: string): void {
62
+ collapsedSections.value[sectionTitle] = !collapsedSections.value[sectionTitle]
63
+ saveCollapsedState()
64
+ }
65
+
66
+ /**
67
+ * Check if section should be shown expanded
68
+ * - Never collapsed if it contains active item
69
+ * - Otherwise respect user preference
70
+ */
71
+ function isSectionExpanded(section: NavSection): boolean {
72
+ if (sectionHasActiveItem(section)) {
73
+ return true
74
+ }
75
+ const title = section.title || section.label || ''
76
+ return !collapsedSections.value[title]
77
+ }
78
+
79
+ // Load state on mount
80
+ onMounted(() => {
81
+ loadCollapsedState()
82
+ })
83
+
84
+ // Auto-expand section when navigating to an item in it
85
+ watch(() => route?.path, () => {
86
+ if (!route) return
87
+ for (const section of navSections.value) {
88
+ const title = section.title || section.label || ''
89
+ if (sectionHasActiveItem(section) && collapsedSections.value[title]) {
90
+ // Auto-expand but don't save (user can collapse again if they want)
91
+ collapsedSections.value[title] = false
92
+ }
93
+ }
94
+ })
95
+ </script>
96
+
97
+ <template>
98
+ <!-- Use sidebar-nav class to inherit global nav-item styles from _sidebar.scss -->
99
+ <nav class="sidebar-nav">
100
+ <div v-for="section in navSections" :key="section.title || section.label" class="nav-section">
101
+ <div
102
+ class="nav-section-title"
103
+ :class="{ 'nav-section-active': sectionHasActiveItem(section) }"
104
+ @click="toggleSection(section.title || section.label || '')"
105
+ >
106
+ <span>{{ section.title }}</span>
107
+ <i
108
+ class="nav-section-chevron pi"
109
+ :class="isSectionExpanded(section) ? 'pi-chevron-down' : 'pi-chevron-right'"
110
+ ></i>
111
+ </div>
112
+ <div class="nav-section-items" :class="{ 'nav-section-collapsed': !isSectionExpanded(section) }">
113
+ <RouterLink
114
+ v-for="item in section.items"
115
+ :key="item.route"
116
+ :to="{ name: item.route }"
117
+ class="nav-item"
118
+ :class="{ 'nav-item-active': isNavActive(item) }"
119
+ @click="handleNavClick($event, item)"
120
+ >
121
+ <i :class="item.icon"></i>
122
+ <span>{{ item.label }}</span>
123
+ </RouterLink>
124
+ </div>
125
+ </div>
126
+ </nav>
127
+ </template>
128
+
129
+ <style scoped>
130
+ /*
131
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
132
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
133
+ */
134
+ /* DefaultMenu uses sidebar-nav class to inherit global nav-item styles from _sidebar.scss
135
+ Only component-specific styles remain here */
136
+
137
+ .sidebar-nav {
138
+ flex: 1;
139
+ overflow-y: auto;
140
+ padding: 1rem 0;
141
+ }
142
+
143
+ .nav-section {
144
+ margin-bottom: 0.5rem;
145
+ }
146
+
147
+ .nav-section-title {
148
+ padding: 0.5rem 1.5rem;
149
+ font-size: 0.75rem;
150
+ font-weight: 600;
151
+ text-transform: uppercase;
152
+ letter-spacing: 0.05em;
153
+ color: var(--p-surface-400, #94a3b8);
154
+ display: flex;
155
+ justify-content: space-between;
156
+ align-items: center;
157
+ cursor: pointer;
158
+ user-select: none;
159
+ transition: color 0.15s;
160
+ }
161
+
162
+ .nav-section-title:hover {
163
+ color: var(--p-surface-200, #e2e8f0);
164
+ }
165
+
166
+ .nav-section-active {
167
+ color: var(--p-primary-400, #60a5fa);
168
+ }
169
+
170
+ .nav-section-chevron {
171
+ font-size: 0.625rem;
172
+ transition: transform 0.2s;
173
+ }
174
+
175
+ .nav-section-items {
176
+ max-height: 500px;
177
+ overflow: hidden;
178
+ transition: max-height 0.2s ease-out, opacity 0.15s;
179
+ }
180
+
181
+ .nav-section-collapsed {
182
+ max-height: 0;
183
+ opacity: 0;
184
+ }
185
+
186
+ /* Nav item styles are in _sidebar.scss (global styles)
187
+ See .sidebar-nav .nav-item for base styles */
188
+ </style>
@@ -0,0 +1,75 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DefaultPagination - Default pagination component for ListLayout
4
+ *
5
+ * Renders a PrimeVue Paginator with standard configuration.
6
+ * Used as the default for the "pagination" zone in ListLayout.
7
+ *
8
+ * When `lazy` is false and pagination is handled by DataTable,
9
+ * this component is typically not shown (DataTable has its own paginator).
10
+ *
11
+ * This is useful for:
12
+ * - Separate pagination controls outside the table
13
+ * - Custom pagination layouts
14
+ * - Server-side (lazy) pagination with separate paginator
15
+ */
16
+ import Paginator from 'primevue/paginator'
17
+
18
+ interface PageEvent {
19
+ first: number
20
+ rows: number
21
+ page: number
22
+ pageCount?: number
23
+ }
24
+
25
+ interface Props {
26
+ /** First record index (0-based) */
27
+ first?: number
28
+ /** Number of rows per page */
29
+ rows?: number
30
+ /** Total number of records */
31
+ totalRecords?: number
32
+ /** Options for rows per page dropdown */
33
+ rowsPerPageOptions?: number[]
34
+ /** Template for the paginator layout */
35
+ template?: string
36
+ }
37
+
38
+ withDefaults(defineProps<Props>(), {
39
+ first: 0,
40
+ rows: 10,
41
+ totalRecords: 0,
42
+ rowsPerPageOptions: () => [10, 25, 50, 100],
43
+ template: 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown'
44
+ })
45
+
46
+ const emit = defineEmits<{
47
+ page: [event: PageEvent]
48
+ }>()
49
+
50
+ function onPage(event: PageEvent): void {
51
+ emit('page', event)
52
+ }
53
+ </script>
54
+
55
+ <template>
56
+ <Paginator
57
+ :first="first"
58
+ :rows="rows"
59
+ :totalRecords="totalRecords"
60
+ :rowsPerPageOptions="rowsPerPageOptions"
61
+ :template="template"
62
+ @page="onPage"
63
+ class="default-pagination"
64
+ />
65
+ </template>
66
+
67
+ <style scoped>
68
+ /*
69
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
70
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
71
+ */
72
+ .default-pagination {
73
+ margin-top: 1rem;
74
+ }
75
+ </style>
@@ -0,0 +1,137 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DefaultTable - Default table component for ListLayout
4
+ *
5
+ * Renders a PrimeVue DataTable with standard configuration.
6
+ * Used as the default for the "table" zone in ListLayout.
7
+ *
8
+ * Accepts props from the list page builder for data binding.
9
+ * Supports slots for column definitions.
10
+ *
11
+ * This is a minimal wrapper that expects:
12
+ * - items: Array of data to display
13
+ * - loading: Boolean loading state
14
+ * - Columns via default slot
15
+ */
16
+ import { computed, useSlots, type PropType } from 'vue'
17
+ import DataTable, { type DataTableSortEvent } from 'primevue/datatable'
18
+ import Column from 'primevue/column'
19
+
20
+ defineProps({
21
+ /**
22
+ * Data items to display in the table
23
+ */
24
+ items: {
25
+ type: Array as PropType<unknown[]>,
26
+ default: () => []
27
+ },
28
+ /**
29
+ * Loading state
30
+ */
31
+ loading: {
32
+ type: Boolean,
33
+ default: false
34
+ },
35
+ /**
36
+ * Field to use as unique key for rows
37
+ */
38
+ dataKey: {
39
+ type: String,
40
+ default: 'id'
41
+ },
42
+ /**
43
+ * Current selection (for selectable tables)
44
+ */
45
+ selection: {
46
+ type: Array as PropType<unknown[] | undefined>,
47
+ default: undefined
48
+ },
49
+ /**
50
+ * Enable row selection
51
+ */
52
+ selectable: {
53
+ type: Boolean,
54
+ default: false
55
+ },
56
+ /**
57
+ * Enable striped rows
58
+ */
59
+ stripedRows: {
60
+ type: Boolean,
61
+ default: true
62
+ },
63
+ /**
64
+ * Enable removable sort
65
+ */
66
+ removableSort: {
67
+ type: Boolean,
68
+ default: true
69
+ },
70
+ /**
71
+ * Current sort field
72
+ */
73
+ sortField: {
74
+ type: String as PropType<string | undefined>,
75
+ default: undefined
76
+ },
77
+ /**
78
+ * Current sort order (1 = asc, -1 = desc)
79
+ */
80
+ sortOrder: {
81
+ type: Number as PropType<1 | -1>,
82
+ default: 1
83
+ }
84
+ })
85
+
86
+ const emit = defineEmits<{
87
+ 'update:selection': [value: unknown[]]
88
+ 'sort': [event: DataTableSortEvent]
89
+ }>()
90
+
91
+ const slots = useSlots()
92
+ const hasDefaultSlot = computed<boolean>(() => !!slots.default)
93
+
94
+ function onSelectionChange(value: unknown[]): void {
95
+ emit('update:selection', value)
96
+ }
97
+
98
+ function onSort(event: DataTableSortEvent): void {
99
+ emit('sort', event)
100
+ }
101
+ </script>
102
+
103
+ <template>
104
+ <DataTable
105
+ :value="items"
106
+ :loading="loading"
107
+ :dataKey="dataKey"
108
+ :selection="selection"
109
+ :sortField="sortField"
110
+ :sortOrder="sortOrder"
111
+ :stripedRows="stripedRows"
112
+ :removableSort="removableSort"
113
+ @update:selection="onSelectionChange"
114
+ @sort="onSort"
115
+ class="default-table"
116
+ >
117
+ <!-- Columns from slot -->
118
+ <slot />
119
+
120
+ <!-- Selection column if selectable -->
121
+ <Column
122
+ v-if="selectable && !hasDefaultSlot"
123
+ selectionMode="multiple"
124
+ headerStyle="width: 3rem"
125
+ />
126
+ </DataTable>
127
+ </template>
128
+
129
+ <style scoped>
130
+ /*
131
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
132
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
133
+ */
134
+ .default-table {
135
+ width: 100%;
136
+ }
137
+ </style>
@@ -0,0 +1,111 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DefaultUserInfo - Default user info component for sidebar
4
+ *
5
+ * Displays user avatar, name, role, and logout button.
6
+ * Extracted from AppLayout for zone-based architecture.
7
+ *
8
+ * This component is used when auth is enabled.
9
+ */
10
+ import { computed, type ComputedRef } from 'vue'
11
+ import { useRouter } from 'vue-router'
12
+ import { useAuth } from '../../../composables/useAuth'
13
+ import Button from 'primevue/button'
14
+
15
+ interface UserData {
16
+ username?: string
17
+ name?: string
18
+ email?: string
19
+ role?: string
20
+ [key: string]: unknown
21
+ }
22
+
23
+ const router = useRouter()
24
+ const { user, logout, authEnabled } = useAuth()
25
+
26
+ // Cast user to expected shape (useAuth returns unknown)
27
+ const userData = computed<UserData | null>(() => user.value as UserData | null)
28
+
29
+ const userInitials: ComputedRef<string> = computed(() => {
30
+ const username = userData.value?.username
31
+ if (!username) return '?'
32
+ return username.substring(0, 2).toUpperCase()
33
+ })
34
+
35
+ const userDisplayName: ComputedRef<string> = computed(() => {
36
+ return userData.value?.username || userData.value?.name || 'User'
37
+ })
38
+
39
+ const userSubtitle: ComputedRef<string> = computed(() => {
40
+ return userData.value?.email || userData.value?.role || ''
41
+ })
42
+
43
+ function handleLogout(): void {
44
+ logout()
45
+ router.push({ name: 'login' })
46
+ }
47
+ </script>
48
+
49
+ <template>
50
+ <div v-if="authEnabled" class="default-user-info">
51
+ <div class="user-avatar">{{ userInitials }}</div>
52
+ <div class="user-details">
53
+ <div class="user-name">{{ userDisplayName }}</div>
54
+ <div class="user-role">{{ userSubtitle }}</div>
55
+ </div>
56
+ <Button
57
+ icon="pi pi-sign-out"
58
+ severity="secondary"
59
+ text
60
+ rounded
61
+ @click="handleLogout"
62
+ v-tooltip.top="'Logout'"
63
+ />
64
+ </div>
65
+ </template>
66
+
67
+ <style scoped>
68
+ /*
69
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
70
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
71
+ */
72
+ .default-user-info {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 0.75rem;
76
+ padding: 1rem 1.5rem;
77
+ border-top: 1px solid var(--p-surface-700, #334155);
78
+ }
79
+
80
+ .user-avatar {
81
+ width: 36px;
82
+ height: 36px;
83
+ border-radius: 50%;
84
+ background: var(--p-primary-600, #2563eb);
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ font-size: 0.875rem;
89
+ font-weight: 600;
90
+ color: var(--p-surface-0, white);
91
+ }
92
+
93
+ .user-details {
94
+ flex: 1;
95
+ min-width: 0;
96
+ }
97
+
98
+ .user-name {
99
+ font-weight: 500;
100
+ font-size: 0.875rem;
101
+ color: var(--p-surface-0, white);
102
+ }
103
+
104
+ .user-role {
105
+ font-size: 0.75rem;
106
+ color: var(--p-surface-400, #94a3b8);
107
+ overflow: hidden;
108
+ text-overflow: ellipsis;
109
+ white-space: nowrap;
110
+ }
111
+ </style>
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Default zone components for layouts
3
+ *
4
+ * These components are rendered when no blocks are registered
5
+ * in their respective zones.
6
+ */
7
+
8
+ // BaseLayout defaults
9
+ export { default as DefaultHeader } from './DefaultHeader.vue'
10
+ export { default as DefaultMenu } from './DefaultMenu.vue'
11
+ export { default as DefaultFooter } from './DefaultFooter.vue'
12
+ export { default as DefaultUserInfo } from './DefaultUserInfo.vue'
13
+ export { default as DefaultBreadcrumb } from './DefaultBreadcrumb.vue'
14
+
15
+ // FormLayout defaults
16
+ export { default as DefaultFormActions } from './DefaultFormActions.vue'