@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,392 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * FieldGroups - Generic field group renderer with configurable layouts
4
+ *
5
+ * Shared component for both edit (form) and show pages.
6
+ * Uses slots for field rendering to support different field components.
7
+ *
8
+ * Supports multiple layout modes:
9
+ * - flat: Simple sections with headers
10
+ * - sections: Fieldset-style sections
11
+ * - cards: Each group in a card
12
+ * - tabs: TabView for top-level groups
13
+ * - accordion: Collapsible panels
14
+ *
15
+ * ```vue
16
+ * <FieldGroups :groups="groups" :data="data" layout="tabs">
17
+ * <template #field="{ field, value }">
18
+ * <FormField :field="field" v-model="data[field.name]" />
19
+ * </template>
20
+ * </FieldGroups>
21
+ * ```
22
+ */
23
+ import { computed, type PropType } from 'vue'
24
+ import Tabs from 'primevue/tabs'
25
+ import TabList from 'primevue/tablist'
26
+ import Tab from 'primevue/tab'
27
+ import TabPanels from 'primevue/tabpanels'
28
+ import TabPanel from 'primevue/tabpanel'
29
+ import Accordion from 'primevue/accordion'
30
+ import AccordionPanel from 'primevue/accordionpanel'
31
+ import AccordionHeader from 'primevue/accordionheader'
32
+ import AccordionContent from 'primevue/accordioncontent'
33
+ import Fieldset from 'primevue/fieldset'
34
+ import Card from 'primevue/card'
35
+ import Tag from 'primevue/tag'
36
+
37
+ type BadgeSeverity = 'secondary' | 'info' | 'success' | 'warn' | 'danger' | 'contrast'
38
+
39
+ /**
40
+ * Base field config interface
41
+ */
42
+ interface BaseFieldConfig {
43
+ name: string
44
+ type: string
45
+ label: string
46
+ [key: string]: unknown
47
+ }
48
+
49
+ /**
50
+ * Field group structure
51
+ */
52
+ interface FieldGroup<T extends BaseFieldConfig = BaseFieldConfig> {
53
+ name: string
54
+ label: string
55
+ fields: T[]
56
+ children: FieldGroup<T>[]
57
+ parent?: string
58
+ // Tab/accordion options
59
+ icon?: string
60
+ badge?: string | number
61
+ badgeSeverity?: BadgeSeverity
62
+ count?: number
63
+ visible?: boolean
64
+ disabled?: boolean
65
+ }
66
+
67
+ export type LayoutMode = 'flat' | 'sections' | 'cards' | 'tabs' | 'accordion'
68
+
69
+ const props = defineProps({
70
+ groups: {
71
+ type: Array as PropType<FieldGroup[]>,
72
+ required: true,
73
+ },
74
+ data: {
75
+ type: Object as PropType<Record<string, unknown> | null>,
76
+ default: null,
77
+ },
78
+ layout: {
79
+ type: String as PropType<LayoutMode>,
80
+ default: 'flat',
81
+ },
82
+ // Nested layout (for children groups)
83
+ childLayout: {
84
+ type: String as PropType<LayoutMode>,
85
+ default: 'sections',
86
+ },
87
+ })
88
+
89
+ // Expose slots for field rendering
90
+ defineSlots<{
91
+ field: (props: { field: BaseFieldConfig; value: unknown; groupName: string }) => unknown
92
+ // Optional: custom group header
93
+ 'group-header'?: (props: { group: FieldGroup; layout: LayoutMode }) => unknown
94
+ // Optional: custom group content wrapper
95
+ 'group-content'?: (props: { group: FieldGroup; layout: LayoutMode }) => unknown
96
+ }>()
97
+
98
+ // Filter out default group for labeling purposes and hidden groups
99
+ const displayGroups = computed(() => {
100
+ return props.groups.filter((g) => {
101
+ // Filter by visibility (default to true)
102
+ if (g.visible === false) return false
103
+ // Filter out empty default groups
104
+ return g.name !== '_default' || g.fields.length > 0
105
+ })
106
+ })
107
+
108
+ // Get value from data
109
+ function getValue(fieldName: string): unknown {
110
+ return props.data?.[fieldName]
111
+ }
112
+
113
+ // Normalize icon class
114
+ function getIconClass(icon: string | undefined): string | null {
115
+ if (!icon) return null
116
+ // Already has 'pi' prefix
117
+ if (icon.startsWith('pi')) {
118
+ return icon.includes(' ') ? icon : `pi ${icon}`
119
+ }
120
+ // Just icon name
121
+ return `pi pi-${icon}`
122
+ }
123
+
124
+ // Get badge value to display
125
+ function getBadgeValue(group: FieldGroup): string | number | null {
126
+ if (group.badge !== undefined && group.badge !== null) return group.badge
127
+ if (group.count !== undefined && group.count !== null && group.count > 0) return group.count
128
+ return null
129
+ }
130
+ </script>
131
+
132
+ <template>
133
+ <div class="field-groups" :class="`field-groups--${layout}`">
134
+ <!-- Flat layout: simple sections with optional headers -->
135
+ <template v-if="layout === 'flat'">
136
+ <div v-for="group in displayGroups" :key="group.name" class="field-group">
137
+ <slot name="group-header" :group="group" :layout="layout">
138
+ <h3 v-if="group.label && group.name !== '_default'" class="field-group-header">
139
+ {{ group.label }}
140
+ </h3>
141
+ </slot>
142
+ <div class="field-group-fields">
143
+ <template v-for="field in group.fields" :key="field.name">
144
+ <slot
145
+ name="field"
146
+ :field="field"
147
+ :value="getValue(field.name)"
148
+ :group-name="group.name"
149
+ />
150
+ </template>
151
+ </div>
152
+ <!-- Recursive children -->
153
+ <FieldGroups
154
+ v-if="group.children.length > 0"
155
+ :groups="group.children"
156
+ :data="data"
157
+ :layout="childLayout"
158
+ >
159
+ <template #field="slotProps">
160
+ <slot name="field" v-bind="slotProps" />
161
+ </template>
162
+ </FieldGroups>
163
+ </div>
164
+ </template>
165
+
166
+ <!-- Sections layout: Fieldset style -->
167
+ <template v-else-if="layout === 'sections'">
168
+ <Fieldset
169
+ v-for="group in displayGroups"
170
+ :key="group.name"
171
+ :legend="group.label || undefined"
172
+ :toggleable="false"
173
+ >
174
+ <div class="field-group-fields">
175
+ <template v-for="field in group.fields" :key="field.name">
176
+ <slot
177
+ name="field"
178
+ :field="field"
179
+ :value="getValue(field.name)"
180
+ :group-name="group.name"
181
+ />
182
+ </template>
183
+ </div>
184
+ <!-- Recursive children -->
185
+ <FieldGroups
186
+ v-if="group.children.length > 0"
187
+ :groups="group.children"
188
+ :data="data"
189
+ :layout="childLayout"
190
+ >
191
+ <template #field="slotProps">
192
+ <slot name="field" v-bind="slotProps" />
193
+ </template>
194
+ </FieldGroups>
195
+ </Fieldset>
196
+ </template>
197
+
198
+ <!-- Cards layout -->
199
+ <template v-else-if="layout === 'cards'">
200
+ <Card v-for="group in displayGroups" :key="group.name">
201
+ <template #title>{{ group.label }}</template>
202
+ <template #content>
203
+ <div class="field-group-fields">
204
+ <template v-for="field in group.fields" :key="field.name">
205
+ <slot
206
+ name="field"
207
+ :field="field"
208
+ :value="getValue(field.name)"
209
+ :group-name="group.name"
210
+ />
211
+ </template>
212
+ </div>
213
+ <!-- Recursive children -->
214
+ <FieldGroups
215
+ v-if="group.children.length > 0"
216
+ :groups="group.children"
217
+ :data="data"
218
+ :layout="childLayout"
219
+ >
220
+ <template #field="slotProps">
221
+ <slot name="field" v-bind="slotProps" />
222
+ </template>
223
+ </FieldGroups>
224
+ </template>
225
+ </Card>
226
+ </template>
227
+
228
+ <!-- Tabs layout -->
229
+ <template v-else-if="layout === 'tabs'">
230
+ <Tabs :value="displayGroups[0]?.name || '0'">
231
+ <TabList>
232
+ <Tab
233
+ v-for="group in displayGroups"
234
+ :key="group.name"
235
+ :value="group.name"
236
+ :disabled="group.disabled"
237
+ >
238
+ <i v-if="group.icon" :class="getIconClass(group.icon)" class="tab-icon" />
239
+ <span class="tab-label">{{ group.label }}</span>
240
+ <Tag
241
+ v-if="getBadgeValue(group) !== null"
242
+ :value="String(getBadgeValue(group))"
243
+ :severity="group.badgeSeverity || 'secondary'"
244
+ class="tab-badge"
245
+ />
246
+ </Tab>
247
+ </TabList>
248
+ <TabPanels>
249
+ <TabPanel v-for="group in displayGroups" :key="group.name" :value="group.name">
250
+ <div class="field-group-fields">
251
+ <template v-for="field in group.fields" :key="field.name">
252
+ <slot
253
+ name="field"
254
+ :field="field"
255
+ :value="getValue(field.name)"
256
+ :group-name="group.name"
257
+ />
258
+ </template>
259
+ </div>
260
+ <!-- Recursive children -->
261
+ <FieldGroups
262
+ v-if="group.children.length > 0"
263
+ :groups="group.children"
264
+ :data="data"
265
+ :layout="childLayout"
266
+ >
267
+ <template #field="slotProps">
268
+ <slot name="field" v-bind="slotProps" />
269
+ </template>
270
+ </FieldGroups>
271
+ </TabPanel>
272
+ </TabPanels>
273
+ </Tabs>
274
+ </template>
275
+
276
+ <!-- Accordion layout -->
277
+ <template v-else-if="layout === 'accordion'">
278
+ <Accordion :value="[displayGroups[0]?.name || '0']" multiple>
279
+ <AccordionPanel
280
+ v-for="group in displayGroups"
281
+ :key="group.name"
282
+ :value="group.name"
283
+ :disabled="group.disabled"
284
+ >
285
+ <AccordionHeader>
286
+ <i v-if="group.icon" :class="getIconClass(group.icon)" class="accordion-icon" />
287
+ <span class="accordion-label">{{ group.label }}</span>
288
+ <Tag
289
+ v-if="getBadgeValue(group) !== null"
290
+ :value="String(getBadgeValue(group))"
291
+ :severity="group.badgeSeverity || 'secondary'"
292
+ class="accordion-badge"
293
+ />
294
+ </AccordionHeader>
295
+ <AccordionContent>
296
+ <div class="field-group-fields">
297
+ <template v-for="field in group.fields" :key="field.name">
298
+ <slot
299
+ name="field"
300
+ :field="field"
301
+ :value="getValue(field.name)"
302
+ :group-name="group.name"
303
+ />
304
+ </template>
305
+ </div>
306
+ <!-- Recursive children -->
307
+ <FieldGroups
308
+ v-if="group.children.length > 0"
309
+ :groups="group.children"
310
+ :data="data"
311
+ :layout="childLayout"
312
+ >
313
+ <template #field="slotProps">
314
+ <slot name="field" v-bind="slotProps" />
315
+ </template>
316
+ </FieldGroups>
317
+ </AccordionContent>
318
+ </AccordionPanel>
319
+ </Accordion>
320
+ </template>
321
+ </div>
322
+ </template>
323
+
324
+ <style scoped>
325
+ /*
326
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
327
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
328
+ */
329
+ .field-groups {
330
+ display: flex;
331
+ flex-direction: column;
332
+ gap: 1rem;
333
+ }
334
+
335
+ .field-group {
336
+ display: flex;
337
+ flex-direction: column;
338
+ gap: 0.5rem;
339
+ }
340
+
341
+ .field-group-header {
342
+ font-size: 1rem;
343
+ font-weight: 600;
344
+ margin: 0 0 0.5rem 0;
345
+ padding-bottom: 0.5rem;
346
+ border-bottom: 1px solid var(--p-surface-200, #e2e8f0);
347
+ color: var(--p-text-color);
348
+ }
349
+
350
+ .field-group-fields {
351
+ display: flex;
352
+ flex-direction: column;
353
+ gap: 0.75rem;
354
+ }
355
+
356
+ /* Cards layout spacing */
357
+ .field-groups--cards {
358
+ gap: 1rem;
359
+ }
360
+
361
+ /* Tabs and accordion panels padding */
362
+ .field-groups--tabs :deep(.p-tabpanel),
363
+ .field-groups--accordion :deep(.p-accordioncontent-content) {
364
+ padding-top: 1rem;
365
+ }
366
+
367
+ /* Tab icon and badge styling */
368
+ .tab-icon {
369
+ margin-right: 0.5rem;
370
+ }
371
+
372
+ .tab-badge {
373
+ margin-left: 0.5rem;
374
+ font-size: 0.7rem;
375
+ padding: 0.15rem 0.4rem;
376
+ min-width: 1.25rem;
377
+ text-align: center;
378
+ }
379
+
380
+ /* Accordion icon and badge styling */
381
+ .accordion-icon {
382
+ margin-right: 0.5rem;
383
+ }
384
+
385
+ .accordion-badge {
386
+ margin-left: 0.5rem;
387
+ font-size: 0.7rem;
388
+ padding: 0.15rem 0.4rem;
389
+ min-width: 1.25rem;
390
+ text-align: center;
391
+ }
392
+ </style>