@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,163 @@
1
+ /**
2
+ * UsersManager - System entity manager for users
3
+ *
4
+ * Provides standard user entity with:
5
+ * - username, password, role fields
6
+ * - role linked to roles entity via reference
7
+ * - Admin-only access by default (customizable)
8
+ *
9
+ * @example
10
+ * // In a module:
11
+ * ctx.userEntity({
12
+ * storage: new ApiStorage({ endpoint: '/api/users' }),
13
+ * extraFields: {
14
+ * email: { type: 'email', label: 'Email' }
15
+ * }
16
+ * })
17
+ */
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
20
+ import { EntityManager } from '../entity/EntityManager'
21
+ import type { FieldConfig } from '../types'
22
+
23
+ /**
24
+ * User entity structure
25
+ */
26
+ export interface UserEntity {
27
+ id: string | number
28
+ username: string
29
+ password?: string
30
+ role?: string
31
+ [key: string]: unknown
32
+ }
33
+
34
+ /**
35
+ * Field definitions for users
36
+ */
37
+ export type FieldDefinitions = Record<string, FieldConfig>
38
+
39
+ /**
40
+ * Storage interface for users
41
+ */
42
+ export interface UserStorage {
43
+ list(params?: unknown): Promise<{ items: UserEntity[]; total: number }>
44
+ get(id: string | number): Promise<UserEntity | null>
45
+ create(data: Partial<UserEntity>): Promise<UserEntity>
46
+ update(id: string | number, data: Partial<UserEntity>): Promise<UserEntity>
47
+ delete(id: string | number): Promise<void>
48
+ }
49
+
50
+ /**
51
+ * Options for UsersManager
52
+ */
53
+ export interface UsersManagerOptions {
54
+ storage: UserStorage
55
+ extraFields?: FieldDefinitions
56
+ adminPermission?: string
57
+ fieldOverrides?: {
58
+ username?: Partial<FieldConfig>
59
+ password?: Partial<FieldConfig>
60
+ role?: Partial<FieldConfig>
61
+ }
62
+ [key: string]: unknown
63
+ }
64
+
65
+ // Type for EntityManager base class
66
+ interface EntityManagerInstance {
67
+ _hasSecurityChecker(): boolean
68
+ authAdapter: {
69
+ isGranted(permission: string): boolean
70
+ }
71
+ }
72
+
73
+ export class UsersManager extends EntityManager {
74
+ private _adminPermission: string
75
+
76
+ constructor(options: UsersManagerOptions) {
77
+ const {
78
+ storage,
79
+ extraFields = {},
80
+ adminPermission = 'security:users:manage',
81
+ fieldOverrides = {},
82
+ ...rest
83
+ } = options
84
+
85
+ if (!storage) {
86
+ throw new Error('[UsersManager] storage is required')
87
+ }
88
+
89
+ // Default fields for users
90
+ const defaultFields: FieldDefinitions = {
91
+ username: {
92
+ type: 'text',
93
+ label: 'Username',
94
+ required: true,
95
+ default: '',
96
+ ...fieldOverrides.username,
97
+ },
98
+ password: {
99
+ type: 'password',
100
+ label: 'Password',
101
+ required: true,
102
+ default: '',
103
+ listable: false, // Don't show in list view
104
+ ...fieldOverrides.password,
105
+ },
106
+ role: {
107
+ type: 'select',
108
+ label: 'Role',
109
+ reference: { entity: 'roles' }, // Links to roles entity
110
+ default: 'ROLE_USER',
111
+ ...fieldOverrides.role,
112
+ },
113
+ }
114
+
115
+ super({
116
+ name: 'users',
117
+ labelField: 'username',
118
+ system: true, // System-provided entity
119
+ fields: {
120
+ ...defaultFields,
121
+ ...extraFields,
122
+ },
123
+ storage,
124
+ ...rest,
125
+ })
126
+
127
+ this._adminPermission = adminPermission
128
+ }
129
+
130
+ /**
131
+ * Get admin permission (for external registration)
132
+ */
133
+ get adminPermission(): string {
134
+ return this._adminPermission
135
+ }
136
+
137
+ private _isAdmin(): boolean {
138
+ const self = this as unknown as EntityManagerInstance
139
+ if (self._hasSecurityChecker()) {
140
+ return self.authAdapter.isGranted(this._adminPermission)
141
+ }
142
+ return false
143
+ }
144
+
145
+ canRead(): boolean {
146
+ return this._isAdmin()
147
+ }
148
+
149
+ canCreate(): boolean {
150
+ // little dumb but should work for simple storages
151
+ return this._isAdmin()
152
+ }
153
+
154
+ canUpdate(): boolean {
155
+ // little dumb but should work for simple storages
156
+ return this._isAdmin()
157
+ }
158
+
159
+ canDelete(): boolean {
160
+ // little dumb but should work for simple storages
161
+ return this._isAdmin()
162
+ }
163
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Security module - Permission and role management
3
+ *
4
+ * Provides:
5
+ * - PermissionMatcher: Wildcard pattern matching (*, **)
6
+ * - PermissionRegistry: Central registry for module permissions
7
+ * - RoleProvider: Interface for role → permissions mapping
8
+ * - StaticRoleProvider: Config-based roles provider (default)
9
+ * - EntityRoleProvider: Entity-based roles provider (for UI management)
10
+ * - PersistableRoleProvider: Load/persist from any source (localStorage, API)
11
+ * - SecurityModule: System module for roles UI (uses RolesManager)
12
+ * - RolesManager: System entity manager for roles
13
+ * - UsersManager: System entity manager for users (linked to roles)
14
+ *
15
+ * @example
16
+ * import {
17
+ * PermissionMatcher,
18
+ * PermissionRegistry,
19
+ * StaticRoleProvider,
20
+ * EntityRoleProvider,
21
+ * PersistableRoleProvider,
22
+ * createLocalStorageRolesProvider,
23
+ * UsersManager
24
+ * } from '@quazardous/qdadm/security'
25
+ */
26
+
27
+ export { PermissionMatcher } from './PermissionMatcher'
28
+ export {
29
+ PermissionRegistry,
30
+ type PermissionDefinition,
31
+ type PermissionMeta,
32
+ type RegisterOptions,
33
+ type RegisterEntityOptions,
34
+ type GroupedPermissions,
35
+ } from './PermissionRegistry'
36
+ export {
37
+ RoleProvider,
38
+ type RoleMeta,
39
+ type RoleData,
40
+ type RoleHierarchyMap,
41
+ type RolePermissionsMap,
42
+ type RoleLabelsMap,
43
+ type RoleProviderContext,
44
+ } from './RolesProvider'
45
+ export {
46
+ StaticRoleProvider,
47
+ type StaticRolesProviderConfig,
48
+ } from './StaticRolesProvider'
49
+ export {
50
+ EntityRoleProvider,
51
+ type EntityRolesProviderOptions,
52
+ } from './EntityRolesProvider'
53
+ export {
54
+ PersistableRoleProvider,
55
+ createLocalStorageRolesProvider,
56
+ type RoleConfig,
57
+ type MergeStrategy,
58
+ type LoadCallback,
59
+ type PersistCallback,
60
+ type PersistableRolesProviderOptions,
61
+ type LocalStorageRolesProviderOptions,
62
+ } from './PersistableRolesProvider'
63
+ export { SecurityModule } from './SecurityModule'
64
+ export { RolesManager, type RolesManagerOptions } from './RolesManager'
65
+ export { UsersManager, type UsersManagerOptions, type UserEntity } from './UsersManager'
66
+ export {
67
+ RoleProviderStorage,
68
+ type RoleListParams,
69
+ type RoleListResult,
70
+ type RoleInput,
71
+ } from './RolesProviderStorage'
72
+
73
+ /**
74
+ * Standard entity actions for CRUD operations
75
+ * Used by PermissionRegistry.registerEntity()
76
+ */
77
+ export const ENTITY_ACTIONS = ['read', 'list', 'create', 'update', 'delete'] as const
@@ -0,0 +1,276 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * RoleForm - Role create/edit form (standard FormPage pattern)
4
+ */
5
+
6
+ import { ref, computed, inject } from 'vue'
7
+ import { useEntityItemFormPage, FormPage, useOrchestrator, PermissionEditor } from '../../index.js'
8
+ import InputText from 'primevue/inputtext'
9
+ import AutoComplete from 'primevue/autocomplete'
10
+ import Chip from 'primevue/chip'
11
+ import type { PermissionRegistry } from '../PermissionRegistry'
12
+
13
+ /**
14
+ * Role option for autocomplete
15
+ */
16
+ interface RoleOption {
17
+ name: string
18
+ label: string
19
+ display: string
20
+ }
21
+
22
+ /**
23
+ * Role data from roles provider
24
+ */
25
+ interface RoleData {
26
+ label?: string
27
+ [key: string]: unknown
28
+ }
29
+
30
+ /**
31
+ * RolesProvider interface
32
+ */
33
+ interface RolesProviderInterface {
34
+ getRoles: () => string[]
35
+ getRole?: (name: string) => RoleData | null
36
+ }
37
+
38
+ /**
39
+ * Roles manager interface
40
+ */
41
+ interface RolesManager {
42
+ rolesProvider?: RolesProviderInterface
43
+ }
44
+
45
+ /**
46
+ * Autocomplete search event
47
+ */
48
+ interface AutocompleteSearchEvent {
49
+ query?: string
50
+ }
51
+
52
+ /**
53
+ * Autocomplete select event
54
+ */
55
+ interface AutocompleteSelectEvent {
56
+ value: RoleOption
57
+ }
58
+
59
+ /**
60
+ * Role form data structure
61
+ */
62
+ interface RoleFormData {
63
+ name: string
64
+ label: string
65
+ inherits: string[]
66
+ permissions: string[]
67
+ [key: string]: unknown
68
+ }
69
+
70
+ // ============ FORM BUILDER ============
71
+ const form = useEntityItemFormPage({ entity: 'roles' })
72
+
73
+ // ============ HELPERS ============
74
+ const { getManager } = useOrchestrator()
75
+ const manager = getManager('roles') as RolesManager | null
76
+
77
+ // Get permissionRegistry directly from Kernel (via provide/inject)
78
+ const permissionRegistry = inject<PermissionRegistry | null>('qdadmPermissionRegistry', null)
79
+
80
+ // Role options for inheritance (exclude self)
81
+ const allRoles = computed<RoleOption[]>(() => {
82
+ const currentName = form.data.value?.name as string | undefined
83
+ const roles = manager?.rolesProvider?.getRoles() || []
84
+ return roles
85
+ .filter((name: string) => name !== currentName)
86
+ .map((name: string) => {
87
+ const role = manager?.rolesProvider?.getRole?.(name)
88
+ return {
89
+ name,
90
+ label: role?.label || name,
91
+ display: `${name} (${role?.label || name})`
92
+ }
93
+ })
94
+ })
95
+
96
+ // Autocomplete for roles
97
+ const roleInput = ref<string>('')
98
+ const roleSuggestions = ref<RoleOption[]>([])
99
+
100
+ function searchRoles(event: AutocompleteSearchEvent): void {
101
+ const query = (event.query || '').toLowerCase()
102
+ const selected = (form.data.value.inherits as string[]) || []
103
+
104
+ roleSuggestions.value = allRoles.value
105
+ .filter((r: RoleOption) => !selected.includes(r.name))
106
+ .filter((r: RoleOption) =>
107
+ query === '' ||
108
+ r.name.toLowerCase().includes(query) ||
109
+ r.label.toLowerCase().includes(query)
110
+ )
111
+ }
112
+
113
+ function onRoleSelect(event: AutocompleteSelectEvent): void {
114
+ const role = event.value
115
+ const inherits = (form.data.value.inherits as string[]) || []
116
+ if (role && !inherits.includes(role.name)) {
117
+ form.data.value.inherits = [...inherits, role.name]
118
+ }
119
+ roleInput.value = ''
120
+ }
121
+
122
+ function removeRole(roleName: string): void {
123
+ const inherits = (form.data.value.inherits as string[]) || []
124
+ form.data.value.inherits = inherits.filter((r: string) => r !== roleName)
125
+ }
126
+
127
+ function getRoleLabel(roleName: string): string {
128
+ const role = allRoles.value.find((r: RoleOption) => r.name === roleName)
129
+ return role?.label || roleName
130
+ }
131
+ </script>
132
+
133
+ <template>
134
+ <FormPage v-bind="form.props.value" v-on="form.events">
135
+ <template #fields>
136
+ <div class="role-form-fields">
137
+ <!-- Role Name -->
138
+ <div class="form-field">
139
+ <label class="font-medium">Role Name</label>
140
+ <InputText
141
+ v-model="(form.data.value as RoleFormData).name"
142
+ :disabled="form.isEdit.value"
143
+ placeholder="ROLE_ADMIN"
144
+ class="w-full"
145
+ />
146
+ <small class="field-hint">Convention: ROLE_UPPERCASE_NAME</small>
147
+ </div>
148
+
149
+ <!-- Label -->
150
+ <div class="form-field">
151
+ <label class="font-medium">Display Label</label>
152
+ <InputText
153
+ v-model="(form.data.value as RoleFormData).label"
154
+ placeholder="Administrator"
155
+ class="w-full"
156
+ />
157
+ </div>
158
+
159
+ <!-- Inherits -->
160
+ <div class="form-field">
161
+ <label class="font-medium">Inherits From</label>
162
+ <div class="inherits-editor">
163
+ <div v-if="((form.data.value as RoleFormData).inherits || []).length > 0" class="inherits-chips">
164
+ <Chip
165
+ v-for="roleName in (form.data.value as RoleFormData).inherits"
166
+ :key="roleName"
167
+ removable
168
+ @remove="removeRole(roleName)"
169
+ class="inherits-chip"
170
+ >
171
+ <span class="chip-name">{{ roleName }}</span>
172
+ <span class="chip-label">({{ getRoleLabel(roleName) }})</span>
173
+ </Chip>
174
+ </div>
175
+ <AutoComplete
176
+ v-model="roleInput"
177
+ :suggestions="roleSuggestions"
178
+ optionLabel="display"
179
+ placeholder="Type role name..."
180
+ :minLength="0"
181
+ completeOnFocus
182
+ @complete="searchRoles"
183
+ @item-select="onRoleSelect"
184
+ dropdown
185
+ class="w-full"
186
+ >
187
+ <template #option="{ option }">
188
+ <div class="role-option">
189
+ <span class="role-name">{{ option.name }}</span>
190
+ <span class="role-label">{{ option.label }}</span>
191
+ </div>
192
+ </template>
193
+ </AutoComplete>
194
+ </div>
195
+ <small class="field-hint">
196
+ This role inherits all permissions from selected roles
197
+ </small>
198
+ </div>
199
+
200
+ <!-- Permissions -->
201
+ <div class="form-field">
202
+ <label class="font-medium">
203
+ Permissions ({{ ((form.data.value as RoleFormData).permissions || []).length }})
204
+ </label>
205
+ <PermissionEditor
206
+ v-model="(form.data.value as RoleFormData).permissions"
207
+ :permissionRegistry="permissionRegistry"
208
+ placeholder="Type namespace:action..."
209
+ />
210
+ </div>
211
+ </div>
212
+ </template>
213
+ </FormPage>
214
+ </template>
215
+
216
+ <style scoped>
217
+ /*
218
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
219
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
220
+ */
221
+ /* Form layout - .form-field and .field-hint are global (main.css) */
222
+ .role-form-fields {
223
+ display: flex;
224
+ flex-direction: column;
225
+ gap: 1.5rem;
226
+ }
227
+
228
+ /* Inherits editor - uses .editor-box pattern */
229
+ .inherits-editor {
230
+ border: 1px solid var(--p-surface-200);
231
+ border-radius: 0.5rem;
232
+ padding: 0.75rem;
233
+ background: var(--p-surface-50);
234
+ }
235
+
236
+ /* Inherits chips - uses .editor-chips pattern */
237
+ .inherits-chips {
238
+ display: flex;
239
+ flex-wrap: wrap;
240
+ gap: 0.5rem;
241
+ margin-bottom: 0.75rem;
242
+ padding-bottom: 0.75rem;
243
+ border-bottom: 1px solid var(--p-surface-200);
244
+ }
245
+
246
+ .inherits-chip {
247
+ font-family: monospace;
248
+ font-size: 0.875rem;
249
+ }
250
+
251
+ .chip-name {
252
+ font-weight: 500;
253
+ }
254
+
255
+ .chip-label {
256
+ color: var(--p-surface-500);
257
+ margin-left: 0.25rem;
258
+ }
259
+
260
+ .role-option {
261
+ display: flex;
262
+ justify-content: space-between;
263
+ align-items: center;
264
+ gap: 1rem;
265
+ }
266
+
267
+ .role-name {
268
+ font-family: monospace;
269
+ font-weight: 500;
270
+ }
271
+
272
+ .role-label {
273
+ color: var(--p-surface-500);
274
+ font-size: 0.875rem;
275
+ }
276
+ </style>
@@ -0,0 +1,123 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * RoleList - Role listing page (standard ListPage pattern)
4
+ */
5
+
6
+ import { useListPage, ListPage, useOrchestrator } from '../../index.js'
7
+ import Column from 'primevue/column'
8
+ import Message from 'primevue/message'
9
+ import Chip from 'primevue/chip'
10
+
11
+ /**
12
+ * RolesProvider interface
13
+ */
14
+ interface RolesProviderInterface {
15
+ canPersist?: boolean
16
+ }
17
+
18
+ /**
19
+ * Roles manager interface
20
+ */
21
+ interface RolesManager {
22
+ rolesProvider?: RolesProviderInterface
23
+ }
24
+
25
+ // ============ LIST BUILDER ============
26
+ const list = useListPage({ entity: 'roles' })
27
+
28
+ // ============ SEARCH ============
29
+ list.setSearch({
30
+ placeholder: 'Search roles...',
31
+ fields: ['name', 'label']
32
+ })
33
+
34
+ // ============ HEADER ACTIONS ============
35
+ list.addCreateAction('New Role')
36
+
37
+ // ============ ROW ACTIONS ============
38
+ list.addEditAction()
39
+ list.addDeleteAction({ labelField: 'label' })
40
+
41
+ // ============ HELPERS ============
42
+ const { getManager } = useOrchestrator()
43
+ const manager = getManager('roles') as RolesManager | null
44
+ // Storage capability (not user permission) - for showing read-only warning
45
+ const storageCanPersist: boolean = manager?.rolesProvider?.canPersist ?? false
46
+ </script>
47
+
48
+ <template>
49
+ <ListPage v-bind="list.props.value" v-on="list.events">
50
+ <!-- Read-only Warning -->
51
+ <template #before-table>
52
+ <Message v-if="!storageCanPersist" severity="info" :closable="false" class="mb-4">
53
+ <div class="flex align-items-center gap-2">
54
+ <i class="pi pi-info-circle text-xl"></i>
55
+ <span>
56
+ <strong>Read-only:</strong> Roles are configured statically.
57
+ Use a PersistableRoleProvider for editing.
58
+ </span>
59
+ </div>
60
+ </Message>
61
+ </template>
62
+
63
+ <template #columns>
64
+ <Column field="name" header="Role" sortable style="width: 25%">
65
+ <template #body="{ data }">
66
+ <div>
67
+ <span class="p-role-label">{{ data.name }}</span>
68
+ <div v-if="data.label && data.label !== data.name" class="text-sm text-color-secondary mt-1">
69
+ {{ data.label }}
70
+ </div>
71
+ </div>
72
+ </template>
73
+ </Column>
74
+
75
+ <Column header="Inherits" style="width: 20%">
76
+ <template #body="{ data }">
77
+ <div v-if="data.inherits?.length" class="p-role-labels">
78
+ <span
79
+ v-for="parent in data.inherits"
80
+ :key="parent"
81
+ class="p-role-label"
82
+ >{{ parent }}</span>
83
+ </div>
84
+ <span v-else class="text-color-secondary">-</span>
85
+ </template>
86
+ </Column>
87
+
88
+ <Column header="Permissions" style="width: 40%">
89
+ <template #body="{ data }">
90
+ <div v-if="data.permissions?.length" class="chips-container">
91
+ <Chip
92
+ v-for="perm in data.permissions.slice(0, 5)"
93
+ :key="perm"
94
+ :label="perm"
95
+ />
96
+ <Chip
97
+ v-if="data.permissions.length > 5"
98
+ :label="`+${data.permissions.length - 5}`"
99
+ class="chip-more"
100
+ />
101
+ </div>
102
+ <span v-else class="text-color-secondary">No permissions</span>
103
+ </template>
104
+ </Column>
105
+ </template>
106
+ </ListPage>
107
+ </template>
108
+
109
+ <style scoped>
110
+ /*
111
+ * Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
112
+ * Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
113
+ */
114
+ .chips-container {
115
+ display: flex;
116
+ flex-wrap: wrap;
117
+ gap: 0.25rem;
118
+ }
119
+
120
+ .chip-more {
121
+ border-style: dashed;
122
+ }
123
+ </style>