@conduction/nextcloud-vue 2.83.0 → 2.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +131 -3
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +54 -18
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +232 -4
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +95 -51
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue.js +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js +22 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue3.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +47 -4
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +239 -3
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +5 -5
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +50 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -12
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +226 -8
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +3 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +18 -5
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +38 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +45 -5
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +548 -77
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +191 -82
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +11 -2
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +29 -6
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +126 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +24 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +106 -3
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +67 -5
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +181 -124
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +135 -2
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +95 -43
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +5 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue.js +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +72 -5
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +33 -8
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +135 -11
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -2
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +17 -2
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +39 -6
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +140 -7
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +37 -9
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +6 -5
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue.js +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +16 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +2 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js +53 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js +3 -2
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +135 -0
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +19 -0
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +37 -19
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/composables/useListView.js +21 -0
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +68 -6
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +133 -105
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +38 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +78 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1030 -533
- package/dist/esm/store/useObjectStore.js +16 -2
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/boardColumns.js +8 -1
- package/dist/esm/utils/boardColumns.js.map +1 -1
- package/dist/esm/utils/boardReferenceColumns.js +278 -0
- package/dist/esm/utils/boardReferenceColumns.js.map +1 -0
- package/dist/esm/utils/buildManifest.js +41 -4
- package/dist/esm/utils/buildManifest.js.map +1 -1
- package/dist/esm/utils/detailWidgetVisibility.js +149 -0
- package/dist/esm/utils/detailWidgetVisibility.js.map +1 -0
- package/dist/esm/utils/formatMetric.js +39 -3
- package/dist/esm/utils/formatMetric.js.map +1 -1
- package/dist/esm/utils/gridStack.js +40 -1
- package/dist/esm/utils/gridStack.js.map +1 -1
- package/dist/esm/utils/humaniseKey.js +41 -0
- package/dist/esm/utils/humaniseKey.js.map +1 -0
- package/dist/esm/utils/notificationRuleLabel.js +6 -23
- package/dist/esm/utils/notificationRuleLabel.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +1 -0
- package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +9 -0
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +8 -2
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +17159 -13422
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1030 -533
- package/l10n/en.json +38 -1
- package/l10n/nl.json +78 -1
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +160 -8
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +1 -1
- package/src/components/CnBoardView/CnBoardView.vue +262 -6
- package/src/components/CnBodySections/CnBodySections.vue +22 -1
- package/src/components/CnCountdownWidget/CnCountdownWidget.vue +47 -4
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +244 -8
- package/src/components/CnDashboardPage/CnDashboardPage.vue +62 -2
- package/src/components/CnDeltaWidget/CnDeltaWidget.vue +1 -1
- package/src/components/CnDetailPage/CnDetailPage.md +3 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +226 -8
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +3 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -5
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +63 -1
- package/src/components/CnGaugeWidget/CnGaugeWidget.vue +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +586 -25
- package/src/components/CnIndexPage/useSelfFetchList.js +10 -1
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +29 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +154 -5
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +106 -3
- package/src/components/CnRegisterMapping/CnRegisterMapping.md +5 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +231 -96
- package/src/components/CnReportsPage/CnReportsPage.vue +179 -1
- package/src/components/CnSettingsPage/CnSettingsPage.vue +5 -1
- package/src/components/CnStagesWidget/CnStagesWidget.vue +103 -6
- package/src/components/CnStatWidget/CnStatWidget.vue +137 -12
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +34 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +39 -6
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +140 -7
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.md +2 -1
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +42 -13
- package/src/components/CnWalkthrough/CnWalkthrough.vue +16 -0
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +2 -1
- package/src/components/CnWidgetCardGrid/CnWidgetCardGrid.vue +55 -2
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +135 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +10 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +44 -1
- package/src/composables/useListView.js +21 -0
- package/src/css/board.css +65 -4
- package/src/css/dialog.css +4 -2
- package/src/css/form-field.css +14 -0
- package/src/css/form-widget.css +11 -0
- package/src/css/index-page.css +49 -0
- package/src/css/kpi-card.css +13 -0
- package/src/css/look-board-reports.css +58 -0
- package/src/css/look-board.css +108 -7
- package/src/dialogs/CnTransitionInputDialog.vue +87 -7
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +137 -6
- package/src/store/useObjectStore.js +16 -2
- package/src/utils/boardColumns.js +8 -1
- package/src/utils/boardReferenceColumns.js +274 -0
- package/src/utils/buildManifest.js +40 -3
- package/src/utils/detailWidgetVisibility.js +145 -0
- package/src/utils/formatMetric.js +38 -2
- package/src/utils/gridStack.js +39 -0
- package/src/utils/humaniseKey.js +38 -0
- package/src/utils/notificationRuleLabel.js +5 -23
- package/src/utils/resolveManifestTokens.js +1 -0
- package/src/utils/sentinelTokens.js +9 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/widgetDispatch.js +8 -2
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { prefixUrl, buildQueryString, buildHeaders } from './headers.js';
|
|
2
|
+
import { readPath } from './readPath.js';
|
|
3
|
+
import { schemaRefSlug } from './schemaRefSlug.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
|
|
7
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
8
|
+
*
|
|
9
|
+
* Board columns from a REFERENCED field (screens-board-reference-columns).
|
|
10
|
+
*
|
|
11
|
+
* A status that is an enum or a lifecycle list carries its stages on the
|
|
12
|
+
* field, and `boardColumns.js` reads them there. A status that is a reference
|
|
13
|
+
* to another object (a case's `status` pointing at a statusType row) carries
|
|
14
|
+
* nothing on the field: the stages are the rows of the referenced schema. This
|
|
15
|
+
* module turns those rows into the same `states` list `stagesOf()` reads, plus
|
|
16
|
+
* the map from a row's raw value (the referenced id) to its column.
|
|
17
|
+
*
|
|
18
|
+
* 🔴 THE COLUMNS ARE STILL THE SCHEMA'S, NOT THE DATA'S. They are the
|
|
19
|
+
* referenced rows, fetched, not the values present on the loaded cards, so an
|
|
20
|
+
* empty stage is still a column to drop a card into. The one exception is a
|
|
21
|
+
* field with no referenced schema at all (a Nextcloud user id): there the
|
|
22
|
+
* values present are all there is, and a "nobody" column is always added so
|
|
23
|
+
* work without an owner stays visible and can be dropped back to nobody.
|
|
24
|
+
*
|
|
25
|
+
* 🔴 MERGED COLUMNS RESOLVE PER CARD. Referenced rows are often per parent
|
|
26
|
+
* (every case type has its own "In behandeling"). Merged by name they make one
|
|
27
|
+
* column, and a drop must then write the row of THAT card's parent. A column
|
|
28
|
+
* with no row for the card's parent is not a place the card can go, and the
|
|
29
|
+
* drop is refused rather than written to some other parent's row.
|
|
30
|
+
*
|
|
31
|
+
* Pure apart from `fetchReferenceObjects`: no store, no Vue.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/** The column of cards with no value, for a `values` column field. */
|
|
36
|
+
const NO_VALUE_KEY = '__none__';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The id a reference value carries: a string as is, an object by its `id`,
|
|
40
|
+
* `uuid` or `@self.id`.
|
|
41
|
+
*
|
|
42
|
+
* @param {unknown} value A reference as a row holds it.
|
|
43
|
+
* @return {string} The id, '' when there is none.
|
|
44
|
+
*/
|
|
45
|
+
function refIdOf(value) {
|
|
46
|
+
if (value === null || value === undefined) {
|
|
47
|
+
return ''
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === 'object') {
|
|
50
|
+
const id = value.id ?? value.uuid ?? value['@self']?.id ?? value.value ?? '';
|
|
51
|
+
return id === null || id === undefined ? '' : String(id)
|
|
52
|
+
}
|
|
53
|
+
return String(value)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A value as text: a string or number as is, an object by its name.
|
|
58
|
+
*
|
|
59
|
+
* @param {unknown} value The raw value.
|
|
60
|
+
* @return {string} The text.
|
|
61
|
+
*/
|
|
62
|
+
function textOf(value) {
|
|
63
|
+
if (value === null || value === undefined) {
|
|
64
|
+
return ''
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === 'object') {
|
|
67
|
+
return String(value.displayName ?? value.name ?? value.title ?? value.label ?? '').trim()
|
|
68
|
+
}
|
|
69
|
+
return String(value).trim()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The column fields a board offers, normalised. Without `columnFields` the
|
|
74
|
+
* board has one, its `statusField`, read from the schema as before.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} board The board configuration (manifest `config.board`).
|
|
77
|
+
* @return {Array<{field: string, label: string, source: (object|null), kind: string, readOnly: boolean, moveHandler: string, moveTargetsHandler: string}>} The fields.
|
|
78
|
+
* @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-a-board-can-switch-the-field-its-columns-come-from
|
|
79
|
+
*/
|
|
80
|
+
function columnFieldsOf(board) {
|
|
81
|
+
const declared = Array.isArray(board?.columnFields) ? board.columnFields : [];
|
|
82
|
+
return declared
|
|
83
|
+
.map((entry) => (typeof entry === 'string' ? { field: entry } : entry))
|
|
84
|
+
.filter((entry) => entry && typeof entry.field === 'string' && entry.field !== '')
|
|
85
|
+
.map((entry) => ({
|
|
86
|
+
field: entry.field,
|
|
87
|
+
label: typeof entry.label === 'string' && entry.label !== '' ? entry.label : entry.field,
|
|
88
|
+
source: entry.source && typeof entry.source === 'object' ? entry.source : null,
|
|
89
|
+
kind: entry.kind === 'values' ? 'values' : (entry.source ? 'reference' : 'schema'),
|
|
90
|
+
readOnly: entry.readOnly === true,
|
|
91
|
+
moveHandler: typeof entry.moveHandler === 'string' ? entry.moveHandler : '',
|
|
92
|
+
moveTargetsHandler: typeof entry.moveTargetsHandler === 'string' ? entry.moveTargetsHandler : '',
|
|
93
|
+
valueLabels: entry.valueLabels && typeof entry.valueLabels === 'object' ? entry.valueLabels : null,
|
|
94
|
+
emptyLabel: typeof entry.emptyLabel === 'string' ? entry.emptyLabel : '',
|
|
95
|
+
}))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The register and schema a reference column reads its rows from: the
|
|
100
|
+
* source's own, else the page's register and the property's `$ref`.
|
|
101
|
+
*
|
|
102
|
+
* @param {object} source The column field's `source`.
|
|
103
|
+
* @param {object} options The context.
|
|
104
|
+
* @param {string} options.register The page's register.
|
|
105
|
+
* @param {object} [options.property] The field's schema property.
|
|
106
|
+
* @return {{register: string, schema: string}} Both, '' when unknown.
|
|
107
|
+
*/
|
|
108
|
+
function referenceTargetOf(source, { register = '', property = null } = {}) {
|
|
109
|
+
const schema = source?.schema || schemaRefSlug(property?.$ref ?? property?.items?.$ref ?? '');
|
|
110
|
+
return {
|
|
111
|
+
register: String(source?.register || register || ''),
|
|
112
|
+
schema: schema === '' || schema === null || schema === undefined ? '' : String(schema),
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The stages of a referenced field: the referenced rows, narrowed to one
|
|
118
|
+
* parent when asked, ordered, and merged by a field when asked.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} options The call.
|
|
121
|
+
* @param {Array<object>} options.refs The referenced rows.
|
|
122
|
+
* @param {string} [options.labelField] The row field that names a column.
|
|
123
|
+
* @param {string} [options.orderField] The numeric row field the columns are ordered by.
|
|
124
|
+
* @param {string} [options.colorField] The row field that holds the dot colour.
|
|
125
|
+
* @param {string} [options.mergeBy] The row field whose equal values make one column.
|
|
126
|
+
* @param {{field: string, rowField: string}|null} [options.narrowBy] The parent link.
|
|
127
|
+
* @param {string} [options.narrowValue] The parent the board is narrowed to, '' for all.
|
|
128
|
+
* @return {{states: Array<{key: string, label: string, color: string}>, keyByValue: Map<string, string>, refsByKey: Map<string, Array<object>>}} The stages.
|
|
129
|
+
* @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-columns-can-be-the-rows-a-field-references
|
|
130
|
+
*/
|
|
131
|
+
function buildReferenceStages({
|
|
132
|
+
refs = [],
|
|
133
|
+
labelField = '',
|
|
134
|
+
orderField = '',
|
|
135
|
+
colorField = '',
|
|
136
|
+
mergeBy = '',
|
|
137
|
+
narrowBy = null,
|
|
138
|
+
narrowValue = '',
|
|
139
|
+
} = {}) {
|
|
140
|
+
const list = (Array.isArray(refs) ? refs : []).filter((ref) => ref && typeof ref === 'object' && refIdOf(ref) !== '');
|
|
141
|
+
const narrowed = narrowBy && narrowBy.field && narrowValue !== ''
|
|
142
|
+
? list.filter((ref) => refIdOf(readPath(ref, narrowBy.field)) === String(narrowValue))
|
|
143
|
+
: list;
|
|
144
|
+
|
|
145
|
+
const orderOf = (ref) => {
|
|
146
|
+
const n = Number(orderField ? readPath(ref, orderField) : NaN);
|
|
147
|
+
return Number.isFinite(n) ? n : Number.MAX_SAFE_INTEGER
|
|
148
|
+
};
|
|
149
|
+
const ordered = narrowed
|
|
150
|
+
.map((ref, index) => ({ ref, index }))
|
|
151
|
+
.sort((a, b) => (orderOf(a.ref) - orderOf(b.ref)) || (a.index - b.index))
|
|
152
|
+
.map((entry) => entry.ref);
|
|
153
|
+
|
|
154
|
+
const states = [];
|
|
155
|
+
const keyByValue = new Map();
|
|
156
|
+
const refsByKey = new Map();
|
|
157
|
+
for (const ref of ordered) {
|
|
158
|
+
const id = refIdOf(ref);
|
|
159
|
+
const label = textOf(labelField ? readPath(ref, labelField) : (ref.name ?? ref.title)) || id;
|
|
160
|
+
const key = mergeBy ? (textOf(readPath(ref, mergeBy)) || id) : id;
|
|
161
|
+
keyByValue.set(id, key);
|
|
162
|
+
if (!refsByKey.has(key)) {
|
|
163
|
+
refsByKey.set(key, []);
|
|
164
|
+
const color = colorField ? readPath(ref, colorField) : ref.color;
|
|
165
|
+
states.push({ key, label, color: typeof color === 'string' ? color : '' });
|
|
166
|
+
}
|
|
167
|
+
refsByKey.get(key).push(ref);
|
|
168
|
+
}
|
|
169
|
+
return { states, keyByValue, refsByKey }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The stages of a field with no referenced schema (a user id): the values
|
|
174
|
+
* present on the rows, by label, then one column for no value.
|
|
175
|
+
*
|
|
176
|
+
* @param {object} options The call.
|
|
177
|
+
* @param {Array<object>} options.rows The rows the list holds.
|
|
178
|
+
* @param {string} options.field The row field.
|
|
179
|
+
* @param {object} [options.valueLabels] Raw value to the label to show.
|
|
180
|
+
* @param {string} [options.emptyLabel] The no-value column's label.
|
|
181
|
+
* @return {{states: Array<{key: string, label: string, color: string}>, keyByValue: Map<string, string>, refsByKey: Map<string, Array<object>>}} The stages.
|
|
182
|
+
* @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-a-field-without-a-referenced-schema-takes-its-values
|
|
183
|
+
*/
|
|
184
|
+
function buildValueStages({ rows = [], field = '', valueLabels = null, emptyLabel = 'Nobody' } = {}) {
|
|
185
|
+
const labels = valueLabels && typeof valueLabels === 'object' ? valueLabels : {};
|
|
186
|
+
const seen = new Map();
|
|
187
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
188
|
+
const raw = readPath(row, field);
|
|
189
|
+
const id = refIdOf(raw);
|
|
190
|
+
if (id !== '' && !seen.has(id)) {
|
|
191
|
+
seen.set(id, typeof labels[id] === 'string' ? labels[id] : (textOf(raw) || id));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const states = [...seen.entries()]
|
|
195
|
+
.sort((a, b) => a[1].localeCompare(b[1]))
|
|
196
|
+
.map(([key, label]) => ({ key, label, color: '' }));
|
|
197
|
+
states.push({ key: NO_VALUE_KEY, label: emptyLabel, color: '' });
|
|
198
|
+
const keyByValue = new Map(states.map((state) => [state.key, state.key]));
|
|
199
|
+
keyByValue.set('', NO_VALUE_KEY);
|
|
200
|
+
return { states, keyByValue, refsByKey: new Map() }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* The column a row sits in, under a value-to-column map.
|
|
205
|
+
*
|
|
206
|
+
* @param {object} row The row.
|
|
207
|
+
* @param {string} field The row field.
|
|
208
|
+
* @param {Map<string, string>} keyByValue The value-to-column map.
|
|
209
|
+
* @return {string} The column key, or the raw value when the map has none
|
|
210
|
+
* (which lands the card in the off-board column).
|
|
211
|
+
*/
|
|
212
|
+
function columnKeyOfRow(row, field, keyByValue) {
|
|
213
|
+
const id = refIdOf(readPath(row, field));
|
|
214
|
+
return keyByValue.has(id) ? keyByValue.get(id) : id
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* What a drop on a column writes for one card: the referenced row of that
|
|
219
|
+
* column which belongs to the card's parent, or null when the column has none
|
|
220
|
+
* (the card cannot go there).
|
|
221
|
+
*
|
|
222
|
+
* @param {object} options The call.
|
|
223
|
+
* @param {object} options.card The card.
|
|
224
|
+
* @param {string} options.toKey The column it was dropped on.
|
|
225
|
+
* @param {string} options.kind `reference` or `values`.
|
|
226
|
+
* @param {Map<string, Array<object>>} [options.refsByKey] The column's rows.
|
|
227
|
+
* @param {{field: string, rowField: string}|null} [options.narrowBy] The parent link.
|
|
228
|
+
* @return {{value: (string|null), target: (object|null)}|null} The value to write and its row, or null.
|
|
229
|
+
* @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-a-drop-writes-the-reference
|
|
230
|
+
*/
|
|
231
|
+
function resolveDropValue({ card, toKey, kind, refsByKey = new Map(), narrowBy = null } = {}) {
|
|
232
|
+
if (kind === 'values') {
|
|
233
|
+
return { value: toKey === NO_VALUE_KEY ? null : String(toKey), target: null }
|
|
234
|
+
}
|
|
235
|
+
const candidates = refsByKey.get(String(toKey)) || [];
|
|
236
|
+
if (candidates.length === 0) {
|
|
237
|
+
return null
|
|
238
|
+
}
|
|
239
|
+
if (narrowBy && narrowBy.field && narrowBy.rowField) {
|
|
240
|
+
const parent = refIdOf(readPath(card, narrowBy.rowField));
|
|
241
|
+
const match = candidates.find((ref) => refIdOf(readPath(ref, narrowBy.field)) === parent);
|
|
242
|
+
return match ? { value: refIdOf(match), target: match } : null
|
|
243
|
+
}
|
|
244
|
+
return { value: refIdOf(candidates[0]), target: candidates[0] }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Fetch the rows a reference column is built from, one list request.
|
|
249
|
+
*
|
|
250
|
+
* @param {object} options The call.
|
|
251
|
+
* @param {string} options.register The register slug.
|
|
252
|
+
* @param {string} options.schema The schema slug.
|
|
253
|
+
* @param {object} [options.filter] Equality filters, e.g. `{ isFinal: false }`.
|
|
254
|
+
* @param {number} [options.limit] The page size, 500 by default.
|
|
255
|
+
* @return {Promise<Array<object>>} The rows.
|
|
256
|
+
* @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-columns-can-be-the-rows-a-field-references
|
|
257
|
+
*/
|
|
258
|
+
async function fetchReferenceObjects({ register, schema, filter = {}, limit = 500 } = {}) {
|
|
259
|
+
if (!register || !schema) {
|
|
260
|
+
return []
|
|
261
|
+
}
|
|
262
|
+
const params = { _limit: limit };
|
|
263
|
+
for (const [key, value] of Object.entries(filter || {})) {
|
|
264
|
+
if (value !== undefined && value !== null && typeof value !== 'object') {
|
|
265
|
+
params[key] = typeof value === 'boolean' ? String(value) : value;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const url = prefixUrl(`/apps/openregister/api/objects/${encodeURIComponent(register)}/${encodeURIComponent(schema)}${buildQueryString(params)}`);
|
|
269
|
+
const response = await fetch(url, { headers: buildHeaders() });
|
|
270
|
+
if (!response.ok) {
|
|
271
|
+
throw new Error(`list returned ${response.status}`)
|
|
272
|
+
}
|
|
273
|
+
const data = await response.json();
|
|
274
|
+
return Array.isArray(data?.results) ? data.results : (Array.isArray(data) ? data : [])
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export { NO_VALUE_KEY, buildReferenceStages, buildValueStages, columnFieldsOf, columnKeyOfRow, fetchReferenceObjects, refIdOf, referenceTargetOf, resolveDropValue };
|
|
278
|
+
//# sourceMappingURL=boardReferenceColumns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boardReferenceColumns.js","sources":["../../../src/utils/boardReferenceColumns.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Board columns from a REFERENCED field (screens-board-reference-columns).\n *\n * A status that is an enum or a lifecycle list carries its stages on the\n * field, and `boardColumns.js` reads them there. A status that is a reference\n * to another object (a case's `status` pointing at a statusType row) carries\n * nothing on the field: the stages are the rows of the referenced schema. This\n * module turns those rows into the same `states` list `stagesOf()` reads, plus\n * the map from a row's raw value (the referenced id) to its column.\n *\n * 🔴 THE COLUMNS ARE STILL THE SCHEMA'S, NOT THE DATA'S. They are the\n * referenced rows, fetched, not the values present on the loaded cards, so an\n * empty stage is still a column to drop a card into. The one exception is a\n * field with no referenced schema at all (a Nextcloud user id): there the\n * values present are all there is, and a \"nobody\" column is always added so\n * work without an owner stays visible and can be dropped back to nobody.\n *\n * 🔴 MERGED COLUMNS RESOLVE PER CARD. Referenced rows are often per parent\n * (every case type has its own \"In behandeling\"). Merged by name they make one\n * column, and a drop must then write the row of THAT card's parent. A column\n * with no row for the card's parent is not a place the card can go, and the\n * drop is refused rather than written to some other parent's row.\n *\n * Pure apart from `fetchReferenceObjects`: no store, no Vue.\n */\n\nimport { buildHeaders, buildQueryString, prefixUrl } from './headers.js'\nimport { readPath } from './readPath.js'\nimport { schemaRefSlug } from './schemaRefSlug.js'\n\n/** The column of cards with no value, for a `values` column field. */\nexport const NO_VALUE_KEY = '__none__'\n\n/**\n * The id a reference value carries: a string as is, an object by its `id`,\n * `uuid` or `@self.id`.\n *\n * @param {unknown} value A reference as a row holds it.\n * @return {string} The id, '' when there is none.\n */\nexport function refIdOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\tconst id = value.id ?? value.uuid ?? value['@self']?.id ?? value.value ?? ''\n\t\treturn id === null || id === undefined ? '' : String(id)\n\t}\n\treturn String(value)\n}\n\n/**\n * A value as text: a string or number as is, an object by its name.\n *\n * @param {unknown} value The raw value.\n * @return {string} The text.\n */\nfunction textOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.displayName ?? value.name ?? value.title ?? value.label ?? '').trim()\n\t}\n\treturn String(value).trim()\n}\n\n/**\n * The column fields a board offers, normalised. Without `columnFields` the\n * board has one, its `statusField`, read from the schema as before.\n *\n * @param {object} board The board configuration (manifest `config.board`).\n * @return {Array<{field: string, label: string, source: (object|null), kind: string, readOnly: boolean, moveHandler: string, moveTargetsHandler: string}>} The fields.\n * @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-a-board-can-switch-the-field-its-columns-come-from\n */\nexport function columnFieldsOf(board) {\n\tconst declared = Array.isArray(board?.columnFields) ? board.columnFields : []\n\treturn declared\n\t\t.map((entry) => (typeof entry === 'string' ? { field: entry } : entry))\n\t\t.filter((entry) => entry && typeof entry.field === 'string' && entry.field !== '')\n\t\t.map((entry) => ({\n\t\t\tfield: entry.field,\n\t\t\tlabel: typeof entry.label === 'string' && entry.label !== '' ? entry.label : entry.field,\n\t\t\tsource: entry.source && typeof entry.source === 'object' ? entry.source : null,\n\t\t\tkind: entry.kind === 'values' ? 'values' : (entry.source ? 'reference' : 'schema'),\n\t\t\treadOnly: entry.readOnly === true,\n\t\t\tmoveHandler: typeof entry.moveHandler === 'string' ? entry.moveHandler : '',\n\t\t\tmoveTargetsHandler: typeof entry.moveTargetsHandler === 'string' ? entry.moveTargetsHandler : '',\n\t\t\tvalueLabels: entry.valueLabels && typeof entry.valueLabels === 'object' ? entry.valueLabels : null,\n\t\t\temptyLabel: typeof entry.emptyLabel === 'string' ? entry.emptyLabel : '',\n\t\t}))\n}\n\n/**\n * The register and schema a reference column reads its rows from: the\n * source's own, else the page's register and the property's `$ref`.\n *\n * @param {object} source The column field's `source`.\n * @param {object} options The context.\n * @param {string} options.register The page's register.\n * @param {object} [options.property] The field's schema property.\n * @return {{register: string, schema: string}} Both, '' when unknown.\n */\nexport function referenceTargetOf(source, { register = '', property = null } = {}) {\n\tconst schema = source?.schema || schemaRefSlug(property?.$ref ?? property?.items?.$ref ?? '')\n\treturn {\n\t\tregister: String(source?.register || register || ''),\n\t\tschema: schema === '' || schema === null || schema === undefined ? '' : String(schema),\n\t}\n}\n\n/**\n * The stages of a referenced field: the referenced rows, narrowed to one\n * parent when asked, ordered, and merged by a field when asked.\n *\n * @param {object} options The call.\n * @param {Array<object>} options.refs The referenced rows.\n * @param {string} [options.labelField] The row field that names a column.\n * @param {string} [options.orderField] The numeric row field the columns are ordered by.\n * @param {string} [options.colorField] The row field that holds the dot colour.\n * @param {string} [options.mergeBy] The row field whose equal values make one column.\n * @param {{field: string, rowField: string}|null} [options.narrowBy] The parent link.\n * @param {string} [options.narrowValue] The parent the board is narrowed to, '' for all.\n * @return {{states: Array<{key: string, label: string, color: string}>, keyByValue: Map<string, string>, refsByKey: Map<string, Array<object>>}} The stages.\n * @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-columns-can-be-the-rows-a-field-references\n */\nexport function buildReferenceStages({\n\trefs = [],\n\tlabelField = '',\n\torderField = '',\n\tcolorField = '',\n\tmergeBy = '',\n\tnarrowBy = null,\n\tnarrowValue = '',\n} = {}) {\n\tconst list = (Array.isArray(refs) ? refs : []).filter((ref) => ref && typeof ref === 'object' && refIdOf(ref) !== '')\n\tconst narrowed = narrowBy && narrowBy.field && narrowValue !== ''\n\t\t? list.filter((ref) => refIdOf(readPath(ref, narrowBy.field)) === String(narrowValue))\n\t\t: list\n\n\tconst orderOf = (ref) => {\n\t\tconst n = Number(orderField ? readPath(ref, orderField) : NaN)\n\t\treturn Number.isFinite(n) ? n : Number.MAX_SAFE_INTEGER\n\t}\n\tconst ordered = narrowed\n\t\t.map((ref, index) => ({ ref, index }))\n\t\t.sort((a, b) => (orderOf(a.ref) - orderOf(b.ref)) || (a.index - b.index))\n\t\t.map((entry) => entry.ref)\n\n\tconst states = []\n\tconst keyByValue = new Map()\n\tconst refsByKey = new Map()\n\tfor (const ref of ordered) {\n\t\tconst id = refIdOf(ref)\n\t\tconst label = textOf(labelField ? readPath(ref, labelField) : (ref.name ?? ref.title)) || id\n\t\tconst key = mergeBy ? (textOf(readPath(ref, mergeBy)) || id) : id\n\t\tkeyByValue.set(id, key)\n\t\tif (!refsByKey.has(key)) {\n\t\t\trefsByKey.set(key, [])\n\t\t\tconst color = colorField ? readPath(ref, colorField) : ref.color\n\t\t\tstates.push({ key, label, color: typeof color === 'string' ? color : '' })\n\t\t}\n\t\trefsByKey.get(key).push(ref)\n\t}\n\treturn { states, keyByValue, refsByKey }\n}\n\n/**\n * The stages of a field with no referenced schema (a user id): the values\n * present on the rows, by label, then one column for no value.\n *\n * @param {object} options The call.\n * @param {Array<object>} options.rows The rows the list holds.\n * @param {string} options.field The row field.\n * @param {object} [options.valueLabels] Raw value to the label to show.\n * @param {string} [options.emptyLabel] The no-value column's label.\n * @return {{states: Array<{key: string, label: string, color: string}>, keyByValue: Map<string, string>, refsByKey: Map<string, Array<object>>}} The stages.\n * @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-a-field-without-a-referenced-schema-takes-its-values\n */\nexport function buildValueStages({ rows = [], field = '', valueLabels = null, emptyLabel = 'Nobody' } = {}) {\n\tconst labels = valueLabels && typeof valueLabels === 'object' ? valueLabels : {}\n\tconst seen = new Map()\n\tfor (const row of Array.isArray(rows) ? rows : []) {\n\t\tconst raw = readPath(row, field)\n\t\tconst id = refIdOf(raw)\n\t\tif (id !== '' && !seen.has(id)) {\n\t\t\tseen.set(id, typeof labels[id] === 'string' ? labels[id] : (textOf(raw) || id))\n\t\t}\n\t}\n\tconst states = [...seen.entries()]\n\t\t.sort((a, b) => a[1].localeCompare(b[1]))\n\t\t.map(([key, label]) => ({ key, label, color: '' }))\n\tstates.push({ key: NO_VALUE_KEY, label: emptyLabel, color: '' })\n\tconst keyByValue = new Map(states.map((state) => [state.key, state.key]))\n\tkeyByValue.set('', NO_VALUE_KEY)\n\treturn { states, keyByValue, refsByKey: new Map() }\n}\n\n/**\n * The column a row sits in, under a value-to-column map.\n *\n * @param {object} row The row.\n * @param {string} field The row field.\n * @param {Map<string, string>} keyByValue The value-to-column map.\n * @return {string} The column key, or the raw value when the map has none\n * (which lands the card in the off-board column).\n */\nexport function columnKeyOfRow(row, field, keyByValue) {\n\tconst id = refIdOf(readPath(row, field))\n\treturn keyByValue.has(id) ? keyByValue.get(id) : id\n}\n\n/**\n * What a drop on a column writes for one card: the referenced row of that\n * column which belongs to the card's parent, or null when the column has none\n * (the card cannot go there).\n *\n * @param {object} options The call.\n * @param {object} options.card The card.\n * @param {string} options.toKey The column it was dropped on.\n * @param {string} options.kind `reference` or `values`.\n * @param {Map<string, Array<object>>} [options.refsByKey] The column's rows.\n * @param {{field: string, rowField: string}|null} [options.narrowBy] The parent link.\n * @return {{value: (string|null), target: (object|null)}|null} The value to write and its row, or null.\n * @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-a-drop-writes-the-reference\n */\nexport function resolveDropValue({ card, toKey, kind, refsByKey = new Map(), narrowBy = null } = {}) {\n\tif (kind === 'values') {\n\t\treturn { value: toKey === NO_VALUE_KEY ? null : String(toKey), target: null }\n\t}\n\tconst candidates = refsByKey.get(String(toKey)) || []\n\tif (candidates.length === 0) {\n\t\treturn null\n\t}\n\tif (narrowBy && narrowBy.field && narrowBy.rowField) {\n\t\tconst parent = refIdOf(readPath(card, narrowBy.rowField))\n\t\tconst match = candidates.find((ref) => refIdOf(readPath(ref, narrowBy.field)) === parent)\n\t\treturn match ? { value: refIdOf(match), target: match } : null\n\t}\n\treturn { value: refIdOf(candidates[0]), target: candidates[0] }\n}\n\n/**\n * Fetch the rows a reference column is built from, one list request.\n *\n * @param {object} options The call.\n * @param {string} options.register The register slug.\n * @param {string} options.schema The schema slug.\n * @param {object} [options.filter] Equality filters, e.g. `{ isFinal: false }`.\n * @param {number} [options.limit] The page size, 500 by default.\n * @return {Promise<Array<object>>} The rows.\n * @spec openspec/changes/screens-board-reference-columns/specs/board-view/spec.md#requirement-columns-can-be-the-rows-a-field-references\n */\nexport async function fetchReferenceObjects({ register, schema, filter = {}, limit = 500 } = {}) {\n\tif (!register || !schema) {\n\t\treturn []\n\t}\n\tconst params = { _limit: limit }\n\tfor (const [key, value] of Object.entries(filter || {})) {\n\t\tif (value !== undefined && value !== null && typeof value !== 'object') {\n\t\t\tparams[key] = typeof value === 'boolean' ? String(value) : value\n\t\t}\n\t}\n\tconst url = prefixUrl(`/apps/openregister/api/objects/${encodeURIComponent(register)}/${encodeURIComponent(schema)}${buildQueryString(params)}`)\n\tconst response = await fetch(url, { headers: buildHeaders() })\n\tif (!response.ok) {\n\t\tthrow new Error(`list returned ${response.status}`)\n\t}\n\tconst data = await response.json()\n\treturn Array.isArray(data?.results) ? data.results : (Array.isArray(data) ? data : [])\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -138,7 +138,10 @@ function mergeMenuItems(target, incoming) {
|
|
|
138
138
|
target.push(copy);
|
|
139
139
|
return
|
|
140
140
|
}
|
|
141
|
-
|
|
141
|
+
// `query` and `params` belong to `route`: an entry that takes its route
|
|
142
|
+
// from here without them opens the list unfiltered (a "board" entry to
|
|
143
|
+
// `Cases` with `{ _view: 'board' }` opened the table).
|
|
144
|
+
for (const key of ['label', 'icon', 'route', 'query', 'params', 'order', 'section', 'featureFlag', 'permission', 'visibleIf', 'href', 'action']) {
|
|
142
145
|
if (existing[key] === undefined && item[key] !== undefined) {
|
|
143
146
|
existing[key] = item[key];
|
|
144
147
|
}
|
|
@@ -196,6 +199,7 @@ function applyMenuRelocations(menu, relocations) {
|
|
|
196
199
|
if (!relocations || typeof relocations !== 'object') {
|
|
197
200
|
return menu
|
|
198
201
|
}
|
|
202
|
+
const heldEntries = captionsHoldingEntries(menu);
|
|
199
203
|
for (let pass = 0; pass < 5; pass++) {
|
|
200
204
|
const moves = [];
|
|
201
205
|
for (let i = menu.length - 1; i >= 0; i--) {
|
|
@@ -241,9 +245,42 @@ function applyMenuRelocations(menu, relocations) {
|
|
|
241
245
|
}
|
|
242
246
|
});
|
|
243
247
|
}
|
|
244
|
-
// Drop empty group shells left behind by relocations.
|
|
245
|
-
|
|
246
|
-
|
|
248
|
+
// Drop empty group shells left behind by relocations. A caption (a
|
|
249
|
+
// section heading) has no route and no children and is kept, unless the
|
|
250
|
+
// relocations emptied it: every entry under it moved away.
|
|
251
|
+
const kept = menu.filter((m) => m.type === 'caption' || m.route || m.href || m.action
|
|
252
|
+
|| (Array.isArray(m.children) && m.children.length > 0));
|
|
253
|
+
const holdingNow = captionsHoldingEntries(kept);
|
|
254
|
+
return kept.filter((m) => m.type !== 'caption' || !heldEntries.has(m) || holdingNow.has(m))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The captions (`type: "caption"`) that have at least one entry under them:
|
|
259
|
+
* a non-caption entry of the same section before the next caption.
|
|
260
|
+
*
|
|
261
|
+
* @param {Array<object>} menu The top-level menu.
|
|
262
|
+
* @return {Set<object>} The captions holding an entry.
|
|
263
|
+
*/
|
|
264
|
+
function captionsHoldingEntries(menu) {
|
|
265
|
+
const holding = new Set();
|
|
266
|
+
menu.forEach((entry, index) => {
|
|
267
|
+
if (entry.type !== 'caption') {
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
const section = entry.section ?? 'main';
|
|
271
|
+
for (let i = index + 1; i < menu.length; i++) {
|
|
272
|
+
const next = menu[i];
|
|
273
|
+
if ((next.section ?? 'main') !== section) {
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
if (next.type === 'caption') {
|
|
277
|
+
break
|
|
278
|
+
}
|
|
279
|
+
holding.add(entry);
|
|
280
|
+
break
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
return holding
|
|
247
284
|
}
|
|
248
285
|
|
|
249
286
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildManifest.js","sources":["../../../src/utils/buildManifest.js"],"sourcesContent":["/**\n * Build an app's effective manifest from its bundled base, its modular\n * `manifest.d/*.json` fragments (ADR-037), and its `menu-layout.json`.\n *\n * Every manifest-v2 app ran an identical ~200-line copy of this pipeline\n * inline in its `src/main.js`. Lifting it here makes the navigation contract\n * a single shared implementation: fragment merging, canonical relocations,\n * duplicate-entry removals, promotion of config entries into Nextcloud's\n * settings foldout (`section: \"settings\"`), and relocation of cross-app links\n * out of the navigation into the per-user settings modal's Integrations\n * section (`section: \"integrations\"`, ADR-110) all behave the same across the\n * fleet, and a new app adopts the whole IA model by passing its three inputs.\n *\n * The only app-local piece is collecting the fragments: `require.context` is\n * resolved by each app's own webpack build, so the caller gathers the fragment\n * objects and passes them in:\n *\n * const ctx = require.context('./manifest.d/', false, /\\.json$/)\n * const fragments = ctx.keys().sort().map((k) => ctx(k))\n * const manifest = buildManifest(bundledManifest, fragments, menuLayout)\n *\n * If the base (or a fragment) declares `pageTemplates[]` + `pageInstances[]`\n * (manifest-entity-scaffold-templating), the entity-scaffold expander runs as\n * the final step: each instantiation is materialised into a concrete page and\n * appended to `pages[]`, so the runtime renderer only ever sees concrete pages.\n * A manifest without templating is passed through unchanged (no-op fast path).\n *\n * @param {object} base The bundled base manifest (`src/manifest.json`).\n * @param {Array<object>} [fragments] Fragment objects (each may carry `pages`/`menu`).\n * @param {object} [menuLayout] `{ relocations?, removals?, settingsSection?, integrationsSection? }`.\n * @return {object} The merged manifest: `{ ...base, pages, menu }`.\n */\n\nimport { expandPageTemplates } from './expandPageTemplates.js'\n\nexport function buildManifest(base, fragments = [], menuLayout = {}) {\n\tconst merged = { ...base, pages: [...(base.pages || [])], menu: [] }\n\tmergeMenuItems(merged.menu, base.menu || [])\n\t// Fragments may also carry page-template instantiations/templates/sets;\n\t// collect them so a fragment-authored entity scaffold expands too.\n\tconst fragTemplates = []\n\tconst fragInstances = []\n\tfor (const frag of (Array.isArray(fragments) ? fragments : [])) {\n\t\tif (frag && Array.isArray(frag.pages)) {\n\t\t\tmergePages(merged.pages, frag.pages)\n\t\t}\n\t\tif (frag && Array.isArray(frag.menu)) {\n\t\t\tmergeMenuItems(merged.menu, frag.menu)\n\t\t}\n\t\tif (frag && Array.isArray(frag.pageTemplates)) {\n\t\t\tfragTemplates.push(...frag.pageTemplates)\n\t\t}\n\t\tif (frag && Array.isArray(frag.pageInstances)) {\n\t\t\tfragInstances.push(...frag.pageInstances)\n\t\t}\n\t\tif (frag && frag.sets && typeof frag.sets === 'object') {\n\t\t\tmerged.sets = { ...(merged.sets || {}), ...frag.sets }\n\t\t}\n\t}\n\tif (fragTemplates.length) {\n\t\tmerged.pageTemplates = [...(merged.pageTemplates || []), ...fragTemplates]\n\t}\n\tif (fragInstances.length) {\n\t\tmerged.pageInstances = [...(merged.pageInstances || []), ...fragInstances]\n\t}\n\tmerged.menu = applyMenuLayout(merged.menu, menuLayout)\n\n\t// Entity-scaffold expansion (runtime/boot path). No-op unless the manifest\n\t// declares pageTemplates + pageInstances. Runtime fallback semantics: a bad\n\t// instantiation is skipped + warned rather than blanking the whole app.\n\tif (Array.isArray(merged.pageTemplates) || Array.isArray(merged.pageInstances)) {\n\t\tconst result = expandPageTemplates(merged, { throwOnError: false })\n\t\tif (result.errors.length) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn('[buildManifest] page-template expansion reported errors; the offending instantiations were skipped.', result.errors)\n\t\t}\n\t\treturn result.manifest\n\t}\n\treturn merged\n}\n\n/**\n * Apply the canonical navigation layout (`relocations` → `removals` →\n * `settingsSection` → `integrationsSection`) to an already-merged menu.\n * Exposed separately for apps that assemble their menu through a different\n * path but still want the shared layout semantics.\n *\n * `integrationsSection` runs LAST so it wins over `settingsSection` if an id\n * is listed in both: an integration link is not a settings page of this app,\n * and the entry must end up in exactly one place. Running it last also means\n * an id lifted into the foldout by a stale `settingsSection` entry is still\n * reachable to be re-lifted, rather than silently staying in the nav.\n *\n * @param {Array<object>} menu The merged menu (mutated in place by the steps).\n * @param {object} [menuLayout] `{ relocations?, removals?, settingsSection?, integrationsSection? }`.\n * @return {Array<object>} The laid-out menu.\n */\nexport function applyMenuLayout(menu, menuLayout = {}) {\n\tlet out = applyMenuRelocations(menu, menuLayout.relocations)\n\tout = applyMenuRemovals(out, menuLayout.removals)\n\tout = applySettingsSection(out, menuLayout.settingsSection)\n\tout = applyIntegrationsSection(out, menuLayout.integrationsSection)\n\treturn out\n}\n\n/**\n * Merge an array of incoming menu items into a target array, keyed by `id`.\n * New ids are appended; existing ids are merged in place: the first\n * definition of each listed key wins (the base manifest loads first, so its\n * canonical group definitions take precedence), and `children` are unioned\n * recursively by the same rule. Fragments may therefore extend an existing\n * group by re-declaring only its `id` plus their own `children`.\n *\n * @param {Array<object>} target The accumulated menu (mutated in place).\n * @param {Array<object>} incoming Menu items from a fragment.\n * @return {void}\n */\nexport function mergeMenuItems(target, incoming) {\n\tincoming.forEach((item) => {\n\t\tconst existing = target.find((t) => t.id === item.id)\n\t\tif (!existing) {\n\t\t\t// Copy `children` ONLY when there is an array to copy. The previous\n\t\t\t// ternary's else-branch assigned `item.children` — i.e. `undefined`\n\t\t\t// — which is not the same as leaving the key off: `Object.keys()`\n\t\t\t// and JSON-Schema's `additionalProperties: false` both see a key\n\t\t\t// that is present. Every leaf entry therefore carried a `children`\n\t\t\t// property, and the BUILT manifest failed validation against the\n\t\t\t// same schema its source passes. Invisible in practice because\n\t\t\t// `JSON.stringify` drops undefined values, so nothing that logged\n\t\t\t// or persisted the manifest ever showed it.\n\t\t\tconst copy = { ...item }\n\t\t\tif (Array.isArray(item.children)) {\n\t\t\t\tcopy.children = [...item.children]\n\t\t\t} else {\n\t\t\t\tdelete copy.children\n\t\t\t}\n\t\t\ttarget.push(copy)\n\t\t\treturn\n\t\t}\n\t\tfor (const key of ['label', 'icon', 'route', 'order', 'section', 'featureFlag', 'permission', 'visibleIf', 'href', 'action']) {\n\t\t\tif (existing[key] === undefined && item[key] !== undefined) {\n\t\t\t\texisting[key] = item[key]\n\t\t\t}\n\t\t}\n\t\tif (Array.isArray(item.children) && item.children.length > 0) {\n\t\t\tif (!Array.isArray(existing.children)) {\n\t\t\t\texisting.children = []\n\t\t\t}\n\t\t\tmergeMenuItems(existing.children, item.children)\n\t\t}\n\t})\n}\n\n/**\n * Merge fragment pages onto the accumulated page list by `id` — a later\n * declaration REPLACES an earlier one wholesale, so a fragment can overlay a\n * base page (e.g. swap a schema-driven detail for a bespoke `type: \"custom\"`\n * component) and guarantee one route per page id.\n *\n * @param {Array<object>} target Accumulated pages (mutated in place).\n * @param {Array<object>} incoming Pages from a fragment.\n * @return {void}\n */\nexport function mergePages(target, incoming) {\n\tincoming.forEach((page) => {\n\t\tconst idx = target.findIndex((p) => p.id === page.id)\n\t\tif (idx === -1) {\n\t\t\ttarget.push(page)\n\t\t} else {\n\t\t\ttarget[idx] = page\n\t\t}\n\t})\n}\n\n/**\n * Re-home merged menu entries onto the canonical navigation layout declared\n * by `menu-layout.json#relocations` (`{ sourceId: targetGroupId }`).\n *\n * - A relocated GROUP dissolves: its children merge (by id) into the target\n * group and the now-empty shell is dropped.\n * - A relocated LEAF (top-level or child of any group) moves under the\n * target group.\n * - A child group relocated onto its own parent flattens into it.\n * - Unknown source ids are inert; a missing target keeps the entry at the\n * top level so nothing silently disappears.\n *\n * Runs in passes until stable (children freed by a dissolved group can\n * themselves be relocated on the next pass).\n *\n * @param {Array<object>} menu The merged menu (mutated in place).\n * @param {Record<string, string>|undefined} relocations Source-id → target-group-id map.\n * @return {Array<object>} The menu with relocations applied.\n */\nexport function applyMenuRelocations(menu, relocations) {\n\tif (!relocations || typeof relocations !== 'object') {\n\t\treturn menu\n\t}\n\tfor (let pass = 0; pass < 5; pass++) {\n\t\tconst moves = []\n\t\tfor (let i = menu.length - 1; i >= 0; i--) {\n\t\t\tconst node = menu[i]\n\t\t\tconst target = relocations[node.id]\n\t\t\tif (target && target !== node.id) {\n\t\t\t\tmenu.splice(i, 1)\n\t\t\t\tmoves.push({ node, target })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!Array.isArray(node.children)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor (let j = node.children.length - 1; j >= 0; j--) {\n\t\t\t\tconst child = node.children[j]\n\t\t\t\tconst childTarget = relocations[child.id]\n\t\t\t\tif (!childTarget) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (childTarget === node.id && !Array.isArray(child.children)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnode.children.splice(j, 1)\n\t\t\t\tmoves.push({ node: child, target: childTarget })\n\t\t\t}\n\t\t}\n\t\tif (moves.length === 0) {\n\t\t\tbreak\n\t\t}\n\t\tmoves.forEach(({ node, target }) => {\n\t\t\tconst group = menu.find((m) => m.id === target)\n\t\t\tif (!group) {\n\t\t\t\tmenu.push(node)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!Array.isArray(group.children)) {\n\t\t\t\tgroup.children = []\n\t\t\t}\n\t\t\tif (Array.isArray(node.children)) {\n\t\t\t\tmergeMenuItems(group.children, node.children)\n\t\t\t} else {\n\t\t\t\tmergeMenuItems(group.children, [node])\n\t\t\t}\n\t\t})\n\t}\n\t// Drop empty group shells left behind by relocations.\n\treturn menu.filter((m) => m.route || m.href || m.action\n\t\t|| (Array.isArray(m.children) && m.children.length > 0))\n}\n\n/**\n * Remove individual menu entries by id after relocation — used to retire\n * duplicate navigation entries whose PAGE must stay routable (deep links and\n * e2e specs hit the route directly). Declared in `menu-layout.json#removals`.\n * Only leaf entries are removed; a group is dropped only when it is explicitly\n * removed OR left empty after its children were removed/relocated away. A\n * group carrying a `route`/`href`/`action` is itself clickable (a direct-link\n * top-level item, e.g. a \"Reporting & Compliance\" → cards-page link) and\n * survives even when all its children are gone.\n *\n * @param {Array<object>} menu The merged menu.\n * @param {Array<string>|undefined} removals Menu-entry ids to drop.\n * @return {Array<object>} The menu without the removed entries.\n */\nexport function applyMenuRemovals(menu, removals) {\n\tif (!Array.isArray(removals) || removals.length === 0) {\n\t\treturn menu\n\t}\n\tconst drop = new Set(removals)\n\tconst wasGroup = (n) => Array.isArray(n.children) && n.children.length > 0\n\tconst isClickable = (n) => n.route !== undefined || n.href !== undefined || n.action !== undefined\n\tconst prune = (nodes) => nodes.reduce((acc, n) => {\n\t\tif (drop.has(n.id) && !wasGroup(n)) {\n\t\t\treturn acc\n\t\t}\n\t\tif (Array.isArray(n.children)) {\n\t\t\tconst children = prune(n.children)\n\t\t\tconst hadChildren = wasGroup(n)\n\t\t\tif (children.length === 0 && hadChildren && !isClickable(n)) {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\tacc.push({ ...n, children })\n\t\t\treturn acc\n\t\t}\n\t\tacc.push(n)\n\t\treturn acc\n\t}, [])\n\treturn prune(menu)\n}\n\n/**\n * Lift the menu entries listed by id out of wherever they currently sit, tag\n * them with `section`, flatten them (neither destination renders nested\n * groups), and append them at the top level. Empty non-clickable groups left\n * behind are dropped; a clickable group is kept.\n *\n * Shared by `applySettingsSection` and `applyIntegrationsSection` so the two\n * destinations cannot drift in their lift semantics — the only difference\n * between them is the section tag, and therefore which surface renders them.\n *\n * @param {Array<object>} menu The merged + relocated + pruned menu.\n * @param {Array<string>|undefined} ids Entry ids to lift.\n * @param {string} section The `section` tag to stamp on each lifted entry.\n * @return {Array<object>} The menu with the listed entries lifted out.\n */\nfunction liftToSection(menu, ids, section) {\n\tif (!Array.isArray(ids) || ids.length === 0) {\n\t\treturn menu\n\t}\n\tconst want = new Set(ids)\n\tconst isClickable = (n) => n.route !== undefined || n.href !== undefined || n.action !== undefined\n\tconst lifted = []\n\tconst strip = (nodes) => nodes.reduce((acc, n) => {\n\t\tif (want.has(n.id)) {\n\t\t\tconst { children, ...leaf } = n\n\t\t\tlifted.push({ ...leaf, section })\n\t\t\treturn acc\n\t\t}\n\t\tif (Array.isArray(n.children)) {\n\t\t\tconst children = strip(n.children)\n\t\t\tif (children.length === 0 && n.children.length > 0 && !isClickable(n)) {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\tacc.push({ ...n, children })\n\t\t\treturn acc\n\t\t}\n\t\tacc.push(n)\n\t\treturn acc\n\t}, [])\n\tconst remaining = strip(menu)\n\treturn [...remaining, ...lifted]\n}\n\n/**\n * Promote the menu entries listed in `menu-layout.json#settingsSection` into\n * Nextcloud's settings foldout — the `NcAppNavigationSettings` gear at the\n * bottom-left of the navigation, OUTSIDE the scrollable list. CnAppNav renders\n * every TOP-LEVEL item carrying `section: \"settings\"` as a flat entry inside\n * that foldout (with an auto-prepended \"Personal settings\").\n *\n * @param {Array<object>} menu The merged + relocated + pruned menu.\n * @param {Array<string>|undefined} settingsIds Entry ids to move to the foldout.\n * @return {Array<object>} The menu with the settings entries lifted out.\n */\nexport function applySettingsSection(menu, settingsIds) {\n\treturn liftToSection(menu, settingsIds, 'settings')\n}\n\n/**\n * Move the menu entries listed in `menu-layout.json#integrationsSection` OUT of\n * the navigation entirely and into the Integrations section at the bottom of\n * the per-user settings modal (ADR-110).\n *\n * This is where a link that LEAVES this app for another one belongs. Such a\n * link is not a page of this app — it cannot be active, it has no counter, and\n * putting it in the nav makes another app's capability read as this app's\n * feature. The one deep link that stays in the navigation is Admin settings,\n * and CnAppNav auto-prepends that itself (ADR-079); no app declares it.\n *\n * The entry survives — it is relocated, never dropped — which is what keeps\n * this compatible with the ADR-044 Decision 5 no-functionality-loss invariant.\n *\n * @param {Array<object>} menu The merged + relocated + pruned menu.\n * @param {Array<string>|undefined} integrationIds Entry ids to move to Integrations.\n * @return {Array<object>} The menu with the integration entries lifted out.\n */\nexport function applyIntegrationsSection(menu, integrationIds) {\n\treturn liftToSection(menu, integrationIds, 'integrations')\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"buildManifest.js","sources":["../../../src/utils/buildManifest.js"],"sourcesContent":["/**\n * Build an app's effective manifest from its bundled base, its modular\n * `manifest.d/*.json` fragments (ADR-037), and its `menu-layout.json`.\n *\n * Every manifest-v2 app ran an identical ~200-line copy of this pipeline\n * inline in its `src/main.js`. Lifting it here makes the navigation contract\n * a single shared implementation: fragment merging, canonical relocations,\n * duplicate-entry removals, promotion of config entries into Nextcloud's\n * settings foldout (`section: \"settings\"`), and relocation of cross-app links\n * out of the navigation into the per-user settings modal's Integrations\n * section (`section: \"integrations\"`, ADR-110) all behave the same across the\n * fleet, and a new app adopts the whole IA model by passing its three inputs.\n *\n * The only app-local piece is collecting the fragments: `require.context` is\n * resolved by each app's own webpack build, so the caller gathers the fragment\n * objects and passes them in:\n *\n * const ctx = require.context('./manifest.d/', false, /\\.json$/)\n * const fragments = ctx.keys().sort().map((k) => ctx(k))\n * const manifest = buildManifest(bundledManifest, fragments, menuLayout)\n *\n * If the base (or a fragment) declares `pageTemplates[]` + `pageInstances[]`\n * (manifest-entity-scaffold-templating), the entity-scaffold expander runs as\n * the final step: each instantiation is materialised into a concrete page and\n * appended to `pages[]`, so the runtime renderer only ever sees concrete pages.\n * A manifest without templating is passed through unchanged (no-op fast path).\n *\n * @param {object} base The bundled base manifest (`src/manifest.json`).\n * @param {Array<object>} [fragments] Fragment objects (each may carry `pages`/`menu`).\n * @param {object} [menuLayout] `{ relocations?, removals?, settingsSection?, integrationsSection? }`.\n * @return {object} The merged manifest: `{ ...base, pages, menu }`.\n */\n\nimport { expandPageTemplates } from './expandPageTemplates.js'\n\nexport function buildManifest(base, fragments = [], menuLayout = {}) {\n\tconst merged = { ...base, pages: [...(base.pages || [])], menu: [] }\n\tmergeMenuItems(merged.menu, base.menu || [])\n\t// Fragments may also carry page-template instantiations/templates/sets;\n\t// collect them so a fragment-authored entity scaffold expands too.\n\tconst fragTemplates = []\n\tconst fragInstances = []\n\tfor (const frag of (Array.isArray(fragments) ? fragments : [])) {\n\t\tif (frag && Array.isArray(frag.pages)) {\n\t\t\tmergePages(merged.pages, frag.pages)\n\t\t}\n\t\tif (frag && Array.isArray(frag.menu)) {\n\t\t\tmergeMenuItems(merged.menu, frag.menu)\n\t\t}\n\t\tif (frag && Array.isArray(frag.pageTemplates)) {\n\t\t\tfragTemplates.push(...frag.pageTemplates)\n\t\t}\n\t\tif (frag && Array.isArray(frag.pageInstances)) {\n\t\t\tfragInstances.push(...frag.pageInstances)\n\t\t}\n\t\tif (frag && frag.sets && typeof frag.sets === 'object') {\n\t\t\tmerged.sets = { ...(merged.sets || {}), ...frag.sets }\n\t\t}\n\t}\n\tif (fragTemplates.length) {\n\t\tmerged.pageTemplates = [...(merged.pageTemplates || []), ...fragTemplates]\n\t}\n\tif (fragInstances.length) {\n\t\tmerged.pageInstances = [...(merged.pageInstances || []), ...fragInstances]\n\t}\n\tmerged.menu = applyMenuLayout(merged.menu, menuLayout)\n\n\t// Entity-scaffold expansion (runtime/boot path). No-op unless the manifest\n\t// declares pageTemplates + pageInstances. Runtime fallback semantics: a bad\n\t// instantiation is skipped + warned rather than blanking the whole app.\n\tif (Array.isArray(merged.pageTemplates) || Array.isArray(merged.pageInstances)) {\n\t\tconst result = expandPageTemplates(merged, { throwOnError: false })\n\t\tif (result.errors.length) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn('[buildManifest] page-template expansion reported errors; the offending instantiations were skipped.', result.errors)\n\t\t}\n\t\treturn result.manifest\n\t}\n\treturn merged\n}\n\n/**\n * Apply the canonical navigation layout (`relocations` → `removals` →\n * `settingsSection` → `integrationsSection`) to an already-merged menu.\n * Exposed separately for apps that assemble their menu through a different\n * path but still want the shared layout semantics.\n *\n * `integrationsSection` runs LAST so it wins over `settingsSection` if an id\n * is listed in both: an integration link is not a settings page of this app,\n * and the entry must end up in exactly one place. Running it last also means\n * an id lifted into the foldout by a stale `settingsSection` entry is still\n * reachable to be re-lifted, rather than silently staying in the nav.\n *\n * @param {Array<object>} menu The merged menu (mutated in place by the steps).\n * @param {object} [menuLayout] `{ relocations?, removals?, settingsSection?, integrationsSection? }`.\n * @return {Array<object>} The laid-out menu.\n */\nexport function applyMenuLayout(menu, menuLayout = {}) {\n\tlet out = applyMenuRelocations(menu, menuLayout.relocations)\n\tout = applyMenuRemovals(out, menuLayout.removals)\n\tout = applySettingsSection(out, menuLayout.settingsSection)\n\tout = applyIntegrationsSection(out, menuLayout.integrationsSection)\n\treturn out\n}\n\n/**\n * Merge an array of incoming menu items into a target array, keyed by `id`.\n * New ids are appended; existing ids are merged in place: the first\n * definition of each listed key wins (the base manifest loads first, so its\n * canonical group definitions take precedence), and `children` are unioned\n * recursively by the same rule. Fragments may therefore extend an existing\n * group by re-declaring only its `id` plus their own `children`.\n *\n * @param {Array<object>} target The accumulated menu (mutated in place).\n * @param {Array<object>} incoming Menu items from a fragment.\n * @return {void}\n */\nexport function mergeMenuItems(target, incoming) {\n\tincoming.forEach((item) => {\n\t\tconst existing = target.find((t) => t.id === item.id)\n\t\tif (!existing) {\n\t\t\t// Copy `children` ONLY when there is an array to copy. The previous\n\t\t\t// ternary's else-branch assigned `item.children` — i.e. `undefined`\n\t\t\t// — which is not the same as leaving the key off: `Object.keys()`\n\t\t\t// and JSON-Schema's `additionalProperties: false` both see a key\n\t\t\t// that is present. Every leaf entry therefore carried a `children`\n\t\t\t// property, and the BUILT manifest failed validation against the\n\t\t\t// same schema its source passes. Invisible in practice because\n\t\t\t// `JSON.stringify` drops undefined values, so nothing that logged\n\t\t\t// or persisted the manifest ever showed it.\n\t\t\tconst copy = { ...item }\n\t\t\tif (Array.isArray(item.children)) {\n\t\t\t\tcopy.children = [...item.children]\n\t\t\t} else {\n\t\t\t\tdelete copy.children\n\t\t\t}\n\t\t\ttarget.push(copy)\n\t\t\treturn\n\t\t}\n\t\t// `query` and `params` belong to `route`: an entry that takes its route\n\t\t// from here without them opens the list unfiltered (a \"board\" entry to\n\t\t// `Cases` with `{ _view: 'board' }` opened the table).\n\t\tfor (const key of ['label', 'icon', 'route', 'query', 'params', 'order', 'section', 'featureFlag', 'permission', 'visibleIf', 'href', 'action']) {\n\t\t\tif (existing[key] === undefined && item[key] !== undefined) {\n\t\t\t\texisting[key] = item[key]\n\t\t\t}\n\t\t}\n\t\tif (Array.isArray(item.children) && item.children.length > 0) {\n\t\t\tif (!Array.isArray(existing.children)) {\n\t\t\t\texisting.children = []\n\t\t\t}\n\t\t\tmergeMenuItems(existing.children, item.children)\n\t\t}\n\t})\n}\n\n/**\n * Merge fragment pages onto the accumulated page list by `id` — a later\n * declaration REPLACES an earlier one wholesale, so a fragment can overlay a\n * base page (e.g. swap a schema-driven detail for a bespoke `type: \"custom\"`\n * component) and guarantee one route per page id.\n *\n * @param {Array<object>} target Accumulated pages (mutated in place).\n * @param {Array<object>} incoming Pages from a fragment.\n * @return {void}\n */\nexport function mergePages(target, incoming) {\n\tincoming.forEach((page) => {\n\t\tconst idx = target.findIndex((p) => p.id === page.id)\n\t\tif (idx === -1) {\n\t\t\ttarget.push(page)\n\t\t} else {\n\t\t\ttarget[idx] = page\n\t\t}\n\t})\n}\n\n/**\n * Re-home merged menu entries onto the canonical navigation layout declared\n * by `menu-layout.json#relocations` (`{ sourceId: targetGroupId }`).\n *\n * - A relocated GROUP dissolves: its children merge (by id) into the target\n * group and the now-empty shell is dropped.\n * - A relocated LEAF (top-level or child of any group) moves under the\n * target group.\n * - A child group relocated onto its own parent flattens into it.\n * - Unknown source ids are inert; a missing target keeps the entry at the\n * top level so nothing silently disappears.\n *\n * Runs in passes until stable (children freed by a dissolved group can\n * themselves be relocated on the next pass).\n *\n * @param {Array<object>} menu The merged menu (mutated in place).\n * @param {Record<string, string>|undefined} relocations Source-id → target-group-id map.\n * @return {Array<object>} The menu with relocations applied.\n */\nexport function applyMenuRelocations(menu, relocations) {\n\tif (!relocations || typeof relocations !== 'object') {\n\t\treturn menu\n\t}\n\tconst heldEntries = captionsHoldingEntries(menu)\n\tfor (let pass = 0; pass < 5; pass++) {\n\t\tconst moves = []\n\t\tfor (let i = menu.length - 1; i >= 0; i--) {\n\t\t\tconst node = menu[i]\n\t\t\tconst target = relocations[node.id]\n\t\t\tif (target && target !== node.id) {\n\t\t\t\tmenu.splice(i, 1)\n\t\t\t\tmoves.push({ node, target })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!Array.isArray(node.children)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor (let j = node.children.length - 1; j >= 0; j--) {\n\t\t\t\tconst child = node.children[j]\n\t\t\t\tconst childTarget = relocations[child.id]\n\t\t\t\tif (!childTarget) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (childTarget === node.id && !Array.isArray(child.children)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnode.children.splice(j, 1)\n\t\t\t\tmoves.push({ node: child, target: childTarget })\n\t\t\t}\n\t\t}\n\t\tif (moves.length === 0) {\n\t\t\tbreak\n\t\t}\n\t\tmoves.forEach(({ node, target }) => {\n\t\t\tconst group = menu.find((m) => m.id === target)\n\t\t\tif (!group) {\n\t\t\t\tmenu.push(node)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!Array.isArray(group.children)) {\n\t\t\t\tgroup.children = []\n\t\t\t}\n\t\t\tif (Array.isArray(node.children)) {\n\t\t\t\tmergeMenuItems(group.children, node.children)\n\t\t\t} else {\n\t\t\t\tmergeMenuItems(group.children, [node])\n\t\t\t}\n\t\t})\n\t}\n\t// Drop empty group shells left behind by relocations. A caption (a\n\t// section heading) has no route and no children and is kept, unless the\n\t// relocations emptied it: every entry under it moved away.\n\tconst kept = menu.filter((m) => m.type === 'caption' || m.route || m.href || m.action\n\t\t|| (Array.isArray(m.children) && m.children.length > 0))\n\tconst holdingNow = captionsHoldingEntries(kept)\n\treturn kept.filter((m) => m.type !== 'caption' || !heldEntries.has(m) || holdingNow.has(m))\n}\n\n/**\n * The captions (`type: \"caption\"`) that have at least one entry under them:\n * a non-caption entry of the same section before the next caption.\n *\n * @param {Array<object>} menu The top-level menu.\n * @return {Set<object>} The captions holding an entry.\n */\nfunction captionsHoldingEntries(menu) {\n\tconst holding = new Set()\n\tmenu.forEach((entry, index) => {\n\t\tif (entry.type !== 'caption') {\n\t\t\treturn\n\t\t}\n\t\tconst section = entry.section ?? 'main'\n\t\tfor (let i = index + 1; i < menu.length; i++) {\n\t\t\tconst next = menu[i]\n\t\t\tif ((next.section ?? 'main') !== section) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (next.type === 'caption') {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tholding.add(entry)\n\t\t\tbreak\n\t\t}\n\t})\n\treturn holding\n}\n\n/**\n * Remove individual menu entries by id after relocation — used to retire\n * duplicate navigation entries whose PAGE must stay routable (deep links and\n * e2e specs hit the route directly). Declared in `menu-layout.json#removals`.\n * Only leaf entries are removed; a group is dropped only when it is explicitly\n * removed OR left empty after its children were removed/relocated away. A\n * group carrying a `route`/`href`/`action` is itself clickable (a direct-link\n * top-level item, e.g. a \"Reporting & Compliance\" → cards-page link) and\n * survives even when all its children are gone.\n *\n * @param {Array<object>} menu The merged menu.\n * @param {Array<string>|undefined} removals Menu-entry ids to drop.\n * @return {Array<object>} The menu without the removed entries.\n */\nexport function applyMenuRemovals(menu, removals) {\n\tif (!Array.isArray(removals) || removals.length === 0) {\n\t\treturn menu\n\t}\n\tconst drop = new Set(removals)\n\tconst wasGroup = (n) => Array.isArray(n.children) && n.children.length > 0\n\tconst isClickable = (n) => n.route !== undefined || n.href !== undefined || n.action !== undefined\n\tconst prune = (nodes) => nodes.reduce((acc, n) => {\n\t\tif (drop.has(n.id) && !wasGroup(n)) {\n\t\t\treturn acc\n\t\t}\n\t\tif (Array.isArray(n.children)) {\n\t\t\tconst children = prune(n.children)\n\t\t\tconst hadChildren = wasGroup(n)\n\t\t\tif (children.length === 0 && hadChildren && !isClickable(n)) {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\tacc.push({ ...n, children })\n\t\t\treturn acc\n\t\t}\n\t\tacc.push(n)\n\t\treturn acc\n\t}, [])\n\treturn prune(menu)\n}\n\n/**\n * Lift the menu entries listed by id out of wherever they currently sit, tag\n * them with `section`, flatten them (neither destination renders nested\n * groups), and append them at the top level. Empty non-clickable groups left\n * behind are dropped; a clickable group is kept.\n *\n * Shared by `applySettingsSection` and `applyIntegrationsSection` so the two\n * destinations cannot drift in their lift semantics — the only difference\n * between them is the section tag, and therefore which surface renders them.\n *\n * @param {Array<object>} menu The merged + relocated + pruned menu.\n * @param {Array<string>|undefined} ids Entry ids to lift.\n * @param {string} section The `section` tag to stamp on each lifted entry.\n * @return {Array<object>} The menu with the listed entries lifted out.\n */\nfunction liftToSection(menu, ids, section) {\n\tif (!Array.isArray(ids) || ids.length === 0) {\n\t\treturn menu\n\t}\n\tconst want = new Set(ids)\n\tconst isClickable = (n) => n.route !== undefined || n.href !== undefined || n.action !== undefined\n\tconst lifted = []\n\tconst strip = (nodes) => nodes.reduce((acc, n) => {\n\t\tif (want.has(n.id)) {\n\t\t\tconst { children, ...leaf } = n\n\t\t\tlifted.push({ ...leaf, section })\n\t\t\treturn acc\n\t\t}\n\t\tif (Array.isArray(n.children)) {\n\t\t\tconst children = strip(n.children)\n\t\t\tif (children.length === 0 && n.children.length > 0 && !isClickable(n)) {\n\t\t\t\treturn acc\n\t\t\t}\n\t\t\tacc.push({ ...n, children })\n\t\t\treturn acc\n\t\t}\n\t\tacc.push(n)\n\t\treturn acc\n\t}, [])\n\tconst remaining = strip(menu)\n\treturn [...remaining, ...lifted]\n}\n\n/**\n * Promote the menu entries listed in `menu-layout.json#settingsSection` into\n * Nextcloud's settings foldout — the `NcAppNavigationSettings` gear at the\n * bottom-left of the navigation, OUTSIDE the scrollable list. CnAppNav renders\n * every TOP-LEVEL item carrying `section: \"settings\"` as a flat entry inside\n * that foldout (with an auto-prepended \"Personal settings\").\n *\n * @param {Array<object>} menu The merged + relocated + pruned menu.\n * @param {Array<string>|undefined} settingsIds Entry ids to move to the foldout.\n * @return {Array<object>} The menu with the settings entries lifted out.\n */\nexport function applySettingsSection(menu, settingsIds) {\n\treturn liftToSection(menu, settingsIds, 'settings')\n}\n\n/**\n * Move the menu entries listed in `menu-layout.json#integrationsSection` OUT of\n * the navigation entirely and into the Integrations section at the bottom of\n * the per-user settings modal (ADR-110).\n *\n * This is where a link that LEAVES this app for another one belongs. Such a\n * link is not a page of this app — it cannot be active, it has no counter, and\n * putting it in the nav makes another app's capability read as this app's\n * feature. The one deep link that stays in the navigation is Admin settings,\n * and CnAppNav auto-prepends that itself (ADR-079); no app declares it.\n *\n * The entry survives — it is relocated, never dropped — which is what keeps\n * this compatible with the ADR-044 Decision 5 no-functionality-loss invariant.\n *\n * @param {Array<object>} menu The merged + relocated + pruned menu.\n * @param {Array<string>|undefined} integrationIds Entry ids to move to Integrations.\n * @return {Array<object>} The menu with the integration entries lifted out.\n */\nexport function applyIntegrationsSection(menu, integrationIds) {\n\treturn liftToSection(menu, integrationIds, 'integrations')\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACO,SAAS,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE;AACrE,CAAC,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,GAAE;AACrE,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,EAAC;AAC7C;AACA;AACA,CAAC,MAAM,aAAa,GAAG,GAAE;AACzB,CAAC,MAAM,aAAa,GAAG,GAAE;AACzB,CAAC,KAAK,MAAM,IAAI,KAAK,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,EAAE,GAAG;AACjE,EAAE,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;AACzC,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAC;AACvC,GAAG;AACH,EAAE,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;AACxC,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAC;AACzC,GAAG;AACH,EAAE,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE;AACjD,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,EAAC;AAC5C,GAAG;AACH,EAAE,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE;AACjD,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,EAAC;AAC5C,GAAG;AACH,EAAE,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC1D,GAAG,MAAM,CAAC,IAAI,GAAG,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,GAAE;AACzD,GAAG;AACH,EAAE;AACF,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE;AAC3B,EAAE,MAAM,CAAC,aAAa,GAAG,CAAC,IAAI,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,GAAG,aAAa,EAAC;AAC5E,EAAE;AACF,CAAC,IAAI,aAAa,CAAC,MAAM,EAAE;AAC3B,EAAE,MAAM,CAAC,aAAa,GAAG,CAAC,IAAI,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,GAAG,aAAa,EAAC;AAC5E,EAAE;AACF,CAAC,MAAM,CAAC,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAC;AACvD;AACA;AACA;AACA;AACA,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE;AACjF,EAAE,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,EAAC;AACrE,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;AAC5B;AACA,GAAG,OAAO,CAAC,IAAI,CAAC,qGAAqG,EAAE,MAAM,CAAC,MAAM,EAAC;AACrI,GAAG;AACH,EAAE,OAAO,MAAM,CAAC,QAAQ;AACxB,EAAE;AACF,CAAC,OAAO,MAAM;AACd,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE;AACvD,CAAC,IAAI,GAAG,GAAG,oBAAoB,CAAC,IAAI,EAAE,UAAU,CAAC,WAAW,EAAC;AAC7D,CAAC,GAAG,GAAG,iBAAiB,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,EAAC;AAClD,CAAC,GAAG,GAAG,oBAAoB,CAAC,GAAG,EAAE,UAAU,CAAC,eAAe,EAAC;AAC5D,CAAC,GAAG,GAAG,wBAAwB,CAAC,GAAG,EAAE,UAAU,CAAC,mBAAmB,EAAC;AACpE,CAAC,OAAO,GAAG;AACX,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE;AACjD,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK;AAC5B,EAAE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,EAAC;AACvD,EAAE,IAAI,CAAC,QAAQ,EAAE;AACjB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,GAAE;AAC3B,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AACrC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAC;AACtC,IAAI,MAAM;AACV,IAAI,OAAO,IAAI,CAAC,SAAQ;AACxB,IAAI;AACJ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAC;AACpB,GAAG,MAAM;AACT,GAAG;AACH;AACA;AACA;AACA,EAAE,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE;AACnJ,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;AAC/D,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,EAAC;AAC7B,IAAI;AACJ,GAAG;AACH,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;AAChE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;AAC1C,IAAI,QAAQ,CAAC,QAAQ,GAAG,GAAE;AAC1B,IAAI;AACJ,GAAG,cAAc,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAC;AACnD,GAAG;AACH,EAAE,EAAC;AACH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE;AAC7C,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK;AAC5B,EAAE,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,EAAC;AACvD,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE;AAClB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAC;AACpB,GAAG,MAAM;AACT,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,KAAI;AACrB,GAAG;AACH,EAAE,EAAC;AACH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE;AACxD,CAAC,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;AACtD,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,WAAW,GAAG,sBAAsB,CAAC,IAAI,EAAC;AACjD,CAAC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE;AACtC,EAAE,MAAM,KAAK,GAAG,GAAE;AAClB,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,GAAG,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,EAAC;AACvB,GAAG,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,EAAC;AACtC,GAAG,IAAI,MAAM,IAAI,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE;AACrC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAC;AACrB,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAC;AAChC,IAAI,QAAQ;AACZ,IAAI;AACJ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AACtC,IAAI,QAAQ;AACZ,IAAI;AACJ,GAAG,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;AACvD,IAAI,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAC;AAClC,IAAI,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,EAAE,EAAC;AAC7C,IAAI,IAAI,CAAC,WAAW,EAAE;AACtB,KAAK,QAAQ;AACb,KAAK;AACL,IAAI,IAAI,WAAW,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;AACnE,KAAK,QAAQ;AACb,KAAK;AACL,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAC;AAC9B,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,EAAC;AACpD,IAAI;AACJ,GAAG;AACH,EAAE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1B,GAAG,KAAK;AACR,GAAG;AACH,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;AACtC,GAAG,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,MAAM,EAAC;AAClD,GAAG,IAAI,CAAC,KAAK,EAAE;AACf,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAC;AACnB,IAAI,MAAM;AACV,IAAI;AACJ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;AACvC,IAAI,KAAK,CAAC,QAAQ,GAAG,GAAE;AACvB,IAAI;AACJ,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;AACrC,IAAI,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAC;AACjD,IAAI,MAAM;AACV,IAAI,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAC;AAC1C,IAAI;AACJ,GAAG,EAAC;AACJ,EAAE;AACF;AACA;AACA;AACA,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM;AACtF,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,EAAC;AAC1D,CAAC,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,EAAC;AAChD,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,sBAAsB,CAAC,IAAI,EAAE;AACtC,CAAC,MAAM,OAAO,GAAG,IAAI,GAAG,GAAE;AAC1B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK;AAChC,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;AAChC,GAAG,MAAM;AACT,GAAG;AACH,EAAE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,OAAM;AACzC,EAAE,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AAChD,GAAG,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,EAAC;AACvB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,MAAM,MAAM,OAAO,EAAE;AAC7C,IAAI,QAAQ;AACZ,IAAI;AACJ,GAAG,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;AAChC,IAAI,KAAK;AACT,IAAI;AACJ,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAC;AACrB,GAAG,KAAK;AACR,GAAG;AACH,EAAE,EAAC;AACH,CAAC,OAAO,OAAO;AACf,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE;AAClD,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACxD,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAC;AAC/B,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,EAAC;AAC3E,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,UAAS;AACnG,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK;AACnD,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;AACtC,GAAG,OAAO,GAAG;AACb,GAAG;AACH,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE;AACjC,GAAG,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAC;AACrC,GAAG,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,EAAC;AAClC,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;AAChE,IAAI,OAAO,GAAG;AACd,IAAI;AACJ,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAC;AAC/B,GAAG,OAAO,GAAG;AACb,GAAG;AACH,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,EAAC;AACb,EAAE,OAAO,GAAG;AACZ,EAAE,EAAE,EAAE,EAAC;AACP,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC;AACnB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE;AAC3C,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE;AAC9C,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,EAAC;AAC1B,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,UAAS;AACnG,CAAC,MAAM,MAAM,GAAG,GAAE;AAClB,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK;AACnD,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;AACtB,GAAG,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,EAAC;AAClC,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAC;AACpC,GAAG,OAAO,GAAG;AACb,GAAG;AACH,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE;AACjC,GAAG,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAC;AACrC,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;AAC1E,IAAI,OAAO,GAAG;AACd,IAAI;AACJ,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAC;AAC/B,GAAG,OAAO,GAAG;AACb,GAAG;AACH,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,EAAC;AACb,EAAE,OAAO,GAAG;AACZ,EAAE,EAAE,EAAE,EAAC;AACP,CAAC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAC;AAC9B,CAAC,OAAO,CAAC,GAAG,SAAS,EAAE,GAAG,MAAM,CAAC;AACjC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE;AACxD,CAAC,OAAO,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC;AACpD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,wBAAwB,CAAC,IAAI,EAAE,cAAc,EAAE;AAC/D,CAAC,OAAO,aAAa,CAAC,IAAI,EAAE,cAAc,EAAE,cAAc,CAAC;AAC3D;;;;"}
|