@ngbplatform/ui 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +44 -0
  3. package/package.json +61 -0
  4. package/public/favicon.svg +6 -0
  5. package/public/silent-check-sso.html +8 -0
  6. package/src/assets/ngb_logo.svg +17 -0
  7. package/src/assets/ngb_logo_color.svg +17 -0
  8. package/src/index.ts +1023 -0
  9. package/src/ngb/README.md +27 -0
  10. package/src/ngb/accounting/NgbAccountingPeriodClosingPage.vue +1157 -0
  11. package/src/ngb/accounting/NgbChartOfAccountEditor.vue +590 -0
  12. package/src/ngb/accounting/NgbChartOfAccountsPage.vue +420 -0
  13. package/src/ngb/accounting/NgbGeneralJournalEntryEditPage.vue +796 -0
  14. package/src/ngb/accounting/NgbGeneralJournalEntryLinesEditor.vue +454 -0
  15. package/src/ngb/accounting/NgbGeneralJournalEntryListPage.vue +186 -0
  16. package/src/ngb/accounting/api.ts +79 -0
  17. package/src/ngb/accounting/generalJournalEntry.ts +127 -0
  18. package/src/ngb/accounting/generalJournalEntryApi.ts +112 -0
  19. package/src/ngb/accounting/generalJournalEntryTypes.ts +155 -0
  20. package/src/ngb/accounting/navigation.ts +83 -0
  21. package/src/ngb/accounting/periodClosing.ts +75 -0
  22. package/src/ngb/accounting/periodClosingApi.ts +60 -0
  23. package/src/ngb/accounting/periodClosingTypes.ts +73 -0
  24. package/src/ngb/accounting/types.ts +57 -0
  25. package/src/ngb/api/audit.ts +23 -0
  26. package/src/ngb/api/catalogs.ts +50 -0
  27. package/src/ngb/api/contracts.ts +285 -0
  28. package/src/ngb/api/documents.ts +170 -0
  29. package/src/ngb/api/http.ts +446 -0
  30. package/src/ngb/api/lookups.ts +14 -0
  31. package/src/ngb/api/types.ts +6 -0
  32. package/src/ngb/auth/index.ts +13 -0
  33. package/src/ngb/auth/keycloak.ts +374 -0
  34. package/src/ngb/auth/router.ts +35 -0
  35. package/src/ngb/auth/types.ts +12 -0
  36. package/src/ngb/auth/useAuthStore.ts +209 -0
  37. package/src/ngb/command-palette/NgbCommandPaletteDialog.vue +342 -0
  38. package/src/ngb/command-palette/NgbCommandPaletteEmptyState.vue +55 -0
  39. package/src/ngb/command-palette/NgbCommandPaletteFooterHints.vue +29 -0
  40. package/src/ngb/command-palette/api.ts +10 -0
  41. package/src/ngb/command-palette/config.ts +16 -0
  42. package/src/ngb/command-palette/presentation.ts +85 -0
  43. package/src/ngb/command-palette/search.ts +130 -0
  44. package/src/ngb/command-palette/staticItems.ts +243 -0
  45. package/src/ngb/command-palette/storage.ts +45 -0
  46. package/src/ngb/command-palette/store.ts +716 -0
  47. package/src/ngb/command-palette/types.ts +120 -0
  48. package/src/ngb/command-palette/useCommandPaletteHotkeys.ts +27 -0
  49. package/src/ngb/command-palette/useCommandPalettePageContext.ts +18 -0
  50. package/src/ngb/components/NgbConfirmDialog.vue +63 -0
  51. package/src/ngb/components/NgbDialog.vue +44 -0
  52. package/src/ngb/components/NgbDrawer.vue +156 -0
  53. package/src/ngb/components/NgbHeaderActionCluster.vue +140 -0
  54. package/src/ngb/components/NgbModalShell.vue +99 -0
  55. package/src/ngb/components/NgbToolbar.vue +14 -0
  56. package/src/ngb/components/forms/NgbFormLayout.vue +13 -0
  57. package/src/ngb/components/forms/NgbFormRow.vue +21 -0
  58. package/src/ngb/components/forms/NgbFormSection.vue +16 -0
  59. package/src/ngb/components/forms/NgbValidationSummary.vue +18 -0
  60. package/src/ngb/components/navigation/NgbNavigationTree.vue +68 -0
  61. package/src/ngb/components/navigation/NgbTreeNode.vue +94 -0
  62. package/src/ngb/components/register/NgbColumnChooser.vue +101 -0
  63. package/src/ngb/components/register/NgbRegisterGrid.vue +473 -0
  64. package/src/ngb/components/register/registerTypes.ts +51 -0
  65. package/src/ngb/components/register/useRegisterColumnResize.ts +99 -0
  66. package/src/ngb/components/register/useRegisterColumnState.ts +220 -0
  67. package/src/ngb/components/register/useRegisterRows.ts +209 -0
  68. package/src/ngb/components/register/useRegisterViewport.ts +56 -0
  69. package/src/ngb/editor/NgbDocumentEffectsPage.vue +512 -0
  70. package/src/ngb/editor/NgbDocumentFlowPage.vue +409 -0
  71. package/src/ngb/editor/NgbDocumentPrintPage.vue +712 -0
  72. package/src/ngb/editor/NgbEditorDiscardDialog.vue +40 -0
  73. package/src/ngb/editor/NgbEntityAuditSidebar.vue +288 -0
  74. package/src/ngb/editor/NgbEntityEditor.vue +250 -0
  75. package/src/ngb/editor/NgbEntityEditorDrawerActions.vue +88 -0
  76. package/src/ngb/editor/NgbEntityEditorHeader.vue +114 -0
  77. package/src/ngb/editor/catalogNavigation.ts +13 -0
  78. package/src/ngb/editor/config.ts +252 -0
  79. package/src/ngb/editor/documentCopyDraft.ts +245 -0
  80. package/src/ngb/editor/documentEffects.ts +43 -0
  81. package/src/ngb/editor/documentEffectsDateFormatting.ts +18 -0
  82. package/src/ngb/editor/documentNavigation.ts +115 -0
  83. package/src/ngb/editor/documentStatus.ts +45 -0
  84. package/src/ngb/editor/entityEditorErrors.ts +162 -0
  85. package/src/ngb/editor/entityEditorPersistence.ts +241 -0
  86. package/src/ngb/editor/extensions.ts +40 -0
  87. package/src/ngb/editor/types.ts +295 -0
  88. package/src/ngb/editor/useConfiguredEntityEditorDocumentActions.ts +183 -0
  89. package/src/ngb/editor/useDocumentEditorDrawerState.ts +152 -0
  90. package/src/ngb/editor/useEditorDrawerState.ts +88 -0
  91. package/src/ngb/editor/useEntityEditorBusinessContext.ts +97 -0
  92. package/src/ngb/editor/useEntityEditorCapabilities.ts +190 -0
  93. package/src/ngb/editor/useEntityEditorCommandPalette.ts +154 -0
  94. package/src/ngb/editor/useEntityEditorCommitHandlers.ts +102 -0
  95. package/src/ngb/editor/useEntityEditorHeaderActions.ts +253 -0
  96. package/src/ngb/editor/useEntityEditorLeaveGuard.ts +66 -0
  97. package/src/ngb/editor/useEntityEditorNavigationActions.ts +235 -0
  98. package/src/ngb/editor/useEntityEditorOutputs.ts +59 -0
  99. package/src/ngb/editor/useEntityEditorPageActions.ts +57 -0
  100. package/src/ngb/editor/useRouteQueryEditorDrawer.ts +173 -0
  101. package/src/ngb/env/runtimeConfig.ts +25 -0
  102. package/src/ngb/lookup/config.ts +42 -0
  103. package/src/ngb/lookup/defaultConfig.ts +136 -0
  104. package/src/ngb/lookup/navigation.ts +76 -0
  105. package/src/ngb/lookup/prefetch.ts +40 -0
  106. package/src/ngb/lookup/store.ts +261 -0
  107. package/src/ngb/metadata/NgbDocumentListFiltersDrawer.vue +78 -0
  108. package/src/ngb/metadata/NgbDocumentPeriodFilter.vue +53 -0
  109. package/src/ngb/metadata/NgbEntityForm.vue +84 -0
  110. package/src/ngb/metadata/NgbEntityFormFieldsBlock.vue +110 -0
  111. package/src/ngb/metadata/NgbEntityListPageHeader.vue +80 -0
  112. package/src/ngb/metadata/NgbFilterFieldControl.vue +104 -0
  113. package/src/ngb/metadata/NgbMetadataCatalogEditPage.vue +42 -0
  114. package/src/ngb/metadata/NgbMetadataCatalogListPage.vue +269 -0
  115. package/src/ngb/metadata/NgbMetadataDocumentEditPage.vue +68 -0
  116. package/src/ngb/metadata/NgbMetadataDocumentListPage.vue +353 -0
  117. package/src/ngb/metadata/NgbMetadataFieldRenderer.vue +124 -0
  118. package/src/ngb/metadata/NgbMetadataLookupControl.vue +92 -0
  119. package/src/ngb/metadata/NgbRecycleBinFilter.vue +53 -0
  120. package/src/ngb/metadata/NgbRegisterPageLayout.vue +143 -0
  121. package/src/ngb/metadata/config.ts +28 -0
  122. package/src/ngb/metadata/dataTypes.ts +27 -0
  123. package/src/ngb/metadata/entityForm.ts +114 -0
  124. package/src/ngb/metadata/entityModel.ts +34 -0
  125. package/src/ngb/metadata/entityValueFormatting.ts +83 -0
  126. package/src/ngb/metadata/fieldRendererState.ts +84 -0
  127. package/src/ngb/metadata/filtering.ts +153 -0
  128. package/src/ngb/metadata/lookup.ts +25 -0
  129. package/src/ngb/metadata/monthPagedListQuery.ts +108 -0
  130. package/src/ngb/metadata/normalization.ts +100 -0
  131. package/src/ngb/metadata/referenceHydration.ts +99 -0
  132. package/src/ngb/metadata/register.ts +135 -0
  133. package/src/ngb/metadata/routePages.ts +86 -0
  134. package/src/ngb/metadata/store.ts +35 -0
  135. package/src/ngb/metadata/types.ts +276 -0
  136. package/src/ngb/metadata/useMetadataListFilters.ts +306 -0
  137. package/src/ngb/metadata/useMetadataRegisterPageData.ts +246 -0
  138. package/src/ngb/metadata/useValidationFocus.ts +49 -0
  139. package/src/ngb/primitives/NgbBadge.vue +31 -0
  140. package/src/ngb/primitives/NgbButton.vue +53 -0
  141. package/src/ngb/primitives/NgbCheckbox.vue +33 -0
  142. package/src/ngb/primitives/NgbDatePicker.vue +141 -0
  143. package/src/ngb/primitives/NgbIcon.vue +545 -0
  144. package/src/ngb/primitives/NgbInput.vue +57 -0
  145. package/src/ngb/primitives/NgbLookup.vue +264 -0
  146. package/src/ngb/primitives/NgbMonthPicker.vue +132 -0
  147. package/src/ngb/primitives/NgbMultiSelect.vue +179 -0
  148. package/src/ngb/primitives/NgbPeriodPicker.vue +125 -0
  149. package/src/ngb/primitives/NgbPickerNavButton.vue +31 -0
  150. package/src/ngb/primitives/NgbPickerPopover.vue +79 -0
  151. package/src/ngb/primitives/NgbRadioGroup.vue +53 -0
  152. package/src/ngb/primitives/NgbSelect.vue +108 -0
  153. package/src/ngb/primitives/NgbStatusIcon.vue +63 -0
  154. package/src/ngb/primitives/NgbSwitch.vue +42 -0
  155. package/src/ngb/primitives/NgbTabs.vue +116 -0
  156. package/src/ngb/primitives/NgbToastHost.vue +49 -0
  157. package/src/ngb/primitives/NgbTooltip.vue +27 -0
  158. package/src/ngb/primitives/iconNames.ts +83 -0
  159. package/src/ngb/primitives/toast.ts +66 -0
  160. package/src/ngb/primitives/useAsyncComboboxQuery.ts +85 -0
  161. package/src/ngb/primitives/useFloatingDropdownPosition.ts +129 -0
  162. package/src/ngb/reporting/NgbReportComposerCollectionSection.vue +106 -0
  163. package/src/ngb/reporting/NgbReportComposerPanel.vue +1187 -0
  164. package/src/ngb/reporting/NgbReportDateRangeFilter.vue +54 -0
  165. package/src/ngb/reporting/NgbReportPage.vue +1124 -0
  166. package/src/ngb/reporting/NgbReportSheet.vue +467 -0
  167. package/src/ngb/reporting/api.ts +35 -0
  168. package/src/ngb/reporting/composer.ts +904 -0
  169. package/src/ngb/reporting/config.ts +120 -0
  170. package/src/ngb/reporting/defaultConfig.ts +15 -0
  171. package/src/ngb/reporting/lookupFilters.ts +43 -0
  172. package/src/ngb/reporting/navigation.ts +181 -0
  173. package/src/ngb/reporting/pageHelpers.ts +73 -0
  174. package/src/ngb/reporting/pageSession.ts +92 -0
  175. package/src/ngb/reporting/paging.ts +66 -0
  176. package/src/ngb/reporting/types.ts +333 -0
  177. package/src/ngb/router/backNavigation.ts +122 -0
  178. package/src/ngb/router/queryParams.ts +129 -0
  179. package/src/ngb/router/queryState.ts +121 -0
  180. package/src/ngb/router/routeAliases.ts +66 -0
  181. package/src/ngb/router/routeParams.ts +15 -0
  182. package/src/ngb/router/shareLink.ts +39 -0
  183. package/src/ngb/security/NgbAccessAuditPanel.vue +32 -0
  184. package/src/ngb/security/NgbAccessDeniedState.vue +27 -0
  185. package/src/ngb/security/NgbEffectiveAccessPanel.vue +60 -0
  186. package/src/ngb/security/NgbPermissionMatrix.vue +160 -0
  187. package/src/ngb/security/NgbRoleEditorPage.vue +319 -0
  188. package/src/ngb/security/NgbRolesPage.vue +125 -0
  189. package/src/ngb/security/NgbUserEditorPage.vue +615 -0
  190. package/src/ngb/security/NgbUsersPage.vue +125 -0
  191. package/src/ngb/security/api.ts +91 -0
  192. package/src/ngb/security/audit.ts +21 -0
  193. package/src/ngb/security/permissions.ts +65 -0
  194. package/src/ngb/security/types.ts +157 -0
  195. package/src/ngb/security/useAccessStore.ts +69 -0
  196. package/src/ngb/site/NgbDashboardAsOfToolbar.vue +100 -0
  197. package/src/ngb/site/NgbDashboardStatusBanner.vue +91 -0
  198. package/src/ngb/site/NgbLogo.vue +44 -0
  199. package/src/ngb/site/NgbPageHeader.vue +49 -0
  200. package/src/ngb/site/NgbSiteShell.vue +286 -0
  201. package/src/ngb/site/NgbSiteSidebar.vue +270 -0
  202. package/src/ngb/site/NgbTopBar.vue +425 -0
  203. package/src/ngb/site/NgbTrendChart.vue +224 -0
  204. package/src/ngb/site/dashboardData.ts +324 -0
  205. package/src/ngb/site/mainMenuStore.ts +49 -0
  206. package/src/ngb/site/types.ts +31 -0
  207. package/src/ngb/site/useDashboardPageState.ts +98 -0
  208. package/src/ngb/site/useTheme.ts +95 -0
  209. package/src/ngb/utils/clone.ts +43 -0
  210. package/src/ngb/utils/dateValues.ts +107 -0
  211. package/src/ngb/utils/errorMessage.ts +14 -0
  212. package/src/ngb/utils/guid.ts +21 -0
  213. package/src/ngb/utils/stableValue.ts +21 -0
  214. package/src/ngb/utils/storage.ts +135 -0
  215. package/src/styles/tailwind.css +165 -0
  216. package/src/vite/publicAssets.ts +104 -0
  217. package/vite-public-assets.js +68 -0
@@ -0,0 +1,104 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import NgbInput from '../primitives/NgbInput.vue'
4
+ import NgbLookup from '../primitives/NgbLookup.vue'
5
+ import NgbMultiSelect from '../primitives/NgbMultiSelect.vue'
6
+ import NgbSelect from '../primitives/NgbSelect.vue'
7
+ import NgbSwitch from '../primitives/NgbSwitch.vue'
8
+ import { filterInputType, filterPlaceholder, filterSelectOptions } from './filtering'
9
+ import type { FilterFieldLike, FilterFieldState, FilterLookupItem } from './types'
10
+
11
+ const props = withDefaults(defineProps<{
12
+ field: FilterFieldLike
13
+ state: FilterFieldState
14
+ lookupItems: FilterLookupItem[]
15
+ disabled?: boolean
16
+ selectEmptyLabel?: string
17
+ showOpen?: boolean
18
+ showClear?: boolean
19
+ allowIncludeDescendants?: boolean
20
+ includeDescendantsLabel?: string
21
+ }>(), {
22
+ disabled: false,
23
+ selectEmptyLabel: 'Any',
24
+ showOpen: false,
25
+ showClear: false,
26
+ allowIncludeDescendants: false,
27
+ includeDescendantsLabel: 'Include descendants',
28
+ })
29
+
30
+ const emit = defineEmits<{
31
+ (e: 'lookup-query', query: string): void
32
+ (e: 'update:items', items: FilterLookupItem[]): void
33
+ (e: 'update:raw', value: string): void
34
+ (e: 'update:includeDescendants', value: boolean): void
35
+ (e: 'open'): void
36
+ }>()
37
+
38
+ const selectedItem = computed(() => props.state.items[0] ?? null)
39
+ const isLookup = computed(() => !!props.field.lookup)
40
+ const hasOptions = computed(() => (props.field.options?.length ?? 0) > 0)
41
+ const shouldShowIncludeDescendants = computed(() =>
42
+ props.allowIncludeDescendants
43
+ && !!props.field.supportsIncludeDescendants
44
+ && props.state.items.length > 0)
45
+ const selectOptions = computed(() => filterSelectOptions(props.field, props.selectEmptyLabel))
46
+ const placeholder = computed(() => filterPlaceholder(props.field))
47
+ const inputType = computed(() => filterInputType(props.field.dataType))
48
+ </script>
49
+
50
+ <template>
51
+ <div class="space-y-3">
52
+ <NgbMultiSelect
53
+ v-if="isLookup && field.isMulti"
54
+ :model-value="state.items"
55
+ :items="lookupItems"
56
+ :disabled="disabled"
57
+ :placeholder="placeholder"
58
+ @query="emit('lookup-query', $event)"
59
+ @update:model-value="emit('update:items', $event as FilterLookupItem[])"
60
+ />
61
+
62
+ <NgbLookup
63
+ v-else-if="isLookup"
64
+ :model-value="selectedItem"
65
+ :items="lookupItems"
66
+ :disabled="disabled"
67
+ :show-open="showOpen"
68
+ :show-clear="showClear"
69
+ :placeholder="placeholder"
70
+ @query="emit('lookup-query', $event)"
71
+ @update:model-value="emit('update:items', $event ? [$event as FilterLookupItem] : [])"
72
+ @open="emit('open')"
73
+ />
74
+
75
+ <NgbSelect
76
+ v-else-if="hasOptions && !field.isMulti"
77
+ :model-value="state.raw"
78
+ :options="selectOptions"
79
+ :disabled="disabled"
80
+ @update:model-value="emit('update:raw', String($event ?? ''))"
81
+ />
82
+
83
+ <NgbInput
84
+ v-else
85
+ :model-value="state.raw"
86
+ :type="inputType"
87
+ :disabled="disabled"
88
+ :placeholder="placeholder"
89
+ @update:model-value="emit('update:raw', String($event ?? ''))"
90
+ />
91
+
92
+ <div
93
+ v-if="shouldShowIncludeDescendants"
94
+ class="flex items-center justify-between gap-3 rounded-[var(--ngb-radius)] border border-ngb-border bg-ngb-card px-3 py-2"
95
+ >
96
+ <div class="text-sm text-ngb-text">{{ includeDescendantsLabel }}</div>
97
+ <NgbSwitch
98
+ :model-value="!!state.includeDescendants"
99
+ :disabled="disabled"
100
+ @update:model-value="emit('update:includeDescendants', $event)"
101
+ />
102
+ </div>
103
+ </div>
104
+ </template>
@@ -0,0 +1,42 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useRoute } from 'vue-router'
4
+
5
+ import { buildCatalogCompactPageUrl, buildCatalogListUrl } from '../editor/catalogNavigation'
6
+ import type { MetadataCatalogEditPageProps } from './routePages'
7
+ import { normalizeEntityEditorIdRouteParam, normalizeRequiredRouteParam } from '../router/routeParams'
8
+
9
+ const props = withDefaults(defineProps<MetadataCatalogEditPageProps>(), {
10
+ canBack: true,
11
+ resolveCompactTo: null,
12
+ resolveCloseTo: null,
13
+ })
14
+
15
+ const route = useRoute()
16
+
17
+ const catalogType = computed(() => normalizeRequiredRouteParam(route.params.catalogType))
18
+ const idParam = computed(() => normalizeEntityEditorIdRouteParam(route.params.id))
19
+
20
+ const compactTo = computed(() => {
21
+ if (!catalogType.value) return null
22
+ return props.resolveCompactTo?.(catalogType.value, idParam.value) ?? buildCatalogCompactPageUrl(catalogType.value, idParam.value)
23
+ })
24
+
25
+ const closeTo = computed(() => {
26
+ if (!catalogType.value) return '/'
27
+ return props.resolveCloseTo?.(catalogType.value) ?? buildCatalogListUrl(catalogType.value)
28
+ })
29
+ </script>
30
+
31
+ <template>
32
+ <component
33
+ :is="editorComponent"
34
+ kind="catalog"
35
+ :type-code="catalogType"
36
+ :id="idParam"
37
+ mode="page"
38
+ :can-back="canBack"
39
+ :compact-to="compactTo"
40
+ :close-to="closeTo"
41
+ />
42
+ </template>
@@ -0,0 +1,269 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import { useRoute, useRouter } from 'vue-router'
4
+
5
+ import NgbEditorDiscardDialog from '../editor/NgbEditorDiscardDialog.vue'
6
+ import NgbEntityEditorDrawerActions from '../editor/NgbEntityEditorDrawerActions.vue'
7
+ import { buildCatalogFullPageUrl } from '../editor/catalogNavigation'
8
+ import { useEditorDrawerState } from '../editor/useEditorDrawerState'
9
+ import { useEntityEditorCommitHandlers } from '../editor/useEntityEditorCommitHandlers'
10
+ import type { EntityEditorHandle } from '../editor/types'
11
+ import { useRouteQueryEditorDrawer } from '../editor/useRouteQueryEditorDrawer'
12
+ import NgbRecycleBinFilter from './NgbRecycleBinFilter.vue'
13
+ import NgbRegisterPageLayout from './NgbRegisterPageLayout.vue'
14
+ import type { MetadataCatalogListPageProps } from './routePages'
15
+ import { useMetadataPageReloadKey, useMetadataRegisterPageData } from './useMetadataRegisterPageData'
16
+ import { useMetadataStore } from './store'
17
+ import { navigateBack } from '../router/backNavigation'
18
+ import { normalizeTrashMode, pushCleanRouteQuery, type QueryTrashMode } from '../router/queryParams'
19
+
20
+ const props = withDefaults(defineProps<MetadataCatalogListPageProps>(), {
21
+ resolveTitle: null,
22
+ resolveStorageKey: null,
23
+ resolveDrawerExtraActions: null,
24
+ handleDrawerExtraAction: null,
25
+ backTarget: '/',
26
+ })
27
+
28
+ const route = useRoute()
29
+ const router = useRouter()
30
+ const metaStore = useMetadataStore()
31
+
32
+ const resolvedBackTarget = computed(() => String(props.backTarget ?? '').trim() || '/')
33
+ const canBack = computed(() => route.path !== resolvedBackTarget.value)
34
+ const catalogType = computed(() => String(route.params.catalogType ?? ''))
35
+ const offset = computed(() => Number(route.query.offset ?? 0) || 0)
36
+ const limit = computed(() => Number(route.query.limit ?? 50) || 50)
37
+
38
+ const trashMode = computed<QueryTrashMode>({
39
+ get() {
40
+ return normalizeTrashMode(route.query.trash)
41
+ },
42
+ set(value) {
43
+ void pushCleanRouteQuery(route, router, {
44
+ trash: value,
45
+ offset: 0,
46
+ })
47
+ },
48
+ })
49
+
50
+ function isCatalogEditorDrawerQueryKey(key: string): boolean {
51
+ return key === 'panel' || key === 'id'
52
+ }
53
+
54
+ const routeReloadKey = useMetadataPageReloadKey({
55
+ route,
56
+ entityTypeCode: catalogType,
57
+ ignoreQueryKey: isCatalogEditorDrawerQueryKey,
58
+ })
59
+
60
+ const {
61
+ loading,
62
+ error,
63
+ metadata,
64
+ page,
65
+ columns,
66
+ rows,
67
+ load,
68
+ } = useMetadataRegisterPageData({
69
+ route,
70
+ entityTypeCode: catalogType,
71
+ reloadKey: routeReloadKey,
72
+ loadMetadata: (entityTypeCode) => metaStore.ensureCatalogType(entityTypeCode),
73
+ loadPage: ({ entityTypeCode }) =>
74
+ props.loadPage({
75
+ catalogType: entityTypeCode,
76
+ offset: offset.value,
77
+ limit: limit.value,
78
+ search: String(route.query.search ?? '') || undefined,
79
+ trashMode: trashMode.value,
80
+ }),
81
+ })
82
+
83
+ const resolvedTitle = computed(() => {
84
+ if (!metadata.value) return `Catalog: ${catalogType.value}`
85
+ return props.resolveTitle?.(catalogType.value, metadata.value.displayName) ?? metadata.value.displayName
86
+ })
87
+
88
+ const resolvedStorageKey = computed(() =>
89
+ String(props.resolveStorageKey?.(catalogType.value) ?? '').trim() || `ngb:catalog:${catalogType.value}`,
90
+ )
91
+
92
+ const editorRef = ref<EntityEditorHandle | null>(null)
93
+ const {
94
+ drawerTitle,
95
+ drawerSubtitle,
96
+ editorFlags,
97
+ discardOpen,
98
+ handleEditorFlags,
99
+ handleEditorState,
100
+ resetDrawerHeading,
101
+ requestDiscard,
102
+ discardConfirm,
103
+ discardCancel,
104
+ beforeCloseDrawer,
105
+ } = useEditorDrawerState()
106
+
107
+ const {
108
+ panelMode,
109
+ currentId,
110
+ isPanelOpen,
111
+ openCreateDrawer: openRouteCreateDrawer,
112
+ openEditDrawer: openRouteEditDrawer,
113
+ closeDrawer: closeRouteDrawer,
114
+ } = useRouteQueryEditorDrawer({
115
+ route,
116
+ router,
117
+ onBeforeOpen: async (_next, current) => {
118
+ if (current.mode === null || !editorFlags.value.isDirty) return true
119
+ return await requestDiscard()
120
+ },
121
+ onBeforeClose: async (current) => {
122
+ if (current.mode === null || !editorFlags.value.isDirty) return true
123
+ return await requestDiscard()
124
+ },
125
+ })
126
+
127
+ async function openCreateDrawer() {
128
+ await openRouteCreateDrawer({
129
+ onCommit: () => {
130
+ resetDrawerHeading()
131
+ },
132
+ })
133
+ }
134
+
135
+ async function openEditDrawer(id: string) {
136
+ await openRouteEditDrawer(id, {
137
+ onCommit: () => {
138
+ resetDrawerHeading()
139
+ },
140
+ })
141
+ }
142
+
143
+ async function closeDrawer() {
144
+ await closeRouteDrawer()
145
+ }
146
+
147
+ const {
148
+ handleCreated,
149
+ handleSaved,
150
+ handleChanged,
151
+ handleDeleted,
152
+ } = useEntityEditorCommitHandlers({
153
+ reload: load,
154
+ closeDrawer,
155
+ })
156
+
157
+ function nextPage() {
158
+ void pushCleanRouteQuery(route, router, { offset: offset.value + limit.value })
159
+ }
160
+
161
+ function prevPage() {
162
+ void pushCleanRouteQuery(route, router, { offset: Math.max(0, offset.value - limit.value) })
163
+ }
164
+
165
+ const resolvedDrawerExtraActions = computed(() =>
166
+ props.resolveDrawerExtraActions?.({
167
+ editorFlags: editorFlags.value,
168
+ }) ?? [],
169
+ )
170
+
171
+ async function handleDrawerAction(action: string) {
172
+ switch (action) {
173
+ case 'expand':
174
+ editorRef.value?.openFullPage()
175
+ return
176
+ case 'share':
177
+ void editorRef.value?.copyShareLink()
178
+ return
179
+ case 'audit':
180
+ editorRef.value?.openAuditLog()
181
+ return
182
+ case 'mark':
183
+ editorRef.value?.toggleMarkForDeletion()
184
+ return
185
+ case 'save':
186
+ void editorRef.value?.save()
187
+ return
188
+ default:
189
+ await props.handleDrawerExtraAction?.({
190
+ action,
191
+ editor: editorRef.value,
192
+ })
193
+ }
194
+ }
195
+
196
+ const expandTo = computed(() => {
197
+ if (panelMode.value === 'new') return buildCatalogFullPageUrl(catalogType.value)
198
+ if (currentId.value) return buildCatalogFullPageUrl(catalogType.value, currentId.value)
199
+ return null
200
+ })
201
+ </script>
202
+
203
+ <template>
204
+ <NgbRegisterPageLayout
205
+ :title="resolvedTitle"
206
+ :can-back="canBack"
207
+ :items-count="page ? page.items.length : null"
208
+ :total="page?.total ?? null"
209
+ :loading="loading"
210
+ :error="error"
211
+ :show-filter="false"
212
+ :disable-create="metadata?.capabilities?.canCreate === false"
213
+ :disable-prev="offset === 0"
214
+ :disable-next="(page?.items?.length ?? 0) < limit"
215
+ :columns="columns"
216
+ :rows="rows"
217
+ :storage-key="resolvedStorageKey"
218
+ :drawer-open="isPanelOpen"
219
+ :drawer-title="drawerTitle || (panelMode === 'new' ? `New ${metadata?.displayName ?? 'Record'}` : metadata?.displayName ?? 'Edit')"
220
+ :drawer-subtitle="drawerSubtitle"
221
+ :before-close="beforeCloseDrawer"
222
+ @back="navigateBack(router, route, resolvedBackTarget)"
223
+ @refresh="load"
224
+ @create="() => void openCreateDrawer()"
225
+ @prev="prevPage"
226
+ @next="nextPage"
227
+ @rowActivate="(id) => void openEditDrawer(String(id))"
228
+ @update:drawerOpen="(value) => (!value ? void closeDrawer() : null)"
229
+ >
230
+ <template #filters>
231
+ <NgbRecycleBinFilter v-model="trashMode" :disabled="loading" />
232
+ </template>
233
+
234
+ <template #drawerActions>
235
+ <NgbEntityEditorDrawerActions
236
+ :flags="editorFlags"
237
+ :extra-actions="resolvedDrawerExtraActions"
238
+ @action="(action) => void handleDrawerAction(action)"
239
+ />
240
+ </template>
241
+
242
+ <template #drawerContent>
243
+ <component
244
+ :is="editorComponent"
245
+ ref="editorRef"
246
+ v-if="isPanelOpen"
247
+ kind="catalog"
248
+ :type-code="catalogType"
249
+ :id="currentId"
250
+ mode="drawer"
251
+ :expand-to="expandTo"
252
+ :navigate-on-create="false"
253
+ @state="handleEditorState"
254
+ @flags="handleEditorFlags"
255
+ @created="handleCreated"
256
+ @saved="handleSaved"
257
+ @changed="handleChanged"
258
+ @deleted="handleDeleted"
259
+ @close="closeDrawer"
260
+ />
261
+ </template>
262
+ </NgbRegisterPageLayout>
263
+
264
+ <NgbEditorDiscardDialog
265
+ :open="discardOpen"
266
+ @cancel="discardCancel"
267
+ @confirm="discardConfirm"
268
+ />
269
+ </template>
@@ -0,0 +1,68 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useRoute } from 'vue-router'
4
+
5
+ import { buildEntityFallbackCloseTarget } from '../editor/documentNavigation'
6
+ import { buildDocumentCompactPageUrl } from '../editor/documentNavigation'
7
+ import { resolveCompactDocumentSourceTarget } from '../editor/documentNavigation'
8
+ import { readDocumentCopyDraft } from '../editor/documentCopyDraft'
9
+ import type { MetadataDocumentEditPageProps } from './routePages'
10
+ import type { EntityFormModel, RecordPayload } from './types'
11
+ import { normalizeEntityEditorIdRouteParam, normalizeRequiredRouteParam } from '../router/routeParams'
12
+
13
+ const props = withDefaults(defineProps<MetadataDocumentEditPageProps>(), {
14
+ canBack: true,
15
+ resolveCompactTo: null,
16
+ resolveCloseTo: null,
17
+ })
18
+
19
+ const route = useRoute()
20
+
21
+ function normalizeQueryString(value: unknown): string | null {
22
+ if (Array.isArray(value)) return normalizeQueryString(value[0])
23
+ const normalized = String(value ?? '').trim()
24
+ return normalized || null
25
+ }
26
+
27
+ const documentType = computed(() => normalizeRequiredRouteParam(route.params.documentType))
28
+ const idParam = computed(() => normalizeEntityEditorIdRouteParam(route.params.id))
29
+
30
+ const compactTo = computed(() => {
31
+ if (!documentType.value) return null
32
+
33
+ const fallbackCompactTarget =
34
+ props.resolveCompactTo?.(documentType.value, idParam.value)
35
+ ?? buildDocumentCompactPageUrl(documentType.value, idParam.value)
36
+
37
+ return resolveCompactDocumentSourceTarget(route, fallbackCompactTarget) ?? fallbackCompactTarget
38
+ })
39
+
40
+ const closeTo = computed(() => {
41
+ if (!documentType.value) return '/'
42
+ return props.resolveCloseTo?.(documentType.value) ?? buildEntityFallbackCloseTarget('document', documentType.value)
43
+ })
44
+
45
+ const copyDraftToken = computed(() => (!idParam.value ? normalizeQueryString(route.query.copyDraft) : null))
46
+ const copyDraftSnapshot = computed(() =>
47
+ (!idParam.value && documentType.value)
48
+ ? readDocumentCopyDraft(copyDraftToken.value, documentType.value)
49
+ : null,
50
+ )
51
+ const initialFields = computed<EntityFormModel | null>(() => copyDraftSnapshot.value?.fields ?? null)
52
+ const initialParts = computed<RecordPayload['parts'] | null>(() => copyDraftSnapshot.value?.parts ?? null)
53
+ </script>
54
+
55
+ <template>
56
+ <component
57
+ :is="editorComponent"
58
+ kind="document"
59
+ :type-code="documentType"
60
+ :id="idParam"
61
+ mode="page"
62
+ :can-back="canBack"
63
+ :initial-fields="initialFields"
64
+ :initial-parts="initialParts"
65
+ :compact-to="compactTo"
66
+ :close-to="closeTo"
67
+ />
68
+ </template>