@conduction/nextcloud-vue 2.114.0 → 2.115.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue.js +1 -1
  2. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js +1 -1
  3. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js.map +1 -1
  4. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue3.js.map +1 -1
  5. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  6. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +6 -2
  7. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  8. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  9. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  10. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +2 -1
  11. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  12. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  13. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  14. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +19 -2
  15. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  16. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -3
  17. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  18. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  19. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +2 -2
  20. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  21. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  22. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +37 -1
  23. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  24. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  25. package/dist/esm/components/CnIndexPage/rowsToCsv.js +90 -0
  26. package/dist/esm/components/CnIndexPage/rowsToCsv.js.map +1 -0
  27. package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue.js +9 -0
  28. package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue.js.map +1 -0
  29. package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue2.js +161 -0
  30. package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue2.js.map +1 -0
  31. package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue3.js +97 -0
  32. package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue3.js.map +1 -0
  33. package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue.js +9 -0
  34. package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue.js.map +1 -0
  35. package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue2.js +63 -0
  36. package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue2.js.map +1 -0
  37. package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue3.js +54 -0
  38. package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue3.js.map +1 -0
  39. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  40. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +15 -9
  41. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  42. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +55 -63
  43. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  44. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +1 -1
  45. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +2 -1
  46. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  47. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  48. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  49. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +13 -4
  50. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  51. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +2 -1
  52. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  53. package/dist/esm/components/CnStepper/CnStepper.vue.js +1 -1
  54. package/dist/esm/components/CnStepper/CnStepper.vue2.js.map +1 -1
  55. package/dist/esm/components/CnStepper/CnStepper.vue3.js.map +1 -1
  56. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  57. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +10 -5
  58. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +1 -1
  60. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue.js +1 -1
  62. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +2 -2
  64. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +32 -20
  66. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  67. package/dist/esm/composables/indexSources.js +43 -11
  68. package/dist/esm/composables/indexSources.js.map +1 -1
  69. package/dist/esm/composables/useTaskInboxStore.js +41 -0
  70. package/dist/esm/composables/useTaskInboxStore.js.map +1 -1
  71. package/dist/esm/index.js +1 -0
  72. package/dist/esm/index.js.map +1 -1
  73. package/dist/esm/l10n/en.json.js +4 -1
  74. package/dist/esm/l10n/en.json.js.map +1 -1
  75. package/dist/esm/l10n/nl.json.js +9 -1
  76. package/dist/esm/l10n/nl.json.js.map +1 -1
  77. package/dist/esm/library-style.js +1 -1
  78. package/dist/esm/nextcloud-vue.css +482 -330
  79. package/dist/esm/utils/contentHeaderLink.js +37 -1
  80. package/dist/esm/utils/contentHeaderLink.js.map +1 -1
  81. package/dist/esm/utils/declaredMacros.js +31 -0
  82. package/dist/esm/utils/declaredMacros.js.map +1 -0
  83. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  84. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  85. package/dist/nextcloud-vue.cjs.js +2673 -1977
  86. package/dist/nextcloud-vue.cjs.js.map +2 -2
  87. package/dist/nextcloud-vue.css +482 -330
  88. package/l10n/en.json +4 -1
  89. package/l10n/nl.json +9 -1
  90. package/package.json +1 -1
  91. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +1 -1
  92. package/src/components/CnCellRenderer/CnCellRenderer.vue +6 -2
  93. package/src/components/CnChoiceCards/CnChoiceCards.vue +2 -1
  94. package/src/components/CnDashboardPage/CnDashboardPage.vue +45 -3
  95. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
  96. package/src/components/CnIndexPage/CnIndexPage.vue +37 -1
  97. package/src/components/CnIndexPage/rowsToCsv.js +87 -0
  98. package/src/components/CnMacrosWidget/CnMacrosWidget.md +30 -0
  99. package/src/components/CnMacrosWidget/CnMacrosWidget.vue +266 -0
  100. package/src/components/CnMacrosWidget/index.js +6 -0
  101. package/src/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue +104 -0
  102. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +53 -38
  103. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +2 -1
  104. package/src/components/CnSetupWizard/CnSetupWizard.vue +28 -4
  105. package/src/components/CnStepper/CnStepper.vue +2 -1
  106. package/src/components/CnTabsWidget/CnTabsWidget.vue +11 -6
  107. package/src/components/CnTasksWidget/CnTasksWidget.vue +2 -2
  108. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  109. package/src/components/index.js +1 -0
  110. package/src/composables/indexSources.js +43 -11
  111. package/src/composables/useTaskInboxStore.js +41 -0
  112. package/src/css/look-board-detail.css +4 -3
  113. package/src/css/look-board-index.css +62 -0
  114. package/src/css/look-board.css +20 -0
  115. package/src/index.js +1 -0
  116. package/src/utils/contentHeaderLink.js +35 -0
  117. package/src/utils/declaredMacros.js +28 -0
  118. package/src/utils/libraryWidgetKeys.js +1 -0
@@ -2,7 +2,9 @@ import { getCurrentUser } from '@nextcloud/auth';
2
2
  import axios__default from '@nextcloud/axios';
3
3
  import { translate } from '@nextcloud/l10n';
4
4
  import { generateUrl } from '@nextcloud/router';
5
- import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField } from '@nextcloud/vue';
5
+ import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
6
+ import Refresh from 'vue-material-design-icons/Refresh.vue';
7
+ import __sfc_main from './CnRetentionRecomputeDialog.vue.js';
6
8
  import { readPath } from '../../utils/readPath.js';
7
9
 
8
10
  /**
@@ -31,7 +33,7 @@ import { readPath } from '../../utils/readPath.js';
31
33
  var script = {
32
34
  name: 'CnRetentionWidget',
33
35
 
34
- components: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField },
36
+ components: { CnRetentionRecomputeDialog: __sfc_main, NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, Refresh },
35
37
 
36
38
  inject: {
37
39
  /** The host app's translate (CnAppRoot `cnTranslate`). */
@@ -87,7 +89,8 @@ var script = {
87
89
  loading: true,
88
90
  error: '',
89
91
  record: null,
90
- reason: '',
92
+ /** Whether the recompute dialog is open. */
93
+ recomputeOpen: false,
91
94
  busy: false,
92
95
  }
93
96
  },
@@ -249,19 +252,22 @@ var script = {
249
252
  },
250
253
 
251
254
  /**
252
- * Ask OpenRegister to derive the nomination again, then read it back.
255
+ * Ask OpenRegister to derive the nomination again with the reason the
256
+ * dialog asked for, then read it back.
253
257
  *
254
- * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention
258
+ * @spec openspec/changes/retention-recompute-header/specs/retention-recompute-header/spec.md#requirement-recomputing-the-nomination-starts-in-the-card-header-and-asks-for-a-reason
259
+ * @param {string} reason Why it is recomputed.
255
260
  * @return {Promise<void>}
256
261
  */
257
- async recompute() {
258
- if (this.busy || this.reason.trim() === '') {
262
+ async recompute(reason) {
263
+ const why = String(reason ?? '').trim();
264
+ if (this.busy || why === '') {
259
265
  return
260
266
  }
261
267
  this.busy = true;
262
268
  try {
263
- await axios__default.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: this.reason.trim() });
264
- this.reason = '';
269
+ await axios__default.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: why });
270
+ this.recomputeOpen = false;
265
271
  await this.load();
266
272
  } catch (failure) {
267
273
  const body = failure && failure.response && failure.response.data;
@@ -1 +1 @@
1
- {"version":3,"file":"CnRetentionWidget.vue2.js","sources":["../../../../src/components/CnRetentionWidget/CnRetentionWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-retention\" data-testid=\"cn-retention\">\n\t\t<header class=\"cn-retention__head\">\n\t\t\t<h3 class=\"cn-retention__title\">\n\t\t\t\t{{ text(content.title) || t('nextcloud-vue', 'Archiving') }}\n\t\t\t</h3>\n\t\t\t<p class=\"cn-retention__muted\">\n\t\t\t\t{{ text(content.subtitle) || t('nextcloud-vue', 'What happens to this record once it is closed') }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\t\t<NcEmptyContent v-else-if=\"error\"\n\t\t\t:name=\"t('nextcloud-vue', 'The archival facts are unavailable')\"\n\t\t\t:description=\"error\"\n\t\t\tdata-testid=\"cn-retention-error\">\n\t\t\t<template #action>\n\t\t\t\t<NcButton data-testid=\"cn-retention-retry\" @click=\"load\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Try again') }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<p v-if=\"archived\" class=\"cn-retention__archived\" data-testid=\"cn-retention-archived\">\n\t\t\t\t{{ t('nextcloud-vue', 'Archived on {at} by {by}. {reason}', { at: String(archived.at ?? ''), by: String(archived.by ?? ''), reason: String(archived.reason ?? '') }) }}\n\t\t\t</p>\n\t\t\t<NcNoteCard v-if=\"disagrees\" type=\"warning\" data-testid=\"cn-retention-disagreement\">\n\t\t\t\t{{ t('nextcloud-vue', 'This record and OpenRegister do not agree on whether it is archived.') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<NcNoteCard v-if=\"nomination && nomination.status === 'unnominatable'\" type=\"warning\" data-testid=\"cn-retention-unnominatable\">\n\t\t\t\t{{ t('nextcloud-vue', 'Nothing could decide what happens to this record.') }}\n\t\t\t\t{{ String(nomination.unnominatableReason ?? '') }}\n\t\t\t</NcNoteCard>\n\t\t\t<div v-else-if=\"!nomination\" data-testid=\"cn-retention-pending\">\n\t\t\t\t<dl class=\"cn-retention__facts\">\n\t\t\t\t\t<div v-for=\"fact in pendingFacts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t\t<NcNoteCard type=\"info\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'This record has not been closed yet. Its archival future is recorded when it closes.') }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</div>\n\t\t\t<dl v-else class=\"cn-retention__facts\" data-testid=\"cn-retention-facts\">\n\t\t\t\t<div v-for=\"fact in facts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t<dd :data-testid=\"`cn-retention-${fact.key}`\">\n\t\t\t\t\t\t{{ fact.value }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\n\t\t\t<div v-if=\"outcome\" class=\"cn-retention__outcome\" data-testid=\"cn-retention-outcome\">\n\t\t\t\t<h4>{{ t('nextcloud-vue', 'Outcome') }}</h4>\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Handed over as {kind} on {at} by {by}.', { kind: String(outcome.kind ?? ''), at: String(outcome.at ?? ''), by: String(outcome.by ?? '') }) }}</p>\n\t\t\t\t<p v-if=\"outcome.transferListUuid\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Transfer list') }}: {{ outcome.transferListUuid }}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"content.recompute !== false\" class=\"cn-retention__recompute\">\n\t\t\t\t<NcButton v-if=\"!mayRecompute\" disabled data-testid=\"cn-retention-recompute-denied\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Recomputing needs the archivist role') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<template v-else>\n\t\t\t\t\t<NcTextField :modelValue=\"reason\"\n\t\t\t\t\t\t:label=\"t('nextcloud-vue', 'Why are you recomputing this?')\"\n\t\t\t\t\t\tdata-testid=\"cn-retention-reason\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => (reason = String(v ?? ''))\" />\n\t\t\t\t\t<p class=\"cn-retention__muted\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'This is recorded against {user}.', { user: currentUid }) }}\n\t\t\t\t\t</p>\n\t\t\t\t\t<NcButton :disabled=\"busy || reason.trim() === ''\" data-testid=\"cn-retention-recompute\" @click=\"recompute\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Recompute the nomination') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnRetentionWidget — the `retention` detail widget: what happens to a\n * record when its business use ends, as OpenRegister decided it. Every value\n * is read off the record's `@self._retention` (appraisal, disposal date,\n * retention period, selection list row, the rule and when it was written)\n * and `@self.archived`; the widget derives nothing, so it can never disagree\n * with the stored decision. A failed read is an error with a retry, never an\n * empty block. A record nothing could nominate is drawn apart from one that\n * is not closed yet. An archivist (or an admin) may ask OpenRegister to\n * recompute the nomination, with a reason. Any app on OpenRegister uses it;\n * dossiq's case Archiving tab is the first.\n *\n * `content`:\n * - `title`, `subtitle`: texts, through the host translate.\n * - `statusField`, `archivedValues`: a record field and the values that mean\n * archived (`archiveStatus`, `[\"archived\", ...]`); when set, a mismatch with\n * `@self.archived` is said out loud.\n * - `archivistGroups`: groups that may recompute (default `[\"archivaris\"]`;\n * admins always may); `recompute: false` hides the control.\n *\n * The record is read fresh from OpenRegister by `register`, `schema` and\n * `objectId` (props, else the bound record's `@self`).\n */\nexport default {\n\tname: 'CnRetentionWidget',\n\n\tcomponents: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration (see the component docs).\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/**\n\t\t * The bound record, on a detail page.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The record's register (else the bound record's `@self.register`). */\n\t\tregister: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record's schema (else the bound record's `@self.schema`); a schema object gives its slug.\n\t\t *\n\t\t * @type {string|number|object}\n\t\t */\n\t\tschema: {\n\t\t\ttype: [String, Number, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The record's id (else the bound record's). */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\trecord: null,\n\t\t\treason: '',\n\t\t\tbusy: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The bound record's `@self`. */\n\t\tboundSelf() {\n\t\t\treturn (this.objectData && this.objectData['@self']) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tid() {\n\t\t\treturn String(this.objectId || (this.objectData && this.objectData.id) || this.boundSelf.id || '')\n\t\t},\n\n\t\t/** @return {string} The register. */\n\t\tregisterRef() {\n\t\t\treturn String(this.register || this.boundSelf.register || '')\n\t\t},\n\n\t\t/** @return {string} The schema. */\n\t\tschemaRef() {\n\t\t\tconst s = this.schema || this.boundSelf.schema || ''\n\t\t\treturn String(typeof s === 'object' ? (s.slug || s.id || '') : s)\n\t\t},\n\n\t\t/** @return {object} The record's `@self`. */\n\t\tself() {\n\t\t\treturn (this.record && this.record['@self']) || {}\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's stored retention decision. */\n\t\tretention() {\n\t\t\treturn this.self._retention || null\n\t\t},\n\n\t\t/** @return {object|null} The nomination. */\n\t\tnomination() {\n\t\t\treturn (this.retention && this.retention.nomination) || null\n\t\t},\n\n\t\t/** @return {object|null} The hand-over outcome. */\n\t\toutcome() {\n\t\t\treturn (this.retention && this.retention.outcome) || null\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's archived marker. */\n\t\tarchived() {\n\t\t\treturn this.self.archived || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the record's own field and the platform marker disagree.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {boolean}\n\t\t */\n\t\tdisagrees() {\n\t\t\tconst field = this.content.statusField\n\t\t\tif (!field || !this.record) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst values = Array.isArray(this.content.archivedValues) ? this.content.archivedValues.map(String) : ['archived']\n\t\t\tconst saysArchived = values.includes(String(readPath(this.record, field) ?? ''))\n\t\t\treturn saysArchived !== Boolean(this.archived)\n\t\t},\n\n\t\t/**\n\t\t * The stored facts that have a value, in reading order.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Array<{key: string, label: string, value: string}>}\n\t\t */\n\t\tfacts() {\n\t\t\tconst r = this.retention || {}\n\t\t\tconst n = this.nomination || {}\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: r.appraisal },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: r.disposalDate },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: r.retentionPeriod },\n\t\t\t\t{ key: 'selection-list-row', label: t('nextcloud-vue', 'Selection list row'), value: r.selectionListRow },\n\t\t\t\t{ key: 'rule', label: t('nextcloud-vue', 'Decided by'), value: n.rule },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: n.at },\n\t\t\t].filter((fact) => fact.value !== undefined && fact.value !== null && fact.value !== '').map((fact) => ({ ...fact, value: String(fact.value) }))\n\t\t},\n\n\t\t/** @return {Array<{key: string, label: string, value: string}>} The facts of a record not closed yet. */\n\t\tpendingFacts() {\n\t\t\tconst atClosure = t('nextcloud-vue', 'Decided when it closes')\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: atClosure },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: atClosure },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: atClosure },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: t('nextcloud-vue', 'Not yet') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} The signed-in user's id. */\n\t\tcurrentUid() {\n\t\t\tconst user = getCurrentUser()\n\t\t\treturn String((user && user.uid) || '')\n\t\t},\n\n\t\t/**\n\t\t * Whether this user may recompute: an admin or an archivist group member.\n\t\t * Shown disabled otherwise, so the user learns which role it needs.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tmayRecompute() {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (user && user.isAdmin === true) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst groups = Array.isArray(user && user.groups) ? user.groups : []\n\t\t\tconst allowed = Array.isArray(this.content.archivistGroups) ? this.content.archivistGroups : ['archivaris']\n\t\t\treturn allowed.some((g) => groups.includes(g))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the record fresh from OpenRegister; a failure is an error, never an empty block.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tif (!this.registerRef || !this.schemaRef || !this.id) {\n\t\t\t\t\tthrow new Error(t('nextcloud-vue', 'This widget has no record to read.'))\n\t\t\t\t}\n\t\t\t\tconst { data } = await axios.get(generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(this.registerRef)}/${encodeURIComponent(this.schemaRef)}/${encodeURIComponent(this.id)}`))\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : null\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Ask OpenRegister to derive the nomination again, then read it back.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync recompute() {\n\t\t\tif (this.busy || this.reason.trim() === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\ttry {\n\t\t\t\tawait axios.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: this.reason.trim() })\n\t\t\t\tthis.reason = ''\n\t\t\t\tawait this.load()\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-retention {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-retention__head {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-retention__title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-retention__muted {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-retention__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-retention__fact dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-retention__fact dd {\n\tmargin: 0;\n\tfont-weight: 600;\n}\n\n.cn-retention__recompute {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmax-width: 480px;\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}
1
+ {"version":3,"file":"CnRetentionWidget.vue2.js","sources":["../../../../src/components/CnRetentionWidget/CnRetentionWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-retention\" data-testid=\"cn-retention\">\n\t\t<header class=\"cn-retention__head\">\n\t\t\t<div class=\"cn-retention__head-text\">\n\t\t\t\t<h3 class=\"cn-retention__title\">\n\t\t\t\t\t{{ text(content.title) || t('nextcloud-vue', 'Archiving') }}\n\t\t\t\t</h3>\n\t\t\t\t<p class=\"cn-retention__muted\">\n\t\t\t\t\t{{ text(content.subtitle) || t('nextcloud-vue', 'What happens to this record once it is closed') }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<!-- Recompute starts in the header (board DqZaakArchivering); the\n\t\t\t dialog asks for the reason OpenRegister records with it. -->\n\t\t\t<NcButton\n\t\t\t\tv-if=\"content.recompute !== false && !loading && !error\"\n\t\t\t\t:disabled=\"!mayRecompute || busy\"\n\t\t\t\t:title=\"mayRecompute ? undefined : t('nextcloud-vue', 'Recomputing needs the archivist role')\"\n\t\t\t\t:aria-description=\"mayRecompute ? undefined : t('nextcloud-vue', 'Recomputing needs the archivist role')\"\n\t\t\t\t:data-testid=\"mayRecompute ? 'cn-retention-recompute-open' : 'cn-retention-recompute-denied'\"\n\t\t\t\t@click=\"recomputeOpen = true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Refresh :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Recompute nomination') }}\n\t\t\t</NcButton>\n\t\t</header>\n\t\t<CnRetentionRecomputeDialog\n\t\t\tv-if=\"recomputeOpen\"\n\t\t\t:user=\"currentUid\"\n\t\t\t:busy=\"busy\"\n\t\t\t@close=\"recomputeOpen = false\"\n\t\t\t@confirm=\"recompute\" />\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\t\t<NcEmptyContent v-else-if=\"error\"\n\t\t\t:name=\"t('nextcloud-vue', 'The archival facts are unavailable')\"\n\t\t\t:description=\"error\"\n\t\t\tdata-testid=\"cn-retention-error\">\n\t\t\t<template #action>\n\t\t\t\t<NcButton data-testid=\"cn-retention-retry\" @click=\"load\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Try again') }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<p v-if=\"archived\" class=\"cn-retention__archived\" data-testid=\"cn-retention-archived\">\n\t\t\t\t{{ t('nextcloud-vue', 'Archived on {at} by {by}. {reason}', { at: String(archived.at ?? ''), by: String(archived.by ?? ''), reason: String(archived.reason ?? '') }) }}\n\t\t\t</p>\n\t\t\t<NcNoteCard v-if=\"disagrees\" type=\"warning\" data-testid=\"cn-retention-disagreement\">\n\t\t\t\t{{ t('nextcloud-vue', 'This record and OpenRegister do not agree on whether it is archived.') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<NcNoteCard v-if=\"nomination && nomination.status === 'unnominatable'\" type=\"warning\" data-testid=\"cn-retention-unnominatable\">\n\t\t\t\t{{ t('nextcloud-vue', 'Nothing could decide what happens to this record.') }}\n\t\t\t\t{{ String(nomination.unnominatableReason ?? '') }}\n\t\t\t</NcNoteCard>\n\t\t\t<div v-else-if=\"!nomination\" data-testid=\"cn-retention-pending\">\n\t\t\t\t<dl class=\"cn-retention__facts\">\n\t\t\t\t\t<div v-for=\"fact in pendingFacts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t\t<NcNoteCard type=\"info\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'This record has not been closed yet. Its archival future is recorded when it closes.') }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</div>\n\t\t\t<dl v-else class=\"cn-retention__facts\" data-testid=\"cn-retention-facts\">\n\t\t\t\t<div v-for=\"fact in facts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t<dd :data-testid=\"`cn-retention-${fact.key}`\">\n\t\t\t\t\t\t{{ fact.value }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\n\t\t\t<div v-if=\"outcome\" class=\"cn-retention__outcome\" data-testid=\"cn-retention-outcome\">\n\t\t\t\t<h4>{{ t('nextcloud-vue', 'Outcome') }}</h4>\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Handed over as {kind} on {at} by {by}.', { kind: String(outcome.kind ?? ''), at: String(outcome.at ?? ''), by: String(outcome.by ?? '') }) }}</p>\n\t\t\t\t<p v-if=\"outcome.transferListUuid\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Transfer list') }}: {{ outcome.transferListUuid }}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport Refresh from 'vue-material-design-icons/Refresh.vue'\nimport CnRetentionRecomputeDialog from './CnRetentionRecomputeDialog.vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnRetentionWidget — the `retention` detail widget: what happens to a\n * record when its business use ends, as OpenRegister decided it. Every value\n * is read off the record's `@self._retention` (appraisal, disposal date,\n * retention period, selection list row, the rule and when it was written)\n * and `@self.archived`; the widget derives nothing, so it can never disagree\n * with the stored decision. A failed read is an error with a retry, never an\n * empty block. A record nothing could nominate is drawn apart from one that\n * is not closed yet. An archivist (or an admin) may ask OpenRegister to\n * recompute the nomination, with a reason. Any app on OpenRegister uses it;\n * dossiq's case Archiving tab is the first.\n *\n * `content`:\n * - `title`, `subtitle`: texts, through the host translate.\n * - `statusField`, `archivedValues`: a record field and the values that mean\n * archived (`archiveStatus`, `[\"archived\", ...]`); when set, a mismatch with\n * `@self.archived` is said out loud.\n * - `archivistGroups`: groups that may recompute (default `[\"archivaris\"]`;\n * admins always may); `recompute: false` hides the control.\n *\n * The record is read fresh from OpenRegister by `register`, `schema` and\n * `objectId` (props, else the bound record's `@self`).\n */\nexport default {\n\tname: 'CnRetentionWidget',\n\n\tcomponents: { CnRetentionRecomputeDialog, NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, Refresh },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration (see the component docs).\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/**\n\t\t * The bound record, on a detail page.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The record's register (else the bound record's `@self.register`). */\n\t\tregister: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record's schema (else the bound record's `@self.schema`); a schema object gives its slug.\n\t\t *\n\t\t * @type {string|number|object}\n\t\t */\n\t\tschema: {\n\t\t\ttype: [String, Number, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The record's id (else the bound record's). */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\trecord: null,\n\t\t\t/** Whether the recompute dialog is open. */\n\t\t\trecomputeOpen: false,\n\t\t\tbusy: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The bound record's `@self`. */\n\t\tboundSelf() {\n\t\t\treturn (this.objectData && this.objectData['@self']) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tid() {\n\t\t\treturn String(this.objectId || (this.objectData && this.objectData.id) || this.boundSelf.id || '')\n\t\t},\n\n\t\t/** @return {string} The register. */\n\t\tregisterRef() {\n\t\t\treturn String(this.register || this.boundSelf.register || '')\n\t\t},\n\n\t\t/** @return {string} The schema. */\n\t\tschemaRef() {\n\t\t\tconst s = this.schema || this.boundSelf.schema || ''\n\t\t\treturn String(typeof s === 'object' ? (s.slug || s.id || '') : s)\n\t\t},\n\n\t\t/** @return {object} The record's `@self`. */\n\t\tself() {\n\t\t\treturn (this.record && this.record['@self']) || {}\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's stored retention decision. */\n\t\tretention() {\n\t\t\treturn this.self._retention || null\n\t\t},\n\n\t\t/** @return {object|null} The nomination. */\n\t\tnomination() {\n\t\t\treturn (this.retention && this.retention.nomination) || null\n\t\t},\n\n\t\t/** @return {object|null} The hand-over outcome. */\n\t\toutcome() {\n\t\t\treturn (this.retention && this.retention.outcome) || null\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's archived marker. */\n\t\tarchived() {\n\t\t\treturn this.self.archived || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the record's own field and the platform marker disagree.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {boolean}\n\t\t */\n\t\tdisagrees() {\n\t\t\tconst field = this.content.statusField\n\t\t\tif (!field || !this.record) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst values = Array.isArray(this.content.archivedValues) ? this.content.archivedValues.map(String) : ['archived']\n\t\t\tconst saysArchived = values.includes(String(readPath(this.record, field) ?? ''))\n\t\t\treturn saysArchived !== Boolean(this.archived)\n\t\t},\n\n\t\t/**\n\t\t * The stored facts that have a value, in reading order.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Array<{key: string, label: string, value: string}>}\n\t\t */\n\t\tfacts() {\n\t\t\tconst r = this.retention || {}\n\t\t\tconst n = this.nomination || {}\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: r.appraisal },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: r.disposalDate },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: r.retentionPeriod },\n\t\t\t\t{ key: 'selection-list-row', label: t('nextcloud-vue', 'Selection list row'), value: r.selectionListRow },\n\t\t\t\t{ key: 'rule', label: t('nextcloud-vue', 'Decided by'), value: n.rule },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: n.at },\n\t\t\t].filter((fact) => fact.value !== undefined && fact.value !== null && fact.value !== '').map((fact) => ({ ...fact, value: String(fact.value) }))\n\t\t},\n\n\t\t/** @return {Array<{key: string, label: string, value: string}>} The facts of a record not closed yet. */\n\t\tpendingFacts() {\n\t\t\tconst atClosure = t('nextcloud-vue', 'Decided when it closes')\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: atClosure },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: atClosure },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: atClosure },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: t('nextcloud-vue', 'Not yet') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} The signed-in user's id. */\n\t\tcurrentUid() {\n\t\t\tconst user = getCurrentUser()\n\t\t\treturn String((user && user.uid) || '')\n\t\t},\n\n\t\t/**\n\t\t * Whether this user may recompute: an admin or an archivist group member.\n\t\t * Shown disabled otherwise, so the user learns which role it needs.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tmayRecompute() {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (user && user.isAdmin === true) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst groups = Array.isArray(user && user.groups) ? user.groups : []\n\t\t\tconst allowed = Array.isArray(this.content.archivistGroups) ? this.content.archivistGroups : ['archivaris']\n\t\t\treturn allowed.some((g) => groups.includes(g))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the record fresh from OpenRegister; a failure is an error, never an empty block.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tif (!this.registerRef || !this.schemaRef || !this.id) {\n\t\t\t\t\tthrow new Error(t('nextcloud-vue', 'This widget has no record to read.'))\n\t\t\t\t}\n\t\t\t\tconst { data } = await axios.get(generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(this.registerRef)}/${encodeURIComponent(this.schemaRef)}/${encodeURIComponent(this.id)}`))\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : null\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Ask OpenRegister to derive the nomination again with the reason the\n\t\t * dialog asked for, then read it back.\n\t\t *\n\t\t * @spec openspec/changes/retention-recompute-header/specs/retention-recompute-header/spec.md#requirement-recomputing-the-nomination-starts-in-the-card-header-and-asks-for-a-reason\n\t\t * @param {string} reason Why it is recomputed.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync recompute(reason) {\n\t\t\tconst why = String(reason ?? '').trim()\n\t\t\tif (this.busy || why === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\ttry {\n\t\t\t\tawait axios.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: why })\n\t\t\t\tthis.recomputeOpen = false\n\t\t\t\tawait this.load()\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-retention {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-retention__head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-retention__head-text {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-retention__title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-retention__muted {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-retention__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-retention__fact dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-retention__fact dd {\n\tmargin: 0;\n\tfont-weight: 600;\n}\n\n</style>\n"],"names":["CnRetentionRecomputeDialog","t","axios"],"mappings":"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"}
@@ -1,60 +1,86 @@
1
- import { resolveComponent, openBlock, createElementBlock, createElementVNode, toDisplayString, createBlock, withCtx, createVNode, createTextVNode, Fragment, createCommentVNode, renderList } from 'vue';
1
+ import { resolveComponent, openBlock, createElementBlock, createElementVNode, toDisplayString, createBlock, withCtx, createVNode, createTextVNode, createCommentVNode, Fragment, renderList } from 'vue';
2
2
 
3
3
  const _hoisted_1 = {
4
4
  class: "cn-retention",
5
5
  "data-testid": "cn-retention"
6
6
  };
7
7
  const _hoisted_2 = { class: "cn-retention__head" };
8
- const _hoisted_3 = { class: "cn-retention__title" };
9
- const _hoisted_4 = { class: "cn-retention__muted" };
10
- const _hoisted_5 = {
8
+ const _hoisted_3 = { class: "cn-retention__head-text" };
9
+ const _hoisted_4 = { class: "cn-retention__title" };
10
+ const _hoisted_5 = { class: "cn-retention__muted" };
11
+ const _hoisted_6 = {
11
12
  key: 0,
12
13
  class: "cn-retention__archived",
13
14
  "data-testid": "cn-retention-archived"
14
15
  };
15
- const _hoisted_6 = {
16
+ const _hoisted_7 = {
16
17
  key: 3,
17
18
  "data-testid": "cn-retention-pending"
18
19
  };
19
- const _hoisted_7 = { class: "cn-retention__facts" };
20
- const _hoisted_8 = {
20
+ const _hoisted_8 = { class: "cn-retention__facts" };
21
+ const _hoisted_9 = {
21
22
  key: 4,
22
23
  class: "cn-retention__facts",
23
24
  "data-testid": "cn-retention-facts"
24
25
  };
25
- const _hoisted_9 = ["data-testid"];
26
- const _hoisted_10 = {
26
+ const _hoisted_10 = ["data-testid"];
27
+ const _hoisted_11 = {
27
28
  key: 5,
28
29
  class: "cn-retention__outcome",
29
30
  "data-testid": "cn-retention-outcome"
30
31
  };
31
- const _hoisted_11 = { key: 0 };
32
- const _hoisted_12 = {
33
- key: 6,
34
- class: "cn-retention__recompute"
35
- };
36
- const _hoisted_13 = { class: "cn-retention__muted" };
32
+ const _hoisted_12 = { key: 0 };
37
33
 
38
34
  function render(_ctx, _cache, $props, $setup, $data, $options) {
39
- const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
35
+ const _component_Refresh = resolveComponent("Refresh");
40
36
  const _component_NcButton = resolveComponent("NcButton");
37
+ const _component_CnRetentionRecomputeDialog = resolveComponent("CnRetentionRecomputeDialog");
38
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
41
39
  const _component_NcEmptyContent = resolveComponent("NcEmptyContent");
42
40
  const _component_NcNoteCard = resolveComponent("NcNoteCard");
43
- const _component_NcTextField = resolveComponent("NcTextField");
44
41
 
45
42
  return (openBlock(), createElementBlock("div", _hoisted_1, [
46
43
  createElementVNode("header", _hoisted_2, [
47
- createElementVNode("h3", _hoisted_3, toDisplayString($options.text($props.content.title) || $options.t('nextcloud-vue', 'Archiving')), 1 /* TEXT */),
48
- createElementVNode("p", _hoisted_4, toDisplayString($options.text($props.content.subtitle) || $options.t('nextcloud-vue', 'What happens to this record once it is closed')), 1 /* TEXT */)
44
+ createElementVNode("div", _hoisted_3, [
45
+ createElementVNode("h3", _hoisted_4, toDisplayString($options.text($props.content.title) || $options.t('nextcloud-vue', 'Archiving')), 1 /* TEXT */),
46
+ createElementVNode("p", _hoisted_5, toDisplayString($options.text($props.content.subtitle) || $options.t('nextcloud-vue', 'What happens to this record once it is closed')), 1 /* TEXT */)
47
+ ]),
48
+ ($props.content.recompute !== false && !$data.loading && !$data.error)
49
+ ? (openBlock(), createBlock(_component_NcButton, {
50
+ key: 0,
51
+ disabled: !$options.mayRecompute || $data.busy,
52
+ title: $options.mayRecompute ? undefined : $options.t('nextcloud-vue', 'Recomputing needs the archivist role'),
53
+ "aria-description": $options.mayRecompute ? undefined : $options.t('nextcloud-vue', 'Recomputing needs the archivist role'),
54
+ "data-testid": $options.mayRecompute ? 'cn-retention-recompute-open' : 'cn-retention-recompute-denied',
55
+ onClick: _cache[0] || (_cache[0] = $event => ($data.recomputeOpen = true))
56
+ }, {
57
+ icon: withCtx(() => [
58
+ createVNode(_component_Refresh, { size: 20 })
59
+ ]),
60
+ default: withCtx(() => [
61
+ createTextVNode(" " + toDisplayString($options.t('nextcloud-vue', 'Recompute nomination')), 1 /* TEXT */)
62
+ ]),
63
+ _: 1 /* STABLE */
64
+ }, 8 /* PROPS */, ["disabled", "title", "aria-description", "data-testid"]))
65
+ : createCommentVNode("v-if", true)
49
66
  ]),
67
+ ($data.recomputeOpen)
68
+ ? (openBlock(), createBlock(_component_CnRetentionRecomputeDialog, {
69
+ key: 0,
70
+ user: $options.currentUid,
71
+ busy: $data.busy,
72
+ onClose: _cache[1] || (_cache[1] = $event => ($data.recomputeOpen = false)),
73
+ onConfirm: $options.recompute
74
+ }, null, 8 /* PROPS */, ["user", "busy", "onConfirm"]))
75
+ : createCommentVNode("v-if", true),
50
76
  ($data.loading)
51
77
  ? (openBlock(), createBlock(_component_NcLoadingIcon, {
52
- key: 0,
78
+ key: 1,
53
79
  size: 24
54
80
  }))
55
81
  : ($data.error)
56
82
  ? (openBlock(), createBlock(_component_NcEmptyContent, {
57
- key: 1,
83
+ key: 2,
58
84
  name: $options.t('nextcloud-vue', 'The archival facts are unavailable'),
59
85
  description: $data.error,
60
86
  "data-testid": "cn-retention-error"
@@ -72,9 +98,9 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
72
98
  ]),
73
99
  _: 1 /* STABLE */
74
100
  }, 8 /* PROPS */, ["name", "description"]))
75
- : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
101
+ : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
76
102
  ($options.archived)
77
- ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString($options.t('nextcloud-vue', 'Archived on {at} by {by}. {reason}', { at: String($options.archived.at ?? ''), by: String($options.archived.by ?? ''), reason: String($options.archived.reason ?? '') })), 1 /* TEXT */))
103
+ ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString($options.t('nextcloud-vue', 'Archived on {at} by {by}. {reason}', { at: String($options.archived.at ?? ''), by: String($options.archived.by ?? ''), reason: String($options.archived.reason ?? '') })), 1 /* TEXT */))
78
104
  : createCommentVNode("v-if", true),
79
105
  ($options.disagrees)
80
106
  ? (openBlock(), createBlock(_component_NcNoteCard, {
@@ -100,8 +126,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
100
126
  _: 1 /* STABLE */
101
127
  }))
102
128
  : (!$options.nomination)
103
- ? (openBlock(), createElementBlock("div", _hoisted_6, [
104
- createElementVNode("dl", _hoisted_7, [
129
+ ? (openBlock(), createElementBlock("div", _hoisted_7, [
130
+ createElementVNode("dl", _hoisted_8, [
105
131
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.pendingFacts, (fact) => {
106
132
  return (openBlock(), createElementBlock("div", {
107
133
  key: fact.key,
@@ -119,7 +145,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
119
145
  _: 1 /* STABLE */
120
146
  })
121
147
  ]))
122
- : (openBlock(), createElementBlock("dl", _hoisted_8, [
148
+ : (openBlock(), createElementBlock("dl", _hoisted_9, [
123
149
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.facts, (fact) => {
124
150
  return (openBlock(), createElementBlock("div", {
125
151
  key: fact.key,
@@ -128,52 +154,18 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
128
154
  createElementVNode("dt", null, toDisplayString(fact.label), 1 /* TEXT */),
129
155
  createElementVNode("dd", {
130
156
  "data-testid": `cn-retention-${fact.key}`
131
- }, toDisplayString(fact.value), 9 /* TEXT, PROPS */, _hoisted_9)
157
+ }, toDisplayString(fact.value), 9 /* TEXT, PROPS */, _hoisted_10)
132
158
  ]))
133
159
  }), 128 /* KEYED_FRAGMENT */))
134
160
  ])),
135
161
  ($options.outcome)
136
- ? (openBlock(), createElementBlock("div", _hoisted_10, [
162
+ ? (openBlock(), createElementBlock("div", _hoisted_11, [
137
163
  createElementVNode("h4", null, toDisplayString($options.t('nextcloud-vue', 'Outcome')), 1 /* TEXT */),
138
164
  createElementVNode("p", null, toDisplayString($options.t('nextcloud-vue', 'Handed over as {kind} on {at} by {by}.', { kind: String($options.outcome.kind ?? ''), at: String($options.outcome.at ?? ''), by: String($options.outcome.by ?? '') })), 1 /* TEXT */),
139
165
  ($options.outcome.transferListUuid)
140
- ? (openBlock(), createElementBlock("p", _hoisted_11, toDisplayString($options.t('nextcloud-vue', 'Transfer list')) + ": " + toDisplayString($options.outcome.transferListUuid), 1 /* TEXT */))
166
+ ? (openBlock(), createElementBlock("p", _hoisted_12, toDisplayString($options.t('nextcloud-vue', 'Transfer list')) + ": " + toDisplayString($options.outcome.transferListUuid), 1 /* TEXT */))
141
167
  : createCommentVNode("v-if", true)
142
168
  ]))
143
- : createCommentVNode("v-if", true),
144
- ($props.content.recompute !== false)
145
- ? (openBlock(), createElementBlock("div", _hoisted_12, [
146
- (!$options.mayRecompute)
147
- ? (openBlock(), createBlock(_component_NcButton, {
148
- key: 0,
149
- disabled: "",
150
- "data-testid": "cn-retention-recompute-denied"
151
- }, {
152
- default: withCtx(() => [
153
- createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Recomputing needs the archivist role')), 1 /* TEXT */)
154
- ]),
155
- _: 1 /* STABLE */
156
- }))
157
- : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
158
- createVNode(_component_NcTextField, {
159
- modelValue: $data.reason,
160
- label: $options.t('nextcloud-vue', 'Why are you recomputing this?'),
161
- "data-testid": "cn-retention-reason",
162
- "onUpdate:modelValue": _cache[0] || (_cache[0] = (v) => ($data.reason = String(v ?? '')))
163
- }, null, 8 /* PROPS */, ["modelValue", "label"]),
164
- createElementVNode("p", _hoisted_13, toDisplayString($options.t('nextcloud-vue', 'This is recorded against {user}.', { user: $options.currentUid })), 1 /* TEXT */),
165
- createVNode(_component_NcButton, {
166
- disabled: $data.busy || $data.reason.trim() === '',
167
- "data-testid": "cn-retention-recompute",
168
- onClick: $options.recompute
169
- }, {
170
- default: withCtx(() => [
171
- createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Recompute the nomination')), 1 /* TEXT */)
172
- ]),
173
- _: 1 /* STABLE */
174
- }, 8 /* PROPS */, ["disabled", "onClick"])
175
- ], 64 /* STABLE_FRAGMENT */))
176
- ]))
177
169
  : createCommentVNode("v-if", true)
178
170
  ], 64 /* STABLE_FRAGMENT */))
179
171
  ]))
@@ -1 +1 @@
1
- {"version":3,"file":"CnRetentionWidget.vue3.js","sources":["../../../../src/components/CnRetentionWidget/CnRetentionWidget.vue?vue&type=template&id=d883c442&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-retention\" data-testid=\"cn-retention\">\n\t\t<header class=\"cn-retention__head\">\n\t\t\t<h3 class=\"cn-retention__title\">\n\t\t\t\t{{ text(content.title) || t('nextcloud-vue', 'Archiving') }}\n\t\t\t</h3>\n\t\t\t<p class=\"cn-retention__muted\">\n\t\t\t\t{{ text(content.subtitle) || t('nextcloud-vue', 'What happens to this record once it is closed') }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\t\t<NcEmptyContent v-else-if=\"error\"\n\t\t\t:name=\"t('nextcloud-vue', 'The archival facts are unavailable')\"\n\t\t\t:description=\"error\"\n\t\t\tdata-testid=\"cn-retention-error\">\n\t\t\t<template #action>\n\t\t\t\t<NcButton data-testid=\"cn-retention-retry\" @click=\"load\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Try again') }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<p v-if=\"archived\" class=\"cn-retention__archived\" data-testid=\"cn-retention-archived\">\n\t\t\t\t{{ t('nextcloud-vue', 'Archived on {at} by {by}. {reason}', { at: String(archived.at ?? ''), by: String(archived.by ?? ''), reason: String(archived.reason ?? '') }) }}\n\t\t\t</p>\n\t\t\t<NcNoteCard v-if=\"disagrees\" type=\"warning\" data-testid=\"cn-retention-disagreement\">\n\t\t\t\t{{ t('nextcloud-vue', 'This record and OpenRegister do not agree on whether it is archived.') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<NcNoteCard v-if=\"nomination && nomination.status === 'unnominatable'\" type=\"warning\" data-testid=\"cn-retention-unnominatable\">\n\t\t\t\t{{ t('nextcloud-vue', 'Nothing could decide what happens to this record.') }}\n\t\t\t\t{{ String(nomination.unnominatableReason ?? '') }}\n\t\t\t</NcNoteCard>\n\t\t\t<div v-else-if=\"!nomination\" data-testid=\"cn-retention-pending\">\n\t\t\t\t<dl class=\"cn-retention__facts\">\n\t\t\t\t\t<div v-for=\"fact in pendingFacts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t\t<NcNoteCard type=\"info\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'This record has not been closed yet. Its archival future is recorded when it closes.') }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</div>\n\t\t\t<dl v-else class=\"cn-retention__facts\" data-testid=\"cn-retention-facts\">\n\t\t\t\t<div v-for=\"fact in facts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t<dd :data-testid=\"`cn-retention-${fact.key}`\">\n\t\t\t\t\t\t{{ fact.value }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\n\t\t\t<div v-if=\"outcome\" class=\"cn-retention__outcome\" data-testid=\"cn-retention-outcome\">\n\t\t\t\t<h4>{{ t('nextcloud-vue', 'Outcome') }}</h4>\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Handed over as {kind} on {at} by {by}.', { kind: String(outcome.kind ?? ''), at: String(outcome.at ?? ''), by: String(outcome.by ?? '') }) }}</p>\n\t\t\t\t<p v-if=\"outcome.transferListUuid\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Transfer list') }}: {{ outcome.transferListUuid }}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"content.recompute !== false\" class=\"cn-retention__recompute\">\n\t\t\t\t<NcButton v-if=\"!mayRecompute\" disabled data-testid=\"cn-retention-recompute-denied\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Recomputing needs the archivist role') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<template v-else>\n\t\t\t\t\t<NcTextField :modelValue=\"reason\"\n\t\t\t\t\t\t:label=\"t('nextcloud-vue', 'Why are you recomputing this?')\"\n\t\t\t\t\t\tdata-testid=\"cn-retention-reason\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => (reason = String(v ?? ''))\" />\n\t\t\t\t\t<p class=\"cn-retention__muted\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'This is recorded against {user}.', { user: currentUid }) }}\n\t\t\t\t\t</p>\n\t\t\t\t\t<NcButton :disabled=\"busy || reason.trim() === ''\" data-testid=\"cn-retention-recompute\" @click=\"recompute\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Recompute the nomination') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnRetentionWidget — the `retention` detail widget: what happens to a\n * record when its business use ends, as OpenRegister decided it. Every value\n * is read off the record's `@self._retention` (appraisal, disposal date,\n * retention period, selection list row, the rule and when it was written)\n * and `@self.archived`; the widget derives nothing, so it can never disagree\n * with the stored decision. A failed read is an error with a retry, never an\n * empty block. A record nothing could nominate is drawn apart from one that\n * is not closed yet. An archivist (or an admin) may ask OpenRegister to\n * recompute the nomination, with a reason. Any app on OpenRegister uses it;\n * dossiq's case Archiving tab is the first.\n *\n * `content`:\n * - `title`, `subtitle`: texts, through the host translate.\n * - `statusField`, `archivedValues`: a record field and the values that mean\n * archived (`archiveStatus`, `[\"archived\", ...]`); when set, a mismatch with\n * `@self.archived` is said out loud.\n * - `archivistGroups`: groups that may recompute (default `[\"archivaris\"]`;\n * admins always may); `recompute: false` hides the control.\n *\n * The record is read fresh from OpenRegister by `register`, `schema` and\n * `objectId` (props, else the bound record's `@self`).\n */\nexport default {\n\tname: 'CnRetentionWidget',\n\n\tcomponents: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration (see the component docs).\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/**\n\t\t * The bound record, on a detail page.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The record's register (else the bound record's `@self.register`). */\n\t\tregister: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record's schema (else the bound record's `@self.schema`); a schema object gives its slug.\n\t\t *\n\t\t * @type {string|number|object}\n\t\t */\n\t\tschema: {\n\t\t\ttype: [String, Number, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The record's id (else the bound record's). */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\trecord: null,\n\t\t\treason: '',\n\t\t\tbusy: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The bound record's `@self`. */\n\t\tboundSelf() {\n\t\t\treturn (this.objectData && this.objectData['@self']) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tid() {\n\t\t\treturn String(this.objectId || (this.objectData && this.objectData.id) || this.boundSelf.id || '')\n\t\t},\n\n\t\t/** @return {string} The register. */\n\t\tregisterRef() {\n\t\t\treturn String(this.register || this.boundSelf.register || '')\n\t\t},\n\n\t\t/** @return {string} The schema. */\n\t\tschemaRef() {\n\t\t\tconst s = this.schema || this.boundSelf.schema || ''\n\t\t\treturn String(typeof s === 'object' ? (s.slug || s.id || '') : s)\n\t\t},\n\n\t\t/** @return {object} The record's `@self`. */\n\t\tself() {\n\t\t\treturn (this.record && this.record['@self']) || {}\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's stored retention decision. */\n\t\tretention() {\n\t\t\treturn this.self._retention || null\n\t\t},\n\n\t\t/** @return {object|null} The nomination. */\n\t\tnomination() {\n\t\t\treturn (this.retention && this.retention.nomination) || null\n\t\t},\n\n\t\t/** @return {object|null} The hand-over outcome. */\n\t\toutcome() {\n\t\t\treturn (this.retention && this.retention.outcome) || null\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's archived marker. */\n\t\tarchived() {\n\t\t\treturn this.self.archived || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the record's own field and the platform marker disagree.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {boolean}\n\t\t */\n\t\tdisagrees() {\n\t\t\tconst field = this.content.statusField\n\t\t\tif (!field || !this.record) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst values = Array.isArray(this.content.archivedValues) ? this.content.archivedValues.map(String) : ['archived']\n\t\t\tconst saysArchived = values.includes(String(readPath(this.record, field) ?? ''))\n\t\t\treturn saysArchived !== Boolean(this.archived)\n\t\t},\n\n\t\t/**\n\t\t * The stored facts that have a value, in reading order.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Array<{key: string, label: string, value: string}>}\n\t\t */\n\t\tfacts() {\n\t\t\tconst r = this.retention || {}\n\t\t\tconst n = this.nomination || {}\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: r.appraisal },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: r.disposalDate },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: r.retentionPeriod },\n\t\t\t\t{ key: 'selection-list-row', label: t('nextcloud-vue', 'Selection list row'), value: r.selectionListRow },\n\t\t\t\t{ key: 'rule', label: t('nextcloud-vue', 'Decided by'), value: n.rule },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: n.at },\n\t\t\t].filter((fact) => fact.value !== undefined && fact.value !== null && fact.value !== '').map((fact) => ({ ...fact, value: String(fact.value) }))\n\t\t},\n\n\t\t/** @return {Array<{key: string, label: string, value: string}>} The facts of a record not closed yet. */\n\t\tpendingFacts() {\n\t\t\tconst atClosure = t('nextcloud-vue', 'Decided when it closes')\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: atClosure },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: atClosure },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: atClosure },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: t('nextcloud-vue', 'Not yet') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} The signed-in user's id. */\n\t\tcurrentUid() {\n\t\t\tconst user = getCurrentUser()\n\t\t\treturn String((user && user.uid) || '')\n\t\t},\n\n\t\t/**\n\t\t * Whether this user may recompute: an admin or an archivist group member.\n\t\t * Shown disabled otherwise, so the user learns which role it needs.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tmayRecompute() {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (user && user.isAdmin === true) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst groups = Array.isArray(user && user.groups) ? user.groups : []\n\t\t\tconst allowed = Array.isArray(this.content.archivistGroups) ? this.content.archivistGroups : ['archivaris']\n\t\t\treturn allowed.some((g) => groups.includes(g))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the record fresh from OpenRegister; a failure is an error, never an empty block.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tif (!this.registerRef || !this.schemaRef || !this.id) {\n\t\t\t\t\tthrow new Error(t('nextcloud-vue', 'This widget has no record to read.'))\n\t\t\t\t}\n\t\t\t\tconst { data } = await axios.get(generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(this.registerRef)}/${encodeURIComponent(this.schemaRef)}/${encodeURIComponent(this.id)}`))\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : null\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Ask OpenRegister to derive the nomination again, then read it back.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync recompute() {\n\t\t\tif (this.busy || this.reason.trim() === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\ttry {\n\t\t\t\tawait axios.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: this.reason.trim() })\n\t\t\t\tthis.reason = ''\n\t\t\t\tawait this.load()\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-retention {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-retention__head {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-retention__title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-retention__muted {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-retention__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-retention__fact dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-retention__fact dd {\n\tmargin: 0;\n\tfont-weight: 600;\n}\n\n.cn-retention__recompute {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmax-width: 480px;\n}\n</style>\n"],"names":["_createElementVNode","_toDisplayString","_createBlock","_createVNode","_createElementBlock","_createTextVNode","_Fragment","_renderList","_openBlock"],"mappings":"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"}
1
+ {"version":3,"file":"CnRetentionWidget.vue3.js","sources":["../../../../src/components/CnRetentionWidget/CnRetentionWidget.vue?vue&type=template&id=170cb650&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-retention\" data-testid=\"cn-retention\">\n\t\t<header class=\"cn-retention__head\">\n\t\t\t<div class=\"cn-retention__head-text\">\n\t\t\t\t<h3 class=\"cn-retention__title\">\n\t\t\t\t\t{{ text(content.title) || t('nextcloud-vue', 'Archiving') }}\n\t\t\t\t</h3>\n\t\t\t\t<p class=\"cn-retention__muted\">\n\t\t\t\t\t{{ text(content.subtitle) || t('nextcloud-vue', 'What happens to this record once it is closed') }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<!-- Recompute starts in the header (board DqZaakArchivering); the\n\t\t\t dialog asks for the reason OpenRegister records with it. -->\n\t\t\t<NcButton\n\t\t\t\tv-if=\"content.recompute !== false && !loading && !error\"\n\t\t\t\t:disabled=\"!mayRecompute || busy\"\n\t\t\t\t:title=\"mayRecompute ? undefined : t('nextcloud-vue', 'Recomputing needs the archivist role')\"\n\t\t\t\t:aria-description=\"mayRecompute ? undefined : t('nextcloud-vue', 'Recomputing needs the archivist role')\"\n\t\t\t\t:data-testid=\"mayRecompute ? 'cn-retention-recompute-open' : 'cn-retention-recompute-denied'\"\n\t\t\t\t@click=\"recomputeOpen = true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Refresh :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Recompute nomination') }}\n\t\t\t</NcButton>\n\t\t</header>\n\t\t<CnRetentionRecomputeDialog\n\t\t\tv-if=\"recomputeOpen\"\n\t\t\t:user=\"currentUid\"\n\t\t\t:busy=\"busy\"\n\t\t\t@close=\"recomputeOpen = false\"\n\t\t\t@confirm=\"recompute\" />\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\t\t<NcEmptyContent v-else-if=\"error\"\n\t\t\t:name=\"t('nextcloud-vue', 'The archival facts are unavailable')\"\n\t\t\t:description=\"error\"\n\t\t\tdata-testid=\"cn-retention-error\">\n\t\t\t<template #action>\n\t\t\t\t<NcButton data-testid=\"cn-retention-retry\" @click=\"load\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Try again') }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<p v-if=\"archived\" class=\"cn-retention__archived\" data-testid=\"cn-retention-archived\">\n\t\t\t\t{{ t('nextcloud-vue', 'Archived on {at} by {by}. {reason}', { at: String(archived.at ?? ''), by: String(archived.by ?? ''), reason: String(archived.reason ?? '') }) }}\n\t\t\t</p>\n\t\t\t<NcNoteCard v-if=\"disagrees\" type=\"warning\" data-testid=\"cn-retention-disagreement\">\n\t\t\t\t{{ t('nextcloud-vue', 'This record and OpenRegister do not agree on whether it is archived.') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<NcNoteCard v-if=\"nomination && nomination.status === 'unnominatable'\" type=\"warning\" data-testid=\"cn-retention-unnominatable\">\n\t\t\t\t{{ t('nextcloud-vue', 'Nothing could decide what happens to this record.') }}\n\t\t\t\t{{ String(nomination.unnominatableReason ?? '') }}\n\t\t\t</NcNoteCard>\n\t\t\t<div v-else-if=\"!nomination\" data-testid=\"cn-retention-pending\">\n\t\t\t\t<dl class=\"cn-retention__facts\">\n\t\t\t\t\t<div v-for=\"fact in pendingFacts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t\t<NcNoteCard type=\"info\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'This record has not been closed yet. Its archival future is recorded when it closes.') }}\n\t\t\t\t</NcNoteCard>\n\t\t\t</div>\n\t\t\t<dl v-else class=\"cn-retention__facts\" data-testid=\"cn-retention-facts\">\n\t\t\t\t<div v-for=\"fact in facts\" :key=\"fact.key\" class=\"cn-retention__fact\">\n\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t<dd :data-testid=\"`cn-retention-${fact.key}`\">\n\t\t\t\t\t\t{{ fact.value }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\n\t\t\t<div v-if=\"outcome\" class=\"cn-retention__outcome\" data-testid=\"cn-retention-outcome\">\n\t\t\t\t<h4>{{ t('nextcloud-vue', 'Outcome') }}</h4>\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Handed over as {kind} on {at} by {by}.', { kind: String(outcome.kind ?? ''), at: String(outcome.at ?? ''), by: String(outcome.by ?? '') }) }}</p>\n\t\t\t\t<p v-if=\"outcome.transferListUuid\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Transfer list') }}: {{ outcome.transferListUuid }}\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport Refresh from 'vue-material-design-icons/Refresh.vue'\nimport CnRetentionRecomputeDialog from './CnRetentionRecomputeDialog.vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * CnRetentionWidget — the `retention` detail widget: what happens to a\n * record when its business use ends, as OpenRegister decided it. Every value\n * is read off the record's `@self._retention` (appraisal, disposal date,\n * retention period, selection list row, the rule and when it was written)\n * and `@self.archived`; the widget derives nothing, so it can never disagree\n * with the stored decision. A failed read is an error with a retry, never an\n * empty block. A record nothing could nominate is drawn apart from one that\n * is not closed yet. An archivist (or an admin) may ask OpenRegister to\n * recompute the nomination, with a reason. Any app on OpenRegister uses it;\n * dossiq's case Archiving tab is the first.\n *\n * `content`:\n * - `title`, `subtitle`: texts, through the host translate.\n * - `statusField`, `archivedValues`: a record field and the values that mean\n * archived (`archiveStatus`, `[\"archived\", ...]`); when set, a mismatch with\n * `@self.archived` is said out loud.\n * - `archivistGroups`: groups that may recompute (default `[\"archivaris\"]`;\n * admins always may); `recompute: false` hides the control.\n *\n * The record is read fresh from OpenRegister by `register`, `schema` and\n * `objectId` (props, else the bound record's `@self`).\n */\nexport default {\n\tname: 'CnRetentionWidget',\n\n\tcomponents: { CnRetentionRecomputeDialog, NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, Refresh },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration (see the component docs).\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/**\n\t\t * The bound record, on a detail page.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The record's register (else the bound record's `@self.register`). */\n\t\tregister: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record's schema (else the bound record's `@self.schema`); a schema object gives its slug.\n\t\t *\n\t\t * @type {string|number|object}\n\t\t */\n\t\tschema: {\n\t\t\ttype: [String, Number, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The record's id (else the bound record's). */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\trecord: null,\n\t\t\t/** Whether the recompute dialog is open. */\n\t\t\trecomputeOpen: false,\n\t\t\tbusy: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The bound record's `@self`. */\n\t\tboundSelf() {\n\t\t\treturn (this.objectData && this.objectData['@self']) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tid() {\n\t\t\treturn String(this.objectId || (this.objectData && this.objectData.id) || this.boundSelf.id || '')\n\t\t},\n\n\t\t/** @return {string} The register. */\n\t\tregisterRef() {\n\t\t\treturn String(this.register || this.boundSelf.register || '')\n\t\t},\n\n\t\t/** @return {string} The schema. */\n\t\tschemaRef() {\n\t\t\tconst s = this.schema || this.boundSelf.schema || ''\n\t\t\treturn String(typeof s === 'object' ? (s.slug || s.id || '') : s)\n\t\t},\n\n\t\t/** @return {object} The record's `@self`. */\n\t\tself() {\n\t\t\treturn (this.record && this.record['@self']) || {}\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's stored retention decision. */\n\t\tretention() {\n\t\t\treturn this.self._retention || null\n\t\t},\n\n\t\t/** @return {object|null} The nomination. */\n\t\tnomination() {\n\t\t\treturn (this.retention && this.retention.nomination) || null\n\t\t},\n\n\t\t/** @return {object|null} The hand-over outcome. */\n\t\toutcome() {\n\t\t\treturn (this.retention && this.retention.outcome) || null\n\t\t},\n\n\t\t/** @return {object|null} OpenRegister's archived marker. */\n\t\tarchived() {\n\t\t\treturn this.self.archived || null\n\t\t},\n\n\t\t/**\n\t\t * Whether the record's own field and the platform marker disagree.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {boolean}\n\t\t */\n\t\tdisagrees() {\n\t\t\tconst field = this.content.statusField\n\t\t\tif (!field || !this.record) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst values = Array.isArray(this.content.archivedValues) ? this.content.archivedValues.map(String) : ['archived']\n\t\t\tconst saysArchived = values.includes(String(readPath(this.record, field) ?? ''))\n\t\t\treturn saysArchived !== Boolean(this.archived)\n\t\t},\n\n\t\t/**\n\t\t * The stored facts that have a value, in reading order.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Array<{key: string, label: string, value: string}>}\n\t\t */\n\t\tfacts() {\n\t\t\tconst r = this.retention || {}\n\t\t\tconst n = this.nomination || {}\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: r.appraisal },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: r.disposalDate },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: r.retentionPeriod },\n\t\t\t\t{ key: 'selection-list-row', label: t('nextcloud-vue', 'Selection list row'), value: r.selectionListRow },\n\t\t\t\t{ key: 'rule', label: t('nextcloud-vue', 'Decided by'), value: n.rule },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: n.at },\n\t\t\t].filter((fact) => fact.value !== undefined && fact.value !== null && fact.value !== '').map((fact) => ({ ...fact, value: String(fact.value) }))\n\t\t},\n\n\t\t/** @return {Array<{key: string, label: string, value: string}>} The facts of a record not closed yet. */\n\t\tpendingFacts() {\n\t\t\tconst atClosure = t('nextcloud-vue', 'Decided when it closes')\n\t\t\treturn [\n\t\t\t\t{ key: 'appraisal', label: t('nextcloud-vue', 'Appraisal'), value: atClosure },\n\t\t\t\t{ key: 'retention-period', label: t('nextcloud-vue', 'Retention period'), value: atClosure },\n\t\t\t\t{ key: 'disposal-date', label: t('nextcloud-vue', 'Disposal date'), value: atClosure },\n\t\t\t\t{ key: 'decided-at', label: t('nextcloud-vue', 'Written on'), value: t('nextcloud-vue', 'Not yet') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} The signed-in user's id. */\n\t\tcurrentUid() {\n\t\t\tconst user = getCurrentUser()\n\t\t\treturn String((user && user.uid) || '')\n\t\t},\n\n\t\t/**\n\t\t * Whether this user may recompute: an admin or an archivist group member.\n\t\t * Shown disabled otherwise, so the user learns which role it needs.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tmayRecompute() {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (user && user.isAdmin === true) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst groups = Array.isArray(user && user.groups) ? user.groups : []\n\t\t\tconst allowed = Array.isArray(this.content.archivistGroups) ? this.content.archivistGroups : ['archivaris']\n\t\t\treturn allowed.some((g) => groups.includes(g))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the record fresh from OpenRegister; a failure is an error, never an empty block.\n\t\t *\n\t\t * @spec openspec/changes/retention-widget/specs/retention-widget/spec.md#requirement-a-record-shows-its-stored-retention\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tif (!this.registerRef || !this.schemaRef || !this.id) {\n\t\t\t\t\tthrow new Error(t('nextcloud-vue', 'This widget has no record to read.'))\n\t\t\t\t}\n\t\t\t\tconst { data } = await axios.get(generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(this.registerRef)}/${encodeURIComponent(this.schemaRef)}/${encodeURIComponent(this.id)}`))\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : null\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Ask OpenRegister to derive the nomination again with the reason the\n\t\t * dialog asked for, then read it back.\n\t\t *\n\t\t * @spec openspec/changes/retention-recompute-header/specs/retention-recompute-header/spec.md#requirement-recomputing-the-nomination-starts-in-the-card-header-and-asks-for-a-reason\n\t\t * @param {string} reason Why it is recomputed.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync recompute(reason) {\n\t\t\tconst why = String(reason ?? '').trim()\n\t\t\tif (this.busy || why === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\ttry {\n\t\t\t\tawait axios.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: why })\n\t\t\t\tthis.recomputeOpen = false\n\t\t\t\tawait this.load()\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-retention {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-retention__head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-retention__head-text {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-retention__title {\n\tmargin: 0;\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n.cn-retention__muted {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-retention__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-retention__fact dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-retention__fact dd {\n\tmargin: 0;\n\tfont-weight: 600;\n}\n\n</style>\n"],"names":["_createElementVNode","_toDisplayString","_createVNode","_createTextVNode","_createBlock","_createElementBlock","_Fragment","_renderList","_openBlock"],"mappings":"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"}
@@ -2,7 +2,7 @@ import script from './CnSegmentedControl.vue2.js';
2
2
  import { render } from './CnSegmentedControl.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-128aa2ef";
5
+ script.__scopeId = "data-v-1f251ad6";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -41,7 +41,8 @@ var script = {
41
41
  * @type {string|number|boolean|null}
42
42
  */
43
43
  modelValue: {
44
- type: [Boolean, String, Number],
44
+ // eslint-disable-next-line vue/prefer-prop-type-boolean-first -- an option value of '' must stay a string
45
+ type: [String, Number, Boolean],
45
46
  default: null,
46
47
  },
47
48
 
@@ -1 +1 @@
1
- {"version":3,"file":"CnSegmentedControl.vue2.js","sources":["../../../../src/components/CnSegmentedControl/CnSegmentedControl.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div\n\t\tclass=\"cn-segmented-control\"\n\t\t:class=\"{ 'cn-segmented-control--stretch': stretch, 'cn-segmented-control--compact': size === 'compact' }\"\n\t\t:role=\"isToggle ? 'group' : 'radiogroup'\"\n\t\t:aria-label=\"ariaLabel || null\"\n\t\t:aria-labelledby=\"ariaLabelledby || null\"\n\t\t@keydown=\"onKeydown\">\n\t\t<button\n\t\t\tv-for=\"(option, index) in normalizedOptions\"\n\t\t\t:key=\"option.key\"\n\t\t\ttype=\"button\"\n\t\t\t:role=\"isToggle ? null : 'radio'\"\n\t\t\tclass=\"cn-segmented-control__option\"\n\t\t\t:class=\"{ 'cn-segmented-control__option--checked': isChecked(option), 'cn-segmented-control__option--icon-only': option.iconOnly }\"\n\t\t\t:aria-checked=\"isToggle ? null : (isChecked(option) ? 'true' : 'false')\"\n\t\t\t:aria-pressed=\"isToggle ? (isChecked(option) ? 'true' : 'false') : null\"\n\t\t\t:tabindex=\"isToggle || index === focusableIndex ? 0 : -1\"\n\t\t\t:disabled=\"option.disabled || null\"\n\t\t\t:aria-controls=\"controls || null\"\n\t\t\t:data-value=\"option.key\"\n\t\t\t@click=\"select(option)\">\n\t\t\t<!-- @slot option Replaces the content of one option. -->\n\t\t\t<!-- @binding {object} option The option: `{ value, label, disabled, count }`. -->\n\t\t\t<!-- @binding {boolean} checked Whether this option is the chosen one. -->\n\t\t\t<slot name=\"option\" :option=\"option\" :checked=\"isChecked(option)\">\n\t\t\t\t<span class=\"cn-segmented-control__label\">{{ option.label }}</span>\n\t\t\t\t<!-- The leading space is not drawn in this flex row; it keeps a\n\t\t\t\t screen reader from reading label and count as one word. -->\n\t\t\t\t<span v-if=\"option.count !== null\" class=\"cn-segmented-control__count\">{{ ' ' + option.count }}</span>\n\t\t\t</slot>\n\t\t</button>\n\t</div>\n</template>\n\n<script>\n/**\n * CnSegmentedControl is a switch between a few views of the same thing, such\n * as \"My work / My team\". One option is always the chosen one.\n *\n * It is a radio group: screen readers announce the group and which option is\n * checked, the arrow keys move the choice, and Tab enters and leaves the group\n * in one step. Give it a name with `aria-label` (or `aria-labelledby`).\n *\n * ```vue\n * <CnSegmentedControl\n * v-model=\"view\"\n * aria-label=\"View\"\n * :options=\"[\n * { value: 'mine', label: 'My work' },\n * { value: 'team', label: 'My team' },\n * ]\" />\n * ```\n *\n * To switch between panels of content, use `CnTabs` with `variant=\"segmented\"`\n * instead: that keeps the tab and panel semantics and looks the same.\n */\nexport default {\n\tname: 'CnSegmentedControl',\n\n\tprops: {\n\t\t/**\n\t\t * The options. Each is `{ value, label, disabled?, count? }`, or a\n\t\t * plain string that is both the value and the label. `count` shows a\n\t\t * number after the label.\n\t\t *\n\t\t * @type {Array<string|{value: (string|number|boolean), label: string, disabled?: boolean, count?: number}>}\n\t\t */\n\t\toptions: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * The value of the chosen option (v-model).\n\t\t *\n\t\t * @type {string|number|boolean|null}\n\t\t */\n\t\tmodelValue: {\n\t\t\ttype: [Boolean, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Accessible name of the group, read out when focus enters it. */\n\t\tariaLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Id of an element that names the group, as an alternative to `ariaLabel`. */\n\t\tariaLabelledby: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Id of the element the options switch, such as the region holding a\n\t\t * page view's widgets. Set as `aria-controls` on every option.\n\t\t *\n\t\t * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-the-view-switch-is-accessible\n\t\t */\n\t\tcontrols: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Size of the control: `normal` (default) or `compact` (the board\n\t\t * group: 3px track padding, 2px gap, 34px segments).\n\t\t *\n\t\t * @spec openspec/changes/screens-dashboard-parity/specs/dashboard-page/spec.md#requirement-a-compact-segmented-control\n\t\t */\n\t\tsize: {\n\t\t\ttype: String,\n\t\t\tdefault: 'normal',\n\t\t\tvalidator: (v) => ['normal', 'compact'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * `radio` (default) is a radio group: one option is always chosen and\n\t\t * the arrow keys move the choice. `toggle` is a group of buttons, each\n\t\t * with `aria-pressed`, for a set of presets (the board's period group);\n\t\t * Tab visits every button.\n\t\t *\n\t\t * @spec openspec/changes/screens-dashboard-parity/specs/dashboard-page/spec.md#requirement-the-period-as-a-segmented-group\n\t\t */\n\t\tmode: {\n\t\t\ttype: String,\n\t\t\tdefault: 'radio',\n\t\t\tvalidator: (v) => ['radio', 'toggle'].includes(v),\n\t\t},\n\n\t\t/** Stretch the control to the full width, with options of equal width. */\n\t\tstretch: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted with the value of the newly chosen option.\n\t\t *\n\t\t * @event update:modelValue\n\t\t * @type {string|number|boolean}\n\t\t */\n\t\t'update:modelValue',\n\t],\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the options are pressable buttons rather than radios.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisToggle() {\n\t\t\treturn this.mode === 'toggle'\n\t\t},\n\n\t\t/**\n\t\t * The options in one shape.\n\t\t *\n\t\t * @return {Array<{key: string, value: (string|number|boolean), label: string, disabled: boolean, count: (number|null)}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tnormalizedOptions() {\n\t\t\treturn (Array.isArray(this.options) ? this.options : [])\n\t\t\t\t.filter((option) => option !== null && option !== undefined)\n\t\t\t\t.map((option) => {\n\t\t\t\t\tif (typeof option !== 'object') {\n\t\t\t\t\t\treturn { key: String(option), value: option, label: String(option), disabled: false, count: null, iconOnly: false }\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: String(option.value),\n\t\t\t\t\t\tvalue: option.value,\n\t\t\t\t\t\tlabel: String(option.label ?? option.value),\n\t\t\t\t\t\tdisabled: option.disabled === true,\n\t\t\t\t\t\tcount: Number.isFinite(option.count) ? option.count : null,\n\t\t\t\t\t\ticonOnly: option.iconOnly === true,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The option Tab lands on: the chosen one, else the first enabled one.\n\t\t *\n\t\t * @return {number} Its index, or -1 when no option can take focus.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tfocusableIndex() {\n\t\t\tconst checked = this.normalizedOptions.findIndex((option) => this.isChecked(option) && !option.disabled)\n\t\t\tif (checked !== -1) {\n\t\t\t\treturn checked\n\t\t\t}\n\t\t\treturn this.normalizedOptions.findIndex((option) => !option.disabled)\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether an option is the chosen one.\n\t\t *\n\t\t * @param {{value: (string|number|boolean)}} option The option.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tisChecked(option) {\n\t\t\treturn this.modelValue !== null && String(option.value) === String(this.modelValue)\n\t\t},\n\n\t\t/**\n\t\t * Choose an option.\n\t\t *\n\t\t * @param {{value: (string|number|boolean), disabled: boolean}} option The option.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tselect(option) {\n\t\t\tif (option.disabled || this.isChecked(option)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:modelValue', option.value)\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move the choice, as in a native radio\n\t\t * group. Disabled options are passed over and the ends wrap around.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key press.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tonKeydown(event) {\n\t\t\tif (this.isToggle) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst enabled = this.normalizedOptions\n\t\t\t\t.map((option, index) => ({ option, index }))\n\t\t\t\t.filter((entry) => !entry.option.disabled)\n\t\t\tif (enabled.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst buttons = Array.from(this.$el.querySelectorAll('[role=\"radio\"]'))\n\t\t\tconst focused = buttons.indexOf(document.activeElement)\n\t\t\tconst from = focused !== -1 ? focused : this.focusableIndex\n\t\t\tlet position = enabled.findIndex((entry) => entry.index === from)\n\t\t\tif (position === -1) {\n\t\t\t\tposition = 0\n\t\t\t}\n\t\t\tif (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n\t\t\t\tposition = (position + 1) % enabled.length\n\t\t\t} else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n\t\t\t\tposition = (position - 1 + enabled.length) % enabled.length\n\t\t\t} else if (event.key === 'Home') {\n\t\t\t\tposition = 0\n\t\t\t} else if (event.key === 'End') {\n\t\t\t\tposition = enabled.length - 1\n\t\t\t} else {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tconst target = enabled[position]\n\t\t\tthis.select(target.option)\n\t\t\tif (buttons[target.index] && typeof buttons[target.index].focus === 'function') {\n\t\t\t\tbuttons[target.index].focus()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-segmented-control {\n\tdisplay: inline-flex;\n\tgap: 4px;\n\tmax-width: 100%;\n\tpadding: 4px;\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 10px));\n\tbackground-color: var(--color-background-dark);\n\toverflow-x: auto;\n}\n\n.cn-segmented-control--stretch {\n\tdisplay: flex;\n}\n\n.cn-segmented-control--stretch .cn-segmented-control__option {\n\tflex: 1 1 0;\n}\n\n/* The parent class outscores Nextcloud's server rule that gives every plain\n `button` a margin and a border. */\n.cn-segmented-control .cn-segmented-control__option {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\tmin-height: 36px;\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid transparent;\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tbackground-color: transparent;\n\t/* Full-contrast label on every option: the choice is carried by the\n\t raised surface, not by dimming the others. */\n\tcolor: var(--color-main-text);\n\tfont: inherit;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n\tcursor: pointer;\n}\n\n.cn-segmented-control .cn-segmented-control__option:hover {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-segmented-control .cn-segmented-control__option:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n/* The border survives forced-colours mode, which drops background and shadow. */\n.cn-segmented-control .cn-segmented-control__option--checked,\n.cn-segmented-control .cn-segmented-control__option--checked:hover {\n\tborder-color: var(--color-border-dark);\n\tbackground-color: var(--color-main-background);\n\tbox-shadow: 0 1px 2px var(--color-box-shadow);\n}\n\n.cn-segmented-control .cn-segmented-control__option[disabled] {\n\tcursor: default;\n\topacity: 0.5;\n}\n\n.cn-segmented-control__count {\n\tfont-weight: 400;\n}\n\n/* Compact (screens-dashboard-parity): the board's period group. */\n.cn-segmented-control.cn-segmented-control--compact {\n\tgap: 2px;\n\tpadding: 3px;\n\tborder-radius: 8px;\n}\n\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option {\n\tmin-height: 34px;\n\theight: 34px;\n\tpadding: 0 12px;\n\tborder-radius: 6px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option--icon-only {\n\tmin-width: 40px;\n\tpadding: 0;\n}\n\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option--checked,\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option--checked:hover {\n\tborder-color: transparent;\n\tbox-shadow: 0 1px 2px var(--color-box-shadow);\n}\n\n/* On a phone (screens-dashboard-phone) the compact group's segments are 44px\n high touch targets, and the group wraps onto a second row instead of\n running off the screen. */\n@media (max-width: 640px) {\n\t.cn-segmented-control.cn-segmented-control--compact {\n\t\tflex-wrap: wrap;\n\t}\n\n\t.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option {\n\t\tmin-height: 44px;\n\t}\n}\n</style>\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CnSegmentedControl.vue2.js","sources":["../../../../src/components/CnSegmentedControl/CnSegmentedControl.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div\n\t\tclass=\"cn-segmented-control\"\n\t\t:class=\"{ 'cn-segmented-control--stretch': stretch, 'cn-segmented-control--compact': size === 'compact' }\"\n\t\t:role=\"isToggle ? 'group' : 'radiogroup'\"\n\t\t:aria-label=\"ariaLabel || null\"\n\t\t:aria-labelledby=\"ariaLabelledby || null\"\n\t\t@keydown=\"onKeydown\">\n\t\t<button\n\t\t\tv-for=\"(option, index) in normalizedOptions\"\n\t\t\t:key=\"option.key\"\n\t\t\ttype=\"button\"\n\t\t\t:role=\"isToggle ? null : 'radio'\"\n\t\t\tclass=\"cn-segmented-control__option\"\n\t\t\t:class=\"{ 'cn-segmented-control__option--checked': isChecked(option), 'cn-segmented-control__option--icon-only': option.iconOnly }\"\n\t\t\t:aria-checked=\"isToggle ? null : (isChecked(option) ? 'true' : 'false')\"\n\t\t\t:aria-pressed=\"isToggle ? (isChecked(option) ? 'true' : 'false') : null\"\n\t\t\t:tabindex=\"isToggle || index === focusableIndex ? 0 : -1\"\n\t\t\t:disabled=\"option.disabled || null\"\n\t\t\t:aria-controls=\"controls || null\"\n\t\t\t:data-value=\"option.key\"\n\t\t\t@click=\"select(option)\">\n\t\t\t<!-- @slot option Replaces the content of one option. -->\n\t\t\t<!-- @binding {object} option The option: `{ value, label, disabled, count }`. -->\n\t\t\t<!-- @binding {boolean} checked Whether this option is the chosen one. -->\n\t\t\t<slot name=\"option\" :option=\"option\" :checked=\"isChecked(option)\">\n\t\t\t\t<span class=\"cn-segmented-control__label\">{{ option.label }}</span>\n\t\t\t\t<!-- The leading space is not drawn in this flex row; it keeps a\n\t\t\t\t screen reader from reading label and count as one word. -->\n\t\t\t\t<span v-if=\"option.count !== null\" class=\"cn-segmented-control__count\">{{ ' ' + option.count }}</span>\n\t\t\t</slot>\n\t\t</button>\n\t</div>\n</template>\n\n<script>\n/**\n * CnSegmentedControl is a switch between a few views of the same thing, such\n * as \"My work / My team\". One option is always the chosen one.\n *\n * It is a radio group: screen readers announce the group and which option is\n * checked, the arrow keys move the choice, and Tab enters and leaves the group\n * in one step. Give it a name with `aria-label` (or `aria-labelledby`).\n *\n * ```vue\n * <CnSegmentedControl\n * v-model=\"view\"\n * aria-label=\"View\"\n * :options=\"[\n * { value: 'mine', label: 'My work' },\n * { value: 'team', label: 'My team' },\n * ]\" />\n * ```\n *\n * To switch between panels of content, use `CnTabs` with `variant=\"segmented\"`\n * instead: that keeps the tab and panel semantics and looks the same.\n */\nexport default {\n\tname: 'CnSegmentedControl',\n\n\tprops: {\n\t\t/**\n\t\t * The options. Each is `{ value, label, disabled?, count? }`, or a\n\t\t * plain string that is both the value and the label. `count` shows a\n\t\t * number after the label.\n\t\t *\n\t\t * @type {Array<string|{value: (string|number|boolean), label: string, disabled?: boolean, count?: number}>}\n\t\t */\n\t\toptions: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * The value of the chosen option (v-model).\n\t\t *\n\t\t * @type {string|number|boolean|null}\n\t\t */\n\t\tmodelValue: {\n\t\t\t// eslint-disable-next-line vue/prefer-prop-type-boolean-first -- an option value of '' must stay a string\n\t\t\ttype: [String, Number, Boolean],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Accessible name of the group, read out when focus enters it. */\n\t\tariaLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Id of an element that names the group, as an alternative to `ariaLabel`. */\n\t\tariaLabelledby: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Id of the element the options switch, such as the region holding a\n\t\t * page view's widgets. Set as `aria-controls` on every option.\n\t\t *\n\t\t * @spec openspec/changes/view-switch-containers/specs/view-switch-containers/spec.md#requirement-the-view-switch-is-accessible\n\t\t */\n\t\tcontrols: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Size of the control: `normal` (default) or `compact` (the board\n\t\t * group: 3px track padding, 2px gap, 34px segments).\n\t\t *\n\t\t * @spec openspec/changes/screens-dashboard-parity/specs/dashboard-page/spec.md#requirement-a-compact-segmented-control\n\t\t */\n\t\tsize: {\n\t\t\ttype: String,\n\t\t\tdefault: 'normal',\n\t\t\tvalidator: (v) => ['normal', 'compact'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * `radio` (default) is a radio group: one option is always chosen and\n\t\t * the arrow keys move the choice. `toggle` is a group of buttons, each\n\t\t * with `aria-pressed`, for a set of presets (the board's period group);\n\t\t * Tab visits every button.\n\t\t *\n\t\t * @spec openspec/changes/screens-dashboard-parity/specs/dashboard-page/spec.md#requirement-the-period-as-a-segmented-group\n\t\t */\n\t\tmode: {\n\t\t\ttype: String,\n\t\t\tdefault: 'radio',\n\t\t\tvalidator: (v) => ['radio', 'toggle'].includes(v),\n\t\t},\n\n\t\t/** Stretch the control to the full width, with options of equal width. */\n\t\tstretch: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted with the value of the newly chosen option.\n\t\t *\n\t\t * @event update:modelValue\n\t\t * @type {string|number|boolean}\n\t\t */\n\t\t'update:modelValue',\n\t],\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the options are pressable buttons rather than radios.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisToggle() {\n\t\t\treturn this.mode === 'toggle'\n\t\t},\n\n\t\t/**\n\t\t * The options in one shape.\n\t\t *\n\t\t * @return {Array<{key: string, value: (string|number|boolean), label: string, disabled: boolean, count: (number|null)}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tnormalizedOptions() {\n\t\t\treturn (Array.isArray(this.options) ? this.options : [])\n\t\t\t\t.filter((option) => option !== null && option !== undefined)\n\t\t\t\t.map((option) => {\n\t\t\t\t\tif (typeof option !== 'object') {\n\t\t\t\t\t\treturn { key: String(option), value: option, label: String(option), disabled: false, count: null, iconOnly: false }\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: String(option.value),\n\t\t\t\t\t\tvalue: option.value,\n\t\t\t\t\t\tlabel: String(option.label ?? option.value),\n\t\t\t\t\t\tdisabled: option.disabled === true,\n\t\t\t\t\t\tcount: Number.isFinite(option.count) ? option.count : null,\n\t\t\t\t\t\ticonOnly: option.iconOnly === true,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The option Tab lands on: the chosen one, else the first enabled one.\n\t\t *\n\t\t * @return {number} Its index, or -1 when no option can take focus.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tfocusableIndex() {\n\t\t\tconst checked = this.normalizedOptions.findIndex((option) => this.isChecked(option) && !option.disabled)\n\t\t\tif (checked !== -1) {\n\t\t\t\treturn checked\n\t\t\t}\n\t\t\treturn this.normalizedOptions.findIndex((option) => !option.disabled)\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether an option is the chosen one.\n\t\t *\n\t\t * @param {{value: (string|number|boolean)}} option The option.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tisChecked(option) {\n\t\t\treturn this.modelValue !== null && String(option.value) === String(this.modelValue)\n\t\t},\n\n\t\t/**\n\t\t * Choose an option.\n\t\t *\n\t\t * @param {{value: (string|number|boolean), disabled: boolean}} option The option.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tselect(option) {\n\t\t\tif (option.disabled || this.isChecked(option)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:modelValue', option.value)\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move the choice, as in a native radio\n\t\t * group. Disabled options are passed over and the ends wrap around.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key press.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control\n\t\t */\n\t\tonKeydown(event) {\n\t\t\tif (this.isToggle) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst enabled = this.normalizedOptions\n\t\t\t\t.map((option, index) => ({ option, index }))\n\t\t\t\t.filter((entry) => !entry.option.disabled)\n\t\t\tif (enabled.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst buttons = Array.from(this.$el.querySelectorAll('[role=\"radio\"]'))\n\t\t\tconst focused = buttons.indexOf(document.activeElement)\n\t\t\tconst from = focused !== -1 ? focused : this.focusableIndex\n\t\t\tlet position = enabled.findIndex((entry) => entry.index === from)\n\t\t\tif (position === -1) {\n\t\t\t\tposition = 0\n\t\t\t}\n\t\t\tif (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n\t\t\t\tposition = (position + 1) % enabled.length\n\t\t\t} else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n\t\t\t\tposition = (position - 1 + enabled.length) % enabled.length\n\t\t\t} else if (event.key === 'Home') {\n\t\t\t\tposition = 0\n\t\t\t} else if (event.key === 'End') {\n\t\t\t\tposition = enabled.length - 1\n\t\t\t} else {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tconst target = enabled[position]\n\t\t\tthis.select(target.option)\n\t\t\tif (buttons[target.index] && typeof buttons[target.index].focus === 'function') {\n\t\t\t\tbuttons[target.index].focus()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-segmented-control {\n\tdisplay: inline-flex;\n\tgap: 4px;\n\tmax-width: 100%;\n\tpadding: 4px;\n\tborder-radius: var(--border-radius-container, var(--border-radius-large, 10px));\n\tbackground-color: var(--color-background-dark);\n\toverflow-x: auto;\n}\n\n.cn-segmented-control--stretch {\n\tdisplay: flex;\n}\n\n.cn-segmented-control--stretch .cn-segmented-control__option {\n\tflex: 1 1 0;\n}\n\n/* The parent class outscores Nextcloud's server rule that gives every plain\n `button` a margin and a border. */\n.cn-segmented-control .cn-segmented-control__option {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\tmin-height: 36px;\n\tmargin: 0;\n\tpadding: 0 16px;\n\tborder: 1px solid transparent;\n\tborder-radius: var(--border-radius-element, var(--border-radius-large, 8px));\n\tbackground-color: transparent;\n\t/* Full-contrast label on every option: the choice is carried by the\n\t raised surface, not by dimming the others. */\n\tcolor: var(--color-main-text);\n\tfont: inherit;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n\tcursor: pointer;\n}\n\n.cn-segmented-control .cn-segmented-control__option:hover {\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-segmented-control .cn-segmented-control__option:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n/* The border survives forced-colours mode, which drops background and shadow. */\n.cn-segmented-control .cn-segmented-control__option--checked,\n.cn-segmented-control .cn-segmented-control__option--checked:hover {\n\tborder-color: var(--color-border-dark);\n\tbackground-color: var(--color-main-background);\n\tbox-shadow: 0 1px 2px var(--color-box-shadow);\n}\n\n.cn-segmented-control .cn-segmented-control__option[disabled] {\n\tcursor: default;\n\topacity: 0.5;\n}\n\n.cn-segmented-control__count {\n\tfont-weight: 400;\n}\n\n/* Compact (screens-dashboard-parity): the board's period group. */\n.cn-segmented-control.cn-segmented-control--compact {\n\tgap: 2px;\n\tpadding: 3px;\n\tborder-radius: 8px;\n}\n\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option {\n\tmin-height: 34px;\n\theight: 34px;\n\tpadding: 0 12px;\n\tborder-radius: 6px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option--icon-only {\n\tmin-width: 40px;\n\tpadding: 0;\n}\n\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option--checked,\n.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option--checked:hover {\n\tborder-color: transparent;\n\tbox-shadow: 0 1px 2px var(--color-box-shadow);\n}\n\n/* On a phone (screens-dashboard-phone) the compact group's segments are 44px\n high touch targets, and the group wraps onto a second row instead of\n running off the screen. */\n@media (max-width: 640px) {\n\t.cn-segmented-control.cn-segmented-control--compact {\n\t\tflex-wrap: wrap;\n\t}\n\n\t.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option {\n\t\tmin-height: 44px;\n\t}\n}\n</style>\n"],"names":[],"mappings":"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"}