@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,285 @@
1
+ /**
2
+ * Core types for qdadm framework
3
+ */
4
+
5
+ // ============ ENTITY TYPES ============
6
+
7
+ /**
8
+ * Base interface for all entity records
9
+ */
10
+ export interface EntityRecord {
11
+ id: string | number
12
+ [key: string]: unknown
13
+ }
14
+
15
+ /**
16
+ * Parameters for list operations
17
+ */
18
+ export interface ListParams {
19
+ page?: number
20
+ page_size?: number
21
+ search?: string
22
+ filters?: Record<string, unknown>
23
+ sort_by?: string
24
+ sort_order?: 'asc' | 'desc'
25
+ cacheSafe?: boolean
26
+ [key: string]: unknown
27
+ }
28
+
29
+ /**
30
+ * Result of list operations
31
+ */
32
+ export interface ListResult<T = EntityRecord> {
33
+ items: T[]
34
+ total: number
35
+ fromCache?: boolean
36
+ }
37
+
38
+ // ============ STORAGE TYPES ============
39
+
40
+ /**
41
+ * Storage capabilities declaration
42
+ */
43
+ export interface StorageCapabilities {
44
+ supportsTotal: boolean
45
+ supportsFilters: boolean
46
+ supportsPagination: boolean
47
+ supportsCaching: boolean
48
+ requiresAuth?: boolean
49
+ searchFields?: string[]
50
+ /** Cache TTL in milliseconds (0=disabled, -1=infinite, >0=TTL). Can be set dynamically from API headers. */
51
+ cacheTtlMs?: number
52
+ /** Asymmetric mode: list() and get() return different structures. Falls back from EntityManagerOptions. */
53
+ asymmetric?: boolean
54
+ }
55
+
56
+ // ============ FIELD TYPES ============
57
+
58
+ /**
59
+ * Field type definitions
60
+ */
61
+ export type FieldType =
62
+ | 'text'
63
+ | 'number'
64
+ | 'boolean'
65
+ | 'select'
66
+ | 'multiselect'
67
+ | 'date'
68
+ | 'datetime'
69
+ | 'email'
70
+ | 'url'
71
+ | 'textarea'
72
+ | 'password'
73
+ | 'array'
74
+ | 'object'
75
+ | 'json'
76
+ | string
77
+
78
+ /**
79
+ * Field configuration
80
+ */
81
+ export interface FieldConfig {
82
+ type: FieldType
83
+ label?: string
84
+ required?: boolean
85
+ default?: unknown | (() => unknown)
86
+ options?: Array<{ label: string; value: unknown }>
87
+ reference?: {
88
+ entity: string
89
+ labelField?: string
90
+ valueField?: string
91
+ }
92
+ listable?: boolean
93
+ editable?: boolean
94
+ sortable?: boolean
95
+ filterable?: boolean
96
+ searchable?: boolean
97
+ validator?: (value: unknown) => boolean | string
98
+ [key: string]: unknown
99
+ }
100
+
101
+ // ============ ENTITY MANAGER TYPES ============
102
+
103
+ /**
104
+ * Child entity configuration
105
+ */
106
+ export interface ChildConfig {
107
+ entity: string
108
+ endpoint?: string
109
+ }
110
+
111
+ /**
112
+ * Parent entity configuration
113
+ */
114
+ export interface ParentConfig {
115
+ entity: string
116
+ foreignKey: string
117
+ }
118
+
119
+ /**
120
+ * Relation configuration
121
+ */
122
+ export interface RelationConfig {
123
+ entity: string
124
+ through?: string
125
+ }
126
+
127
+ /**
128
+ * Navigation configuration for auto-generated menus
129
+ */
130
+ export interface NavConfig {
131
+ icon?: string
132
+ section?: string
133
+ weight?: number
134
+ visible?: boolean | (() => boolean)
135
+ }
136
+
137
+ /**
138
+ * EntityManager constructor options
139
+ */
140
+ export interface EntityManagerOptions<T extends EntityRecord = EntityRecord> {
141
+ name: string
142
+ storage?: IStorage<T> | null
143
+ idField?: string
144
+ label?: string
145
+ labelPlural?: string
146
+ routePrefix?: string
147
+ labelField?: string | ((entity: T) => string)
148
+ fields?: Record<string, FieldConfig>
149
+ localFilterThreshold?: number | null
150
+ /** Cache TTL in milliseconds (0=disabled, -1=infinite, >0=TTL). Overrides global, overridden by storage. */
151
+ cacheTtlMs?: number | null
152
+ /** Asymmetric mode: list() and get() return different structures. get() skips list cache. */
153
+ asymmetric?: boolean
154
+ /** Detail cache TTL in milliseconds (0=disabled (default), -1=infinite, >0=TTL). Only used when asymmetric=true. */
155
+ detailCacheTtlMs?: number
156
+ readOnly?: boolean
157
+ warmup?: boolean
158
+ authSensitive?: boolean
159
+ system?: boolean
160
+ scopeWhitelist?: string[] | null
161
+ isOwn?: ((record: T, user: unknown) => boolean) | null
162
+ children?: Record<string, ChildConfig>
163
+ parent?: ParentConfig | null
164
+ parents?: Record<string, ParentConfig>
165
+ relations?: Record<string, RelationConfig>
166
+ authAdapter?: EntityAuthAdapter | null
167
+ nav?: NavConfig
168
+ }
169
+
170
+ // ============ AUTH TYPES ============
171
+
172
+ /**
173
+ * Standard auth actions
174
+ */
175
+ export type AuthAction = 'read' | 'list' | 'create' | 'update' | 'delete'
176
+
177
+ /**
178
+ * Entity auth adapter interface
179
+ */
180
+ export interface EntityAuthAdapter {
181
+ canPerform(entity: string, action: AuthAction): boolean
182
+ canAccessRecord?(entity: string, record: unknown): boolean
183
+ isGranted?(permission: string, record?: unknown): boolean
184
+ getCurrentUser?(): unknown
185
+ }
186
+
187
+ // ============ SIGNAL TYPES ============
188
+
189
+ /**
190
+ * Signal handler context
191
+ */
192
+ export interface ListenerContext {
193
+ id?: string
194
+ cancel: () => void
195
+ emit: (signal: string, payload: unknown) => Promise<void>
196
+ }
197
+
198
+ /**
199
+ * Signal event object
200
+ */
201
+ export interface SignalEvent {
202
+ name: string
203
+ data: unknown
204
+ }
205
+
206
+ /**
207
+ * Signal handler function
208
+ */
209
+ export type SignalHandler = (
210
+ event: SignalEvent,
211
+ ctx: ListenerContext
212
+ ) => void | Promise<void>
213
+
214
+ // ============ HOOK TYPES ============
215
+
216
+ /**
217
+ * Hook registration options
218
+ */
219
+ export interface HookOptions {
220
+ priority?: number
221
+ id?: string
222
+ after?: string | string[]
223
+ once?: boolean
224
+ }
225
+
226
+ /**
227
+ * Hook event object
228
+ */
229
+ export interface HookEvent {
230
+ name: string
231
+ data: unknown
232
+ cancel: () => void
233
+ }
234
+
235
+ /**
236
+ * Hook handler function
237
+ */
238
+ export type HookHandler = (event: HookEvent) => void | Promise<void>
239
+
240
+ // ============ PERMISSION TYPES ============
241
+
242
+ /**
243
+ * Permission definition
244
+ */
245
+ export interface PermissionDefinition {
246
+ key: string
247
+ namespace: string
248
+ action: string
249
+ module: string | null
250
+ label: string
251
+ description: string | null
252
+ custom: boolean
253
+ }
254
+
255
+ // ============ FORWARD DECLARATIONS ============
256
+
257
+ // These will be properly typed when their modules are converted
258
+
259
+ /**
260
+ * IStorage interface (forward declaration)
261
+ */
262
+ export interface IStorage<T extends EntityRecord = EntityRecord> {
263
+ list(params?: ListParams, context?: unknown): Promise<ListResult<T>>
264
+ get(id: string | number, context?: unknown): Promise<T | null>
265
+ create(data: Partial<T>): Promise<T>
266
+ update(id: string | number, data: Partial<T>): Promise<T>
267
+ patch?(id: string | number, data: Partial<T>): Promise<T>
268
+ delete(id: string | number): Promise<void>
269
+ request?(
270
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
271
+ endpoint: string,
272
+ options?: RequestOptions
273
+ ): Promise<unknown>
274
+ reset?(): void
275
+ }
276
+
277
+ /**
278
+ * Request options for storage
279
+ */
280
+ export interface RequestOptions {
281
+ data?: unknown
282
+ params?: Record<string, unknown>
283
+ headers?: Record<string, string>
284
+ context?: unknown
285
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Type declarations for pluralize module
3
+ */
4
+ declare module 'pluralize' {
5
+ function pluralize(word: string, count?: number, inclusive?: boolean): string
6
+
7
+ namespace pluralize {
8
+ function plural(word: string): string
9
+ function singular(word: string): string
10
+ function isPlural(word: string): boolean
11
+ function isSingular(word: string): boolean
12
+ function addPluralRule(rule: string | RegExp, replacement: string): void
13
+ function addSingularRule(rule: string | RegExp, replacement: string): void
14
+ function addIrregularRule(single: string, plural: string): void
15
+ function addUncountableRule(word: string | RegExp): void
16
+ }
17
+
18
+ export = pluralize
19
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Type declarations for @quazardous/quarkernel
3
+ *
4
+ * quarkernel is an event-driven kernel library used by qdadm
5
+ * for signal/event handling.
6
+ */
7
+
8
+ declare module '@quazardous/quarkernel' {
9
+ export interface KernelOptions {
10
+ debug?: boolean
11
+ [key: string]: unknown
12
+ }
13
+
14
+ export interface ListenerOptions {
15
+ priority?: number
16
+ id?: string
17
+ after?: string | string[]
18
+ once?: boolean
19
+ }
20
+
21
+ export interface ListenerContext {
22
+ id?: string
23
+ cancel: () => void
24
+ emit: (signal: string, payload: unknown) => Promise<void>
25
+ }
26
+
27
+ export type ListenerCallback = (
28
+ event: { name: string; data: unknown },
29
+ ctx: ListenerContext
30
+ ) => void | Promise<void>
31
+
32
+ export interface ExecutionError {
33
+ listenerId: string
34
+ error: Error
35
+ }
36
+
37
+ export interface QuarKernel {
38
+ on(signal: string, handler: ListenerCallback, options?: ListenerOptions): () => void
39
+ once(signal: string, handler?: ListenerCallback, options?: ListenerOptions): () => void
40
+ off(signal: string, handler: ListenerCallback): void
41
+ emit(signal: string, payload?: unknown): Promise<void>
42
+ emitSerial(signal: string, payload?: unknown): Promise<void>
43
+ clearExecutionErrors(): void
44
+ getExecutionErrors(): ExecutionError[]
45
+ debug(enabled: boolean): void
46
+ destroy(): void
47
+ }
48
+
49
+ export function createKernel(options?: KernelOptions): QuarKernel
50
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Centralized formatting utilities
3
+ *
4
+ * Usage:
5
+ * import { formatDate, formatDateTime, formatDuration } from '@/dashboard/utils/formatters'
6
+ *
7
+ * {{ formatDate(item.created_at) }}
8
+ * {{ formatDateTime(item.updated_at) }}
9
+ */
10
+
11
+ // =============================================================================
12
+ // DATE/TIME FORMATTING
13
+ // =============================================================================
14
+
15
+ /**
16
+ * Format a date string to localized date+time
17
+ * Default format: toLocaleString() (e.g., "20/12/2025, 14:30:00")
18
+ *
19
+ * @param dateStr - ISO date string or Date object
20
+ * @param options - Intl.DateTimeFormat options
21
+ * @returns Formatted date string or '-' if empty
22
+ */
23
+ export function formatDate(
24
+ dateStr: string | Date | null | undefined,
25
+ options: Intl.DateTimeFormatOptions = {}
26
+ ): string {
27
+ if (!dateStr) return '-'
28
+ const date = new Date(dateStr)
29
+ if (isNaN(date.getTime())) return '-'
30
+
31
+ if (Object.keys(options).length === 0) {
32
+ return date.toLocaleString()
33
+ }
34
+ return date.toLocaleString(undefined, options)
35
+ }
36
+
37
+ /**
38
+ * Format to full datetime with year (20/12/2025 14:30)
39
+ */
40
+ export function formatDateTime(dateStr: string | Date | null | undefined): string {
41
+ return formatDate(dateStr, {
42
+ year: 'numeric',
43
+ month: '2-digit',
44
+ day: '2-digit',
45
+ hour: '2-digit',
46
+ minute: '2-digit',
47
+ })
48
+ }
49
+
50
+ /**
51
+ * Format to short datetime without year (20/12 14:30)
52
+ */
53
+ export function formatDateTimeShort(dateStr: string | Date | null | undefined): string {
54
+ return formatDate(dateStr, {
55
+ month: '2-digit',
56
+ day: '2-digit',
57
+ hour: '2-digit',
58
+ minute: '2-digit',
59
+ })
60
+ }
61
+
62
+ /**
63
+ * Format to date only (20/12/2025)
64
+ */
65
+ export function formatDateOnly(dateStr: string | Date | null | undefined): string {
66
+ if (!dateStr) return '-'
67
+ const date = new Date(dateStr)
68
+ if (isNaN(date.getTime())) return '-'
69
+ return date.toLocaleDateString(undefined, {
70
+ year: 'numeric',
71
+ month: '2-digit',
72
+ day: '2-digit',
73
+ })
74
+ }
75
+
76
+ /**
77
+ * Format to time only (14:30:00)
78
+ */
79
+ export function formatTimeOnly(dateStr: string | Date | null | undefined): string {
80
+ if (!dateStr) return '-'
81
+ const date = new Date(dateStr)
82
+ if (isNaN(date.getTime())) return '-'
83
+ return date.toLocaleTimeString()
84
+ }
85
+
86
+ /**
87
+ * Format with "Never" as default (for last_used fields)
88
+ */
89
+ export function formatDateOrNever(dateStr: string | Date | null | undefined): string {
90
+ if (!dateStr) return 'Never'
91
+ return formatDate(dateStr)
92
+ }
93
+
94
+ // =============================================================================
95
+ // DURATION FORMATTING
96
+ // =============================================================================
97
+
98
+ /**
99
+ * Format a duration in milliseconds to human-readable string
100
+ *
101
+ * @param ms - Duration in milliseconds
102
+ * @returns Human-readable duration (e.g., "2h 30m 15s" or "45.2s")
103
+ */
104
+ export function formatDuration(ms: number | null | undefined): string {
105
+ if (ms === null || ms === undefined) return '-'
106
+ if (ms < 1000) return `${ms}ms`
107
+
108
+ const seconds = Math.floor(ms / 1000)
109
+ const minutes = Math.floor(seconds / 60)
110
+ const hours = Math.floor(minutes / 60)
111
+
112
+ if (hours > 0) {
113
+ const remainingMinutes = minutes % 60
114
+ const remainingSeconds = seconds % 60
115
+ return `${hours}h ${remainingMinutes}m ${remainingSeconds}s`
116
+ }
117
+ if (minutes > 0) {
118
+ const remainingSeconds = seconds % 60
119
+ return `${minutes}m ${remainingSeconds}s`
120
+ }
121
+ // Show decimal seconds for short durations
122
+ return `${(ms / 1000).toFixed(1)}s`
123
+ }
124
+
125
+ /**
126
+ * Format duration from start/end timestamps
127
+ */
128
+ export function formatDurationBetween(
129
+ startDate: string | Date | null | undefined,
130
+ endDate: string | Date | null | undefined
131
+ ): string {
132
+ if (!startDate || !endDate) return '-'
133
+ const start = new Date(startDate)
134
+ const end = new Date(endDate)
135
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) return '-'
136
+ return formatDuration(end.getTime() - start.getTime())
137
+ }
138
+
139
+ // =============================================================================
140
+ // NUMBER FORMATTING
141
+ // =============================================================================
142
+
143
+ /**
144
+ * Format a number with locale-specific separators
145
+ */
146
+ export function formatNumber(
147
+ value: number | null | undefined,
148
+ options: Intl.NumberFormatOptions = {}
149
+ ): string {
150
+ if (value === null || value === undefined) return '-'
151
+ return value.toLocaleString(undefined, options)
152
+ }
153
+
154
+ /**
155
+ * Format bytes to human-readable size
156
+ */
157
+ export function formatBytes(bytes: number | null | undefined): string {
158
+ if (bytes === null || bytes === undefined) return '-'
159
+ if (bytes === 0) return '0 B'
160
+
161
+ const units = ['B', 'KB', 'MB', 'GB', 'TB']
162
+ const k = 1024
163
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
164
+
165
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${units[i]}`
166
+ }
167
+
168
+ /**
169
+ * Format a percentage
170
+ */
171
+ export function formatPercent(value: number | null | undefined, decimals = 1): string {
172
+ if (value === null || value === undefined) return '-'
173
+ return `${(value * 100).toFixed(decimals)}%`
174
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * qdadm - Utils exports
3
+ */
4
+
5
+ // Formatters
6
+ export {
7
+ formatDate,
8
+ formatDateTime,
9
+ formatDateTimeShort,
10
+ formatDateOnly,
11
+ formatTimeOnly,
12
+ formatDateOrNever,
13
+ formatDuration,
14
+ formatDurationBetween,
15
+ formatNumber,
16
+ formatBytes,
17
+ formatPercent,
18
+ } from './formatters'
19
+
20
+ // Transformers
21
+ export {
22
+ toKeyValueArray,
23
+ toKeyValueObject,
24
+ toLanguageCodes,
25
+ toLanguageObjects,
26
+ toDateObject,
27
+ toIsoDate,
28
+ toIsoDateTime,
29
+ deepClone,
30
+ getSparseUpdate,
31
+ isEqual,
32
+ } from './transformers'
33
+
34
+ // Type exports
35
+ export type { KeyValuePair, LanguageObject } from './transformers'
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Data transformation utilities for forms
3
+ *
4
+ * Provides consistent conversions between API data formats and form structures.
5
+ */
6
+
7
+ /**
8
+ * Key-value pair structure for KeyValueEditor
9
+ */
10
+ export interface KeyValuePair {
11
+ key: string
12
+ value: unknown
13
+ }
14
+
15
+ /**
16
+ * Language object structure
17
+ */
18
+ export interface LanguageObject {
19
+ code: string
20
+ fluency: number
21
+ }
22
+
23
+ /**
24
+ * Convert object {key: value} to array [{key, value}] for KeyValueEditor
25
+ */
26
+ export function toKeyValueArray(obj: Record<string, unknown> | null | undefined): KeyValuePair[] {
27
+ if (!obj || typeof obj !== 'object') return []
28
+ return Object.entries(obj).map(([key, value]) => ({ key, value }))
29
+ }
30
+
31
+ /**
32
+ * Convert array [{key, value}] to object {key: value} for API
33
+ */
34
+ export function toKeyValueObject(arr: KeyValuePair[] | null | undefined): Record<string, unknown> {
35
+ if (!Array.isArray(arr)) return {}
36
+ return Object.fromEntries(
37
+ arr.filter((item) => item.key && item.key.trim()).map((item) => [item.key.trim(), item.value])
38
+ )
39
+ }
40
+
41
+ /**
42
+ * Convert language objects [{code, fluency}] to array of codes ['en', 'fr']
43
+ */
44
+ export function toLanguageCodes(
45
+ languages: Array<string | LanguageObject> | null | undefined
46
+ ): string[] {
47
+ if (!Array.isArray(languages)) return []
48
+ return languages
49
+ .map((l) => (typeof l === 'string' ? l : l.code))
50
+ .filter((code): code is string => Boolean(code))
51
+ }
52
+
53
+ /**
54
+ * Convert array of codes to language objects [{code, fluency}]
55
+ */
56
+ export function toLanguageObjects(
57
+ codes: Array<string | LanguageObject> | null | undefined,
58
+ defaultFluency = 1
59
+ ): LanguageObject[] {
60
+ if (!Array.isArray(codes)) return []
61
+ return codes.filter(Boolean).map((code) => ({
62
+ code: typeof code === 'string' ? code : code.code,
63
+ fluency: typeof code === 'object' && code !== null ? code.fluency : defaultFluency,
64
+ }))
65
+ }
66
+
67
+ /**
68
+ * Convert ISO date string to Date object
69
+ */
70
+ export function toDateObject(dateStr: string | Date | null | undefined): Date | null {
71
+ if (!dateStr) return null
72
+ if (dateStr instanceof Date) return dateStr
73
+ return new Date(dateStr)
74
+ }
75
+
76
+ /**
77
+ * Convert Date to ISO date string (YYYY-MM-DD)
78
+ */
79
+ export function toIsoDate(date: string | Date | null | undefined): string | null {
80
+ if (!date) return null
81
+ if (typeof date === 'string') return date.split('T')[0] ?? date
82
+ return date.toISOString().split('T')[0] ?? null
83
+ }
84
+
85
+ /**
86
+ * Convert Date to ISO datetime string
87
+ */
88
+ export function toIsoDateTime(date: string | Date | null | undefined): string | null {
89
+ if (!date) return null
90
+ if (typeof date === 'string') return date
91
+ return date.toISOString()
92
+ }
93
+
94
+ /**
95
+ * Deep clone an object (for snapshots)
96
+ */
97
+ export function deepClone<T>(obj: T): T {
98
+ if (obj === null || typeof obj !== 'object') return obj
99
+ if (obj instanceof Date) return new Date(obj) as unknown as T
100
+ if (Array.isArray(obj)) return obj.map(deepClone) as unknown as T
101
+ return Object.fromEntries(
102
+ Object.entries(obj as Record<string, unknown>).map(([k, v]) => [k, deepClone(v)])
103
+ ) as unknown as T
104
+ }
105
+
106
+ /**
107
+ * Get sparse update object (only changed fields)
108
+ */
109
+ export function getSparseUpdate<T extends Record<string, unknown>>(
110
+ original: T,
111
+ modified: T,
112
+ excludeFields: string[] = []
113
+ ): Partial<T> {
114
+ const patch: Partial<T> = {}
115
+ for (const key in modified) {
116
+ if (excludeFields.includes(key)) continue
117
+ const origVal = JSON.stringify(original[key])
118
+ const modVal = JSON.stringify(modified[key])
119
+ if (origVal !== modVal) {
120
+ patch[key as keyof T] = modified[key]
121
+ }
122
+ }
123
+ return patch
124
+ }
125
+
126
+ /**
127
+ * Check if two values are deeply equal
128
+ */
129
+ export function isEqual(a: unknown, b: unknown): boolean {
130
+ return JSON.stringify(a) === JSON.stringify(b)
131
+ }