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.
- package/CHANGELOG.md +270 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/package.json +48 -0
- package/src/assets/logo.svg +6 -0
- package/src/components/BoolCell.vue +28 -0
- package/src/components/dialogs/BulkStatusDialog.vue +43 -0
- package/src/components/dialogs/MultiStepDialog.vue +321 -0
- package/src/components/dialogs/SimpleDialog.vue +108 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +87 -0
- package/src/components/display/CardsGrid.vue +155 -0
- package/src/components/display/CopyableId.vue +92 -0
- package/src/components/display/EmptyState.vue +114 -0
- package/src/components/display/IntensityBar.vue +171 -0
- package/src/components/display/RichCardsGrid.vue +220 -0
- package/src/components/editors/JsonEditorFoldable.vue +467 -0
- package/src/components/editors/JsonStructuredField.vue +218 -0
- package/src/components/editors/JsonViewer.vue +91 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +245 -0
- package/src/components/editors/ScopeEditor.vue +341 -0
- package/src/components/editors/VanillaJsonEditor.vue +185 -0
- package/src/components/forms/FormActions.vue +104 -0
- package/src/components/forms/FormField.vue +64 -0
- package/src/components/forms/FormTab.vue +217 -0
- package/src/components/forms/FormTabs.vue +108 -0
- package/src/components/index.js +44 -0
- package/src/components/layout/AppLayout.vue +430 -0
- package/src/components/layout/Breadcrumb.vue +106 -0
- package/src/components/layout/PageHeader.vue +75 -0
- package/src/components/layout/PageLayout.vue +93 -0
- package/src/components/lists/ActionButtons.vue +41 -0
- package/src/components/lists/ActionColumn.vue +37 -0
- package/src/components/lists/FilterBar.vue +53 -0
- package/src/components/lists/ListPage.vue +319 -0
- package/src/composables/index.js +19 -0
- package/src/composables/useApp.js +43 -0
- package/src/composables/useAuth.js +49 -0
- package/src/composables/useBareForm.js +143 -0
- package/src/composables/useBreadcrumb.js +221 -0
- package/src/composables/useDirtyState.js +103 -0
- package/src/composables/useEntityTitle.js +121 -0
- package/src/composables/useForm.js +254 -0
- package/src/composables/useGuardStore.js +37 -0
- package/src/composables/useJsonSyntax.js +101 -0
- package/src/composables/useListPageBuilder.js +1176 -0
- package/src/composables/useNavigation.js +89 -0
- package/src/composables/usePageBuilder.js +334 -0
- package/src/composables/useStatus.js +146 -0
- package/src/composables/useSubEditor.js +165 -0
- package/src/composables/useTabSync.js +110 -0
- package/src/composables/useUnsavedChangesGuard.js +122 -0
- package/src/entity/EntityManager.js +540 -0
- package/src/entity/index.js +11 -0
- package/src/entity/storage/ApiStorage.js +146 -0
- package/src/entity/storage/LocalStorage.js +220 -0
- package/src/entity/storage/MemoryStorage.js +201 -0
- package/src/entity/storage/index.js +10 -0
- package/src/index.js +29 -0
- package/src/kernel/Kernel.js +234 -0
- package/src/kernel/index.js +7 -0
- package/src/module/index.js +16 -0
- package/src/module/moduleRegistry.js +222 -0
- package/src/orchestrator/Orchestrator.js +141 -0
- package/src/orchestrator/index.js +8 -0
- package/src/orchestrator/useOrchestrator.js +61 -0
- package/src/plugin.js +142 -0
- package/src/styles/_alerts.css +48 -0
- package/src/styles/_code.css +33 -0
- package/src/styles/_dialogs.css +17 -0
- package/src/styles/_markdown.css +82 -0
- package/src/styles/_show-pages.css +84 -0
- package/src/styles/index.css +16 -0
- package/src/styles/main.css +845 -0
- package/src/styles/theme/components.css +286 -0
- package/src/styles/theme/index.css +10 -0
- package/src/styles/theme/tokens.css +125 -0
- package/src/styles/theme/utilities.css +172 -0
- package/src/utils/debugInjector.js +261 -0
- package/src/utils/formatters.js +165 -0
- package/src/utils/index.js +35 -0
- package/src/utils/transformers.js +105 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to qdadm will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [0.13.0] - 2025-12-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `EntityManager.getMany(ids)`: Batch fetch multiple entities by IDs
|
|
12
|
+
- Delegates to `storage.getMany(ids)` if available
|
|
13
|
+
- Falls back to parallel `get()` calls
|
|
14
|
+
- `EntityManager.canCreate()`: Permission check for creating entities
|
|
15
|
+
- `EntityManager.canUpdate(entity?)`: Permission check for updating entities
|
|
16
|
+
- `EntityManager.children`: Config for parent-child entity relationships
|
|
17
|
+
- `BoolCell` component: Standardized tristate boolean display
|
|
18
|
+
- `true` → green check (`--p-green-500`)
|
|
19
|
+
- `false` → red cross (`--p-red-500`)
|
|
20
|
+
- `null/undefined` → empty
|
|
21
|
+
- `LocalStorage.getMany(ids)`: Optimized batch fetch for localStorage adapter
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- **BREAKING**: Removed `canWrite()` - use `canCreate()` / `canUpdate()` instead
|
|
25
|
+
- `addCreateAction()`: Uses `manager.canCreate()` for visibility
|
|
26
|
+
- `addEditAction()`: Uses `manager.canUpdate(row)` for visibility
|
|
27
|
+
|
|
28
|
+
### Demo
|
|
29
|
+
- `read` field moved from `books` to `loans` entity
|
|
30
|
+
- `BookForm`: Tabs in edit mode showing child entities (Loans)
|
|
31
|
+
- `LoanList`: Bulk actions "Mark Read" / "Mark Unread"
|
|
32
|
+
- `LoanForm`: Toggle for `read` field
|
|
33
|
+
- Permission examples updated to use `canCreate()` / `canUpdate()`
|
|
34
|
+
|
|
35
|
+
## [0.12.0] - 2025-12-21
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- `useListPageBuilder.addFilter({ local_filter })`: Custom filter callback for local mode
|
|
39
|
+
- Specifies HOW to filter when in local mode (items < threshold)
|
|
40
|
+
- Example: `local_filter: (item, value) => value === 'active' ? !item.returned_at : true`
|
|
41
|
+
- Filters with `local_filter` are **not sent to manager** (virtual fields)
|
|
42
|
+
- `local_filter: false` = manager only, skip in local mode
|
|
43
|
+
- `useListPageBuilder.setSearch({ local_search })`: Custom search callback for local mode
|
|
44
|
+
- Specifies HOW to search when in local mode
|
|
45
|
+
- Example: `local_search: (item, query) => booksMap[item.book_id]?.title.includes(query)`
|
|
46
|
+
- `local_search: false` = manager only, skip in local mode
|
|
47
|
+
- `EntityManager.localFilterThreshold`: Per-entity threshold for auto local filtering
|
|
48
|
+
- Priority: config `autoFilterThreshold` > `manager.localFilterThreshold` > default (100)
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
- **BREAKING**: Renamed `filterMode: 'api'` → `filterMode: 'manager'` (clearer intent)
|
|
52
|
+
- **Demo**: `LoanList` uses `local_filter` for virtual `status` filter
|
|
53
|
+
- **Demo**: `LoanList` uses `local_search` for book title lookup
|
|
54
|
+
|
|
55
|
+
### Documentation
|
|
56
|
+
- **The threshold decides everything**:
|
|
57
|
+
- `items >= threshold` → **manager mode**: delegate ALL filtering to EntityManager
|
|
58
|
+
- `items < threshold` → **local mode**: filter client-side
|
|
59
|
+
- **Modes**:
|
|
60
|
+
- `filterMode: 'manager'`: Always delegate to EntityManager
|
|
61
|
+
- `filterMode: 'local'`: Always filter client-side
|
|
62
|
+
- `filterMode: 'auto'` (default): Switch based on threshold
|
|
63
|
+
- **Behavior Matrix**:
|
|
64
|
+
| Type | Mode `manager` | Mode `local` |
|
|
65
|
+
|-----------------------------|-----------------|-----------------------------|
|
|
66
|
+
| Filter standard | Manager handles | `item[field] === value` |
|
|
67
|
+
| Filter + `local_filter` | Manager handles | `local_filter(item, value)` |
|
|
68
|
+
| Filter + `local_filter:false` | Manager handles | (skipped) |
|
|
69
|
+
| Search standard | Manager handles | `field.includes(query)` |
|
|
70
|
+
| Search + `local_search` | Manager handles | `local_search(item, query)` |
|
|
71
|
+
| Search + `local_search:false` | Manager handles | (skipped) |
|
|
72
|
+
- **Threshold Priority**: config > `manager.localFilterThreshold` > 100
|
|
73
|
+
|
|
74
|
+
## [0.11.0] - 2025-12-20
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- **Demo**: Permission-aware form fields pattern
|
|
78
|
+
- `LoanForm`: Non-admin users get `user_id` auto-set and field locked
|
|
79
|
+
- `LoansManager.create()`: Enforces `user_id` server-side for non-admin
|
|
80
|
+
- **Demo**: Detailed comments on `useForm` return values (`loading`, `saving`, `dirty`, `isEdit`)
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
- **Demo**: Simplified `onMounted` pattern replaces `watch` for form field initialization
|
|
84
|
+
|
|
85
|
+
## [0.10.0] - 2025-12-20
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
- **Demo**: Complete permission patterns with documented examples
|
|
89
|
+
- `UsersManager`: Admin-only access (canRead/canWrite)
|
|
90
|
+
- `BooksManager`: Everyone edits, admin-only delete (canDelete)
|
|
91
|
+
- `LoansManager`: Ownership-based filtering (list override + row-level permissions)
|
|
92
|
+
- **Demo**: Detailed JSDoc comments explaining all qdadm patterns
|
|
93
|
+
- **Demo**: Fixture seeding system with JSON files
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
- **Demo**: Login page shows all available demo accounts with role explanations
|
|
97
|
+
|
|
98
|
+
## [0.9.0] - 2025-12-20
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
- `useListPageBuilder.props`: Computed object with all ListPage props - use with `v-bind="list.props.value"`
|
|
102
|
+
- `useListPageBuilder.events`: Event handlers object - use with `v-on="list.events"`
|
|
103
|
+
- `useListPageBuilder.hasBulkActions`: Auto-detects if bulk actions are available
|
|
104
|
+
- Auto-selectable: Checkboxes shown only when bulk actions exist (no manual `selectable` prop needed)
|
|
105
|
+
|
|
106
|
+
### Changed
|
|
107
|
+
- ListPage template simplified from ~20 props/events to just `v-bind` + `v-on`
|
|
108
|
+
|
|
109
|
+
## [0.8.0] - 2025-12-20
|
|
110
|
+
|
|
111
|
+
### Added
|
|
112
|
+
- `EntityManager.canDelete(entity?)`: Fine-grained delete permission check
|
|
113
|
+
- `useListPageBuilder`: Standard actions now respect permissions:
|
|
114
|
+
- `addCreateAction()`: Hidden if `manager.canCreate()` returns false
|
|
115
|
+
- `addEditAction()`: Hidden per row if `manager.canUpdate(row)` returns false
|
|
116
|
+
- `addDeleteAction()`: Hidden per row if `manager.canDelete(row)` returns false
|
|
117
|
+
- `addBulkDeleteAction()`: Hidden if `manager.canDelete()` returns false
|
|
118
|
+
|
|
119
|
+
## [0.7.0] - 2025-12-20
|
|
120
|
+
|
|
121
|
+
### Added
|
|
122
|
+
- `Kernel`: All-in-one bootstrap class - handles Vue app, Pinia, PrimeVue, router, auth guard, and qdadm
|
|
123
|
+
- Constructor is declarative (stores config only)
|
|
124
|
+
- `createApp()` does all initialization, returns Vue app for mount
|
|
125
|
+
- Allows tweaking `kernel.options` before `createApp()`, or adding plugins/directives before `mount()`
|
|
126
|
+
```js
|
|
127
|
+
const kernel = new Kernel({
|
|
128
|
+
root: App,
|
|
129
|
+
modules: import.meta.glob('./modules/*/init.js', { eager: true }),
|
|
130
|
+
managers, authAdapter,
|
|
131
|
+
pages: { login, layout },
|
|
132
|
+
homeRoute: 'book',
|
|
133
|
+
app: { name: 'My App' },
|
|
134
|
+
primevue: { plugin: PrimeVue, theme: Aura }
|
|
135
|
+
})
|
|
136
|
+
kernel.createApp().mount('#app')
|
|
137
|
+
```
|
|
138
|
+
- `kernel.getRouter()`, `kernel.getApp()`, `kernel.getOrchestrator()` - access internals
|
|
139
|
+
|
|
140
|
+
## [0.6.0] - 2025-12-20
|
|
141
|
+
|
|
142
|
+
### Added
|
|
143
|
+
- `registry.addRoutes(prefix, routes, { entity })`: Route-level entity binding for permission checks
|
|
144
|
+
- `registry.addNavItem({ entity })`: Nav item entity binding for permission checks
|
|
145
|
+
- **Auto Route Guard**: Plugin adds `beforeEach` guard that redirects to `/` if `manager.canRead()` returns false
|
|
146
|
+
- **Auto Nav Filtering**: `useNavigation` filters items and hides empty sections based on `manager.canRead()`
|
|
147
|
+
|
|
148
|
+
## [0.5.0] - 2025-12-20
|
|
149
|
+
|
|
150
|
+
### Added
|
|
151
|
+
- `useGuardStore`: Shared reactive store for automatic UnsavedChangesDialog rendering
|
|
152
|
+
- `AppLayout`: Automatically renders UnsavedChangesDialog when a form is active (no code needed in forms)
|
|
153
|
+
- `EntityManager.canRead(entity?)`: Permission check for reading (general or specific entity)
|
|
154
|
+
- `EntityManager.canWrite(entity?)`: Permission check for writing (general or specific entity)
|
|
155
|
+
|
|
156
|
+
### Changed
|
|
157
|
+
- `useBareForm`: Uses `registerGuardDialog`/`unregisterGuardDialog` from store instead of provide/inject
|
|
158
|
+
- `UnsavedChangesDialog`: Refactored to use `SimpleDialog` internally
|
|
159
|
+
- `PageLayout`: Removed `guardDialog` prop (dialog is now automatic via AppLayout)
|
|
160
|
+
|
|
161
|
+
### Removed
|
|
162
|
+
- `features.scopes`: Removed from plugin config - permission logic belongs in EntityManager
|
|
163
|
+
- `useAuth.hasScope()`: Replaced by `EntityManager.canRead()`/`canWrite()` pattern
|
|
164
|
+
|
|
165
|
+
### Fixed
|
|
166
|
+
- `useDirtyState`: Fixed timing issue where `takeSnapshot()` used `nextTick()` causing navigation to be blocked after save
|
|
167
|
+
|
|
168
|
+
## [0.4.0] - 2025-12-20
|
|
169
|
+
|
|
170
|
+
### Added
|
|
171
|
+
- `EntityManager.getEntityLabel(entity)`: Method to get display label, handles both string field and callback
|
|
172
|
+
- `EntityManager.labelField`: Can now be a string (field name) or callback function `(entity) => string`
|
|
173
|
+
- `PageLayout`: `manager` prop - derives labelField automatically from EntityManager
|
|
174
|
+
|
|
175
|
+
### Changed
|
|
176
|
+
- `PageLayout`: Uses `manager.getEntityLabel()` instead of manual field access
|
|
177
|
+
- `useBreadcrumb`: Shows entity label in breadcrumb (replaces ID segment), keeps action labels (Edit/Create/View)
|
|
178
|
+
|
|
179
|
+
## [0.3.1] - 2025-12-20
|
|
180
|
+
|
|
181
|
+
### Added
|
|
182
|
+
- `AppLayout`: "powered by qdadm" footer with version display
|
|
183
|
+
- `assets/logo.svg`: Hexagon logo with "QD" in two-tone blue
|
|
184
|
+
- `features.poweredBy`: Option to hide "powered by" footer (default: true)
|
|
185
|
+
|
|
186
|
+
### Fixed
|
|
187
|
+
- `AppLayout`: Vue Flow and full-height components now render correctly (flex layout)
|
|
188
|
+
- `AppLayout`: Use `<RouterView />` fallback when no slot content provided
|
|
189
|
+
|
|
190
|
+
### Changed
|
|
191
|
+
- `AppLayout`: Sidebar width uses CSS variable `var(--fad-sidebar-width, 15rem)` instead of hardcoded `250px`
|
|
192
|
+
- `tokens.css`: Layout tokens now use `rem` units (`--fad-sidebar-width`, `--fad-header-height`, `--fad-content-max-width`)
|
|
193
|
+
- `main.css`: Removed ~130 lines of duplicated styles (sidebar, nav, login, user-info) - now only in AppLayout scoped CSS
|
|
194
|
+
|
|
195
|
+
## [0.3.0] - 2025-12-20
|
|
196
|
+
|
|
197
|
+
### Added
|
|
198
|
+
- `AppLayout` component with auto-nav from module registry
|
|
199
|
+
- `useNavigation` composable for navigation state management
|
|
200
|
+
- `useStatus` composable for generic status/option loading
|
|
201
|
+
- `setSectionOrder()` to configure navigation section order
|
|
202
|
+
- `initModules()` accepts external module imports and core nav items
|
|
203
|
+
|
|
204
|
+
### Changed
|
|
205
|
+
- Module registry is now fully configurable (no hardcoded imports)
|
|
206
|
+
- Navigation auto-builds from module declarations (registry.addNavItem)
|
|
207
|
+
- Branding comes from bootstrap config (createQdadm app option)
|
|
208
|
+
- Dashboard now uses qdadm AppLayout instead of local implementation
|
|
209
|
+
|
|
210
|
+
### Removed
|
|
211
|
+
- Hardcoded import.meta.glob in moduleRegistry
|
|
212
|
+
- Hardcoded sectionOrder in moduleRegistry
|
|
213
|
+
|
|
214
|
+
## [0.2.0] - 2025-12-20
|
|
215
|
+
|
|
216
|
+
### Added
|
|
217
|
+
- README.md with quick start guide
|
|
218
|
+
- TODO.md for planned features
|
|
219
|
+
- docs/FRAMEWORK.md moved from dashboard
|
|
220
|
+
|
|
221
|
+
### Changed
|
|
222
|
+
- Successfully integrated with Faketual dashboard
|
|
223
|
+
- All 60+ dashboard modules migrated to use qdadm imports
|
|
224
|
+
- Validated build and runtime functionality
|
|
225
|
+
|
|
226
|
+
### Fixed
|
|
227
|
+
- Component import paths (PageHeader, CardsGrid, ListPage)
|
|
228
|
+
- useJsonSyntax import paths in editor components
|
|
229
|
+
- Theme CSS import path in styles/index.css
|
|
230
|
+
|
|
231
|
+
## [0.1.0] - 2025-12-20
|
|
232
|
+
|
|
233
|
+
### Added
|
|
234
|
+
- Initial extraction from Faketual dashboard
|
|
235
|
+
- `createQdadm` plugin for Vue 3 bootstrap
|
|
236
|
+
- **Adapters**:
|
|
237
|
+
- `ApiAdapter` interface for CRUD operations
|
|
238
|
+
- `AuthAdapter` interface for authentication (optional)
|
|
239
|
+
- **Composables**:
|
|
240
|
+
- `useForm` - Form state management with validation
|
|
241
|
+
- `useListPageBuilder` - Paginated list with filters and actions
|
|
242
|
+
- `useBareForm` - Minimal form without routing
|
|
243
|
+
- `useBreadcrumb` - Breadcrumb builder
|
|
244
|
+
- `useDirtyState` - Track unsaved changes
|
|
245
|
+
- `useEntityTitle` - Dynamic page titles
|
|
246
|
+
- `useJsonSyntax` - JSON validation helpers
|
|
247
|
+
- `usePageBuilder` - Generic page builder
|
|
248
|
+
- `useSubEditor` - Sub-component for complex forms
|
|
249
|
+
- `useTabSync` - Sync tabs with URL
|
|
250
|
+
- `useUnsavedChangesGuard` - Block navigation if dirty
|
|
251
|
+
- `useAuth` - Authentication access
|
|
252
|
+
- `useApp` - App branding access
|
|
253
|
+
- **Components**:
|
|
254
|
+
- Layout: `PageLayout`, `PageHeader`, `Breadcrumb`
|
|
255
|
+
- Forms: `FormField`, `FormActions`, `FormTabs`, `FormTab`
|
|
256
|
+
- Lists: `ListPage`, `ActionButtons`, `ActionColumn`, `FilterBar`
|
|
257
|
+
- Editors: `KeyValueEditor`, `LanguageEditor`, `ScopeEditor`, `VanillaJsonEditor`, `JsonEditorFoldable`, `JsonStructuredField`, `JsonViewer`
|
|
258
|
+
- Dialogs: `SimpleDialog`, `MultiStepDialog`, `BulkStatusDialog`, `UnsavedChangesDialog`
|
|
259
|
+
- Display: `CardsGrid`, `RichCardsGrid`, `CopyableId`, `EmptyState`, `IntensityBar`
|
|
260
|
+
- Auth: `LoginPage`, `AuthGuard`, `ScopeGuard`
|
|
261
|
+
- **Module System**:
|
|
262
|
+
- `moduleRegistry` for auto-discovery of modules
|
|
263
|
+
- Route registration with path prefix
|
|
264
|
+
- Navigation item registration
|
|
265
|
+
- Route family for active state detection
|
|
266
|
+
- **Configuration**:
|
|
267
|
+
- `app` config for branding (name, logo, version, theme)
|
|
268
|
+
- `features` toggles (auth, scopes)
|
|
269
|
+
- `builtinModules` for users/roles
|
|
270
|
+
- `endpoints` configuration
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 quazardous
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# qdadm
|
|
2
|
+
|
|
3
|
+
Vue 3 framework for building admin dashboards with PrimeVue.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Kernel**: All-in-one bootstrap (Vue app, router, Pinia, PrimeVue, auth guard)
|
|
8
|
+
- **EntityManager**: CRUD operations with permission control (`canRead`/`canCreate`/`canUpdate`/`canDelete`)
|
|
9
|
+
- **Module System**: Auto-discovery of modules with routes and navigation
|
|
10
|
+
- **Components**: Forms, lists, dialogs, editors ready to use
|
|
11
|
+
- **Composables**: `useForm`, `useListPageBuilder`, `useBareForm`, etc.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install qdadm
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start with Kernel
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { Kernel, EntityManager, LocalStorage } from 'qdadm'
|
|
23
|
+
import PrimeVue from 'primevue/config'
|
|
24
|
+
import Aura from '@primeuix/themes/aura'
|
|
25
|
+
import 'qdadm/styles'
|
|
26
|
+
|
|
27
|
+
import App from './App.vue'
|
|
28
|
+
import { authAdapter } from './adapters/authAdapter'
|
|
29
|
+
|
|
30
|
+
const managers = {
|
|
31
|
+
books: new EntityManager({
|
|
32
|
+
name: 'books',
|
|
33
|
+
storage: new LocalStorage({ key: 'my_books' }),
|
|
34
|
+
fields: {
|
|
35
|
+
title: { type: 'text', label: 'Title', required: true },
|
|
36
|
+
author: { type: 'text', label: 'Author' }
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const kernel = new Kernel({
|
|
42
|
+
root: App,
|
|
43
|
+
modules: import.meta.glob('./modules/*/init.js', { eager: true }),
|
|
44
|
+
sectionOrder: ['Library'],
|
|
45
|
+
managers,
|
|
46
|
+
authAdapter,
|
|
47
|
+
pages: {
|
|
48
|
+
login: () => import('./pages/LoginPage.vue'),
|
|
49
|
+
layout: () => import('./pages/MainLayout.vue')
|
|
50
|
+
},
|
|
51
|
+
homeRoute: 'book',
|
|
52
|
+
app: { name: 'My App', version: '1.0.0' },
|
|
53
|
+
primevue: { plugin: PrimeVue, theme: Aura }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
kernel.createApp().mount('#app')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Manual Bootstrap (without Kernel)
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { createQdadm, initModules, getRoutes } from 'qdadm'
|
|
63
|
+
|
|
64
|
+
// Init modules
|
|
65
|
+
initModules(import.meta.glob('./modules/*/init.js', { eager: true }))
|
|
66
|
+
|
|
67
|
+
// Create router with getRoutes()
|
|
68
|
+
const router = createRouter({
|
|
69
|
+
history: createWebHistory(),
|
|
70
|
+
routes: [
|
|
71
|
+
{ path: '/login', component: LoginPage },
|
|
72
|
+
{ path: '/', component: Layout, children: getRoutes() }
|
|
73
|
+
]
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Install plugin
|
|
77
|
+
app.use(createQdadm({ managers, authAdapter, router, toast }))
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Module Structure
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
modules/
|
|
84
|
+
└── books/
|
|
85
|
+
├── init.js # Route & nav registration
|
|
86
|
+
└── pages/
|
|
87
|
+
├── BookList.vue
|
|
88
|
+
└── BookForm.vue
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**init.js:**
|
|
92
|
+
```js
|
|
93
|
+
export function init(registry) {
|
|
94
|
+
registry.addRoutes('books', [
|
|
95
|
+
{ path: '', name: 'book', component: () => import('./pages/BookList.vue') },
|
|
96
|
+
{ path: 'create', name: 'book-create', component: () => import('./pages/BookForm.vue') },
|
|
97
|
+
{ path: ':id/edit', name: 'book-edit', component: () => import('./pages/BookForm.vue') }
|
|
98
|
+
], { entity: 'books' })
|
|
99
|
+
|
|
100
|
+
registry.addNavItem({
|
|
101
|
+
section: 'Library',
|
|
102
|
+
route: 'book',
|
|
103
|
+
icon: 'pi pi-book',
|
|
104
|
+
label: 'Books',
|
|
105
|
+
entity: 'books'
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
registry.addRouteFamily('book', ['book-'])
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## EntityManager Permissions
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
class UsersManager extends EntityManager {
|
|
116
|
+
canRead() {
|
|
117
|
+
return authAdapter.getUser()?.role === 'admin'
|
|
118
|
+
}
|
|
119
|
+
canCreate() {
|
|
120
|
+
return authAdapter.getUser()?.role === 'admin'
|
|
121
|
+
}
|
|
122
|
+
canUpdate(entity) {
|
|
123
|
+
return authAdapter.getUser()?.role === 'admin'
|
|
124
|
+
}
|
|
125
|
+
canDelete(entity) {
|
|
126
|
+
return authAdapter.getUser()?.role === 'admin'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When `canRead()` returns false:
|
|
132
|
+
- Navigation items are hidden
|
|
133
|
+
- Routes redirect to `/`
|
|
134
|
+
|
|
135
|
+
## Components
|
|
136
|
+
|
|
137
|
+
| Category | Components |
|
|
138
|
+
|----------|------------|
|
|
139
|
+
| Layout | `AppLayout`, `PageLayout`, `PageHeader`, `Breadcrumb` |
|
|
140
|
+
| Forms | `FormField`, `FormActions`, `FormTabs`, `FormTab` |
|
|
141
|
+
| Lists | `ListPage`, `ActionButtons`, `FilterBar` |
|
|
142
|
+
| Editors | `JsonViewer`, `KeyValueEditor`, `VanillaJsonEditor` |
|
|
143
|
+
| Dialogs | `SimpleDialog`, `MultiStepDialog`, `UnsavedChangesDialog` |
|
|
144
|
+
| Display | `CardsGrid`, `CopyableId`, `EmptyState` |
|
|
145
|
+
|
|
146
|
+
## Composables
|
|
147
|
+
|
|
148
|
+
| Composable | Description |
|
|
149
|
+
|------------|-------------|
|
|
150
|
+
| `useForm` | Form with validation, dirty state, navigation guard |
|
|
151
|
+
| `useBareForm` | Lightweight form without routing |
|
|
152
|
+
| `useListPageBuilder` | Paginated list with filters and actions |
|
|
153
|
+
| `useTabSync` | Sync tabs with URL query params |
|
|
154
|
+
| `useBreadcrumb` | Dynamic breadcrumb from route |
|
|
155
|
+
|
|
156
|
+
## Peer Dependencies
|
|
157
|
+
|
|
158
|
+
- vue ^3.3.0
|
|
159
|
+
- vue-router ^4.0.0
|
|
160
|
+
- primevue ^4.0.0
|
|
161
|
+
- pinia ^2.0.0
|
|
162
|
+
- vanilla-jsoneditor ^0.23.0
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qdadm",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Vue 3 framework for admin dashboards with PrimeVue",
|
|
5
|
+
"author": "quazardous",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/quazardous/qdadm.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/quazardous/qdadm#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/quazardous/qdadm/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "src/index.js",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./src/index.js",
|
|
19
|
+
"./composables": "./src/composables/index.js",
|
|
20
|
+
"./components": "./src/components/index.js",
|
|
21
|
+
"./module": "./src/module/index.js",
|
|
22
|
+
"./utils": "./src/utils/index.js",
|
|
23
|
+
"./styles": "./src/styles/index.css"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"README.md",
|
|
28
|
+
"CHANGELOG.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"vue": "^3.3.0",
|
|
33
|
+
"vue-router": "^4.0.0",
|
|
34
|
+
"primevue": "^4.0.0",
|
|
35
|
+
"pinia": "^2.0.0",
|
|
36
|
+
"vanilla-jsoneditor": "^0.23.0"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"vue",
|
|
40
|
+
"vue3",
|
|
41
|
+
"admin",
|
|
42
|
+
"dashboard",
|
|
43
|
+
"framework",
|
|
44
|
+
"primevue",
|
|
45
|
+
"crud",
|
|
46
|
+
"entity-manager"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
|
2
|
+
<polygon points="50,5 93,27.5 93,72.5 50,95 7,72.5 7,27.5" fill="#1E3A8A"/>
|
|
3
|
+
<text x="48" y="50" text-anchor="middle" dominant-baseline="central" font-family="system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="58" font-weight="800" letter-spacing="-4">
|
|
4
|
+
<tspan fill="#60A5FA">Q</tspan><tspan fill="#93C5FD">D</tspan>
|
|
5
|
+
</text>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* BoolCell - Standardized boolean display for list columns
|
|
4
|
+
*
|
|
5
|
+
* Tristate display:
|
|
6
|
+
* - true: green check
|
|
7
|
+
* - false: red cross
|
|
8
|
+
* - null/undefined: empty
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* <Column field="read" header="Read">
|
|
12
|
+
* <template #body="{ data }">
|
|
13
|
+
* <BoolCell :value="data.read" />
|
|
14
|
+
* </template>
|
|
15
|
+
* </Column>
|
|
16
|
+
*/
|
|
17
|
+
defineProps({
|
|
18
|
+
value: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: null
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<i v-if="value === true" class="pi pi-check" style="color: var(--p-green-500)" />
|
|
27
|
+
<i v-else-if="value === false" class="pi pi-times" style="color: var(--p-red-500)" />
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import SimpleDialog from './SimpleDialog.vue'
|
|
3
|
+
import Select from 'primevue/select'
|
|
4
|
+
|
|
5
|
+
defineProps({
|
|
6
|
+
visible: Boolean,
|
|
7
|
+
title: { type: String, default: 'Change Status' },
|
|
8
|
+
options: { type: Array, required: true },
|
|
9
|
+
modelValue: [String, Number],
|
|
10
|
+
loading: Boolean,
|
|
11
|
+
selectionCount: { type: Number, default: 0 }
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
defineEmits(['update:visible', 'update:modelValue', 'confirm', 'cancel'])
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<SimpleDialog
|
|
19
|
+
:visible="visible"
|
|
20
|
+
:title="title"
|
|
21
|
+
width="400px"
|
|
22
|
+
:loading="loading"
|
|
23
|
+
confirmLabel="Update"
|
|
24
|
+
confirmIcon="pi pi-check"
|
|
25
|
+
:confirmDisabled="!modelValue"
|
|
26
|
+
@update:visible="$emit('update:visible', $event)"
|
|
27
|
+
@confirm="$emit('confirm')"
|
|
28
|
+
@cancel="$emit('cancel')"
|
|
29
|
+
>
|
|
30
|
+
<p v-if="selectionCount > 0" class="mb-3">
|
|
31
|
+
Change status for {{ selectionCount }} selected item(s):
|
|
32
|
+
</p>
|
|
33
|
+
<Select
|
|
34
|
+
:modelValue="modelValue"
|
|
35
|
+
:options="options"
|
|
36
|
+
optionLabel="label"
|
|
37
|
+
optionValue="value"
|
|
38
|
+
placeholder="Select new status"
|
|
39
|
+
class="w-full"
|
|
40
|
+
@update:modelValue="$emit('update:modelValue', $event)"
|
|
41
|
+
/>
|
|
42
|
+
</SimpleDialog>
|
|
43
|
+
</template>
|