@conduction/nextcloud-vue 2.135.0 → 2.137.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 (123) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +2 -1
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +65 -50
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +9 -0
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +4 -3
  10. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  11. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +4 -1
  12. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +61 -28
  14. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  15. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  16. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +6 -0
  17. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  18. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js +9 -1
  19. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  20. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
  21. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
  22. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +4 -11
  23. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +32 -0
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +34 -26
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +24 -0
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -2
  31. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  32. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  33. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  34. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
  35. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  36. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +39 -2
  37. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  38. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -5
  39. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  40. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
  41. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +11 -0
  42. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  43. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +10 -2
  44. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  45. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  46. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +18 -0
  47. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  48. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +38 -23
  49. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  50. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
  51. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
  52. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +1 -0
  53. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
  54. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  55. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  56. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  57. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue.js +1 -1
  58. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue2.js +14 -4
  59. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue2.js.map +1 -1
  60. package/dist/esm/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue3.js.map +1 -1
  61. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +1 -1
  62. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +10 -0
  63. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -1
  64. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +29 -13
  65. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -1
  66. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +3 -1
  67. package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -1
  68. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +1 -1
  69. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -1
  70. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -1
  71. package/dist/esm/composables/indexSources.js +69 -3
  72. package/dist/esm/composables/indexSources.js.map +1 -1
  73. package/dist/esm/l10n/en.json.js +20 -20
  74. package/dist/esm/l10n/nl.json.js +21 -21
  75. package/dist/esm/library-style.js +1 -1
  76. package/dist/esm/nextcloud-vue.css +580 -324
  77. package/dist/esm/utils/contentHeaderLink.js +7 -1
  78. package/dist/esm/utils/contentHeaderLink.js.map +1 -1
  79. package/dist/esm/utils/savedViewMenu.js +4 -1
  80. package/dist/esm/utils/savedViewMenu.js.map +1 -1
  81. package/dist/esm/utils/savedViewSummary.js +11 -4
  82. package/dist/esm/utils/savedViewSummary.js.map +1 -1
  83. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  84. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  85. package/dist/esm/utils/widgetDispatch.js +3 -1
  86. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  87. package/dist/nextcloud-vue.cjs.js +1159 -812
  88. package/dist/nextcloud-vue.cjs.js.map +2 -2
  89. package/dist/nextcloud-vue.css +580 -324
  90. package/l10n/en.json +20 -20
  91. package/l10n/nl.json +21 -21
  92. package/package.json +1 -1
  93. package/src/components/CnActionDialog/CnActionDialog.vue +45 -4
  94. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +13 -1
  95. package/src/components/CnAppNav/CnAppNav.vue +28 -3
  96. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +15 -0
  97. package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +2 -10
  98. package/src/components/CnDataTable/CnDataTable.md +1 -0
  99. package/src/components/CnDataTable/CnDataTable.vue +38 -1
  100. package/src/components/CnDetailPage/CnDetailPage.md +1 -0
  101. package/src/components/CnDetailPage/CnDetailPage.vue +26 -2
  102. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +1 -0
  103. package/src/components/CnIndexPage/CnIndexPage.vue +44 -6
  104. package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -0
  105. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +21 -0
  106. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +38 -2
  107. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +7 -0
  108. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +13 -0
  109. package/src/components/CnSavedViewMenuSettings/CnSavedViewMenuSettings.vue +14 -4
  110. package/src/components/CnTimelineWidget/CnTimelineManage.vue +23 -0
  111. package/src/components/CnTimelineWidget/dashboardRegistration.js +3 -1
  112. package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +7 -0
  113. package/src/composables/indexSources.js +69 -3
  114. package/src/css/look-board-detail.css +57 -1
  115. package/src/css/look-board-index.css +76 -1
  116. package/src/css/look-board.css +51 -4
  117. package/src/css/table.css +5 -0
  118. package/src/schemas/app-manifest-v2.schema.json +4 -2
  119. package/src/utils/contentHeaderLink.js +7 -1
  120. package/src/utils/savedViewMenu.js +4 -1
  121. package/src/utils/savedViewSummary.js +11 -4
  122. package/src/utils/validateManifestV2.compiled.js +2 -2
  123. package/src/utils/widgetDispatch.js +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CnTimelineManage.vue3.js","sources":["../../../../src/components/CnTimelineWidget/CnTimelineManage.vue?vue&type=template&id=b7edba04&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnTimelineManage — the `manage` mode of CnTimelineWidget: one chronological\n read of OpenRegister's timeline on an object (notes, calls, messages), newest\n first with pinned entries on top, plus what a reader who may manage it can do\n there: filter by kind and visibility, pin, close a follow-up, write a note\n (optionally from a standard text block, optionally visible outside), export\n the shown entries as CSV, and run configured entry actions such as a reply\n dialog.\n\n The list read is filtered by OpenRegister whether or not a filter is asked\n for; the response says whether the reader may manage (`canManage`), and the\n controls are drawn from that rather than guessed. A failed read is not an\n empty history.\n-->\n<template>\n\t<div class=\"cn-timeline-manage\"\n\t\t:class=\"{ 'cn-timeline-manage--composer-only': composerOnly }\"\n\t\tdata-testid=\"cn-timeline-manage\">\n\t\t<template v-if=\"!composerOnly\">\n\t\t\t<div class=\"cn-timeline-manage__header\">\n\t\t\t\t<div class=\"cn-timeline-manage__heading\">\n\t\t\t\t\t<h3 class=\"cn-timeline-manage__title\">\n\t\t\t\t\t\t{{ titleText }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p class=\"cn-timeline-manage__subtitle\">\n\t\t\t\t\t\t{{ subtitleText }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-timeline-manage__header-actions\">\n\t\t\t\t\t<NcButton v-if=\"canManage && (composerEnabled || composerSeparate)\"\n\t\t\t\t\t\tdata-testid=\"cn-timeline-add-note\"\n\t\t\t\t\t\t@click=\"focusComposer\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Add note') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton data-testid=\"cn-timeline-export\" @click=\"exportEntries\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Download :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Export') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div class=\"cn-timeline-manage__chips\"\n\t\t\t\trole=\"group\"\n\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Kind')\"\n\t\t\t\tdata-testid=\"cn-timeline-kind-filter\">\n\t\t\t\t<button v-for=\"chip in kindChips\"\n\t\t\t\t\t:key=\"chip.key\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-timeline-manage__chip\"\n\t\t\t\t\t:class=\"{ 'cn-timeline-manage__chip--active': chip.active }\"\n\t\t\t\t\t:aria-pressed=\"chip.active ? 'true' : 'false'\"\n\t\t\t\t\t:data-testid=\"'cn-timeline-chip-' + chip.key\"\n\t\t\t\t\t@click=\"chooseKind(chip.id)\">\n\t\t\t\t\t{{ chip.label }}\n\t\t\t\t\t<span class=\"cn-timeline-manage__chip-count\">{{ chip.count }}</span>\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div class=\"cn-timeline-manage__controls\">\n\t\t\t\t<NcSelect v-if=\"canManage\"\n\t\t\t\t\tv-model=\"visibilityFilter\"\n\t\t\t\t\tclass=\"cn-timeline-manage__filter\"\n\t\t\t\t\t:options=\"visibilityOptions\"\n\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t:inputLabel=\"visibilityLabelShown ? t('nextcloud-vue', 'Visibility') : undefined\"\n\t\t\t\t\t:labelOutside=\"!visibilityLabelShown\"\n\t\t\t\t\t:ariaLabelCombobox=\"t('nextcloud-vue', 'Visibility')\"\n\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Internal and public')\"\n\t\t\t\t\tdata-testid=\"cn-timeline-visibility-filter\"\n\t\t\t\t\t@update:modelValue=\"load\" />\n\t\t\t</div>\n\n\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" />\n\n\t\t\t<NcNoteCard v-else-if=\"error\" type=\"error\" data-testid=\"cn-timeline-error\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<NcEmptyContent v-else-if=\"entries.length === 0\"\n\t\t\t\t:name=\"t('nextcloud-vue', 'Nothing recorded yet')\"\n\t\t\t\t:description=\"t('nextcloud-vue', 'Notes, calls and messages appear here in the order they happened.')\"\n\t\t\t\tdata-testid=\"cn-timeline-empty\" />\n\n\t\t\t<ul v-else class=\"cn-timeline-manage__list\" data-testid=\"cn-timeline-list\">\n\t\t\t\t<li v-for=\"entry in entries\"\n\t\t\t\t\t:key=\"entry.id\"\n\t\t\t\t\tclass=\"cn-timeline-manage__entry\"\n\t\t\t\t\t:class=\"{ 'cn-timeline-manage__entry--pinned': entry.pinned }\"\n\t\t\t\t\t:data-kind=\"entry.kind || 'note'\"\n\t\t\t\t\tdata-testid=\"cn-timeline-entry\">\n\t\t\t\t\t<div class=\"cn-timeline-manage__head\">\n\t\t\t\t\t\t<span class=\"cn-timeline-manage__kind\">{{ kindLabel(entry) }}</span>\n\t\t\t\t\t\t<span class=\"cn-timeline-manage__author\">{{ entry.author }}</span>\n\t\t\t\t\t\t<span class=\"cn-timeline-manage__moment\">{{ moment(entry.created) }}</span>\n\t\t\t\t\t\t<span v-if=\"entry.visibility === 'public'\"\n\t\t\t\t\t\t\tclass=\"cn-timeline-manage__badge\"\n\t\t\t\t\t\t\tdata-testid=\"cn-timeline-public\">\n\t\t\t\t\t\t\t{{ publicLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span v-if=\"entry.followUp === 'open'\"\n\t\t\t\t\t\t\tclass=\"cn-timeline-manage__badge cn-timeline-manage__badge--open\"\n\t\t\t\t\t\t\tdata-testid=\"cn-timeline-followup-open\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Needs an answer') }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p class=\"cn-timeline-manage__message\">\n\t\t\t\t\t\t{{ entry.message }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<p v-if=\"entry.siblings && entry.siblings.length\"\n\t\t\t\t\t\tclass=\"cn-timeline-manage__siblings\"\n\t\t\t\t\t\tdata-testid=\"cn-timeline-siblings\">\n\t\t\t\t\t\t{{ n('nextcloud-vue', 'Also written on %n other record', 'Also written on %n other records', entry.siblings.length) }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<div v-if=\"canManage && !entry.isRead\" class=\"cn-timeline-manage__actions\">\n\t\t\t\t\t\t<NcButton variant=\"tertiary\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-timeline-pin-' + entry.id\"\n\t\t\t\t\t\t\t@click=\"togglePin(entry)\">\n\t\t\t\t\t\t\t{{ entry.pinned ? t('nextcloud-vue', 'Unpin') : t('nextcloud-vue', 'Pin') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<NcButton v-for=\"action in actionsFor(entry)\"\n\t\t\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-timeline-action-' + action.id + '-' + entry.id\"\n\t\t\t\t\t\t\t@click=\"openAction(action, entry)\">\n\t\t\t\t\t\t\t{{ translate(action.label) }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<NcButton v-if=\"entry.followUp === 'open'\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-timeline-followup-' + entry.id\"\n\t\t\t\t\t\t\t@click=\"closeFollowUp(entry)\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Mark as answered') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\n\t\t\t<div v-if=\"(!loading && !error && allEntries.length > 0) || auditTrailLinkLabel || readsToggleLabel\"\n\t\t\t\tclass=\"cn-timeline-manage__footer\">\n\t\t\t\t<p v-if=\"!loading && !error && allEntries.length > 0\"\n\t\t\t\t\tclass=\"cn-timeline-manage__count\"\n\t\t\t\t\tdata-testid=\"cn-timeline-count\">\n\t\t\t\t\t{{ countLine }}\n\t\t\t\t</p>\n\t\t\t\t<NcCheckboxRadioSwitch v-if=\"readsToggleLabel && readsAllowed\"\n\t\t\t\t\tv-model=\"includeReads\"\n\t\t\t\t\tdata-testid=\"cn-timeline-include-reads\"\n\t\t\t\t\t@update:modelValue=\"load\">\n\t\t\t\t\t{{ readsToggleLabel }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<NcButton v-if=\"auditTrailLinkLabel\"\n\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\tclass=\"cn-timeline-manage__audit-link\"\n\t\t\t\t\tdata-testid=\"cn-timeline-audit-link\"\n\t\t\t\t\t@click=\"openAuditTrail\">\n\t\t\t\t\t{{ auditTrailLinkLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<NcNoteCard v-if=\"composerOnly && error\" type=\"error\" data-testid=\"cn-timeline-error\">\n\t\t\t{{ error }}\n\t\t</NcNoteCard>\n\n\t\t<form v-if=\"canManage && composerEnabled\"\n\t\t\tref=\"composer\"\n\t\t\tclass=\"cn-timeline-manage__composer\"\n\t\t\tdata-testid=\"cn-timeline-composer\"\n\t\t\t@submit.prevent=\"submit\">\n\t\t\t<template v-if=\"!composerOnly\">\n\t\t\t\t<h3 class=\"cn-timeline-manage__composer-title\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Add note') }}\n\t\t\t\t</h3>\n\t\t\t\t<p class=\"cn-timeline-manage__composer-hint\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Goes on the timeline above, internal unless you make it visible') }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t\t<NcSelect v-if=\"textBlocks.length\"\n\t\t\t\tv-model=\"chosenBlock\"\n\t\t\t\tclass=\"cn-timeline-manage__filter\"\n\t\t\t\t:options=\"blockOptions\"\n\t\t\t\t:clearable=\"true\"\n\t\t\t\tlabel=\"label\"\n\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Template')\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Write your own')\"\n\t\t\t\tdata-testid=\"cn-timeline-block\"\n\t\t\t\t@update:modelValue=\"pickBlock\" />\n\n\t\t\t<label class=\"cn-timeline-manage__draft-label\" :for=\"draftId\">\n\t\t\t\t{{ t('nextcloud-vue', 'Note') }}\n\t\t\t</label>\n\t\t\t<textarea :id=\"draftId\"\n\t\t\t\tv-model=\"draft\"\n\t\t\t\tclass=\"cn-timeline-manage__draft\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Write a note on this record')\"\n\t\t\t\tdata-testid=\"cn-timeline-draft\" />\n\n\t\t\t<div class=\"cn-timeline-manage__composer-foot\">\n\t\t\t\t<NcCheckboxRadioSwitch v-model=\"draftIsPublic\" data-testid=\"cn-timeline-draft-public\">\n\t\t\t\t\t{{ publicLabel }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<NcButton variant=\"primary\"\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t:disabled=\"saving || !draft.trim()\"\n\t\t\t\t\tdata-testid=\"cn-timeline-save\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Add it to the timeline') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</form>\n\n\t\t<component :is=\"activeModal.component\"\n\t\t\tv-if=\"activeModal\"\n\t\t\tv-bind=\"activeModal.props\"\n\t\t\tv-on=\"activeModal.listeners\" />\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport Download from 'vue-material-design-icons/Download.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport { prefixUrl } from '../../utils/headers.js'\n\nlet instanceCount = 0\n\n/** The kind of a read row (`readsToggle`), never a kind OpenRegister declares. */\nconst READ_KIND = 'cn-read'\n\n/** Event-bus channel: a note was added to an object's timeline (`{ objectId, source }`). */\nconst TIMELINE_CHANGED = 'cn:timeline:changed'\n\n/** Event-bus channel: the list's \"Add note\" asks the separate form card to take focus (`{ objectId }`). */\nconst TIMELINE_COMPOSE = 'cn:timeline:compose'\n\n/**\n * Read a dotted path off a value.\n *\n * @param {object} value The value.\n * @param {string} path The path.\n * @return {unknown} What is there, or undefined.\n */\nfunction readPath(value, path) {\n\treturn String(path).split('.').reduce((at, key) => (at === null || at === undefined ? undefined : at[key]), value)\n}\n\nexport default {\n\tname: 'CnTimelineManage',\n\n\tcomponents: { Download, NcButton, NcCheckboxRadioSwitch, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcSelect, Plus },\n\n\tinject: {\n\t\t/** The app's component registry; entry actions resolve their modal here. */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Consumer translation function; manifest labels are authored in English. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's object sidebar channel; the audit trail link opens its tab. */\n\t\tobjectSidebarState: { default: null },\n\t},\n\n\tprops: {\n\t\t/** Register slug. */\n\t\tregister: { type: String, default: '' },\n\t\t/** Schema slug. */\n\t\tschema: { type: String, default: '' },\n\t\t/** The object's id. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** OpenRegister API base. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/**\n\t\t * The widget content: `{ title?, subtitle?, publicLabel?, exportName?,\n\t\t * composer?, textBlocks?, entryActions?, auditTrailLink? }`.\n\t\t * `composer`: `true` (default) draws the note form under the list,\n\t\t * `false` none, `\"separate\"` none but keeps the \"Add note\" button,\n\t\t * which brings a `\"only\"` instance (the form alone, a card of its own\n\t\t * in a stack) into view; a note saved there reloads the list.\n\t\t * `readsToggle` (`true` or a label) adds a footer switch, off by\n\t\t * default, that puts who read the record (its audit trail's reads)\n\t\t * among the entries; the count then says \"changes only\" while it is off.\n\t\t * `auditTrailLink` (`true` or a label) draws a footer link that opens\n\t\t * the object sidebar on its audit trail tab, `auditTrailTab` when the\n\t\t * page declares that tab under its own id.\n\t\t */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tdata() {\n\t\tinstanceCount += 1\n\t\treturn {\n\t\t\tuid: instanceCount,\n\t\t\tallEntries: [],\n\t\t\tentries: [],\n\t\t\tkinds: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tcanManage: false,\n\t\t\tkindFilter: null,\n\t\t\tvisibilityFilter: null,\n\t\t\ttextBlocks: [],\n\t\t\tchosenBlock: null,\n\t\t\tdraft: '',\n\t\t\tdraftIsPublic: false,\n\t\t\tsaving: false,\n\t\t\tactiveModal: null,\n\t\t\t/** Whether the list also shows who read the record (`readsToggle`). */\n\t\t\tincludeReads: false,\n\t\t\t/** Whether OpenRegister gives this reader read entries (`readEventsVisible`); null until asked. */\n\t\t\treadsAllowed: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\ttitleText() {\n\t\t\treturn this.content.title ? this.translate(this.content.title) : t('nextcloud-vue', 'History')\n\t\t},\n\n\t\tsubtitleText() {\n\t\t\treturn this.content.subtitle ? this.translate(this.content.subtitle) : t('nextcloud-vue', 'What happened, who did it and when, newest first')\n\t\t},\n\n\t\tpublicLabel() {\n\t\t\treturn this.content.publicLabel ? this.translate(this.content.publicLabel) : t('nextcloud-vue', 'Visible to the requester')\n\t\t},\n\n\t\t/**\n\t\t * The audit trail link's words, or '' when it is not drawn.\n\t\t *\n\t\t * @spec openspec/changes/timeline-audit-trail-link/specs/timeline/spec.md#requirement-the-manage-timeline-can-point-at-the-audit-trail-in-the-sidebar\n\t\t * @return {string}\n\t\t */\n\t\tauditTrailLinkLabel() {\n\t\t\tconst link = this.content.auditTrailLink\n\t\t\tif (!link || !this.objectSidebarState) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof link === 'string' ? this.translate(link) : t('nextcloud-vue', 'Full audit trail in the sidebar')\n\t\t},\n\n\t\t/**\n\t\t * Whether this instance draws the note form: inline (`composer` true or\n\t\t * unset) or as the whole widget (`\"only\"`).\n\t\t *\n\t\t * @spec openspec/changes/timeline-composer-card/specs/timeline/spec.md#requirement-the-note-form-can-be-its-own-card\n\t\t * @return {boolean}\n\t\t */\n\t\tcomposerEnabled() {\n\t\t\treturn this.content.composer !== false && this.content.composer !== 'separate'\n\t\t},\n\n\t\t/**\n\t\t * The words of the \"also who read it\" switch, or '' when the widget\n\t\t * does not offer it (`readsToggle`: true or a label).\n\t\t *\n\t\t * @spec openspec/changes/timeline-reads-toggle/specs/timeline/spec.md#requirement-the-history-can-show-who-read-the-record\n\t\t * @return {string}\n\t\t */\n\t\treadsToggleLabel() {\n\t\t\tconst toggle = this.content.readsToggle\n\t\t\tif (!toggle) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof toggle === 'string' ? this.translate(toggle) : t('nextcloud-vue', 'Also who read it')\n\t\t},\n\n\t\t/**\n\t\t * \"3 of 10 events\", and \", changes only\" while reads can be shown but are not.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tcountLine() {\n\t\t\tconst values = { shown: this.entries.length, total: this.allEntries.length }\n\t\t\treturn this.readsToggleLabel && this.readsAllowed && !this.includeReads\n\t\t\t\t? t('nextcloud-vue', '{shown} of {total} events, changes only', values)\n\t\t\t\t: t('nextcloud-vue', '{shown} of {total} events', values)\n\t\t},\n\n\t\t/**\n\t\t * Whether the visibility filter shows its \"Visibility\" label. With\n\t\t * `visibilityLabel: false` it keeps only its accessible name and the\n\t\t * select reads \"Internal and public\" on its own (board DqZaakHistorie).\n\t\t *\n\t\t * @spec openspec/changes/timeline-visibility-filter-unlabelled/specs/timeline/spec.md#requirement-the-visibility-filter-can-go-without-a-visible-label\n\t\t * @return {boolean}\n\t\t */\n\t\tvisibilityLabelShown() {\n\t\t\treturn this.content.visibilityLabel !== false\n\t\t},\n\n\t\t/** @return {boolean} The widget is the note form alone (`composer: \"only\"`), a card of its own beside the list. */\n\t\tcomposerOnly() {\n\t\t\treturn this.content.composer === 'only'\n\t\t},\n\n\t\t/** @return {boolean} The list draws no form; its \"Add note\" brings the separate form card into view (`composer: \"separate\"`). */\n\t\tcomposerSeparate() {\n\t\t\treturn this.content.composer === 'separate'\n\t\t},\n\n\t\tdraftId() {\n\t\t\treturn `cn-timeline-draft-${this.uid}`\n\t\t},\n\n\t\t/**\n\t\t * The kind options, the plain note first. A note is an entry with NO\n\t\t * kind, so \"Note\" filters on its absence.\n\t\t *\n\t\t * @return {Array<object>} The options.\n\t\t */\n\t\tkindOptions() {\n\t\t\tconst options = [{ id: '', label: t('nextcloud-vue', 'Note') }].concat(this.kinds.map((kind) => ({ id: kind.slug, label: kind.title || kind.slug })))\n\t\t\treturn this.includeReads ? options.concat([{ id: READ_KIND, label: t('nextcloud-vue', 'Viewed') }]) : options\n\t\t},\n\n\t\t/**\n\t\t * All, then every kind the object has entries of, each with its count.\n\t\t *\n\t\t * @spec openspec/changes/timeline-all-chip-host-label/specs/timeline/spec.md#requirement-the-timelines-all-chip-reads-the-hosts-word\n\t\t * @return {Array<object>} The chips.\n\t\t */\n\t\tkindChips() {\n\t\t\tconst chosen = this.kindFilter ? (this.kindFilter.id || '') : null\n\t\t\tconst counts = new Map()\n\t\t\tfor (const entry of this.allEntries) {\n\t\t\t\tconst kind = entry.kind || ''\n\t\t\t\tcounts.set(kind, (counts.get(kind) || 0) + 1)\n\t\t\t}\n\t\t\t// The host's word first, as its own filter chips read (\"Alles\"), then the library's.\n\t\t\tconst hostAll = typeof this.cnTranslate === 'function' ? this.cnTranslate('All') : 'All'\n\t\t\tconst allLabel = typeof hostAll === 'string' && hostAll !== '' && hostAll !== 'All' ? hostAll : t('nextcloud-vue', 'All')\n\t\t\tconst chips = [{ key: 'all', id: null, label: allLabel, count: this.allEntries.length, active: chosen === null }]\n\t\t\tfor (const option of this.kindOptions) {\n\t\t\t\tconst count = counts.get(option.id) || 0\n\t\t\t\tif (count > 0) {\n\t\t\t\t\tchips.push({ key: option.id || 'note', id: option.id, label: option.label, count, active: chosen === option.id })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chips\n\t\t},\n\n\t\tblockOptions() {\n\t\t\treturn this.textBlocks.map((block) => ({ id: block.slug, label: block.title || block.slug }))\n\t\t},\n\n\t\tvisibilityOptions() {\n\t\t\treturn [\n\t\t\t\t{ id: 'internal', label: t('nextcloud-vue', 'Internal only') },\n\t\t\t\t{ id: 'public', label: this.publicLabel },\n\t\t\t]\n\t\t},\n\n\t\tbase() {\n\t\t\treturn this.apiBase.replace(/\\/$/, '')\n\t\t},\n\n\t\ttimelineUrl() {\n\t\t\tif (!this.objectId || !this.register || !this.schema) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn prefixUrl(`${this.base}/objects/${this.register}/${this.schema}/${this.objectId}/timeline`)\n\t\t},\n\n\t\tentryActions() {\n\t\t\treturn Array.isArray(this.content.entryActions) ? this.content.entryActions.filter((action) => action && action.id) : []\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: 'load',\n\t},\n\n\tcreated() {\n\t\tthis.onTimelineChanged = (payload) => {\n\t\t\tif (!payload || payload.objectId !== this.objectId || payload.source === this.uid || this.composerOnly) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.load()\n\t\t}\n\t\tthis.onComposeRequested = (payload) => {\n\t\t\tif (this.composerOnly && payload && payload.objectId === this.objectId) {\n\t\t\t\tthis.focusComposer()\n\t\t\t}\n\t\t}\n\t\tsubscribe(TIMELINE_CHANGED, this.onTimelineChanged)\n\t\tsubscribe(TIMELINE_COMPOSE, this.onComposeRequested)\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(TIMELINE_CHANGED, this.onTimelineChanged)\n\t\tunsubscribe(TIMELINE_COMPOSE, this.onComposeRequested)\n\t},\n\n\tmounted() {\n\t\tthis.loadKinds()\n\t\tif (this.content.textBlocks !== false) {\n\t\t\tthis.loadTextBlocks()\n\t\t}\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tn,\n\n\t\ttranslate(text) {\n\t\t\treturn text ? this.cnTranslate(text) : ''\n\t\t},\n\n\t\t/** Read the declared kinds, so the filter names them as administered. */\n\t\tasync loadKinds() {\n\t\t\ttry {\n\t\t\t\tconst response = await axios.get(prefixUrl(`${this.base}/timeline/kinds`))\n\t\t\t\tthis.kinds = response.data?.results || []\n\t\t\t} catch {\n\t\t\t\tthis.kinds = []\n\t\t\t}\n\t\t},\n\n\t\t/** Read the seeded standard notes. */\n\t\tasync loadTextBlocks() {\n\t\t\ttry {\n\t\t\t\tconst response = await axios.get(prefixUrl(`${this.base}/timeline/text-blocks`))\n\t\t\t\tthis.textBlocks = response.data?.results || []\n\t\t\t} catch {\n\t\t\t\tthis.textBlocks = []\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Note which standard note was picked. The BLOCK is sent, not its body:\n\t\t * OpenRegister fills the object's own values into it on the way in.\n\t\t *\n\t\t * @param {object|null} block The picked option.\n\t\t */\n\t\tpickBlock(block) {\n\t\t\tthis.chosenBlock = block || null\n\t\t\tif (block) {\n\t\t\t\tconst declared = this.textBlocks.find((entry) => entry.slug === block.id)\n\t\t\t\tthis.draft = declared?.title || block.label\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write the drafted note onto this object.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync submit() {\n\t\t\tif (!this.draft.trim() || !this.timelineUrl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst payload = { visibility: this.draftIsPublic ? 'public' : 'internal' }\n\t\t\t\tif (this.chosenBlock?.id) {\n\t\t\t\t\tpayload.textBlock = this.chosenBlock.id\n\t\t\t\t} else {\n\t\t\t\t\tpayload.message = this.draft\n\t\t\t\t}\n\t\t\t\tawait axios.post(this.timelineUrl, payload)\n\t\t\t\tthis.draft = ''\n\t\t\t\tthis.chosenBlock = null\n\t\t\t\tthis.draftIsPublic = false\n\t\t\t\temit(TIMELINE_CHANGED, { objectId: this.objectId, source: this.uid })\n\t\t\t\tawait this.load()\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'That note could not be saved.')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Read the timeline with the visibility filter, if one is set.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.timelineUrl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst params = {}\n\t\t\t\tif (this.visibilityFilter?.id) {\n\t\t\t\t\tparams.visibility = this.visibilityFilter.id\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(this.timelineUrl, { params })\n\t\t\t\tthis.canManage = response.data?.canManage === true\n\t\t\t\tconst entries = response.data?.results || []\n\t\t\t\tif (this.readsToggleLabel && this.readsAllowed === null) {\n\t\t\t\t\tawait this.readEntries()\n\t\t\t\t}\n\t\t\t\tthis.allEntries = this.readsToggleLabel && this.readsAllowed && this.includeReads ? this.withReads(entries, await this.readEntries()) : entries\n\t\t\t\tthis.entries = this.applyKindFilter(this.allEntries)\n\t\t\t} catch {\n\t\t\t\tthis.allEntries = []\n\t\t\t\tthis.entries = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'The timeline could not be read. Try again, or ask an administrator to check OpenRegister.')\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 * Who read the record, from its audit trail (`action=read`), as timeline\n\t\t * rows. OpenRegister gives read entries only to the groups named for\n\t\t * them and says so in `readEventsVisible`, which decides whether the\n\t\t * switch is offered at all; a read it refuses or that fails leaves the\n\t\t * reads out and offers no switch.\n\t\t *\n\t\t * @spec openspec/changes/timeline-reads-toggle/specs/timeline/spec.md#requirement-the-history-can-show-who-read-the-record\n\t\t * @return {Promise<Array<object>>} The read rows.\n\t\t */\n\t\tasync readEntries() {\n\t\t\ttry {\n\t\t\t\tconst url = prefixUrl(`${this.base}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails`)\n\t\t\t\tconst response = await axios.get(url, { params: { action: 'read', limit: 50 } })\n\t\t\t\tthis.readsAllowed = response.data?.readEventsVisible === true\n\t\t\t\tif (!this.readsAllowed) {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\tconst list = response.data?.results || []\n\t\t\t\treturn list\n\t\t\t\t\t.filter((entry) => String(entry.action || '').toLowerCase() === 'read')\n\t\t\t\t\t.map((entry) => ({\n\t\t\t\t\t\tid: `read-${entry.id}`,\n\t\t\t\t\t\tkind: READ_KIND,\n\t\t\t\t\t\tisRead: true,\n\t\t\t\t\t\tauthor: entry.actorDisplayName || entry.userName || entry.user || '',\n\t\t\t\t\t\tcreated: entry.created || entry.creationDateTime || '',\n\t\t\t\t\t\tmessage: t('nextcloud-vue', 'Opened the record'),\n\t\t\t\t\t}))\n\t\t\t} catch {\n\t\t\t\tthis.readsAllowed = false\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The timeline's entries with the reads among them, newest first; the\n\t\t * pinned entries stay on top.\n\t\t *\n\t\t * @param {Array<object>} entries The timeline's entries.\n\t\t * @param {Array<object>} reads The read rows.\n\t\t * @return {Array<object>} The merged list.\n\t\t */\n\t\twithReads(entries, reads) {\n\t\t\tconst time = (entry) => new Date(entry.created || 0).getTime() || 0\n\t\t\tconst pinned = entries.filter((entry) => entry.pinned)\n\t\t\tconst rest = [...entries.filter((entry) => !entry.pinned), ...reads].sort((a, b) => time(b) - time(a))\n\t\t\treturn [...pinned, ...rest]\n\t\t},\n\n\t\t/**\n\t\t * Show one kind, or every kind for `null`, without reading again.\n\t\t *\n\t\t * @param {string|null} id The kind slug, '' for a note, null for all.\n\t\t */\n\t\tchooseKind(id) {\n\t\t\tthis.kindFilter = id === null ? null : { id }\n\t\t\tthis.entries = this.applyKindFilter(this.allEntries)\n\t\t},\n\n\t\t/**\n\t\t * The kind filter is applied in the reading: the list endpoint does not\n\t\t * read a kind parameter, so sending one would be dropped in silence.\n\t\t *\n\t\t * @param {Array<object>} results The entries as read.\n\t\t * @return {Array<object>} The entries to show.\n\t\t */\n\t\tapplyKindFilter(results) {\n\t\t\tif (!this.kindFilter) {\n\t\t\t\treturn results\n\t\t\t}\n\t\t\tconst wanted = this.kindFilter.id || ''\n\t\t\treturn results.filter((entry) => (entry.kind || '') === wanted)\n\t\t},\n\n\t\t/**\n\t\t * The shown entries as CSV: when, kind, who, visibility and what.\n\t\t *\n\t\t * @return {string} The CSV text.\n\t\t */\n\t\tentriesCsv() {\n\t\t\tconst cell = (value) => `\"${String(value ?? '').replace(/\"/g, '\"\"')}\"`\n\t\t\tconst rows = [[t('nextcloud-vue', 'When'), t('nextcloud-vue', 'Kind'), t('nextcloud-vue', 'Who'), t('nextcloud-vue', 'Visibility'), t('nextcloud-vue', 'Message')]]\n\t\t\tfor (const entry of this.entries) {\n\t\t\t\trows.push([entry.created, this.kindLabel(entry), entry.author, entry.visibility, entry.message])\n\t\t\t}\n\t\t\treturn rows.map((row) => row.map(cell).join(',')).join('\\r\\n')\n\t\t},\n\n\t\t/** Download the shown entries as a CSV file. */\n\t\texportEntries() {\n\t\t\tconst blob = new Blob([this.entriesCsv()], { type: 'text/csv;charset=utf-8' })\n\t\t\tconst url = URL.createObjectURL(blob)\n\t\t\tconst link = document.createElement('a')\n\t\t\tlink.href = url\n\t\t\tlink.download = `${this.content.exportName || 'history'}-${this.objectId || 'record'}.csv`\n\t\t\tdocument.body.appendChild(link)\n\t\t\tlink.click()\n\t\t\tlink.remove()\n\t\t\tURL.revokeObjectURL(url)\n\t\t},\n\n\t\t/**\n\t\t * Open the object sidebar on its audit trail tab.\n\t\t *\n\t\t * @spec openspec/changes/timeline-audit-trail-link/specs/timeline/spec.md#requirement-the-manage-timeline-can-point-at-the-audit-trail-in-the-sidebar\n\t\t */\n\t\topenAuditTrail() {\n\t\t\tthis.objectSidebarState.open = true\n\t\t\tthis.objectSidebarState.requestedTab = this.content.auditTrailTab || 'auditTrail'\n\t\t},\n\n\t\t/** Bring the composer into view and focus its text; a separate form card is asked to do so. */\n\t\tfocusComposer() {\n\t\t\tif (this.composerSeparate) {\n\t\t\t\temit(TIMELINE_COMPOSE, { objectId: this.objectId })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst form = this.$refs.composer\n\t\t\tif (!form) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tform.scrollIntoView?.({ behavior: 'smooth', block: 'center' })\n\t\t\tform.querySelector('textarea')?.focus()\n\t\t},\n\n\t\t/**\n\t\t * The label of one entry's kind, falling back to \"Note\".\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The label.\n\t\t */\n\t\tkindLabel(entry) {\n\t\t\tif (entry.kind === READ_KIND) {\n\t\t\t\treturn t('nextcloud-vue', 'Viewed')\n\t\t\t}\n\t\t\tif (!entry.kind) {\n\t\t\t\treturn t('nextcloud-vue', 'Note')\n\t\t\t}\n\t\t\tconst declared = this.kinds.find((kind) => kind.slug === entry.kind)\n\t\t\treturn declared?.title || entry.kind\n\t\t},\n\n\t\t/**\n\t\t * One moment in the reader's locale.\n\t\t *\n\t\t * @param {string} value An ISO 8601 stamp.\n\t\t * @return {string} The moment, or the raw value.\n\t\t */\n\t\tmoment(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst parsed = new Date(value)\n\t\t\treturn isNaN(parsed.getTime()) ? value : parsed.toLocaleString()\n\t\t},\n\n\t\tasync togglePin(entry) {\n\t\t\tawait this.patch(entry, { pinned: !entry.pinned })\n\t\t},\n\n\t\tasync closeFollowUp(entry) {\n\t\t\tawait this.patch(entry, { followUp: 'done' })\n\t\t},\n\n\t\t/**\n\t\t * Write one change onto one entry and read the timeline back (pinning\n\t\t * changes the order, so patching in place would look like nothing).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {object} changes The fields to write.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync patch(entry, changes) {\n\t\t\ttry {\n\t\t\t\tawait axios.patch(`${this.timelineUrl}/${entry.id}`, changes)\n\t\t\t\tawait this.load()\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'That change could not be saved.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The configured entry actions that apply to one entry (`kinds` limits\n\t\t * an action to entries of those kinds).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {Array<object>} The actions.\n\t\t */\n\t\tactionsFor(entry) {\n\t\t\treturn this.entryActions.filter((action) => !Array.isArray(action.kinds) || action.kinds.includes(entry.kind || ''))\n\t\t},\n\n\t\t/**\n\t\t * Resolve `@objectId` and `@row.<path>` tokens in an action's props.\n\t\t *\n\t\t * @param {object} props The props as declared.\n\t\t * @param {object} entry The entry.\n\t\t * @return {object} The resolved props.\n\t\t */\n\t\tresolveProps(props, entry) {\n\t\t\tconst out = {}\n\t\t\tfor (const [key, value] of Object.entries(props || {})) {\n\t\t\t\tif (value === '@objectId') {\n\t\t\t\t\tout[key] = this.objectId\n\t\t\t\t} else if (typeof value === 'string' && value.startsWith('@row.')) {\n\t\t\t\t\tconst found = readPath(entry, value.slice('@row.'.length))\n\t\t\t\t\tout[key] = found === undefined || found === null ? '' : found\n\t\t\t\t} else {\n\t\t\t\t\tout[key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Open an entry action's modal from the app's registry. On its\n\t\t * `doneEvent` (default `done`) the entry's open follow-up is closed when\n\t\t * the action says so; otherwise the timeline is read again.\n\t\t *\n\t\t * @param {object} action The action.\n\t\t * @param {object} entry The entry.\n\t\t */\n\t\topenAction(action, entry) {\n\t\t\tconst registered = (this.cnRegistry || {})[action.modal]\n\t\t\tconst component = registered && (registered.component || registered)\n\t\t\tif (!component) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnTimelineWidget] entry action \"${action.id}\" names modal \"${action.modal}\", which the registry does not hold.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst close = () => {\n\t\t\t\tthis.activeModal = null\n\t\t\t}\n\t\t\tconst done = async () => {\n\t\t\t\tthis.activeModal = null\n\t\t\t\tif (action.closesFollowUp === true && entry.followUp === 'open') {\n\t\t\t\t\tawait this.closeFollowUp(entry)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tawait this.load()\n\t\t\t}\n\t\t\tthis.activeModal = {\n\t\t\t\tcomponent,\n\t\t\t\tprops: this.resolveProps(action.props, entry),\n\t\t\t\tlisteners: { [action.doneEvent || 'done']: done, close },\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-timeline-manage__header {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-block-end: 16px;\n}\n\n.cn-timeline-manage__heading {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-timeline-manage__header-actions {\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-timeline-manage__title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\tline-height: 1.2;\n}\n\n.cn-timeline-manage__subtitle {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n.cn-timeline-manage__chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tmargin-block-end: 12px;\n}\n\n.cn-timeline-manage__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\theight: 36px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 18px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcursor: pointer;\n}\n\n/* The chosen kind is a filled chip with no outline. */\n.cn-timeline-manage__chip--active {\n\tborder: 0;\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-background);\n}\n\n.cn-timeline-manage__chip-count {\n\tpadding: 1px 7px;\n\tborder-radius: 10px;\n\tbackground: var(--color-border);\n\tcolor: var(--color-main-text);\n\tfont-size: 12px;\n}\n\n/* On the filled chip the count is a lighter ink bubble with light text. */\n.cn-timeline-manage__chip--active .cn-timeline-manage__chip-count {\n\tbackground: color-mix(in srgb, var(--color-main-background) 18%, var(--color-main-text));\n\tcolor: var(--color-main-background);\n}\n\n.cn-timeline-manage__footer {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tmargin: 12px 0 0;\n}\n\n.cn-timeline-manage__count {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-manage__composer {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmargin-block-start: 24px;\n\tpadding-block-start: 16px;\n\tborder-block-start: 1px solid var(--color-border);\n}\n\n.cn-timeline-manage--composer-only .cn-timeline-manage__composer {\n\tmargin-block-start: 0;\n\tpadding-block-start: 0;\n\tborder-block-start: none;\n}\n\n/* The checkbox at the start, the button at the end, as one row. */\n.cn-timeline-manage__composer-foot {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-timeline-manage__composer-title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-timeline-manage__composer-hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-manage__draft-label {\n\tfont-weight: bold;\n}\n\n.cn-timeline-manage__draft {\n\twidth: 100%;\n\tmin-height: 70px;\n}\n\n.cn-timeline-manage__controls {\n\tdisplay: flex;\n\tgap: 12px;\n\tflex-wrap: wrap;\n\tmargin-block-end: 12px;\n}\n\n.cn-timeline-manage__filter {\n\tmin-width: 200px;\n}\n\n.cn-timeline-manage__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-timeline-manage__entry {\n\tborder-block-end: 1px solid var(--color-border);\n\tpadding-block: 12px;\n}\n\n.cn-timeline-manage__entry--pinned {\n\tborder-inline-start: 3px solid var(--color-primary-element);\n\tpadding-inline-start: 8px;\n}\n\n.cn-timeline-manage__head {\n\tdisplay: flex;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-timeline-manage__kind {\n\tfont-weight: bold;\n\tcolor: var(--color-main-text);\n}\n\n.cn-timeline-manage__badge {\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 0 6px;\n}\n\n.cn-timeline-manage__badge--open {\n\tborder-color: var(--color-warning);\n\tcolor: var(--color-warning-text);\n}\n\n.cn-timeline-manage__message {\n\tmargin-block: 4px 0;\n\twhite-space: pre-wrap;\n}\n\n.cn-timeline-manage__siblings {\n\tmargin-block: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-timeline-manage__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-block-start: 4px;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_Fragment","_createElementVNode","_toDisplayString","_createBlock","_createVNode","_createTextVNode","_openBlock","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnTimelineManage.vue3.js","sources":["../../../../src/components/CnTimelineWidget/CnTimelineManage.vue?vue&type=template&id=a4c73a6a&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnTimelineManage — the `manage` mode of CnTimelineWidget: one chronological\n read of OpenRegister's timeline on an object (notes, calls, messages), newest\n first with pinned entries on top, plus what a reader who may manage it can do\n there: filter by kind and visibility, pin, close a follow-up, write a note\n (optionally from a standard text block, optionally visible outside), export\n the shown entries as CSV, and run configured entry actions such as a reply\n dialog.\n\n The list read is filtered by OpenRegister whether or not a filter is asked\n for; the response says whether the reader may manage (`canManage`), and the\n controls are drawn from that rather than guessed. A failed read is not an\n empty history.\n-->\n<template>\n\t<div class=\"cn-timeline-manage\"\n\t\t:class=\"{ 'cn-timeline-manage--composer-only': composerOnly }\"\n\t\tdata-testid=\"cn-timeline-manage\">\n\t\t<template v-if=\"!composerOnly\">\n\t\t\t<div class=\"cn-timeline-manage__header\">\n\t\t\t\t<div class=\"cn-timeline-manage__heading\">\n\t\t\t\t\t<h3 class=\"cn-timeline-manage__title\">\n\t\t\t\t\t\t{{ titleText }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p class=\"cn-timeline-manage__subtitle\">\n\t\t\t\t\t\t{{ subtitleText }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-timeline-manage__header-actions\">\n\t\t\t\t\t<NcButton v-if=\"canManage && (composerEnabled || composerSeparate)\"\n\t\t\t\t\t\tdata-testid=\"cn-timeline-add-note\"\n\t\t\t\t\t\t@click=\"focusComposer\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Add note') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton data-testid=\"cn-timeline-export\" @click=\"exportEntries\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Download :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Export') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div class=\"cn-timeline-manage__chips\"\n\t\t\t\trole=\"group\"\n\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Kind')\"\n\t\t\t\tdata-testid=\"cn-timeline-kind-filter\">\n\t\t\t\t<button v-for=\"chip in kindChips\"\n\t\t\t\t\t:key=\"chip.key\"\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"cn-timeline-manage__chip\"\n\t\t\t\t\t:class=\"{ 'cn-timeline-manage__chip--active': chip.active }\"\n\t\t\t\t\t:aria-pressed=\"chip.active ? 'true' : 'false'\"\n\t\t\t\t\t:data-testid=\"'cn-timeline-chip-' + chip.key\"\n\t\t\t\t\t@click=\"chooseKind(chip.id)\">\n\t\t\t\t\t{{ chip.label }}\n\t\t\t\t\t<span class=\"cn-timeline-manage__chip-count\">{{ chip.count }}</span>\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t<div class=\"cn-timeline-manage__controls\">\n\t\t\t\t<NcSelect v-if=\"canManage\"\n\t\t\t\t\tv-model=\"visibilityFilter\"\n\t\t\t\t\tclass=\"cn-timeline-manage__filter\"\n\t\t\t\t\t:options=\"visibilityOptions\"\n\t\t\t\t\t:clearable=\"true\"\n\t\t\t\t\tlabel=\"label\"\n\t\t\t\t\t:inputLabel=\"visibilityLabelShown ? t('nextcloud-vue', 'Visibility') : undefined\"\n\t\t\t\t\t:labelOutside=\"!visibilityLabelShown\"\n\t\t\t\t\t:ariaLabelCombobox=\"t('nextcloud-vue', 'Visibility')\"\n\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Internal and public')\"\n\t\t\t\t\tdata-testid=\"cn-timeline-visibility-filter\"\n\t\t\t\t\t@update:modelValue=\"load\" />\n\t\t\t</div>\n\n\t\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" />\n\n\t\t\t<NcNoteCard v-else-if=\"error\" type=\"error\" data-testid=\"cn-timeline-error\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<NcEmptyContent v-else-if=\"entries.length === 0\"\n\t\t\t\t:name=\"t('nextcloud-vue', 'Nothing recorded yet')\"\n\t\t\t\t:description=\"t('nextcloud-vue', 'Notes, calls and messages appear here in the order they happened.')\"\n\t\t\t\tdata-testid=\"cn-timeline-empty\" />\n\n\t\t\t<ul v-else class=\"cn-timeline-manage__list\" data-testid=\"cn-timeline-list\">\n\t\t\t\t<li v-for=\"entry in entries\"\n\t\t\t\t\t:key=\"entry.id\"\n\t\t\t\t\tclass=\"cn-timeline-manage__entry\"\n\t\t\t\t\t:class=\"{ 'cn-timeline-manage__entry--pinned': entry.pinned }\"\n\t\t\t\t\t:data-kind=\"entry.kind || 'note'\"\n\t\t\t\t\tdata-testid=\"cn-timeline-entry\">\n\t\t\t\t\t<div class=\"cn-timeline-manage__head\">\n\t\t\t\t\t\t<span class=\"cn-timeline-manage__kind\">{{ kindLabel(entry) }}</span>\n\t\t\t\t\t\t<span class=\"cn-timeline-manage__author\">{{ entry.author }}</span>\n\t\t\t\t\t\t<span class=\"cn-timeline-manage__moment\">{{ moment(entry.created) }}</span>\n\t\t\t\t\t\t<span v-if=\"entry.visibility === 'public'\"\n\t\t\t\t\t\t\tclass=\"cn-timeline-manage__badge\"\n\t\t\t\t\t\t\tdata-testid=\"cn-timeline-public\">\n\t\t\t\t\t\t\t{{ publicLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span v-if=\"entry.followUp === 'open'\"\n\t\t\t\t\t\t\tclass=\"cn-timeline-manage__badge cn-timeline-manage__badge--open\"\n\t\t\t\t\t\t\tdata-testid=\"cn-timeline-followup-open\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Needs an answer') }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p class=\"cn-timeline-manage__message\">\n\t\t\t\t\t\t{{ entry.message }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<p v-if=\"entry.siblings && entry.siblings.length\"\n\t\t\t\t\t\tclass=\"cn-timeline-manage__siblings\"\n\t\t\t\t\t\tdata-testid=\"cn-timeline-siblings\">\n\t\t\t\t\t\t{{ n('nextcloud-vue', 'Also written on %n other record', 'Also written on %n other records', entry.siblings.length) }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<div v-if=\"canManage && !entry.isRead\" class=\"cn-timeline-manage__actions\">\n\t\t\t\t\t\t<NcButton variant=\"tertiary\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-timeline-pin-' + entry.id\"\n\t\t\t\t\t\t\t@click=\"togglePin(entry)\">\n\t\t\t\t\t\t\t{{ entry.pinned ? t('nextcloud-vue', 'Unpin') : t('nextcloud-vue', 'Pin') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<NcButton v-for=\"action in actionsFor(entry)\"\n\t\t\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-timeline-action-' + action.id + '-' + entry.id\"\n\t\t\t\t\t\t\t@click=\"openAction(action, entry)\">\n\t\t\t\t\t\t\t{{ translate(action.label) }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t<NcButton v-if=\"entry.followUp === 'open'\"\n\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t:data-testid=\"'cn-timeline-followup-' + entry.id\"\n\t\t\t\t\t\t\t@click=\"closeFollowUp(entry)\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Mark as answered') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t</ul>\n\n\t\t\t<div v-if=\"(!loading && !error && allEntries.length > 0) || auditTrailLinkLabel || readsToggleLabel\"\n\t\t\t\tclass=\"cn-timeline-manage__footer\">\n\t\t\t\t<p v-if=\"!loading && !error && allEntries.length > 0\"\n\t\t\t\t\tclass=\"cn-timeline-manage__count\"\n\t\t\t\t\tdata-testid=\"cn-timeline-count\">\n\t\t\t\t\t{{ countLine }}\n\t\t\t\t</p>\n\t\t\t\t<NcCheckboxRadioSwitch v-if=\"readsToggleLabel && readsAllowed\"\n\t\t\t\t\tv-model=\"includeReads\"\n\t\t\t\t\tdata-testid=\"cn-timeline-include-reads\"\n\t\t\t\t\t@update:modelValue=\"load\">\n\t\t\t\t\t{{ readsToggleLabel }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<NcButton v-if=\"auditTrailLinkLabel\"\n\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\tclass=\"cn-timeline-manage__audit-link\"\n\t\t\t\t\tdata-testid=\"cn-timeline-audit-link\"\n\t\t\t\t\t@click=\"openAuditTrail\">\n\t\t\t\t\t{{ auditTrailLinkLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<div v-if=\"composerOnly && canManage && composerEnabled\"\n\t\t\tclass=\"cn-timeline-manage__header\"\n\t\t\tdata-testid=\"cn-timeline-composer-heading\">\n\t\t\t<div class=\"cn-timeline-manage__heading\">\n\t\t\t\t<h3 class=\"cn-timeline-manage__title\">\n\t\t\t\t\t{{ composerTitleText }}\n\t\t\t\t</h3>\n\t\t\t\t<p class=\"cn-timeline-manage__subtitle\">\n\t\t\t\t\t{{ composerSubtitleText }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<NcNoteCard v-if=\"composerOnly && error\" type=\"error\" data-testid=\"cn-timeline-error\">\n\t\t\t{{ error }}\n\t\t</NcNoteCard>\n\n\t\t<form v-if=\"canManage && composerEnabled\"\n\t\t\tref=\"composer\"\n\t\t\tclass=\"cn-timeline-manage__composer\"\n\t\t\tdata-testid=\"cn-timeline-composer\"\n\t\t\t@submit.prevent=\"submit\">\n\t\t\t<template v-if=\"!composerOnly\">\n\t\t\t\t<h3 class=\"cn-timeline-manage__composer-title\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Add note') }}\n\t\t\t\t</h3>\n\t\t\t\t<p class=\"cn-timeline-manage__composer-hint\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Goes on the timeline above, internal unless you make it visible') }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t\t<NcSelect v-if=\"textBlocks.length\"\n\t\t\t\tv-model=\"chosenBlock\"\n\t\t\t\tclass=\"cn-timeline-manage__filter\"\n\t\t\t\t:options=\"blockOptions\"\n\t\t\t\t:clearable=\"true\"\n\t\t\t\tlabel=\"label\"\n\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Template')\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Write your own')\"\n\t\t\t\tdata-testid=\"cn-timeline-block\"\n\t\t\t\t@update:modelValue=\"pickBlock\" />\n\n\t\t\t<label class=\"cn-timeline-manage__draft-label\" :for=\"draftId\">\n\t\t\t\t{{ t('nextcloud-vue', 'Note') }}\n\t\t\t</label>\n\t\t\t<textarea :id=\"draftId\"\n\t\t\t\tv-model=\"draft\"\n\t\t\t\tclass=\"cn-timeline-manage__draft\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Write a note on this record')\"\n\t\t\t\tdata-testid=\"cn-timeline-draft\" />\n\n\t\t\t<div class=\"cn-timeline-manage__composer-foot\">\n\t\t\t\t<NcCheckboxRadioSwitch v-model=\"draftIsPublic\" data-testid=\"cn-timeline-draft-public\">\n\t\t\t\t\t{{ publicLabel }}\n\t\t\t\t</NcCheckboxRadioSwitch>\n\n\t\t\t\t<NcButton variant=\"primary\"\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t:disabled=\"saving || !draft.trim()\"\n\t\t\t\t\tdata-testid=\"cn-timeline-save\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Add it to the timeline') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</form>\n\n\t\t<component :is=\"activeModal.component\"\n\t\t\tv-if=\"activeModal\"\n\t\t\tv-bind=\"activeModal.props\"\n\t\t\tv-on=\"activeModal.listeners\" />\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport Download from 'vue-material-design-icons/Download.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport { prefixUrl } from '../../utils/headers.js'\n\nlet instanceCount = 0\n\n/** The kind of a read row (`readsToggle`), never a kind OpenRegister declares. */\nconst READ_KIND = 'cn-read'\n\n/** Event-bus channel: a note was added to an object's timeline (`{ objectId, source }`). */\nconst TIMELINE_CHANGED = 'cn:timeline:changed'\n\n/** Event-bus channel: the list's \"Add note\" asks the separate form card to take focus (`{ objectId }`). */\nconst TIMELINE_COMPOSE = 'cn:timeline:compose'\n\n/**\n * Read a dotted path off a value.\n *\n * @param {object} value The value.\n * @param {string} path The path.\n * @return {unknown} What is there, or undefined.\n */\nfunction readPath(value, path) {\n\treturn String(path).split('.').reduce((at, key) => (at === null || at === undefined ? undefined : at[key]), value)\n}\n\nexport default {\n\tname: 'CnTimelineManage',\n\n\tcomponents: { Download, NcButton, NcCheckboxRadioSwitch, NcEmptyContent, NcLoadingIcon, NcNoteCard, NcSelect, Plus },\n\n\tinject: {\n\t\t/** The app's component registry; entry actions resolve their modal here. */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Consumer translation function; manifest labels are authored in English. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's object sidebar channel; the audit trail link opens its tab. */\n\t\tobjectSidebarState: { default: null },\n\t},\n\n\tprops: {\n\t\t/** Register slug. */\n\t\tregister: { type: String, default: '' },\n\t\t/** Schema slug. */\n\t\tschema: { type: String, default: '' },\n\t\t/** The object's id. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** OpenRegister API base. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/**\n\t\t * The widget content: `{ title?, subtitle?, publicLabel?, exportName?,\n\t\t * composer?, textBlocks?, entryActions?, auditTrailLink? }`.\n\t\t * `composer`: `true` (default) draws the note form under the list,\n\t\t * `false` none, `\"separate\"` none but keeps the \"Add note\" button,\n\t\t * which brings a `\"only\"` instance (the form alone, a card of its own\n\t\t * in a stack) into view; a note saved there reloads the list.\n\t\t * `readsToggle` (`true` or a label) adds a footer switch, off by\n\t\t * default, that puts who read the record (its audit trail's reads)\n\t\t * among the entries; the count then says \"changes only\" while it is off.\n\t\t * `auditTrailLink` (`true` or a label) draws a footer link that opens\n\t\t * the object sidebar on its audit trail tab, `auditTrailTab` when the\n\t\t * page declares that tab under its own id.\n\t\t */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tdata() {\n\t\tinstanceCount += 1\n\t\treturn {\n\t\t\tuid: instanceCount,\n\t\t\tallEntries: [],\n\t\t\tentries: [],\n\t\t\tkinds: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tcanManage: false,\n\t\t\tkindFilter: null,\n\t\t\tvisibilityFilter: null,\n\t\t\ttextBlocks: [],\n\t\t\tchosenBlock: null,\n\t\t\tdraft: '',\n\t\t\tdraftIsPublic: false,\n\t\t\tsaving: false,\n\t\t\tactiveModal: null,\n\t\t\t/** Whether the list also shows who read the record (`readsToggle`). */\n\t\t\tincludeReads: false,\n\t\t\t/** Whether OpenRegister gives this reader read entries (`readEventsVisible`); null until asked. */\n\t\t\treadsAllowed: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\ttitleText() {\n\t\t\treturn this.content.title ? this.translate(this.content.title) : t('nextcloud-vue', 'History')\n\t\t},\n\n\t\tsubtitleText() {\n\t\t\treturn this.content.subtitle ? this.translate(this.content.subtitle) : t('nextcloud-vue', 'What happened, who did it and when, newest first')\n\t\t},\n\n\t\t/** @return {string} The title of the note form's own card (`composer: \"only\"`): `content.title`, else Add note. */\n\t\tcomposerTitleText() {\n\t\t\treturn this.content.title ? this.translate(this.content.title) : t('nextcloud-vue', 'Add note')\n\t\t},\n\n\t\t/** @return {string} The line under that title: `content.subtitle`, else where the note goes. */\n\t\tcomposerSubtitleText() {\n\t\t\treturn this.content.subtitle ? this.translate(this.content.subtitle) : t('nextcloud-vue', 'Goes on the timeline above, internal unless you make it visible')\n\t\t},\n\n\t\tpublicLabel() {\n\t\t\treturn this.content.publicLabel ? this.translate(this.content.publicLabel) : t('nextcloud-vue', 'Visible to the requester')\n\t\t},\n\n\t\t/**\n\t\t * The audit trail link's words, or '' when it is not drawn.\n\t\t *\n\t\t * @spec openspec/changes/timeline-audit-trail-link/specs/timeline/spec.md#requirement-the-manage-timeline-can-point-at-the-audit-trail-in-the-sidebar\n\t\t * @return {string}\n\t\t */\n\t\tauditTrailLinkLabel() {\n\t\t\tconst link = this.content.auditTrailLink\n\t\t\tif (!link || !this.objectSidebarState) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof link === 'string' ? this.translate(link) : t('nextcloud-vue', 'Full audit trail in the sidebar')\n\t\t},\n\n\t\t/**\n\t\t * Whether this instance draws the note form: inline (`composer` true or\n\t\t * unset) or as the whole widget (`\"only\"`).\n\t\t *\n\t\t * @spec openspec/changes/timeline-composer-card/specs/timeline/spec.md#requirement-the-note-form-can-be-its-own-card\n\t\t * @return {boolean}\n\t\t */\n\t\tcomposerEnabled() {\n\t\t\treturn this.content.composer !== false && this.content.composer !== 'separate'\n\t\t},\n\n\t\t/**\n\t\t * The words of the \"also who read it\" switch, or '' when the widget\n\t\t * does not offer it (`readsToggle`: true or a label).\n\t\t *\n\t\t * @spec openspec/changes/timeline-reads-toggle/specs/timeline/spec.md#requirement-the-history-can-show-who-read-the-record\n\t\t * @return {string}\n\t\t */\n\t\treadsToggleLabel() {\n\t\t\tconst toggle = this.content.readsToggle\n\t\t\tif (!toggle) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof toggle === 'string' ? this.translate(toggle) : t('nextcloud-vue', 'Also who read it')\n\t\t},\n\n\t\t/**\n\t\t * \"3 of 10 events\", and \", changes only\" while reads can be shown but are not.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tcountLine() {\n\t\t\tconst values = { shown: this.entries.length, total: this.allEntries.length }\n\t\t\treturn this.readsToggleLabel && this.readsAllowed && !this.includeReads\n\t\t\t\t? t('nextcloud-vue', '{shown} of {total} events, changes only', values)\n\t\t\t\t: t('nextcloud-vue', '{shown} of {total} events', values)\n\t\t},\n\n\t\t/**\n\t\t * Whether the visibility filter shows its \"Visibility\" label. With\n\t\t * `visibilityLabel: false` it keeps only its accessible name and the\n\t\t * select reads \"Internal and public\" on its own (board DqZaakHistorie).\n\t\t *\n\t\t * @spec openspec/changes/timeline-visibility-filter-unlabelled/specs/timeline/spec.md#requirement-the-visibility-filter-can-go-without-a-visible-label\n\t\t * @return {boolean}\n\t\t */\n\t\tvisibilityLabelShown() {\n\t\t\treturn this.content.visibilityLabel !== false\n\t\t},\n\n\t\t/** @return {boolean} The widget is the note form alone (`composer: \"only\"`), a card of its own beside the list. */\n\t\tcomposerOnly() {\n\t\t\treturn this.content.composer === 'only'\n\t\t},\n\n\t\t/** @return {boolean} The list draws no form; its \"Add note\" brings the separate form card into view (`composer: \"separate\"`). */\n\t\tcomposerSeparate() {\n\t\t\treturn this.content.composer === 'separate'\n\t\t},\n\n\t\tdraftId() {\n\t\t\treturn `cn-timeline-draft-${this.uid}`\n\t\t},\n\n\t\t/**\n\t\t * The kind options, the plain note first. A note is an entry with NO\n\t\t * kind, so \"Note\" filters on its absence.\n\t\t *\n\t\t * @return {Array<object>} The options.\n\t\t */\n\t\tkindOptions() {\n\t\t\tconst options = [{ id: '', label: t('nextcloud-vue', 'Note') }].concat(this.kinds.map((kind) => ({ id: kind.slug, label: kind.title || kind.slug })))\n\t\t\treturn this.includeReads ? options.concat([{ id: READ_KIND, label: t('nextcloud-vue', 'Viewed') }]) : options\n\t\t},\n\n\t\t/**\n\t\t * All, then every kind the object has entries of, each with its count.\n\t\t *\n\t\t * @spec openspec/changes/timeline-all-chip-host-label/specs/timeline/spec.md#requirement-the-timelines-all-chip-reads-the-hosts-word\n\t\t * @return {Array<object>} The chips.\n\t\t */\n\t\tkindChips() {\n\t\t\tconst chosen = this.kindFilter ? (this.kindFilter.id || '') : null\n\t\t\tconst counts = new Map()\n\t\t\tfor (const entry of this.allEntries) {\n\t\t\t\tconst kind = entry.kind || ''\n\t\t\t\tcounts.set(kind, (counts.get(kind) || 0) + 1)\n\t\t\t}\n\t\t\t// The host's word first, as its own filter chips read (\"Alles\"), then the library's.\n\t\t\tconst hostAll = typeof this.cnTranslate === 'function' ? this.cnTranslate('All') : 'All'\n\t\t\tconst allLabel = typeof hostAll === 'string' && hostAll !== '' && hostAll !== 'All' ? hostAll : t('nextcloud-vue', 'All')\n\t\t\tconst chips = [{ key: 'all', id: null, label: allLabel, count: this.allEntries.length, active: chosen === null }]\n\t\t\tfor (const option of this.kindOptions) {\n\t\t\t\tconst count = counts.get(option.id) || 0\n\t\t\t\tif (count > 0) {\n\t\t\t\t\tchips.push({ key: option.id || 'note', id: option.id, label: option.label, count, active: chosen === option.id })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chips\n\t\t},\n\n\t\tblockOptions() {\n\t\t\treturn this.textBlocks.map((block) => ({ id: block.slug, label: block.title || block.slug }))\n\t\t},\n\n\t\tvisibilityOptions() {\n\t\t\treturn [\n\t\t\t\t{ id: 'internal', label: t('nextcloud-vue', 'Internal only') },\n\t\t\t\t{ id: 'public', label: this.publicLabel },\n\t\t\t]\n\t\t},\n\n\t\tbase() {\n\t\t\treturn this.apiBase.replace(/\\/$/, '')\n\t\t},\n\n\t\ttimelineUrl() {\n\t\t\tif (!this.objectId || !this.register || !this.schema) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn prefixUrl(`${this.base}/objects/${this.register}/${this.schema}/${this.objectId}/timeline`)\n\t\t},\n\n\t\tentryActions() {\n\t\t\treturn Array.isArray(this.content.entryActions) ? this.content.entryActions.filter((action) => action && action.id) : []\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: 'load',\n\t},\n\n\tcreated() {\n\t\tthis.onTimelineChanged = (payload) => {\n\t\t\tif (!payload || payload.objectId !== this.objectId || payload.source === this.uid || this.composerOnly) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.load()\n\t\t}\n\t\tthis.onComposeRequested = (payload) => {\n\t\t\tif (this.composerOnly && payload && payload.objectId === this.objectId) {\n\t\t\t\tthis.focusComposer()\n\t\t\t}\n\t\t}\n\t\tsubscribe(TIMELINE_CHANGED, this.onTimelineChanged)\n\t\tsubscribe(TIMELINE_COMPOSE, this.onComposeRequested)\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(TIMELINE_CHANGED, this.onTimelineChanged)\n\t\tunsubscribe(TIMELINE_COMPOSE, this.onComposeRequested)\n\t},\n\n\tmounted() {\n\t\tthis.loadKinds()\n\t\tif (this.content.textBlocks !== false) {\n\t\t\tthis.loadTextBlocks()\n\t\t}\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\t\tn,\n\n\t\ttranslate(text) {\n\t\t\treturn text ? this.cnTranslate(text) : ''\n\t\t},\n\n\t\t/** Read the declared kinds, so the filter names them as administered. */\n\t\tasync loadKinds() {\n\t\t\ttry {\n\t\t\t\tconst response = await axios.get(prefixUrl(`${this.base}/timeline/kinds`))\n\t\t\t\tthis.kinds = response.data?.results || []\n\t\t\t} catch {\n\t\t\t\tthis.kinds = []\n\t\t\t}\n\t\t},\n\n\t\t/** Read the seeded standard notes. */\n\t\tasync loadTextBlocks() {\n\t\t\ttry {\n\t\t\t\tconst response = await axios.get(prefixUrl(`${this.base}/timeline/text-blocks`))\n\t\t\t\tthis.textBlocks = response.data?.results || []\n\t\t\t} catch {\n\t\t\t\tthis.textBlocks = []\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Note which standard note was picked. The BLOCK is sent, not its body:\n\t\t * OpenRegister fills the object's own values into it on the way in.\n\t\t *\n\t\t * @param {object|null} block The picked option.\n\t\t */\n\t\tpickBlock(block) {\n\t\t\tthis.chosenBlock = block || null\n\t\t\tif (block) {\n\t\t\t\tconst declared = this.textBlocks.find((entry) => entry.slug === block.id)\n\t\t\t\tthis.draft = declared?.title || block.label\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write the drafted note onto this object.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync submit() {\n\t\t\tif (!this.draft.trim() || !this.timelineUrl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst payload = { visibility: this.draftIsPublic ? 'public' : 'internal' }\n\t\t\t\tif (this.chosenBlock?.id) {\n\t\t\t\t\tpayload.textBlock = this.chosenBlock.id\n\t\t\t\t} else {\n\t\t\t\t\tpayload.message = this.draft\n\t\t\t\t}\n\t\t\t\tawait axios.post(this.timelineUrl, payload)\n\t\t\t\tthis.draft = ''\n\t\t\t\tthis.chosenBlock = null\n\t\t\t\tthis.draftIsPublic = false\n\t\t\t\temit(TIMELINE_CHANGED, { objectId: this.objectId, source: this.uid })\n\t\t\t\tawait this.load()\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'That note could not be saved.')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Read the timeline with the visibility filter, if one is set.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.timelineUrl) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst params = {}\n\t\t\t\tif (this.visibilityFilter?.id) {\n\t\t\t\t\tparams.visibility = this.visibilityFilter.id\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(this.timelineUrl, { params })\n\t\t\t\tthis.canManage = response.data?.canManage === true\n\t\t\t\tconst entries = response.data?.results || []\n\t\t\t\tif (this.readsToggleLabel && this.readsAllowed === null) {\n\t\t\t\t\tawait this.readEntries()\n\t\t\t\t}\n\t\t\t\tthis.allEntries = this.readsToggleLabel && this.readsAllowed && this.includeReads ? this.withReads(entries, await this.readEntries()) : entries\n\t\t\t\tthis.entries = this.applyKindFilter(this.allEntries)\n\t\t\t} catch {\n\t\t\t\tthis.allEntries = []\n\t\t\t\tthis.entries = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'The timeline could not be read. Try again, or ask an administrator to check OpenRegister.')\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 * Who read the record, from its audit trail (`action=read`), as timeline\n\t\t * rows. OpenRegister gives read entries only to the groups named for\n\t\t * them and says so in `readEventsVisible`, which decides whether the\n\t\t * switch is offered at all; a read it refuses or that fails leaves the\n\t\t * reads out and offers no switch.\n\t\t *\n\t\t * @spec openspec/changes/timeline-reads-toggle/specs/timeline/spec.md#requirement-the-history-can-show-who-read-the-record\n\t\t * @return {Promise<Array<object>>} The read rows.\n\t\t */\n\t\tasync readEntries() {\n\t\t\ttry {\n\t\t\t\tconst url = prefixUrl(`${this.base}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails`)\n\t\t\t\tconst response = await axios.get(url, { params: { action: 'read', limit: 50 } })\n\t\t\t\tthis.readsAllowed = response.data?.readEventsVisible === true\n\t\t\t\tif (!this.readsAllowed) {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\tconst list = response.data?.results || []\n\t\t\t\treturn list\n\t\t\t\t\t.filter((entry) => String(entry.action || '').toLowerCase() === 'read')\n\t\t\t\t\t.map((entry) => ({\n\t\t\t\t\t\tid: `read-${entry.id}`,\n\t\t\t\t\t\tkind: READ_KIND,\n\t\t\t\t\t\tisRead: true,\n\t\t\t\t\t\tauthor: entry.actorDisplayName || entry.userName || entry.user || '',\n\t\t\t\t\t\tcreated: entry.created || entry.creationDateTime || '',\n\t\t\t\t\t\tmessage: t('nextcloud-vue', 'Opened the record'),\n\t\t\t\t\t}))\n\t\t\t} catch {\n\t\t\t\tthis.readsAllowed = false\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The timeline's entries with the reads among them, newest first; the\n\t\t * pinned entries stay on top.\n\t\t *\n\t\t * @param {Array<object>} entries The timeline's entries.\n\t\t * @param {Array<object>} reads The read rows.\n\t\t * @return {Array<object>} The merged list.\n\t\t */\n\t\twithReads(entries, reads) {\n\t\t\tconst time = (entry) => new Date(entry.created || 0).getTime() || 0\n\t\t\tconst pinned = entries.filter((entry) => entry.pinned)\n\t\t\tconst rest = [...entries.filter((entry) => !entry.pinned), ...reads].sort((a, b) => time(b) - time(a))\n\t\t\treturn [...pinned, ...rest]\n\t\t},\n\n\t\t/**\n\t\t * Show one kind, or every kind for `null`, without reading again.\n\t\t *\n\t\t * @param {string|null} id The kind slug, '' for a note, null for all.\n\t\t */\n\t\tchooseKind(id) {\n\t\t\tthis.kindFilter = id === null ? null : { id }\n\t\t\tthis.entries = this.applyKindFilter(this.allEntries)\n\t\t},\n\n\t\t/**\n\t\t * The kind filter is applied in the reading: the list endpoint does not\n\t\t * read a kind parameter, so sending one would be dropped in silence.\n\t\t *\n\t\t * @param {Array<object>} results The entries as read.\n\t\t * @return {Array<object>} The entries to show.\n\t\t */\n\t\tapplyKindFilter(results) {\n\t\t\tif (!this.kindFilter) {\n\t\t\t\treturn results\n\t\t\t}\n\t\t\tconst wanted = this.kindFilter.id || ''\n\t\t\treturn results.filter((entry) => (entry.kind || '') === wanted)\n\t\t},\n\n\t\t/**\n\t\t * The shown entries as CSV: when, kind, who, visibility and what.\n\t\t *\n\t\t * @return {string} The CSV text.\n\t\t */\n\t\tentriesCsv() {\n\t\t\tconst cell = (value) => `\"${String(value ?? '').replace(/\"/g, '\"\"')}\"`\n\t\t\tconst rows = [[t('nextcloud-vue', 'When'), t('nextcloud-vue', 'Kind'), t('nextcloud-vue', 'Who'), t('nextcloud-vue', 'Visibility'), t('nextcloud-vue', 'Message')]]\n\t\t\tfor (const entry of this.entries) {\n\t\t\t\trows.push([entry.created, this.kindLabel(entry), entry.author, entry.visibility, entry.message])\n\t\t\t}\n\t\t\treturn rows.map((row) => row.map(cell).join(',')).join('\\r\\n')\n\t\t},\n\n\t\t/** Download the shown entries as a CSV file. */\n\t\texportEntries() {\n\t\t\tconst blob = new Blob([this.entriesCsv()], { type: 'text/csv;charset=utf-8' })\n\t\t\tconst url = URL.createObjectURL(blob)\n\t\t\tconst link = document.createElement('a')\n\t\t\tlink.href = url\n\t\t\tlink.download = `${this.content.exportName || 'history'}-${this.objectId || 'record'}.csv`\n\t\t\tdocument.body.appendChild(link)\n\t\t\tlink.click()\n\t\t\tlink.remove()\n\t\t\tURL.revokeObjectURL(url)\n\t\t},\n\n\t\t/**\n\t\t * Open the object sidebar on its audit trail tab.\n\t\t *\n\t\t * @spec openspec/changes/timeline-audit-trail-link/specs/timeline/spec.md#requirement-the-manage-timeline-can-point-at-the-audit-trail-in-the-sidebar\n\t\t */\n\t\topenAuditTrail() {\n\t\t\tthis.objectSidebarState.open = true\n\t\t\tthis.objectSidebarState.requestedTab = this.content.auditTrailTab || 'auditTrail'\n\t\t},\n\n\t\t/** Bring the composer into view and focus its text; a separate form card is asked to do so. */\n\t\tfocusComposer() {\n\t\t\tif (this.composerSeparate) {\n\t\t\t\temit(TIMELINE_COMPOSE, { objectId: this.objectId })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst form = this.$refs.composer\n\t\t\tif (!form) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tform.scrollIntoView?.({ behavior: 'smooth', block: 'center' })\n\t\t\tform.querySelector('textarea')?.focus()\n\t\t},\n\n\t\t/**\n\t\t * The label of one entry's kind, falling back to \"Note\".\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The label.\n\t\t */\n\t\tkindLabel(entry) {\n\t\t\tif (entry.kind === READ_KIND) {\n\t\t\t\treturn t('nextcloud-vue', 'Viewed')\n\t\t\t}\n\t\t\tif (!entry.kind) {\n\t\t\t\treturn t('nextcloud-vue', 'Note')\n\t\t\t}\n\t\t\tconst declared = this.kinds.find((kind) => kind.slug === entry.kind)\n\t\t\treturn declared?.title || entry.kind\n\t\t},\n\n\t\t/**\n\t\t * One moment in the reader's locale.\n\t\t *\n\t\t * @param {string} value An ISO 8601 stamp.\n\t\t * @return {string} The moment, or the raw value.\n\t\t */\n\t\tmoment(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst parsed = new Date(value)\n\t\t\treturn isNaN(parsed.getTime()) ? value : parsed.toLocaleString()\n\t\t},\n\n\t\tasync togglePin(entry) {\n\t\t\tawait this.patch(entry, { pinned: !entry.pinned })\n\t\t},\n\n\t\tasync closeFollowUp(entry) {\n\t\t\tawait this.patch(entry, { followUp: 'done' })\n\t\t},\n\n\t\t/**\n\t\t * Write one change onto one entry and read the timeline back (pinning\n\t\t * changes the order, so patching in place would look like nothing).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {object} changes The fields to write.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync patch(entry, changes) {\n\t\t\ttry {\n\t\t\t\tawait axios.patch(`${this.timelineUrl}/${entry.id}`, changes)\n\t\t\t\tawait this.load()\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'That change could not be saved.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The configured entry actions that apply to one entry (`kinds` limits\n\t\t * an action to entries of those kinds).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {Array<object>} The actions.\n\t\t */\n\t\tactionsFor(entry) {\n\t\t\treturn this.entryActions.filter((action) => !Array.isArray(action.kinds) || action.kinds.includes(entry.kind || ''))\n\t\t},\n\n\t\t/**\n\t\t * Resolve `@objectId` and `@row.<path>` tokens in an action's props.\n\t\t *\n\t\t * @param {object} props The props as declared.\n\t\t * @param {object} entry The entry.\n\t\t * @return {object} The resolved props.\n\t\t */\n\t\tresolveProps(props, entry) {\n\t\t\tconst out = {}\n\t\t\tfor (const [key, value] of Object.entries(props || {})) {\n\t\t\t\tif (value === '@objectId') {\n\t\t\t\t\tout[key] = this.objectId\n\t\t\t\t} else if (typeof value === 'string' && value.startsWith('@row.')) {\n\t\t\t\t\tconst found = readPath(entry, value.slice('@row.'.length))\n\t\t\t\t\tout[key] = found === undefined || found === null ? '' : found\n\t\t\t\t} else {\n\t\t\t\t\tout[key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Open an entry action's modal from the app's registry. On its\n\t\t * `doneEvent` (default `done`) the entry's open follow-up is closed when\n\t\t * the action says so; otherwise the timeline is read again.\n\t\t *\n\t\t * @param {object} action The action.\n\t\t * @param {object} entry The entry.\n\t\t */\n\t\topenAction(action, entry) {\n\t\t\tconst registered = (this.cnRegistry || {})[action.modal]\n\t\t\tconst component = registered && (registered.component || registered)\n\t\t\tif (!component) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnTimelineWidget] entry action \"${action.id}\" names modal \"${action.modal}\", which the registry does not hold.`)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst close = () => {\n\t\t\t\tthis.activeModal = null\n\t\t\t}\n\t\t\tconst done = async () => {\n\t\t\t\tthis.activeModal = null\n\t\t\t\tif (action.closesFollowUp === true && entry.followUp === 'open') {\n\t\t\t\t\tawait this.closeFollowUp(entry)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tawait this.load()\n\t\t\t}\n\t\t\tthis.activeModal = {\n\t\t\t\tcomponent,\n\t\t\t\tprops: this.resolveProps(action.props, entry),\n\t\t\t\tlisteners: { [action.doneEvent || 'done']: done, close },\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-timeline-manage__header {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-block-end: 16px;\n}\n\n.cn-timeline-manage__heading {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-timeline-manage__header-actions {\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-timeline-manage__title {\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\tline-height: 1.2;\n}\n\n.cn-timeline-manage__subtitle {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n.cn-timeline-manage__chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tmargin-block-end: 12px;\n}\n\n.cn-timeline-manage__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tbox-sizing: border-box;\n\theight: 36px;\n\tpadding: 0 12px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: 18px;\n\tbackground: var(--color-main-background);\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcursor: pointer;\n}\n\n/* The chosen kind is a filled chip with no outline. */\n.cn-timeline-manage__chip--active {\n\tborder: 0;\n\tbackground: var(--color-main-text);\n\tcolor: var(--color-main-background);\n}\n\n.cn-timeline-manage__chip-count {\n\tpadding: 1px 7px;\n\tborder-radius: 10px;\n\tbackground: var(--color-border);\n\tcolor: var(--color-main-text);\n\tfont-size: 12px;\n}\n\n/* On the filled chip the count is a lighter ink bubble with light text. */\n.cn-timeline-manage__chip--active .cn-timeline-manage__chip-count {\n\tbackground: color-mix(in srgb, var(--color-main-background) 18%, var(--color-main-text));\n\tcolor: var(--color-main-background);\n}\n\n.cn-timeline-manage__footer {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tmargin: 12px 0 0;\n}\n\n.cn-timeline-manage__count {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-manage__composer {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmargin-block-start: 24px;\n\tpadding-block-start: 16px;\n\tborder-block-start: 1px solid var(--color-border);\n}\n\n.cn-timeline-manage--composer-only .cn-timeline-manage__composer {\n\tmargin-block-start: 0;\n\tpadding-block-start: 0;\n\tborder-block-start: none;\n}\n\n/* The checkbox at the start, the button at the end, as one row. */\n.cn-timeline-manage__composer-foot {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-timeline-manage__composer-title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-timeline-manage__composer-hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-timeline-manage__draft-label {\n\tfont-weight: bold;\n}\n\n.cn-timeline-manage__draft {\n\twidth: 100%;\n\tmin-height: 70px;\n}\n\n.cn-timeline-manage__controls {\n\tdisplay: flex;\n\tgap: 12px;\n\tflex-wrap: wrap;\n\tmargin-block-end: 12px;\n}\n\n.cn-timeline-manage__filter {\n\tmin-width: 200px;\n}\n\n.cn-timeline-manage__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-timeline-manage__entry {\n\tborder-block-end: 1px solid var(--color-border);\n\tpadding-block: 12px;\n}\n\n.cn-timeline-manage__entry--pinned {\n\tborder-inline-start: 3px solid var(--color-primary-element);\n\tpadding-inline-start: 8px;\n}\n\n.cn-timeline-manage__head {\n\tdisplay: flex;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\talign-items: baseline;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-timeline-manage__kind {\n\tfont-weight: bold;\n\tcolor: var(--color-main-text);\n}\n\n.cn-timeline-manage__badge {\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 0 6px;\n}\n\n.cn-timeline-manage__badge--open {\n\tborder-color: var(--color-warning);\n\tcolor: var(--color-warning-text);\n}\n\n.cn-timeline-manage__message {\n\tmargin-block: 4px 0;\n\twhite-space: pre-wrap;\n}\n\n.cn-timeline-manage__siblings {\n\tmargin-block: 4px 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-timeline-manage__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-block-start: 4px;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_Fragment","_createElementVNode","_toDisplayString","_createBlock","_createVNode","_createTextVNode","_openBlock","_renderList"],"mappings":"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"}
@@ -9,7 +9,8 @@ import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry
9
9
  * dashboardWidgetRegistry. Like `audit-trail` it is a DETAIL-PAGE surface:
10
10
  * the object comes from the page's object context. It has no config form
11
11
  * yet, so it is placed from the manifest and never offered in the
12
- * Add-widget picker.
12
+ * Add-widget picker. It draws its own title row and asks for the card, so a
13
+ * tab stack or the side column stands it in an untitled card.
13
14
  *
14
15
  * @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order
15
16
  */
@@ -30,5 +31,6 @@ registerDashboardWidget('timeline', {
30
31
  icon: 'TimelineClockOutline',
31
32
  surfaces: ['detail-page'],
32
33
  ownsTitle: true,
34
+ chrome: 'card',
33
35
  });
34
36
  //# sourceMappingURL=dashboardRegistration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dashboardRegistration.js","sources":["../../../../src/components/CnTimelineWidget/dashboardRegistration.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Registers the `timeline` widget type (CnTimelineWidget) in the shared\n * dashboardWidgetRegistry. Like `audit-trail` it is a DETAIL-PAGE surface:\n * the object comes from the page's object context. It has no config form\n * yet, so it is placed from the manifest and never offered in the\n * Add-widget picker.\n *\n * @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order\n */\n\nimport CnTimelineWidget from './CnTimelineWidget.vue'\nimport { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\nregisterDashboardWidget('timeline', {\n\trenderer: CnTimelineWidget,\n\tform: null,\n\tdefaultContent: {\n\t\ttitle: '',\n\t\tfields: [],\n\t\trelated: [],\n\t\tauditTrail: false,\n\t\ttimeline: false,\n\t\torder: 'asc',\n\t},\n\tdisplayName: 'Timeline',\n\ticon: 'TimelineClockOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n})\n"],"names":["CnTimelineWidget"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA,uBAAuB,CAAC,UAAU,EAAE;AACpC,CAAC,QAAQ,EAAEA,UAAgB;AAC3B,CAAC,IAAI,EAAE,IAAI;AACX,CAAC,cAAc,EAAE;AACjB,EAAE,KAAK,EAAE,EAAE;AACX,EAAE,MAAM,EAAE,EAAE;AACZ,EAAE,OAAO,EAAE,EAAE;AACb,EAAE,UAAU,EAAE,KAAK;AACnB,EAAE,QAAQ,EAAE,KAAK;AACjB,EAAE,KAAK,EAAE,KAAK;AACd,EAAE;AACF,CAAC,WAAW,EAAE,UAAU;AACxB,CAAC,IAAI,EAAE,sBAAsB;AAC7B,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC;AAC1B,CAAC,SAAS,EAAE,IAAI;AAChB,CAAC"}
1
+ {"version":3,"file":"dashboardRegistration.js","sources":["../../../../src/components/CnTimelineWidget/dashboardRegistration.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Registers the `timeline` widget type (CnTimelineWidget) in the shared\n * dashboardWidgetRegistry. Like `audit-trail` it is a DETAIL-PAGE surface:\n * the object comes from the page's object context. It has no config form\n * yet, so it is placed from the manifest and never offered in the\n * Add-widget picker. It draws its own title row and asks for the card, so a\n * tab stack or the side column stands it in an untitled card.\n *\n * @spec openspec/changes/timeline-widget/specs/timeline-widget/spec.md#requirement-a-timeline-widget-shows-an-objects-dated-events-in-order\n */\n\nimport CnTimelineWidget from './CnTimelineWidget.vue'\nimport { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\nregisterDashboardWidget('timeline', {\n\trenderer: CnTimelineWidget,\n\tform: null,\n\tdefaultContent: {\n\t\ttitle: '',\n\t\tfields: [],\n\t\trelated: [],\n\t\tauditTrail: false,\n\t\ttimeline: false,\n\t\torder: 'asc',\n\t},\n\tdisplayName: 'Timeline',\n\ticon: 'TimelineClockOutline',\n\tsurfaces: ['detail-page'],\n\townsTitle: true,\n\tchrome: 'card',\n})\n"],"names":["CnTimelineWidget"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA,uBAAuB,CAAC,UAAU,EAAE;AACpC,CAAC,QAAQ,EAAEA,UAAgB;AAC3B,CAAC,IAAI,EAAE,IAAI;AACX,CAAC,cAAc,EAAE;AACjB,EAAE,KAAK,EAAE,EAAE;AACX,EAAE,MAAM,EAAE,EAAE;AACZ,EAAE,OAAO,EAAE,EAAE;AACb,EAAE,UAAU,EAAE,KAAK;AACnB,EAAE,QAAQ,EAAE,KAAK;AACjB,EAAE,KAAK,EAAE,KAAK;AACd,EAAE;AACF,CAAC,WAAW,EAAE,UAAU;AACxB,CAAC,IAAI,EAAE,sBAAsB;AAC7B,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC;AAC1B,CAAC,SAAS,EAAE,IAAI;AAChB,CAAC,MAAM,EAAE,MAAM;AACf,CAAC"}
@@ -2,7 +2,7 @@ import script from './CnWorkingCalendarWidget.vue2.js';
2
2
  import { render } from './CnWorkingCalendarWidget.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-5c34a46e";
5
+ script.__scopeId = "data-v-23316897";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -1 +1 @@
1
- {"version":3,"file":"CnWorkingCalendarWidget.vue2.js","sources":["../../../../src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-working-calendar\" data-testid=\"cn-working-calendar\">\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-working-calendar__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-working-calendar-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && !calendar\" class=\"cn-working-calendar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<template v-else-if=\"calendar\">\n\t\t\t<div class=\"cn-working-calendar__head\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"previousMonthLabel\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-previous\"\n\t\t\t\t\t@click=\"shiftMonth(-1)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<strong :id=\"`${uid}-title`\" class=\"cn-working-calendar__title\" data-testid=\"cn-working-calendar-title\">{{ monthTitle }}</strong>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"nextMonthLabel\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-next\"\n\t\t\t\t\t@click=\"shiftMonth(1)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editable && content.addButton !== false\"\n\t\t\t\t\tclass=\"cn-working-calendar__add\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-add\"\n\t\t\t\t\t@click=\"openAdd()\">\n\t\t\t\t\t{{ addLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"previewNote\" class=\"cn-working-calendar__note\" data-testid=\"cn-working-calendar-note\">\n\t\t\t\t{{ previewNote }}\n\t\t\t</p>\n\t\t\t<span class=\"cn-working-calendar__live\" aria-live=\"polite\">{{ liveText }}</span>\n\n\t\t\t<div\n\t\t\t\trole=\"grid\"\n\t\t\t\tclass=\"cn-working-calendar__grid\"\n\t\t\t\t:aria-labelledby=\"`${uid}-title`\"\n\t\t\t\tdata-testid=\"cn-working-calendar-grid\"\n\t\t\t\t@keydown=\"onKeydown\">\n\t\t\t\t<div role=\"row\" class=\"cn-working-calendar__row\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-for=\"name in weekdayNames\"\n\t\t\t\t\t\t:key=\"name.long\"\n\t\t\t\t\t\trole=\"columnheader\"\n\t\t\t\t\t\tclass=\"cn-working-calendar__weekday\"\n\t\t\t\t\t\t:aria-label=\"name.long\">{{ name.short }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(week, w) in weeks\"\n\t\t\t\t\t:key=\"w\"\n\t\t\t\t\trole=\"row\"\n\t\t\t\t\tclass=\"cn-working-calendar__row\">\n\t\t\t\t\t<template v-for=\"(slot, d) in week\">\n\t\t\t\t\t\t<span v-if=\"!slot\"\n\t\t\t\t\t\t\t:key=\"`empty-${w}-${d}`\"\n\t\t\t\t\t\t\trole=\"gridcell\"\n\t\t\t\t\t\t\tclass=\"cn-working-calendar__blank\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t:key=\"slot.date\"\n\t\t\t\t\t\t\trole=\"gridcell\"\n\t\t\t\t\t\t\tclass=\"cn-working-calendar__day\"\n\t\t\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--off': !slot.working && !slot.closure,\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--closed': slot.closure,\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--today': slot.today,\n\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t:tabindex=\"slot.date === focusDate ? 0 : -1\"\n\t\t\t\t\t\t\t:aria-label=\"dayLabel(slot)\"\n\t\t\t\t\t\t\t:aria-current=\"slot.today ? 'date' : null\"\n\t\t\t\t\t\t\t:title=\"slot.closure ? closureName(slot.closure) : null\"\n\t\t\t\t\t\t\t:data-date=\"slot.date\"\n\t\t\t\t\t\t\tdata-testid=\"cn-working-calendar-day\"\n\t\t\t\t\t\t\t@click=\"onDayClick(slot)\"\n\t\t\t\t\t\t\t@focus=\"focusDate = slot.date\">{{ slot.day }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<p v-if=\"content.footnote\" class=\"cn-working-calendar__footnote\" data-testid=\"cn-working-calendar-footnote\">\n\t\t\t\t{{ cnTranslate(content.footnote) }}\n\t\t\t</p>\n\n\t\t\t<form\n\t\t\t\tv-if=\"adding\"\n\t\t\t\tclass=\"cn-working-calendar__form\"\n\t\t\t\tdata-testid=\"cn-working-calendar-form\"\n\t\t\t\t@submit.prevent=\"saveDay\">\n\t\t\t\t<label class=\"cn-working-calendar__field\">\n\t\t\t\t\t{{ dateLabel }}\n\t\t\t\t\t<input\n\t\t\t\t\t\tref=\"dateInput\"\n\t\t\t\t\t\tv-model=\"newDate\"\n\t\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t\trequired\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-date\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-working-calendar__field\">\n\t\t\t\t\t{{ nameLabel }}\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"newName\"\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-name\">\n\t\t\t\t</label>\n\t\t\t\t<div class=\"cn-working-calendar__form-actions\">\n\t\t\t\t\t<NcButton variant=\"tertiary\" data-testid=\"cn-working-calendar-cancel\" @click=\"adding = false\">\n\t\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t:disabled=\"saving || !newDate\"\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-save\">\n\t\t\t\t\t\t{{ saveLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"saveError\"\n\t\t\t\t\tclass=\"cn-working-calendar__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-save-error\">\n\t\t\t\t\t{{ saveError }}\n\t\t\t\t</p>\n\t\t\t</form>\n\t\t</template>\n\t\t<p v-else class=\"cn-working-calendar__note\" data-testid=\"cn-working-calendar-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\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, NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport { fetchReferenceObjects } from '../../utils/boardReferenceColumns.js'\nimport { hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { userLocale } from '../../utils/userLocale.js'\nimport { buildMonth, isoDate, normaliseExceptions } from '../../utils/workingCalendarMonth.js'\n\nconst OBJECT_API = '/apps/openregister/api/objects'\nconst PREVIEW_URL = '/apps/openregister/api/flow-timers/calendars/preview'\n\nlet instanceCounter = 0\n\n/**\n * The id of an object, wherever OpenRegister carries it.\n *\n * @param {object|null} object The object.\n * @return {string} The id, or ''.\n */\nfunction idOf(object) {\n\treturn String(object?.id ?? object?.['@self']?.id ?? object?.uuid ?? '')\n}\n\n/**\n * Today as `YYYY-MM-DD`, in local time.\n *\n * @return {string} The date.\n */\nfunction todayIso() {\n\tconst now = new Date()\n\treturn isoDate(now.getFullYear(), now.getMonth(), now.getDate())\n}\n\n/**\n * CnWorkingCalendarWidget — the `working-calendar` widget: one month of a\n * working calendar kept as an OpenRegister object (board\n * DqInstellingenAlgemeen, \"Werkdagen en feestdagen\").\n *\n * The grid runs Monday to Sunday. Weekdays the calendar does not work are\n * grey; the dates it is closed on are red, with their name on hover and in\n * the day's label. The closures are not computed here: the widget asks\n * OpenRegister's calendar preview (`/api/flow-timers/calendars/preview`) for\n * the year shown, which expands the calendar's rules and its exceptions.\n *\n * \"Add day\" appends `{ date, name }` to the object's `exceptions` with one\n * update through the object API, under the reader's own rights; it shows for\n * administrators, and the server decides. The preview is administrators-only\n * too; for anyone else the widget shows the stored exceptions and says the\n * holidays could not be read.\n *\n * ```json\n * {\n * \"type\": \"working-calendar\",\n * \"content\": {\n * \"calendar\": { \"register\": \"flow-timers\", \"schema\": \"working-calendar\", \"filter\": { \"slug\": \"default\" } }\n * }\n * }\n * ```\n *\n * Keyboard: the days form a grid with one tab stop; the arrow keys move a\n * day or a week, Home and End to the start and end of the week, Page Up and\n * Page Down a month. Enter on a day opens Add day for that date.\n */\nexport default {\n\tname: 'CnWorkingCalendarWidget',\n\n\tcomponents: { ChevronLeft, ChevronRight, NcButton, NcLoadingIcon },\n\n\tinject: {\n\t\t/** The page's workspace values, for `@workspace.<key>` tokens in the filter. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host's translate, for the manifest's texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ calendar: { register, schema, id? | filter? },\n\t\t * previewUrl?, editable?, addButton?, emptyText?, footnote? }`. `addButton: false` leaves the Add day button\n\t\t * out, for a host that draws it in its own title row and calls `openAdd()`. `footnote` is a\n\t\t * line under the month grid, through the host's translate: what the colours mean\n\t\t * (DqInstellingenAlgemeen \"Rood: feestdag of sluitingsdag.\").\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` in the filter. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` in the filter. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tinstanceCounter += 1\n\t\tconst now = new Date()\n\t\treturn {\n\t\t\tuid: `cn-working-calendar-${instanceCounter}`,\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tcalendar: null,\n\t\t\tpreviews: {},\n\t\t\tpreviewFailed: false,\n\t\t\tyear: now.getFullYear(),\n\t\t\tmonth: now.getMonth(),\n\t\t\tfocusDate: todayIso(),\n\t\t\tadding: false,\n\t\t\tnewDate: '',\n\t\t\tnewName: '',\n\t\t\tsaving: false,\n\t\t\tsaveError: '',\n\t\t\tliveText: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{register: string, schema: string, id: string, filter: (object|null)}|null} The calendar source, tokens resolved, or null while unresolved. */\n\t\tsource() {\n\t\t\tconst c = this.content?.calendar\n\t\t\tif (!c || !c.register || !c.schema) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst unwrap = (v) => (v && typeof v === 'object' && 'value' in v ? v.value : v)\n\t\t\tconst ctx = { objectId: this.objectId || null, object: this.objectData, workspace: unwrap(this.cnWorkspaceContext) || {} }\n\t\t\tconst filter = c.filter && typeof c.filter === 'object' ? resolveFilterTokens(c.filter, ctx) : null\n\t\t\tif (filter && hasUnresolvedTokens(filter)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { register: String(c.register), schema: String(c.schema), id: c.id ? String(c.id) : '', filter }\n\t\t},\n\n\t\t/** @return {string} A key of the source, to read it again when it changes. */\n\t\tsourceKey() {\n\t\t\treturn this.source ? JSON.stringify(this.source) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether Add day shows: administrators, unless the manifest turns it off. */\n\t\teditable() {\n\t\t\treturn this.content?.editable !== false && Boolean(getCurrentUser()?.isAdmin)\n\t\t},\n\n\t\t/** @return {number[]} The ISO weekdays that work. */\n\t\tworkingWeekdays() {\n\t\t\tconst days = this.calendar?.workingWeekdays\n\t\t\treturn Array.isArray(days) ? days.map((d) => Number(d)) : [1, 2, 3, 4, 5]\n\t\t},\n\n\t\t/** @return {Array<{date: string, name: string}>} The stored exceptions. */\n\t\texceptions() {\n\t\t\treturn normaliseExceptions(this.calendar?.exceptions)\n\t\t},\n\n\t\t/**\n\t\t * The closed dates of the year shown: the preview's, each marked as an\n\t\t * exception when the calendar lists it, else as a holiday; without a\n\t\t * preview, the stored exceptions.\n\t\t *\n\t\t * @return {Map<string, {name: string, kind: string}>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month\n\t\t */\n\t\tclosures() {\n\t\t\tconst own = new Set(this.exceptions.map((e) => e.date))\n\t\t\tconst map = new Map()\n\t\t\tconst preview = this.previews[this.year]\n\t\t\tfor (const entry of Array.isArray(preview) ? preview : []) {\n\t\t\t\tif (entry && typeof entry.date === 'string') {\n\t\t\t\t\tmap.set(entry.date, { name: String(entry.name || ''), kind: own.has(entry.date) ? 'exception' : 'holiday' })\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const e of this.exceptions) {\n\t\t\t\tif (!map.has(e.date)) {\n\t\t\t\t\tmap.set(e.date, { name: e.name, kind: 'exception' })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn map\n\t\t},\n\n\t\t/** @return {Array<Array<object|null>>} The weeks of the month shown. */\n\t\tweeks() {\n\t\t\treturn buildMonth(this.year, this.month, { workingWeekdays: this.workingWeekdays, closures: this.closures, today: todayIso() })\n\t\t},\n\n\t\t/** @return {Array<{short: string, long: string}>} Monday to Sunday in the user's language. */\n\t\tweekdayNames() {\n\t\t\tconst short = new Intl.DateTimeFormat(userLocale(), { weekday: 'short' })\n\t\t\tconst long = new Intl.DateTimeFormat(userLocale(), { weekday: 'long' })\n\t\t\treturn Array.from({ length: 7 }, (_v, i) => {\n\t\t\t\tconst day = new Date(Date.UTC(2024, 0, 1 + i, 12))\n\t\t\t\treturn { short: short.format(day).replace(/\\.$/, ''), long: long.format(day) }\n\t\t\t})\n\t\t},\n\n\t\t/** @return {string} \"December 2026\" in the user's language. */\n\t\tmonthTitle() {\n\t\t\tconst text = new Intl.DateTimeFormat(userLocale(), { month: 'long', year: 'numeric' }).format(new Date(this.year, this.month, 15))\n\t\t\treturn text.charAt(0).toUpperCase() + text.slice(1)\n\t\t},\n\n\t\t/** @return {string} Why holidays are missing, or ''. */\n\t\tpreviewNote() {\n\t\t\treturn this.previewFailed ? t('nextcloud-vue', 'Holidays could not be read. Only the days added by hand are shown.') : ''\n\t\t},\n\n\t\tloadingLabel: () => t('nextcloud-vue', 'Loading …'),\n\t\tpreviousMonthLabel: () => t('nextcloud-vue', 'Previous month'),\n\t\tnextMonthLabel: () => t('nextcloud-vue', 'Next month'),\n\t\taddLabel: () => t('nextcloud-vue', 'Add day'),\n\t\tdateLabel: () => t('nextcloud-vue', 'Date'),\n\t\tnameLabel: () => t('nextcloud-vue', 'Name'),\n\t\tcancelLabel: () => t('nextcloud-vue', 'Cancel'),\n\t\tsaveLabel: () => t('nextcloud-vue', 'Save'),\n\n\t\t/** @return {string} The empty-state text. */\n\t\temptyLabel() {\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'No working calendar found.')\n\t\t},\n\t},\n\n\twatch: {\n\t\tsourceKey: {\n\t\t\thandler() {\n\t\t\t\tthis.load()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\n\t\tyear() {\n\t\t\tthis.loadPreview()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The host's translate, or the text unchanged.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/** @return {string} The calendar object's API URL. */\n\t\tobjectUrl() {\n\t\t\tconst s = this.source\n\t\t\tconst id = idOf(this.calendar) || s.id\n\t\t\treturn generateUrl(`${OBJECT_API}/${encodeURIComponent(s.register)}/${encodeURIComponent(s.schema)}/${encodeURIComponent(id)}`)\n\t\t},\n\n\t\t/**\n\t\t * Read the calendar object, then the closures of the year shown.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst key = this.sourceKey\n\t\t\tif (key === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst s = this.source\n\t\t\t\tlet calendar = null\n\t\t\t\tif (s.id) {\n\t\t\t\t\tconst response = await axios.get(generateUrl(`${OBJECT_API}/${encodeURIComponent(s.register)}/${encodeURIComponent(s.schema)}/${encodeURIComponent(s.id)}`))\n\t\t\t\t\tcalendar = response?.data || null\n\t\t\t\t} else {\n\t\t\t\t\tconst found = await fetchReferenceObjects({ register: s.register, schema: s.schema, filter: s.filter || {}, limit: 1 })\n\t\t\t\t\tcalendar = found[0] || null\n\t\t\t\t}\n\t\t\t\tif (key !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.calendar = calendar\n\t\t\t\tthis.previews = {}\n\t\t\t\tif (calendar) {\n\t\t\t\t\tawait this.loadPreview()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'The working calendar could not be loaded.')\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 the calendar preview for the closed dates of the year shown,\n\t\t * once per year.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month\n\t\t */\n\t\tasync loadPreview() {\n\t\t\tconst year = this.year\n\t\t\tif (!this.calendar || this.previews[year]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(this.content?.previewUrl || PREVIEW_URL)\n\t\t\t\tconst response = await axios.post(url, { calendar: this.calendar, year })\n\t\t\t\tconst dates = Array.isArray(response?.data?.dates) ? response.data.dates : []\n\t\t\t\tthis.previews = { ...this.previews, [year]: dates }\n\t\t\t\tthis.previewFailed = false\n\t\t\t} catch {\n\t\t\t\tthis.previewFailed = true\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Show another month, keeping focus on the same day where it exists.\n\t\t *\n\t\t * @param {number} delta Months to move.\n\t\t * @return {void}\n\t\t */\n\t\tshiftMonth(delta) {\n\t\t\tconst date = new Date(this.year, this.month + delta, 1)\n\t\t\tthis.year = date.getFullYear()\n\t\t\tthis.month = date.getMonth()\n\t\t\tconst day = Math.min(Number(this.focusDate.slice(8, 10)) || 1, new Date(this.year, this.month + 1, 0).getDate())\n\t\t\tthis.focusDate = isoDate(this.year, this.month, day)\n\t\t\tthis.liveText = this.monthTitle\n\t\t},\n\n\t\t/**\n\t\t * A day's accessible label: the full date, whether it works, and the\n\t\t * closure's name.\n\t\t *\n\t\t * @param {object} slot The day.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdayLabel(slot) {\n\t\t\tconst [y, m, d] = slot.date.split('-').map(Number)\n\t\t\tconst date = new Intl.DateTimeFormat(userLocale(), { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(y, m - 1, d))\n\t\t\tif (slot.closure) {\n\t\t\t\tconst name = this.closureName(slot.closure)\n\t\t\t\treturn name\n\t\t\t\t\t? t('nextcloud-vue', '{date}, closed: {name}', { date, name })\n\t\t\t\t\t: t('nextcloud-vue', '{date}, closed', { date })\n\t\t\t}\n\t\t\treturn slot.working ? t('nextcloud-vue', '{date}, working day', { date }) : t('nextcloud-vue', '{date}, not a working day', { date })\n\t\t},\n\n\t\t/**\n\t\t * A closure's name, with a fallback for an unnamed added day.\n\t\t *\n\t\t * @param {{name: string, kind: string}} closure The closure.\n\t\t * @return {string} The name.\n\t\t */\n\t\tclosureName(closure) {\n\t\t\tif (closure.name) {\n\t\t\t\treturn this.translate(closure.name)\n\t\t\t}\n\t\t\treturn closure.kind === 'exception' ? t('nextcloud-vue', 'Day added by hand') : ''\n\t\t},\n\n\t\t/**\n\t\t * Open Add day, with a date filled in.\n\t\t *\n\t\t * @param {string} [date] The date.\n\t\t * @return {void}\n\t\t */\n\t\topenAdd(date) {\n\t\t\tthis.newDate = date || this.focusDate || ''\n\t\t\tthis.newName = ''\n\t\t\tthis.saveError = ''\n\t\t\tthis.adding = true\n\t\t\tthis.$nextTick(() => this.$refs.dateInput?.focus?.())\n\t\t},\n\n\t\t/**\n\t\t * A click on a day: open Add day for it when the reader may edit.\n\t\t *\n\t\t * @param {object} slot The day.\n\t\t * @return {void}\n\t\t */\n\t\tonDayClick(slot) {\n\t\t\tthis.focusDate = slot.date\n\t\t\tif (this.editable) {\n\t\t\t\tthis.openAdd(slot.date)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Append the new day to the calendar's exceptions with one update,\n\t\t * under the reader's rights, then read the closures again.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-day-can-be-added-to-the-calendar\n\t\t */\n\t\tasync saveDay() {\n\t\t\tif (!/^\\d{4}-\\d{2}-\\d{2}$/.test(this.newDate) || !this.calendar) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tthis.saveError = ''\n\t\t\ttry {\n\t\t\t\tconst stored = Array.isArray(this.calendar.exceptions) ? this.calendar.exceptions : []\n\t\t\t\tconst name = this.newName.trim()\n\t\t\t\tconst exceptions = [...stored.filter((e) => (typeof e === 'string' ? e : e?.date) !== this.newDate), name ? { date: this.newDate, name } : { date: this.newDate }]\n\t\t\t\tconst response = await axios.patch(this.objectUrl(), { exceptions })\n\t\t\t\tthis.calendar = { ...this.calendar, ...(response?.data && typeof response.data === 'object' ? response.data : {}), exceptions }\n\t\t\t\tconst year = Number(this.newDate.slice(0, 4))\n\t\t\t\tconst { [year]: _stale, ...rest } = this.previews\n\t\t\t\tthis.previews = rest\n\t\t\t\tconst [y, m] = this.newDate.split('-').map(Number)\n\t\t\t\tthis.year = y\n\t\t\t\tthis.month = m - 1\n\t\t\t\tthis.focusDate = this.newDate\n\t\t\t\tawait this.loadPreview()\n\t\t\t\tthis.adding = false\n\t\t\t\tthis.liveText = t('nextcloud-vue', 'Day added.')\n\t\t\t} catch (e) {\n\t\t\t\tthis.saveError = e?.response?.status === 403\n\t\t\t\t\t? t('nextcloud-vue', 'Only an administrator can change the working calendar.')\n\t\t\t\t\t: t('nextcloud-vue', 'The day could not be added. Try again.')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home / End and Page Up / Down move focus across the days.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key event.\n\t\t * @return {void}\n\t\t */\n\t\tonKeydown(event) {\n\t\t\tconst moves = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }\n\t\t\tconst [y, m, d] = (this.focusDate || todayIso()).split('-').map(Number)\n\t\t\tlet target\n\t\t\tif (event.key in moves) {\n\t\t\t\ttarget = new Date(y, m - 1, d + moves[event.key])\n\t\t\t} else if (event.key === 'Home' || event.key === 'End') {\n\t\t\t\tconst weekday = (new Date(y, m - 1, d).getDay() + 6) % 7\n\t\t\t\ttarget = new Date(y, m - 1, d + (event.key === 'Home' ? -weekday : 6 - weekday))\n\t\t\t} else if (event.key === 'PageUp' || event.key === 'PageDown') {\n\t\t\t\ttarget = new Date(y, m - 1 + (event.key === 'PageUp' ? -1 : 1), 1)\n\t\t\t\ttarget.setDate(Math.min(d, new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate()))\n\t\t\t} else if ((event.key === 'Enter' || event.key === ' ') && this.editable) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.openAdd(this.focusDate)\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.focusDate = isoDate(target.getFullYear(), target.getMonth(), target.getDate())\n\t\t\tif (target.getFullYear() !== this.year || target.getMonth() !== this.month) {\n\t\t\t\tthis.year = target.getFullYear()\n\t\t\t\tthis.month = target.getMonth()\n\t\t\t\tthis.liveText = this.monthTitle\n\t\t\t}\n\t\t\tthis.$nextTick(() => this.$el.querySelector?.(`[data-date=\"${this.focusDate}\"]`)?.focus?.())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-working-calendar {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-working-calendar__head {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-working-calendar__title {\n\tfont-size: 15px;\n\tmin-width: 9em;\n\ttext-align: center;\n}\n\n.cn-working-calendar__add {\n\tmargin-inline-start: auto;\n}\n\n.cn-working-calendar__grid {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-working-calendar__row {\n\tdisplay: grid;\n\tgap: 4px;\n\tgrid-template-columns: repeat(7, minmax(0, 1fr));\n}\n\n.cn-working-calendar__weekday {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\n.cn-working-calendar__day {\n\talign-items: center;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 6px;\n\tcursor: default;\n\tdisplay: flex;\n\tfont-size: 13px;\n\theight: 34px;\n\tjustify-content: center;\n}\n\n.cn-working-calendar__day:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 1px;\n}\n\n.cn-working-calendar__day--off {\n\tbackground: var(--color-background-dark);\n}\n\n.cn-working-calendar__day--closed {\n\tbackground: var(--color-error-light, color-mix(in srgb, var(--color-error) 12%, var(--color-main-background)));\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-main-text));\n\tfont-weight: 600;\n}\n\n.cn-working-calendar__day--today {\n\tbox-shadow: inset 0 0 0 2px var(--color-primary-element);\n}\n\n.cn-working-calendar__live {\n\tclip-path: inset(50%);\n\theight: 1px;\n\toverflow: hidden;\n\tposition: absolute;\n\twhite-space: nowrap;\n\twidth: 1px;\n}\n\n.cn-working-calendar__note {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n/* @spec openspec/changes/working-calendar-footnote/specs/working-calendar-footnote/spec.md */\n.cn-working-calendar__footnote {\n\tmargin: 0;\n\tfont-size: 13px;\n\tline-height: 1.3;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-working-calendar__form {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px 12px;\n}\n\n.cn-working-calendar__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-working-calendar__form-actions {\n\talign-items: flex-end;\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-working-calendar__error {\n\tcolor: var(--color-error-text, var(--color-error));\n\tmargin: 0;\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}
1
+ {"version":3,"file":"CnWorkingCalendarWidget.vue2.js","sources":["../../../../src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-working-calendar\" data-testid=\"cn-working-calendar\">\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-working-calendar__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-working-calendar-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && !calendar\" class=\"cn-working-calendar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<template v-else-if=\"calendar\">\n\t\t\t<div class=\"cn-working-calendar__head\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"previousMonthLabel\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-previous\"\n\t\t\t\t\t@click=\"shiftMonth(-1)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<strong :id=\"`${uid}-title`\" class=\"cn-working-calendar__title\" data-testid=\"cn-working-calendar-title\">{{ monthTitle }}</strong>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"nextMonthLabel\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-next\"\n\t\t\t\t\t@click=\"shiftMonth(1)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editable && content.addButton !== false\"\n\t\t\t\t\tclass=\"cn-working-calendar__add\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-add\"\n\t\t\t\t\t@click=\"openAdd()\">\n\t\t\t\t\t{{ addLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"previewNote\" class=\"cn-working-calendar__note\" data-testid=\"cn-working-calendar-note\">\n\t\t\t\t{{ previewNote }}\n\t\t\t</p>\n\t\t\t<span class=\"cn-working-calendar__live\" aria-live=\"polite\">{{ liveText }}</span>\n\n\t\t\t<div\n\t\t\t\trole=\"grid\"\n\t\t\t\tclass=\"cn-working-calendar__grid\"\n\t\t\t\t:aria-labelledby=\"`${uid}-title`\"\n\t\t\t\tdata-testid=\"cn-working-calendar-grid\"\n\t\t\t\t@keydown=\"onKeydown\">\n\t\t\t\t<div role=\"row\" class=\"cn-working-calendar__row\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-for=\"name in weekdayNames\"\n\t\t\t\t\t\t:key=\"name.long\"\n\t\t\t\t\t\trole=\"columnheader\"\n\t\t\t\t\t\tclass=\"cn-working-calendar__weekday\"\n\t\t\t\t\t\t:aria-label=\"name.long\">{{ name.short }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(week, w) in weeks\"\n\t\t\t\t\t:key=\"w\"\n\t\t\t\t\trole=\"row\"\n\t\t\t\t\tclass=\"cn-working-calendar__row\">\n\t\t\t\t\t<template v-for=\"(slot, d) in week\">\n\t\t\t\t\t\t<span v-if=\"!slot\"\n\t\t\t\t\t\t\t:key=\"`empty-${w}-${d}`\"\n\t\t\t\t\t\t\trole=\"gridcell\"\n\t\t\t\t\t\t\tclass=\"cn-working-calendar__blank\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t:key=\"slot.date\"\n\t\t\t\t\t\t\trole=\"gridcell\"\n\t\t\t\t\t\t\tclass=\"cn-working-calendar__day\"\n\t\t\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--off': !slot.working && !slot.closure,\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--closed': slot.closure,\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--today': slot.today,\n\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t:tabindex=\"slot.date === focusDate ? 0 : -1\"\n\t\t\t\t\t\t\t:aria-label=\"dayLabel(slot)\"\n\t\t\t\t\t\t\t:aria-current=\"slot.today ? 'date' : null\"\n\t\t\t\t\t\t\t:title=\"slot.closure ? closureName(slot.closure) : null\"\n\t\t\t\t\t\t\t:data-date=\"slot.date\"\n\t\t\t\t\t\t\tdata-testid=\"cn-working-calendar-day\"\n\t\t\t\t\t\t\t@click=\"onDayClick(slot)\"\n\t\t\t\t\t\t\t@focus=\"focusDate = slot.date\">{{ slot.day }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<p v-if=\"content.footnote\" class=\"cn-working-calendar__footnote\" data-testid=\"cn-working-calendar-footnote\">\n\t\t\t\t{{ cnTranslate(content.footnote) }}\n\t\t\t</p>\n\n\t\t\t<form\n\t\t\t\tv-if=\"adding\"\n\t\t\t\tclass=\"cn-working-calendar__form\"\n\t\t\t\tdata-testid=\"cn-working-calendar-form\"\n\t\t\t\t@submit.prevent=\"saveDay\">\n\t\t\t\t<label class=\"cn-working-calendar__field\">\n\t\t\t\t\t{{ dateLabel }}\n\t\t\t\t\t<input\n\t\t\t\t\t\tref=\"dateInput\"\n\t\t\t\t\t\tv-model=\"newDate\"\n\t\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t\trequired\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-date\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-working-calendar__field\">\n\t\t\t\t\t{{ nameLabel }}\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"newName\"\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-name\">\n\t\t\t\t</label>\n\t\t\t\t<div class=\"cn-working-calendar__form-actions\">\n\t\t\t\t\t<NcButton variant=\"tertiary\" data-testid=\"cn-working-calendar-cancel\" @click=\"adding = false\">\n\t\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t:disabled=\"saving || !newDate\"\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-save\">\n\t\t\t\t\t\t{{ saveLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"saveError\"\n\t\t\t\t\tclass=\"cn-working-calendar__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-save-error\">\n\t\t\t\t\t{{ saveError }}\n\t\t\t\t</p>\n\t\t\t</form>\n\t\t</template>\n\t\t<p v-else class=\"cn-working-calendar__note\" data-testid=\"cn-working-calendar-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\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, NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport { fetchReferenceObjects } from '../../utils/boardReferenceColumns.js'\nimport { hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { userLocale } from '../../utils/userLocale.js'\nimport { buildMonth, isoDate, normaliseExceptions } from '../../utils/workingCalendarMonth.js'\n\nconst OBJECT_API = '/apps/openregister/api/objects'\nconst PREVIEW_URL = '/apps/openregister/api/flow-timers/calendars/preview'\n\nlet instanceCounter = 0\n\n/**\n * The id of an object, wherever OpenRegister carries it.\n *\n * @param {object|null} object The object.\n * @return {string} The id, or ''.\n */\nfunction idOf(object) {\n\treturn String(object?.id ?? object?.['@self']?.id ?? object?.uuid ?? '')\n}\n\n/**\n * Today as `YYYY-MM-DD`, in local time.\n *\n * @return {string} The date.\n */\nfunction todayIso() {\n\tconst now = new Date()\n\treturn isoDate(now.getFullYear(), now.getMonth(), now.getDate())\n}\n\n/**\n * CnWorkingCalendarWidget — the `working-calendar` widget: one month of a\n * working calendar kept as an OpenRegister object (board\n * DqInstellingenAlgemeen, \"Werkdagen en feestdagen\").\n *\n * The grid runs Monday to Sunday. Weekdays the calendar does not work are\n * grey; the dates it is closed on are red, with their name on hover and in\n * the day's label. The closures are not computed here: the widget asks\n * OpenRegister's calendar preview (`/api/flow-timers/calendars/preview`) for\n * the year shown, which expands the calendar's rules and its exceptions.\n *\n * \"Add day\" appends `{ date, name }` to the object's `exceptions` with one\n * update through the object API, under the reader's own rights; it shows for\n * administrators, and the server decides. The preview is administrators-only\n * too; for anyone else the widget shows the stored exceptions and says the\n * holidays could not be read.\n *\n * ```json\n * {\n * \"type\": \"working-calendar\",\n * \"content\": {\n * \"calendar\": { \"register\": \"flow-timers\", \"schema\": \"working-calendar\", \"filter\": { \"slug\": \"default\" } }\n * }\n * }\n * ```\n *\n * Keyboard: the days form a grid with one tab stop; the arrow keys move a\n * day or a week, Home and End to the start and end of the week, Page Up and\n * Page Down a month. Enter on a day opens Add day for that date.\n */\nexport default {\n\tname: 'CnWorkingCalendarWidget',\n\n\tcomponents: { ChevronLeft, ChevronRight, NcButton, NcLoadingIcon },\n\n\tinject: {\n\t\t/** The page's workspace values, for `@workspace.<key>` tokens in the filter. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host's translate, for the manifest's texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ calendar: { register, schema, id? | filter? },\n\t\t * previewUrl?, editable?, addButton?, emptyText?, footnote? }`. `addButton: false` leaves the Add day button\n\t\t * out, for a host that draws it in its own title row and calls `openAdd()`. `footnote` is a\n\t\t * line under the month grid, through the host's translate: what the colours mean\n\t\t * (DqInstellingenAlgemeen \"Rood: feestdag of sluitingsdag.\").\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` in the filter. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` in the filter. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tinstanceCounter += 1\n\t\tconst now = new Date()\n\t\treturn {\n\t\t\tuid: `cn-working-calendar-${instanceCounter}`,\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tcalendar: null,\n\t\t\tpreviews: {},\n\t\t\tpreviewFailed: false,\n\t\t\tyear: now.getFullYear(),\n\t\t\tmonth: now.getMonth(),\n\t\t\tfocusDate: todayIso(),\n\t\t\tadding: false,\n\t\t\tnewDate: '',\n\t\t\tnewName: '',\n\t\t\tsaving: false,\n\t\t\tsaveError: '',\n\t\t\tliveText: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{register: string, schema: string, id: string, filter: (object|null)}|null} The calendar source, tokens resolved, or null while unresolved. */\n\t\tsource() {\n\t\t\tconst c = this.content?.calendar\n\t\t\tif (!c || !c.register || !c.schema) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst unwrap = (v) => (v && typeof v === 'object' && 'value' in v ? v.value : v)\n\t\t\tconst ctx = { objectId: this.objectId || null, object: this.objectData, workspace: unwrap(this.cnWorkspaceContext) || {} }\n\t\t\tconst filter = c.filter && typeof c.filter === 'object' ? resolveFilterTokens(c.filter, ctx) : null\n\t\t\tif (filter && hasUnresolvedTokens(filter)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { register: String(c.register), schema: String(c.schema), id: c.id ? String(c.id) : '', filter }\n\t\t},\n\n\t\t/** @return {string} A key of the source, to read it again when it changes. */\n\t\tsourceKey() {\n\t\t\treturn this.source ? JSON.stringify(this.source) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether Add day shows: administrators, unless the manifest turns it off. */\n\t\teditable() {\n\t\t\treturn this.content?.editable !== false && Boolean(getCurrentUser()?.isAdmin)\n\t\t},\n\n\t\t/** @return {number[]} The ISO weekdays that work. */\n\t\tworkingWeekdays() {\n\t\t\tconst days = this.calendar?.workingWeekdays\n\t\t\treturn Array.isArray(days) ? days.map((d) => Number(d)) : [1, 2, 3, 4, 5]\n\t\t},\n\n\t\t/** @return {Array<{date: string, name: string}>} The stored exceptions. */\n\t\texceptions() {\n\t\t\treturn normaliseExceptions(this.calendar?.exceptions)\n\t\t},\n\n\t\t/**\n\t\t * The closed dates of the year shown: the preview's, each marked as an\n\t\t * exception when the calendar lists it, else as a holiday; without a\n\t\t * preview, the stored exceptions.\n\t\t *\n\t\t * @return {Map<string, {name: string, kind: string}>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month\n\t\t */\n\t\tclosures() {\n\t\t\tconst own = new Set(this.exceptions.map((e) => e.date))\n\t\t\tconst map = new Map()\n\t\t\tconst preview = this.previews[this.year]\n\t\t\tfor (const entry of Array.isArray(preview) ? preview : []) {\n\t\t\t\tif (entry && typeof entry.date === 'string') {\n\t\t\t\t\tmap.set(entry.date, { name: String(entry.name || ''), kind: own.has(entry.date) ? 'exception' : 'holiday' })\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const e of this.exceptions) {\n\t\t\t\tif (!map.has(e.date)) {\n\t\t\t\t\tmap.set(e.date, { name: e.name, kind: 'exception' })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn map\n\t\t},\n\n\t\t/** @return {Array<Array<object|null>>} The weeks of the month shown. */\n\t\tweeks() {\n\t\t\treturn buildMonth(this.year, this.month, { workingWeekdays: this.workingWeekdays, closures: this.closures, today: todayIso() })\n\t\t},\n\n\t\t/** @return {Array<{short: string, long: string}>} Monday to Sunday in the user's language. */\n\t\tweekdayNames() {\n\t\t\tconst short = new Intl.DateTimeFormat(userLocale(), { weekday: 'short' })\n\t\t\tconst long = new Intl.DateTimeFormat(userLocale(), { weekday: 'long' })\n\t\t\treturn Array.from({ length: 7 }, (_v, i) => {\n\t\t\t\tconst day = new Date(Date.UTC(2024, 0, 1 + i, 12))\n\t\t\t\treturn { short: short.format(day).replace(/\\.$/, ''), long: long.format(day) }\n\t\t\t})\n\t\t},\n\n\t\t/** @return {string} \"December 2026\" in the user's language. */\n\t\tmonthTitle() {\n\t\t\tconst text = new Intl.DateTimeFormat(userLocale(), { month: 'long', year: 'numeric' }).format(new Date(this.year, this.month, 15))\n\t\t\treturn text.charAt(0).toUpperCase() + text.slice(1)\n\t\t},\n\n\t\t/** @return {string} Why holidays are missing, or ''. */\n\t\tpreviewNote() {\n\t\t\treturn this.previewFailed ? t('nextcloud-vue', 'Holidays could not be read. Only the days added by hand are shown.') : ''\n\t\t},\n\n\t\tloadingLabel: () => t('nextcloud-vue', 'Loading …'),\n\t\tpreviousMonthLabel: () => t('nextcloud-vue', 'Previous month'),\n\t\tnextMonthLabel: () => t('nextcloud-vue', 'Next month'),\n\t\taddLabel: () => t('nextcloud-vue', 'Add day'),\n\t\tdateLabel: () => t('nextcloud-vue', 'Date'),\n\t\tnameLabel: () => t('nextcloud-vue', 'Name'),\n\t\tcancelLabel: () => t('nextcloud-vue', 'Cancel'),\n\t\tsaveLabel: () => t('nextcloud-vue', 'Save'),\n\n\t\t/** @return {string} The empty-state text. */\n\t\temptyLabel() {\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'No working calendar found.')\n\t\t},\n\t},\n\n\twatch: {\n\t\tsourceKey: {\n\t\t\thandler() {\n\t\t\t\tthis.load()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\n\t\tyear() {\n\t\t\tthis.loadPreview()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The host's translate, or the text unchanged.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/** @return {string} The calendar object's API URL. */\n\t\tobjectUrl() {\n\t\t\tconst s = this.source\n\t\t\tconst id = idOf(this.calendar) || s.id\n\t\t\treturn generateUrl(`${OBJECT_API}/${encodeURIComponent(s.register)}/${encodeURIComponent(s.schema)}/${encodeURIComponent(id)}`)\n\t\t},\n\n\t\t/**\n\t\t * Read the calendar object, then the closures of the year shown.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst key = this.sourceKey\n\t\t\tif (key === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst s = this.source\n\t\t\t\tlet calendar = null\n\t\t\t\tif (s.id) {\n\t\t\t\t\tconst response = await axios.get(generateUrl(`${OBJECT_API}/${encodeURIComponent(s.register)}/${encodeURIComponent(s.schema)}/${encodeURIComponent(s.id)}`))\n\t\t\t\t\tcalendar = response?.data || null\n\t\t\t\t} else {\n\t\t\t\t\tconst found = await fetchReferenceObjects({ register: s.register, schema: s.schema, filter: s.filter || {}, limit: 1 })\n\t\t\t\t\tcalendar = found[0] || null\n\t\t\t\t}\n\t\t\t\tif (key !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.calendar = calendar\n\t\t\t\tthis.previews = {}\n\t\t\t\tif (calendar) {\n\t\t\t\t\tawait this.loadPreview()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'The working calendar could not be loaded.')\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 the calendar preview for the closed dates of the year shown,\n\t\t * once per year.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month\n\t\t */\n\t\tasync loadPreview() {\n\t\t\tconst year = this.year\n\t\t\tif (!this.calendar || this.previews[year]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(this.content?.previewUrl || PREVIEW_URL)\n\t\t\t\tconst response = await axios.post(url, { calendar: this.calendar, year })\n\t\t\t\tconst dates = Array.isArray(response?.data?.dates) ? response.data.dates : []\n\t\t\t\tthis.previews = { ...this.previews, [year]: dates }\n\t\t\t\tthis.previewFailed = false\n\t\t\t} catch {\n\t\t\t\tthis.previewFailed = true\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Show another month, keeping focus on the same day where it exists.\n\t\t *\n\t\t * @param {number} delta Months to move.\n\t\t * @return {void}\n\t\t */\n\t\tshiftMonth(delta) {\n\t\t\tconst date = new Date(this.year, this.month + delta, 1)\n\t\t\tthis.year = date.getFullYear()\n\t\t\tthis.month = date.getMonth()\n\t\t\tconst day = Math.min(Number(this.focusDate.slice(8, 10)) || 1, new Date(this.year, this.month + 1, 0).getDate())\n\t\t\tthis.focusDate = isoDate(this.year, this.month, day)\n\t\t\tthis.liveText = this.monthTitle\n\t\t},\n\n\t\t/**\n\t\t * A day's accessible label: the full date, whether it works, and the\n\t\t * closure's name.\n\t\t *\n\t\t * @param {object} slot The day.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdayLabel(slot) {\n\t\t\tconst [y, m, d] = slot.date.split('-').map(Number)\n\t\t\tconst date = new Intl.DateTimeFormat(userLocale(), { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(y, m - 1, d))\n\t\t\tif (slot.closure) {\n\t\t\t\tconst name = this.closureName(slot.closure)\n\t\t\t\treturn name\n\t\t\t\t\t? t('nextcloud-vue', '{date}, closed: {name}', { date, name })\n\t\t\t\t\t: t('nextcloud-vue', '{date}, closed', { date })\n\t\t\t}\n\t\t\treturn slot.working ? t('nextcloud-vue', '{date}, working day', { date }) : t('nextcloud-vue', '{date}, not a working day', { date })\n\t\t},\n\n\t\t/**\n\t\t * A closure's name, with a fallback for an unnamed added day.\n\t\t *\n\t\t * @param {{name: string, kind: string}} closure The closure.\n\t\t * @return {string} The name.\n\t\t */\n\t\tclosureName(closure) {\n\t\t\tif (closure.name) {\n\t\t\t\treturn this.translate(closure.name)\n\t\t\t}\n\t\t\treturn closure.kind === 'exception' ? t('nextcloud-vue', 'Day added by hand') : ''\n\t\t},\n\n\t\t/**\n\t\t * Open Add day, with a date filled in.\n\t\t *\n\t\t * @param {string} [date] The date.\n\t\t * @return {void}\n\t\t */\n\t\topenAdd(date) {\n\t\t\tthis.newDate = date || this.focusDate || ''\n\t\t\tthis.newName = ''\n\t\t\tthis.saveError = ''\n\t\t\tthis.adding = true\n\t\t\tthis.$nextTick(() => this.$refs.dateInput?.focus?.())\n\t\t},\n\n\t\t/**\n\t\t * A click on a day: open Add day for it when the reader may edit.\n\t\t *\n\t\t * @param {object} slot The day.\n\t\t * @return {void}\n\t\t */\n\t\tonDayClick(slot) {\n\t\t\tthis.focusDate = slot.date\n\t\t\tif (this.editable) {\n\t\t\t\tthis.openAdd(slot.date)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Append the new day to the calendar's exceptions with one update,\n\t\t * under the reader's rights, then read the closures again.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-day-can-be-added-to-the-calendar\n\t\t */\n\t\tasync saveDay() {\n\t\t\tif (!/^\\d{4}-\\d{2}-\\d{2}$/.test(this.newDate) || !this.calendar) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tthis.saveError = ''\n\t\t\ttry {\n\t\t\t\tconst stored = Array.isArray(this.calendar.exceptions) ? this.calendar.exceptions : []\n\t\t\t\tconst name = this.newName.trim()\n\t\t\t\tconst exceptions = [...stored.filter((e) => (typeof e === 'string' ? e : e?.date) !== this.newDate), name ? { date: this.newDate, name } : { date: this.newDate }]\n\t\t\t\tconst response = await axios.patch(this.objectUrl(), { exceptions })\n\t\t\t\tthis.calendar = { ...this.calendar, ...(response?.data && typeof response.data === 'object' ? response.data : {}), exceptions }\n\t\t\t\tconst year = Number(this.newDate.slice(0, 4))\n\t\t\t\tconst { [year]: _stale, ...rest } = this.previews\n\t\t\t\tthis.previews = rest\n\t\t\t\tconst [y, m] = this.newDate.split('-').map(Number)\n\t\t\t\tthis.year = y\n\t\t\t\tthis.month = m - 1\n\t\t\t\tthis.focusDate = this.newDate\n\t\t\t\tawait this.loadPreview()\n\t\t\t\tthis.adding = false\n\t\t\t\tthis.liveText = t('nextcloud-vue', 'Day added.')\n\t\t\t} catch (e) {\n\t\t\t\tthis.saveError = e?.response?.status === 403\n\t\t\t\t\t? t('nextcloud-vue', 'Only an administrator can change the working calendar.')\n\t\t\t\t\t: t('nextcloud-vue', 'The day could not be added. Try again.')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home / End and Page Up / Down move focus across the days.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key event.\n\t\t * @return {void}\n\t\t */\n\t\tonKeydown(event) {\n\t\t\tconst moves = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }\n\t\t\tconst [y, m, d] = (this.focusDate || todayIso()).split('-').map(Number)\n\t\t\tlet target\n\t\t\tif (event.key in moves) {\n\t\t\t\ttarget = new Date(y, m - 1, d + moves[event.key])\n\t\t\t} else if (event.key === 'Home' || event.key === 'End') {\n\t\t\t\tconst weekday = (new Date(y, m - 1, d).getDay() + 6) % 7\n\t\t\t\ttarget = new Date(y, m - 1, d + (event.key === 'Home' ? -weekday : 6 - weekday))\n\t\t\t} else if (event.key === 'PageUp' || event.key === 'PageDown') {\n\t\t\t\ttarget = new Date(y, m - 1 + (event.key === 'PageUp' ? -1 : 1), 1)\n\t\t\t\ttarget.setDate(Math.min(d, new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate()))\n\t\t\t} else if ((event.key === 'Enter' || event.key === ' ') && this.editable) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.openAdd(this.focusDate)\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.focusDate = isoDate(target.getFullYear(), target.getMonth(), target.getDate())\n\t\t\tif (target.getFullYear() !== this.year || target.getMonth() !== this.month) {\n\t\t\t\tthis.year = target.getFullYear()\n\t\t\t\tthis.month = target.getMonth()\n\t\t\t\tthis.liveText = this.monthTitle\n\t\t\t}\n\t\t\tthis.$nextTick(() => this.$el.querySelector?.(`[data-date=\"${this.focusDate}\"]`)?.focus?.())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-working-calendar {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-working-calendar__head {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-working-calendar__title {\n\tfont-size: 15px;\n\tmin-width: 9em;\n\ttext-align: center;\n}\n\n.cn-working-calendar__add {\n\tmargin-inline-start: auto;\n}\n\n.cn-working-calendar__grid {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-working-calendar__row {\n\tdisplay: grid;\n\tgap: 4px;\n\tgrid-template-columns: repeat(7, minmax(0, 1fr));\n}\n\n.cn-working-calendar__weekday {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\n.cn-working-calendar__day {\n\talign-items: center;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 6px;\n\tcursor: default;\n\tdisplay: flex;\n\tfont-size: 13px;\n\theight: 34px;\n\tjustify-content: center;\n}\n\n.cn-working-calendar__day:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 1px;\n}\n\n.cn-working-calendar__day--off {\n\tbackground: var(--color-background-dark);\n}\n\n.cn-working-calendar__day--closed {\n\tbackground: var(--color-error-light, color-mix(in srgb, var(--color-error) 12%, var(--color-main-background)));\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-main-text));\n\tfont-weight: 600;\n}\n\n.cn-working-calendar__day--today {\n\tbox-shadow: inset 0 0 0 2px var(--color-primary-element);\n}\n\n.cn-working-calendar__live {\n\tclip-path: inset(50%);\n\theight: 1px;\n\toverflow: hidden;\n\tposition: absolute;\n\twhite-space: nowrap;\n\twidth: 1px;\n}\n\n/* A theme's paragraph rule sets every p's size and colour with !important\n (thematiq), so the note and the footnote also set its tokens.\n @spec openspec/changes/calendar-footnote-paragraph-tokens/specs/working-calendar-footnote/spec.md */\n.cn-working-calendar__note {\n\t--nldesign-component-paragraph-color: var(--color-text-maxcontrast);\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n/* @spec openspec/changes/working-calendar-footnote/specs/working-calendar-footnote/spec.md */\n.cn-working-calendar__footnote {\n\t--nldesign-component-paragraph-font-size: 13px;\n\t--nldesign-component-paragraph-line-height: 1.3;\n\t--nldesign-component-paragraph-color: var(--color-text-maxcontrast);\n\tmargin: 0;\n\tfont-size: 13px;\n\tline-height: 1.3;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-working-calendar__form {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px 12px;\n}\n\n.cn-working-calendar__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-working-calendar__form-actions {\n\talign-items: flex-end;\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-working-calendar__error {\n\tcolor: var(--color-error-text, var(--color-error));\n\tmargin: 0;\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}