qdadm 0.13.0

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 (82) hide show
  1. package/CHANGELOG.md +270 -0
  2. package/LICENSE +21 -0
  3. package/README.md +166 -0
  4. package/package.json +48 -0
  5. package/src/assets/logo.svg +6 -0
  6. package/src/components/BoolCell.vue +28 -0
  7. package/src/components/dialogs/BulkStatusDialog.vue +43 -0
  8. package/src/components/dialogs/MultiStepDialog.vue +321 -0
  9. package/src/components/dialogs/SimpleDialog.vue +108 -0
  10. package/src/components/dialogs/UnsavedChangesDialog.vue +87 -0
  11. package/src/components/display/CardsGrid.vue +155 -0
  12. package/src/components/display/CopyableId.vue +92 -0
  13. package/src/components/display/EmptyState.vue +114 -0
  14. package/src/components/display/IntensityBar.vue +171 -0
  15. package/src/components/display/RichCardsGrid.vue +220 -0
  16. package/src/components/editors/JsonEditorFoldable.vue +467 -0
  17. package/src/components/editors/JsonStructuredField.vue +218 -0
  18. package/src/components/editors/JsonViewer.vue +91 -0
  19. package/src/components/editors/KeyValueEditor.vue +314 -0
  20. package/src/components/editors/LanguageEditor.vue +245 -0
  21. package/src/components/editors/ScopeEditor.vue +341 -0
  22. package/src/components/editors/VanillaJsonEditor.vue +185 -0
  23. package/src/components/forms/FormActions.vue +104 -0
  24. package/src/components/forms/FormField.vue +64 -0
  25. package/src/components/forms/FormTab.vue +217 -0
  26. package/src/components/forms/FormTabs.vue +108 -0
  27. package/src/components/index.js +44 -0
  28. package/src/components/layout/AppLayout.vue +430 -0
  29. package/src/components/layout/Breadcrumb.vue +106 -0
  30. package/src/components/layout/PageHeader.vue +75 -0
  31. package/src/components/layout/PageLayout.vue +93 -0
  32. package/src/components/lists/ActionButtons.vue +41 -0
  33. package/src/components/lists/ActionColumn.vue +37 -0
  34. package/src/components/lists/FilterBar.vue +53 -0
  35. package/src/components/lists/ListPage.vue +319 -0
  36. package/src/composables/index.js +19 -0
  37. package/src/composables/useApp.js +43 -0
  38. package/src/composables/useAuth.js +49 -0
  39. package/src/composables/useBareForm.js +143 -0
  40. package/src/composables/useBreadcrumb.js +221 -0
  41. package/src/composables/useDirtyState.js +103 -0
  42. package/src/composables/useEntityTitle.js +121 -0
  43. package/src/composables/useForm.js +254 -0
  44. package/src/composables/useGuardStore.js +37 -0
  45. package/src/composables/useJsonSyntax.js +101 -0
  46. package/src/composables/useListPageBuilder.js +1176 -0
  47. package/src/composables/useNavigation.js +89 -0
  48. package/src/composables/usePageBuilder.js +334 -0
  49. package/src/composables/useStatus.js +146 -0
  50. package/src/composables/useSubEditor.js +165 -0
  51. package/src/composables/useTabSync.js +110 -0
  52. package/src/composables/useUnsavedChangesGuard.js +122 -0
  53. package/src/entity/EntityManager.js +540 -0
  54. package/src/entity/index.js +11 -0
  55. package/src/entity/storage/ApiStorage.js +146 -0
  56. package/src/entity/storage/LocalStorage.js +220 -0
  57. package/src/entity/storage/MemoryStorage.js +201 -0
  58. package/src/entity/storage/index.js +10 -0
  59. package/src/index.js +29 -0
  60. package/src/kernel/Kernel.js +234 -0
  61. package/src/kernel/index.js +7 -0
  62. package/src/module/index.js +16 -0
  63. package/src/module/moduleRegistry.js +222 -0
  64. package/src/orchestrator/Orchestrator.js +141 -0
  65. package/src/orchestrator/index.js +8 -0
  66. package/src/orchestrator/useOrchestrator.js +61 -0
  67. package/src/plugin.js +142 -0
  68. package/src/styles/_alerts.css +48 -0
  69. package/src/styles/_code.css +33 -0
  70. package/src/styles/_dialogs.css +17 -0
  71. package/src/styles/_markdown.css +82 -0
  72. package/src/styles/_show-pages.css +84 -0
  73. package/src/styles/index.css +16 -0
  74. package/src/styles/main.css +845 -0
  75. package/src/styles/theme/components.css +286 -0
  76. package/src/styles/theme/index.css +10 -0
  77. package/src/styles/theme/tokens.css +125 -0
  78. package/src/styles/theme/utilities.css +172 -0
  79. package/src/utils/debugInjector.js +261 -0
  80. package/src/utils/formatters.js +165 -0
  81. package/src/utils/index.js +35 -0
  82. package/src/utils/transformers.js +105 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * LocalStorage - Browser localStorage storage adapter
3
+ *
4
+ * Implements the storage interface using browser localStorage.
5
+ * Useful for:
6
+ * - Offline-first applications
7
+ * - User preferences
8
+ * - Draft saving
9
+ * - Small datasets that persist across sessions
10
+ *
11
+ * Usage:
12
+ * ```js
13
+ * const storage = new LocalStorage({
14
+ * key: 'my_entities',
15
+ * idField: 'id',
16
+ * generateId: () => crypto.randomUUID()
17
+ * })
18
+ * ```
19
+ */
20
+ export class LocalStorage {
21
+ constructor(options = {}) {
22
+ const {
23
+ key,
24
+ idField = 'id',
25
+ generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2)
26
+ } = options
27
+
28
+ this.key = key
29
+ this.idField = idField
30
+ this.generateId = generateId
31
+ }
32
+
33
+ /**
34
+ * Get all items from localStorage
35
+ * @returns {Array}
36
+ */
37
+ _getAll() {
38
+ try {
39
+ const stored = localStorage.getItem(this.key)
40
+ return stored ? JSON.parse(stored) : []
41
+ } catch {
42
+ return []
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Save all items to localStorage
48
+ * @param {Array} items
49
+ */
50
+ _saveAll(items) {
51
+ localStorage.setItem(this.key, JSON.stringify(items))
52
+ }
53
+
54
+ /**
55
+ * List entities with pagination/filtering
56
+ * @param {object} params - { page, page_size, sort_by, sort_order, ...filters }
57
+ * @returns {Promise<{ items: Array, total: number }>}
58
+ */
59
+ async list(params = {}) {
60
+ const { page = 1, page_size = 20, sort_by, sort_order = 'asc', ...filters } = params
61
+
62
+ let items = this._getAll()
63
+
64
+ // Apply filters
65
+ for (const [key, value] of Object.entries(filters)) {
66
+ if (value === null || value === undefined || value === '') continue
67
+ items = items.filter(item => {
68
+ const itemValue = item[key]
69
+ if (typeof value === 'string' && typeof itemValue === 'string') {
70
+ return itemValue.toLowerCase().includes(value.toLowerCase())
71
+ }
72
+ return itemValue === value
73
+ })
74
+ }
75
+
76
+ const total = items.length
77
+
78
+ // Apply sorting
79
+ if (sort_by) {
80
+ items.sort((a, b) => {
81
+ const aVal = a[sort_by]
82
+ const bVal = b[sort_by]
83
+ if (aVal < bVal) return sort_order === 'asc' ? -1 : 1
84
+ if (aVal > bVal) return sort_order === 'asc' ? 1 : -1
85
+ return 0
86
+ })
87
+ }
88
+
89
+ // Apply pagination
90
+ const start = (page - 1) * page_size
91
+ items = items.slice(start, start + page_size)
92
+
93
+ return { items, total }
94
+ }
95
+
96
+ /**
97
+ * Get a single entity by ID
98
+ * @param {string|number} id
99
+ * @returns {Promise<object>}
100
+ */
101
+ async get(id) {
102
+ const items = this._getAll()
103
+ const item = items.find(i => i[this.idField] === id)
104
+ if (!item) {
105
+ const error = new Error(`Entity not found: ${id}`)
106
+ error.status = 404
107
+ throw error
108
+ }
109
+ return item
110
+ }
111
+
112
+ /**
113
+ * Get multiple entities by IDs (batch fetch)
114
+ * @param {Array<string|number>} ids
115
+ * @returns {Promise<Array<object>>}
116
+ */
117
+ async getMany(ids) {
118
+ if (!ids || ids.length === 0) return []
119
+ const items = this._getAll()
120
+ const idSet = new Set(ids)
121
+ return items.filter(i => idSet.has(i[this.idField]))
122
+ }
123
+
124
+ /**
125
+ * Create a new entity
126
+ * @param {object} data
127
+ * @returns {Promise<object>}
128
+ */
129
+ async create(data) {
130
+ const items = this._getAll()
131
+ const newItem = {
132
+ ...data,
133
+ [this.idField]: data[this.idField] || this.generateId(),
134
+ created_at: data.created_at || new Date().toISOString()
135
+ }
136
+ items.push(newItem)
137
+ this._saveAll(items)
138
+ return newItem
139
+ }
140
+
141
+ /**
142
+ * Update an entity (PUT - full replacement)
143
+ * @param {string|number} id
144
+ * @param {object} data
145
+ * @returns {Promise<object>}
146
+ */
147
+ async update(id, data) {
148
+ const items = this._getAll()
149
+ const index = items.findIndex(i => i[this.idField] === id)
150
+ if (index === -1) {
151
+ const error = new Error(`Entity not found: ${id}`)
152
+ error.status = 404
153
+ throw error
154
+ }
155
+ const updated = {
156
+ ...data,
157
+ [this.idField]: id,
158
+ updated_at: new Date().toISOString()
159
+ }
160
+ items[index] = updated
161
+ this._saveAll(items)
162
+ return updated
163
+ }
164
+
165
+ /**
166
+ * Partially update an entity (PATCH)
167
+ * @param {string|number} id
168
+ * @param {object} data
169
+ * @returns {Promise<object>}
170
+ */
171
+ async patch(id, data) {
172
+ const items = this._getAll()
173
+ const index = items.findIndex(i => i[this.idField] === id)
174
+ if (index === -1) {
175
+ const error = new Error(`Entity not found: ${id}`)
176
+ error.status = 404
177
+ throw error
178
+ }
179
+ const updated = {
180
+ ...items[index],
181
+ ...data,
182
+ updated_at: new Date().toISOString()
183
+ }
184
+ items[index] = updated
185
+ this._saveAll(items)
186
+ return updated
187
+ }
188
+
189
+ /**
190
+ * Delete an entity
191
+ * @param {string|number} id
192
+ * @returns {Promise<void>}
193
+ */
194
+ async delete(id) {
195
+ const items = this._getAll()
196
+ const index = items.findIndex(i => i[this.idField] === id)
197
+ if (index === -1) {
198
+ const error = new Error(`Entity not found: ${id}`)
199
+ error.status = 404
200
+ throw error
201
+ }
202
+ items.splice(index, 1)
203
+ this._saveAll(items)
204
+ }
205
+
206
+ /**
207
+ * Clear all items
208
+ * @returns {Promise<void>}
209
+ */
210
+ async clear() {
211
+ localStorage.removeItem(this.key)
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Factory function to create a LocalStorage
217
+ */
218
+ export function createLocalStorage(options) {
219
+ return new LocalStorage(options)
220
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * MemoryStorage - In-memory storage adapter
3
+ *
4
+ * Implements the storage interface using in-memory Map.
5
+ * Useful for:
6
+ * - Testing
7
+ * - Ephemeral data
8
+ * - Caching layer
9
+ * - Client-side state management
10
+ *
11
+ * Usage:
12
+ * ```js
13
+ * const storage = new MemoryStorage({
14
+ * idField: 'id',
15
+ * generateId: () => crypto.randomUUID(),
16
+ * initialData: [{ id: '1', name: 'Alice' }]
17
+ * })
18
+ * ```
19
+ */
20
+ export class MemoryStorage {
21
+ constructor(options = {}) {
22
+ const {
23
+ idField = 'id',
24
+ generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2),
25
+ initialData = []
26
+ } = options
27
+
28
+ this.idField = idField
29
+ this.generateId = generateId
30
+ this._data = new Map()
31
+
32
+ // Initialize with initial data
33
+ for (const item of initialData) {
34
+ const id = item[idField]
35
+ if (id) {
36
+ this._data.set(String(id), { ...item })
37
+ }
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Get all items as array
43
+ * @returns {Array}
44
+ */
45
+ _getAll() {
46
+ return Array.from(this._data.values())
47
+ }
48
+
49
+ /**
50
+ * List entities with pagination/filtering
51
+ * @param {object} params - { page, page_size, sort_by, sort_order, ...filters }
52
+ * @returns {Promise<{ items: Array, total: number }>}
53
+ */
54
+ async list(params = {}) {
55
+ const { page = 1, page_size = 20, sort_by, sort_order = 'asc', ...filters } = params
56
+
57
+ let items = this._getAll()
58
+
59
+ // Apply filters
60
+ for (const [key, value] of Object.entries(filters)) {
61
+ if (value === null || value === undefined || value === '') continue
62
+ items = items.filter(item => {
63
+ const itemValue = item[key]
64
+ if (typeof value === 'string' && typeof itemValue === 'string') {
65
+ return itemValue.toLowerCase().includes(value.toLowerCase())
66
+ }
67
+ return itemValue === value
68
+ })
69
+ }
70
+
71
+ const total = items.length
72
+
73
+ // Apply sorting
74
+ if (sort_by) {
75
+ items.sort((a, b) => {
76
+ const aVal = a[sort_by]
77
+ const bVal = b[sort_by]
78
+ if (aVal < bVal) return sort_order === 'asc' ? -1 : 1
79
+ if (aVal > bVal) return sort_order === 'asc' ? 1 : -1
80
+ return 0
81
+ })
82
+ }
83
+
84
+ // Apply pagination
85
+ const start = (page - 1) * page_size
86
+ items = items.slice(start, start + page_size)
87
+
88
+ return { items, total }
89
+ }
90
+
91
+ /**
92
+ * Get a single entity by ID
93
+ * @param {string|number} id
94
+ * @returns {Promise<object>}
95
+ */
96
+ async get(id) {
97
+ const item = this._data.get(String(id))
98
+ if (!item) {
99
+ const error = new Error(`Entity not found: ${id}`)
100
+ error.status = 404
101
+ throw error
102
+ }
103
+ return { ...item }
104
+ }
105
+
106
+ /**
107
+ * Create a new entity
108
+ * @param {object} data
109
+ * @returns {Promise<object>}
110
+ */
111
+ async create(data) {
112
+ const id = data[this.idField] || this.generateId()
113
+ const newItem = {
114
+ ...data,
115
+ [this.idField]: id,
116
+ created_at: data.created_at || new Date().toISOString()
117
+ }
118
+ this._data.set(String(id), newItem)
119
+ return { ...newItem }
120
+ }
121
+
122
+ /**
123
+ * Update an entity (PUT - full replacement)
124
+ * @param {string|number} id
125
+ * @param {object} data
126
+ * @returns {Promise<object>}
127
+ */
128
+ async update(id, data) {
129
+ if (!this._data.has(String(id))) {
130
+ const error = new Error(`Entity not found: ${id}`)
131
+ error.status = 404
132
+ throw error
133
+ }
134
+ const updated = {
135
+ ...data,
136
+ [this.idField]: id,
137
+ updated_at: new Date().toISOString()
138
+ }
139
+ this._data.set(String(id), updated)
140
+ return { ...updated }
141
+ }
142
+
143
+ /**
144
+ * Partially update an entity (PATCH)
145
+ * @param {string|number} id
146
+ * @param {object} data
147
+ * @returns {Promise<object>}
148
+ */
149
+ async patch(id, data) {
150
+ const existing = this._data.get(String(id))
151
+ if (!existing) {
152
+ const error = new Error(`Entity not found: ${id}`)
153
+ error.status = 404
154
+ throw error
155
+ }
156
+ const updated = {
157
+ ...existing,
158
+ ...data,
159
+ updated_at: new Date().toISOString()
160
+ }
161
+ this._data.set(String(id), updated)
162
+ return { ...updated }
163
+ }
164
+
165
+ /**
166
+ * Delete an entity
167
+ * @param {string|number} id
168
+ * @returns {Promise<void>}
169
+ */
170
+ async delete(id) {
171
+ if (!this._data.has(String(id))) {
172
+ const error = new Error(`Entity not found: ${id}`)
173
+ error.status = 404
174
+ throw error
175
+ }
176
+ this._data.delete(String(id))
177
+ }
178
+
179
+ /**
180
+ * Clear all items
181
+ * @returns {Promise<void>}
182
+ */
183
+ async clear() {
184
+ this._data.clear()
185
+ }
186
+
187
+ /**
188
+ * Get current item count
189
+ * @returns {number}
190
+ */
191
+ get size() {
192
+ return this._data.size
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Factory function to create a MemoryStorage
198
+ */
199
+ export function createMemoryStorage(options) {
200
+ return new MemoryStorage(options)
201
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Storage Adapters
3
+ *
4
+ * Boilerplate implementations for common storage backends.
5
+ * EntityManagers can use these or implement their own storage.
6
+ */
7
+
8
+ export { ApiStorage, createApiStorage } from './ApiStorage.js'
9
+ export { LocalStorage, createLocalStorage } from './LocalStorage.js'
10
+ export { MemoryStorage, createMemoryStorage } from './MemoryStorage.js'
package/src/index.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * qdadm - Vue 3 Admin Dashboard Framework
3
+ *
4
+ * A framework for building admin dashboards with Vue 3, PrimeVue, and Vue Router.
5
+ */
6
+
7
+ // Kernel (simplified bootstrap)
8
+ export * from './kernel/index.js'
9
+
10
+ // Plugin (manual bootstrap)
11
+ export { createQdadm } from './plugin.js'
12
+
13
+ // Entity system
14
+ export * from './entity/index.js'
15
+
16
+ // Orchestrator
17
+ export * from './orchestrator/index.js'
18
+
19
+ // Composables
20
+ export * from './composables/index.js'
21
+
22
+ // Components
23
+ export * from './components/index.js'
24
+
25
+ // Module system
26
+ export * from './module/index.js'
27
+
28
+ // Utils
29
+ export * from './utils/index.js'
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Kernel - Simplified bootstrap for qdadm applications
3
+ *
4
+ * Handles all the boilerplate:
5
+ * - Vue app creation
6
+ * - Pinia, PrimeVue, ToastService, ConfirmationService
7
+ * - Router with auth guard
8
+ * - Module discovery
9
+ * - qdadm plugin installation
10
+ *
11
+ * The constructor is purely declarative (stores config only).
12
+ * All initialization happens in createApp(), allowing you to:
13
+ * - Modify options before app creation
14
+ * - Add custom plugins/directives after createApp() but before mount()
15
+ *
16
+ * Basic usage:
17
+ * ```js
18
+ * const kernel = new Kernel({ root: App, managers, authAdapter, ... })
19
+ * kernel.createApp().mount('#app')
20
+ * ```
21
+ *
22
+ * Advanced usage (tweaking before/after):
23
+ * ```js
24
+ * const kernel = new Kernel({ ... })
25
+ *
26
+ * // Tweak config before app creation
27
+ * kernel.options.features.poweredBy = false
28
+ *
29
+ * // Create app
30
+ * const app = kernel.createApp()
31
+ *
32
+ * // Add custom plugins/components before mount
33
+ * app.component('MyGlobalComponent', MyComponent)
34
+ * app.directive('focus', focusDirective)
35
+ *
36
+ * app.mount('#app')
37
+ * ```
38
+ */
39
+
40
+ import { createApp } from 'vue'
41
+ import { createPinia } from 'pinia'
42
+ import { createRouter, createWebHistory } from 'vue-router'
43
+ import ToastService from 'primevue/toastservice'
44
+ import ConfirmationService from 'primevue/confirmationservice'
45
+ import Tooltip from 'primevue/tooltip'
46
+
47
+ import { createQdadm } from '../plugin.js'
48
+ import { initModules, getRoutes, setSectionOrder } from '../module/moduleRegistry.js'
49
+ import { Orchestrator } from '../orchestrator/Orchestrator.js'
50
+
51
+ export class Kernel {
52
+ /**
53
+ * @param {object} options
54
+ * @param {object} options.root - Root Vue component
55
+ * @param {object} options.modules - Result of import.meta.glob for module init files
56
+ * @param {string[]} options.sectionOrder - Navigation section order
57
+ * @param {object} options.managers - Entity managers { name: EntityManager }
58
+ * @param {object} options.authAdapter - Auth adapter for login/logout
59
+ * @param {object} options.pages - Page components { login, layout }
60
+ * @param {string} options.homeRoute - Route name for home redirect
61
+ * @param {object} options.app - App config { name, shortName, version, logo, theme }
62
+ * @param {object} options.features - Feature toggles { auth, poweredBy }
63
+ * @param {object} options.primevue - PrimeVue config { plugin, theme, options }
64
+ */
65
+ constructor(options) {
66
+ this.options = options
67
+ this.vueApp = null
68
+ this.router = null
69
+ this.orchestrator = null
70
+ }
71
+
72
+ /**
73
+ * Create and configure the Vue app
74
+ * @returns {App} Vue app instance ready to mount
75
+ */
76
+ createApp() {
77
+ this._initModules()
78
+ this._createRouter()
79
+ this._createOrchestrator()
80
+ this._createVueApp()
81
+ this._installPlugins()
82
+ return this.vueApp
83
+ }
84
+
85
+ /**
86
+ * Initialize modules from glob import
87
+ */
88
+ _initModules() {
89
+ if (this.options.sectionOrder) {
90
+ setSectionOrder(this.options.sectionOrder)
91
+ }
92
+ if (this.options.modules) {
93
+ initModules(this.options.modules)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Create Vue Router with auth guard
99
+ */
100
+ _createRouter() {
101
+ const { pages, homeRoute, authAdapter } = this.options
102
+
103
+ // Validate required pages
104
+ if (!pages?.login) {
105
+ throw new Error('[Kernel] pages.login is required')
106
+ }
107
+ if (!pages?.layout) {
108
+ throw new Error('[Kernel] pages.layout is required')
109
+ }
110
+
111
+ // Build routes
112
+ const routes = [
113
+ {
114
+ path: '/login',
115
+ name: 'login',
116
+ component: pages.login
117
+ },
118
+ {
119
+ path: '/',
120
+ component: pages.layout,
121
+ meta: { requiresAuth: true },
122
+ children: [
123
+ { path: '', redirect: { name: homeRoute || 'home' } },
124
+ ...getRoutes()
125
+ ]
126
+ }
127
+ ]
128
+
129
+ this.router = createRouter({
130
+ history: createWebHistory(),
131
+ routes
132
+ })
133
+
134
+ // Auth guard
135
+ if (authAdapter) {
136
+ this.router.beforeEach((to, from, next) => {
137
+ if (to.meta.requiresAuth && !authAdapter.isAuthenticated()) {
138
+ next({ name: 'login' })
139
+ } else {
140
+ next()
141
+ }
142
+ })
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Create orchestrator with managers
148
+ */
149
+ _createOrchestrator() {
150
+ this.orchestrator = new Orchestrator({
151
+ managers: this.options.managers || {}
152
+ })
153
+ }
154
+
155
+ /**
156
+ * Create Vue app instance
157
+ */
158
+ _createVueApp() {
159
+ if (!this.options.root) {
160
+ throw new Error('[Kernel] root component is required')
161
+ }
162
+ this.vueApp = createApp(this.options.root)
163
+ }
164
+
165
+ /**
166
+ * Install all plugins on Vue app
167
+ */
168
+ _installPlugins() {
169
+ const app = this.vueApp
170
+ const { managers, authAdapter, features, primevue } = this.options
171
+
172
+ // Pinia
173
+ app.use(createPinia())
174
+
175
+ // PrimeVue (plugin passed by app to avoid peer dep issues)
176
+ if (primevue?.plugin) {
177
+ const pvConfig = {
178
+ theme: {
179
+ preset: primevue.theme,
180
+ options: {
181
+ darkModeSelector: '.dark-mode',
182
+ ...primevue.options
183
+ }
184
+ }
185
+ }
186
+ app.use(primevue.plugin, pvConfig)
187
+ app.use(ToastService)
188
+ app.use(ConfirmationService)
189
+ app.directive('tooltip', Tooltip)
190
+ }
191
+
192
+ // Router
193
+ app.use(this.router)
194
+
195
+ // qdadm plugin
196
+ app.use(createQdadm({
197
+ orchestrator: this.orchestrator,
198
+ managers,
199
+ authAdapter,
200
+ router: this.router,
201
+ toast: app.config.globalProperties.$toast,
202
+ app: this.options.app,
203
+ features: {
204
+ auth: !!authAdapter,
205
+ poweredBy: true,
206
+ ...features
207
+ }
208
+ }))
209
+ }
210
+
211
+ /**
212
+ * Get the Vue Router instance
213
+ * @returns {Router}
214
+ */
215
+ getRouter() {
216
+ return this.router
217
+ }
218
+
219
+ /**
220
+ * Get the Vue app instance (available after mount)
221
+ * @returns {App}
222
+ */
223
+ getApp() {
224
+ return this.vueApp
225
+ }
226
+
227
+ /**
228
+ * Get the Orchestrator instance
229
+ * @returns {Orchestrator}
230
+ */
231
+ getOrchestrator() {
232
+ return this.orchestrator
233
+ }
234
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Kernel Module
3
+ *
4
+ * Simplified bootstrap for qdadm applications.
5
+ */
6
+
7
+ export { Kernel } from './Kernel.js'
@@ -0,0 +1,16 @@
1
+ /**
2
+ * qdadm - Module system exports
3
+ */
4
+
5
+ export {
6
+ registry,
7
+ initModules,
8
+ setSectionOrder,
9
+ getRoutes,
10
+ getNavSections,
11
+ getRouteFamilies,
12
+ getEntityConfigs,
13
+ getEntityConfig,
14
+ isRouteInFamily,
15
+ resetRegistry
16
+ } from './moduleRegistry'