@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,431 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* ShowPage - Unified show/detail page component
|
|
4
|
+
*
|
|
5
|
+
* Renders a complete read-only detail page with:
|
|
6
|
+
* - PageHeader with title and action buttons
|
|
7
|
+
* - Loading and error states
|
|
8
|
+
* - Optional media zone (for images, avatars, flags)
|
|
9
|
+
* - Field content via slots
|
|
10
|
+
* - Action footer (edit, delete, back)
|
|
11
|
+
*
|
|
12
|
+
* Props come from useEntityItemShowPage composable:
|
|
13
|
+
*
|
|
14
|
+
* ```vue
|
|
15
|
+
* const show = useEntityItemShowPage({ entity: 'books' })
|
|
16
|
+
* show.generateFields()
|
|
17
|
+
* show.addEditAction()
|
|
18
|
+
* show.addBackAction()
|
|
19
|
+
*
|
|
20
|
+
* <ShowPage v-bind="show.props.value" v-on="show.events">
|
|
21
|
+
* <template #media>
|
|
22
|
+
* <img :src="show.data.value?.thumbnail" />
|
|
23
|
+
* </template>
|
|
24
|
+
* <template #fields>
|
|
25
|
+
* <ShowField v-for="f in show.fields.value" :key="f.name" :field="f" :value="show.data.value[f.name]" />
|
|
26
|
+
* </template>
|
|
27
|
+
* </ShowPage>
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Slots:
|
|
31
|
+
* - #nav: PageNav for breadcrumb customization
|
|
32
|
+
* - #toolbar: Custom toolbar actions (between header and content)
|
|
33
|
+
* - #header-actions: Custom header action buttons
|
|
34
|
+
* - #media: Media zone (image, avatar, flag) - renders in left column
|
|
35
|
+
* - #fields: Field content (required) - renders in right column (or full width if no media)
|
|
36
|
+
* - #footer: Custom footer (replaces ShowActions if provided)
|
|
37
|
+
* - #error: Custom error display
|
|
38
|
+
* - #loading: Custom loading display
|
|
39
|
+
*/
|
|
40
|
+
import { computed, type PropType } from 'vue'
|
|
41
|
+
import PageHeader from '../layout/PageHeader.vue'
|
|
42
|
+
import Card from 'primevue/card'
|
|
43
|
+
import Button from 'primevue/button'
|
|
44
|
+
import Message from 'primevue/message'
|
|
45
|
+
import FieldGroups from '../item/FieldGroups.vue'
|
|
46
|
+
import ShowField from './ShowField.vue'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Page title parts for PageHeader
|
|
50
|
+
*/
|
|
51
|
+
interface PageTitleParts {
|
|
52
|
+
action: string
|
|
53
|
+
entityName: string | undefined
|
|
54
|
+
entityLabel: string | undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Action configuration
|
|
59
|
+
*/
|
|
60
|
+
interface ResolvedAction {
|
|
61
|
+
name: string
|
|
62
|
+
label: string
|
|
63
|
+
icon?: string
|
|
64
|
+
severity?: string
|
|
65
|
+
isLoading: boolean
|
|
66
|
+
isDisabled: boolean
|
|
67
|
+
onClick: () => void | Promise<void>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolved field config
|
|
72
|
+
*/
|
|
73
|
+
interface ResolvedFieldConfig {
|
|
74
|
+
name: string
|
|
75
|
+
type: string
|
|
76
|
+
label: string
|
|
77
|
+
[key: string]: unknown
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Fetch error type
|
|
82
|
+
*/
|
|
83
|
+
interface FetchError {
|
|
84
|
+
message?: string
|
|
85
|
+
detail?: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Field group structure
|
|
90
|
+
*/
|
|
91
|
+
interface FieldGroup {
|
|
92
|
+
name: string
|
|
93
|
+
label: string
|
|
94
|
+
fields: ResolvedFieldConfig[]
|
|
95
|
+
children: FieldGroup[]
|
|
96
|
+
parent?: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Layout mode for groups
|
|
101
|
+
*/
|
|
102
|
+
type LayoutMode = 'flat' | 'sections' | 'cards' | 'tabs' | 'accordion'
|
|
103
|
+
|
|
104
|
+
const props = defineProps({
|
|
105
|
+
// State
|
|
106
|
+
loading: { type: Boolean, default: false },
|
|
107
|
+
|
|
108
|
+
// Title (use title OR titleParts)
|
|
109
|
+
title: { type: String as PropType<string | null>, default: null },
|
|
110
|
+
titleParts: { type: Object as PropType<PageTitleParts | null>, default: null },
|
|
111
|
+
|
|
112
|
+
// Custom badges from entity manager
|
|
113
|
+
badges: { type: Array as PropType<Array<{ label: string; severity?: string }>>, default: () => [] },
|
|
114
|
+
|
|
115
|
+
// Fields (for auto-rendering - optional, can use #fields slot instead)
|
|
116
|
+
fields: { type: Array as PropType<ResolvedFieldConfig[]>, default: () => [] },
|
|
117
|
+
|
|
118
|
+
// Groups (hierarchical field organization)
|
|
119
|
+
groups: { type: Array as PropType<FieldGroup[]>, default: () => [] },
|
|
120
|
+
|
|
121
|
+
// Data (entity data for field values)
|
|
122
|
+
data: { type: Object as PropType<Record<string, unknown> | null>, default: null },
|
|
123
|
+
|
|
124
|
+
// Actions (from builder.actions)
|
|
125
|
+
actions: { type: Array as PropType<ResolvedAction[]>, default: () => [] },
|
|
126
|
+
|
|
127
|
+
// Error for fetch failures
|
|
128
|
+
fetchError: { type: [String, Object] as PropType<string | FetchError | null>, default: null },
|
|
129
|
+
|
|
130
|
+
// UI options
|
|
131
|
+
showActions: { type: Boolean, default: true },
|
|
132
|
+
cardWrapper: { type: Boolean, default: true },
|
|
133
|
+
horizontalFields: { type: Boolean, default: true },
|
|
134
|
+
labelWidth: { type: String, default: '140px' },
|
|
135
|
+
// Media zone options
|
|
136
|
+
mediaWidth: { type: String, default: '200px' },
|
|
137
|
+
|
|
138
|
+
// Group layout mode: flat, sections, cards, tabs, accordion
|
|
139
|
+
layout: { type: String as PropType<LayoutMode>, default: 'flat' },
|
|
140
|
+
|
|
141
|
+
// Child group layout mode (for nested groups)
|
|
142
|
+
childLayout: { type: String as PropType<LayoutMode>, default: 'sections' }
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Check if media slot is used
|
|
146
|
+
const slots = defineSlots<{
|
|
147
|
+
nav?: () => unknown
|
|
148
|
+
toolbar?: () => unknown
|
|
149
|
+
'header-actions'?: () => unknown
|
|
150
|
+
media?: () => unknown
|
|
151
|
+
fields?: () => unknown
|
|
152
|
+
groups?: () => unknown
|
|
153
|
+
footer?: () => unknown
|
|
154
|
+
error?: (props: { error: unknown }) => unknown
|
|
155
|
+
loading?: () => unknown
|
|
156
|
+
}>()
|
|
157
|
+
|
|
158
|
+
// Determine if we should use group rendering
|
|
159
|
+
const useGroupLayout = computed(() => {
|
|
160
|
+
// Use groups if: layout is not flat, or groups are explicitly defined (not just _default)
|
|
161
|
+
const hasRealGroups = props.groups.length > 0 && !(props.groups.length === 1 && props.groups[0]?.name === '_default')
|
|
162
|
+
return hasRealGroups && props.layout !== 'flat'
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const emit = defineEmits<{
|
|
166
|
+
(e: 'edit'): void
|
|
167
|
+
(e: 'delete'): void
|
|
168
|
+
(e: 'back'): void
|
|
169
|
+
}>()
|
|
170
|
+
|
|
171
|
+
// Header actions: actions that go in the header (e.g., edit)
|
|
172
|
+
const headerActions = computed<ResolvedAction[]>(() =>
|
|
173
|
+
props.actions.filter((a: ResolvedAction) => ['edit'].includes(a.name))
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
// Footer actions: actions that go in the footer (e.g., back, delete)
|
|
177
|
+
const footerActions = computed<ResolvedAction[]>(() =>
|
|
178
|
+
props.actions.filter((a: ResolvedAction) => !['edit'].includes(a.name))
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Get error message from fetchError
|
|
182
|
+
const fetchErrorMessage = computed<string | null>(() => {
|
|
183
|
+
if (!props.fetchError) return null
|
|
184
|
+
if (typeof props.fetchError === 'string') return props.fetchError
|
|
185
|
+
return props.fetchError.message || props.fetchError.detail || 'Failed to load entity'
|
|
186
|
+
})
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
<template>
|
|
190
|
+
<div class="show-page">
|
|
191
|
+
<!-- Nav slot for PageNav (child routes) -->
|
|
192
|
+
<slot name="nav" />
|
|
193
|
+
|
|
194
|
+
<PageHeader :title="title" :title-parts="titleParts" :badges="badges">
|
|
195
|
+
<template #actions>
|
|
196
|
+
<slot name="header-actions" />
|
|
197
|
+
<!-- Header actions from builder (e.g., edit) -->
|
|
198
|
+
<Button
|
|
199
|
+
v-for="action in headerActions"
|
|
200
|
+
:key="action.name"
|
|
201
|
+
:label="action.label"
|
|
202
|
+
:icon="action.icon"
|
|
203
|
+
:severity="(action.severity as any)"
|
|
204
|
+
:loading="action.isLoading"
|
|
205
|
+
:disabled="action.isDisabled"
|
|
206
|
+
@click="action.onClick"
|
|
207
|
+
/>
|
|
208
|
+
</template>
|
|
209
|
+
</PageHeader>
|
|
210
|
+
|
|
211
|
+
<!-- Toolbar slot (between header and content) -->
|
|
212
|
+
<slot name="toolbar" />
|
|
213
|
+
|
|
214
|
+
<!-- Loading State -->
|
|
215
|
+
<template v-if="loading">
|
|
216
|
+
<slot name="loading">
|
|
217
|
+
<div class="loading-state">
|
|
218
|
+
<i class="pi pi-spin pi-spinner" style="font-size: 2rem"></i>
|
|
219
|
+
</div>
|
|
220
|
+
</slot>
|
|
221
|
+
</template>
|
|
222
|
+
|
|
223
|
+
<!-- Error State (fetch error) -->
|
|
224
|
+
<template v-else-if="fetchError">
|
|
225
|
+
<slot name="error" :error="fetchError">
|
|
226
|
+
<Message severity="error" :closable="false" class="show-error-message">
|
|
227
|
+
{{ fetchErrorMessage }}
|
|
228
|
+
</Message>
|
|
229
|
+
</slot>
|
|
230
|
+
</template>
|
|
231
|
+
|
|
232
|
+
<!-- Content -->
|
|
233
|
+
<template v-else>
|
|
234
|
+
<!-- Card wrapper or direct content -->
|
|
235
|
+
<Card v-if="cardWrapper">
|
|
236
|
+
<template #content>
|
|
237
|
+
<!-- Grid layout with optional media zone -->
|
|
238
|
+
<div
|
|
239
|
+
class="show-content"
|
|
240
|
+
:class="{ 'show-content--with-media': slots.media }"
|
|
241
|
+
:style="slots.media ? { '--media-width': mediaWidth } : {}"
|
|
242
|
+
>
|
|
243
|
+
<!-- Media zone (optional) -->
|
|
244
|
+
<div v-if="slots.media" class="show-media">
|
|
245
|
+
<slot name="media" />
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<!-- Fields/Groups zone -->
|
|
249
|
+
<div class="show-fields" :class="{ 'show-fields--horizontal': horizontalFields && !useGroupLayout }">
|
|
250
|
+
<!-- Group layout mode -->
|
|
251
|
+
<template v-if="useGroupLayout">
|
|
252
|
+
<slot name="groups">
|
|
253
|
+
<FieldGroups
|
|
254
|
+
:groups="groups"
|
|
255
|
+
:data="data"
|
|
256
|
+
:layout="layout"
|
|
257
|
+
:child-layout="childLayout"
|
|
258
|
+
>
|
|
259
|
+
<template #field="{ field, value }">
|
|
260
|
+
<ShowField
|
|
261
|
+
:field="field"
|
|
262
|
+
:value="value as string | number | boolean | Date | Record<string, unknown> | unknown[]"
|
|
263
|
+
:horizontal="horizontalFields"
|
|
264
|
+
:label-width="labelWidth"
|
|
265
|
+
/>
|
|
266
|
+
</template>
|
|
267
|
+
</FieldGroups>
|
|
268
|
+
</slot>
|
|
269
|
+
</template>
|
|
270
|
+
<!-- Flat fields mode (default) -->
|
|
271
|
+
<template v-else>
|
|
272
|
+
<slot name="fields" />
|
|
273
|
+
</template>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<!-- Footer Actions -->
|
|
278
|
+
<template v-if="showActions && footerActions.length > 0">
|
|
279
|
+
<slot name="footer">
|
|
280
|
+
<div class="show-actions">
|
|
281
|
+
<Button
|
|
282
|
+
v-for="action in footerActions"
|
|
283
|
+
:key="action.name"
|
|
284
|
+
:label="action.label"
|
|
285
|
+
:icon="action.icon"
|
|
286
|
+
:severity="(action.severity as any)"
|
|
287
|
+
:loading="action.isLoading"
|
|
288
|
+
:disabled="action.isDisabled"
|
|
289
|
+
@click="action.onClick"
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
</slot>
|
|
293
|
+
</template>
|
|
294
|
+
</template>
|
|
295
|
+
</Card>
|
|
296
|
+
|
|
297
|
+
<template v-else>
|
|
298
|
+
<!-- Grid layout with optional media zone -->
|
|
299
|
+
<div
|
|
300
|
+
class="show-content"
|
|
301
|
+
:class="{ 'show-content--with-media': slots.media }"
|
|
302
|
+
:style="slots.media ? { '--media-width': mediaWidth } : {}"
|
|
303
|
+
>
|
|
304
|
+
<!-- Media zone (optional) -->
|
|
305
|
+
<div v-if="slots.media" class="show-media">
|
|
306
|
+
<slot name="media" />
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<!-- Fields/Groups zone -->
|
|
310
|
+
<div class="show-fields" :class="{ 'show-fields--horizontal': horizontalFields && !useGroupLayout }">
|
|
311
|
+
<!-- Group layout mode -->
|
|
312
|
+
<template v-if="useGroupLayout">
|
|
313
|
+
<slot name="groups">
|
|
314
|
+
<FieldGroups
|
|
315
|
+
:groups="groups"
|
|
316
|
+
:data="data"
|
|
317
|
+
:layout="layout"
|
|
318
|
+
:child-layout="childLayout"
|
|
319
|
+
>
|
|
320
|
+
<template #field="{ field, value }">
|
|
321
|
+
<ShowField
|
|
322
|
+
:field="field"
|
|
323
|
+
:value="value as string | number | boolean | Date | Record<string, unknown> | unknown[]"
|
|
324
|
+
:horizontal="horizontalFields"
|
|
325
|
+
:label-width="labelWidth"
|
|
326
|
+
/>
|
|
327
|
+
</template>
|
|
328
|
+
</FieldGroups>
|
|
329
|
+
</slot>
|
|
330
|
+
</template>
|
|
331
|
+
<!-- Flat fields mode (default) -->
|
|
332
|
+
<template v-else>
|
|
333
|
+
<slot name="fields" />
|
|
334
|
+
</template>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<!-- Footer Actions -->
|
|
339
|
+
<template v-if="showActions && footerActions.length > 0">
|
|
340
|
+
<slot name="footer">
|
|
341
|
+
<div class="show-actions">
|
|
342
|
+
<Button
|
|
343
|
+
v-for="action in footerActions"
|
|
344
|
+
:key="action.name"
|
|
345
|
+
:label="action.label"
|
|
346
|
+
:icon="action.icon"
|
|
347
|
+
:severity="(action.severity as any)"
|
|
348
|
+
:loading="action.isLoading"
|
|
349
|
+
:disabled="action.isDisabled"
|
|
350
|
+
@click="action.onClick"
|
|
351
|
+
/>
|
|
352
|
+
</div>
|
|
353
|
+
</slot>
|
|
354
|
+
</template>
|
|
355
|
+
</template>
|
|
356
|
+
</template>
|
|
357
|
+
</div>
|
|
358
|
+
</template>
|
|
359
|
+
|
|
360
|
+
<style scoped>
|
|
361
|
+
/*
|
|
362
|
+
* Only keep styles here that REQUIRE scoping (:deep, dynamic binding, component-specific overrides).
|
|
363
|
+
* Generic/reusable styles belong in src/styles/ partials (see _forms.scss, _cards.scss, etc.).
|
|
364
|
+
*/
|
|
365
|
+
.show-page {
|
|
366
|
+
display: flex;
|
|
367
|
+
flex-direction: column;
|
|
368
|
+
gap: 1rem;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.loading-state {
|
|
372
|
+
display: flex;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
padding: 3rem;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.show-error-message {
|
|
378
|
+
margin: 1rem 0;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* Content grid layout */
|
|
382
|
+
.show-content {
|
|
383
|
+
display: block;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.show-content--with-media {
|
|
387
|
+
display: grid;
|
|
388
|
+
grid-template-columns: var(--media-width, 200px) 1fr;
|
|
389
|
+
gap: 2rem;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
@media (max-width: 768px) {
|
|
393
|
+
.show-content--with-media {
|
|
394
|
+
grid-template-columns: 1fr;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* Media zone */
|
|
399
|
+
.show-media {
|
|
400
|
+
display: flex;
|
|
401
|
+
flex-direction: column;
|
|
402
|
+
align-items: center;
|
|
403
|
+
text-align: center;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* Fields zone */
|
|
407
|
+
.show-fields {
|
|
408
|
+
display: flex;
|
|
409
|
+
flex-direction: column;
|
|
410
|
+
gap: 1rem;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.show-fields--horizontal :deep(.show-field) {
|
|
414
|
+
flex-direction: row;
|
|
415
|
+
align-items: flex-start;
|
|
416
|
+
border-bottom: 1px solid var(--p-surface-200, #e2e8f0);
|
|
417
|
+
padding-bottom: 0.75rem;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.show-fields--horizontal :deep(.show-field:last-child) {
|
|
421
|
+
border-bottom: none;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.show-actions {
|
|
425
|
+
display: flex;
|
|
426
|
+
gap: 0.5rem;
|
|
427
|
+
margin-top: 1.5rem;
|
|
428
|
+
padding-top: 1rem;
|
|
429
|
+
border-top: 1px solid var(--p-surface-200, #e2e8f0);
|
|
430
|
+
}
|
|
431
|
+
</style>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* qdadm - Composables exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { useBareForm, type UseBareFormOptions, type UseBareFormReturn } from './useBareForm'
|
|
6
|
+
export {
|
|
7
|
+
useBreadcrumb,
|
|
8
|
+
type UseBreadcrumbOptions,
|
|
9
|
+
type UseBreadcrumbReturn,
|
|
10
|
+
type BreadcrumbDisplayItem,
|
|
11
|
+
} from './useBreadcrumb'
|
|
12
|
+
export {
|
|
13
|
+
useSemanticBreadcrumb,
|
|
14
|
+
computeSemanticBreadcrumb,
|
|
15
|
+
type SemanticBreadcrumbItem,
|
|
16
|
+
} from './useSemanticBreadcrumb'
|
|
17
|
+
export { useDirtyState, type UseDirtyStateReturn } from './useDirtyState'
|
|
18
|
+
export {
|
|
19
|
+
useForm,
|
|
20
|
+
type UseFormOptions,
|
|
21
|
+
type UseFormReturn,
|
|
22
|
+
type FormAlterConfig,
|
|
23
|
+
} from './useForm'
|
|
24
|
+
export {
|
|
25
|
+
useEntityItemFormPage,
|
|
26
|
+
useEntityItemFormPage as useFormPageBuilder,
|
|
27
|
+
type UseEntityItemFormPageOptions,
|
|
28
|
+
type UseEntityItemFormPageReturn,
|
|
29
|
+
type FieldDefinition,
|
|
30
|
+
type ResolvedFieldConfig,
|
|
31
|
+
type ActionConfig as FormActionConfig,
|
|
32
|
+
type FormPageProps,
|
|
33
|
+
type FormPageEvents,
|
|
34
|
+
} from './useEntityItemFormPage'
|
|
35
|
+
export * from './useJsonSyntax'
|
|
36
|
+
export {
|
|
37
|
+
useListPage,
|
|
38
|
+
PAGE_SIZE_OPTIONS,
|
|
39
|
+
type UseListPageOptions,
|
|
40
|
+
type UseListPageReturn,
|
|
41
|
+
type ColumnConfig,
|
|
42
|
+
type FilterConfig,
|
|
43
|
+
type ActionConfig as ListActionConfig,
|
|
44
|
+
type ResolvedAction,
|
|
45
|
+
type HeaderActionConfig,
|
|
46
|
+
type ResolvedHeaderAction,
|
|
47
|
+
type CardConfig,
|
|
48
|
+
type SearchConfig,
|
|
49
|
+
type ListPageProps,
|
|
50
|
+
type ListPageEvents,
|
|
51
|
+
type BulkStatusActionReturn,
|
|
52
|
+
} from './useListPage'
|
|
53
|
+
export { usePageTitle, type UsePageTitleReturn, type TitleParts, type TitleInput } from './usePageTitle'
|
|
54
|
+
export { useApp, type UseAppReturn } from './useApp'
|
|
55
|
+
export { useAuth, type UseAuthReturn } from './useAuth'
|
|
56
|
+
export {
|
|
57
|
+
useEntityItemPage,
|
|
58
|
+
type UseEntityItemPageOptions,
|
|
59
|
+
type UseEntityItemPageReturn,
|
|
60
|
+
type ParentConfig,
|
|
61
|
+
} from './useEntityItemPage'
|
|
62
|
+
export {
|
|
63
|
+
useEntityItemShowPage,
|
|
64
|
+
useEntityItemShowPage as useShowPageBuilder,
|
|
65
|
+
type UseEntityItemShowPageOptions,
|
|
66
|
+
type UseEntityItemShowPageReturn,
|
|
67
|
+
type FieldDefinition as ShowFieldDefinition,
|
|
68
|
+
type ResolvedFieldConfig as ShowResolvedFieldConfig,
|
|
69
|
+
type ActionConfig as ShowActionConfig,
|
|
70
|
+
type LazyActionConfig as ShowLazyActionConfig,
|
|
71
|
+
type ShowPageProps,
|
|
72
|
+
type ShowPageEvents,
|
|
73
|
+
} from './useEntityItemShowPage'
|
|
74
|
+
export {
|
|
75
|
+
useNavContext,
|
|
76
|
+
type UseNavContextOptions,
|
|
77
|
+
type UseNavContextReturn,
|
|
78
|
+
type NavChainSegment,
|
|
79
|
+
type NavChainType,
|
|
80
|
+
type BreadcrumbItem,
|
|
81
|
+
type NavLinkItem,
|
|
82
|
+
} from './useNavContext'
|
|
83
|
+
export { useNavigation, type UseNavigationReturn, type NavItem, type NavSection } from './useNavigation'
|
|
84
|
+
export {
|
|
85
|
+
useUnsavedChangesGuard,
|
|
86
|
+
type UseUnsavedChangesGuardOptions,
|
|
87
|
+
type UseUnsavedChangesGuardReturn,
|
|
88
|
+
type GuardDialogState,
|
|
89
|
+
} from './useUnsavedChangesGuard'
|
|
90
|
+
export { useGuardDialog, type GuardDialog } from './useGuardStore'
|
|
91
|
+
export { useSignals } from './useSignals'
|
|
92
|
+
export {
|
|
93
|
+
useZoneRegistry,
|
|
94
|
+
type UseZoneRegistryReturn,
|
|
95
|
+
type BlockConfig,
|
|
96
|
+
type ZoneDefinition,
|
|
97
|
+
type ZoneInfo,
|
|
98
|
+
type ZoneRegistry,
|
|
99
|
+
} from './useZoneRegistry'
|
|
100
|
+
export { useHooks } from './useHooks'
|
|
101
|
+
export {
|
|
102
|
+
useLayoutResolver,
|
|
103
|
+
createLayoutComponents,
|
|
104
|
+
layoutMeta,
|
|
105
|
+
LAYOUT_TYPES,
|
|
106
|
+
type UseLayoutResolverOptions,
|
|
107
|
+
type UseLayoutResolverReturn,
|
|
108
|
+
type LayoutType,
|
|
109
|
+
type LayoutComponentsMap,
|
|
110
|
+
type LayoutComponentsInput,
|
|
111
|
+
type LayoutMeta,
|
|
112
|
+
} from './useLayoutResolver'
|
|
113
|
+
export { useSSEBridge, type UseSSEBridgeReturn } from './useSSEBridge'
|
|
114
|
+
export {
|
|
115
|
+
useDeferred,
|
|
116
|
+
useDeferredValue,
|
|
117
|
+
DEFERRED_INJECTION_KEY,
|
|
118
|
+
type UseDeferredValueReturn,
|
|
119
|
+
} from './useDeferred'
|
|
120
|
+
export {
|
|
121
|
+
useInfoBanner,
|
|
122
|
+
provideBannerStore,
|
|
123
|
+
createBannerStore,
|
|
124
|
+
type Banner,
|
|
125
|
+
type BannerOptions,
|
|
126
|
+
type BannerSeverity,
|
|
127
|
+
type BannerStore,
|
|
128
|
+
} from './useInfoBanner'
|
|
129
|
+
export {
|
|
130
|
+
useUserImpersonator,
|
|
131
|
+
type UseUserImpersonatorOptions,
|
|
132
|
+
type UseUserImpersonatorReturn,
|
|
133
|
+
type UserRecord,
|
|
134
|
+
} from './useUserImpersonator'
|
|
135
|
+
export { useCurrentEntity, type UseCurrentEntityReturn } from './useCurrentEntity'
|
|
136
|
+
export {
|
|
137
|
+
useOptionsLookup,
|
|
138
|
+
type UseOptionsLookupConfig,
|
|
139
|
+
type UseOptionsLookupReturn,
|
|
140
|
+
type OptionsLookupItem,
|
|
141
|
+
} from './useOptionsLookup'
|
|
142
|
+
export {
|
|
143
|
+
useFieldManager,
|
|
144
|
+
snakeCaseToTitle,
|
|
145
|
+
type BaseFieldDefinition,
|
|
146
|
+
type ResolvedFieldConfig as FieldManagerFieldConfig,
|
|
147
|
+
type FieldGroup,
|
|
148
|
+
type GroupDefinition,
|
|
149
|
+
type GroupOptions,
|
|
150
|
+
type GenerateFieldsOptions as FieldManagerGenerateOptions,
|
|
151
|
+
type AddFieldOptions as FieldManagerAddOptions,
|
|
152
|
+
type MoveFieldPosition,
|
|
153
|
+
type UseFieldManagerOptions,
|
|
154
|
+
type UseFieldManagerReturn,
|
|
155
|
+
} from './useFieldManager'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useApp - Access app branding and configuration
|
|
3
|
+
*
|
|
4
|
+
* Provides access to app config set via createQdadm bootstrap.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const { name, version, logo } = useApp()
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { inject, computed, type ComputedRef } from 'vue'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* App configuration interface
|
|
14
|
+
*/
|
|
15
|
+
export interface AppConfig {
|
|
16
|
+
name?: string
|
|
17
|
+
shortName?: string
|
|
18
|
+
logo?: string | null
|
|
19
|
+
logoSmall?: string | null
|
|
20
|
+
version?: string | (() => string) | null
|
|
21
|
+
theme?: Record<string, unknown>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Return type for useApp
|
|
26
|
+
*/
|
|
27
|
+
export interface UseAppReturn {
|
|
28
|
+
name: string
|
|
29
|
+
shortName: string
|
|
30
|
+
logo: string | null | undefined
|
|
31
|
+
logoSmall: string | null | undefined
|
|
32
|
+
version: ComputedRef<string | null | undefined>
|
|
33
|
+
theme: Record<string, unknown>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function useApp(): UseAppReturn {
|
|
37
|
+
const appConfig = inject<AppConfig | null>('qdadmApp', null)
|
|
38
|
+
|
|
39
|
+
if (!appConfig) {
|
|
40
|
+
console.warn('[qdadm] qdadmApp not provided. Using defaults.')
|
|
41
|
+
return {
|
|
42
|
+
name: 'Admin',
|
|
43
|
+
shortName: 'Admin',
|
|
44
|
+
logo: null,
|
|
45
|
+
logoSmall: null,
|
|
46
|
+
version: computed(() => null),
|
|
47
|
+
theme: {},
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Version can be string or callback
|
|
52
|
+
const version = computed(() => {
|
|
53
|
+
if (typeof appConfig.version === 'function') {
|
|
54
|
+
return appConfig.version()
|
|
55
|
+
}
|
|
56
|
+
return appConfig.version ?? null
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
name: appConfig.name || 'Admin',
|
|
61
|
+
shortName: appConfig.shortName || appConfig.name || 'Admin',
|
|
62
|
+
logo: appConfig.logo,
|
|
63
|
+
logoSmall: appConfig.logoSmall,
|
|
64
|
+
version,
|
|
65
|
+
theme: appConfig.theme || {},
|
|
66
|
+
}
|
|
67
|
+
}
|