@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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +101 -0
- package/src/assets/logo.svg +6 -0
- package/src/auth/SessionAuthAdapter.ts +459 -0
- package/src/auth/index.ts +15 -0
- package/src/chain/ActiveStack.ts +154 -0
- package/src/chain/StackHydrator.ts +292 -0
- package/src/chain/index.ts +25 -0
- package/src/chain/useActiveStack.ts +120 -0
- package/src/chain/useStackHydrator.ts +155 -0
- package/src/components/BoolCell.vue +34 -0
- package/src/components/InfoBanner.vue +73 -0
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/SeverityTag.vue +86 -0
- package/src/components/dialogs/BulkStatusDialog.vue +60 -0
- package/src/components/dialogs/MultiStepDialog.vue +331 -0
- package/src/components/dialogs/SimpleDialog.vue +122 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
- package/src/components/display/BannerZone.vue +65 -0
- package/src/components/display/CardsGrid.vue +72 -0
- package/src/components/display/CopyableId.vue +84 -0
- package/src/components/display/EmptyState.vue +62 -0
- package/src/components/display/IntensityBar.vue +190 -0
- package/src/components/display/RichCardsGrid.vue +101 -0
- package/src/components/edit/FormActions.vue +78 -0
- package/src/components/edit/FormField.vue +82 -0
- package/src/components/edit/FormInput.vue +158 -0
- package/src/components/edit/FormPage.vue +260 -0
- package/src/components/edit/FormTab.vue +217 -0
- package/src/components/edit/FormTabs.vue +120 -0
- package/src/components/edit/LookupField.vue +406 -0
- package/src/components/edit/LookupPickerDialog.vue +230 -0
- package/src/components/editors/JsonEditorFoldable.vue +470 -0
- package/src/components/editors/JsonStructuredField.vue +264 -0
- package/src/components/editors/JsonViewer.vue +51 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +259 -0
- package/src/components/editors/PermissionEditor.vue +558 -0
- package/src/components/editors/ScopeEditor.vue +372 -0
- package/src/components/editors/VanillaJsonEditor.vue +183 -0
- package/src/components/index.ts +83 -0
- package/src/components/item/FieldGroups.vue +392 -0
- package/src/components/layout/AppLayout.vue +1032 -0
- package/src/components/layout/BaseLayout.vue +190 -0
- package/src/components/layout/Breadcrumb.vue +55 -0
- package/src/components/layout/DashboardLayout.vue +104 -0
- package/src/components/layout/FormLayout.vue +256 -0
- package/src/components/layout/ListLayout.vue +278 -0
- package/src/components/layout/PageHeader.vue +156 -0
- package/src/components/layout/PageLayout.vue +126 -0
- package/src/components/layout/PageNav.vue +192 -0
- package/src/components/layout/SidebarBox.vue +87 -0
- package/src/components/layout/Zone.vue +182 -0
- package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
- package/src/components/layout/defaults/DefaultFooter.vue +89 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
- package/src/components/layout/defaults/DefaultHeader.vue +78 -0
- package/src/components/layout/defaults/DefaultMenu.vue +188 -0
- package/src/components/layout/defaults/DefaultPagination.vue +75 -0
- package/src/components/layout/defaults/DefaultTable.vue +137 -0
- package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
- package/src/components/layout/defaults/index.ts +16 -0
- package/src/components/lists/ActionButtons.vue +49 -0
- package/src/components/lists/ActionColumn.vue +40 -0
- package/src/components/lists/FilterBar.vue +54 -0
- package/src/components/lists/ListPage.vue +467 -0
- package/src/components/pages/LoginPage.vue +334 -0
- package/src/components/pages/NotFoundPage.vue +98 -0
- package/src/components/show/ShowDisplay.vue +343 -0
- package/src/components/show/ShowField.vue +67 -0
- package/src/components/show/ShowPage.vue +431 -0
- package/src/composables/index.ts +155 -0
- package/src/composables/useApp.ts +67 -0
- package/src/composables/useAuth.ts +98 -0
- package/src/composables/useBareForm.ts +319 -0
- package/src/composables/useBreadcrumb.ts +252 -0
- package/src/composables/useChildPage.ts +113 -0
- package/src/composables/useCurrentEntity.ts +60 -0
- package/src/composables/useDeferred.ts +94 -0
- package/src/composables/useDirtyState.ts +138 -0
- package/src/composables/useEntityItemFormPage.ts +942 -0
- package/src/composables/useEntityItemFormPage.types.ts +451 -0
- package/src/composables/useEntityItemPage.ts +438 -0
- package/src/composables/useEntityItemShowPage.ts +722 -0
- package/src/composables/useFieldManager.ts +692 -0
- package/src/composables/useForm.ts +544 -0
- package/src/composables/useGuardStore.ts +53 -0
- package/src/composables/useHooks.ts +56 -0
- package/src/composables/useInfoBanner.ts +217 -0
- package/src/composables/useJsonSyntax.ts +122 -0
- package/src/composables/useLayoutResolver.ts +329 -0
- package/src/composables/useListPage.ts +1535 -0
- package/src/composables/useListPage.types.ts +437 -0
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavContext.ts +439 -0
- package/src/composables/useNavigation.ts +216 -0
- package/src/composables/useOptionsLookup.ts +323 -0
- package/src/composables/usePageTitle.ts +84 -0
- package/src/composables/useSSEBridge.ts +142 -0
- package/src/composables/useSemanticBreadcrumb.ts +222 -0
- package/src/composables/useSidebarState.ts +24 -0
- package/src/composables/useSignals.ts +52 -0
- package/src/composables/useUnsavedChangesGuard.ts +155 -0
- package/src/composables/useUserImpersonator.ts +317 -0
- package/src/composables/useZoneRegistry.ts +252 -0
- package/src/core/bundles.ts +558 -0
- package/src/core/decorator.ts +420 -0
- package/src/core/extension.ts +470 -0
- package/src/core/index.ts +59 -0
- package/src/deferred/DeferredRegistry.ts +353 -0
- package/src/deferred/index.ts +15 -0
- package/src/editors/index.ts +12 -0
- package/src/entity/EntityManager.cache.ts +530 -0
- package/src/entity/EntityManager.crud.ts +615 -0
- package/src/entity/EntityManager.interface.ts +78 -0
- package/src/entity/EntityManager.query.ts +221 -0
- package/src/entity/EntityManager.relations.ts +151 -0
- package/src/entity/EntityManager.ts +944 -0
- package/src/entity/EntityManager.types.ts +288 -0
- package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
- package/src/entity/auth/EntityAuthAdapter.ts +119 -0
- package/src/entity/auth/PermissiveAdapter.ts +43 -0
- package/src/entity/auth/RoleHierarchy.ts +128 -0
- package/src/entity/auth/SecurityChecker.ts +202 -0
- package/src/entity/auth/factory.ts +203 -0
- package/src/entity/auth/index.ts +47 -0
- package/src/entity/factory.test.js +189 -0
- package/src/entity/factory.ts +146 -0
- package/src/entity/index.ts +49 -0
- package/src/entity/storage/ApiStorage.ts +228 -0
- package/src/entity/storage/IStorage.ts +100 -0
- package/src/entity/storage/LocalStorage.ts +200 -0
- package/src/entity/storage/MemoryStorage.ts +184 -0
- package/src/entity/storage/MockApiStorage.ts +318 -0
- package/src/entity/storage/SdkStorage.ts +377 -0
- package/src/entity/storage/factory.test.js +159 -0
- package/src/entity/storage/factory.ts +209 -0
- package/src/entity/storage/index.ts +75 -0
- package/src/gen/FieldMapper.ts +122 -0
- package/src/gen/StorageProfileFactory.ts +93 -0
- package/src/gen/connectors/BaseConnector.ts +150 -0
- package/src/gen/connectors/ManualConnector.test.js +499 -0
- package/src/gen/connectors/ManualConnector.ts +414 -0
- package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
- package/src/gen/connectors/OpenAPIConnector.ts +693 -0
- package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
- package/src/gen/connectors/index.ts +27 -0
- package/src/gen/createManagers.ts +245 -0
- package/src/gen/decorators.ts +134 -0
- package/src/gen/generateManagers.test.js +540 -0
- package/src/gen/generateManagers.ts +398 -0
- package/src/gen/index.ts +66 -0
- package/src/gen/schema.ts +248 -0
- package/src/gen/vite-plugin.ts +114 -0
- package/src/generated/managers/testManager.js +45 -0
- package/src/generated/managers/testManager.ts +46 -0
- package/src/hooks/HookRegistry.ts +16 -0
- package/src/hooks/index.ts +15 -0
- package/src/i18n/I18n.ts +344 -0
- package/src/i18n/IncrementalDomainProvider.ts +153 -0
- package/src/i18n/InlineTranslationProvider.ts +4 -0
- package/src/i18n/LazyTranslationProvider.ts +102 -0
- package/src/i18n/MessagesRegistry.ts +4 -0
- package/src/i18n/Resolver.ts +4 -0
- package/src/i18n/__tests__/I18n.test.ts +169 -0
- package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
- package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
- package/src/i18n/__tests__/Resolver.test.ts +271 -0
- package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
- package/src/i18n/defaults/core.en.yml +55 -0
- package/src/i18n/defaults/core.fr.yml +55 -0
- package/src/i18n/index.ts +55 -0
- package/src/i18n/loaders/raw-modules.d.ts +15 -0
- package/src/i18n/loaders/yaml.ts +35 -0
- package/src/i18n/strategies.ts +4 -0
- package/src/i18n/types.ts +34 -0
- package/src/i18n/useI18n.ts +34 -0
- package/src/index.ts +453 -0
- package/src/kernel/EventRouter.ts +23 -0
- package/src/kernel/Kernel.api.ts +79 -0
- package/src/kernel/Kernel.auth.ts +120 -0
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +170 -0
- package/src/kernel/Kernel.registries.ts +237 -0
- package/src/kernel/Kernel.routing.ts +334 -0
- package/src/kernel/Kernel.ts +460 -0
- package/src/kernel/Kernel.types.ts +263 -0
- package/src/kernel/Kernel.vue.ts +304 -0
- package/src/kernel/KernelContext.entities.ts +80 -0
- package/src/kernel/KernelContext.events.ts +57 -0
- package/src/kernel/KernelContext.i18n.ts +37 -0
- package/src/kernel/KernelContext.permissions.ts +38 -0
- package/src/kernel/KernelContext.routing.ts +280 -0
- package/src/kernel/KernelContext.ts +252 -0
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/Module.ts +169 -0
- package/src/kernel/ModuleLoader.ts +645 -0
- package/src/kernel/SSEBridge.ts +11 -0
- package/src/kernel/SignalBus.ts +48 -0
- package/src/kernel/index.ts +56 -0
- package/src/module/index.ts +33 -0
- package/src/module/moduleRegistry.ts +492 -0
- package/src/modules/debug/AuthCollector.ts +501 -0
- package/src/modules/debug/Collector.ts +20 -0
- package/src/modules/debug/DebugBridge.ts +14 -0
- package/src/modules/debug/DebugModule.ts +220 -0
- package/src/modules/debug/EntitiesCollector.ts +839 -0
- package/src/modules/debug/ErrorCollector.ts +4 -0
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +7 -0
- package/src/modules/debug/RouterCollector.ts +449 -0
- package/src/modules/debug/SignalCollector.ts +4 -0
- package/src/modules/debug/ToastCollector.ts +4 -0
- package/src/modules/debug/ZonesCollector.ts +447 -0
- package/src/modules/debug/components/DebugBar.vue +31 -0
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +9 -0
- package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
- package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
- package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
- package/src/modules/debug/components/panels/index.ts +16 -0
- package/src/modules/debug/index.ts +58 -0
- package/src/modules/debug/styles.scss +2671 -0
- package/src/notifications/NotificationBadge.vue +35 -0
- package/src/notifications/NotificationListener.vue +71 -0
- package/src/notifications/NotificationModule.ts +76 -0
- package/src/notifications/NotificationPanel.vue +478 -0
- package/src/notifications/NotificationStore.ts +288 -0
- package/src/notifications/index.ts +42 -0
- package/src/notifications/styles.scss +32 -0
- package/src/orchestrator/Orchestrator.ts +427 -0
- package/src/orchestrator/index.ts +8 -0
- package/src/orchestrator/useOrchestrator.ts +80 -0
- package/src/plugin.ts +251 -0
- package/src/query/FilterQuery.ts +306 -0
- package/src/query/QueryExecutor.ts +341 -0
- package/src/query/index.ts +16 -0
- package/src/security/EntityRolesProvider.ts +385 -0
- package/src/security/PermissionMatcher.ts +152 -0
- package/src/security/PermissionRegistry.ts +273 -0
- package/src/security/PersistableRolesProvider.ts +584 -0
- package/src/security/RolesManager.ts +136 -0
- package/src/security/RolesProvider.ts +167 -0
- package/src/security/RolesProviderStorage.ts +211 -0
- package/src/security/SecurityModule.ts +94 -0
- package/src/security/StaticRolesProvider.ts +117 -0
- package/src/security/UsersManager.ts +163 -0
- package/src/security/index.ts +77 -0
- package/src/security/pages/RoleForm.vue +276 -0
- package/src/security/pages/RoleList.vue +123 -0
- package/src/styles/_alerts.scss +95 -0
- package/src/styles/_buttons.scss +391 -0
- package/src/styles/_cards.scss +241 -0
- package/src/styles/_code.scss +78 -0
- package/src/styles/_dialogs.scss +55 -0
- package/src/styles/_filter-bar.scss +70 -0
- package/src/styles/_forms.scss +434 -0
- package/src/styles/_helpers.scss +153 -0
- package/src/styles/_lists.scss +225 -0
- package/src/styles/_main.scss +356 -0
- package/src/styles/_markdown.scss +88 -0
- package/src/styles/_responsive.scss +255 -0
- package/src/styles/_show-pages.scss +186 -0
- package/src/styles/_sidebar.scss +380 -0
- package/src/styles/_states.scss +115 -0
- package/src/styles/_stats.scss +47 -0
- package/src/styles/_variables.scss +224 -0
- package/src/styles/index.scss +38 -0
- package/src/styles/theme/_components.scss +331 -0
- package/src/styles/theme/_index.scss +10 -0
- package/src/styles/theme/_tokens.scss +147 -0
- package/src/styles/theme/_utilities.scss +175 -0
- package/src/toast/ToastBridgeModule.ts +68 -0
- package/src/toast/ToastListener.vue +55 -0
- package/src/toast/index.ts +26 -0
- package/src/toast/useSignalToast.ts +154 -0
- package/src/types/index.ts +285 -0
- package/src/types/pluralize.d.ts +19 -0
- package/src/types/quarkernel.d.ts +50 -0
- package/src/utils/formatters.ts +174 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/transformers.ts +131 -0
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +50 -0
- package/src/zones/ZoneRegistry.ts +934 -0
- package/src/zones/index.ts +29 -0
- package/src/zones/zones.ts +191 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch KernelContext prototype with routing-related methods:
|
|
3
|
+
* - routes(basePath, routes, opts)
|
|
4
|
+
* - navItem(item)
|
|
5
|
+
* - routeFamily(base, prefixes)
|
|
6
|
+
* - crud(entity, pages, options) — convention CRUD generator
|
|
7
|
+
* - childPage(parent, page, options) — non-entity child route
|
|
8
|
+
*
|
|
9
|
+
* The string helpers (`singularize`, `toKebab`, `capitalize`) are kept
|
|
10
|
+
* module-local — they're pure, stateless, and were only used by `crud()` and
|
|
11
|
+
* `childPage()`. Keeping them off the prototype keeps the public shape clean.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
15
|
+
import { registry, getRoutes } from '../module/moduleRegistry'
|
|
16
|
+
import type {
|
|
17
|
+
ChildPageOptions,
|
|
18
|
+
CrudOptions,
|
|
19
|
+
CrudPages,
|
|
20
|
+
NavItem,
|
|
21
|
+
ParentConfig,
|
|
22
|
+
RouteOptions,
|
|
23
|
+
} from './KernelContext.types'
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
type Self = any
|
|
27
|
+
|
|
28
|
+
/** Singularize a plural word (simple English rules) */
|
|
29
|
+
function singularize(plural: string): string {
|
|
30
|
+
if (plural.endsWith('ies')) return plural.slice(0, -3) + 'y'
|
|
31
|
+
if (plural.endsWith('ses') || plural.endsWith('xes') || plural.endsWith('zes')) {
|
|
32
|
+
return plural.slice(0, -2)
|
|
33
|
+
}
|
|
34
|
+
if (plural.endsWith('s') && !plural.endsWith('ss')) return plural.slice(0, -1)
|
|
35
|
+
return plural
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Convert camelCase to kebab-case (e.g. 'botTasks' → 'bot-tasks') */
|
|
39
|
+
function toKebab(str: string): string {
|
|
40
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Capitalize first letter */
|
|
44
|
+
function capitalize(str: string): string {
|
|
45
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
export function applyRoutingMethods(KernelContextClass: { prototype: any }): void {
|
|
50
|
+
const proto = KernelContextClass.prototype as Self
|
|
51
|
+
|
|
52
|
+
proto.routes = function (
|
|
53
|
+
this: Self,
|
|
54
|
+
basePath: string,
|
|
55
|
+
routes: RouteRecordRaw[],
|
|
56
|
+
opts: RouteOptions = {}
|
|
57
|
+
): Self {
|
|
58
|
+
registry.addRoutes(basePath, routes, opts)
|
|
59
|
+
return this
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
proto.navItem = function (this: Self, item: NavItem): Self {
|
|
63
|
+
registry.addNavItem(item)
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
proto.routeFamily = function (this: Self, base: string, prefixes: string[]): Self {
|
|
68
|
+
registry.addRouteFamily(base, prefixes)
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Register standard CRUD routes with naming conventions
|
|
74
|
+
* (entity 'books' → route prefix 'book', /books → 'book', etc.)
|
|
75
|
+
*/
|
|
76
|
+
proto.crud = function (
|
|
77
|
+
this: Self,
|
|
78
|
+
entity: string,
|
|
79
|
+
pages: CrudPages,
|
|
80
|
+
options: CrudOptions = {}
|
|
81
|
+
): Self {
|
|
82
|
+
// Entity name is always used for permission binding.
|
|
83
|
+
// Manager may not be registered yet (child entity before parent module loads).
|
|
84
|
+
const entityBinding = entity
|
|
85
|
+
const manager = this._kernel.orchestrator?.isRegistered(entity)
|
|
86
|
+
? this._kernel.orchestrator.get(entity)
|
|
87
|
+
: null
|
|
88
|
+
const idParam = manager?.idField || 'id'
|
|
89
|
+
|
|
90
|
+
// Handle parent route configuration
|
|
91
|
+
const urlSegment = options.pathSegment || toKebab(entity)
|
|
92
|
+
let basePath = urlSegment
|
|
93
|
+
let parentConfig: ParentConfig | null = null
|
|
94
|
+
let parentRoutePrefix: string | null = null
|
|
95
|
+
|
|
96
|
+
if (options.parentRoute) {
|
|
97
|
+
const parentRouteName = options.parentRoute
|
|
98
|
+
const allRoutes = getRoutes()
|
|
99
|
+
const parentRoute = allRoutes.find((r) => r.name === parentRouteName)
|
|
100
|
+
|
|
101
|
+
if (parentRoute) {
|
|
102
|
+
const parentEntityName = parentRoute.meta?.entity
|
|
103
|
+
const parentManager = parentEntityName
|
|
104
|
+
? this._kernel.orchestrator?.get(parentEntityName)
|
|
105
|
+
: null
|
|
106
|
+
const parentIdParam = parentManager?.idField || 'id'
|
|
107
|
+
|
|
108
|
+
// Build base path: parentPath/:parentId/entity (e.g., books/:bookId/loans)
|
|
109
|
+
const parentBasePath =
|
|
110
|
+
parentRoute.path.replace(/\/(create|:.*)?$/, '') || parentEntityName
|
|
111
|
+
basePath = `${parentBasePath}/:${parentIdParam}/${urlSegment}`
|
|
112
|
+
|
|
113
|
+
if (parentEntityName) {
|
|
114
|
+
parentConfig = {
|
|
115
|
+
entity: parentEntityName,
|
|
116
|
+
param: parentIdParam,
|
|
117
|
+
foreignKey: options.foreignKey || `${singularize(parentEntityName)}_id`,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
parentRoutePrefix = parentRouteName
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Derive route prefix:
|
|
126
|
+
// With parent: 'book' + '-loan' → 'book-loan'
|
|
127
|
+
// Without parent: 'books' → 'book'
|
|
128
|
+
const routePrefix =
|
|
129
|
+
options.routePrefix ||
|
|
130
|
+
(parentRoutePrefix
|
|
131
|
+
? `${parentRoutePrefix}-${singularize(entity)}`
|
|
132
|
+
: singularize(entity))
|
|
133
|
+
|
|
134
|
+
const routes: RouteRecordRaw[] = []
|
|
135
|
+
|
|
136
|
+
if (pages.list) {
|
|
137
|
+
routes.push({
|
|
138
|
+
path: '',
|
|
139
|
+
name: routePrefix,
|
|
140
|
+
component: pages.list,
|
|
141
|
+
meta: { layout: 'list' },
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (pages.show) {
|
|
146
|
+
routes.push({
|
|
147
|
+
path: `:${idParam}`,
|
|
148
|
+
name: `${routePrefix}-show`,
|
|
149
|
+
component: pages.show,
|
|
150
|
+
meta: { layout: 'show' },
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (pages.form) {
|
|
155
|
+
// Single form pattern (recommended)
|
|
156
|
+
routes.push({
|
|
157
|
+
path: 'create',
|
|
158
|
+
name: `${routePrefix}-create`,
|
|
159
|
+
component: pages.form,
|
|
160
|
+
})
|
|
161
|
+
routes.push({
|
|
162
|
+
path: `:${idParam}/edit`,
|
|
163
|
+
name: `${routePrefix}-edit`,
|
|
164
|
+
component: pages.form,
|
|
165
|
+
})
|
|
166
|
+
} else {
|
|
167
|
+
if (pages.create) {
|
|
168
|
+
routes.push({
|
|
169
|
+
path: 'create',
|
|
170
|
+
name: `${routePrefix}-create`,
|
|
171
|
+
component: pages.create,
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
if (pages.edit) {
|
|
175
|
+
routes.push({
|
|
176
|
+
path: `:${idParam}/edit`,
|
|
177
|
+
name: `${routePrefix}-edit`,
|
|
178
|
+
component: pages.edit,
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const routeOpts: RouteOptions = {}
|
|
184
|
+
// Set entity if registered, OR if this is a child route — child routes
|
|
185
|
+
// need entity binding for permission checks even before the manager
|
|
186
|
+
// exists.
|
|
187
|
+
if (manager || parentConfig) {
|
|
188
|
+
routeOpts.entity = entityBinding
|
|
189
|
+
}
|
|
190
|
+
if (parentConfig) {
|
|
191
|
+
routeOpts.parent = parentConfig
|
|
192
|
+
}
|
|
193
|
+
if (options.label) {
|
|
194
|
+
routeOpts.label = options.label
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.routes(basePath, routes, routeOpts)
|
|
198
|
+
this.routeFamily(routePrefix, [`${routePrefix}-`])
|
|
199
|
+
|
|
200
|
+
if (options.nav) {
|
|
201
|
+
const label = options.nav.label || capitalize(entity)
|
|
202
|
+
const navItem: NavItem = {
|
|
203
|
+
section: options.nav.section,
|
|
204
|
+
route: routePrefix,
|
|
205
|
+
icon: options.nav.icon,
|
|
206
|
+
label,
|
|
207
|
+
}
|
|
208
|
+
// Only set entity on nav item if registered (avoids permission check
|
|
209
|
+
// failure). Routes always get entity binding, but nav items need it
|
|
210
|
+
// resolvable.
|
|
211
|
+
if (manager) {
|
|
212
|
+
navItem.entity = entityBinding
|
|
213
|
+
}
|
|
214
|
+
this.navItem(navItem)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return this
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Register a custom child page on an entity item (e.g. /books/:id/statistics).
|
|
222
|
+
*/
|
|
223
|
+
proto.childPage = function (
|
|
224
|
+
this: Self,
|
|
225
|
+
parentRouteName: string,
|
|
226
|
+
pageName: string,
|
|
227
|
+
options: ChildPageOptions
|
|
228
|
+
): Self {
|
|
229
|
+
const allRoutes = getRoutes()
|
|
230
|
+
const parentRoute = allRoutes.find((r) => r.name === parentRouteName)
|
|
231
|
+
|
|
232
|
+
if (!parentRoute) {
|
|
233
|
+
console.warn(`[qdadm] childPage: parent route '${parentRouteName}' not found`)
|
|
234
|
+
return this
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const parentEntityName = parentRoute.meta?.entity as string | undefined
|
|
238
|
+
const parentManager = parentEntityName
|
|
239
|
+
? this._kernel.orchestrator?.get(parentEntityName)
|
|
240
|
+
: null
|
|
241
|
+
const parentIdParam = parentManager?.idField || 'id'
|
|
242
|
+
|
|
243
|
+
const parentBasePath =
|
|
244
|
+
parentRoute.path.replace(/\/(create|:.*)?$/, '') || parentEntityName
|
|
245
|
+
const basePath = `${parentBasePath}/:${parentIdParam}/${pageName}`
|
|
246
|
+
|
|
247
|
+
const routeName = `${parentRouteName}-${pageName}`
|
|
248
|
+
|
|
249
|
+
const parentConfig: ParentConfig | undefined = parentEntityName
|
|
250
|
+
? { entity: parentEntityName, param: parentIdParam }
|
|
251
|
+
: undefined
|
|
252
|
+
|
|
253
|
+
const routeOpts: RouteOptions = {}
|
|
254
|
+
if (parentConfig) {
|
|
255
|
+
routeOpts.parent = parentConfig
|
|
256
|
+
}
|
|
257
|
+
if (options.label) {
|
|
258
|
+
routeOpts.label = options.label
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this.routes(
|
|
262
|
+
basePath,
|
|
263
|
+
[
|
|
264
|
+
{
|
|
265
|
+
path: '',
|
|
266
|
+
name: routeName,
|
|
267
|
+
component: options.component,
|
|
268
|
+
meta: {
|
|
269
|
+
layout: 'page',
|
|
270
|
+
...(options.icon && { icon: options.icon }),
|
|
271
|
+
...options.meta,
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
routeOpts
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return this
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KernelContext - Fluent API wrapper for module registration.
|
|
3
|
+
*
|
|
4
|
+
* Provides a chainable interface for modules to register entities, routes,
|
|
5
|
+
* navigation items, zones, blocks, signal handlers, hooks, permissions and
|
|
6
|
+
* translations.
|
|
7
|
+
*
|
|
8
|
+
* The class shell here keeps the constructor + service getters; the fluent
|
|
9
|
+
* registration methods live in dedicated patch modules following the same
|
|
10
|
+
* pattern as `Kernel.ts`:
|
|
11
|
+
*
|
|
12
|
+
* - KernelContext.entities.ts — entity, userEntity
|
|
13
|
+
* - KernelContext.routing.ts — routes, navItem, routeFamily, crud, childPage
|
|
14
|
+
* - KernelContext.zones.ts — zone, block, provide, component
|
|
15
|
+
* - KernelContext.events.ts — on, hook, defer
|
|
16
|
+
* - KernelContext.permissions.ts — permissions, entityPermissions
|
|
17
|
+
* - KernelContext.i18n.ts — messages, aliases, messagesProvider
|
|
18
|
+
*
|
|
19
|
+
* Usage in a module:
|
|
20
|
+
* ```ts
|
|
21
|
+
* class UsersModule extends Module {
|
|
22
|
+
* async connect(ctx: KernelContext) {
|
|
23
|
+
* ctx
|
|
24
|
+
* .entity('users', { storage: 'api:/api/users' })
|
|
25
|
+
* .routes('users', [
|
|
26
|
+
* { path: '', name: 'users', component: UserList },
|
|
27
|
+
* { path: ':id', name: 'users-edit', component: UserEdit }
|
|
28
|
+
* ])
|
|
29
|
+
* .navItem({ section: 'Admin', route: 'users', icon: 'pi pi-users', label: 'Users' })
|
|
30
|
+
* .zone('users-list-header')
|
|
31
|
+
* .block('users-list-header', { component: UserStats, weight: 10 })
|
|
32
|
+
* .on('users:created', (event) => console.log('User created:', event))
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import type { App, Component } from 'vue'
|
|
39
|
+
import type { Router, RouteRecordRaw } from 'vue-router'
|
|
40
|
+
import type { ListenerOptions } from '@quazardous/quarkernel'
|
|
41
|
+
import type { SignalBus } from './SignalBus'
|
|
42
|
+
import type { Orchestrator } from '../orchestrator/Orchestrator'
|
|
43
|
+
import type { HookRegistry } from '../hooks/HookRegistry'
|
|
44
|
+
import type { ZoneRegistry } from '../zones/ZoneRegistry'
|
|
45
|
+
import type { DeferredRegistry } from '../deferred/DeferredRegistry.js'
|
|
46
|
+
import type { SecurityChecker } from '../entity/auth/SecurityChecker'
|
|
47
|
+
import type { PermissionRegistry } from '../security/PermissionRegistry'
|
|
48
|
+
import type { EntityManager } from '../entity/EntityManager'
|
|
49
|
+
import type { I18n } from '../i18n/I18n'
|
|
50
|
+
import type { AliasPattern, MessagesBundle, TranslationProvider } from '../i18n/types'
|
|
51
|
+
|
|
52
|
+
// Internal types (shared with patch modules, not exported here).
|
|
53
|
+
import type {
|
|
54
|
+
AuthAdapter,
|
|
55
|
+
DeferredFactory,
|
|
56
|
+
HookHandler,
|
|
57
|
+
KernelInterface,
|
|
58
|
+
ModuleWithCleanup,
|
|
59
|
+
SignalHandler,
|
|
60
|
+
} from './KernelContext.types'
|
|
61
|
+
|
|
62
|
+
// Public types — re-exported to keep the existing barrel API stable.
|
|
63
|
+
export type {
|
|
64
|
+
BlockConfig,
|
|
65
|
+
ChildPageOptions,
|
|
66
|
+
CrudOptions,
|
|
67
|
+
CrudPages,
|
|
68
|
+
NavItem,
|
|
69
|
+
ParentConfig,
|
|
70
|
+
PermissionMeta,
|
|
71
|
+
PermissionOptions,
|
|
72
|
+
RouteOptions,
|
|
73
|
+
UserEntityOptions,
|
|
74
|
+
ZoneOptions,
|
|
75
|
+
} from './KernelContext.types'
|
|
76
|
+
import type {
|
|
77
|
+
BlockConfig,
|
|
78
|
+
ChildPageOptions,
|
|
79
|
+
CrudOptions,
|
|
80
|
+
CrudPages,
|
|
81
|
+
NavItem,
|
|
82
|
+
PermissionMeta,
|
|
83
|
+
PermissionOptions,
|
|
84
|
+
RouteOptions,
|
|
85
|
+
UserEntityOptions,
|
|
86
|
+
ZoneOptions,
|
|
87
|
+
} from './KernelContext.types'
|
|
88
|
+
|
|
89
|
+
// Prototype-patched method groups
|
|
90
|
+
import { applyEntityMethods } from './KernelContext.entities'
|
|
91
|
+
import { applyRoutingMethods } from './KernelContext.routing'
|
|
92
|
+
import { applyZoneMethods } from './KernelContext.zones'
|
|
93
|
+
import { applyEventMethods } from './KernelContext.events'
|
|
94
|
+
import { applyPermissionMethods } from './KernelContext.permissions'
|
|
95
|
+
import { applyI18nMethods } from './KernelContext.i18n'
|
|
96
|
+
|
|
97
|
+
export class KernelContext {
|
|
98
|
+
// Marked public so the patch modules (which read this.* via Self=any) can
|
|
99
|
+
// see them. They were `private` in the previous monolithic file but were
|
|
100
|
+
// already accessed across patch boundaries the moment we split.
|
|
101
|
+
_kernel: KernelInterface
|
|
102
|
+
_module: ModuleWithCleanup | null
|
|
103
|
+
|
|
104
|
+
constructor(kernel: KernelInterface, module: ModuleWithCleanup | null) {
|
|
105
|
+
this._kernel = kernel
|
|
106
|
+
this._module = module
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
110
|
+
// Getters for kernel services
|
|
111
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
/** Vue app instance */
|
|
114
|
+
get app(): App | null {
|
|
115
|
+
return this._kernel.vueApp
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Vue router instance */
|
|
119
|
+
get router(): Router | null {
|
|
120
|
+
return this._kernel.router
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** SignalBus instance */
|
|
124
|
+
get signals(): SignalBus | null {
|
|
125
|
+
return this._kernel.signals
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Orchestrator instance (entity managers) */
|
|
129
|
+
get orchestrator(): Orchestrator | null {
|
|
130
|
+
return this._kernel.orchestrator
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** ZoneRegistry instance */
|
|
134
|
+
get zones(): ZoneRegistry | null {
|
|
135
|
+
return this._kernel.zoneRegistry
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** HookRegistry instance */
|
|
139
|
+
get hooks(): HookRegistry | null {
|
|
140
|
+
return this._kernel.hookRegistry
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** DeferredRegistry instance */
|
|
144
|
+
get deferred(): DeferredRegistry | null {
|
|
145
|
+
return this._kernel.deferred
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Whether running in development mode */
|
|
149
|
+
get isDev(): boolean {
|
|
150
|
+
return (import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV ?? false
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Debug mode from kernel options */
|
|
154
|
+
get debug(): boolean {
|
|
155
|
+
return this._kernel.options?.debug ?? false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Auth adapter */
|
|
159
|
+
get authAdapter(): AuthAdapter | null {
|
|
160
|
+
return this._kernel.options?.authAdapter ?? null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Security checker (role hierarchy, permissions) */
|
|
164
|
+
get security(): SecurityChecker | null {
|
|
165
|
+
return this._kernel.securityChecker
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Permission registry */
|
|
169
|
+
get permissionRegistry(): PermissionRegistry | null {
|
|
170
|
+
return this._kernel.permissionRegistry
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Auth adapter shortcut */
|
|
174
|
+
get auth(): AuthAdapter | null {
|
|
175
|
+
return this._kernel.options?.authAdapter ?? null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** ActiveStack instance (sync navigation context) */
|
|
179
|
+
get activeStack(): unknown {
|
|
180
|
+
return this._kernel.activeStack ?? null
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** StackHydrator instance (async data loading) */
|
|
184
|
+
get stackHydrator(): unknown {
|
|
185
|
+
return this._kernel.stackHydrator ?? null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** I18n subsystem */
|
|
189
|
+
get i18n(): I18n | null {
|
|
190
|
+
return this._kernel.i18nInstance ?? null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Declare prototype-patched methods (declaration merging)
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
196
|
+
export interface KernelContext {
|
|
197
|
+
// Entities (KernelContext.entities.ts)
|
|
198
|
+
entity(name: string, config: string | Record<string, unknown> | EntityManager): this
|
|
199
|
+
userEntity(options: UserEntityOptions): this
|
|
200
|
+
|
|
201
|
+
// Routing (KernelContext.routing.ts)
|
|
202
|
+
routes(basePath: string, routes: RouteRecordRaw[], opts?: RouteOptions): this
|
|
203
|
+
navItem(item: NavItem): this
|
|
204
|
+
routeFamily(base: string, prefixes: string[]): this
|
|
205
|
+
crud(entity: string, pages: CrudPages, options?: CrudOptions): this
|
|
206
|
+
childPage(parentRouteName: string, pageName: string, options: ChildPageOptions): this
|
|
207
|
+
|
|
208
|
+
// Zones / Vue (KernelContext.zones.ts)
|
|
209
|
+
zone(name: string, options?: ZoneOptions): this
|
|
210
|
+
block(zoneName: string, config: BlockConfig): this
|
|
211
|
+
provide(key: string | symbol, value: unknown): this
|
|
212
|
+
component(name: string, component: Component): this
|
|
213
|
+
|
|
214
|
+
// Events (KernelContext.events.ts)
|
|
215
|
+
on(signal: string, handler: SignalHandler, options?: ListenerOptions): this
|
|
216
|
+
hook(hookName: string, handler: HookHandler, options?: Record<string, unknown>): this
|
|
217
|
+
defer(name: string, factory: DeferredFactory): this
|
|
218
|
+
|
|
219
|
+
// Permissions (KernelContext.permissions.ts)
|
|
220
|
+
permissions(
|
|
221
|
+
namespace: string,
|
|
222
|
+
permissions: Record<string, string | PermissionMeta>,
|
|
223
|
+
options?: PermissionOptions
|
|
224
|
+
): this
|
|
225
|
+
entityPermissions(
|
|
226
|
+
entity: string,
|
|
227
|
+
permissions: Record<string, string | PermissionMeta>
|
|
228
|
+
): this
|
|
229
|
+
|
|
230
|
+
// i18n (KernelContext.i18n.ts)
|
|
231
|
+
messages(locale: string, bundle: MessagesBundle): this
|
|
232
|
+
aliases(patterns: AliasPattern[]): this
|
|
233
|
+
messagesProvider(provider: TranslationProvider): this
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Apply prototype patches
|
|
237
|
+
applyEntityMethods(KernelContext)
|
|
238
|
+
applyRoutingMethods(KernelContext)
|
|
239
|
+
applyZoneMethods(KernelContext)
|
|
240
|
+
applyEventMethods(KernelContext)
|
|
241
|
+
applyPermissionMethods(KernelContext)
|
|
242
|
+
applyI18nMethods(KernelContext)
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Factory function to create a KernelContext instance.
|
|
246
|
+
*/
|
|
247
|
+
export function createKernelContext(
|
|
248
|
+
kernel: KernelInterface,
|
|
249
|
+
module: ModuleWithCleanup | null
|
|
250
|
+
): KernelContext {
|
|
251
|
+
return new KernelContext(kernel, module)
|
|
252
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KernelContext types - shared between the class shell and the prototype-patch
|
|
3
|
+
* modules (KernelContext.{entities,routing,zones,events,permissions,i18n}.ts).
|
|
4
|
+
*
|
|
5
|
+
* Internal types (ModuleWithCleanup, AuthAdapter, KernelOptions,
|
|
6
|
+
* KernelInterface, function types) stay un-exported by KernelContext.ts so
|
|
7
|
+
* they remain private to qdadm internals; the public types (NavItem,
|
|
8
|
+
* ZoneOptions, …) are re-exported from KernelContext.ts to preserve the
|
|
9
|
+
* existing barrel API.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { App, Component } from 'vue'
|
|
13
|
+
import type { Router } from 'vue-router'
|
|
14
|
+
import type { UsersManagerOptions } from '../security/UsersManager'
|
|
15
|
+
import type { SignalBus } from './SignalBus'
|
|
16
|
+
import type { Orchestrator } from '../orchestrator/Orchestrator'
|
|
17
|
+
import type { HookRegistry } from '../hooks/HookRegistry'
|
|
18
|
+
import type { ZoneRegistry } from '../zones/ZoneRegistry'
|
|
19
|
+
import type { DeferredRegistry } from '../deferred/DeferredRegistry.js'
|
|
20
|
+
import type { SecurityChecker } from '../entity/auth/SecurityChecker'
|
|
21
|
+
import type { PermissionRegistry } from '../security/PermissionRegistry'
|
|
22
|
+
import type { I18n } from '../i18n/I18n'
|
|
23
|
+
|
|
24
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
// Internal types — shared across patch modules, not exposed by index.ts
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/** Extended Module interface with internal cleanup method */
|
|
29
|
+
export interface ModuleWithCleanup {
|
|
30
|
+
constructor?: { name?: string }
|
|
31
|
+
_addSignalCleanup?: (cleanup: () => void) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Auth adapter interface */
|
|
35
|
+
export interface AuthAdapter {
|
|
36
|
+
getUser(): unknown
|
|
37
|
+
[key: string]: unknown
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Kernel options interface */
|
|
41
|
+
export interface KernelOptions {
|
|
42
|
+
debug?: boolean
|
|
43
|
+
authAdapter?: AuthAdapter | null
|
|
44
|
+
storageResolver?: unknown
|
|
45
|
+
managerResolver?: unknown
|
|
46
|
+
managerRegistry?: Record<string, unknown>
|
|
47
|
+
[key: string]: unknown
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Kernel interface for type safety inside KernelContext */
|
|
51
|
+
export interface KernelInterface {
|
|
52
|
+
vueApp: App | null
|
|
53
|
+
router: Router | null
|
|
54
|
+
signals: SignalBus | null
|
|
55
|
+
orchestrator: Orchestrator | null
|
|
56
|
+
zoneRegistry: ZoneRegistry | null
|
|
57
|
+
hookRegistry: HookRegistry | null
|
|
58
|
+
deferred: DeferredRegistry | null
|
|
59
|
+
securityChecker: SecurityChecker | null
|
|
60
|
+
permissionRegistry: PermissionRegistry | null
|
|
61
|
+
activeStack: unknown
|
|
62
|
+
stackHydrator: unknown
|
|
63
|
+
options: KernelOptions
|
|
64
|
+
i18nInstance: I18n | null
|
|
65
|
+
_pendingProvides: Map<string | symbol, unknown>
|
|
66
|
+
_pendingComponents: Map<string, Component>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Signal handler function type */
|
|
70
|
+
export type SignalHandler = (event: { data: unknown; [key: string]: unknown }) => void
|
|
71
|
+
|
|
72
|
+
/** Hook handler function type */
|
|
73
|
+
export type HookHandler = (context: unknown) => unknown | Promise<unknown>
|
|
74
|
+
|
|
75
|
+
/** Deferred factory function type */
|
|
76
|
+
export type DeferredFactory = () => Promise<void>
|
|
77
|
+
|
|
78
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
79
|
+
// Public types — re-exported by KernelContext.ts (and ultimately by index.ts)
|
|
80
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/** Navigation item configuration */
|
|
83
|
+
export interface NavItem {
|
|
84
|
+
section: string
|
|
85
|
+
route: string
|
|
86
|
+
label: string
|
|
87
|
+
icon?: string
|
|
88
|
+
exact?: boolean
|
|
89
|
+
entity?: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Zone configuration */
|
|
93
|
+
export interface ZoneOptions {
|
|
94
|
+
default?: Component
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Block configuration */
|
|
98
|
+
export interface BlockConfig {
|
|
99
|
+
component: Component
|
|
100
|
+
weight?: number
|
|
101
|
+
props?: Record<string, unknown>
|
|
102
|
+
id?: string
|
|
103
|
+
operation?: 'add' | 'replace' | 'extend' | 'wrap'
|
|
104
|
+
/** Block ID to replace (required if operation='replace') */
|
|
105
|
+
replaces?: string
|
|
106
|
+
/** Block ID to insert before (for operation='extend') */
|
|
107
|
+
before?: string
|
|
108
|
+
/** Block ID to insert after (for operation='extend') */
|
|
109
|
+
after?: string
|
|
110
|
+
/** Block ID to wrap (required if operation='wrap') */
|
|
111
|
+
wraps?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Route options */
|
|
115
|
+
export interface RouteOptions {
|
|
116
|
+
entity?: string
|
|
117
|
+
parent?: ParentConfig
|
|
118
|
+
label?: string
|
|
119
|
+
layout?: string
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Parent route configuration */
|
|
123
|
+
export interface ParentConfig {
|
|
124
|
+
entity: string
|
|
125
|
+
param: string
|
|
126
|
+
foreignKey?: string
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** CRUD page components */
|
|
130
|
+
export interface CrudPages {
|
|
131
|
+
list?: () => Promise<{ default: Component }>
|
|
132
|
+
show?: () => Promise<{ default: Component }>
|
|
133
|
+
form?: () => Promise<{ default: Component }>
|
|
134
|
+
create?: () => Promise<{ default: Component }>
|
|
135
|
+
edit?: () => Promise<{ default: Component }>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** CRUD options */
|
|
139
|
+
export interface CrudOptions {
|
|
140
|
+
nav?: {
|
|
141
|
+
section: string
|
|
142
|
+
icon?: string
|
|
143
|
+
label?: string
|
|
144
|
+
}
|
|
145
|
+
routePrefix?: string
|
|
146
|
+
/** Override the URL path segment (defaults to entity name). Useful for camelCase entities like 'jobTasks' → 'tasks'. */
|
|
147
|
+
pathSegment?: string
|
|
148
|
+
parentRoute?: string
|
|
149
|
+
foreignKey?: string
|
|
150
|
+
label?: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Child page options for non-entity child routes */
|
|
154
|
+
export interface ChildPageOptions {
|
|
155
|
+
component: () => Promise<{ default: Component }>
|
|
156
|
+
label?: string
|
|
157
|
+
icon?: string
|
|
158
|
+
meta?: Record<string, unknown>
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** User entity options */
|
|
162
|
+
export type UserEntityOptions = UsersManagerOptions
|
|
163
|
+
|
|
164
|
+
/** Permission metadata */
|
|
165
|
+
export interface PermissionMeta {
|
|
166
|
+
label: string
|
|
167
|
+
description?: string
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Permission options */
|
|
171
|
+
export interface PermissionOptions {
|
|
172
|
+
isEntity?: boolean
|
|
173
|
+
}
|