@conduction/nextcloud-vue 2.84.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/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/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +114 -0
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +2 -2
- 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 +45 -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/CnDetailPage/CnDetailPage.vue2.js +23 -4
- 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/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/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +95 -0
- 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 +70 -2
- 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/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/CnStatWidget/CnStatWidget.vue.js +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +134 -10
- 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/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +6 -3
- 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/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/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +23 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +41 -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 +742 -397
- 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 +4 -1
- package/dist/esm/utils/buildManifest.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/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 +15075 -12590
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +742 -397
- package/l10n/en.json +23 -1
- package/l10n/nl.json +41 -1
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +160 -8
- package/src/components/CnBoardView/CnBoardView.vue +262 -6
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +116 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +57 -2
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +23 -4
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +3 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -5
- package/src/components/CnIndexPage/CnIndexPage.vue +586 -25
- package/src/components/CnIndexPage/useSelfFetchList.js +10 -1
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +123 -3
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +70 -2
- package/src/components/CnRegisterMapping/CnRegisterMapping.md +5 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +231 -96
- package/src/components/CnSettingsPage/CnSettingsPage.vue +5 -1
- package/src/components/CnStatWidget/CnStatWidget.vue +136 -11
- package/src/components/CnTabsWidget/CnTabsWidget.vue +6 -3
- 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/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/index-page.css +49 -0
- package/src/css/kpi-card.css +13 -0
- package/src/css/look-board.css +96 -7
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +115 -5
- 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 +4 -1
- package/src/utils/gridStack.js +39 -0
- package/src/utils/humaniseKey.js +38 -0
- package/src/utils/notificationRuleLabel.js +5 -23
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/widgetDispatch.js +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnRecordListWidget.vue3.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordListWidget.vue?vue&type=template&id=bc186ef8&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-record-list\"\n\t\t:class=\"`cn-record-list--${presentation}`\"\n\t\tdata-testid=\"cn-record-list\">\n\t\t<p v-if=\"error\" class=\"cn-record-list__error\" data-testid=\"cn-record-list-error\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-record-list__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-record-list__empty\" data-testid=\"cn-record-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-record-list__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"item in items\"\n\t\t\t\t:key=\"item.key\"\n\t\t\t\tclass=\"cn-record-list__item\"\n\t\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t\t<!-- rows with `rowAction`: the title is the link, and one action\n\t\t\t\t ends the row instead of the chevron (\"Beoordelen\"). -->\n\t\t\t\t<div\n\t\t\t\t\tv-if=\"presentation === 'rows' && item.action\"\n\t\t\t\t\tclass=\"cn-record-list__row cn-record-list__row--action\">\n\t\t\t\t\t<span v-if=\"item.initials\"\n\t\t\t\t\t\tclass=\"cn-record-list__avatar\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-avatar\">{{ item.initials }}</span>\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\t\t\tv-if=\"item.title\"\n\t\t\t\t\t\t\tclass=\"cn-record-list__title\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-record-list__title--link': item.tag !== 'div' }\"\n\t\t\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t\t</component>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.badges.length > 0\" class=\"cn-record-list__pills\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-for=\"badge in item.badges\"\n\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t:label=\"badge.label\"\n\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-record-list-badge-${badge.kind}`\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"item.due\"\n\t\t\t\t\t\tclass=\"cn-record-list__due cn-record-list__due--column\"\n\t\t\t\t\t\t:class=\"`cn-record-list__due--${item.due.tone}`\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-due\">{{ item.due.text }}</span>\n\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"item.action.tag\"\n\t\t\t\t\t\tclass=\"cn-record-list__action\"\n\t\t\t\t\t\tv-bind=\"item.action.attrs\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-action\"\n\t\t\t\t\t\tv-on=\"item.action.tag === 'button' ? { click: () => runRowAction(item.row) } : {}\">\n\t\t\t\t\t\t{{ item.action.label }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t\t<!-- rows: title and meta, then the pill, the date and a chevron. -->\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-else-if=\"presentation === 'rows'\"\n\t\t\t\t\tclass=\"cn-record-list__row\"\n\t\t\t\t\t:class=\"{ 'cn-record-list__row--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t<span v-if=\"item.initials\"\n\t\t\t\t\t\tclass=\"cn-record-list__avatar\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-avatar\">{{ item.initials }}</span>\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<span v-if=\"item.title\" class=\"cn-record-list__title\">{{ item.title }}</span>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.badges.length > 0 || item.due || item.trailing\" class=\"cn-record-list__aside\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-for=\"badge in item.badges\"\n\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t:label=\"badge.label\"\n\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-record-list-badge-${badge.kind}`\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"item.due\"\n\t\t\t\t\t\t\tclass=\"cn-record-list__due\"\n\t\t\t\t\t\t\t:class=\"`cn-record-list__due--${item.due.tone}`\"\n\t\t\t\t\t\t\tdata-testid=\"cn-record-list-due\">{{ item.due.text }}</span>\n\t\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ChevronRight\n\t\t\t\t\t\tv-if=\"item.tag !== 'div'\"\n\t\t\t\t\t\tclass=\"cn-record-list__chevron\"\n\t\t\t\t\t\t:size=\"18\"\n\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t</component>\n\n\t\t\t\t<!-- feed: a round icon, a meta line and the text. -->\n\t\t\t\t<div v-else-if=\"presentation === 'feed'\" class=\"cn-record-list__feed-row\">\n\t\t\t\t\t<span class=\"cn-record-list__feed-icon\" aria-hidden=\"true\">\n\t\t\t\t\t\t<CnIcon :name=\"item.icon\" :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__main\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t<span v-if=\"item.text\" class=\"cn-record-list__text\">{{ item.text }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- inline: a label and the title, the title a link. -->\n\t\t\t\t<div v-else class=\"cn-record-list__inline-row\">\n\t\t\t\t\t<span v-if=\"item.label\" class=\"cn-record-list__label\">{{ item.label }}</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__inline-title\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { actionLink, dispatchAction, interpolateActionTarget } from '../../utils/actionsDispatcher.js'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { ageInDays } from '../../utils/boardDate.js'\nimport { resolveDue } from '../../utils/dueText.js'\nimport { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'\nimport { unwrapAppConfig } from '../../utils/formatMetric.js'\nimport { initialsOf } from '../../utils/initials.js'\nimport { lensUnavailableText, readLensReports } from '../../utils/lensAvailability.js'\nimport { pageFilter, pageSort } from '../../utils/pageControls.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\nconst PRESENTATIONS = ['rows', 'feed', 'inline']\n\n/**\n * Unwrap a value that may be a ref.\n *\n * @param {unknown} value The value.\n * @return {unknown} The plain value.\n */\nfunction unwrap(value) {\n\treturn value && typeof value === 'object' && 'value' in value ? value.value : value\n}\n\n/**\n * CnRecordListWidget — the objects of another schema that point at this\n * record, drawn as the boards draw them (screens-relation-pages).\n *\n * A person page lists its open cases (\"Verlichting fietspad Lindelaan,\n * 2026-0082 · Woo-verzoek, In behandeling, Termijn 1 nov\"), its latest contact\n * moments (an icon by channel, a meta line, the summary) and its relations\n * (\"Secretaris van Buurtvereniging Oost\"). Each is a list of another schema's\n * objects filtered on a reference to this record. The source keys are those of\n * `object-list` (`register`, `schema`, `filter` with the `@objectId` /\n * `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,\n * `rowRoute`, `pageControls`); `avatar` puts a circle with the initials of a name\n * template before the title; what differs is the drawing: `presentation` is `rows`, `feed`\n * or `inline`, and each line is a template (`\"{identifier} · {caseTypeName}\"`,\n * `\"Termijn {deadline|date}\"`).\n *\n * A content-only catalog type: on a card surface the detail host or the\n * dashboard page draws the titled card. The widget emits `header` with `{ link, note }` (the\n * `headerLink` and `headerNote` with `{total}` filled in) for that card's\n * header.\n */\nexport default {\n\tname: 'CnRecordListWidget',\n\n\tcomponents: { ChevronRight, CnIcon, CnStatusBadge, NcLoadingIcon },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** Page workspace bag, for `@workspace.<key>` tokens. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host app's translate function. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's app config map; its `currency` is the reporting currency of a money token. */\n\t\tcnAppConfig: { default: () => ({}) },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `rowAction`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list config: `{ register, schema, filter?, sort?, limit?, extend?,\n\t\t * presentation?, title?, meta?, text?, label?, pill?, trailing?, icon?,\n\t\t * rowRoute?, rowIdField?, headerLink?, headerNote?, emptyText?, errorText?,\n\t\t * valueLabels? }`. `valueLabels` maps a field's stored values to the labels\n\t\t * the templates show.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current record, for `@object.<field>` tokens. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\ttotal: 0,\n\t\t\tloading: false,\n\t\t\terror: false,\n\t\t\t/** Personal-lens reports (`@self.lenses`) of the latest response. */\n\t\t\tlenses: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} `rows`, `feed` or `inline`. */\n\t\tpresentation() {\n\t\t\tconst value = this.content?.presentation\n\t\t\treturn PRESENTATIONS.includes(value) ? value : 'rows'\n\t\t},\n\n\t\t/** @return {object} The token context: record, id and workspace. */\n\t\ttokenCtx() {\n\t\t\tconst injected = unwrap(this.cnObjectContext) || {}\n\t\t\treturn {\n\t\t\t\t...injected,\n\t\t\t\tobjectId: this.objectId ?? injected.objectId ?? this.objectData?.id ?? null,\n\t\t\t\tobject: this.objectData || injected.object || null,\n\t\t\t\tworkspace: unwrap(this.cnWorkspaceContext) || {},\n\t\t\t}\n\t\t},\n\n\t\t/** @return {object} The filter with its tokens resolved. */\n\t\tresolvedFilter() {\n\t\t\tconst filter = pageFilter(this.content?.pageControls, this.tokenCtx.workspace, this.content?.filter)\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))\n\t\t},\n\n\t\t/** @return {boolean} A required token (the record id, say) has no value yet. */\n\t\twaiting() {\n\t\t\treturn hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/** @return {object|null} The sort: the page's sort control when the widget listens to one, else its own. */\n\t\teffectiveSort() {\n\t\t\treturn pageSort(this.content?.pageControls, this.tokenCtx.workspace, this.content?.sort)\n\t\t},\n\n\t\t/**\n\t\t * What a template may read beside the row: the app config, whose\n\t\t * `currency` is the reporting currency of a `{path|currency}` token.\n\t\t *\n\t\t * @return {{config: object}} The template context.\n\t\t * @spec openspec/changes/screens-currency-format/specs/currency-format/spec.md#requirement-a-money-value-reads-in-its-currency\n\t\t */\n\t\ttemplateContext() {\n\t\t\treturn { config: unwrapAppConfig(this.cnAppConfig) }\n\t\t},\n\n\t\t/** @return {number} The fetch cap. */\n\t\tlimit() {\n\t\t\tconst value = Number(this.content?.limit)\n\t\t\treturn Number.isFinite(value) && value > 0 ? Math.floor(value) : 5\n\t\t},\n\n\t\t/** @return {string} What a fetch depends on; a change fetches again. */\n\t\trequestKey() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn JSON.stringify([c.register, c.schema, this.resolvedFilter, this.effectiveSort, this.limit, c.extend, this.waiting])\n\t\t},\n\n\t\t/** @return {Array<object>} The rows ready to draw. */\n\t\titems() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn this.rows.slice(0, this.limit).map((row, index) => {\n\t\t\t\tconst link = this.rowLink(row)\n\t\t\t\treturn {\n\t\t\t\t\tkey: (row && (row.id || row['@self']?.id)) || `row-${index}`,\n\t\t\t\t\trow,\n\t\t\t\t\taction: this.rowActionOf(row),\n\t\t\t\t\ttag: link ? 'router-link' : 'div',\n\t\t\t\t\tattrs: link ? { to: link } : {},\n\t\t\t\t\tinitials: this.initialsFor(row),\n\t\t\t\t\ttitle: this.fill(c.title, row),\n\t\t\t\t\tmeta: this.metaOf(row),\n\t\t\t\t\ttext: this.fill(c.text, row),\n\t\t\t\t\tlabel: this.fill(c.label, row),\n\t\t\t\t\ttrailing: c.trailing ? firstFilled(c.trailing, row, this.translate, new Date(), this.templateContext) : '',\n\t\t\t\t\tpill: this.pillOf(row),\n\t\t\t\t\tbadges: this.badgesOf(row),\n\t\t\t\t\tdue: this.dueOf(row),\n\t\t\t\t\ticon: this.iconOf(row),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {object|null} The header link with `{total}` filled and tokens resolved. */\n\t\theaderLink() {\n\t\t\tconst link = this.content?.headerLink\n\t\t\tif (!link || typeof link !== 'object' || typeof link.label !== 'string' || link.label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = { label: this.withTotal(link.label) }\n\t\t\tif (link.route) {\n\t\t\t\tresolved.route = link.route\n\t\t\t\tif (link.params && typeof link.params === 'object') {\n\t\t\t\t\tresolved.params = resolveFilterTokens(link.params, this.tokenCtx)\n\t\t\t\t}\n\t\t\t\tif (link.query && typeof link.query === 'object') {\n\t\t\t\t\tresolved.query = resolveFilterTokens(link.query, this.tokenCtx)\n\t\t\t\t}\n\t\t\t} else if (typeof link.href === 'string') {\n\t\t\t\tresolved.href = link.href\n\t\t\t}\n\t\t\treturn resolved\n\t\t},\n\n\t\t/** @return {string} The header note with `{total}` filled. */\n\t\theaderNote() {\n\t\t\tconst note = this.content?.headerNote\n\t\t\treturn typeof note === 'string' && note !== '' ? this.withTotal(note) : ''\n\t\t},\n\n\t\t/**\n\t\t * Why the list is empty when a personal lens could not answer\n\t\t * (`@self.lenses.<lens>.available: false`, as for `filter: { _recent: true }`),\n\t\t * or '' to keep `emptyText`. `lensReasonTexts` holds the app's wording,\n\t\t * keyed `<lens>.<reason>` or `<reason>`.\n\t\t *\n\t\t * @return {string} The explanation, or ''.\n\t\t * @spec openspec/changes/screens-record-list-lens-reason/specs/record-list-lens-reason/spec.md#requirement-a-record-list-says-why-a-lens-is-empty\n\t\t */\n\t\tlensReasonText() {\n\t\t\tif (this.rows.length > 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst text = lensUnavailableText(this.lenses, this.content?.lensReasonTexts)\n\t\t\treturn text ? this.translate(text) : ''\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyLabel() {\n\t\t\tif (this.lensReasonText !== '') {\n\t\t\t\treturn this.lensReasonText\n\t\t\t}\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'Nothing here yet')\n\t\t},\n\n\t\t/** @return {string} The error line. */\n\t\terrorLabel() {\n\t\t\treturn this.content?.errorText ? this.translate(this.content.errorText) : t('nextcloud-vue', 'This list could not be loaded.')\n\t\t},\n\n\t\t/** @return {string} The spinner's accessible name. */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\t},\n\n\twatch: {\n\t\trequestKey() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.onRefresh = () => this.fetchRows()\n\t\tsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t\tthis.fetchRows()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the host app.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The initials of the row's avatar (`avatar`: a name template such as\n\t\t * `\"{client.name}\"`, or `{ text }`), or '' without one.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} One or two letters.\n\t\t * @spec openspec/changes/screens-record-list-avatar/specs/record-list-avatar/spec.md#requirement-a-record-list-row-can-carry-an-avatar\n\t\t */\n\t\tinitialsFor(row) {\n\t\t\tconst avatar = this.content?.avatar\n\t\t\tconst template = typeof avatar === 'string' ? avatar : (avatar && typeof avatar === 'object' ? avatar.text : '')\n\t\t\treturn initialsOf(this.fill(template, row))\n\t\t},\n\n\t\t/**\n\t\t * Fill a template from a row, after translating it.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @param {object} row The row.\n\t\t * @return {string} The text, '' when a token is empty.\n\t\t */\n\t\tfill(template, row) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.translate(template), this.labelledRow(row), new Date(), this.templateContext) : ''\n\t\t},\n\n\t\t/**\n\t\t * The row as the templates read it: a field named in `valueLabels`\n\t\t * (`{ channel: { phone: \"Phone\", telefoon: \"Phone\" } }`) shows the\n\t\t * translated label of its stored value, so \"{channel}\" reads \"Telefoon\"\n\t\t * for both spellings the data holds. A value without a label stays as\n\t\t * stored. The pill, the badges and the sort read the stored value.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object} The row, or a shallow copy with labelled fields.\n\t\t * @spec openspec/changes/screens-record-list-value-labels/specs/record-list-value-labels/spec.md#requirement-a-record-list-template-can-show-a-label-for-a-stored-value\n\t\t */\n\t\tlabelledRow(row) {\n\t\t\tconst map = this.content?.valueLabels\n\t\t\tif (!row || typeof row !== 'object' || !map || typeof map !== 'object') {\n\t\t\t\treturn row\n\t\t\t}\n\t\t\tlet out = row\n\t\t\tfor (const [field, labels] of Object.entries(map)) {\n\t\t\t\tconst raw = row[field]\n\t\t\t\tif (!labels || typeof labels !== 'object' || (typeof raw !== 'string' && typeof raw !== 'number')) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst label = labels[String(raw)]\n\t\t\t\tif (typeof label === 'string' && label !== '') {\n\t\t\t\t\tif (out === row) {\n\t\t\t\t\t\tout = { ...row }\n\t\t\t\t\t}\n\t\t\t\t\tout[field] = this.translate(label)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Replace `{total}` in a translated label.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The text.\n\t\t */\n\t\twithTotal(label) {\n\t\t\treturn this.translate(label).replace(/\\{total\\}/g, String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * The route a row opens: `rowRoute` with the id at `rowIdField`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object|null} The router location, or null.\n\t\t */\n\t\t/**\n\t\t * The one action that ends a row (`rowAction`): its label (from\n\t\t * `labels.map` by the row's `labels.field`, else `label`), and how it\n\t\t * renders: a link when it only navigates, else a button that\n\t\t * dispatches. Null without the key or a label.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{label: string, tag: string, attrs: object}|null}\n\t\t * @spec openspec/changes/screens-record-list-row-action/specs/record-list/spec.md\n\t\t */\n\t\trowActionOf(row) {\n\t\t\tconst action = this.content?.rowAction\n\t\t\tif (!action || typeof action !== 'object' || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tlet label = typeof action.label === 'string' ? action.label : ''\n\t\t\tconst by = action.labels\n\t\t\tif (by && typeof by === 'object' && typeof by.field === 'string' && by.map && typeof by.map === 'object') {\n\t\t\t\tconst raw = readPath(row, by.field)\n\t\t\t\tif (raw !== null && raw !== undefined && typeof by.map[String(raw)] === 'string') {\n\t\t\t\t\tlabel = by.map[String(raw)]\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tlabel = this.translate(label)\n\t\t\tconst link = this.rowActionLink(action, row)\n\t\t\tif (link) {\n\t\t\t\treturn link.external\n\t\t\t\t\t? { label, tag: 'a', attrs: { href: link.href, target: '_blank', rel: 'noopener' } }\n\t\t\t\t\t: { label, tag: 'router-link', attrs: { to: link.to } }\n\t\t\t}\n\t\t\treturn { label, tag: 'button', attrs: { type: 'button' } }\n\t\t},\n\n\t\t/**\n\t\t * The link a `rowAction` that only navigates renders as: `open-page`\n\t\t * opens that page for the row's id, `navigate` reads its\n\t\t * `@object.<field>` / `@objectId` tokens off the row. Null for any\n\t\t * other action, which is dispatched on click.\n\t\t *\n\t\t * @param {object} action The declared row action.\n\t\t * @param {object} row The row.\n\t\t * @return {{href: string, to: (string|object|null), external: boolean}|null}\n\t\t * @spec openspec/changes/screens-record-list-row-action/specs/record-list/spec.md\n\t\t */\n\t\trowActionLink(action, row) {\n\t\t\tif (action.confirm) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (action.type === 'open-page') {\n\t\t\t\tconst id = this.rowIdOf(row)\n\t\t\t\tif (!this.$router || id === null || typeof action.target !== 'string' || action.target === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst to = { name: action.target, params: { id } }\n\t\t\t\ttry {\n\t\t\t\t\tconst href = this.$router.resolve(to).href\n\t\t\t\t\treturn href ? { href, to, external: false } : null\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (action.type !== 'navigate') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst target = interpolateActionTarget(action.target, this.rowTokenCtx(row))\n\t\t\t// An external target needs no router; an in-app one does.\n\t\t\treturn actionLink({ ...action, target }, { router: this.$router || null })\n\t\t},\n\n\t\t/**\n\t\t * The token context of a row: the row is `@object`, its id `@objectId`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object} The token context.\n\t\t */\n\t\trowTokenCtx(row) {\n\t\t\treturn { ...this.tokenCtx, objectId: this.rowIdOf(row), object: row }\n\t\t},\n\n\t\t/**\n\t\t * The id of a row (`rowIdField`, else `id`, else `@self.id`), or null.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string|null} The id.\n\t\t */\n\t\trowIdOf(row) {\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row && (row.id || row['@self']?.id))\n\t\t\treturn (id === null || id === undefined || id === '' || typeof id === 'object') ? null : String(id)\n\t\t},\n\n\t\t/**\n\t\t * Run a `rowAction` that is not a plain link through the page's\n\t\t * dispatcher: a `handler` gets the row as its last argument, an\n\t\t * `open-modal` gets it as the `row` prop, an `object-op` acts on it.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {unknown} What the dispatcher returns.\n\t\t * @spec openspec/changes/screens-record-list-row-action/specs/record-list/spec.md\n\t\t */\n\t\trunRowAction(row) {\n\t\t\tconst action = this.content?.rowAction\n\t\t\tif (!action || typeof action !== 'object') {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst type = action.type || 'handler'\n\t\t\tlet wrapped = action\n\t\t\tif (type === 'handler') {\n\t\t\t\twrapped = { ...action, args: [...(action.args || []), row] }\n\t\t\t} else if (type === 'open-modal') {\n\t\t\t\twrapped = { ...action, props: { ...(action.props || {}), row } }\n\t\t\t}\n\t\t\tif (typeof this.cnDispatchAction === 'function') {\n\t\t\t\treturn this.cnDispatchAction(wrapped)\n\t\t\t}\n\t\t\treturn dispatchAction(wrapped, { router: this.$router || null, row, tokenCtx: this.rowTokenCtx(row), source: { register: this.content?.register, schema: this.content?.schema } })\n\t\t},\n\n\t\trowLink(row) {\n\t\t\tconst route = this.content?.rowRoute\n\t\t\tif (!route || !this.$router || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row.id || row['@self']?.id)\n\t\t\tif (id === null || id === undefined || id === '' || typeof id === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { name: route, params: { id: String(id) } }\n\t\t},\n\n\t\t/**\n\t\t * The row's due text (`due`: `{ field, todayLabel?, overdueLabel?, emptyText? }`):\n\t\t * \"3 dagen te laat\" in the danger tone, \"Vandaag\", the date otherwise,\n\t\t * `emptyText` when the field is empty.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{text: string, tone: string}|null} The text and tone, or null.\n\t\t * @spec openspec/changes/screens-due-text/specs/due-text/spec.md#requirement-a-due-date-reads-as-text-with-a-tone\n\t\t */\n\t\tdueOf(row) {\n\t\t\tconst due = this.content?.due\n\t\t\tif (!due || typeof due !== 'object' || !due.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn resolveDue(readPath(row, due.field), due, (text) => this.translate(text))\n\t\t},\n\n\t\t/**\n\t\t * The row's extra badges (`badges`): a coloured badge from a field\n\t\t * (`{ field, labels?, colorMap?, variant?, hideValues? }`, the value\n\t\t * shown unless `hideValues` lists it) and an age badge\n\t\t * (`{ kind: \"stale\", field, afterDays, label?, variant? }`, shown when the\n\t\t * date field is older than `afterDays` whole days; `{days}` in the\n\t\t * label is the age).\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {Array<{key: string, kind: string, label: string, variant: string}>} The badges that apply.\n\t\t * @spec openspec/changes/screens-record-list-badges/specs/record-list-badges/spec.md#requirement-a-record-list-row-can-carry-badges\n\t\t */\n\t\tbadgesOf(row) {\n\t\t\tconst declared = Array.isArray(this.content?.badges) ? this.content.badges : []\n\t\t\tconst out = []\n\t\t\tdeclared.forEach((badge, index) => {\n\t\t\t\tif (!badge || typeof badge !== 'object' || !badge.field) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif (badge.kind === 'stale') {\n\t\t\t\t\tconst days = ageInDays(readPath(row, badge.field))\n\t\t\t\t\tconst after = Number(badge.afterDays)\n\t\t\t\t\tif (days === null || !Number.isFinite(after) || days <= after) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst template = this.translate(badge.label || 'Stale')\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tkey: `stale-${index}`,\n\t\t\t\t\t\tkind: 'stale',\n\t\t\t\t\t\tlabel: template.replace(/\\{days\\}/g, String(days)),\n\t\t\t\t\t\tvariant: isBadgeVariant(badge.variant) ? badge.variant : 'warning',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst made = this.pillOf(row, badge)\n\t\t\t\tif (made) {\n\t\t\t\t\tout.push({ key: `field-${index}`, kind: 'field', ...made })\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * The row's meta line. A string is one template, and one empty token\n\t\t * empties it. `{ parts: [\"{identifier}\", \"{caseTypeName}\", \"{handler}\"],\n\t\t * separator? }` fills each part on its own, drops the empty ones and\n\t\t * joins the rest (default \" · \"), so a row without a handler still\n\t\t * reads \"2026-0082 · Woo-verzoek\".\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The meta line, '' when nothing fills.\n\t\t * @spec openspec/changes/screens-record-list-meta-parts/specs/record-list-meta-parts/spec.md#requirement-a-meta-line-of-parts-skips-the-empty-ones\n\t\t */\n\t\tmetaOf(row) {\n\t\t\tconst meta = this.content?.meta\n\t\t\tif (!meta || typeof meta !== 'object' || !Array.isArray(meta.parts)) {\n\t\t\t\treturn this.fill(meta, row)\n\t\t\t}\n\t\t\tconst separator = typeof meta.separator === 'string' ? meta.separator : ' · '\n\t\t\treturn meta.parts\n\t\t\t\t.map((part) => this.fill(part, row))\n\t\t\t\t.filter((text) => text !== '')\n\t\t\t\t.join(separator)\n\t\t},\n\n\t\t/**\n\t\t * The row's pill: `{ field, labels?, colorMap?, variant?, hideValues? }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {object} [config] A pill config; the widget's `pill` by default.\n\t\t * @return {{label: string, variant: string}|null} The pill, or null.\n\t\t */\n\t\tpillOf(row, config) {\n\t\t\tconst pill = config || this.content?.pill\n\t\t\tif (!pill || typeof pill !== 'object' || !pill.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(row, pill.field)\n\t\t\tif (raw === null || raw === undefined || raw === '' || typeof raw === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst key = String(raw)\n\t\t\tif (Array.isArray(pill.hideValues) && pill.hideValues.map(String).includes(key)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst labels = pill.labels && typeof pill.labels === 'object' ? pill.labels : {}\n\t\t\tconst map = pill.colorMap && typeof pill.colorMap === 'object' ? pill.colorMap : {}\n\t\t\tconst found = Object.keys(map).find((k) => k.toLowerCase() === key.toLowerCase())\n\t\t\tconst variant = found ? map[found] : pill.variant\n\t\t\treturn {\n\t\t\t\tlabel: labels[key] ? this.translate(labels[key]) : key,\n\t\t\t\tvariant: isBadgeVariant(variant) ? variant : 'default',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The feed icon: an icon name, or `{ field, map, default }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The MDI icon name.\n\t\t */\n\t\ticonOf(row) {\n\t\t\tconst icon = this.content?.icon\n\t\t\tif (typeof icon === 'string' && icon !== '') {\n\t\t\t\treturn icon\n\t\t\t}\n\t\t\tif (icon && typeof icon === 'object') {\n\t\t\t\tconst raw = icon.field ? readPath(row, icon.field) : undefined\n\t\t\t\tconst map = icon.map && typeof icon.map === 'object' ? icon.map : {}\n\t\t\t\tif (raw !== null && raw !== undefined && map[String(raw)]) {\n\t\t\t\t\treturn map[String(raw)]\n\t\t\t\t}\n\t\t\t\tif (typeof icon.default === 'string' && icon.default !== '') {\n\t\t\t\t\treturn icon.default\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 'CircleSmall'\n\t\t},\n\n\t\t/** Tell the card around this list what its header shows. */\n\t\temitHeader() {\n\t\t\tconst header = { link: this.headerLink, note: this.headerNote }\n\t\t\t// `headerCount: true`: the card draws the total as a chip after its title.\n\t\t\tif (this.content?.headerCount === true && !this.error) {\n\t\t\t\theader.count = this.total\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event header Emitted after every read with the card header's parts.\n\t\t\t * @type {{link: (object|null), note: string, count: (number|undefined)}} The header link (resolved, with `{total}` filled), the note, and with `headerCount` the total.\n\t\t\t */\n\t\t\tthis.$emit('header', header)\n\t\t},\n\n\t\t/**\n\t\t * The rank list of a sort (`{ field, rank: [\"urgent\", \"high\", \"normal\", \"low\"], then? }`),\n\t\t * or null when the sort has none or the list's own filter already fixes the field.\n\t\t *\n\t\t * @param {object|null} sort The effective sort.\n\t\t * @return {Array<string>|null} The values in rank order.\n\t\t * @spec openspec/changes/screens-enum-rank-sort/specs/enum-rank-sort/spec.md#requirement-a-record-list-can-sort-by-the-rank-of-an-enum-value\n\t\t */\n\t\trankOf(sort) {\n\t\t\tif (!sort || typeof sort !== 'object' || !sort.field || !Array.isArray(sort.rank)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst rank = sort.rank.filter((value) => typeof value === 'string' && value !== '')\n\t\t\tif (rank.length === 0 || Object.hasOwn(this.resolvedFilter || {}, sort.field)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn rank\n\t\t},\n\n\t\t/**\n\t\t * Read a list in the rank of an enum field. The list endpoint orders a\n\t\t * text field alphabetically (\"high\", \"low\", \"normal\", \"urgent\"), so the\n\t\t * rows are read one rank at a time, best first, each with the rest of the\n\t\t * sort (`then`) inside it, until the page is full; the rows with a value\n\t\t * outside the rank (or none) follow. The result is exact, not a client-side\n\t\t * sort of a partial page. It costs one request for the total, one per rank\n\t\t * value read and one for the remainder.\n\t\t *\n\t\t * @param {(params: object) => Promise<object>} get Reads the list with the given params.\n\t\t * @param {object} base The params without any order.\n\t\t * @param {{field: string, then?: {field: string, dir?: string}}} sort The sort.\n\t\t * @param {Array<string>} rank The values in rank order.\n\t\t * @return {Promise<{rows: Array<object>, total: number}>} The page and the total.\n\t\t * @spec openspec/changes/screens-enum-rank-sort/specs/enum-rank-sort/spec.md#requirement-a-record-list-can-sort-by-the-rank-of-an-enum-value\n\t\t */\n\t\tasync fetchRanked(get, base, sort, rank) {\n\t\t\tconst order = {}\n\t\t\tif (sort.then && typeof sort.then === 'object' && sort.then.field) {\n\t\t\t\torder[`_order[${sort.then.field}]`] = sort.then.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t}\n\t\t\tconst read = async (extra) => {\n\t\t\t\tconst response = await get({ ...base, ...order, ...extra })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\treturn { results: Array.isArray(data.results) ? data.results : [], total: data.total }\n\t\t\t}\n\t\t\tconst head = await read({ _limit: 1 })\n\t\t\tconst rows = []\n\t\t\tconst seen = new Set()\n\t\t\tconst idOf = (row) => (row && (row.id || row['@self']?.id)) || null\n\t\t\tconst take = (list) => {\n\t\t\t\tfor (const row of list) {\n\t\t\t\t\tconst id = idOf(row)\n\t\t\t\t\tif (id !== null && seen.has(id)) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (id !== null) {\n\t\t\t\t\t\tseen.add(id)\n\t\t\t\t\t}\n\t\t\t\t\tif (rows.length < this.limit) {\n\t\t\t\t\t\trows.push(row)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const value of rank) {\n\t\t\t\tif (rows.length >= this.limit) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\ttake((await read({ [sort.field]: value, _limit: this.limit - rows.length })).results)\n\t\t\t}\n\t\t\tif (rows.length < this.limit) {\n\t\t\t\ttake((await read({ _limit: this.limit + rows.length })).results)\n\t\t\t}\n\t\t\treturn { rows, total: typeof head.total === 'number' ? head.total : rows.length }\n\t\t},\n\n\t\t/**\n\t\t * Fetch the list. Sends nothing while a required token is unresolved\n\t\t * or the source is incomplete.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRows() {\n\t\t\tconst c = this.content || {}\n\t\t\tif (!c.register || !c.schema || this.waiting) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = false\n\t\t\t\tthis.emitHeader()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\tconst run = this.fetchRun = (this.fetchRun || 0) + 1\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })\n\t\t\t\tconst sort = this.effectiveSort\n\t\t\t\tconst params = { _limit: this.limit }\n\t\t\t\t// A rank sort (`sort.rank`) cannot be one `_order`: the list endpoint\n\t\t\t\t// orders a text field alphabetically. It is read group by group below.\n\t\t\t\tconst rank = this.rankOf(sort)\n\t\t\t\tif (!rank && sort && typeof sort === 'object' && sort.field) {\n\t\t\t\t\tparams[`_order[${sort.field}]`] = sort.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(c.extend)) {\n\t\t\t\t\tconst extend = c.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t\t\tif (extend.length > 0) {\n\t\t\t\t\t\tparams._extend = extend\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, value] of Object.entries(this.resolvedFilter)) {\n\t\t\t\t\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\t\tfor (const [op, operand] of Object.entries(value)) {\n\t\t\t\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value !== '' && value !== null && value !== undefined) {\n\t\t\t\t\t\tparams[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (rank) {\n\t\t\t\t\t// Several requests: a fetch started later (the sort changed\n\t\t\t\t\t// meanwhile) must not be overwritten by this one.\n\t\t\t\t\tconst ranked = await this.fetchRanked((query) => axios.get(url, { params: query }), params, sort, rank)\n\t\t\t\t\tif (run !== this.fetchRun) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.rows = ranked.rows\n\t\t\t\t\tthis.total = ranked.total\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tthis.rows = Array.isArray(data.results) ? data.results : []\n\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.rows.length\n\t\t\t\tthis.lenses = readLensReports(data)\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnRecordListWidget] failed to load objects:', e)\n\t\t\t\tthis.lenses = {}\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.emitHeader()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-list__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-record-list__empty,\n.cn-record-list__error {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-record-list__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n/* rows */\n.cn-record-list--rows .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__row {\n\talign-items: center;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tgap: 16px;\n\tpadding: 12px 0;\n\ttext-decoration: none;\n}\n\n.cn-record-list__row--link:hover .cn-record-list__title,\n.cn-record-list__row--link:focus-visible .cn-record-list__title {\n\ttext-decoration: underline;\n}\n\n.cn-record-list__avatar {\n\talign-items: center;\n\tbackground: var(--color-background-hover);\n\tborder-radius: 50%;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: none;\n\tfont-size: 12px;\n\tfont-weight: 700;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__main {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-record-list__title {\n\tfont-size: 15px;\n\tfont-weight: 700;\n}\n\n.cn-record-list__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-list__aside {\n\talign-items: center;\n\tdisplay: flex;\n\tflex: none;\n\tgap: 16px;\n}\n\n.cn-record-list__trailing {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__due {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__due--danger {\n\tcolor: var(--color-text-error, var(--color-error-text));\n\tfont-weight: 600;\n}\n\n.cn-record-list__due--empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-record-list__row--action {\n\tflex-wrap: wrap;\n\tgap: 8px 16px;\n\tpadding: 14px 0;\n}\n\n.cn-record-list__row--action .cn-record-list__main {\n\tflex: 1 1 280px;\n\tgap: 3px;\n}\n\n.cn-record-list__title--link {\n\tcolor: var(--color-main-text);\n\ttext-decoration: underline;\n}\n\n.cn-record-list__pills {\n\tdisplay: flex;\n\tflex: 0 0 120px;\n\tgap: 6px;\n\tjustify-content: flex-end;\n}\n\n.cn-record-list__due--column {\n\tflex: 0 0 96px;\n\tfont-size: 13px;\n\ttext-align: end;\n}\n\n.cn-record-list__action {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder: 0;\n\tborder-radius: 17px;\n\tbox-sizing: border-box;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tcursor: pointer;\n\tdisplay: inline-flex;\n\tflex: 0 0 150px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\theight: 34px;\n\tjustify-content: center;\n\tpadding: 0 14px;\n\ttext-decoration: none;\n}\n\n.cn-record-list__action:hover,\n.cn-record-list__action:focus-visible {\n\tbackground: var(--color-primary-element-light-hover, var(--color-primary-element-light));\n}\n\n.cn-record-list__chevron {\n\tcolor: var(--color-text-maxcontrast);\n\tflex: none;\n}\n\n/* feed */\n.cn-record-list--feed .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__feed-row {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tgap: 14px;\n\tpadding: 12px 0;\n}\n\n.cn-record-list__feed-icon {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder-radius: 50%;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tdisplay: flex;\n\tflex: none;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__text {\n\tfont-size: 15px;\n}\n\n.cn-record-list--feed .cn-record-list__main {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n/* inline */\n.cn-record-list__inline-row {\n\tpadding: 4px 0;\n}\n\n.cn-record-list__label {\n\tmargin-inline-end: 4px;\n}\n\n.cn-record-list__inline-title {\n\tcolor: var(--color-main-text);\n}\n\na.cn-record-list__inline-title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n</style>\n\n<style>\n/* In a narrow side column a row stacks: title, meta, then pill and date. */\n.cn-detail-page__side .cn-record-list__row {\n\talign-items: flex-start;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-detail-page__side .cn-record-list__row .cn-record-list__title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-detail-page__side .cn-record-list__chevron {\n\tdisplay: none;\n}\n\n/* A row is a touch target of at least 44px on a phone. */\n@media (max-width: 640px) {\n\t.cn-record-list__row {\n\t\tmin-height: 44px;\n\t}\n}\n</style>\n"],"names":["_normalizeClass","_createElementBlock","_toDisplayString","_openBlock","_createVNode","_createBlock","_resolveDynamicComponent","_mergeProps","_createTextVNode","_Fragment","_renderList","_toHandlers","_createElementVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnRecordListWidget.vue3.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordListWidget.vue?vue&type=template&id=b2ef255e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-record-list\"\n\t\t:class=\"`cn-record-list--${presentation}`\"\n\t\tdata-testid=\"cn-record-list\">\n\t\t<p v-if=\"error\" class=\"cn-record-list__error\" data-testid=\"cn-record-list-error\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-record-list__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-record-list__empty\" data-testid=\"cn-record-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-record-list__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"item in items\"\n\t\t\t\t:key=\"item.key\"\n\t\t\t\tclass=\"cn-record-list__item\"\n\t\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t\t<!-- rows with `rowAction`: the title is the link, and one action\n\t\t\t\t ends the row instead of the chevron (\"Beoordelen\"). -->\n\t\t\t\t<div\n\t\t\t\t\tv-if=\"presentation === 'rows' && item.action\"\n\t\t\t\t\tclass=\"cn-record-list__row cn-record-list__row--action\">\n\t\t\t\t\t<span v-if=\"item.initials\"\n\t\t\t\t\t\tclass=\"cn-record-list__avatar\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-avatar\">{{ item.initials }}</span>\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\t\t\tv-if=\"item.title\"\n\t\t\t\t\t\t\tclass=\"cn-record-list__title\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-record-list__title--link': item.tag !== 'div' }\"\n\t\t\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t\t</component>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.badges.length > 0\" class=\"cn-record-list__pills\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-for=\"badge in item.badges\"\n\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t:label=\"badge.label\"\n\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-record-list-badge-${badge.kind}`\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"item.due\"\n\t\t\t\t\t\tclass=\"cn-record-list__due cn-record-list__due--column\"\n\t\t\t\t\t\t:class=\"`cn-record-list__due--${item.due.tone}`\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-due\">{{ item.due.text }}</span>\n\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"item.action.tag\"\n\t\t\t\t\t\tclass=\"cn-record-list__action\"\n\t\t\t\t\t\tv-bind=\"item.action.attrs\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-action\"\n\t\t\t\t\t\tv-on=\"item.action.tag === 'button' ? { click: () => runRowAction(item.row) } : {}\">\n\t\t\t\t\t\t{{ item.action.label }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t\t<!-- rows: title and meta, then the pill, the date and a chevron. -->\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-else-if=\"presentation === 'rows'\"\n\t\t\t\t\tclass=\"cn-record-list__row\"\n\t\t\t\t\t:class=\"{ 'cn-record-list__row--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t<span v-if=\"item.initials\"\n\t\t\t\t\t\tclass=\"cn-record-list__avatar\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tdata-testid=\"cn-record-list-avatar\">{{ item.initials }}</span>\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<span v-if=\"item.title\" class=\"cn-record-list__title\">{{ item.title }}</span>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.badges.length > 0 || item.due || item.trailing\" class=\"cn-record-list__aside\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-for=\"badge in item.badges\"\n\t\t\t\t\t\t\t:key=\"badge.key\"\n\t\t\t\t\t\t\t:label=\"badge.label\"\n\t\t\t\t\t\t\t:variant=\"badge.variant\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-record-list-badge-${badge.kind}`\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"item.due\"\n\t\t\t\t\t\t\tclass=\"cn-record-list__due\"\n\t\t\t\t\t\t\t:class=\"`cn-record-list__due--${item.due.tone}`\"\n\t\t\t\t\t\t\tdata-testid=\"cn-record-list-due\">{{ item.due.text }}</span>\n\t\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ChevronRight\n\t\t\t\t\t\tv-if=\"item.tag !== 'div'\"\n\t\t\t\t\t\tclass=\"cn-record-list__chevron\"\n\t\t\t\t\t\t:size=\"18\"\n\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t</component>\n\n\t\t\t\t<!-- feed: a round icon, a meta line and the text. -->\n\t\t\t\t<div v-else-if=\"presentation === 'feed'\" class=\"cn-record-list__feed-row\">\n\t\t\t\t\t<span class=\"cn-record-list__feed-icon\" aria-hidden=\"true\">\n\t\t\t\t\t\t<CnIcon :name=\"item.icon\" :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__main\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t<span v-if=\"item.text\" class=\"cn-record-list__text\">{{ item.text }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- inline: a label and the title, the title a link. -->\n\t\t\t\t<div v-else class=\"cn-record-list__inline-row\">\n\t\t\t\t\t<span v-if=\"item.label\" class=\"cn-record-list__label\">{{ item.label }}</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__inline-title\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { inject, ref } from 'vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { actionLink, dispatchAction, interpolateActionTarget } from '../../utils/actionsDispatcher.js'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { ageInDays } from '../../utils/boardDate.js'\nimport { resolveDue } from '../../utils/dueText.js'\nimport { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'\nimport { unwrapAppConfig } from '../../utils/formatMetric.js'\nimport { initialsOf } from '../../utils/initials.js'\nimport { lensUnavailableText, readLensReports } from '../../utils/lensAvailability.js'\nimport { pageFilter, pageSort } from '../../utils/pageControls.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\nconst PRESENTATIONS = ['rows', 'feed', 'inline']\n\n/**\n * Unwrap a value that may be a ref.\n *\n * @param {unknown} value The value.\n * @return {unknown} The plain value.\n */\nfunction unwrap(value) {\n\treturn value && typeof value === 'object' && 'value' in value ? value.value : value\n}\n\n/**\n * CnRecordListWidget — the objects of another schema that point at this\n * record, drawn as the boards draw them (screens-relation-pages).\n *\n * A person page lists its open cases (\"Verlichting fietspad Lindelaan,\n * 2026-0082 · Woo-verzoek, In behandeling, Termijn 1 nov\"), its latest contact\n * moments (an icon by channel, a meta line, the summary) and its relations\n * (\"Secretaris van Buurtvereniging Oost\"). Each is a list of another schema's\n * objects filtered on a reference to this record. The source keys are those of\n * `object-list` (`register`, `schema`, `filter` with the `@objectId` /\n * `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,\n * `rowRoute`, `pageControls`), or `endpointSource` (an app endpoint whose\n * payload, after `responsePath`, is the array of rows, as on the object table); `avatar` puts a circle with the initials of a name\n * template before the title; what differs is the drawing: `presentation` is `rows`, `feed`\n * or `inline`, and each line is a template (`\"{identifier} · {caseTypeName}\"`,\n * `\"Termijn {deadline|date}\"`).\n *\n * A content-only catalog type: on a card surface the detail host or the\n * dashboard page draws the titled card. The widget emits `header` with `{ link, note }` (the\n * `headerLink` and `headerNote` with `{total}` filled in) for that card's\n * header.\n */\nexport default {\n\tname: 'CnRecordListWidget',\n\n\tcomponents: { ChevronRight, CnIcon, CnStatusBadge, NcLoadingIcon },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** Page workspace bag, for `@workspace.<key>` tokens. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host app's translate function. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's app config map; its `currency` is the reporting currency of a money token. */\n\t\tcnAppConfig: { default: () => ({}) },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `rowAction`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list config: `{ register, schema, filter?, sort?, limit?, extend?,\n\t\t * presentation?, title?, meta?, text?, label?, pill?, trailing?, icon?,\n\t\t * rowRoute?, rowIdField?, headerLink?, headerNote?, emptyText?, errorText?,\n\t\t * valueLabels?, endpointSource? }`. `endpointSource` (`{ url, method?,\n\t\t * params?, responsePath? }`) reads the rows from an app endpoint instead\n\t\t * of `register` / `schema`. `valueLabels` maps a field's stored values to the labels\n\t\t * the templates show.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current record, for `@object.<field>` tokens. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tsetup(props) {\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst workspaceRaw = inject('cnWorkspaceContext', null)\n\t\tconst appConfigRaw = inject('cnAppConfig', ref({}))\n\t\tconst { data, loading, error } = useEndpointSource(\n\t\t\t() => props.content?.endpointSource || null,\n\t\t\t{\n\t\t\t\tctx: () => {\n\t\t\t\t\tconst injected = unwrap(objectCtxRaw) || {}\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...injected,\n\t\t\t\t\t\tobjectId: props.objectId ?? injected.objectId ?? props.objectData?.id ?? null,\n\t\t\t\t\t\tobject: props.objectData || injected.object || null,\n\t\t\t\t\t\tworkspace: unwrap(workspaceRaw) || {},\n\t\t\t\t\t\tconfig: unwrapAppConfig(appConfigRaw) || {},\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\treturn { epData: data, epLoading: loading, epError: error }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\ttotal: 0,\n\t\t\tloading: false,\n\t\t\terror: false,\n\t\t\t/** Personal-lens reports (`@self.lenses`) of the latest response. */\n\t\t\tlenses: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} `rows`, `feed` or `inline`. */\n\t\tpresentation() {\n\t\t\tconst value = this.content?.presentation\n\t\t\treturn PRESENTATIONS.includes(value) ? value : 'rows'\n\t\t},\n\n\t\t/** @return {object} The token context: record, id and workspace. */\n\t\ttokenCtx() {\n\t\t\tconst injected = unwrap(this.cnObjectContext) || {}\n\t\t\treturn {\n\t\t\t\t...injected,\n\t\t\t\tobjectId: this.objectId ?? injected.objectId ?? this.objectData?.id ?? null,\n\t\t\t\tobject: this.objectData || injected.object || null,\n\t\t\t\tworkspace: unwrap(this.cnWorkspaceContext) || {},\n\t\t\t}\n\t\t},\n\n\t\t/** @return {object} The filter with its tokens resolved. */\n\t\tresolvedFilter() {\n\t\t\tconst filter = pageFilter(this.content?.pageControls, this.tokenCtx.workspace, this.content?.filter)\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))\n\t\t},\n\n\t\t/** @return {boolean} A required token (the record id, say) has no value yet. */\n\t\twaiting() {\n\t\t\treturn hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/** @return {object|null} The sort: the page's sort control when the widget listens to one, else its own. */\n\t\teffectiveSort() {\n\t\t\treturn pageSort(this.content?.pageControls, this.tokenCtx.workspace, this.content?.sort)\n\t\t},\n\n\t\t/**\n\t\t * What a template may read beside the row: the app config, whose\n\t\t * `currency` is the reporting currency of a `{path|currency}` token.\n\t\t *\n\t\t * @return {{config: object}} The template context.\n\t\t * @spec openspec/changes/screens-currency-format/specs/currency-format/spec.md#requirement-a-money-value-reads-in-its-currency\n\t\t */\n\t\ttemplateContext() {\n\t\t\treturn { config: unwrapAppConfig(this.cnAppConfig) }\n\t\t},\n\n\t\t/**\n\t\t * Whether the rows come from `endpointSource` rather than OpenRegister.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/endpoint-source-card-grid-record-list/specs/dashboard-page/spec.md#requirement-a-record-list-and-a-card-grid-can-read-an-endpoint\n\t\t */\n\t\tendpointActive() {\n\t\t\tconst source = this.content?.endpointSource\n\t\t\treturn Boolean(source && typeof source === 'object' && source.url)\n\t\t},\n\n\t\t/** @return {string} The endpoint read's state, to copy into the list when it changes. */\n\t\tendpointKey() {\n\t\t\treturn this.endpointActive ? JSON.stringify([this.epData, this.epLoading, this.epError]) : ''\n\t\t},\n\n\t\t/** @return {number} The fetch cap. */\n\t\tlimit() {\n\t\t\tconst value = Number(this.content?.limit)\n\t\t\treturn Number.isFinite(value) && value > 0 ? Math.floor(value) : 5\n\t\t},\n\n\t\t/** @return {string} What a fetch depends on; a change fetches again. */\n\t\trequestKey() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn JSON.stringify([c.register, c.schema, this.resolvedFilter, this.effectiveSort, this.limit, c.extend, this.waiting])\n\t\t},\n\n\t\t/** @return {Array<object>} The rows ready to draw. */\n\t\titems() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn this.rows.slice(0, this.limit).map((row, index) => {\n\t\t\t\tconst link = this.rowLink(row)\n\t\t\t\treturn {\n\t\t\t\t\tkey: (row && (row.id || row['@self']?.id)) || `row-${index}`,\n\t\t\t\t\trow,\n\t\t\t\t\taction: this.rowActionOf(row),\n\t\t\t\t\ttag: link ? 'router-link' : 'div',\n\t\t\t\t\tattrs: link ? { to: link } : {},\n\t\t\t\t\tinitials: this.initialsFor(row),\n\t\t\t\t\ttitle: this.fill(c.title, row),\n\t\t\t\t\tmeta: this.metaOf(row),\n\t\t\t\t\ttext: this.fill(c.text, row),\n\t\t\t\t\tlabel: this.fill(c.label, row),\n\t\t\t\t\ttrailing: c.trailing ? firstFilled(c.trailing, row, this.translate, new Date(), this.templateContext) : '',\n\t\t\t\t\tpill: this.pillOf(row),\n\t\t\t\t\tbadges: this.badgesOf(row),\n\t\t\t\t\tdue: this.dueOf(row),\n\t\t\t\t\ticon: this.iconOf(row),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {object|null} The header link with `{total}` filled and tokens resolved. */\n\t\theaderLink() {\n\t\t\tconst link = this.content?.headerLink\n\t\t\tif (!link || typeof link !== 'object' || typeof link.label !== 'string' || link.label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = { label: this.withTotal(link.label) }\n\t\t\tif (link.route) {\n\t\t\t\tresolved.route = link.route\n\t\t\t\tif (link.params && typeof link.params === 'object') {\n\t\t\t\t\tresolved.params = resolveFilterTokens(link.params, this.tokenCtx)\n\t\t\t\t}\n\t\t\t\tif (link.query && typeof link.query === 'object') {\n\t\t\t\t\tresolved.query = resolveFilterTokens(link.query, this.tokenCtx)\n\t\t\t\t}\n\t\t\t} else if (typeof link.href === 'string') {\n\t\t\t\tresolved.href = link.href\n\t\t\t}\n\t\t\treturn resolved\n\t\t},\n\n\t\t/** @return {string} The header note with `{total}` filled. */\n\t\theaderNote() {\n\t\t\tconst note = this.content?.headerNote\n\t\t\treturn typeof note === 'string' && note !== '' ? this.withTotal(note) : ''\n\t\t},\n\n\t\t/**\n\t\t * Why the list is empty when a personal lens could not answer\n\t\t * (`@self.lenses.<lens>.available: false`, as for `filter: { _recent: true }`),\n\t\t * or '' to keep `emptyText`. `lensReasonTexts` holds the app's wording,\n\t\t * keyed `<lens>.<reason>` or `<reason>`.\n\t\t *\n\t\t * @return {string} The explanation, or ''.\n\t\t * @spec openspec/changes/screens-record-list-lens-reason/specs/record-list-lens-reason/spec.md#requirement-a-record-list-says-why-a-lens-is-empty\n\t\t */\n\t\tlensReasonText() {\n\t\t\tif (this.rows.length > 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst text = lensUnavailableText(this.lenses, this.content?.lensReasonTexts)\n\t\t\treturn text ? this.translate(text) : ''\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyLabel() {\n\t\t\tif (this.lensReasonText !== '') {\n\t\t\t\treturn this.lensReasonText\n\t\t\t}\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'Nothing here yet')\n\t\t},\n\n\t\t/** @return {string} The error line. */\n\t\terrorLabel() {\n\t\t\treturn this.content?.errorText ? this.translate(this.content.errorText) : t('nextcloud-vue', 'This list could not be loaded.')\n\t\t},\n\n\t\t/** @return {string} The spinner's accessible name. */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\t},\n\n\twatch: {\n\t\trequestKey() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tendpointKey() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.onRefresh = () => this.fetchRows()\n\t\tsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t\tthis.fetchRows()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the host app.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The initials of the row's avatar (`avatar`: a name template such as\n\t\t * `\"{client.name}\"`, or `{ text }`), or '' without one.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} One or two letters.\n\t\t * @spec openspec/changes/screens-record-list-avatar/specs/record-list-avatar/spec.md#requirement-a-record-list-row-can-carry-an-avatar\n\t\t */\n\t\tinitialsFor(row) {\n\t\t\tconst avatar = this.content?.avatar\n\t\t\tconst template = typeof avatar === 'string' ? avatar : (avatar && typeof avatar === 'object' ? avatar.text : '')\n\t\t\treturn initialsOf(this.fill(template, row))\n\t\t},\n\n\t\t/**\n\t\t * Fill a template from a row, after translating it.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @param {object} row The row.\n\t\t * @return {string} The text, '' when a token is empty.\n\t\t */\n\t\tfill(template, row) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.translate(template), this.labelledRow(row), new Date(), this.templateContext) : ''\n\t\t},\n\n\t\t/**\n\t\t * The row as the templates read it: a field named in `valueLabels`\n\t\t * (`{ channel: { phone: \"Phone\", telefoon: \"Phone\" } }`) shows the\n\t\t * translated label of its stored value, so \"{channel}\" reads \"Telefoon\"\n\t\t * for both spellings the data holds. A value without a label stays as\n\t\t * stored. The pill, the badges and the sort read the stored value.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object} The row, or a shallow copy with labelled fields.\n\t\t * @spec openspec/changes/screens-record-list-value-labels/specs/record-list-value-labels/spec.md#requirement-a-record-list-template-can-show-a-label-for-a-stored-value\n\t\t */\n\t\tlabelledRow(row) {\n\t\t\tconst map = this.content?.valueLabels\n\t\t\tif (!row || typeof row !== 'object' || !map || typeof map !== 'object') {\n\t\t\t\treturn row\n\t\t\t}\n\t\t\tlet out = row\n\t\t\tfor (const [field, labels] of Object.entries(map)) {\n\t\t\t\tconst raw = row[field]\n\t\t\t\tif (!labels || typeof labels !== 'object' || (typeof raw !== 'string' && typeof raw !== 'number')) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst label = labels[String(raw)]\n\t\t\t\tif (typeof label === 'string' && label !== '') {\n\t\t\t\t\tif (out === row) {\n\t\t\t\t\t\tout = { ...row }\n\t\t\t\t\t}\n\t\t\t\t\tout[field] = this.translate(label)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Replace `{total}` in a translated label.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The text.\n\t\t */\n\t\twithTotal(label) {\n\t\t\treturn this.translate(label).replace(/\\{total\\}/g, String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * The route a row opens: `rowRoute` with the id at `rowIdField`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object|null} The router location, or null.\n\t\t */\n\t\t/**\n\t\t * The one action that ends a row (`rowAction`): its label (from\n\t\t * `labels.map` by the row's `labels.field`, else `label`), and how it\n\t\t * renders: a link when it only navigates, else a button that\n\t\t * dispatches. Null without the key or a label.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{label: string, tag: string, attrs: object}|null}\n\t\t * @spec openspec/changes/screens-record-list-row-action/specs/record-list/spec.md\n\t\t */\n\t\trowActionOf(row) {\n\t\t\tconst action = this.content?.rowAction\n\t\t\tif (!action || typeof action !== 'object' || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tlet label = typeof action.label === 'string' ? action.label : ''\n\t\t\tconst by = action.labels\n\t\t\tif (by && typeof by === 'object' && typeof by.field === 'string' && by.map && typeof by.map === 'object') {\n\t\t\t\tconst raw = readPath(row, by.field)\n\t\t\t\tif (raw !== null && raw !== undefined && typeof by.map[String(raw)] === 'string') {\n\t\t\t\t\tlabel = by.map[String(raw)]\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tlabel = this.translate(label)\n\t\t\tconst link = this.rowActionLink(action, row)\n\t\t\tif (link) {\n\t\t\t\treturn link.external\n\t\t\t\t\t? { label, tag: 'a', attrs: { href: link.href, target: '_blank', rel: 'noopener' } }\n\t\t\t\t\t: { label, tag: 'router-link', attrs: { to: link.to } }\n\t\t\t}\n\t\t\treturn { label, tag: 'button', attrs: { type: 'button' } }\n\t\t},\n\n\t\t/**\n\t\t * The link a `rowAction` that only navigates renders as: `open-page`\n\t\t * opens that page for the row's id, `navigate` reads its\n\t\t * `@object.<field>` / `@objectId` tokens off the row. Null for any\n\t\t * other action, which is dispatched on click.\n\t\t *\n\t\t * @param {object} action The declared row action.\n\t\t * @param {object} row The row.\n\t\t * @return {{href: string, to: (string|object|null), external: boolean}|null}\n\t\t * @spec openspec/changes/screens-record-list-row-action/specs/record-list/spec.md\n\t\t */\n\t\trowActionLink(action, row) {\n\t\t\tif (action.confirm) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (action.type === 'open-page') {\n\t\t\t\tconst id = this.rowIdOf(row)\n\t\t\t\tif (!this.$router || id === null || typeof action.target !== 'string' || action.target === '') {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst to = { name: action.target, params: { id } }\n\t\t\t\ttry {\n\t\t\t\t\tconst href = this.$router.resolve(to).href\n\t\t\t\t\treturn href ? { href, to, external: false } : null\n\t\t\t\t} catch {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (action.type !== 'navigate') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst target = interpolateActionTarget(action.target, this.rowTokenCtx(row))\n\t\t\t// An external target needs no router; an in-app one does.\n\t\t\treturn actionLink({ ...action, target }, { router: this.$router || null })\n\t\t},\n\n\t\t/**\n\t\t * The token context of a row: the row is `@object`, its id `@objectId`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object} The token context.\n\t\t */\n\t\trowTokenCtx(row) {\n\t\t\treturn { ...this.tokenCtx, objectId: this.rowIdOf(row), object: row }\n\t\t},\n\n\t\t/**\n\t\t * The id of a row (`rowIdField`, else `id`, else `@self.id`), or null.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string|null} The id.\n\t\t */\n\t\trowIdOf(row) {\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row && (row.id || row['@self']?.id))\n\t\t\treturn (id === null || id === undefined || id === '' || typeof id === 'object') ? null : String(id)\n\t\t},\n\n\t\t/**\n\t\t * Run a `rowAction` that is not a plain link through the page's\n\t\t * dispatcher: a `handler` gets the row as its last argument, an\n\t\t * `open-modal` gets it as the `row` prop, an `object-op` acts on it.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {unknown} What the dispatcher returns.\n\t\t * @spec openspec/changes/screens-record-list-row-action/specs/record-list/spec.md\n\t\t */\n\t\trunRowAction(row) {\n\t\t\tconst action = this.content?.rowAction\n\t\t\tif (!action || typeof action !== 'object') {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst type = action.type || 'handler'\n\t\t\tlet wrapped = action\n\t\t\tif (type === 'handler') {\n\t\t\t\twrapped = { ...action, args: [...(action.args || []), row] }\n\t\t\t} else if (type === 'open-modal') {\n\t\t\t\twrapped = { ...action, props: { ...(action.props || {}), row } }\n\t\t\t}\n\t\t\tif (typeof this.cnDispatchAction === 'function') {\n\t\t\t\treturn this.cnDispatchAction(wrapped)\n\t\t\t}\n\t\t\treturn dispatchAction(wrapped, { router: this.$router || null, row, tokenCtx: this.rowTokenCtx(row), source: { register: this.content?.register, schema: this.content?.schema } })\n\t\t},\n\n\t\trowLink(row) {\n\t\t\tconst route = this.content?.rowRoute\n\t\t\tif (!route || !this.$router || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row.id || row['@self']?.id)\n\t\t\tif (id === null || id === undefined || id === '' || typeof id === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { name: route, params: { id: String(id) } }\n\t\t},\n\n\t\t/**\n\t\t * The row's due text (`due`: `{ field, todayLabel?, overdueLabel?, emptyText? }`):\n\t\t * \"3 dagen te laat\" in the danger tone, \"Vandaag\", the date otherwise,\n\t\t * `emptyText` when the field is empty.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{text: string, tone: string}|null} The text and tone, or null.\n\t\t * @spec openspec/changes/screens-due-text/specs/due-text/spec.md#requirement-a-due-date-reads-as-text-with-a-tone\n\t\t */\n\t\tdueOf(row) {\n\t\t\tconst due = this.content?.due\n\t\t\tif (!due || typeof due !== 'object' || !due.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn resolveDue(readPath(row, due.field), due, (text) => this.translate(text))\n\t\t},\n\n\t\t/**\n\t\t * The row's extra badges (`badges`): a coloured badge from a field\n\t\t * (`{ field, labels?, colorMap?, variant?, hideValues? }`, the value\n\t\t * shown unless `hideValues` lists it) and an age badge\n\t\t * (`{ kind: \"stale\", field, afterDays, label?, variant? }`, shown when the\n\t\t * date field is older than `afterDays` whole days; `{days}` in the\n\t\t * label is the age).\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {Array<{key: string, kind: string, label: string, variant: string}>} The badges that apply.\n\t\t * @spec openspec/changes/screens-record-list-badges/specs/record-list-badges/spec.md#requirement-a-record-list-row-can-carry-badges\n\t\t */\n\t\tbadgesOf(row) {\n\t\t\tconst declared = Array.isArray(this.content?.badges) ? this.content.badges : []\n\t\t\tconst out = []\n\t\t\tdeclared.forEach((badge, index) => {\n\t\t\t\tif (!badge || typeof badge !== 'object' || !badge.field) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif (badge.kind === 'stale') {\n\t\t\t\t\tconst days = ageInDays(readPath(row, badge.field))\n\t\t\t\t\tconst after = Number(badge.afterDays)\n\t\t\t\t\tif (days === null || !Number.isFinite(after) || days <= after) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tconst template = this.translate(badge.label || 'Stale')\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tkey: `stale-${index}`,\n\t\t\t\t\t\tkind: 'stale',\n\t\t\t\t\t\tlabel: template.replace(/\\{days\\}/g, String(days)),\n\t\t\t\t\t\tvariant: isBadgeVariant(badge.variant) ? badge.variant : 'warning',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst made = this.pillOf(row, badge)\n\t\t\t\tif (made) {\n\t\t\t\t\tout.push({ key: `field-${index}`, kind: 'field', ...made })\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * The row's meta line. A string is one template, and one empty token\n\t\t * empties it. `{ parts: [\"{identifier}\", \"{caseTypeName}\", \"{handler}\"],\n\t\t * separator? }` fills each part on its own, drops the empty ones and\n\t\t * joins the rest (default \" · \"), so a row without a handler still\n\t\t * reads \"2026-0082 · Woo-verzoek\".\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The meta line, '' when nothing fills.\n\t\t * @spec openspec/changes/screens-record-list-meta-parts/specs/record-list-meta-parts/spec.md#requirement-a-meta-line-of-parts-skips-the-empty-ones\n\t\t */\n\t\tmetaOf(row) {\n\t\t\tconst meta = this.content?.meta\n\t\t\tif (!meta || typeof meta !== 'object' || !Array.isArray(meta.parts)) {\n\t\t\t\treturn this.fill(meta, row)\n\t\t\t}\n\t\t\tconst separator = typeof meta.separator === 'string' ? meta.separator : ' · '\n\t\t\treturn meta.parts\n\t\t\t\t.map((part) => this.fill(part, row))\n\t\t\t\t.filter((text) => text !== '')\n\t\t\t\t.join(separator)\n\t\t},\n\n\t\t/**\n\t\t * The row's pill: `{ field, labels?, colorMap?, variant?, hideValues? }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {object} [config] A pill config; the widget's `pill` by default.\n\t\t * @return {{label: string, variant: string}|null} The pill, or null.\n\t\t */\n\t\tpillOf(row, config) {\n\t\t\tconst pill = config || this.content?.pill\n\t\t\tif (!pill || typeof pill !== 'object' || !pill.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(row, pill.field)\n\t\t\tif (raw === null || raw === undefined || raw === '' || typeof raw === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst key = String(raw)\n\t\t\tif (Array.isArray(pill.hideValues) && pill.hideValues.map(String).includes(key)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst labels = pill.labels && typeof pill.labels === 'object' ? pill.labels : {}\n\t\t\tconst map = pill.colorMap && typeof pill.colorMap === 'object' ? pill.colorMap : {}\n\t\t\tconst found = Object.keys(map).find((k) => k.toLowerCase() === key.toLowerCase())\n\t\t\tconst variant = found ? map[found] : pill.variant\n\t\t\treturn {\n\t\t\t\tlabel: labels[key] ? this.translate(labels[key]) : key,\n\t\t\t\tvariant: isBadgeVariant(variant) ? variant : 'default',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The feed icon: an icon name, or `{ field, map, default }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The MDI icon name.\n\t\t */\n\t\ticonOf(row) {\n\t\t\tconst icon = this.content?.icon\n\t\t\tif (typeof icon === 'string' && icon !== '') {\n\t\t\t\treturn icon\n\t\t\t}\n\t\t\tif (icon && typeof icon === 'object') {\n\t\t\t\tconst raw = icon.field ? readPath(row, icon.field) : undefined\n\t\t\t\tconst map = icon.map && typeof icon.map === 'object' ? icon.map : {}\n\t\t\t\tif (raw !== null && raw !== undefined && map[String(raw)]) {\n\t\t\t\t\treturn map[String(raw)]\n\t\t\t\t}\n\t\t\t\tif (typeof icon.default === 'string' && icon.default !== '') {\n\t\t\t\t\treturn icon.default\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 'CircleSmall'\n\t\t},\n\n\t\t/** Tell the card around this list what its header shows. */\n\t\temitHeader() {\n\t\t\tconst header = { link: this.headerLink, note: this.headerNote }\n\t\t\t// `headerCount: true`: the card draws the total as a chip after its title.\n\t\t\tif (this.content?.headerCount === true && !this.error) {\n\t\t\t\theader.count = this.total\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event header Emitted after every read with the card header's parts.\n\t\t\t * @type {{link: (object|null), note: string, count: (number|undefined)}} The header link (resolved, with `{total}` filled), the note, and with `headerCount` the total.\n\t\t\t */\n\t\t\tthis.$emit('header', header)\n\t\t},\n\n\t\t/**\n\t\t * The rank list of a sort (`{ field, rank: [\"urgent\", \"high\", \"normal\", \"low\"], then? }`),\n\t\t * or null when the sort has none or the list's own filter already fixes the field.\n\t\t *\n\t\t * @param {object|null} sort The effective sort.\n\t\t * @return {Array<string>|null} The values in rank order.\n\t\t * @spec openspec/changes/screens-enum-rank-sort/specs/enum-rank-sort/spec.md#requirement-a-record-list-can-sort-by-the-rank-of-an-enum-value\n\t\t */\n\t\trankOf(sort) {\n\t\t\tif (!sort || typeof sort !== 'object' || !sort.field || !Array.isArray(sort.rank)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst rank = sort.rank.filter((value) => typeof value === 'string' && value !== '')\n\t\t\tif (rank.length === 0 || Object.hasOwn(this.resolvedFilter || {}, sort.field)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn rank\n\t\t},\n\n\t\t/**\n\t\t * Read a list in the rank of an enum field. The list endpoint orders a\n\t\t * text field alphabetically (\"high\", \"low\", \"normal\", \"urgent\"), so the\n\t\t * rows are read one rank at a time, best first, each with the rest of the\n\t\t * sort (`then`) inside it, until the page is full; the rows with a value\n\t\t * outside the rank (or none) follow. The result is exact, not a client-side\n\t\t * sort of a partial page. It costs one request for the total, one per rank\n\t\t * value read and one for the remainder.\n\t\t *\n\t\t * @param {(params: object) => Promise<object>} get Reads the list with the given params.\n\t\t * @param {object} base The params without any order.\n\t\t * @param {{field: string, then?: {field: string, dir?: string}}} sort The sort.\n\t\t * @param {Array<string>} rank The values in rank order.\n\t\t * @return {Promise<{rows: Array<object>, total: number}>} The page and the total.\n\t\t * @spec openspec/changes/screens-enum-rank-sort/specs/enum-rank-sort/spec.md#requirement-a-record-list-can-sort-by-the-rank-of-an-enum-value\n\t\t */\n\t\tasync fetchRanked(get, base, sort, rank) {\n\t\t\tconst order = {}\n\t\t\tif (sort.then && typeof sort.then === 'object' && sort.then.field) {\n\t\t\t\torder[`_order[${sort.then.field}]`] = sort.then.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t}\n\t\t\tconst read = async (extra) => {\n\t\t\t\tconst response = await get({ ...base, ...order, ...extra })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\treturn { results: Array.isArray(data.results) ? data.results : [], total: data.total }\n\t\t\t}\n\t\t\tconst head = await read({ _limit: 1 })\n\t\t\tconst rows = []\n\t\t\tconst seen = new Set()\n\t\t\tconst idOf = (row) => (row && (row.id || row['@self']?.id)) || null\n\t\t\tconst take = (list) => {\n\t\t\t\tfor (const row of list) {\n\t\t\t\t\tconst id = idOf(row)\n\t\t\t\t\tif (id !== null && seen.has(id)) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif (id !== null) {\n\t\t\t\t\t\tseen.add(id)\n\t\t\t\t\t}\n\t\t\t\t\tif (rows.length < this.limit) {\n\t\t\t\t\t\trows.push(row)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const value of rank) {\n\t\t\t\tif (rows.length >= this.limit) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\ttake((await read({ [sort.field]: value, _limit: this.limit - rows.length })).results)\n\t\t\t}\n\t\t\tif (rows.length < this.limit) {\n\t\t\t\ttake((await read({ _limit: this.limit + rows.length })).results)\n\t\t\t}\n\t\t\treturn { rows, total: typeof head.total === 'number' ? head.total : rows.length }\n\t\t},\n\n\t\t/**\n\t\t * Copy the endpoint read into the list: its payload must be an array of\n\t\t * rows (anything else is an empty list), and a failed read is an error.\n\t\t *\n\t\t * @spec openspec/changes/endpoint-source-card-grid-record-list/specs/dashboard-page/spec.md#requirement-a-record-list-and-a-card-grid-can-read-an-endpoint\n\t\t */\n\t\ttakeEndpointRows() {\n\t\t\tthis.rows = Array.isArray(this.epData) ? this.epData : []\n\t\t\tthis.total = this.rows.length\n\t\t\tthis.loading = this.epLoading === true\n\t\t\tthis.error = Boolean(this.epError)\n\t\t\tthis.lenses = {}\n\t\t\tif (!this.loading) {\n\t\t\t\tthis.emitHeader()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fetch the list. Sends nothing while a required token is unresolved\n\t\t * or the source is incomplete.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRows() {\n\t\t\tconst c = this.content || {}\n\t\t\tif (this.endpointActive) {\n\t\t\t\tthis.takeEndpointRows()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!c.register || !c.schema || this.waiting) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = false\n\t\t\t\tthis.emitHeader()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\tconst run = this.fetchRun = (this.fetchRun || 0) + 1\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })\n\t\t\t\tconst sort = this.effectiveSort\n\t\t\t\tconst params = { _limit: this.limit }\n\t\t\t\t// A rank sort (`sort.rank`) cannot be one `_order`: the list endpoint\n\t\t\t\t// orders a text field alphabetically. It is read group by group below.\n\t\t\t\tconst rank = this.rankOf(sort)\n\t\t\t\tif (!rank && sort && typeof sort === 'object' && sort.field) {\n\t\t\t\t\tparams[`_order[${sort.field}]`] = sort.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(c.extend)) {\n\t\t\t\t\tconst extend = c.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t\t\tif (extend.length > 0) {\n\t\t\t\t\t\tparams._extend = extend\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, value] of Object.entries(this.resolvedFilter)) {\n\t\t\t\t\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\t\tfor (const [op, operand] of Object.entries(value)) {\n\t\t\t\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value !== '' && value !== null && value !== undefined) {\n\t\t\t\t\t\tparams[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (rank) {\n\t\t\t\t\t// Several requests: a fetch started later (the sort changed\n\t\t\t\t\t// meanwhile) must not be overwritten by this one.\n\t\t\t\t\tconst ranked = await this.fetchRanked((query) => axios.get(url, { params: query }), params, sort, rank)\n\t\t\t\t\tif (run !== this.fetchRun) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.rows = ranked.rows\n\t\t\t\t\tthis.total = ranked.total\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tthis.rows = Array.isArray(data.results) ? data.results : []\n\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.rows.length\n\t\t\t\tthis.lenses = readLensReports(data)\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnRecordListWidget] failed to load objects:', e)\n\t\t\t\tthis.lenses = {}\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.emitHeader()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-list__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-record-list__empty,\n.cn-record-list__error {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-record-list__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n/* rows */\n.cn-record-list--rows .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__row {\n\talign-items: center;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tgap: 16px;\n\tpadding: 12px 0;\n\ttext-decoration: none;\n}\n\n.cn-record-list__row--link:hover .cn-record-list__title,\n.cn-record-list__row--link:focus-visible .cn-record-list__title {\n\ttext-decoration: underline;\n}\n\n.cn-record-list__avatar {\n\talign-items: center;\n\tbackground: var(--color-background-hover);\n\tborder-radius: 50%;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: none;\n\tfont-size: 12px;\n\tfont-weight: 700;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__main {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-record-list__title {\n\tfont-size: 15px;\n\tfont-weight: 700;\n}\n\n.cn-record-list__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-list__aside {\n\talign-items: center;\n\tdisplay: flex;\n\tflex: none;\n\tgap: 16px;\n}\n\n.cn-record-list__trailing {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__due {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__due--danger {\n\tcolor: var(--color-text-error, var(--color-error-text));\n\tfont-weight: 600;\n}\n\n.cn-record-list__due--empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-record-list__row--action {\n\tflex-wrap: wrap;\n\tgap: 8px 16px;\n\tpadding: 14px 0;\n}\n\n.cn-record-list__row--action .cn-record-list__main {\n\tflex: 1 1 280px;\n\tgap: 3px;\n}\n\n.cn-record-list__title--link {\n\tcolor: var(--color-main-text);\n\ttext-decoration: underline;\n}\n\n.cn-record-list__pills {\n\tdisplay: flex;\n\tflex: 0 0 120px;\n\tgap: 6px;\n\tjustify-content: flex-end;\n}\n\n.cn-record-list__due--column {\n\tflex: 0 0 96px;\n\tfont-size: 13px;\n\ttext-align: end;\n}\n\n.cn-record-list__action {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder: 0;\n\tborder-radius: 17px;\n\tbox-sizing: border-box;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tcursor: pointer;\n\tdisplay: inline-flex;\n\tflex: 0 0 150px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\theight: 34px;\n\tjustify-content: center;\n\tpadding: 0 14px;\n\ttext-decoration: none;\n}\n\n.cn-record-list__action:hover,\n.cn-record-list__action:focus-visible {\n\tbackground: var(--color-primary-element-light-hover, var(--color-primary-element-light));\n}\n\n.cn-record-list__chevron {\n\tcolor: var(--color-text-maxcontrast);\n\tflex: none;\n}\n\n/* feed */\n.cn-record-list--feed .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__feed-row {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tgap: 14px;\n\tpadding: 12px 0;\n}\n\n.cn-record-list__feed-icon {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder-radius: 50%;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tdisplay: flex;\n\tflex: none;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__text {\n\tfont-size: 15px;\n}\n\n.cn-record-list--feed .cn-record-list__main {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n/* inline */\n.cn-record-list__inline-row {\n\tpadding: 4px 0;\n}\n\n.cn-record-list__label {\n\tmargin-inline-end: 4px;\n}\n\n.cn-record-list__inline-title {\n\tcolor: var(--color-main-text);\n}\n\na.cn-record-list__inline-title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n</style>\n\n<style>\n/* In a narrow side column a row stacks: title, meta, then pill and date. */\n.cn-detail-page__side .cn-record-list__row {\n\talign-items: flex-start;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-detail-page__side .cn-record-list__row .cn-record-list__title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-detail-page__side .cn-record-list__chevron {\n\tdisplay: none;\n}\n\n/* A row is a touch target of at least 44px on a phone. */\n@media (max-width: 640px) {\n\t.cn-record-list__row {\n\t\tmin-height: 44px;\n\t}\n}\n</style>\n"],"names":["_normalizeClass","_createElementBlock","_toDisplayString","_openBlock","_createVNode","_createBlock","_resolveDynamicComponent","_mergeProps","_createTextVNode","_Fragment","_renderList","_toHandlers","_createElementVNode"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnRegisterMapping.vue2.js';
|
|
|
2
2
|
import { render } from './CnRegisterMapping.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-8d3cad98";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -7,6 +7,8 @@ import Refresh from 'vue-material-design-icons/Refresh.vue';
|
|
|
7
7
|
import { prefixUrl, buildHeaders } from '../../utils/headers.js';
|
|
8
8
|
import __sfc_main from '../CnSettingsSection/CnSettingsSection.vue.js';
|
|
9
9
|
|
|
10
|
+
let mappingCount = 0;
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* CnRegisterMapping - OpenRegister register/schema configuration component.
|
|
12
14
|
*
|
|
@@ -131,6 +133,20 @@ var script = {
|
|
|
131
133
|
default: () => translate('nextcloud-vue', 'Re-import configuration'),
|
|
132
134
|
},
|
|
133
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Draw each group as a header that opens and closes it, showing its
|
|
138
|
+
* configured count while closed. The groups start closed. `null` (the
|
|
139
|
+
* default) collapses them when there is more than one group, so a page
|
|
140
|
+
* with several registers is not one long card.
|
|
141
|
+
*
|
|
142
|
+
* @type {boolean|null}
|
|
143
|
+
* @spec openspec/changes/register-mapping-collapsed-groups/specs/register-mapping/spec.md#requirement-register-groups-start-closed-with-their-count
|
|
144
|
+
*/
|
|
145
|
+
collapseGroups: {
|
|
146
|
+
type: Boolean,
|
|
147
|
+
default: null,
|
|
148
|
+
},
|
|
149
|
+
|
|
134
150
|
/** Auto-match schema titles to type slugs on register change */
|
|
135
151
|
autoMatch: {
|
|
136
152
|
type: Boolean,
|
|
@@ -166,6 +182,9 @@ var script = {
|
|
|
166
182
|
// Local state
|
|
167
183
|
localConfig: {},
|
|
168
184
|
expandedRows: {},
|
|
185
|
+
// The groups opened by the reader, by index.
|
|
186
|
+
openGroups: {},
|
|
187
|
+
uid: ++mappingCount,
|
|
169
188
|
}
|
|
170
189
|
},
|
|
171
190
|
|
|
@@ -173,11 +192,21 @@ var script = {
|
|
|
173
192
|
/** Register options for NcSelect */
|
|
174
193
|
registerSelectOptions() {
|
|
175
194
|
return this.registers.map((r) => ({
|
|
176
|
-
label: r.title || r.slug ||
|
|
195
|
+
label: r.title || r.slug || translate('nextcloud-vue', 'Register {id}', { id: r.id }),
|
|
177
196
|
value: String(r.id),
|
|
178
197
|
}))
|
|
179
198
|
},
|
|
180
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Whether the groups open and close.
|
|
202
|
+
*
|
|
203
|
+
* @return {boolean}
|
|
204
|
+
* @spec openspec/changes/register-mapping-collapsed-groups/specs/register-mapping/spec.md#requirement-register-groups-start-closed-with-their-count
|
|
205
|
+
*/
|
|
206
|
+
groupsCollapsible() {
|
|
207
|
+
return this.collapseGroups === null ? this.groups.length > 1 : this.collapseGroups
|
|
208
|
+
},
|
|
209
|
+
|
|
181
210
|
/** Whether local config differs from prop */
|
|
182
211
|
hasChanges() {
|
|
183
212
|
return JSON.stringify(this.localConfig) !== JSON.stringify(this.configuration)
|
|
@@ -200,6 +229,39 @@ var script = {
|
|
|
200
229
|
},
|
|
201
230
|
|
|
202
231
|
methods: {
|
|
232
|
+
t: translate,
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Whether a group's body shows.
|
|
236
|
+
*
|
|
237
|
+
* @param {number} groupIdx Group index
|
|
238
|
+
* @return {boolean}
|
|
239
|
+
*/
|
|
240
|
+
isGroupOpen(groupIdx) {
|
|
241
|
+
return !this.groupsCollapsible || this.openGroups[groupIdx] === true
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Open a closed group, or close an open one.
|
|
246
|
+
*
|
|
247
|
+
* @param {number} groupIdx Group index
|
|
248
|
+
* @return {void}
|
|
249
|
+
* @spec openspec/changes/register-mapping-collapsed-groups/specs/register-mapping/spec.md#requirement-register-groups-start-closed-with-their-count
|
|
250
|
+
*/
|
|
251
|
+
toggleGroup(groupIdx) {
|
|
252
|
+
this.openGroups = { ...this.openGroups, [groupIdx]: !this.isGroupOpen(groupIdx) };
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The id of a group's body, for the header's `aria-controls`.
|
|
257
|
+
*
|
|
258
|
+
* @param {number} groupIdx Group index
|
|
259
|
+
* @return {string}
|
|
260
|
+
*/
|
|
261
|
+
groupBodyId(groupIdx) {
|
|
262
|
+
return `cn-register-mapping-${this.uid}-group-${groupIdx}`
|
|
263
|
+
},
|
|
264
|
+
|
|
203
265
|
/**
|
|
204
266
|
* Get the config key for a group's register.
|
|
205
267
|
*
|
|
@@ -284,7 +346,7 @@ var script = {
|
|
|
284
346
|
return selected.label
|
|
285
347
|
}
|
|
286
348
|
const value = this.schemaValue(groupIdx, type);
|
|
287
|
-
return value ?
|
|
349
|
+
return value ? translate('nextcloud-vue', 'Schema {id}', { id: value }) : ''
|
|
288
350
|
},
|
|
289
351
|
|
|
290
352
|
/**
|
|
@@ -300,7 +362,7 @@ var script = {
|
|
|
300
362
|
}
|
|
301
363
|
const schemas = this.schemasByRegister[reg.value] || [];
|
|
302
364
|
return schemas.map((s) => ({
|
|
303
|
-
label: s.title || s.slug ||
|
|
365
|
+
label: s.title || s.slug || translate('nextcloud-vue', 'Schema {id}', { id: s.id }),
|
|
304
366
|
value: String(s.id),
|
|
305
367
|
}))
|
|
306
368
|
},
|
|
@@ -438,7 +500,7 @@ var script = {
|
|
|
438
500
|
});
|
|
439
501
|
|
|
440
502
|
if (!response.ok) {
|
|
441
|
-
this.registersError =
|
|
503
|
+
this.registersError = translate('nextcloud-vue', 'The registers could not be loaded: {reason}', { reason: response.statusText || response.status });
|
|
442
504
|
return
|
|
443
505
|
}
|
|
444
506
|
|
|
@@ -459,7 +521,7 @@ var script = {
|
|
|
459
521
|
}
|
|
460
522
|
}
|
|
461
523
|
} catch (error) {
|
|
462
|
-
this.registersError = error.message || '
|
|
524
|
+
this.registersError = error.message || translate('nextcloud-vue', 'The registers could not be loaded: {reason}', { reason: translate('nextcloud-vue', 'no connection') });
|
|
463
525
|
} finally {
|
|
464
526
|
this.registersLoading = false;
|
|
465
527
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnRegisterMapping.vue2.js","sources":["../../../../src/components/CnRegisterMapping/CnRegisterMapping.vue"],"sourcesContent":["<template>\n\t<CnSettingsSection\n\t\t:name=\"name\"\n\t\t:description=\"description\"\n\t\t:docUrl=\"docUrl\"\n\t\t:loading=\"registersLoading\"\n\t\tloadingMessage=\"Loading registers...\"\n\t\t:error=\"!!registersError\"\n\t\t:errorMessage=\"registersError || ''\"\n\t\t:onRetry=\"loadRegisters\">\n\t\t<!-- Action buttons -->\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"showSaveButton\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"saving || !hasChanges\"\n\t\t\t\t@click=\"handleSave\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"saving\" :size=\"20\" />\n\t\t\t\t\t<ContentSave v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ saving ? 'Saving...' : saveButtonText }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"showReimportButton\"\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:disabled=\"reimporting\"\n\t\t\t\t@click=\"$emit('reimport')\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"reimporting\" :size=\"20\" />\n\t\t\t\t\t<Refresh v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ reimporting ? 'Importing...' : reimportButtonText }}\n\t\t\t</NcButton>\n\t\t\t<slot name=\"actions\" />\n\t\t</template>\n\n\t\t<!-- Group sections -->\n\t\t<div v-if=\"!registersLoading && !registersError\" class=\"cn-register-mapping\">\n\t\t\t<div\n\t\t\t\tv-for=\"(group, groupIdx) in groups\"\n\t\t\t\t:key=\"groupIdx\"\n\t\t\t\tclass=\"cn-register-mapping__group\">\n\t\t\t\t<!-- Group header -->\n\t\t\t\t<slot name=\"group-header\"\n\t\t\t\t\t:group=\"group\"\n\t\t\t\t\t:configuredCount=\"configuredCount(groupIdx)\"\n\t\t\t\t\t:totalCount=\"group.types.length\">\n\t\t\t\t\t<div class=\"cn-register-mapping__group-header\">\n\t\t\t\t\t\t<h4 class=\"cn-register-mapping__group-title\">\n\t\t\t\t\t\t\t{{ group.name }}\n\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t<span class=\"cn-register-mapping__group-status\">\n\t\t\t\t\t\t\t{{ configuredCount(groupIdx) }}/{{ group.types.length }} {{ labels.partiallyConfigured }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</slot>\n\n\t\t\t\t<!-- Group description -->\n\t\t\t\t<p v-if=\"group.description\" class=\"cn-register-mapping__group-description\">\n\t\t\t\t\t{{ group.description }}\n\t\t\t\t</p>\n\n\t\t\t\t<!-- Register selector -->\n\t\t\t\t<div class=\"cn-register-mapping__register-select\">\n\t\t\t\t\t<label class=\"cn-register-mapping__label\">{{ labels.register }}</label>\n\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t:modelValue=\"selectedRegister(groupIdx)\"\n\t\t\t\t\t\t:options=\"registerSelectOptions\"\n\t\t\t\t\t\t:placeholder=\"labels.selectRegister\"\n\t\t\t\t\t\t:inputLabel=\"labels.register\"\n\t\t\t\t\t\t:loading=\"registersLoading\"\n\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\ttrackBy=\"value\"\n\t\t\t\t\t\t@update:modelValue=\"handleRegisterChange(groupIdx, $event)\" />\n\t\t\t\t</div>\n\n\t\t\t\t<!-- Type list -->\n\t\t\t\t<div v-if=\"selectedRegister(groupIdx)\" class=\"cn-register-mapping__type-list\">\n\t\t\t\t\t<!-- Header row -->\n\t\t\t\t\t<div class=\"cn-register-mapping__type-list-header\">\n\t\t\t\t\t\t<span>Name</span>\n\t\t\t\t\t\t<span>Schema</span>\n\t\t\t\t\t\t<span />\n\t\t\t\t\t\t<span />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Type rows -->\n\t\t\t\t\t<template v-for=\"type in group.types\" :key=\"type.slug + '-row'\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"cn-register-mapping__type-row\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-register-mapping__type-row--expanded': isExpanded(groupIdx, type.slug) }\"\n\t\t\t\t\t\t\t@click=\"toggleExpand(groupIdx, type.slug)\">\n\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-name\">{{ type.label }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-schema\">\n\t\t\t\t\t\t\t\t{{ schemaLabel(groupIdx, type) || labels.notConfigured }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-status\">\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclass=\"cn-register-mapping__status-dot\"\n\t\t\t\t\t\t\t\t\t:class=\"schemaValue(groupIdx, type)\n\t\t\t\t\t\t\t\t\t\t? 'cn-register-mapping__status-dot--configured'\n\t\t\t\t\t\t\t\t\t\t: 'cn-register-mapping__status-dot--unconfigured'\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-chevron\">\n\t\t\t\t\t\t\t\t<ChevronUp v-if=\"isExpanded(groupIdx, type.slug)\" :size=\"20\" />\n\t\t\t\t\t\t\t\t<ChevronDown v-else :size=\"20\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Expanded detail -->\n\t\t\t\t\t\t<transition name=\"slide\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"isExpanded(groupIdx, type.slug)\"\n\t\t\t\t\t\t\t\tclass=\"cn-register-mapping__type-detail\">\n\t\t\t\t\t\t\t\t<p v-if=\"type.description\" class=\"cn-register-mapping__type-description\">\n\t\t\t\t\t\t\t\t\t{{ type.description }}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t\t\t:modelValue=\"selectedSchema(groupIdx, type)\"\n\t\t\t\t\t\t\t\t\t:options=\"schemaSelectOptions(groupIdx)\"\n\t\t\t\t\t\t\t\t\t:placeholder=\"labels.selectSchema\"\n\t\t\t\t\t\t\t\t\t:inputLabel=\"labels.schema\"\n\t\t\t\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\t\t\t\ttrackBy=\"value\"\n\t\t\t\t\t\t\t\t\t@update:modelValue=\"handleSchemaChange(groupIdx, type, $event)\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</transition>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- No register selected -->\n\t\t\t\t<NcNoteCard v-else-if=\"!registersLoading\" type=\"info\">\n\t\t\t\t\t{{ labels.selectRegister }}\n\t\t\t\t</NcNoteCard>\n\n\t\t\t\t<!-- No schemas available -->\n\t\t\t\t<NcNoteCard\n\t\t\t\t\tv-if=\"selectedRegister(groupIdx) && schemaSelectOptions(groupIdx).length === 0 && !registersLoading\"\n\t\t\t\t\tvariant=\"warning\">\n\t\t\t\t\t{{ labels.noSchemas }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer -->\n\t\t<template v-if=\"$slots.footer\" #footer>\n\t\t\t<slot name=\"footer\" />\n\t\t</template>\n\t</CnSettingsSection>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport ChevronDown from 'vue-material-design-icons/ChevronDown.vue'\nimport ChevronUp from 'vue-material-design-icons/ChevronUp.vue'\nimport ContentSave from 'vue-material-design-icons/ContentSave.vue'\nimport Refresh from 'vue-material-design-icons/Refresh.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/headers.js'\nimport { CnSettingsSection } from '../CnSettingsSection/index.js'\n\n/**\n * CnRegisterMapping - OpenRegister register/schema configuration component.\n *\n * Displays and manages register-to-schema mappings for app object types.\n * Self-fetches available registers and schemas from the OpenRegister API.\n * Supports multiple register groups (stacked sections) with expandable\n * type rows for manual schema override.\n *\n * Single register (Pipelinq)\n * ```vue\n * <CnRegisterMapping\n * name=\"Register Configuration\"\n * :groups=\"[{\n * name: 'Core Objects',\n * types: [\n * { slug: 'client', label: 'Client' },\n * { slug: 'contact', label: 'Contact' },\n * ],\n * }]\"\n * :configuration=\"config\"\n * :show-reimport-button=\"true\"\n * @save=\"saveConfig\"\n * @reimport=\"reimport\" />\n * ```\n *\n * Multi-register (Stackiq)\n * ```vue\n * <CnRegisterMapping\n * :groups=\"[\n * { name: 'Voorzieningen', registerConfigKey: 'voorzieningen_register', types: [...] },\n * { name: 'AMEF', registerConfigKey: 'amef_register', types: [...] },\n * ]\"\n * :configuration=\"config\"\n * @save=\"saveConfig\" />\n * ```\n */\nexport default {\n\tname: 'CnRegisterMapping',\n\n\tcomponents: {\n\t\tCnSettingsSection,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t\tContentSave,\n\t\tRefresh,\n\t\tChevronDown,\n\t\tChevronUp,\n\t},\n\n\tprops: {\n\t\t/** Section title */\n\t\tname: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Register configuration'),\n\t\t},\n\n\t\t/** Section description */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Configure OpenRegister schema mappings for your object types'),\n\t\t},\n\n\t\t/** Documentation URL */\n\t\tdocUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Groups of object types that share a register.\n\t\t *\n\t\t * @type {Array<{ name: string, description: string, registerConfigKey: string, types: Array<{ slug: string, label: string, description: string, configKey: string }> }>}\n\t\t */\n\t\tgroups: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t\tvalidator: (groups) => groups.length > 0\n\t\t\t\t&& groups.every((g) => g.name && Array.isArray(g.types) && g.types.length > 0),\n\t\t},\n\n\t\t/** Current configuration values: { register: '5', client_schema: '28', ... } */\n\t\tconfiguration: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Show save button */\n\t\tshowSaveButton: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Whether save is in progress */\n\t\tsaving: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Show reimport button */\n\t\tshowReimportButton: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether reimport is in progress */\n\t\treimporting: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Save button text */\n\t\tsaveButtonText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Save configuration'),\n\t\t},\n\n\t\t/** Reimport button text */\n\t\treimportButtonText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Re-import configuration'),\n\t\t},\n\n\t\t/** Auto-match schema titles to type slugs on register change */\n\t\tautoMatch: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** UI labels (i18n) */\n\t\tlabels: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({\n\t\t\t\tregister: t('nextcloud-vue', 'Register'),\n\t\t\t\tschema: t('nextcloud-vue', 'Schema'),\n\t\t\t\tconfigured: t('nextcloud-vue', 'Configured'),\n\t\t\t\tnotConfigured: t('nextcloud-vue', 'Not configured'),\n\t\t\t\tnoSchemas: t('nextcloud-vue', 'No schemas available in this register'),\n\t\t\t\tselectRegister: t('nextcloud-vue', 'Select a register'),\n\t\t\t\tselectSchema: t('nextcloud-vue', 'Select a schema'),\n\t\t\t\tallConfigured: t('nextcloud-vue', 'All types configured'),\n\t\t\t\tpartiallyConfigured: t('nextcloud-vue', 'configured'),\n\t\t\t}),\n\t\t},\n\t},\n\n\temits: ['update:configuration', 'save', 'reimport'],\n\n\tdata() {\n\t\treturn {\n\t\t\t// Fetched data\n\t\t\tregisters: [],\n\t\t\tschemasByRegister: {},\n\t\t\tregistersLoading: false,\n\t\t\tregistersError: null,\n\t\t\t// Local state\n\t\t\tlocalConfig: {},\n\t\t\texpandedRows: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Register options for NcSelect */\n\t\tregisterSelectOptions() {\n\t\t\treturn this.registers.map((r) => ({\n\t\t\t\tlabel: r.title || r.slug || `Register ${r.id}`,\n\t\t\t\tvalue: String(r.id),\n\t\t\t}))\n\t\t},\n\n\t\t/** Whether local config differs from prop */\n\t\thasChanges() {\n\t\t\treturn JSON.stringify(this.localConfig) !== JSON.stringify(this.configuration)\n\t\t},\n\t},\n\n\twatch: {\n\t\tconfiguration: {\n\t\t\thandler(newVal) {\n\t\t\t\tthis.localConfig = { ...newVal }\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t\tdeep: true,\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait this.loadRegisters()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Get the config key for a group's register.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {string} Config key\n\t\t */\n\t\tregisterConfigKey(groupIdx) {\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\tif (group.registerConfigKey) {\n\t\t\t\treturn group.registerConfigKey\n\t\t\t}\n\t\t\tif (this.groups.length === 1) {\n\t\t\t\treturn 'register'\n\t\t\t}\n\t\t\treturn group.name.toLowerCase().replace(/[^a-z0-9]+/g, '_') + '_register'\n\t\t},\n\n\t\t/**\n\t\t * Get the config key for a type's schema.\n\t\t *\n\t\t * @param {object} type Type definition\n\t\t * @return {string} Config key\n\t\t */\n\t\tschemaConfigKey(type) {\n\t\t\treturn type.configKey || type.slug + '_schema'\n\t\t},\n\n\t\t/**\n\t\t * Get the selected register option for a group.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {object|null} NcSelect option\n\t\t */\n\t\tselectedRegister(groupIdx) {\n\t\t\tconst key = this.registerConfigKey(groupIdx)\n\t\t\tconst value = String(this.localConfig[key] || '')\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.registerSelectOptions.find((o) => o.value === value) || null\n\t\t},\n\n\t\t/**\n\t\t * Get the selected schema option for a type.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @return {object|null} NcSelect option\n\t\t */\n\t\tselectedSchema(groupIdx, type) {\n\t\t\tconst key = this.schemaConfigKey(type)\n\t\t\tconst value = String(this.localConfig[key] || '')\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst options = this.schemaSelectOptions(groupIdx)\n\t\t\treturn options.find((o) => o.value === value) || null\n\t\t},\n\n\t\t/**\n\t\t * Get the schema value (ID) for a type.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @return {string} Schema ID or empty string\n\t\t */\n\t\tschemaValue(groupIdx, type) {\n\t\t\tconst key = this.schemaConfigKey(type)\n\t\t\treturn this.localConfig[key] || ''\n\t\t},\n\n\t\t/**\n\t\t * Get the display label for a type's current schema.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @return {string} Schema label or empty string\n\t\t */\n\t\tschemaLabel(groupIdx, type) {\n\t\t\tconst selected = this.selectedSchema(groupIdx, type)\n\t\t\tif (selected) {\n\t\t\t\treturn selected.label\n\t\t\t}\n\t\t\tconst value = this.schemaValue(groupIdx, type)\n\t\t\treturn value ? `Schema ${value}` : ''\n\t\t},\n\n\t\t/**\n\t\t * Get schema options for a group's selected register.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {Array<{label: string, value: string}>} NcSelect options\n\t\t */\n\t\tschemaSelectOptions(groupIdx) {\n\t\t\tconst reg = this.selectedRegister(groupIdx)\n\t\t\tif (!reg) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst schemas = this.schemasByRegister[reg.value] || []\n\t\t\treturn schemas.map((s) => ({\n\t\t\t\tlabel: s.title || s.slug || `Schema ${s.id}`,\n\t\t\t\tvalue: String(s.id),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Count configured types in a group.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {number} Count\n\t\t */\n\t\tconfiguredCount(groupIdx) {\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\treturn group.types.filter((type) => !!this.schemaValue(groupIdx, type)).length\n\t\t},\n\n\t\t/**\n\t\t * Check if a type row is expanded.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {string} slug Type slug\n\t\t * @return {boolean}\n\t\t */\n\t\tisExpanded(groupIdx, slug) {\n\t\t\treturn !!this.expandedRows[groupIdx + '-' + slug]\n\t\t},\n\n\t\t/**\n\t\t * Toggle a type row expansion.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {string} slug Type slug\n\t\t */\n\t\ttoggleExpand(groupIdx, slug) {\n\t\t\tconst key = groupIdx + '-' + slug\n\t\t\tthis.expandedRows = {\n\t\t\t\t...this.expandedRows,\n\t\t\t\t[key]: !this.expandedRows[key],\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle register selection change for a group.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object|null} option NcSelect option\n\t\t */\n\t\tasync handleRegisterChange(groupIdx, option) {\n\t\t\tconst key = this.registerConfigKey(groupIdx)\n\t\t\tconst value = option?.value || ''\n\n\t\t\tthis.localConfig = { ...this.localConfig, [key]: value }\n\n\t\t\t// Clear schema selections for this group\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\tfor (const type of group.types) {\n\t\t\t\tconst schemaKey = this.schemaConfigKey(type)\n\t\t\t\tthis.localConfig = { ...this.localConfig, [schemaKey]: '' }\n\t\t\t}\n\n\t\t\t// Fetch schemas for the new register\n\t\t\tif (value) {\n\t\t\t\tawait this.loadSchemasForRegister(value)\n\n\t\t\t\t// Auto-match schemas\n\t\t\t\tif (this.autoMatch) {\n\t\t\t\t\tthis.autoMatchSchemas(groupIdx)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.$emit('update:configuration', { ...this.localConfig })\n\t\t},\n\n\t\t/**\n\t\t * Handle schema selection change for a type.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @param {object|null} option NcSelect option\n\t\t */\n\t\thandleSchemaChange(groupIdx, type, option) {\n\t\t\tconst key = this.schemaConfigKey(type)\n\t\t\tconst value = option?.value || ''\n\n\t\t\tthis.localConfig = { ...this.localConfig, [key]: value }\n\t\t\tthis.$emit('update:configuration', { ...this.localConfig })\n\t\t},\n\n\t\t/**\n\t\t * Auto-match schema titles to type slugs/labels (case-insensitive).\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t */\n\t\tautoMatchSchemas(groupIdx) {\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\tconst options = this.schemaSelectOptions(groupIdx)\n\n\t\t\tfor (const type of group.types) {\n\t\t\t\tconst schemaKey = this.schemaConfigKey(type)\n\t\t\t\t// Skip if already has a value\n\t\t\t\tif (this.localConfig[schemaKey]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst slug = type.slug.toLowerCase()\n\t\t\t\tconst label = type.label.toLowerCase()\n\n\t\t\t\tconst match = options.find((o) => {\n\t\t\t\t\tconst optLabel = o.label.toLowerCase()\n\t\t\t\t\treturn optLabel === slug || optLabel === label\n\t\t\t\t\t\t|| optLabel.includes(slug) || slug.includes(optLabel)\n\t\t\t\t})\n\n\t\t\t\tif (match) {\n\t\t\t\t\tthis.localConfig = { ...this.localConfig, [schemaKey]: match.value }\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** Emit save event with current config */\n\t\thandleSave() {\n\t\t\tthis.$emit('save', { ...this.localConfig })\n\t\t},\n\n\t\t/**\n\t\t * Fetch all registers from OpenRegister API.\n\t\t */\n\t\tasync loadRegisters() {\n\t\t\tthis.registersLoading = true\n\t\t\tthis.registersError = null\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl('/apps/openregister/api/registers?_extend[]=schemas'), {\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthis.registersError = `Failed to fetch registers: ${response.statusText}`\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst results = data.results || data\n\t\t\t\tthis.registers = Array.isArray(results) ? results : []\n\n\t\t\t\t// Cache expanded schemas\n\t\t\t\tfor (const reg of this.registers) {\n\t\t\t\t\tif (Array.isArray(reg.schemas) && reg.schemas.length > 0) {\n\t\t\t\t\t\tconst schemas = reg.schemas.filter((s) => s && typeof s === 'object' && s.id)\n\t\t\t\t\t\tif (schemas.length > 0) {\n\t\t\t\t\t\t\tthis.schemasByRegister = {\n\t\t\t\t\t\t\t\t...this.schemasByRegister,\n\t\t\t\t\t\t\t\t[String(reg.id)]: schemas,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.registersError = error.message || 'Network error fetching registers'\n\t\t\t} finally {\n\t\t\t\tthis.registersLoading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fetch schemas for a specific register.\n\t\t *\n\t\t * @param {string} registerId Register ID\n\t\t */\n\t\tasync loadSchemasForRegister(registerId) {\n\t\t\tconst id = String(registerId)\n\n\t\t\t// Return cached\n\t\t\tif (this.schemasByRegister[id]?.length > 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`/apps/openregister/api/registers/${encodeURIComponent(id)}?_extend[]=schemas`),\n\t\t\t\t\t{ method: 'GET', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst schemas = (data.schemas || []).filter((s) => s && typeof s === 'object' && s.id)\n\t\t\t\tthis.schemasByRegister = { ...this.schemasByRegister, [id]: schemas }\n\t\t\t} catch {\n\t\t\t\t// Silently fail — register already selected, schemas just won't populate\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-register-mapping__group {\n\tmargin-bottom: 24px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 20px;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__group:last-child {\n\tmargin-bottom: 0;\n}\n\n.cn-register-mapping__group-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin-bottom: 12px;\n}\n\n.cn-register-mapping__group-title {\n\tfont-size: 16px;\n\tfont-weight: 600;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-register-mapping__group-status {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-register-mapping__group-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n\tmargin: 0 0 12px 0;\n}\n\n.cn-register-mapping__label {\n\tdisplay: block;\n\tfont-weight: 500;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-bottom: 4px;\n}\n\n.cn-register-mapping__register-select {\n\tmargin-bottom: 16px;\n\tmax-width: 400px;\n}\n\n.cn-register-mapping__type-list {\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\toverflow: hidden;\n\tbackground: var(--color-main-background);\n}\n\n.cn-register-mapping__type-list-header {\n\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr 40px 32px;\n\talign-items: center;\n\tpadding: 8px 16px;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.5px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__type-row {\n\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr 40px 32px;\n\talign-items: center;\n\tpadding: 10px 16px;\n\tborder-bottom: 1px solid var(--color-border);\n\tcursor: pointer;\n\ttransition: background-color 0.15s ease;\n}\n\n.cn-register-mapping__type-row:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__type-row--expanded {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__type-name {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n}\n\n.cn-register-mapping__type-schema {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-register-mapping__type-status {\n\tdisplay: flex;\n\tjustify-content: center;\n}\n\n.cn-register-mapping__type-chevron {\n\tdisplay: flex;\n\tjustify-content: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-register-mapping__status-dot {\n\tdisplay: inline-block;\n\twidth: 10px;\n\theight: 10px;\n\tborder-radius: 50%;\n}\n\n.cn-register-mapping__status-dot--configured {\n\tbackground-color: var(--color-success);\n}\n\n.cn-register-mapping__status-dot--unconfigured {\n\tbackground-color: var(--color-warning);\n}\n\n.cn-register-mapping__type-detail {\n\tpadding: 12px 16px 16px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-main-background);\n}\n\n.cn-register-mapping__type-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n\tmargin: 0 0 8px 0;\n}\n\n/* Slide transition */\n.slide-enter-active,\n.slide-leave-active {\n\ttransition: all 0.2s ease;\n\tmax-height: 200px;\n\toverflow: hidden;\n}\n\n.slide-enter,\n.slide-leave-to {\n\tmax-height: 0;\n\tpadding-top: 0;\n\tpadding-bottom: 0;\n\topacity: 0;\n}\n\n/* Last row in list should not have bottom border */\n.cn-register-mapping__type-list > :last-child {\n\tborder-bottom: none;\n}\n\n@media (max-width: 768px) {\n\t.cn-register-mapping__type-list-header {\n\t\tdisplay: none;\n\t}\n\n\t.cn-register-mapping__type-row {\n\t\tgrid-template-columns: 1fr auto 32px;\n\t}\n\n\t.cn-register-mapping__type-schema {\n\t\tdisplay: none;\n\t}\n\n\t.cn-register-mapping__register-select {\n\t\tmax-width: none;\n\t}\n}\n</style>\n"],"names":["CnSettingsSection","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnRegisterMapping.vue2.js","sources":["../../../../src/components/CnRegisterMapping/CnRegisterMapping.vue"],"sourcesContent":["<template>\n\t<CnSettingsSection\n\t\t:name=\"name\"\n\t\t:description=\"description\"\n\t\t:docUrl=\"docUrl\"\n\t\t:loading=\"registersLoading\"\n\t\t:loadingMessage=\"t('nextcloud-vue', 'Loading registers…')\"\n\t\t:error=\"!!registersError\"\n\t\t:errorMessage=\"registersError || ''\"\n\t\t:onRetry=\"loadRegisters\">\n\t\t<!-- Action buttons -->\n\t\t<template #actions>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"showSaveButton\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"saving || !hasChanges\"\n\t\t\t\t@click=\"handleSave\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"saving\" :size=\"20\" />\n\t\t\t\t\t<ContentSave v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ saving ? t('nextcloud-vue', 'Saving…') : saveButtonText }}\n\t\t\t</NcButton>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"showReimportButton\"\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:disabled=\"reimporting\"\n\t\t\t\t@click=\"$emit('reimport')\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"reimporting\" :size=\"20\" />\n\t\t\t\t\t<Refresh v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ reimporting ? t('nextcloud-vue', 'Importing…') : reimportButtonText }}\n\t\t\t</NcButton>\n\t\t\t<slot name=\"actions\" />\n\t\t</template>\n\n\t\t<!-- Group sections -->\n\t\t<div v-if=\"!registersLoading && !registersError\" class=\"cn-register-mapping\">\n\t\t\t<div\n\t\t\t\tv-for=\"(group, groupIdx) in groups\"\n\t\t\t\t:key=\"groupIdx\"\n\t\t\t\tclass=\"cn-register-mapping__group\"\n\t\t\t\t:class=\"{ 'cn-register-mapping__group--collapsed': !isGroupOpen(groupIdx) }\"\n\t\t\t\tdata-testid=\"cn-register-mapping-group\">\n\t\t\t\t<!-- Group header. With collapsible groups a heading holds the button\n\t\t\t\t that opens and closes the group (the WAI-ARIA accordion); the\n\t\t\t\t count stays visible while it is closed. -->\n\t\t\t\t<h4 v-if=\"groupsCollapsible\" class=\"cn-register-mapping__group-heading\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"cn-register-mapping__group-toggle\"\n\t\t\t\t\t\t:aria-expanded=\"String(isGroupOpen(groupIdx))\"\n\t\t\t\t\t\t:aria-controls=\"groupBodyId(groupIdx)\"\n\t\t\t\t\t\tdata-testid=\"cn-register-mapping-group-toggle\"\n\t\t\t\t\t\t@click=\"toggleGroup(groupIdx)\">\n\t\t\t\t\t\t<span class=\"cn-register-mapping__group-chevron\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t<ChevronUp v-if=\"isGroupOpen(groupIdx)\" :size=\"20\" />\n\t\t\t\t\t\t\t<ChevronDown v-else :size=\"20\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<slot name=\"group-header\"\n\t\t\t\t\t\t\t:group=\"group\"\n\t\t\t\t\t\t\t:configuredCount=\"configuredCount(groupIdx)\"\n\t\t\t\t\t\t\t:totalCount=\"group.types.length\">\n\t\t\t\t\t\t\t<span class=\"cn-register-mapping__group-header\">\n\t\t\t\t\t\t\t\t<span class=\"cn-register-mapping__group-title\">{{ group.name }}</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-register-mapping__group-status\" data-testid=\"cn-register-mapping-group-count\">\n\t\t\t\t\t\t\t\t\t{{ configuredCount(groupIdx) }}/{{ group.types.length }} {{ labels.partiallyConfigured }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</slot>\n\t\t\t\t\t</button>\n\t\t\t\t</h4>\n\t\t\t\t<slot v-else\n\t\t\t\t\tname=\"group-header\"\n\t\t\t\t\t:group=\"group\"\n\t\t\t\t\t:configuredCount=\"configuredCount(groupIdx)\"\n\t\t\t\t\t:totalCount=\"group.types.length\">\n\t\t\t\t\t<div class=\"cn-register-mapping__group-header\">\n\t\t\t\t\t\t<h4 class=\"cn-register-mapping__group-title\">\n\t\t\t\t\t\t\t{{ group.name }}\n\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t<span class=\"cn-register-mapping__group-status\">\n\t\t\t\t\t\t\t{{ configuredCount(groupIdx) }}/{{ group.types.length }} {{ labels.partiallyConfigured }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</slot>\n\n\t\t\t\t<div\n\t\t\t\t\tv-show=\"isGroupOpen(groupIdx)\"\n\t\t\t\t\t:id=\"groupBodyId(groupIdx)\"\n\t\t\t\t\tclass=\"cn-register-mapping__group-body\"\n\t\t\t\t\tdata-testid=\"cn-register-mapping-group-body\">\n\t\t\t\t\t<!-- Group description -->\n\t\t\t\t\t<p v-if=\"group.description\" class=\"cn-register-mapping__group-description\">\n\t\t\t\t\t\t{{ group.description }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<!-- Register selector -->\n\t\t\t\t\t<div class=\"cn-register-mapping__register-select\">\n\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t:modelValue=\"selectedRegister(groupIdx)\"\n\t\t\t\t\t\t\t:options=\"registerSelectOptions\"\n\t\t\t\t\t\t\t:placeholder=\"labels.selectRegister\"\n\t\t\t\t\t\t\t:inputLabel=\"labels.register\"\n\t\t\t\t\t\t\t:loading=\"registersLoading\"\n\t\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\t\ttrackBy=\"value\"\n\t\t\t\t\t\t\t@update:modelValue=\"handleRegisterChange(groupIdx, $event)\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Type list -->\n\t\t\t\t\t<div v-if=\"selectedRegister(groupIdx)\" class=\"cn-register-mapping__type-list\">\n\t\t\t\t\t\t<!-- Header row -->\n\t\t\t\t\t\t<div class=\"cn-register-mapping__type-list-header\">\n\t\t\t\t\t\t\t<span>{{ t('nextcloud-vue', 'Object type') }}</span>\n\t\t\t\t\t\t\t<span>{{ labels.schema }}</span>\n\t\t\t\t\t\t\t<span />\n\t\t\t\t\t\t\t<span />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Type rows -->\n\t\t\t\t\t\t<template v-for=\"type in group.types\" :key=\"type.slug + '-row'\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-register-mapping__type-row\"\n\t\t\t\t\t\t\t\t:class=\"{ 'cn-register-mapping__type-row--expanded': isExpanded(groupIdx, type.slug) }\"\n\t\t\t\t\t\t\t\t@click=\"toggleExpand(groupIdx, type.slug)\">\n\t\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-name\">{{ type.label }}</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-schema\">\n\t\t\t\t\t\t\t\t\t{{ schemaLabel(groupIdx, type) || labels.notConfigured }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-status\">\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclass=\"cn-register-mapping__status-dot\"\n\t\t\t\t\t\t\t\t\t\t:class=\"schemaValue(groupIdx, type)\n\t\t\t\t\t\t\t\t\t\t\t? 'cn-register-mapping__status-dot--configured'\n\t\t\t\t\t\t\t\t\t\t\t: 'cn-register-mapping__status-dot--unconfigured'\" />\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-register-mapping__type-chevron\">\n\t\t\t\t\t\t\t\t\t<ChevronUp v-if=\"isExpanded(groupIdx, type.slug)\" :size=\"20\" />\n\t\t\t\t\t\t\t\t\t<ChevronDown v-else :size=\"20\" />\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<!-- Expanded detail -->\n\t\t\t\t\t\t\t<transition name=\"slide\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tv-if=\"isExpanded(groupIdx, type.slug)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-register-mapping__type-detail\">\n\t\t\t\t\t\t\t\t\t<p v-if=\"type.description\" class=\"cn-register-mapping__type-description\">\n\t\t\t\t\t\t\t\t\t\t{{ type.description }}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t<NcSelect\n\t\t\t\t\t\t\t\t\t\t:modelValue=\"selectedSchema(groupIdx, type)\"\n\t\t\t\t\t\t\t\t\t\t:options=\"schemaSelectOptions(groupIdx)\"\n\t\t\t\t\t\t\t\t\t\t:placeholder=\"labels.selectSchema\"\n\t\t\t\t\t\t\t\t\t\t:inputLabel=\"labels.schema\"\n\t\t\t\t\t\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t\t\t\t\t\ttrackBy=\"value\"\n\t\t\t\t\t\t\t\t\t\t@update:modelValue=\"handleSchemaChange(groupIdx, type, $event)\" />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</transition>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- No register selected -->\n\t\t\t\t\t<NcNoteCard v-else-if=\"!registersLoading\" type=\"info\">\n\t\t\t\t\t\t{{ labels.selectRegister }}\n\t\t\t\t\t</NcNoteCard>\n\n\t\t\t\t\t<!-- No schemas available -->\n\t\t\t\t\t<NcNoteCard\n\t\t\t\t\t\tv-if=\"selectedRegister(groupIdx) && schemaSelectOptions(groupIdx).length === 0 && !registersLoading\"\n\t\t\t\t\t\tvariant=\"warning\">\n\t\t\t\t\t\t{{ labels.noSchemas }}\n\t\t\t\t\t</NcNoteCard>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer -->\n\t\t<template v-if=\"$slots.footer\" #footer>\n\t\t\t<slot name=\"footer\" />\n\t\t</template>\n\t</CnSettingsSection>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport ChevronDown from 'vue-material-design-icons/ChevronDown.vue'\nimport ChevronUp from 'vue-material-design-icons/ChevronUp.vue'\nimport ContentSave from 'vue-material-design-icons/ContentSave.vue'\nimport Refresh from 'vue-material-design-icons/Refresh.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/headers.js'\nimport { CnSettingsSection } from '../CnSettingsSection/index.js'\n\nlet mappingCount = 0\n\n/**\n * CnRegisterMapping - OpenRegister register/schema configuration component.\n *\n * Displays and manages register-to-schema mappings for app object types.\n * Self-fetches available registers and schemas from the OpenRegister API.\n * Supports multiple register groups (stacked sections) with expandable\n * type rows for manual schema override.\n *\n * Single register (Pipelinq)\n * ```vue\n * <CnRegisterMapping\n * name=\"Register Configuration\"\n * :groups=\"[{\n * name: 'Core Objects',\n * types: [\n * { slug: 'client', label: 'Client' },\n * { slug: 'contact', label: 'Contact' },\n * ],\n * }]\"\n * :configuration=\"config\"\n * :show-reimport-button=\"true\"\n * @save=\"saveConfig\"\n * @reimport=\"reimport\" />\n * ```\n *\n * Multi-register (Stackiq)\n * ```vue\n * <CnRegisterMapping\n * :groups=\"[\n * { name: 'Voorzieningen', registerConfigKey: 'voorzieningen_register', types: [...] },\n * { name: 'AMEF', registerConfigKey: 'amef_register', types: [...] },\n * ]\"\n * :configuration=\"config\"\n * @save=\"saveConfig\" />\n * ```\n */\nexport default {\n\tname: 'CnRegisterMapping',\n\n\tcomponents: {\n\t\tCnSettingsSection,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t\tContentSave,\n\t\tRefresh,\n\t\tChevronDown,\n\t\tChevronUp,\n\t},\n\n\tprops: {\n\t\t/** Section title */\n\t\tname: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Register configuration'),\n\t\t},\n\n\t\t/** Section description */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Configure OpenRegister schema mappings for your object types'),\n\t\t},\n\n\t\t/** Documentation URL */\n\t\tdocUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Groups of object types that share a register.\n\t\t *\n\t\t * @type {Array<{ name: string, description: string, registerConfigKey: string, types: Array<{ slug: string, label: string, description: string, configKey: string }> }>}\n\t\t */\n\t\tgroups: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t\tvalidator: (groups) => groups.length > 0\n\t\t\t\t&& groups.every((g) => g.name && Array.isArray(g.types) && g.types.length > 0),\n\t\t},\n\n\t\t/** Current configuration values: { register: '5', client_schema: '28', ... } */\n\t\tconfiguration: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Show save button */\n\t\tshowSaveButton: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Whether save is in progress */\n\t\tsaving: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Show reimport button */\n\t\tshowReimportButton: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether reimport is in progress */\n\t\treimporting: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Save button text */\n\t\tsaveButtonText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Save configuration'),\n\t\t},\n\n\t\t/** Reimport button text */\n\t\treimportButtonText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Re-import configuration'),\n\t\t},\n\n\t\t/**\n\t\t * Draw each group as a header that opens and closes it, showing its\n\t\t * configured count while closed. The groups start closed. `null` (the\n\t\t * default) collapses them when there is more than one group, so a page\n\t\t * with several registers is not one long card.\n\t\t *\n\t\t * @type {boolean|null}\n\t\t * @spec openspec/changes/register-mapping-collapsed-groups/specs/register-mapping/spec.md#requirement-register-groups-start-closed-with-their-count\n\t\t */\n\t\tcollapseGroups: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Auto-match schema titles to type slugs on register change */\n\t\tautoMatch: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** UI labels (i18n) */\n\t\tlabels: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({\n\t\t\t\tregister: t('nextcloud-vue', 'Register'),\n\t\t\t\tschema: t('nextcloud-vue', 'Schema'),\n\t\t\t\tconfigured: t('nextcloud-vue', 'Configured'),\n\t\t\t\tnotConfigured: t('nextcloud-vue', 'Not configured'),\n\t\t\t\tnoSchemas: t('nextcloud-vue', 'No schemas available in this register'),\n\t\t\t\tselectRegister: t('nextcloud-vue', 'Select a register'),\n\t\t\t\tselectSchema: t('nextcloud-vue', 'Select a schema'),\n\t\t\t\tallConfigured: t('nextcloud-vue', 'All types configured'),\n\t\t\t\tpartiallyConfigured: t('nextcloud-vue', 'configured'),\n\t\t\t}),\n\t\t},\n\t},\n\n\temits: ['update:configuration', 'save', 'reimport'],\n\n\tdata() {\n\t\treturn {\n\t\t\t// Fetched data\n\t\t\tregisters: [],\n\t\t\tschemasByRegister: {},\n\t\t\tregistersLoading: false,\n\t\t\tregistersError: null,\n\t\t\t// Local state\n\t\t\tlocalConfig: {},\n\t\t\texpandedRows: {},\n\t\t\t// The groups opened by the reader, by index.\n\t\t\topenGroups: {},\n\t\t\tuid: ++mappingCount,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Register options for NcSelect */\n\t\tregisterSelectOptions() {\n\t\t\treturn this.registers.map((r) => ({\n\t\t\t\tlabel: r.title || r.slug || t('nextcloud-vue', 'Register {id}', { id: r.id }),\n\t\t\t\tvalue: String(r.id),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Whether the groups open and close.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/register-mapping-collapsed-groups/specs/register-mapping/spec.md#requirement-register-groups-start-closed-with-their-count\n\t\t */\n\t\tgroupsCollapsible() {\n\t\t\treturn this.collapseGroups === null ? this.groups.length > 1 : this.collapseGroups\n\t\t},\n\n\t\t/** Whether local config differs from prop */\n\t\thasChanges() {\n\t\t\treturn JSON.stringify(this.localConfig) !== JSON.stringify(this.configuration)\n\t\t},\n\t},\n\n\twatch: {\n\t\tconfiguration: {\n\t\t\thandler(newVal) {\n\t\t\t\tthis.localConfig = { ...newVal }\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t\tdeep: true,\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait this.loadRegisters()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Whether a group's body shows.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {boolean}\n\t\t */\n\t\tisGroupOpen(groupIdx) {\n\t\t\treturn !this.groupsCollapsible || this.openGroups[groupIdx] === true\n\t\t},\n\n\t\t/**\n\t\t * Open a closed group, or close an open one.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {void}\n\t\t * @spec openspec/changes/register-mapping-collapsed-groups/specs/register-mapping/spec.md#requirement-register-groups-start-closed-with-their-count\n\t\t */\n\t\ttoggleGroup(groupIdx) {\n\t\t\tthis.openGroups = { ...this.openGroups, [groupIdx]: !this.isGroupOpen(groupIdx) }\n\t\t},\n\n\t\t/**\n\t\t * The id of a group's body, for the header's `aria-controls`.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {string}\n\t\t */\n\t\tgroupBodyId(groupIdx) {\n\t\t\treturn `cn-register-mapping-${this.uid}-group-${groupIdx}`\n\t\t},\n\n\t\t/**\n\t\t * Get the config key for a group's register.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {string} Config key\n\t\t */\n\t\tregisterConfigKey(groupIdx) {\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\tif (group.registerConfigKey) {\n\t\t\t\treturn group.registerConfigKey\n\t\t\t}\n\t\t\tif (this.groups.length === 1) {\n\t\t\t\treturn 'register'\n\t\t\t}\n\t\t\treturn group.name.toLowerCase().replace(/[^a-z0-9]+/g, '_') + '_register'\n\t\t},\n\n\t\t/**\n\t\t * Get the config key for a type's schema.\n\t\t *\n\t\t * @param {object} type Type definition\n\t\t * @return {string} Config key\n\t\t */\n\t\tschemaConfigKey(type) {\n\t\t\treturn type.configKey || type.slug + '_schema'\n\t\t},\n\n\t\t/**\n\t\t * Get the selected register option for a group.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {object|null} NcSelect option\n\t\t */\n\t\tselectedRegister(groupIdx) {\n\t\t\tconst key = this.registerConfigKey(groupIdx)\n\t\t\tconst value = String(this.localConfig[key] || '')\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.registerSelectOptions.find((o) => o.value === value) || null\n\t\t},\n\n\t\t/**\n\t\t * Get the selected schema option for a type.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @return {object|null} NcSelect option\n\t\t */\n\t\tselectedSchema(groupIdx, type) {\n\t\t\tconst key = this.schemaConfigKey(type)\n\t\t\tconst value = String(this.localConfig[key] || '')\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst options = this.schemaSelectOptions(groupIdx)\n\t\t\treturn options.find((o) => o.value === value) || null\n\t\t},\n\n\t\t/**\n\t\t * Get the schema value (ID) for a type.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @return {string} Schema ID or empty string\n\t\t */\n\t\tschemaValue(groupIdx, type) {\n\t\t\tconst key = this.schemaConfigKey(type)\n\t\t\treturn this.localConfig[key] || ''\n\t\t},\n\n\t\t/**\n\t\t * Get the display label for a type's current schema.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @return {string} Schema label or empty string\n\t\t */\n\t\tschemaLabel(groupIdx, type) {\n\t\t\tconst selected = this.selectedSchema(groupIdx, type)\n\t\t\tif (selected) {\n\t\t\t\treturn selected.label\n\t\t\t}\n\t\t\tconst value = this.schemaValue(groupIdx, type)\n\t\t\treturn value ? t('nextcloud-vue', 'Schema {id}', { id: value }) : ''\n\t\t},\n\n\t\t/**\n\t\t * Get schema options for a group's selected register.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {Array<{label: string, value: string}>} NcSelect options\n\t\t */\n\t\tschemaSelectOptions(groupIdx) {\n\t\t\tconst reg = this.selectedRegister(groupIdx)\n\t\t\tif (!reg) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst schemas = this.schemasByRegister[reg.value] || []\n\t\t\treturn schemas.map((s) => ({\n\t\t\t\tlabel: s.title || s.slug || t('nextcloud-vue', 'Schema {id}', { id: s.id }),\n\t\t\t\tvalue: String(s.id),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Count configured types in a group.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @return {number} Count\n\t\t */\n\t\tconfiguredCount(groupIdx) {\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\treturn group.types.filter((type) => !!this.schemaValue(groupIdx, type)).length\n\t\t},\n\n\t\t/**\n\t\t * Check if a type row is expanded.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {string} slug Type slug\n\t\t * @return {boolean}\n\t\t */\n\t\tisExpanded(groupIdx, slug) {\n\t\t\treturn !!this.expandedRows[groupIdx + '-' + slug]\n\t\t},\n\n\t\t/**\n\t\t * Toggle a type row expansion.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {string} slug Type slug\n\t\t */\n\t\ttoggleExpand(groupIdx, slug) {\n\t\t\tconst key = groupIdx + '-' + slug\n\t\t\tthis.expandedRows = {\n\t\t\t\t...this.expandedRows,\n\t\t\t\t[key]: !this.expandedRows[key],\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle register selection change for a group.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object|null} option NcSelect option\n\t\t */\n\t\tasync handleRegisterChange(groupIdx, option) {\n\t\t\tconst key = this.registerConfigKey(groupIdx)\n\t\t\tconst value = option?.value || ''\n\n\t\t\tthis.localConfig = { ...this.localConfig, [key]: value }\n\n\t\t\t// Clear schema selections for this group\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\tfor (const type of group.types) {\n\t\t\t\tconst schemaKey = this.schemaConfigKey(type)\n\t\t\t\tthis.localConfig = { ...this.localConfig, [schemaKey]: '' }\n\t\t\t}\n\n\t\t\t// Fetch schemas for the new register\n\t\t\tif (value) {\n\t\t\t\tawait this.loadSchemasForRegister(value)\n\n\t\t\t\t// Auto-match schemas\n\t\t\t\tif (this.autoMatch) {\n\t\t\t\t\tthis.autoMatchSchemas(groupIdx)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.$emit('update:configuration', { ...this.localConfig })\n\t\t},\n\n\t\t/**\n\t\t * Handle schema selection change for a type.\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t * @param {object} type Type definition\n\t\t * @param {object|null} option NcSelect option\n\t\t */\n\t\thandleSchemaChange(groupIdx, type, option) {\n\t\t\tconst key = this.schemaConfigKey(type)\n\t\t\tconst value = option?.value || ''\n\n\t\t\tthis.localConfig = { ...this.localConfig, [key]: value }\n\t\t\tthis.$emit('update:configuration', { ...this.localConfig })\n\t\t},\n\n\t\t/**\n\t\t * Auto-match schema titles to type slugs/labels (case-insensitive).\n\t\t *\n\t\t * @param {number} groupIdx Group index\n\t\t */\n\t\tautoMatchSchemas(groupIdx) {\n\t\t\tconst group = this.groups[groupIdx]\n\t\t\tconst options = this.schemaSelectOptions(groupIdx)\n\n\t\t\tfor (const type of group.types) {\n\t\t\t\tconst schemaKey = this.schemaConfigKey(type)\n\t\t\t\t// Skip if already has a value\n\t\t\t\tif (this.localConfig[schemaKey]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst slug = type.slug.toLowerCase()\n\t\t\t\tconst label = type.label.toLowerCase()\n\n\t\t\t\tconst match = options.find((o) => {\n\t\t\t\t\tconst optLabel = o.label.toLowerCase()\n\t\t\t\t\treturn optLabel === slug || optLabel === label\n\t\t\t\t\t\t|| optLabel.includes(slug) || slug.includes(optLabel)\n\t\t\t\t})\n\n\t\t\t\tif (match) {\n\t\t\t\t\tthis.localConfig = { ...this.localConfig, [schemaKey]: match.value }\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** Emit save event with current config */\n\t\thandleSave() {\n\t\t\tthis.$emit('save', { ...this.localConfig })\n\t\t},\n\n\t\t/**\n\t\t * Fetch all registers from OpenRegister API.\n\t\t */\n\t\tasync loadRegisters() {\n\t\t\tthis.registersLoading = true\n\t\t\tthis.registersError = null\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl('/apps/openregister/api/registers?_extend[]=schemas'), {\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthis.registersError = t('nextcloud-vue', 'The registers could not be loaded: {reason}', { reason: response.statusText || response.status })\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst results = data.results || data\n\t\t\t\tthis.registers = Array.isArray(results) ? results : []\n\n\t\t\t\t// Cache expanded schemas\n\t\t\t\tfor (const reg of this.registers) {\n\t\t\t\t\tif (Array.isArray(reg.schemas) && reg.schemas.length > 0) {\n\t\t\t\t\t\tconst schemas = reg.schemas.filter((s) => s && typeof s === 'object' && s.id)\n\t\t\t\t\t\tif (schemas.length > 0) {\n\t\t\t\t\t\t\tthis.schemasByRegister = {\n\t\t\t\t\t\t\t\t...this.schemasByRegister,\n\t\t\t\t\t\t\t\t[String(reg.id)]: schemas,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.registersError = error.message || t('nextcloud-vue', 'The registers could not be loaded: {reason}', { reason: t('nextcloud-vue', 'no connection') })\n\t\t\t} finally {\n\t\t\t\tthis.registersLoading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fetch schemas for a specific register.\n\t\t *\n\t\t * @param {string} registerId Register ID\n\t\t */\n\t\tasync loadSchemasForRegister(registerId) {\n\t\t\tconst id = String(registerId)\n\n\t\t\t// Return cached\n\t\t\tif (this.schemasByRegister[id]?.length > 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`/apps/openregister/api/registers/${encodeURIComponent(id)}?_extend[]=schemas`),\n\t\t\t\t\t{ method: 'GET', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst schemas = (data.schemas || []).filter((s) => s && typeof s === 'object' && s.id)\n\t\t\t\tthis.schemasByRegister = { ...this.schemasByRegister, [id]: schemas }\n\t\t\t} catch {\n\t\t\t\t// Silently fail — register already selected, schemas just won't populate\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-register-mapping__group {\n\tmargin-bottom: 24px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 20px;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__group:last-child {\n\tmargin-bottom: 0;\n}\n\n.cn-register-mapping__group-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin-bottom: 12px;\n}\n\n.cn-register-mapping__group-heading {\n\tmargin: 0 0 12px;\n\tfont: inherit;\n}\n\n.cn-register-mapping__group--collapsed .cn-register-mapping__group-heading {\n\tmargin-bottom: 0;\n}\n\n.cn-register-mapping__group-toggle {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\twidth: 100%;\n\tmin-height: 44px;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: transparent;\n\tcolor: inherit;\n\tfont: inherit;\n\ttext-align: start;\n\tcursor: pointer;\n}\n\n.cn-register-mapping__group-toggle:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n\tborder-radius: var(--border-radius);\n}\n\n.cn-register-mapping__group-chevron {\n\tdisplay: inline-flex;\n\tflex: none;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-register-mapping__group-toggle .cn-register-mapping__group-header {\n\tflex: 1 1 auto;\n\tflex-wrap: wrap;\n\tgap: 4px 12px;\n\tmin-width: 0;\n\tmargin-bottom: 0;\n}\n\n.cn-register-mapping__group-title {\n\tfont-size: 16px;\n\tfont-weight: 600;\n\tmargin: 0;\n\tcolor: var(--color-main-text);\n}\n\n.cn-register-mapping__group-status {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-register-mapping__group-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n\tmargin: 0 0 12px 0;\n}\n\n.cn-register-mapping__register-select {\n\tmargin-bottom: 16px;\n\tmax-width: 400px;\n}\n\n.cn-register-mapping__type-list {\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\toverflow: hidden;\n\tbackground: var(--color-main-background);\n}\n\n.cn-register-mapping__type-list-header {\n\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr 40px 32px;\n\talign-items: center;\n\tpadding: 8px 16px;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.5px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__type-row {\n\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr 40px 32px;\n\talign-items: center;\n\tpadding: 10px 16px;\n\tborder-bottom: 1px solid var(--color-border);\n\tcursor: pointer;\n\ttransition: background-color 0.15s ease;\n}\n\n.cn-register-mapping__type-row:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__type-row--expanded {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-register-mapping__type-name {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n}\n\n.cn-register-mapping__type-schema {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-register-mapping__type-status {\n\tdisplay: flex;\n\tjustify-content: center;\n}\n\n.cn-register-mapping__type-chevron {\n\tdisplay: flex;\n\tjustify-content: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-register-mapping__status-dot {\n\tdisplay: inline-block;\n\twidth: 10px;\n\theight: 10px;\n\tborder-radius: 50%;\n}\n\n.cn-register-mapping__status-dot--configured {\n\tbackground-color: var(--color-success);\n}\n\n.cn-register-mapping__status-dot--unconfigured {\n\tbackground-color: var(--color-warning);\n}\n\n.cn-register-mapping__type-detail {\n\tpadding: 12px 16px 16px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-main-background);\n}\n\n.cn-register-mapping__type-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n\tmargin: 0 0 8px 0;\n}\n\n/* Slide transition */\n.slide-enter-active,\n.slide-leave-active {\n\ttransition: all 0.2s ease;\n\tmax-height: 200px;\n\toverflow: hidden;\n}\n\n.slide-enter,\n.slide-leave-to {\n\tmax-height: 0;\n\tpadding-top: 0;\n\tpadding-bottom: 0;\n\topacity: 0;\n}\n\n/* Last row in list should not have bottom border */\n.cn-register-mapping__type-list > :last-child {\n\tborder-bottom: none;\n}\n\n@media (max-width: 768px) {\n\t.cn-register-mapping__type-list-header {\n\t\tdisplay: none;\n\t}\n\n\t.cn-register-mapping__type-row {\n\t\tgrid-template-columns: 1fr auto 32px;\n\t}\n\n\t.cn-register-mapping__type-schema {\n\t\tdisplay: none;\n\t}\n\n\t.cn-register-mapping__register-select {\n\t\tmax-width: none;\n\t}\n}\n</style>\n"],"names":["CnSettingsSection","t"],"mappings":"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"}
|