@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,278 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * ListLayout - Second level of 3-level layout inheritance for list pages
4
+ *
5
+ * Extends BaseLayout by providing content to the "main" zone and
6
+ * defining list-specific zones within:
7
+ * - before-table: Filter bar, search, bulk actions
8
+ * - table: DataTable component
9
+ * - after-table: Summary, additional actions
10
+ * - pagination: Paginator controls
11
+ *
12
+ * Inheritance pattern (Twig-style):
13
+ * BaseLayout (root)
14
+ * -> ListLayout (fills main zone, defines list zones)
15
+ * -> EntityListPage (fills list-specific slots)
16
+ *
17
+ * Usage:
18
+ * <ListLayout :items="data" :loading="isLoading">
19
+ * <template #columns>
20
+ * <Column field="name" header="Name" />
21
+ * </template>
22
+ * </ListLayout>
23
+ *
24
+ * Zone customization via ZoneRegistry:
25
+ * ```js
26
+ * registry.registerBlock(LIST_ZONES.BEFORE_TABLE, {
27
+ * component: CustomFilterBar,
28
+ * id: 'custom-filter'
29
+ * })
30
+ * ```
31
+ */
32
+ import { computed, useSlots, provide, type ComputedRef } from 'vue'
33
+ import BaseLayout from './BaseLayout.vue'
34
+ import Zone from './Zone.vue'
35
+ import { LIST_ZONES } from '../../zones/zones'
36
+
37
+ // Default components for list zones
38
+ import DefaultTable from './defaults/DefaultTable.vue'
39
+ import DefaultPagination from './defaults/DefaultPagination.vue'
40
+ import FilterBar from '../lists/FilterBar.vue'
41
+
42
+ interface PageEvent {
43
+ first: number
44
+ rows: number
45
+ page: number
46
+ pageCount: number
47
+ }
48
+
49
+ interface SortEvent {
50
+ sortField: string | ((item: unknown) => string) | undefined
51
+ sortOrder: 1 | -1 | 0 | undefined | null
52
+ }
53
+
54
+ interface TableProps {
55
+ items: unknown[]
56
+ loading: boolean
57
+ dataKey: string
58
+ selection: unknown[] | undefined
59
+ selectable: boolean
60
+ sortField?: string
61
+ sortOrder: 1 | -1
62
+ }
63
+
64
+ interface PaginationProps {
65
+ first: number
66
+ rows: number
67
+ totalRecords: number
68
+ rowsPerPageOptions: number[]
69
+ }
70
+
71
+ interface ListContext {
72
+ items: ComputedRef<unknown[]>
73
+ loading: ComputedRef<boolean>
74
+ selected: ComputedRef<unknown[] | undefined>
75
+ selectable: ComputedRef<boolean>
76
+ }
77
+
78
+ interface Props {
79
+ // Table data
80
+ /** Data items to display in the table */
81
+ items?: unknown[]
82
+ /** Loading state */
83
+ loading?: boolean
84
+ /** Field to use as unique key for rows */
85
+ dataKey?: string
86
+ // Selection
87
+ /** Current selection (v-model compatible) */
88
+ selected?: unknown[]
89
+ /** Enable row selection */
90
+ selectable?: boolean
91
+ // Sorting
92
+ /** Current sort field */
93
+ sortField?: string
94
+ /** Current sort order (1 = asc, -1 = desc) */
95
+ sortOrder?: 1 | -1
96
+ // Pagination
97
+ /** Enable pagination (in table or separate) */
98
+ paginator?: boolean
99
+ /** Use separate pagination zone (outside table) */
100
+ separatePagination?: boolean
101
+ /** Number of rows per page */
102
+ rows?: number
103
+ /** Options for rows per page dropdown */
104
+ rowsPerPageOptions?: number[]
105
+ /** Total number of records (for lazy/server-side pagination) */
106
+ totalRecords?: number
107
+ /** First record index (for lazy pagination) */
108
+ first?: number
109
+ /** Enable lazy (server-side) loading */
110
+ lazy?: boolean
111
+ // Search/Filter
112
+ /** Current search query (v-model compatible) */
113
+ searchQuery?: string
114
+ /** Search input placeholder */
115
+ searchPlaceholder?: string
116
+ /** Show filter bar zone */
117
+ showFilterBar?: boolean
118
+ }
119
+
120
+ const props = withDefaults(defineProps<Props>(), {
121
+ items: () => [],
122
+ loading: false,
123
+ dataKey: 'id',
124
+ selected: undefined,
125
+ selectable: false,
126
+ sortField: undefined,
127
+ sortOrder: 1,
128
+ paginator: true,
129
+ separatePagination: false,
130
+ rows: 10,
131
+ rowsPerPageOptions: () => [10, 25, 50, 100],
132
+ totalRecords: 0,
133
+ first: 0,
134
+ lazy: false,
135
+ searchQuery: '',
136
+ searchPlaceholder: 'Search...',
137
+ showFilterBar: true
138
+ })
139
+
140
+ const emit = defineEmits<{
141
+ 'update:selected': [value: unknown[]]
142
+ 'update:searchQuery': [value: string]
143
+ 'page': [event: PageEvent]
144
+ 'sort': [event: SortEvent]
145
+ }>()
146
+
147
+ const slots = useSlots()
148
+
149
+ // Check for specific slot content
150
+ const hasColumnsSlot = computed((): boolean => !!slots.columns)
151
+ const hasBeforeTableSlot = computed((): boolean => !!slots['before-table'])
152
+ const hasAfterTableSlot = computed((): boolean => !!slots['after-table'])
153
+ const hasPaginationSlot = computed((): boolean => !!slots.pagination)
154
+
155
+ // Provide list context to child components (for deep customization)
156
+ provide<ListContext>('qdadmListContext', {
157
+ items: computed(() => props.items),
158
+ loading: computed(() => props.loading),
159
+ selected: computed(() => props.selected),
160
+ selectable: computed(() => props.selectable)
161
+ })
162
+
163
+ // Props bundle for table zone
164
+ const tableProps = computed((): TableProps => ({
165
+ items: props.items,
166
+ loading: props.loading,
167
+ dataKey: props.dataKey,
168
+ selection: props.selected,
169
+ selectable: props.selectable,
170
+ sortField: props.sortField,
171
+ sortOrder: props.sortOrder
172
+ }))
173
+
174
+ // Props bundle for pagination zone
175
+ const paginationProps = computed((): PaginationProps => ({
176
+ first: props.first,
177
+ rows: props.rows,
178
+ totalRecords: props.totalRecords,
179
+ rowsPerPageOptions: props.rowsPerPageOptions
180
+ }))
181
+
182
+ // Event handlers
183
+ function onSelectionChange(value: unknown[]): void {
184
+ emit('update:selected', value)
185
+ }
186
+
187
+ function onSearchChange(value: string): void {
188
+ emit('update:searchQuery', value)
189
+ }
190
+
191
+ function onPage(event: PageEvent): void {
192
+ emit('page', event)
193
+ }
194
+
195
+ function onSort(event: SortEvent): void {
196
+ emit('sort', event)
197
+ }
198
+ </script>
199
+
200
+ <template>
201
+ <BaseLayout>
202
+ <template #main>
203
+ <div class="list-layout">
204
+ <!-- Before-table zone: Filter bar, search, bulk actions -->
205
+ <Zone
206
+ v-if="showFilterBar"
207
+ :name="LIST_ZONES.BEFORE_TABLE"
208
+ :default-component="FilterBar"
209
+ :block-props="{
210
+ modelValue: searchQuery,
211
+ placeholder: searchPlaceholder,
212
+ 'onUpdate:modelValue': onSearchChange
213
+ }"
214
+ >
215
+ <!-- Slot fallback if no blocks and no default -->
216
+ <template v-if="hasBeforeTableSlot">
217
+ <slot name="before-table" />
218
+ </template>
219
+ </Zone>
220
+
221
+ <!-- Table zone: DataTable -->
222
+ <!-- When columns slot is provided, we don't set default-component so slot content renders -->
223
+ <Zone
224
+ :name="LIST_ZONES.TABLE"
225
+ :default-component="hasColumnsSlot ? undefined : DefaultTable"
226
+ :block-props="{
227
+ ...tableProps,
228
+ 'onUpdate:selection': onSelectionChange,
229
+ onSort: onSort
230
+ }"
231
+ >
232
+ <!-- Render DefaultTable with columns slot when slot is provided -->
233
+ <DefaultTable
234
+ v-bind="tableProps"
235
+ @update:selection="onSelectionChange"
236
+ @sort="onSort"
237
+ >
238
+ <slot name="columns" />
239
+ </DefaultTable>
240
+ </Zone>
241
+
242
+ <!-- After-table zone: Summary, additional actions -->
243
+ <Zone :name="LIST_ZONES.AFTER_TABLE">
244
+ <template v-if="hasAfterTableSlot">
245
+ <slot name="after-table" />
246
+ </template>
247
+ </Zone>
248
+
249
+ <!-- Pagination zone: Paginator (when separate from table) -->
250
+ <Zone
251
+ v-if="paginator && separatePagination"
252
+ :name="LIST_ZONES.PAGINATION"
253
+ :default-component="DefaultPagination"
254
+ :block-props="{
255
+ ...paginationProps,
256
+ onPage: onPage
257
+ }"
258
+ >
259
+ <template v-if="hasPaginationSlot">
260
+ <slot name="pagination" />
261
+ </template>
262
+ </Zone>
263
+ </div>
264
+ </template>
265
+ </BaseLayout>
266
+ </template>
267
+
268
+ <style scoped>
269
+ /*
270
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
271
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
272
+ */
273
+ .list-layout {
274
+ display: flex;
275
+ flex-direction: column;
276
+ gap: 1rem;
277
+ }
278
+ </style>
@@ -0,0 +1,156 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * PageHeader - Reusable page header with title and actions
4
+ *
5
+ * Props:
6
+ * - title: Page title (simple string) OR
7
+ * - titleParts: { action, entityName, entityLabel } for decorated rendering
8
+ * - badges: Custom badges from entity manager [{ label, severity }]
9
+ *
10
+ * Title rendering:
11
+ * - Simple: "Edit Agent" → <h1>Edit Agent</h1>
12
+ * - Decorated: { action: 'Edit', entityName: 'Agent', entityLabel: 'David' }
13
+ * → <h1>Edit Agent: <span class="entity-label">David</span></h1>
14
+ *
15
+ * Slots:
16
+ * - subtitle: Optional content next to title
17
+ * - actions: Action buttons on the right
18
+ *
19
+ * Note: Breadcrumb is handled globally by AppLayout.
20
+ */
21
+ import { computed, inject, type Ref } from 'vue'
22
+ import Tag from 'primevue/tag'
23
+
24
+ interface TitleParts {
25
+ simple?: string
26
+ action?: string
27
+ entityName?: string
28
+ entityLabel?: string
29
+ }
30
+
31
+ interface BadgeConfig {
32
+ label: string
33
+ severity?: string
34
+ }
35
+
36
+ interface Props {
37
+ title?: string | null
38
+ titleParts?: TitleParts | null
39
+ subtitle?: string | null
40
+ badges?: BadgeConfig[]
41
+ }
42
+
43
+ const props = withDefaults(defineProps<Props>(), {
44
+ title: null,
45
+ titleParts: null,
46
+ subtitle: null,
47
+ badges: () => []
48
+ })
49
+
50
+ // Auto-injected title from useForm (if available)
51
+ const injectedTitleParts = inject<Ref<TitleParts | null> | null>('qdadmPageTitleParts', null)
52
+
53
+ // Use props first, then injected, then fallback
54
+ const effectiveTitleParts = computed((): TitleParts | null => {
55
+ return props.titleParts || injectedTitleParts?.value || null
56
+ })
57
+
58
+ // Simple title from usePageTitle('My Title')
59
+ const simpleTitle = computed((): string | undefined => effectiveTitleParts.value?.simple)
60
+
61
+ // Compute title display
62
+ const hasDecoratedTitle = computed((): boolean => {
63
+ return !!effectiveTitleParts.value?.entityLabel
64
+ })
65
+ </script>
66
+
67
+ <template>
68
+ <div class="page-header">
69
+ <div class="page-header-content">
70
+ <div class="page-header-row">
71
+ <div class="page-header-left">
72
+ <div>
73
+ <h1 class="page-title">
74
+ <template v-if="simpleTitle">{{ simpleTitle }}</template>
75
+ <template v-else-if="effectiveTitleParts">
76
+ <span v-if="hasDecoratedTitle" class="entity-label">{{ effectiveTitleParts.entityLabel }}</span>
77
+ <span v-else>{{ effectiveTitleParts.action }} {{ effectiveTitleParts.entityName }}</span>
78
+ </template>
79
+ <Tag
80
+ v-for="badge in badges"
81
+ :key="badge.label"
82
+ :value="badge.label"
83
+ :severity="(badge.severity as any) || 'secondary'"
84
+ class="entity-badge"
85
+ />
86
+ <template v-if="!effectiveTitleParts && !simpleTitle">{{ title }}</template>
87
+ </h1>
88
+ <p v-if="subtitle" class="page-subtitle">{{ subtitle }}</p>
89
+ </div>
90
+ <slot name="subtitle" ></slot>
91
+ </div>
92
+ <div class="header-actions">
93
+ <slot name="actions" ></slot>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </template>
99
+
100
+ <style scoped>
101
+ /*
102
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
103
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
104
+ */
105
+ .page-header-content {
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: 0.25rem;
109
+ }
110
+
111
+ .page-header-row {
112
+ display: flex;
113
+ justify-content: space-between;
114
+ align-items: center;
115
+ min-height: 2.5rem; /* Consistent height with or without action buttons */
116
+ }
117
+
118
+ .page-header-left {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 1rem;
122
+ }
123
+
124
+ .page-subtitle {
125
+ margin: 0.25rem 0 0 0;
126
+ font-size: 0.875rem;
127
+ color: var(--p-text-secondary);
128
+ }
129
+
130
+ /* Action badge (View Bot / Edit Country) - small grey badge, before title */
131
+ .action-badge {
132
+ display: inline-block;
133
+ font-size: 0.5em;
134
+ font-weight: 600;
135
+ background: var(--p-surface-200);
136
+ color: var(--p-surface-600);
137
+ padding: 0.25em 0.6em;
138
+ border-radius: 4px;
139
+ margin-right: 0.5em;
140
+ vertical-align: middle;
141
+ text-transform: uppercase;
142
+ letter-spacing: 0.03em;
143
+ }
144
+
145
+ /* Entity badges from manager callback */
146
+ .entity-badge {
147
+ vertical-align: middle;
148
+ margin-left: 0.4em;
149
+ font-size: 0.5em;
150
+ }
151
+
152
+ /* Entity label in title - main focus */
153
+ .entity-label {
154
+ font-weight: 600;
155
+ }
156
+ </style>
@@ -0,0 +1,126 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * PageLayout - Base layout for dashboard pages
4
+ *
5
+ * Provides:
6
+ * - PageHeader with title and actions
7
+ * - CardsGrid zone (optional)
8
+ * - Main content slot
9
+ *
10
+ * Slots:
11
+ * - #nav: For PageNav component (provides breadcrumb data to AppLayout)
12
+ * - #header-actions: Custom header action buttons
13
+ * - default: Main content
14
+ *
15
+ * Note: Breadcrumb is handled globally by AppLayout.
16
+ * Use PageNav in #nav slot to customize breadcrumb for child routes.
17
+ */
18
+ import PageHeader from './PageHeader.vue'
19
+ import CardsGrid from '../display/CardsGrid.vue'
20
+ import Button from 'primevue/button'
21
+
22
+ interface TitleParts {
23
+ action?: string
24
+ entityName?: string
25
+ entityLabel?: string
26
+ }
27
+
28
+ interface HeaderAction {
29
+ name: string
30
+ label: string | (() => string)
31
+ icon?: string
32
+ severity?: string
33
+ isLoading?: boolean
34
+ onClick: () => void
35
+ }
36
+
37
+ type Severity = 'success' | 'danger' | 'warning' | 'info'
38
+
39
+ interface Card {
40
+ name: string
41
+ value?: string | number
42
+ label?: string
43
+ severity?: Severity
44
+ icon?: string
45
+ custom?: boolean
46
+ class?: string
47
+ onClick?: () => void
48
+ }
49
+
50
+ interface Props {
51
+ /** Page title (use title OR titleParts) */
52
+ title?: string
53
+ /** Title parts for decorated entity label */
54
+ titleParts?: TitleParts
55
+ /** Page subtitle */
56
+ subtitle?: string
57
+ /** Header action buttons */
58
+ headerActions?: HeaderAction[]
59
+ /** Cards configuration */
60
+ cards?: Card[]
61
+ /** Number of card columns or 'auto' */
62
+ cardsColumns?: number | 'auto'
63
+ }
64
+
65
+ withDefaults(defineProps<Props>(), {
66
+ headerActions: () => [],
67
+ cards: () => [],
68
+ cardsColumns: 'auto'
69
+ })
70
+
71
+ function resolveLabel(label: string | (() => string)): string {
72
+ return typeof label === 'function' ? label() : label
73
+ }
74
+ </script>
75
+
76
+ <template>
77
+ <div>
78
+ <!-- Nav slot for PageNav (provides data to AppLayout, renders nothing visible) -->
79
+ <slot name="nav" />
80
+
81
+ <PageHeader
82
+ :title="title"
83
+ :title-parts="titleParts"
84
+ >
85
+ <template #subtitle>
86
+ <slot name="subtitle">
87
+ <span v-if="subtitle" class="page-subtitle">{{ subtitle }}</span>
88
+ </slot>
89
+ </template>
90
+ <template #actions>
91
+ <slot name="header-actions" ></slot>
92
+ <Button
93
+ v-for="action in headerActions"
94
+ :key="action.name"
95
+ :label="resolveLabel(action.label)"
96
+ :icon="action.icon"
97
+ :severity="action.severity"
98
+ :loading="action.isLoading"
99
+ @click="action.onClick"
100
+ />
101
+ </template>
102
+ </PageHeader>
103
+
104
+ <!-- Cards Zone -->
105
+ <CardsGrid v-if="cards.length > 0" :cards="cards" :columns="cardsColumns">
106
+ <template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
107
+ <slot :name="slotName" v-bind="slotProps" ></slot>
108
+ </template>
109
+ </CardsGrid>
110
+
111
+ <!-- Main Content -->
112
+ <slot ></slot>
113
+ </div>
114
+ </template>
115
+
116
+ <style scoped>
117
+ /*
118
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
119
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
120
+ */
121
+ .page-subtitle {
122
+ color: var(--p-surface-500);
123
+ font-size: 0.9rem;
124
+ margin-left: 1rem;
125
+ }
126
+ </style>