@conduction/nextcloud-vue 2.150.0 → 2.151.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 (67) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +7 -2
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +40 -41
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  6. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  7. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +6 -3
  8. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  9. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +13 -1
  10. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  11. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +1 -1
  12. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +5 -1
  13. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  14. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  15. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  16. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +46 -0
  17. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  18. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +64 -64
  19. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  20. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
  21. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +15 -1
  22. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  23. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  24. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js +1 -1
  25. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js.map +1 -1
  26. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js +16 -5
  27. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js.map +1 -1
  28. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  29. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +25 -2
  30. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  31. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +82 -68
  32. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  33. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  34. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  35. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  36. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +1 -1
  37. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -1
  38. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +1 -1
  39. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -1
  40. package/dist/esm/integrations/builtin/calendar/eventStatus.js +43 -2
  41. package/dist/esm/integrations/builtin/calendar/eventStatus.js.map +1 -1
  42. package/dist/esm/l10n/en.json.js +4 -1
  43. package/dist/esm/l10n/en.json.js.map +1 -1
  44. package/dist/esm/l10n/nl.json.js +5 -2
  45. package/dist/esm/l10n/nl.json.js.map +1 -1
  46. package/dist/esm/library-style.js +1 -1
  47. package/dist/esm/nextcloud-vue.css +623 -362
  48. package/dist/nextcloud-vue.cjs.js +894 -720
  49. package/dist/nextcloud-vue.cjs.js.map +2 -2
  50. package/dist/nextcloud-vue.css +623 -362
  51. package/l10n/en.json +4 -1
  52. package/l10n/nl.json +5 -2
  53. package/package.json +1 -1
  54. package/src/components/CnActionDialog/CnActionDialog.vue +17 -4
  55. package/src/components/CnConversationThread/CnConversationThread.vue +16 -2
  56. package/src/components/CnConversationThread/CnConversationWidget.vue +5 -1
  57. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +47 -2
  58. package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
  59. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +15 -1
  60. package/src/components/CnPlanWidget/CnPlanWidgetRow.vue +17 -2
  61. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +45 -2
  62. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +8 -1
  63. package/src/components/CnTimelineWidget/CnTimelineManage.vue +1 -1
  64. package/src/css/dialog.css +6 -0
  65. package/src/css/look-board-detail.css +137 -0
  66. package/src/css/look-board.css +78 -2
  67. package/src/integrations/builtin/calendar/eventStatus.js +42 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CnActionDialog.vue3.js","sources":["../../../../src/components/CnActionDialog/CnActionDialog.vue?vue&type=template&id=105a37b4&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Confirm')\"\n\t\t:eyebrow=\"eyebrowText\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-action-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-action-dialog\">\n\t\t\t<!-- The record the action is about: its title, a meta line, a badge. -->\n\t\t\t<div v-if=\"summary\" class=\"cn-action-dialog__summary\" data-testid=\"cn-action-dialog-summary\">\n\t\t\t\t<span class=\"cn-action-dialog__summary-icon\">\n\t\t\t\t\t<CnIcon :name=\"config.summary.icon || 'FolderOutline'\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-action-dialog__summary-text\">\n\t\t\t\t\t<strong>{{ summary.title }}</strong>\n\t\t\t\t\t<span v-if=\"summary.meta\" class=\"cn-action-dialog__muted\">{{ summary.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"summary.badge\" class=\"cn-action-dialog__badge\">{{ summary.badge }}</span>\n\t\t\t</div>\n\n\t\t\t<!-- Facts about the record, in titled groups (\"Retention under the case type\"). -->\n\t\t\t<section v-for=\"(section, index) in factSections\"\n\t\t\t\t:key=\"`facts-${index}`\"\n\t\t\t\tclass=\"cn-action-dialog__section\"\n\t\t\t\tdata-testid=\"cn-action-dialog-facts\">\n\t\t\t\t<h3 v-if=\"section.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ section.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-action-dialog__facts\">\n\t\t\t\t\t<div v-for=\"fact in section.facts\" :key=\"fact.label\" class=\"cn-action-dialog__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\n\t\t\t<!-- The checklist, read from an endpoint: each item met, not met or\n\t\t\t unknown. Items named in `required` decide whether the action may\n\t\t\t run; the others inform. -->\n\t\t\t<section v-if=\"config.checklist\" class=\"cn-action-dialog__section\" data-testid=\"cn-action-dialog-checklist\">\n\t\t\t\t<h3 v-if=\"config.checklist.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ text(config.checklist.title) }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"checklistError\" class=\"cn-action-dialog__muted\" role=\"status\">\n\t\t\t\t\t{{ checklistError }}\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cn-action-dialog__checks\">\n\t\t\t\t\t<li v-for=\"item in checklist\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-action-dialog__check\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-dialog-check-${item.id}`\"\n\t\t\t\t\t\t:data-met=\"String(item.met)\">\n\t\t\t\t\t\t<CheckCircle v-if=\"item.met === true\" :size=\"20\" class=\"cn-action-dialog__check-icon cn-action-dialog__check-icon--met\" />\n\t\t\t\t\t\t<HelpCircleOutline v-else-if=\"item.met === null\" :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<CircleOutline v-else :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__check-label\">{{ item.label }}</strong>\n\t\t\t\t\t\t<span v-if=\"item.detail\" class=\"cn-action-dialog__muted\">{{ item.detail }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\n\t\t\t<!-- Fields the action asks for, sent beside the reason. -->\n\t\t\t<div v-for=\"field in fields\"\n\t\t\t\t:key=\"field.field\"\n\t\t\t\tclass=\"cn-action-dialog__field\"\n\t\t\t\t:data-testid=\"`cn-action-dialog-field-${field.field}`\">\n\t\t\t\t<!-- A checkbox field: a tinted box, the label filled from the\n\t\t\t\t record, a hint under it (DqArchiveren \"Sanne de Vries laten\n\t\t\t\t weten...\"). -->\n\t\t\t\t<NcCheckboxRadioSwitch v-if=\"field.type === 'checkbox'\"\n\t\t\t\t\tclass=\"cn-action-dialog__checkbox\"\n\t\t\t\t\t:modelValue=\"checkboxValue(field)\"\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t@update:modelValue=\"(on) => setChecked(field.field, on)\">\n\t\t\t\t\t<span class=\"cn-action-dialog__checkbox-text\">\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__checkbox-label\">{{ fill(field.label) || field.field }}</strong>\n\t\t\t\t\t\t<span v-if=\"field.hint\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<!-- A search field: records of `source` found as the reader\n\t\t\t\t types (DqZaakKoppelen \"Zaak koppelen\"). -->\n\t\t\t\t<CnSearchSelect v-else-if=\"field.type === 'search'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || null\"\n\t\t\t\t\t:options=\"searchOptions[field.field] || []\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"true\"\n\t\t\t\t\t:loading=\"searching === field.field\"\n\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t@search=\"(typed) => onSearch(field, typed)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value && typeof value === 'object' ? value.id : value)\" />\n\t\t\t\t<!-- An optional select or text field: its label above the control,\n\t\t\t\t \"(optional)\" in grey after it (DqUwWachtrij \"Sjabloon (niet\n\t\t\t\t verplicht)\"). -->\n\t\t\t\t<CnFormField v-if=\"labelsOutside(field)\"\n\t\t\t\t\t:controlId=\"controlId(field)\"\n\t\t\t\t\t:text=\"fill(field.label) || field.field\"\n\t\t\t\t\t:optional=\"true\" />\n\t\t\t\t<NcSelect v-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsOf(field)\"\n\t\t\t\t\t:inputId=\"controlId(field)\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t:clearable=\"field.required === false\"\n\t\t\t\t\t@update:modelValue=\"(option) => pick(field, option)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type === 'secret'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\ttype=\"password\"\n\t\t\t\t\tautocomplete=\"new-password\"\n\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t:placeholder=\"secretStored(field) ? t('nextcloud-vue', 'Stored. Type a new value to replace it.') : ''\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type !== 'checkbox' && field.type !== 'search'\"\n\t\t\t\t\t:id=\"controlId(field)\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\t:type=\"field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')\"\n\t\t\t\t\t:label=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<span v-if=\"field.hint && field.type !== 'checkbox'\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.reason\" class=\"cn-action-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"reasonId\"\n\t\t\t\t\tv-model=\"reason\"\n\t\t\t\t\t:label=\"reasonLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:rows=\"config.reason.rows || 2\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-action-dialog-reason\" />\n\t\t\t\t<span v-if=\"config.reason.hint\" class=\"cn-action-dialog__muted\">{{ text(config.reason.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"config.note\"\n\t\t\t\t:type=\"noteType\"\n\t\t\t\t:heading=\"text(config.note.title)\"\n\t\t\t\tdata-testid=\"cn-action-dialog-note\">\n\t\t\t\t<template v-if=\"config.note.icon\" #icon>\n\t\t\t\t\t<CnIcon class=\"cn-action-dialog__note-icon\" :name=\"config.note.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(config.note.text) }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-action-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-action-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-action-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-action-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-action-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template v-if=\"confirmConfig.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Confirm') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CheckCircle from 'vue-material-design-icons/CheckCircle.vue'\nimport CircleOutline from 'vue-material-design-icons/CircleOutline.vue'\nimport HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'\nimport CnFormField from '../CnFormField/CnFormField.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** How long typing rests before a search field searches, in ms. */\nconst SEARCH_DELAY = 250\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** Gives each dialog's reason field its own id. */\nlet reasonSeq = 0\n\n/**\n * CnActionDialog — the generic \"summary, checklist, reason, confirm\" dialog\n * for an act on a record, configured from the manifest (an `open-modal`\n * action's props), so an app does not hand-build each one. dossiq's archive\n * dialog (board DqArchiveren) is the first: the case, what its case type\n * keeps, a readiness checklist read from an endpoint, a reason, a read-only\n * note and Archive it.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record (\"Close off · {identifier}\").\n * - `summary`: `{ source?, title, meta?, badge?, icon? }`: templates filled from\n * the record (`source` an endpointSource to read it from, else `object`).\n * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template; a\n * `{path|<formatter>}` token reads through the built-in and app formatters.\n * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items\n * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an\n * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be\n * met before the action can run. `met: null` reads as unknown.\n * - `permission`: `{ source, listPath?, match?, allowedPath, reasonPath? }`:\n * the action runs only while `allowedPath` reads true; `reasonPath` says\n * why not. With `listPath` the paths read the entry of that list whose\n * fields equal `match` (`{ act: \"archive\" }`).\n * - `fields`: `[{ field, type?: \"text\"|\"date\"|\"datetime\"|\"select\"|\"checkbox\", label?,\n * required?, options?: [{ value, label, fills? }], hint?, default? }]`: inputs\n * sent beside the reason, required by default; a select option's `fills`\n * sets other fields that are still empty. A `checkbox` is sent as a boolean,\n * starts at `default` (false), fills its `label` and `hint` from the record,\n * and is optional unless `required: true` (then it must be ticked). A\n * `search` field finds records of `source: { register, schema }` as the\n * reader types, shows each as `optionLabel` with `optionSecondary` under\n * it (templates on the found record), leaves the record in hand out with\n * `excludeSelf`, and sends the chosen record's id. Every field's `label`,\n * `hint` and `placeholder` (search and select) are templates filled from the record.\n * An optional select or text field (`required: false`) draws its label above\n * the control with \"(optional)\" after it.\n * - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by\n * default; sent as `field` (default `reason`). `rows` is its height in lines\n * (default 2; board DqBesluit draws 4).\n * - `note`: `{ variant?, title?, text, icon? }`: a read-only note; `icon` an\n * icon name in place of the variant's own (\"LockOutline\").\n * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.\n * `request` posts itself (`body` templates filled with the record and\n * `{reason}`); `action` dispatches a manifest action through the page.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url.\n */\nexport default {\n\tname: 'CnActionDialog',\n\n\tcomponents: { CheckCircle, CircleOutline, CnDialog, CnFormField, CnIcon, CnSearchSelect, HelpCircleOutline, NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t\t/** The app's named formatters (CnAppRoot), for `{path|<formatter>}` in a template. */\n\t\tcnFormatters: { default: () => ({}) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it: the summary and facts read it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\titems: [],\n\t\t\tchecklistError: '',\n\t\t\tallowed: true,\n\t\t\treason: '',\n\t\t\tvalues: {},\n\t\t\t/** A search field's found options, by field name. */\n\t\t\tsearchOptions: {},\n\t\t\t/** The search field whose search runs, or ''. */\n\t\t\tsearching: '',\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\treasonId: `cn-action-dialog-reason-${++reasonSeq}`,\n\t\t\tfieldIdPrefix: `cn-action-dialog-field-${reasonSeq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/**\n\t\t * The summary row, or null without one or before the record is known.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {{title: string, meta: string, badge: string}|null}\n\t\t */\n\t\tsummary() {\n\t\t\tconst def = this.config.summary\n\t\t\tif (!def || !this.record) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst title = this.fill(def.title)\n\t\t\treturn title ? { title, meta: this.fill(def.meta), badge: this.fill(def.badge) } : null\n\t\t},\n\n\t\t/** @return {Array<{title: string, facts: Array<{label: string, value: string}>}>} The fact groups. */\n\t\tfactSections() {\n\t\t\tif (!Array.isArray(this.config.facts) || !this.record) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst empty = t('nextcloud-vue', 'Not set')\n\t\t\treturn this.config.facts.map((section) => ({\n\t\t\t\ttitle: this.text(section.title),\n\t\t\t\tfacts: (Array.isArray(section.items) ? section.items : []).map((fact) => ({ label: this.text(fact.label), value: this.fill(fact.value) || empty })),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The checklist rows: label and detail from `labels` by id, else the item's own.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Array<{id: string, met: (boolean|null), label: string, detail: string}>}\n\t\t */\n\t\tchecklist() {\n\t\t\tconst labels = (this.config.checklist && this.config.checklist.labels) || {}\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst met = item.met === null || item.met === undefined ? null : item.met === true\n\t\t\t\tconst own = labels[item.id] || {}\n\t\t\t\tconst detail = met === false && own.unmetDetail ? own.unmetDetail : (own.detail || item.detail || '')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tmet,\n\t\t\t\t\tlabel: this.fill(own.label || item.label || String(item.id), item),\n\t\t\t\t\tdetail: met === null && !item.detail ? t('nextcloud-vue', 'Could not be read') : this.fill(detail, item),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether every required item is met. */\n\t\trequiredMet() {\n\t\t\tconst required = (this.config.checklist && Array.isArray(this.config.checklist.required)) ? this.config.checklist.required.map(String) : []\n\t\t\treturn required.every((id) => this.items.some((item) => String(item.id) === id && item.met === true))\n\t\t},\n\n\t\t/** @return {boolean} Whether a reason is asked and needed. */\n\t\treasonNeeded() {\n\t\t\treturn Boolean(this.config.reason) && this.config.reason.required !== false\n\t\t},\n\n\t\t/** @return {string} The reason field's label. */\n\t\treasonLabel() {\n\t\t\treturn this.text(this.config.reason && this.config.reason.label) || t('nextcloud-vue', 'Reason')\n\t\t},\n\n\t\t/** @return {string} The note's NcNoteCard type. */\n\t\tnoteType() {\n\t\t\tconst variant = this.config.note && this.config.note.variant\n\t\t\treturn ['info', 'warning', 'error', 'success'].includes(variant) ? variant : 'info'\n\t\t},\n\n\t\t/**\n\t\t * Whether the action may run: allowed, required items met, a reason\n\t\t * when one is needed, and nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.allowed && this.requiredMet && this.fieldsFilled && (!this.reasonNeeded || this.reason.trim() !== '')\n\t\t},\n\n\t\t/** @return {string} The eyebrow, filled from the record once it is known. */\n\t\teyebrowText() {\n\t\t\treturn this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)\n\t\t},\n\n\t\t/**\n\t\t * The value of every field as sent: a checkbox as a boolean, the\n\t\t * others as entered ('' when empty).\n\t\t *\n\t\t * @return {object} Values by field name.\n\t\t */\n\t\tfieldValues() {\n\t\t\tconst out = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tout[field.field] = field.type === 'checkbox' ? this.checkboxValue(field) : (this.values[field.field] ?? '')\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/** @return {Array<object>} The fields with a name. */\n\t\tfields() {\n\t\t\treturn (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)\n\t\t},\n\n\t\t/**\n\t\t * Whether every required field has a value.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-fields-and-acts/specs/action-dialog/spec.md#requirement-an-action-dialog-asks-for-fields-and-reads-a-permission-from-a-list\n\t\t * @return {boolean}\n\t\t */\n\t\tfieldsFilled() {\n\t\t\treturn this.fields.every((f) => {\n\t\t\t\tif (f.type === 'checkbox') {\n\t\t\t\t\treturn f.required !== true || this.checkboxValue(f)\n\t\t\t\t}\n\t\t\t\treturn f.required === false || this.secretStored(f) || String(this.values[f.field] ?? '').trim() !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\trecord: {\n\t\t\thandler() {\n\t\t\t\tthis.startValues()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether a field's label sits above its control with \"(optional)\" after\n\t\t * it: a select or a text field the action does not require.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-optional-fields/specs/action-dialog/spec.md#requirement-an-optional-field-says-so-beside-its-label\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tlabelsOutside(field) {\n\t\t\treturn field.required === false && !['checkbox', 'search', 'secret'].includes(field.type)\n\t\t},\n\n\t\t/**\n\t\t * The id of a field's control, which its outside label points at.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {string}\n\t\t */\n\t\tcontrolId(field) {\n\t\t\treturn `${this.fieldIdPrefix}-${field.field}`\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\t/**\n\t\t * Search a search field's records once typing rests.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} typed What the reader typed.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/action-dialog-search-field/specs/action-dialog-search-field/spec.md#requirement-an-action-dialog-field-can-search-records\n\t\t */\n\t\tonSearch(field, typed) {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.searchRecords(field, String(typed || '').trim()), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Read the records of a search field's `source` that match the term.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} term The term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync searchRecords(field, term) {\n\t\t\tconst source = field.source || {}\n\t\t\tif (!term || !source.register || !source.schema) {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.searching = field.field\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(source.register)}/${encodeURIComponent(source.schema)}`)\n\t\t\t\tconst { data } = await axios.get(url, { params: { _search: term, _limit: 10 } })\n\t\t\t\tconst rows = Array.isArray(data) ? data : (data && Array.isArray(data.results) ? data.results : [])\n\t\t\t\tconst self = String(this.recordId || '')\n\t\t\t\tconst options = rows\n\t\t\t\t\t.map((row) => ({ row, id: String(row.id || (row['@self'] && row['@self'].id) || '') }))\n\t\t\t\t\t.filter(({ id }) => id !== '' && !(field.excludeSelf && id === self))\n\t\t\t\t\t.map(({ row, id }) => {\n\t\t\t\t\t\tconst option = { id, label: this.fill(field.optionLabel || '{title}', row) || id }\n\t\t\t\t\t\tconst secondary = this.fill(field.optionSecondary, row)\n\t\t\t\t\t\treturn secondary ? { ...option, secondary } : option\n\t\t\t\t\t})\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: options }\n\t\t\t} catch {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t} finally {\n\t\t\t\tthis.searching = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A checkbox field's state: what was ticked, else its `default`.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/action-dialog-checkbox-field/specs/action-dialog-checkbox-field/spec.md#requirement-an-action-dialog-field-can-be-a-checkbox\n\t\t */\n\t\tcheckboxValue(field) {\n\t\t\treturn field.field in this.values ? this.values[field.field] === true : field.default === true\n\t\t},\n\n\t\t/**\n\t\t * Tick or untick a checkbox field.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {boolean} on Ticked.\n\t\t * @return {void}\n\t\t */\n\t\tsetChecked(name, on) {\n\t\t\tthis.values = { ...this.values, [name]: on === true }\n\t\t},\n\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row (the record by default), translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} [row] The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, row) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst formatters = { ...BUILT_IN_FORMATTERS, ...(this.cnFormatters || {}) }\n\t\t\treturn fillTemplate(this.cnTranslate(template), row || this.record || {}, new Date(), { formatters }).trim()\n\t\t},\n\n\t\t/**\n\t\t * Read an endpoint, fresh.\n\t\t *\n\t\t * @param {object} source An endpointSource block.\n\t\t * @return {Promise<unknown>}\n\t\t */\n\t\tread(source) {\n\t\t\treturn fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadRecord() {\n\t\t\tconst source = this.config.summary && this.config.summary.source\n\t\t\tif (!source || !source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(source)\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The summary stays empty; the checklist and the action still work.\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadChecklist() {\n\t\t\tconst def = this.config.checklist\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst list = def.itemsPath ? readPath(data, def.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && item.id !== undefined) : []\n\t\t\t} catch {\n\t\t\t\tthis.items = []\n\t\t\t\tthis.checklistError = t('nextcloud-vue', 'Whether this is ready could not be read.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entry of a list whose fields equal `match`, or null.\n\t\t *\n\t\t * @param {unknown} list The list.\n\t\t * @param {object} [match] The fields to match.\n\t\t * @return {object|null}\n\t\t */\n\t\tmatchIn(list, match) {\n\t\t\tconst wanted = Object.entries(match || {})\n\t\t\treturn (Array.isArray(list) ? list : []).find((entry) => entry && wanted.every(([key, value]) => String(entry[key]) === String(value))) || null\n\t\t},\n\n\t\t/**\n\t\t * A select field's options, labels translated.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {Array<{id: string, label: string, fills?: object}>}\n\t\t */\n\t\toptionsOf(field) {\n\t\t\treturn (Array.isArray(field.options) ? field.options : []).map((o) => ({ id: String(o.value), label: this.text(o.label) || String(o.value), fills: o.fills }))\n\t\t},\n\n\t\t/**\n\t\t * A select field's chosen option.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\treturn this.optionsOf(field).find((o) => o.id === String(this.values[field.field] ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Set a field's value.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetValue(name, value) {\n\t\t\tthis.values = { ...this.values, [name]: value === null || value === undefined ? '' : String(value) }\n\t\t},\n\n\t\t/**\n\t\t * Choose a select option; its `fills` set the fields still empty.\n\t\t *\n\t\t * @param {object} field The select field.\n\t\t * @param {{id: string, fills?: object}|null} option The option.\n\t\t */\n\t\tpick(field, option) {\n\t\t\tthis.setValue(field.field, option ? option.id : '')\n\t\t\tfor (const [name, value] of Object.entries((option && option.fills) || {})) {\n\t\t\t\tif (String(this.values[name] ?? '').trim() === '') {\n\t\t\t\t\tthis.setValue(name, this.text(value) || value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadPermission() {\n\t\t\tconst def = this.config.permission\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst entry = def.listPath ? this.matchIn(readPath(data, def.listPath), def.match) : data\n\t\t\t\tthis.allowed = readPath(entry, def.allowedPath || 'allowed') === true\n\t\t\t\tthis.refusal = this.allowed || !def.reasonPath ? '' : String(readPath(entry, def.reasonPath) || '')\n\t\t\t} catch {\n\t\t\t\tthis.allowed = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run the action once: post `confirm.request`, or dispatch\n\t\t * `confirm.action`, then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst reason = this.reason.trim()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, { ...(this.record || {}), reason }) : value\n\t\t\t\t\t}\n\t\t\t\t\tif (this.config.reason) {\n\t\t\t\t\t\tbody[this.config.reason.field || 'reason'] = reason\n\t\t\t\t\t}\n\t\t\t\t\tconst values = { ...this.fieldValues }\n\t\t\t\t\tfor (const field of this.fields) {\n\t\t\t\t\t\tif (field.type === 'secret' && String(this.values[field.field] ?? '') === '') {\n\t\t\t\t\t\t\tdelete values[field.field]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tObject.assign(body, values)\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.fieldValues, reason }] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The action ran. Payload: the field values and `reason`. */\n\t\t\t\tthis.$emit('done', { ...this.fieldValues, reason })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start each field with `valuePath` from the record, unless it was\n\t\t * already edited.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-secret-field/specs/action-dialog/spec.md#requirement-a-field-can-start-from-the-record-and-a-secret-is-write-only\n\t\t * @return {void}\n\t\t */\n\t\tstartValues() {\n\t\t\tif (!this.record) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst values = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tif (field.valuePath && field.type !== 'secret' && !(field.field in values)) {\n\t\t\t\t\tconst value = readPath(this.record, field.valuePath)\n\t\t\t\t\tif (value !== null && value !== undefined) {\n\t\t\t\t\t\tvalues[field.field] = String(value)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.values = values\n\t\t},\n\n\t\t/**\n\t\t * Whether a secret field has a value stored: `storedPath` reads true on the record.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tsecretStored(field) {\n\t\t\treturn field.type === 'secret' && Boolean(field.storedPath && this.record && readPath(this.record, field.storedPath))\n\t\t},\n\n\t\t/** Close the dialog, unless the action is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-action-dialog__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n}\n\n/* The record's icon on a tinted tile (DqArchiveren). */\n.cn-action-dialog__summary-icon {\n\tdisplay: flex;\n\tflex: none;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 36px;\n\theight: 36px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text);\n}\n\n.cn-action-dialog__summary-text strong {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.2;\n}\n\n.cn-action-dialog__summary-text .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__summary-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tmin-width: 0;\n}\n\n.cn-action-dialog__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-dark);\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n\n.cn-action-dialog__title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n/* Facts in columns of at least 200px, each a label over its value above a\n hairline (DqArchiveren \"Bewaring volgens het zaaktype\"). */\n.cn-action-dialog__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-action-dialog__fact {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n\tpadding-bottom: 10px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n/* The server draws every dt as a 130px right-aligned inline block and pads\n dt and dd 12px; a fact is a label over its value. */\n.cn-action-dialog__facts dt,\n.cn-action-dialog__facts dd {\n\tdisplay: block;\n\twidth: auto;\n\twhite-space: normal;\n\ttext-align: start;\n}\n\n.cn-action-dialog__facts dt {\n\tmargin: 0;\n\tpadding: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__facts dd {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n/* Checklist rows divided by hairlines, the detail at the row's end\n (DqArchiveren \"Klaar om te archiveren\"). */\n.cn-action-dialog__checks {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-action-dialog__check {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 2px 12px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 15px;\n}\n\n.cn-action-dialog__check-label {\n\tflex: 1 1 auto;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__check-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-action-dialog__check-icon--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-action-dialog__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n/* A checkbox field is a tinted box with a 2px edge, its label bold and its\n hint under it (DqArchiveren). */\n/* A note's own icon stands on the heading's line, as the variant's icon\n does (NcNoteCard centres a slotted icon on the whole note). */\n.cn-action-dialog__note-icon {\n\talign-self: flex-start;\n\tmargin-top: 1px;\n}\n\n.cn-action-dialog__checkbox {\n\tpadding: 12px 14px;\n\tborder: 2px solid var(--color-primary-element);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-main-background);\n}\n\n/* The box sits at the card's inset, 12px before the label, the label on\n its first line (DqArchiveren \"Sanne de Vries laten weten ...\"):\n NcCheckboxRadioSwitch pads its row and centres a 34px target. */\n.cn-action-dialog__checkbox :deep(.checkbox-content) {\n\tpadding: 0;\n\tgap: 12px;\n\talign-items: flex-start;\n\tmin-height: 0;\n}\n\n.cn-action-dialog__checkbox :deep(.checkbox-content__icon) {\n\tmargin: 0;\n}\n\n.cn-action-dialog__checkbox :deep(.checkbox-radio-switch__input) {\n\tmin-height: 0;\n}\n\n.cn-action-dialog__checkbox-text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-action-dialog__checkbox-label {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.2;\n}\n\n.cn-action-dialog__checkbox .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-action-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createVNode","_createBlock","_createTextVNode","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnActionDialog.vue3.js","sources":["../../../../src/components/CnActionDialog/CnActionDialog.vue?vue&type=template&id=c1f73068&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Confirm')\"\n\t\t:eyebrow=\"eyebrowText\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-action-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-action-dialog\">\n\t\t\t<!-- The record the action is about: its title, a meta line, a badge. -->\n\t\t\t<div v-if=\"summary\" class=\"cn-action-dialog__summary\" data-testid=\"cn-action-dialog-summary\">\n\t\t\t\t<span class=\"cn-action-dialog__summary-icon\">\n\t\t\t\t\t<CnIcon :name=\"config.summary.icon || 'FolderOutline'\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-action-dialog__summary-text\">\n\t\t\t\t\t<strong>{{ summary.title }}</strong>\n\t\t\t\t\t<span v-if=\"summary.meta\" class=\"cn-action-dialog__muted\">{{ summary.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"summary.badge\"\n\t\t\t\t\tclass=\"cn-action-dialog__badge\"\n\t\t\t\t\t:class=\"summary.badgeVariant ? 'cn-status-badge--' + summary.badgeVariant : ''\">{{ summary.badge }}</span>\n\t\t\t</div>\n\n\t\t\t<!-- Facts about the record, in titled groups (\"Retention under the case type\"). -->\n\t\t\t<section v-for=\"(section, index) in factSections\"\n\t\t\t\t:key=\"`facts-${index}`\"\n\t\t\t\tclass=\"cn-action-dialog__section\"\n\t\t\t\tdata-testid=\"cn-action-dialog-facts\">\n\t\t\t\t<h3 v-if=\"section.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ section.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-action-dialog__facts\">\n\t\t\t\t\t<div v-for=\"fact in section.facts\" :key=\"fact.label\" class=\"cn-action-dialog__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\n\t\t\t<!-- The checklist, read from an endpoint: each item met, not met or\n\t\t\t unknown. Items named in `required` decide whether the action may\n\t\t\t run; the others inform. -->\n\t\t\t<section v-if=\"config.checklist\" class=\"cn-action-dialog__section\" data-testid=\"cn-action-dialog-checklist\">\n\t\t\t\t<h3 v-if=\"config.checklist.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ text(config.checklist.title) }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"checklistError\" class=\"cn-action-dialog__muted\" role=\"status\">\n\t\t\t\t\t{{ checklistError }}\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cn-action-dialog__checks\">\n\t\t\t\t\t<li v-for=\"item in checklist\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-action-dialog__check\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-dialog-check-${item.id}`\"\n\t\t\t\t\t\t:data-met=\"String(item.met)\">\n\t\t\t\t\t\t<CheckCircle v-if=\"item.met === true\" :size=\"20\" class=\"cn-action-dialog__check-icon cn-action-dialog__check-icon--met\" />\n\t\t\t\t\t\t<HelpCircleOutline v-else-if=\"item.met === null\" :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<CircleOutline v-else :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__check-label\">{{ item.label }}</strong>\n\t\t\t\t\t\t<span v-if=\"item.detail\" class=\"cn-action-dialog__muted\">{{ item.detail }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\n\t\t\t<!-- Fields the action asks for, sent beside the reason. -->\n\t\t\t<div v-for=\"field in fields\"\n\t\t\t\t:key=\"field.field\"\n\t\t\t\tclass=\"cn-action-dialog__field\"\n\t\t\t\t:data-testid=\"`cn-action-dialog-field-${field.field}`\">\n\t\t\t\t<!-- A checkbox field: a tinted box, the label filled from the\n\t\t\t\t record, a hint under it (DqArchiveren \"Sanne de Vries laten\n\t\t\t\t weten...\"). -->\n\t\t\t\t<NcCheckboxRadioSwitch v-if=\"field.type === 'checkbox'\"\n\t\t\t\t\tclass=\"cn-action-dialog__checkbox\"\n\t\t\t\t\t:modelValue=\"checkboxValue(field)\"\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t@update:modelValue=\"(on) => setChecked(field.field, on)\">\n\t\t\t\t\t<span class=\"cn-action-dialog__checkbox-text\">\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__checkbox-label\">{{ fill(field.label) || field.field }}</strong>\n\t\t\t\t\t\t<span v-if=\"field.hint\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<!-- A search field: records of `source` found as the reader\n\t\t\t\t types (DqZaakKoppelen \"Zaak koppelen\"). -->\n\t\t\t\t<CnSearchSelect v-else-if=\"field.type === 'search'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || null\"\n\t\t\t\t\t:options=\"searchOptions[field.field] || []\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"true\"\n\t\t\t\t\t:loading=\"searching === field.field\"\n\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t@search=\"(typed) => onSearch(field, typed)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value && typeof value === 'object' ? value.id : value)\" />\n\t\t\t\t<!-- An optional select or text field: its label above the control,\n\t\t\t\t \"(optional)\" in grey after it (DqUwWachtrij \"Sjabloon (niet\n\t\t\t\t verplicht)\"). -->\n\t\t\t\t<CnFormField v-if=\"labelsOutside(field)\"\n\t\t\t\t\t:controlId=\"controlId(field)\"\n\t\t\t\t\t:text=\"fill(field.label) || field.field\"\n\t\t\t\t\t:optional=\"true\" />\n\t\t\t\t<NcSelect v-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsOf(field)\"\n\t\t\t\t\t:inputId=\"controlId(field)\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t:clearable=\"field.required === false\"\n\t\t\t\t\t@update:modelValue=\"(option) => pick(field, option)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type === 'secret'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\ttype=\"password\"\n\t\t\t\t\tautocomplete=\"new-password\"\n\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t:placeholder=\"secretStored(field) ? t('nextcloud-vue', 'Stored. Type a new value to replace it.') : ''\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type !== 'checkbox' && field.type !== 'search'\"\n\t\t\t\t\t:id=\"controlId(field)\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\t:type=\"field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')\"\n\t\t\t\t\t:label=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<span v-if=\"field.hint && field.type !== 'checkbox'\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.reason\" class=\"cn-action-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"reasonId\"\n\t\t\t\t\tv-model=\"reason\"\n\t\t\t\t\t:label=\"reasonLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:rows=\"config.reason.rows || 2\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-action-dialog-reason\" />\n\t\t\t\t<span v-if=\"config.reason.hint\" class=\"cn-action-dialog__muted\">{{ text(config.reason.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"config.note\"\n\t\t\t\t:type=\"noteType\"\n\t\t\t\t:heading=\"text(config.note.title)\"\n\t\t\t\tdata-testid=\"cn-action-dialog-note\">\n\t\t\t\t<template v-if=\"config.note.icon\" #icon>\n\t\t\t\t\t<CnIcon class=\"cn-action-dialog__note-icon\" :name=\"config.note.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(config.note.text) }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-action-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-action-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-action-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-action-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-action-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template v-if=\"confirmConfig.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Confirm') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CheckCircle from 'vue-material-design-icons/CheckCircle.vue'\nimport CircleOutline from 'vue-material-design-icons/CircleOutline.vue'\nimport HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'\nimport CnFormField from '../CnFormField/CnFormField.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** How long typing rests before a search field searches, in ms. */\nconst SEARCH_DELAY = 250\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** Gives each dialog's reason field its own id. */\nlet reasonSeq = 0\n\n/**\n * CnActionDialog — the generic \"summary, checklist, reason, confirm\" dialog\n * for an act on a record, configured from the manifest (an `open-modal`\n * action's props), so an app does not hand-build each one. dossiq's archive\n * dialog (board DqArchiveren) is the first: the case, what its case type\n * keeps, a readiness checklist read from an endpoint, a reason, a read-only\n * note and Archive it.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record (\"Close off · {identifier}\").\n * - `summary`: `{ source?, title, meta?, badge?, badgeVariant?, badgeColorMap?, icon? }`: templates filled from\n * the record (`source` an endpointSource to read it from, else `object`).\n * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template; a\n * `{path|<formatter>}` token reads through the built-in and app formatters.\n * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items\n * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an\n * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be\n * met before the action can run. `met: null` reads as unknown.\n * - `permission`: `{ source, listPath?, match?, allowedPath, reasonPath? }`:\n * the action runs only while `allowedPath` reads true; `reasonPath` says\n * why not. With `listPath` the paths read the entry of that list whose\n * fields equal `match` (`{ act: \"archive\" }`).\n * - `fields`: `[{ field, type?: \"text\"|\"date\"|\"datetime\"|\"select\"|\"checkbox\", label?,\n * required?, options?: [{ value, label, fills? }], hint?, default? }]`: inputs\n * sent beside the reason, required by default; a select option's `fills`\n * sets other fields that are still empty. A `checkbox` is sent as a boolean,\n * starts at `default` (false), fills its `label` and `hint` from the record,\n * and is optional unless `required: true` (then it must be ticked). A\n * `search` field finds records of `source: { register, schema }` as the\n * reader types, shows each as `optionLabel` with `optionSecondary` under\n * it (templates on the found record), leaves the record in hand out with\n * `excludeSelf`, and sends the chosen record's id. Every field's `label`,\n * `hint` and `placeholder` (search and select) are templates filled from the record.\n * An optional select or text field (`required: false`) draws its label above\n * the control with \"(optional)\" after it.\n * - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by\n * default; sent as `field` (default `reason`). `rows` is its height in lines\n * (default 2; board DqBesluit draws 4).\n * - `note`: `{ variant?, title?, text, icon? }`: a read-only note; `icon` an\n * icon name in place of the variant's own (\"LockOutline\").\n * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.\n * `request` posts itself (`body` templates filled with the record and\n * `{reason}`); `action` dispatches a manifest action through the page.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url.\n */\nexport default {\n\tname: 'CnActionDialog',\n\n\tcomponents: { CheckCircle, CircleOutline, CnDialog, CnFormField, CnIcon, CnSearchSelect, HelpCircleOutline, NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t\t/** The app's named formatters (CnAppRoot), for `{path|<formatter>}` in a template. */\n\t\tcnFormatters: { default: () => ({}) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it: the summary and facts read it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\titems: [],\n\t\t\tchecklistError: '',\n\t\t\tallowed: true,\n\t\t\treason: '',\n\t\t\tvalues: {},\n\t\t\t/** A search field's found options, by field name. */\n\t\t\tsearchOptions: {},\n\t\t\t/** The search field whose search runs, or ''. */\n\t\t\tsearching: '',\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\treasonId: `cn-action-dialog-reason-${++reasonSeq}`,\n\t\t\tfieldIdPrefix: `cn-action-dialog-field-${reasonSeq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/**\n\t\t * The summary row, or null without one or before the record is known.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {{title: string, meta: string, badge: string}|null}\n\t\t */\n\t\tsummary() {\n\t\t\tconst def = this.config.summary\n\t\t\tif (!def || !this.record) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst title = this.fill(def.title)\n\t\t\tconst badge = this.fill(def.badge)\n\t\t\tconst variants = ['default', 'success', 'warning', 'error', 'info', 'purple', 'teal']\n\t\t\tconst mapped = def.badgeColorMap && typeof def.badgeColorMap === 'object' ? def.badgeColorMap[badge] : undefined\n\t\t\tconst wanted = mapped || def.badgeVariant\n\t\t\tconst badgeVariant = variants.includes(wanted) ? wanted : ''\n\t\t\treturn title ? { title, meta: this.fill(def.meta), badge, badgeVariant } : null\n\t\t},\n\n\t\t/** @return {Array<{title: string, facts: Array<{label: string, value: string}>}>} The fact groups. */\n\t\tfactSections() {\n\t\t\tif (!Array.isArray(this.config.facts) || !this.record) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst empty = t('nextcloud-vue', 'Not set')\n\t\t\treturn this.config.facts.map((section) => ({\n\t\t\t\ttitle: this.text(section.title),\n\t\t\t\tfacts: (Array.isArray(section.items) ? section.items : []).map((fact) => ({ label: this.text(fact.label), value: this.fill(fact.value) || empty })),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The checklist rows: label and detail from `labels` by id, else the item's own.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Array<{id: string, met: (boolean|null), label: string, detail: string}>}\n\t\t */\n\t\tchecklist() {\n\t\t\tconst labels = (this.config.checklist && this.config.checklist.labels) || {}\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst met = item.met === null || item.met === undefined ? null : item.met === true\n\t\t\t\tconst own = labels[item.id] || {}\n\t\t\t\tconst detail = met === false && own.unmetDetail ? own.unmetDetail : (own.detail || item.detail || '')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tmet,\n\t\t\t\t\tlabel: this.fill(own.label || item.label || String(item.id), item),\n\t\t\t\t\tdetail: met === null && !item.detail ? t('nextcloud-vue', 'Could not be read') : this.fill(detail, item),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether every required item is met. */\n\t\trequiredMet() {\n\t\t\tconst required = (this.config.checklist && Array.isArray(this.config.checklist.required)) ? this.config.checklist.required.map(String) : []\n\t\t\treturn required.every((id) => this.items.some((item) => String(item.id) === id && item.met === true))\n\t\t},\n\n\t\t/** @return {boolean} Whether a reason is asked and needed. */\n\t\treasonNeeded() {\n\t\t\treturn Boolean(this.config.reason) && this.config.reason.required !== false\n\t\t},\n\n\t\t/** @return {string} The reason field's label. */\n\t\treasonLabel() {\n\t\t\treturn this.text(this.config.reason && this.config.reason.label) || t('nextcloud-vue', 'Reason')\n\t\t},\n\n\t\t/** @return {string} The note's NcNoteCard type. */\n\t\tnoteType() {\n\t\t\tconst variant = this.config.note && this.config.note.variant\n\t\t\treturn ['info', 'warning', 'error', 'success'].includes(variant) ? variant : 'info'\n\t\t},\n\n\t\t/**\n\t\t * Whether the action may run: allowed, required items met, a reason\n\t\t * when one is needed, and nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.allowed && this.requiredMet && this.fieldsFilled && (!this.reasonNeeded || this.reason.trim() !== '')\n\t\t},\n\n\t\t/** @return {string} The eyebrow, filled from the record once it is known. */\n\t\teyebrowText() {\n\t\t\treturn this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)\n\t\t},\n\n\t\t/**\n\t\t * The value of every field as sent: a checkbox as a boolean, the\n\t\t * others as entered ('' when empty).\n\t\t *\n\t\t * @return {object} Values by field name.\n\t\t */\n\t\tfieldValues() {\n\t\t\tconst out = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tout[field.field] = field.type === 'checkbox' ? this.checkboxValue(field) : (this.values[field.field] ?? '')\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/** @return {Array<object>} The fields with a name. */\n\t\tfields() {\n\t\t\treturn (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)\n\t\t},\n\n\t\t/**\n\t\t * Whether every required field has a value.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-fields-and-acts/specs/action-dialog/spec.md#requirement-an-action-dialog-asks-for-fields-and-reads-a-permission-from-a-list\n\t\t * @return {boolean}\n\t\t */\n\t\tfieldsFilled() {\n\t\t\treturn this.fields.every((f) => {\n\t\t\t\tif (f.type === 'checkbox') {\n\t\t\t\t\treturn f.required !== true || this.checkboxValue(f)\n\t\t\t\t}\n\t\t\t\treturn f.required === false || this.secretStored(f) || String(this.values[f.field] ?? '').trim() !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\trecord: {\n\t\t\thandler() {\n\t\t\t\tthis.startValues()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether a field's label sits above its control with \"(optional)\" after\n\t\t * it: a select or a text field the action does not require.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-optional-fields/specs/action-dialog/spec.md#requirement-an-optional-field-says-so-beside-its-label\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tlabelsOutside(field) {\n\t\t\treturn field.required === false && !['checkbox', 'search', 'secret'].includes(field.type)\n\t\t},\n\n\t\t/**\n\t\t * The id of a field's control, which its outside label points at.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {string}\n\t\t */\n\t\tcontrolId(field) {\n\t\t\treturn `${this.fieldIdPrefix}-${field.field}`\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\t/**\n\t\t * Search a search field's records once typing rests.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} typed What the reader typed.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/action-dialog-search-field/specs/action-dialog-search-field/spec.md#requirement-an-action-dialog-field-can-search-records\n\t\t */\n\t\tonSearch(field, typed) {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.searchRecords(field, String(typed || '').trim()), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Read the records of a search field's `source` that match the term.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} term The term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync searchRecords(field, term) {\n\t\t\tconst source = field.source || {}\n\t\t\tif (!term || !source.register || !source.schema) {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.searching = field.field\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(source.register)}/${encodeURIComponent(source.schema)}`)\n\t\t\t\tconst { data } = await axios.get(url, { params: { _search: term, _limit: 10 } })\n\t\t\t\tconst rows = Array.isArray(data) ? data : (data && Array.isArray(data.results) ? data.results : [])\n\t\t\t\tconst self = String(this.recordId || '')\n\t\t\t\tconst options = rows\n\t\t\t\t\t.map((row) => ({ row, id: String(row.id || (row['@self'] && row['@self'].id) || '') }))\n\t\t\t\t\t.filter(({ id }) => id !== '' && !(field.excludeSelf && id === self))\n\t\t\t\t\t.map(({ row, id }) => {\n\t\t\t\t\t\tconst option = { id, label: this.fill(field.optionLabel || '{title}', row) || id }\n\t\t\t\t\t\tconst secondary = this.fill(field.optionSecondary, row)\n\t\t\t\t\t\treturn secondary ? { ...option, secondary } : option\n\t\t\t\t\t})\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: options }\n\t\t\t} catch {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t} finally {\n\t\t\t\tthis.searching = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A checkbox field's state: what was ticked, else its `default`.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/action-dialog-checkbox-field/specs/action-dialog-checkbox-field/spec.md#requirement-an-action-dialog-field-can-be-a-checkbox\n\t\t */\n\t\tcheckboxValue(field) {\n\t\t\treturn field.field in this.values ? this.values[field.field] === true : field.default === true\n\t\t},\n\n\t\t/**\n\t\t * Tick or untick a checkbox field.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {boolean} on Ticked.\n\t\t * @return {void}\n\t\t */\n\t\tsetChecked(name, on) {\n\t\t\tthis.values = { ...this.values, [name]: on === true }\n\t\t},\n\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row (the record by default), translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} [row] The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, row) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst formatters = { ...BUILT_IN_FORMATTERS, ...(this.cnFormatters || {}) }\n\t\t\treturn fillTemplate(this.cnTranslate(template), row || this.record || {}, new Date(), { formatters }).trim()\n\t\t},\n\n\t\t/**\n\t\t * Read an endpoint, fresh.\n\t\t *\n\t\t * @param {object} source An endpointSource block.\n\t\t * @return {Promise<unknown>}\n\t\t */\n\t\tread(source) {\n\t\t\treturn fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadRecord() {\n\t\t\tconst source = this.config.summary && this.config.summary.source\n\t\t\tif (!source || !source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(source)\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The summary stays empty; the checklist and the action still work.\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadChecklist() {\n\t\t\tconst def = this.config.checklist\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst list = def.itemsPath ? readPath(data, def.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && item.id !== undefined) : []\n\t\t\t} catch {\n\t\t\t\tthis.items = []\n\t\t\t\tthis.checklistError = t('nextcloud-vue', 'Whether this is ready could not be read.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entry of a list whose fields equal `match`, or null.\n\t\t *\n\t\t * @param {unknown} list The list.\n\t\t * @param {object} [match] The fields to match.\n\t\t * @return {object|null}\n\t\t */\n\t\tmatchIn(list, match) {\n\t\t\tconst wanted = Object.entries(match || {})\n\t\t\treturn (Array.isArray(list) ? list : []).find((entry) => entry && wanted.every(([key, value]) => String(entry[key]) === String(value))) || null\n\t\t},\n\n\t\t/**\n\t\t * A select field's options, labels translated.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {Array<{id: string, label: string, fills?: object}>}\n\t\t */\n\t\toptionsOf(field) {\n\t\t\treturn (Array.isArray(field.options) ? field.options : []).map((o) => ({ id: String(o.value), label: this.text(o.label) || String(o.value), fills: o.fills }))\n\t\t},\n\n\t\t/**\n\t\t * A select field's chosen option.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\treturn this.optionsOf(field).find((o) => o.id === String(this.values[field.field] ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Set a field's value.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetValue(name, value) {\n\t\t\tthis.values = { ...this.values, [name]: value === null || value === undefined ? '' : String(value) }\n\t\t},\n\n\t\t/**\n\t\t * Choose a select option; its `fills` set the fields still empty.\n\t\t *\n\t\t * @param {object} field The select field.\n\t\t * @param {{id: string, fills?: object}|null} option The option.\n\t\t */\n\t\tpick(field, option) {\n\t\t\tthis.setValue(field.field, option ? option.id : '')\n\t\t\tfor (const [name, value] of Object.entries((option && option.fills) || {})) {\n\t\t\t\tif (String(this.values[name] ?? '').trim() === '') {\n\t\t\t\t\tthis.setValue(name, this.text(value) || value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadPermission() {\n\t\t\tconst def = this.config.permission\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst entry = def.listPath ? this.matchIn(readPath(data, def.listPath), def.match) : data\n\t\t\t\tthis.allowed = readPath(entry, def.allowedPath || 'allowed') === true\n\t\t\t\tthis.refusal = this.allowed || !def.reasonPath ? '' : String(readPath(entry, def.reasonPath) || '')\n\t\t\t} catch {\n\t\t\t\tthis.allowed = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run the action once: post `confirm.request`, or dispatch\n\t\t * `confirm.action`, then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst reason = this.reason.trim()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, { ...(this.record || {}), reason }) : value\n\t\t\t\t\t}\n\t\t\t\t\tif (this.config.reason) {\n\t\t\t\t\t\tbody[this.config.reason.field || 'reason'] = reason\n\t\t\t\t\t}\n\t\t\t\t\tconst values = { ...this.fieldValues }\n\t\t\t\t\tfor (const field of this.fields) {\n\t\t\t\t\t\tif (field.type === 'secret' && String(this.values[field.field] ?? '') === '') {\n\t\t\t\t\t\t\tdelete values[field.field]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tObject.assign(body, values)\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.fieldValues, reason }] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The action ran. Payload: the field values and `reason`. */\n\t\t\t\tthis.$emit('done', { ...this.fieldValues, reason })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start each field with `valuePath` from the record, unless it was\n\t\t * already edited.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-secret-field/specs/action-dialog/spec.md#requirement-a-field-can-start-from-the-record-and-a-secret-is-write-only\n\t\t * @return {void}\n\t\t */\n\t\tstartValues() {\n\t\t\tif (!this.record) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst values = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tif (field.valuePath && field.type !== 'secret' && !(field.field in values)) {\n\t\t\t\t\tconst value = readPath(this.record, field.valuePath)\n\t\t\t\t\tif (value !== null && value !== undefined) {\n\t\t\t\t\t\tvalues[field.field] = String(value)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.values = values\n\t\t},\n\n\t\t/**\n\t\t * Whether a secret field has a value stored: `storedPath` reads true on the record.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tsecretStored(field) {\n\t\t\treturn field.type === 'secret' && Boolean(field.storedPath && this.record && readPath(this.record, field.storedPath))\n\t\t},\n\n\t\t/** Close the dialog, unless the action is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-action-dialog__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n}\n\n/* The record's icon on a tinted tile (DqArchiveren). */\n.cn-action-dialog__summary-icon {\n\tdisplay: flex;\n\tflex: none;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 36px;\n\theight: 36px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text);\n}\n\n.cn-action-dialog__summary-text strong {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.2;\n}\n\n.cn-action-dialog__summary-text .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__summary-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tmin-width: 0;\n}\n\n.cn-action-dialog__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tline-height: normal;\n\twhite-space: nowrap;\n}\n\n/* Without a tone (`badgeVariant`, `badgeColorMap`) the badge is neutral. */\n.cn-action-dialog__badge:not([class*='cn-status-badge--']) {\n\tbackground: var(--color-background-dark);\n}\n\n.cn-action-dialog__title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n/* Facts in columns of at least 200px, each a label over its value above a\n hairline (DqArchiveren \"Bewaring volgens het zaaktype\"). */\n.cn-action-dialog__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-action-dialog__fact {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n\tpadding-bottom: 10px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n/* The server draws every dt as a 130px right-aligned inline block and pads\n dt and dd 12px; a fact is a label over its value. */\n.cn-action-dialog__facts dt,\n.cn-action-dialog__facts dd {\n\tdisplay: block;\n\twidth: auto;\n\twhite-space: normal;\n\ttext-align: start;\n}\n\n.cn-action-dialog__facts dt {\n\tmargin: 0;\n\tpadding: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__facts dd {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n/* Checklist rows divided by hairlines, the detail at the row's end\n (DqArchiveren \"Klaar om te archiveren\"). */\n.cn-action-dialog__checks {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-action-dialog__check {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 2px 12px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 15px;\n}\n\n.cn-action-dialog__check-label {\n\tflex: 1 1 auto;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__check-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-action-dialog__check-icon--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-action-dialog__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n/* A checkbox field is a tinted box with a 2px edge, its label bold and its\n hint under it (DqArchiveren). */\n/* A note's own icon stands on the heading's line, as the variant's icon\n does (NcNoteCard centres a slotted icon on the whole note). */\n.cn-action-dialog__note-icon {\n\talign-self: flex-start;\n\tmargin-top: 1px;\n}\n\n.cn-action-dialog__checkbox {\n\tpadding: 12px 14px;\n\tborder: 2px solid var(--color-primary-element);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-main-background);\n}\n\n/* The box sits at the card's inset, 12px before the label, the label on\n its first line (DqArchiveren \"Sanne de Vries laten weten ...\"):\n NcCheckboxRadioSwitch pads its row and centres a 34px target. */\n.cn-action-dialog__checkbox :deep(.checkbox-content) {\n\tpadding: 0;\n\tgap: 12px;\n\talign-items: flex-start;\n\tmin-height: 0;\n}\n\n.cn-action-dialog__checkbox :deep(.checkbox-content__icon) {\n\tmargin: 0;\n}\n\n.cn-action-dialog__checkbox :deep(.checkbox-radio-switch__input) {\n\tmin-height: 0;\n}\n\n.cn-action-dialog__checkbox-text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-action-dialog__checkbox-label {\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tline-height: 1.2;\n}\n\n.cn-action-dialog__checkbox .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-action-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createVNode","_createBlock","_createTextVNode","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_normalizeClass","_Fragment","_renderList"],"mappings":"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"}
@@ -2,7 +2,7 @@ import script from './CnConversationThread.vue2.js';
2
2
  import { render } from './CnConversationThread.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-e2600a68";
5
+ script.__scopeId = "data-v-40f702bd";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -2,7 +2,8 @@ import axios__default from '@nextcloud/axios';
2
2
  import { translate } from '@nextcloud/l10n';
3
3
  import { NcButton } from '@nextcloud/vue';
4
4
  import Send from 'vue-material-design-icons/Send.vue';
5
- import __sfc_main from '../CnQuickFilterBar/CnQuickFilterBar.vue.js';
5
+ import __sfc_main from '../CnIcon/CnIcon.vue.js';
6
+ import __sfc_main$1 from '../CnQuickFilterBar/CnQuickFilterBar.vue.js';
6
7
  import { prefixUrl } from '../../utils/headers.js';
7
8
  import { userLocale } from '../../utils/userLocale.js';
8
9
 
@@ -36,7 +37,8 @@ var script = {
36
37
  name: 'CnConversationThread',
37
38
 
38
39
  components: {
39
- CnQuickFilterBar: __sfc_main,
40
+ CnIcon: __sfc_main,
41
+ CnQuickFilterBar: __sfc_main$1,
40
42
  NcButton,
41
43
  Send,
42
44
  },
@@ -48,7 +50,7 @@ var script = {
48
50
 
49
51
  props: {
50
52
  /**
51
- * The messages, oldest first: `{ id?, author?, time?, text, side?, channel?, status?, actions?, note? }`.
53
+ * The messages, oldest first: `{ id?, author?, time?, text, side?, channel?, channelIcon?, status?, actions?, note? }`.
52
54
  * `status` is shown after the channel ("Failed"); `actions` are
53
55
  * `{ id, label, disabled? }` buttons under the text, each emitting
54
56
  * `message-action`.
@@ -232,6 +234,7 @@ var script = {
232
234
  timeLabel: time.label,
233
235
  datetime: time.datetime,
234
236
  channel: typeof message.channel === 'string' ? message.channel : '',
237
+ channelIcon: typeof message.channelIcon === 'string' ? message.channelIcon : '',
235
238
  status: typeof message.status === 'string' ? message.status : '',
236
239
  actions: Array.isArray(message.actions) ? message.actions.filter((action) => action && action.id && action.label) : [],
237
240
  directionLabel: side === 'us' ? translate('nextcloud-vue', 'Sent message') : translate('nextcloud-vue', 'Received message'),
@@ -1 +1 @@
1
- {"version":3,"file":"CnConversationThread.vue2.js","sources":["../../../../src/components/CnConversationThread/CnConversationThread.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tclass=\"cn-conversation-thread\"\n\t\tdata-testid=\"cn-conversation-thread\"\n\t\t:aria-labelledby=\"title ? titleId : null\"\n\t\t:aria-label=\"title ? null : fallbackLabel\">\n\t\t<div v-if=\"title || $slots['header-actions']\" class=\"cn-conversation-thread__header\">\n\t\t\t<div v-if=\"title\" class=\"cn-conversation-thread__heading\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"titleTag\"\n\t\t\t\t\t:id=\"titleId\"\n\t\t\t\t\tclass=\"cn-conversation-thread__title\">\n\t\t\t\t\t{{ title }}\n\t\t\t\t</component>\n\t\t\t\t<!-- A div, not a <p>: a theme that sets every paragraph's size with\n\t\t\t\t !important would otherwise override the 14px subtitle. -->\n\t\t\t\t<div v-if=\"subtitle\" class=\"cn-conversation-thread__subtitle\" data-testid=\"cn-conversation-subtitle\">\n\t\t\t\t\t{{ subtitle }}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div v-if=\"$slots['header-actions']\" class=\"cn-conversation-thread__header-actions\">\n\t\t\t\t<!-- @slot header-actions Buttons beside the title, for example \"Log contact\". -->\n\t\t\t\t<slot name=\"header-actions\" />\n\t\t\t</div>\n\t\t</div>\n\n\t\t<CnQuickFilterBar\n\t\t\tv-if=\"channelChips.length > 0\"\n\t\t\tinline\n\t\t\tclass=\"cn-conversation-thread__channels\"\n\t\t\t:tabs=\"channelChips\"\n\t\t\t:activeIndex=\"activeChannelIndex\"\n\t\t\t@update:activeIndex=\"onChannelChip\" />\n\n\t\t<p v-if=\"loading && rows.length === 0\"\n\t\t\tclass=\"cn-conversation-thread__empty\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-conversation-loading\">\n\t\t\t{{ loadingText }}\n\t\t</p>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-conversation-thread__empty\" data-testid=\"cn-conversation-empty\">\n\t\t\t{{ emptyText }}\n\t\t</p>\n\n\t\t<p v-else-if=\"shownRows.length === 0\" class=\"cn-conversation-thread__empty\" data-testid=\"cn-conversation-channel-empty\">\n\t\t\t{{ emptyText }}\n\t\t</p>\n\n\t\t<ol v-else class=\"cn-conversation-thread__messages\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in shownRows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-conversation-thread__message\"\n\t\t\t\t:class=\"`cn-conversation-thread__message--${row.side}`\"\n\t\t\t\tdata-testid=\"cn-conversation-message\"\n\t\t\t\t:data-side=\"row.side\">\n\t\t\t\t<span class=\"cn-conversation-thread__avatar\" aria-hidden=\"true\">{{ row.initials }}</span>\n\t\t\t\t<div class=\"cn-conversation-thread__bubble\">\n\t\t\t\t\t<span class=\"cn-conversation-thread__meta\">\n\t\t\t\t\t\t<span class=\"hidden-visually\">{{ row.directionLabel }}</span>\n\t\t\t\t\t\t<span class=\"cn-conversation-thread__author\">{{ row.author }}</span>\n\t\t\t\t\t\t<span v-if=\"row.channel\" class=\"cn-conversation-thread__channel\" data-testid=\"cn-conversation-channel\">{{ row.channel }}</span>\n\t\t\t\t\t\t<span v-if=\"row.status\" class=\"cn-conversation-thread__status\" data-testid=\"cn-conversation-status\">{{ row.status }}</span>\n\t\t\t\t\t\t<span v-if=\"showDirection\"\n\t\t\t\t\t\t\tclass=\"cn-conversation-thread__direction\"\n\t\t\t\t\t\t\tdata-testid=\"cn-conversation-direction\"\n\t\t\t\t\t\t\taria-hidden=\"true\">{{ row.directionWord }}</span>\n\t\t\t\t\t\t<time v-if=\"row.timeLabel\" :datetime=\"row.datetime || null\">{{ row.timeLabel }}</time>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-conversation-thread__text\">{{ row.text }}</span>\n\t\t\t\t\t<span v-if=\"row.note\" class=\"cn-conversation-thread__note\" data-testid=\"cn-conversation-note\">{{ row.note }}</span>\n\t\t\t\t\t<span v-if=\"row.actions.length > 0\" class=\"cn-conversation-thread__message-actions\">\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-for=\"action in row.actions\"\n\t\t\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t:disabled=\"action.disabled === true\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-conversation-action-${action.id}`\"\n\t\t\t\t\t\t\t@click=\"$emit('message-action', { action: action.id, message: row.key })\">\n\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ol>\n\n\t\t<form\n\t\t\tv-if=\"allowReply\"\n\t\t\tclass=\"cn-conversation-thread__reply\"\n\t\t\tdata-testid=\"cn-conversation-reply\"\n\t\t\t@submit.prevent=\"onSend\">\n\t\t\t<!-- @slot reply-tools Tools above the reply box, for example a template picker. -->\n\t\t\t<!-- @binding {Function} setDraft Puts a text in the reply box. -->\n\t\t\t<slot name=\"reply-tools\" :setDraft=\"setDraft\" />\n\t\t\t<div class=\"cn-conversation-thread__reply-head\">\n\t\t\t\t<label :for=\"inputId\" class=\"cn-conversation-thread__reply-label\">{{ replyLabel }}</label>\n\t\t\t\t<span v-if=\"replyHint\" class=\"cn-conversation-thread__reply-hint\" data-testid=\"cn-conversation-reply-hint\">{{ replyHint }}</span>\n\t\t\t</div>\n\t\t\t<textarea\n\t\t\t\t:id=\"inputId\"\n\t\t\t\tv-model=\"draft\"\n\t\t\t\tclass=\"cn-conversation-thread__input\"\n\t\t\t\trows=\"2\"\n\t\t\t\t:placeholder=\"placeholder || null\"\n\t\t\t\t:disabled=\"sending\"\n\t\t\t\t:aria-describedby=\"errorMessage ? errorId : null\"\n\t\t\t\tdata-testid=\"cn-conversation-input\"\n\t\t\t\t@keydown.ctrl.enter.prevent=\"onSend\"\n\t\t\t\t@keydown.meta.enter.prevent=\"onSend\" />\n\t\t\t<p\n\t\t\t\tv-if=\"errorMessage\"\n\t\t\t\t:id=\"errorId\"\n\t\t\t\tclass=\"cn-conversation-thread__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-conversation-error\">\n\t\t\t\t{{ errorMessage }}\n\t\t\t</p>\n\t\t\t<!-- The option at the start and the buttons at the end of one row. -->\n\t\t\t<div class=\"cn-conversation-thread__reply-foot\">\n\t\t\t\t<label v-if=\"optionShown\" class=\"cn-conversation-thread__reply-option\" data-testid=\"cn-conversation-reply-option\">\n\t\t\t\t\t<input v-model=\"optionOn\" type=\"checkbox\" :disabled=\"sending\">\n\t\t\t\t\t<span>{{ replyOption.label }}</span>\n\t\t\t\t</label>\n\t\t\t\t<div class=\"cn-conversation-thread__reply-actions\">\n\t\t\t\t\t<!-- @slot reply-actions Extra buttons before Send, for example \"Save draft\". -->\n\t\t\t\t\t<!-- @binding {string} draft The text in the reply box. -->\n\t\t\t\t\t<slot name=\"reply-actions\" :draft=\"draft\" />\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=\"!canSend\"\n\t\t\t\t\t\tdata-testid=\"cn-conversation-send\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Send :size=\"16\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ sendLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t</section>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnQuickFilterBar from '../CnQuickFilterBar/CnQuickFilterBar.vue'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\nlet conversationThreadUid = 0\n\n/**\n * CnConversationThread: the messages between you and the other party, and a box to answer.\n *\n * Each message has an author, a time and a side: `us` for the organisation,\n * `them` for the resident or customer. The reply box emits `send`, and also\n * posts to `endpoint` when one is set. It needs no Talk and no other app:\n * where the messages are stored is the host's business.\n *\n * ```vue\n * <CnConversationThread\n * title=\"Contact with the resident\"\n * reply-label=\"Answer to the resident\"\n * :messages=\"[\n * { id: 1, author: 'Sanne de Vries', time: '2026-10-04T10:52:00', side: 'them',\n * text: 'Thank you. I would like to see all documents.' },\n * { id: 2, time: '2026-10-04T10:24:00', side: 'us',\n * text: 'Construction starts in March.' },\n * ]\"\n * @send=\"saveReply\" />\n * ```\n *\n * The `conversation` detail widget type renders this thread from a field on\n * the record.\n */\nexport default {\n\tname: 'CnConversationThread',\n\n\tcomponents: {\n\t\tCnQuickFilterBar,\n\t\tNcButton,\n\t\tSend,\n\t},\n\n\tinject: {\n\t\t/** The host app's translate, for the All chip in the host's word. */\n\t\tcnTranslate: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The messages, oldest first: `{ id?, author?, time?, text, side?, channel?, status?, actions?, note? }`.\n\t\t * `status` is shown after the channel (\"Failed\"); `actions` are\n\t\t * `{ id, label, disabled? }` buttons under the text, each emitting\n\t\t * `message-action`.\n\t\t * `note` is a muted line under the text.\n\t\t * `side` is `us` or `them` (default `them`). `time` is an ISO\n\t\t * date, or any text to show as is. `channel` is shown after the\n\t\t * author (\"E-mail\", \"Phone note\"); `note` is a muted line under the text.\n\t\t */\n\t\tmessages: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Heading of the thread. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** A line under the title, such as \"Oldest first, with the channel of each message\". */\n\t\tsubtitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Say each message's direction in words before its time (\"Incoming · 3 Oct\"), not only to a screen reader. */\n\t\tshowDirection: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Element the heading renders as. */\n\t\ttitleTag: {\n\t\t\ttype: String,\n\t\t\tdefault: 'h3',\n\t\t},\n\n\t\t/** A line beside the reply label saying where the answer goes, such as \"Goes to the message box in the portal\". */\n\t\treplyHint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * A switch under the reply box whose state is sent with the reply:\n\t\t * `{ label, key, value? }`, `value` the starting state (on by default).\n\t\t * The reply's `send` event carries `{ [key]: state }` after the text.\n\t\t *\n\t\t * @type {{label: string, key: string, value?: boolean}|null}\n\t\t */\n\t\treplyOption: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether the reply box renders. Turn it off for a closed or read-only record. */\n\t\tallowReply: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Label of the reply box. */\n\t\treplyLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Reply'),\n\t\t},\n\n\t\t/** Placeholder inside the reply box. */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label of the send button. */\n\t\tsendLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Send'),\n\t\t},\n\n\t\t/** Author shown on an `us` message that names none. */\n\t\tusLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'You'),\n\t\t},\n\n\t\t/**\n\t\t * URL the reply is POSTed to as `{ message }`. Empty means the host\n\t\t * handles `send` itself and nothing is requested.\n\t\t */\n\t\tendpoint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Extra fields sent along with `message` when `endpoint` is set. */\n\t\tpayload: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether the messages are still loading; with none yet the thread says so. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Draw channel chips above the messages: All, then one per channel\n\t\t * with its count. Choosing one shows only that channel's messages.\n\t\t */\n\t\tchannelFilter: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Channel labels that always get a chip, in this order, also when no\n\t\t * message uses them yet. Channels the messages use follow.\n\t\t */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Shown when there are no messages yet. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No messages yet.'),\n\t\t},\n\t},\n\n\temits: [\n\t\t/** A reply was submitted. Payload: the text. */\n\t\t'send',\n\t\t/** The endpoint accepted the reply. Payload: the response body. */\n\t\t'sent',\n\t\t/** The endpoint refused the reply. Payload: the error. */\n\t\t'error',\n\t\t/** A message action was pressed. Payload: `{ action, message }`, the action id and the message key. */\n\t\t'message-action',\n\t],\n\n\tdata() {\n\t\tconversationThreadUid += 1\n\t\treturn {\n\t\t\tdraft: '',\n\t\t\t/** The state of the reply option's switch. */\n\t\t\toptionOn: this.replyOption?.value !== false,\n\t\t\t/** The channel the chips narrow the thread to; '' is All. */\n\t\t\tactiveChannel: '',\n\t\t\tsending: false,\n\t\t\terrorMessage: '',\n\t\t\ttitleId: `cn-conversation-title-${conversationThreadUid}`,\n\t\t\tinputId: `cn-conversation-input-${conversationThreadUid}`,\n\t\t\terrorId: `cn-conversation-error-${conversationThreadUid}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The messages, with author, side, time and initials resolved.\n\t\t *\n\t\t * @return {Array<{key: string, author: string, initials: string, side: string, text: string, timeLabel: string, datetime: string, channel: string, directionLabel: string, directionWord: string, note: string}>} The rows.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\trows() {\n\t\t\treturn (Array.isArray(this.messages) ? this.messages : [])\n\t\t\t\t.filter((message) => message && typeof message === 'object')\n\t\t\t\t.map((message, index) => {\n\t\t\t\t\tconst side = message.side === 'us' ? 'us' : 'them'\n\t\t\t\t\tconst author = typeof message.author === 'string' && message.author !== ''\n\t\t\t\t\t\t? message.author\n\t\t\t\t\t\t: (side === 'us' ? this.usLabel : '')\n\t\t\t\t\tconst time = this.formatTime(message.time)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: message.id === undefined || message.id === null ? `message-${index}` : String(message.id),\n\t\t\t\t\t\tauthor,\n\t\t\t\t\t\tinitials: this.initialsOf(author),\n\t\t\t\t\t\tside,\n\t\t\t\t\t\ttext: message.text === undefined || message.text === null ? '' : String(message.text),\n\t\t\t\t\t\ttimeLabel: time.label,\n\t\t\t\t\t\tdatetime: time.datetime,\n\t\t\t\t\t\tchannel: typeof message.channel === 'string' ? message.channel : '',\n\t\t\t\t\t\tstatus: typeof message.status === 'string' ? message.status : '',\n\t\t\t\t\t\tactions: Array.isArray(message.actions) ? message.actions.filter((action) => action && action.id && action.label) : [],\n\t\t\t\t\t\tdirectionLabel: side === 'us' ? t('nextcloud-vue', 'Sent message') : t('nextcloud-vue', 'Received message'),\n\t\t\t\t\t\tdirectionWord: side === 'us' ? t('nextcloud-vue', 'Outgoing') : t('nextcloud-vue', 'Incoming'),\n\t\t\t\t\t\tnote: typeof message.note === 'string' ? message.note : '',\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The channel labels with a chip: the declared ones, then the ones\n\t\t * the messages use.\n\t\t *\n\t\t * @return {Array<string>} The labels, each once.\n\t\t */\n\t\tchannelLabels() {\n\t\t\tconst declared = (Array.isArray(this.channels) ? this.channels : []).filter((label) => typeof label === 'string' && label !== '')\n\t\t\treturn [...new Set([...declared, ...this.rows.map((row) => row.channel).filter(Boolean)])]\n\t\t},\n\n\t\t/**\n\t\t * The chips: All, then each channel, each with its count. Empty\n\t\t * without `channelFilter` or without channels.\n\t\t *\n\t\t * @return {Array<{label: string, count: number}>} The chips.\n\t\t */\n\t\tchannelChips() {\n\t\t\tif (!this.channelFilter || this.channelLabels.length === 0) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{ label: this.allLabel, count: this.rows.length },\n\t\t\t\t...this.channelLabels.map((label) => ({ label, count: this.rows.filter((row) => row.channel === label).length })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * Whether the reply option renders: it has a label and a key.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/conversation-reply-option/specs/conversation-source/spec.md#requirement-a-reply-can-carry-a-hint-and-an-option\n\t\t */\n\t\toptionShown() {\n\t\t\tconst option = this.replyOption\n\t\t\treturn Boolean(option && typeof option.label === 'string' && option.label !== '' && typeof option.key === 'string' && option.key !== '')\n\t\t},\n\n\t\t/**\n\t\t * What travels with the reply besides its text.\n\t\t *\n\t\t * @return {object} `{ [key]: state }`, or {} without an option.\n\t\t * @spec openspec/changes/conversation-reply-option/specs/conversation-source/spec.md#requirement-a-reply-can-carry-a-hint-and-an-option\n\t\t */\n\t\treplyExtras() {\n\t\t\treturn this.optionShown ? { [this.replyOption.key]: this.optionOn === true } : {}\n\t\t},\n\n\t\t/**\n\t\t * The All chip: the host's word first, as its own filter chips read\n\t\t * (\"Alles\"), then the library's.\n\t\t *\n\t\t * @return {string} The label.\n\t\t * @spec openspec/changes/conversation-all-chip-host-word/specs/conversation-source/spec.md#requirement-the-all-chip-reads-the-hosts-word\n\t\t */\n\t\tallLabel() {\n\t\t\tconst host = typeof this.cnTranslate === 'function' ? this.cnTranslate('All') : 'All'\n\t\t\treturn typeof host === 'string' && host !== '' && host !== 'All' ? host : t('nextcloud-vue', 'All')\n\t\t},\n\n\t\t/** @return {number} The pressed chip: 0 for All. */\n\t\tactiveChannelIndex() {\n\t\t\tconst index = this.channelLabels.indexOf(this.activeChannel)\n\t\t\treturn index < 0 ? 0 : index + 1\n\t\t},\n\n\t\t/** @return {Array<object>} The rows on the chosen channel, or all of them. */\n\t\tshownRows() {\n\t\t\tif (this.channelChips.length === 0 || this.activeChannelIndex === 0) {\n\t\t\t\treturn this.rows\n\t\t\t}\n\t\t\treturn this.rows.filter((row) => row.channel === this.activeChannel)\n\t\t},\n\n\t\t/**\n\t\t * Whether there is something to send and nothing in flight.\n\t\t *\n\t\t * @return {boolean} True when Send is enabled.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tcanSend() {\n\t\t\treturn !this.sending && this.draft.trim() !== ''\n\t\t},\n\n\t\t/**\n\t\t * Accessible name for a thread without a title.\n\t\t *\n\t\t * @return {string} The label.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tfallbackLabel() {\n\t\t\treturn t('nextcloud-vue', 'Conversation')\n\t\t},\n\n\t\t/** @return {string} What the thread says while its messages load. */\n\t\tloadingText() {\n\t\t\treturn t('nextcloud-vue', 'Loading messages…')\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Put a text in the reply box (a template a host tool picked).\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {void}\n\t\t */\n\t\tsetDraft(text) {\n\t\t\tthis.draft = typeof text === 'string' ? text : ''\n\t\t},\n\n\t\t/**\n\t\t * Narrow the thread to the chip's channel; the All chip (0) clears it.\n\t\t *\n\t\t * @param {number|null} index The chip index.\n\t\t * @return {void}\n\t\t */\n\t\tonChannelChip(index) {\n\t\t\tthis.activeChannel = index > 0 ? (this.channelLabels[index - 1] || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * Up to two initials of a name, for the avatar.\n\t\t *\n\t\t * @param {string} name The author.\n\t\t * @return {string} The initials, upper case.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tinitialsOf(name) {\n\t\t\tconst parts = String(name || '').trim().split(/\\s+/).filter(Boolean)\n\t\t\tif (parts.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst first = parts[0].charAt(0)\n\t\t\tconst last = parts.length > 1 ? parts[parts.length - 1].charAt(0) : ''\n\t\t\treturn (first + last).toUpperCase()\n\t\t},\n\n\t\t/**\n\t\t * Turn a message time into what the reader sees and what `<time>` carries.\n\t\t *\n\t\t * @param {unknown} value An ISO date, a Date, or text to show as is.\n\t\t * @return {{label: string, datetime: string}} The visible label and the machine value.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tformatTime(value) {\n\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\treturn { label: '', datetime: '' }\n\t\t\t}\n\t\t\tconst date = value instanceof Date ? value : new Date(value)\n\t\t\tconst looksLikeDate = value instanceof Date || typeof value === 'number' || /^\\d{4}-\\d{2}-\\d{2}/.test(String(value))\n\t\t\tif (!looksLikeDate || Number.isNaN(date.getTime())) {\n\t\t\t\treturn { label: String(value), datetime: '' }\n\t\t\t}\n\t\t\tconst label = date.toLocaleString(userLocale(), { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })\n\t\t\treturn { label, datetime: date.toISOString() }\n\t\t},\n\n\t\t/**\n\t\t * Submit the reply: tell the host, and post it when an endpoint is set.\n\t\t *\n\t\t * Without an endpoint the box clears straight away, because the host\n\t\t * owns the send. With one it clears only when the request succeeds, so\n\t\t * a refused reply is still there to try again.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tasync onSend() {\n\t\t\tif (!this.canSend) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst text = this.draft.trim()\n\t\t\tthis.errorMessage = ''\n\t\t\tthis.$emit('send', text, this.replyExtras)\n\t\t\tif (!this.endpoint) {\n\t\t\t\tthis.draft = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.sending = true\n\t\t\ttry {\n\t\t\t\tconst response = await axios.post(prefixUrl(this.endpoint), { ...this.payload, ...this.replyExtras, message: text })\n\t\t\t\tthis.draft = ''\n\t\t\t\tthis.$emit('sent', response?.data)\n\t\t\t} catch (error) {\n\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'The message could not be sent. Try again.')\n\t\t\t\tthis.$emit('error', error)\n\t\t\t} finally {\n\t\t\t\tthis.sending = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-conversation-thread {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3.5 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__status {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-weight: 700;\n}\n\n.cn-conversation-thread__message-actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tmargin-top: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__header {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tjustify-content: space-between;\n}\n\n.cn-conversation-thread__header-actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__title {\n\tfont-size: 1.1em;\n\tfont-weight: 700;\n\tmargin: 0;\n}\n\n.cn-conversation-thread__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-conversation-thread__subtitle {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n\tline-height: normal;\n\tmargin: 0;\n}\n\n.cn-conversation-thread__direction,\n.cn-conversation-thread__meta time {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-conversation-thread__direction::after {\n\tcontent: ' ·';\n}\n\n.cn-conversation-thread__note {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-conversation-thread__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-conversation-thread__messages {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3.5 * var(--default-grid-baseline));\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-conversation-thread__message {\n\tdisplay: flex;\n\tgap: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__message--us {\n\tflex-direction: row-reverse;\n}\n\n.cn-conversation-thread__avatar {\n\talign-items: center;\n\tbackground: var(--color-background-dark);\n\tborder-radius: 50%;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: none;\n\tfont-size: 0.8em;\n\tfont-weight: 700;\n\theight: 34px;\n\tjustify-content: center;\n\twidth: 34px;\n}\n\n.cn-conversation-thread__bubble {\n\tbackground: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tborder-start-start-radius: var(--border-radius-small, 4px);\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: 1;\n\tflex-direction: column;\n\tgap: var(--default-grid-baseline);\n\tmin-width: 0;\n\tpadding: calc(3 * var(--default-grid-baseline)) calc(3.5 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__message--us .cn-conversation-thread__bubble {\n\tbackground: var(--color-primary-element-light);\n\tborder-start-end-radius: var(--border-radius-small, 4px);\n\tborder-start-start-radius: var(--border-radius-large);\n\tcolor: var(--color-primary-element-light-text);\n}\n\n.cn-conversation-thread__meta {\n\talign-items: center;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tfont-size: 0.9em;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__author {\n\tfont-weight: 600;\n}\n\n.cn-conversation-thread__text {\n\toverflow-wrap: anywhere;\n\twhite-space: pre-wrap;\n}\n\n.cn-conversation-thread__reply {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__reply-head {\n\talign-items: baseline;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tjustify-content: space-between;\n}\n\n.cn-conversation-thread__reply-label {\n\tfont-weight: 600;\n}\n\n.cn-conversation-thread__reply-hint {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-conversation-thread__reply-option {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__input {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tresize: vertical;\n\twidth: 100%;\n}\n\n.cn-conversation-thread__error {\n\tcolor: var(--color-error-text, var(--color-error));\n\tmargin: 0;\n}\n\n.cn-conversation-thread__reply-foot {\n\talign-items: center;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2.5 * var(--default-grid-baseline));\n\tjustify-content: space-between;\n}\n\n.cn-conversation-thread__reply-actions {\n\tmargin-inline-start: auto;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2.5 * var(--default-grid-baseline));\n\tjustify-content: flex-end;\n}\n\n.cn-conversation-thread__channel {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["CnQuickFilterBar","t","axios"],"mappings":"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"}
1
+ {"version":3,"file":"CnConversationThread.vue2.js","sources":["../../../../src/components/CnConversationThread/CnConversationThread.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<section\n\t\tclass=\"cn-conversation-thread\"\n\t\tdata-testid=\"cn-conversation-thread\"\n\t\t:aria-labelledby=\"title ? titleId : null\"\n\t\t:aria-label=\"title ? null : fallbackLabel\">\n\t\t<div v-if=\"title || $slots['header-actions']\" class=\"cn-conversation-thread__header\">\n\t\t\t<div v-if=\"title\" class=\"cn-conversation-thread__heading\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"titleTag\"\n\t\t\t\t\t:id=\"titleId\"\n\t\t\t\t\tclass=\"cn-conversation-thread__title\">\n\t\t\t\t\t{{ title }}\n\t\t\t\t</component>\n\t\t\t\t<!-- A div, not a <p>: a theme that sets every paragraph's size with\n\t\t\t\t !important would otherwise override the 14px subtitle. -->\n\t\t\t\t<div v-if=\"subtitle\" class=\"cn-conversation-thread__subtitle\" data-testid=\"cn-conversation-subtitle\">\n\t\t\t\t\t{{ subtitle }}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div v-if=\"$slots['header-actions']\" class=\"cn-conversation-thread__header-actions\">\n\t\t\t\t<!-- @slot header-actions Buttons beside the title, for example \"Log contact\". -->\n\t\t\t\t<slot name=\"header-actions\" />\n\t\t\t</div>\n\t\t</div>\n\n\t\t<CnQuickFilterBar\n\t\t\tv-if=\"channelChips.length > 0\"\n\t\t\tinline\n\t\t\tclass=\"cn-conversation-thread__channels\"\n\t\t\t:tabs=\"channelChips\"\n\t\t\t:activeIndex=\"activeChannelIndex\"\n\t\t\t@update:activeIndex=\"onChannelChip\" />\n\n\t\t<p v-if=\"loading && rows.length === 0\"\n\t\t\tclass=\"cn-conversation-thread__empty\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-conversation-loading\">\n\t\t\t{{ loadingText }}\n\t\t</p>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-conversation-thread__empty\" data-testid=\"cn-conversation-empty\">\n\t\t\t{{ emptyText }}\n\t\t</p>\n\n\t\t<p v-else-if=\"shownRows.length === 0\" class=\"cn-conversation-thread__empty\" data-testid=\"cn-conversation-channel-empty\">\n\t\t\t{{ emptyText }}\n\t\t</p>\n\n\t\t<ol v-else class=\"cn-conversation-thread__messages\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in shownRows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-conversation-thread__message\"\n\t\t\t\t:class=\"`cn-conversation-thread__message--${row.side}`\"\n\t\t\t\tdata-testid=\"cn-conversation-message\"\n\t\t\t\t:data-side=\"row.side\">\n\t\t\t\t<span class=\"cn-conversation-thread__avatar\" aria-hidden=\"true\">{{ row.initials }}</span>\n\t\t\t\t<div class=\"cn-conversation-thread__bubble\">\n\t\t\t\t\t<span class=\"cn-conversation-thread__meta\">\n\t\t\t\t\t\t<span class=\"hidden-visually\">{{ row.directionLabel }}</span>\n\t\t\t\t\t\t<span class=\"cn-conversation-thread__author\">{{ row.author }}</span>\n\t\t\t\t\t\t<span v-if=\"row.channel\" class=\"cn-conversation-thread__channel\" data-testid=\"cn-conversation-channel\"><CnIcon v-if=\"row.channelIcon\"\n\t\t\t\t\t\t\tclass=\"cn-conversation-thread__channel-icon\"\n\t\t\t\t\t\t\t:name=\"row.channelIcon\"\n\t\t\t\t\t\t\t:size=\"13\"\n\t\t\t\t\t\t\taria-hidden=\"true\" />{{ row.channel }}</span>\n\t\t\t\t\t\t<span v-if=\"row.status\" class=\"cn-conversation-thread__status\" data-testid=\"cn-conversation-status\">{{ row.status }}</span>\n\t\t\t\t\t\t<span v-if=\"showDirection\"\n\t\t\t\t\t\t\tclass=\"cn-conversation-thread__direction\"\n\t\t\t\t\t\t\tdata-testid=\"cn-conversation-direction\"\n\t\t\t\t\t\t\taria-hidden=\"true\">{{ row.directionWord }}</span>\n\t\t\t\t\t\t<time v-if=\"row.timeLabel\" :datetime=\"row.datetime || null\">{{ row.timeLabel }}</time>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-conversation-thread__text\">{{ row.text }}</span>\n\t\t\t\t\t<span v-if=\"row.note\" class=\"cn-conversation-thread__note\" data-testid=\"cn-conversation-note\">{{ row.note }}</span>\n\t\t\t\t\t<span v-if=\"row.actions.length > 0\" class=\"cn-conversation-thread__message-actions\">\n\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\tv-for=\"action in row.actions\"\n\t\t\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t:disabled=\"action.disabled === true\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-conversation-action-${action.id}`\"\n\t\t\t\t\t\t\t@click=\"$emit('message-action', { action: action.id, message: row.key })\">\n\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ol>\n\n\t\t<form\n\t\t\tv-if=\"allowReply\"\n\t\t\tclass=\"cn-conversation-thread__reply\"\n\t\t\tdata-testid=\"cn-conversation-reply\"\n\t\t\t@submit.prevent=\"onSend\">\n\t\t\t<!-- @slot reply-tools Tools above the reply box, for example a template picker. -->\n\t\t\t<!-- @binding {Function} setDraft Puts a text in the reply box. -->\n\t\t\t<slot name=\"reply-tools\" :setDraft=\"setDraft\" />\n\t\t\t<div class=\"cn-conversation-thread__reply-head\">\n\t\t\t\t<label :for=\"inputId\" class=\"cn-conversation-thread__reply-label\">{{ replyLabel }}</label>\n\t\t\t\t<span v-if=\"replyHint\" class=\"cn-conversation-thread__reply-hint\" data-testid=\"cn-conversation-reply-hint\">{{ replyHint }}</span>\n\t\t\t</div>\n\t\t\t<textarea\n\t\t\t\t:id=\"inputId\"\n\t\t\t\tv-model=\"draft\"\n\t\t\t\tclass=\"cn-conversation-thread__input\"\n\t\t\t\trows=\"2\"\n\t\t\t\t:placeholder=\"placeholder || null\"\n\t\t\t\t:disabled=\"sending\"\n\t\t\t\t:aria-describedby=\"errorMessage ? errorId : null\"\n\t\t\t\tdata-testid=\"cn-conversation-input\"\n\t\t\t\t@keydown.ctrl.enter.prevent=\"onSend\"\n\t\t\t\t@keydown.meta.enter.prevent=\"onSend\" />\n\t\t\t<p\n\t\t\t\tv-if=\"errorMessage\"\n\t\t\t\t:id=\"errorId\"\n\t\t\t\tclass=\"cn-conversation-thread__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-conversation-error\">\n\t\t\t\t{{ errorMessage }}\n\t\t\t</p>\n\t\t\t<!-- The option at the start and the buttons at the end of one row. -->\n\t\t\t<div class=\"cn-conversation-thread__reply-foot\">\n\t\t\t\t<label v-if=\"optionShown\" class=\"cn-conversation-thread__reply-option\" data-testid=\"cn-conversation-reply-option\">\n\t\t\t\t\t<input v-model=\"optionOn\" type=\"checkbox\" :disabled=\"sending\">\n\t\t\t\t\t<span>{{ replyOption.label }}</span>\n\t\t\t\t</label>\n\t\t\t\t<div class=\"cn-conversation-thread__reply-actions\">\n\t\t\t\t\t<!-- @slot reply-actions Extra buttons before Send, for example \"Save draft\". -->\n\t\t\t\t\t<!-- @binding {string} draft The text in the reply box. -->\n\t\t\t\t\t<slot name=\"reply-actions\" :draft=\"draft\" />\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=\"!canSend\"\n\t\t\t\t\t\tdata-testid=\"cn-conversation-send\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<Send :size=\"16\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ sendLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t</section>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnQuickFilterBar from '../CnQuickFilterBar/CnQuickFilterBar.vue'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\nlet conversationThreadUid = 0\n\n/**\n * CnConversationThread: the messages between you and the other party, and a box to answer.\n *\n * Each message has an author, a time and a side: `us` for the organisation,\n * `them` for the resident or customer. The reply box emits `send`, and also\n * posts to `endpoint` when one is set. It needs no Talk and no other app:\n * where the messages are stored is the host's business.\n *\n * ```vue\n * <CnConversationThread\n * title=\"Contact with the resident\"\n * reply-label=\"Answer to the resident\"\n * :messages=\"[\n * { id: 1, author: 'Sanne de Vries', time: '2026-10-04T10:52:00', side: 'them',\n * text: 'Thank you. I would like to see all documents.' },\n * { id: 2, time: '2026-10-04T10:24:00', side: 'us',\n * text: 'Construction starts in March.' },\n * ]\"\n * @send=\"saveReply\" />\n * ```\n *\n * The `conversation` detail widget type renders this thread from a field on\n * the record.\n */\nexport default {\n\tname: 'CnConversationThread',\n\n\tcomponents: {\n\t\tCnIcon,\n\t\tCnQuickFilterBar,\n\t\tNcButton,\n\t\tSend,\n\t},\n\n\tinject: {\n\t\t/** The host app's translate, for the All chip in the host's word. */\n\t\tcnTranslate: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The messages, oldest first: `{ id?, author?, time?, text, side?, channel?, channelIcon?, status?, actions?, note? }`.\n\t\t * `status` is shown after the channel (\"Failed\"); `actions` are\n\t\t * `{ id, label, disabled? }` buttons under the text, each emitting\n\t\t * `message-action`.\n\t\t * `note` is a muted line under the text.\n\t\t * `side` is `us` or `them` (default `them`). `time` is an ISO\n\t\t * date, or any text to show as is. `channel` is shown after the\n\t\t * author (\"E-mail\", \"Phone note\"); `note` is a muted line under the text.\n\t\t */\n\t\tmessages: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Heading of the thread. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** A line under the title, such as \"Oldest first, with the channel of each message\". */\n\t\tsubtitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Say each message's direction in words before its time (\"Incoming · 3 Oct\"), not only to a screen reader. */\n\t\tshowDirection: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Element the heading renders as. */\n\t\ttitleTag: {\n\t\t\ttype: String,\n\t\t\tdefault: 'h3',\n\t\t},\n\n\t\t/** A line beside the reply label saying where the answer goes, such as \"Goes to the message box in the portal\". */\n\t\treplyHint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * A switch under the reply box whose state is sent with the reply:\n\t\t * `{ label, key, value? }`, `value` the starting state (on by default).\n\t\t * The reply's `send` event carries `{ [key]: state }` after the text.\n\t\t *\n\t\t * @type {{label: string, key: string, value?: boolean}|null}\n\t\t */\n\t\treplyOption: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether the reply box renders. Turn it off for a closed or read-only record. */\n\t\tallowReply: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Label of the reply box. */\n\t\treplyLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Reply'),\n\t\t},\n\n\t\t/** Placeholder inside the reply box. */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label of the send button. */\n\t\tsendLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Send'),\n\t\t},\n\n\t\t/** Author shown on an `us` message that names none. */\n\t\tusLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'You'),\n\t\t},\n\n\t\t/**\n\t\t * URL the reply is POSTed to as `{ message }`. Empty means the host\n\t\t * handles `send` itself and nothing is requested.\n\t\t */\n\t\tendpoint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Extra fields sent along with `message` when `endpoint` is set. */\n\t\tpayload: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether the messages are still loading; with none yet the thread says so. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Draw channel chips above the messages: All, then one per channel\n\t\t * with its count. Choosing one shows only that channel's messages.\n\t\t */\n\t\tchannelFilter: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Channel labels that always get a chip, in this order, also when no\n\t\t * message uses them yet. Channels the messages use follow.\n\t\t */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Shown when there are no messages yet. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No messages yet.'),\n\t\t},\n\t},\n\n\temits: [\n\t\t/** A reply was submitted. Payload: the text. */\n\t\t'send',\n\t\t/** The endpoint accepted the reply. Payload: the response body. */\n\t\t'sent',\n\t\t/** The endpoint refused the reply. Payload: the error. */\n\t\t'error',\n\t\t/** A message action was pressed. Payload: `{ action, message }`, the action id and the message key. */\n\t\t'message-action',\n\t],\n\n\tdata() {\n\t\tconversationThreadUid += 1\n\t\treturn {\n\t\t\tdraft: '',\n\t\t\t/** The state of the reply option's switch. */\n\t\t\toptionOn: this.replyOption?.value !== false,\n\t\t\t/** The channel the chips narrow the thread to; '' is All. */\n\t\t\tactiveChannel: '',\n\t\t\tsending: false,\n\t\t\terrorMessage: '',\n\t\t\ttitleId: `cn-conversation-title-${conversationThreadUid}`,\n\t\t\tinputId: `cn-conversation-input-${conversationThreadUid}`,\n\t\t\terrorId: `cn-conversation-error-${conversationThreadUid}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The messages, with author, side, time and initials resolved.\n\t\t *\n\t\t * @return {Array<{key: string, author: string, initials: string, side: string, text: string, timeLabel: string, datetime: string, channel: string, directionLabel: string, directionWord: string, note: string}>} The rows.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\trows() {\n\t\t\treturn (Array.isArray(this.messages) ? this.messages : [])\n\t\t\t\t.filter((message) => message && typeof message === 'object')\n\t\t\t\t.map((message, index) => {\n\t\t\t\t\tconst side = message.side === 'us' ? 'us' : 'them'\n\t\t\t\t\tconst author = typeof message.author === 'string' && message.author !== ''\n\t\t\t\t\t\t? message.author\n\t\t\t\t\t\t: (side === 'us' ? this.usLabel : '')\n\t\t\t\t\tconst time = this.formatTime(message.time)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: message.id === undefined || message.id === null ? `message-${index}` : String(message.id),\n\t\t\t\t\t\tauthor,\n\t\t\t\t\t\tinitials: this.initialsOf(author),\n\t\t\t\t\t\tside,\n\t\t\t\t\t\ttext: message.text === undefined || message.text === null ? '' : String(message.text),\n\t\t\t\t\t\ttimeLabel: time.label,\n\t\t\t\t\t\tdatetime: time.datetime,\n\t\t\t\t\t\tchannel: typeof message.channel === 'string' ? message.channel : '',\n\t\t\t\t\t\tchannelIcon: typeof message.channelIcon === 'string' ? message.channelIcon : '',\n\t\t\t\t\t\tstatus: typeof message.status === 'string' ? message.status : '',\n\t\t\t\t\t\tactions: Array.isArray(message.actions) ? message.actions.filter((action) => action && action.id && action.label) : [],\n\t\t\t\t\t\tdirectionLabel: side === 'us' ? t('nextcloud-vue', 'Sent message') : t('nextcloud-vue', 'Received message'),\n\t\t\t\t\t\tdirectionWord: side === 'us' ? t('nextcloud-vue', 'Outgoing') : t('nextcloud-vue', 'Incoming'),\n\t\t\t\t\t\tnote: typeof message.note === 'string' ? message.note : '',\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The channel labels with a chip: the declared ones, then the ones\n\t\t * the messages use.\n\t\t *\n\t\t * @return {Array<string>} The labels, each once.\n\t\t */\n\t\tchannelLabels() {\n\t\t\tconst declared = (Array.isArray(this.channels) ? this.channels : []).filter((label) => typeof label === 'string' && label !== '')\n\t\t\treturn [...new Set([...declared, ...this.rows.map((row) => row.channel).filter(Boolean)])]\n\t\t},\n\n\t\t/**\n\t\t * The chips: All, then each channel, each with its count. Empty\n\t\t * without `channelFilter` or without channels.\n\t\t *\n\t\t * @return {Array<{label: string, count: number}>} The chips.\n\t\t */\n\t\tchannelChips() {\n\t\t\tif (!this.channelFilter || this.channelLabels.length === 0) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{ label: this.allLabel, count: this.rows.length },\n\t\t\t\t...this.channelLabels.map((label) => ({ label, count: this.rows.filter((row) => row.channel === label).length })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * Whether the reply option renders: it has a label and a key.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/conversation-reply-option/specs/conversation-source/spec.md#requirement-a-reply-can-carry-a-hint-and-an-option\n\t\t */\n\t\toptionShown() {\n\t\t\tconst option = this.replyOption\n\t\t\treturn Boolean(option && typeof option.label === 'string' && option.label !== '' && typeof option.key === 'string' && option.key !== '')\n\t\t},\n\n\t\t/**\n\t\t * What travels with the reply besides its text.\n\t\t *\n\t\t * @return {object} `{ [key]: state }`, or {} without an option.\n\t\t * @spec openspec/changes/conversation-reply-option/specs/conversation-source/spec.md#requirement-a-reply-can-carry-a-hint-and-an-option\n\t\t */\n\t\treplyExtras() {\n\t\t\treturn this.optionShown ? { [this.replyOption.key]: this.optionOn === true } : {}\n\t\t},\n\n\t\t/**\n\t\t * The All chip: the host's word first, as its own filter chips read\n\t\t * (\"Alles\"), then the library's.\n\t\t *\n\t\t * @return {string} The label.\n\t\t * @spec openspec/changes/conversation-all-chip-host-word/specs/conversation-source/spec.md#requirement-the-all-chip-reads-the-hosts-word\n\t\t */\n\t\tallLabel() {\n\t\t\tconst host = typeof this.cnTranslate === 'function' ? this.cnTranslate('All') : 'All'\n\t\t\treturn typeof host === 'string' && host !== '' && host !== 'All' ? host : t('nextcloud-vue', 'All')\n\t\t},\n\n\t\t/** @return {number} The pressed chip: 0 for All. */\n\t\tactiveChannelIndex() {\n\t\t\tconst index = this.channelLabels.indexOf(this.activeChannel)\n\t\t\treturn index < 0 ? 0 : index + 1\n\t\t},\n\n\t\t/** @return {Array<object>} The rows on the chosen channel, or all of them. */\n\t\tshownRows() {\n\t\t\tif (this.channelChips.length === 0 || this.activeChannelIndex === 0) {\n\t\t\t\treturn this.rows\n\t\t\t}\n\t\t\treturn this.rows.filter((row) => row.channel === this.activeChannel)\n\t\t},\n\n\t\t/**\n\t\t * Whether there is something to send and nothing in flight.\n\t\t *\n\t\t * @return {boolean} True when Send is enabled.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tcanSend() {\n\t\t\treturn !this.sending && this.draft.trim() !== ''\n\t\t},\n\n\t\t/**\n\t\t * Accessible name for a thread without a title.\n\t\t *\n\t\t * @return {string} The label.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tfallbackLabel() {\n\t\t\treturn t('nextcloud-vue', 'Conversation')\n\t\t},\n\n\t\t/** @return {string} What the thread says while its messages load. */\n\t\tloadingText() {\n\t\t\treturn t('nextcloud-vue', 'Loading messages…')\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Put a text in the reply box (a template a host tool picked).\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {void}\n\t\t */\n\t\tsetDraft(text) {\n\t\t\tthis.draft = typeof text === 'string' ? text : ''\n\t\t},\n\n\t\t/**\n\t\t * Narrow the thread to the chip's channel; the All chip (0) clears it.\n\t\t *\n\t\t * @param {number|null} index The chip index.\n\t\t * @return {void}\n\t\t */\n\t\tonChannelChip(index) {\n\t\t\tthis.activeChannel = index > 0 ? (this.channelLabels[index - 1] || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * Up to two initials of a name, for the avatar.\n\t\t *\n\t\t * @param {string} name The author.\n\t\t * @return {string} The initials, upper case.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tinitialsOf(name) {\n\t\t\tconst parts = String(name || '').trim().split(/\\s+/).filter(Boolean)\n\t\t\tif (parts.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst first = parts[0].charAt(0)\n\t\t\tconst last = parts.length > 1 ? parts[parts.length - 1].charAt(0) : ''\n\t\t\treturn (first + last).toUpperCase()\n\t\t},\n\n\t\t/**\n\t\t * Turn a message time into what the reader sees and what `<time>` carries.\n\t\t *\n\t\t * @param {unknown} value An ISO date, a Date, or text to show as is.\n\t\t * @return {{label: string, datetime: string}} The visible label and the machine value.\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tformatTime(value) {\n\t\t\tif (value === null || value === undefined || value === '') {\n\t\t\t\treturn { label: '', datetime: '' }\n\t\t\t}\n\t\t\tconst date = value instanceof Date ? value : new Date(value)\n\t\t\tconst looksLikeDate = value instanceof Date || typeof value === 'number' || /^\\d{4}-\\d{2}-\\d{2}/.test(String(value))\n\t\t\tif (!looksLikeDate || Number.isNaN(date.getTime())) {\n\t\t\t\treturn { label: String(value), datetime: '' }\n\t\t\t}\n\t\t\tconst label = date.toLocaleString(userLocale(), { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })\n\t\t\treturn { label, datetime: date.toISOString() }\n\t\t},\n\n\t\t/**\n\t\t * Submit the reply: tell the host, and post it when an endpoint is set.\n\t\t *\n\t\t * Without an endpoint the box clears straight away, because the host\n\t\t * owns the send. With one it clears only when the request succeeds, so\n\t\t * a refused reply is still there to try again.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-conversation-thread\n\t\t */\n\t\tasync onSend() {\n\t\t\tif (!this.canSend) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst text = this.draft.trim()\n\t\t\tthis.errorMessage = ''\n\t\t\tthis.$emit('send', text, this.replyExtras)\n\t\t\tif (!this.endpoint) {\n\t\t\t\tthis.draft = ''\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.sending = true\n\t\t\ttry {\n\t\t\t\tconst response = await axios.post(prefixUrl(this.endpoint), { ...this.payload, ...this.replyExtras, message: text })\n\t\t\t\tthis.draft = ''\n\t\t\t\tthis.$emit('sent', response?.data)\n\t\t\t} catch (error) {\n\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'The message could not be sent. Try again.')\n\t\t\t\tthis.$emit('error', error)\n\t\t\t} finally {\n\t\t\t\tthis.sending = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-conversation-thread {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3.5 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__status {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-weight: 700;\n}\n\n.cn-conversation-thread__message-actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tmargin-top: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__header {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tjustify-content: space-between;\n}\n\n.cn-conversation-thread__header-actions {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__title {\n\tfont-size: 1.1em;\n\tfont-weight: 700;\n\tmargin: 0;\n}\n\n.cn-conversation-thread__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-conversation-thread__subtitle {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n\tline-height: normal;\n\tmargin: 0;\n}\n\n.cn-conversation-thread__direction,\n.cn-conversation-thread__meta time {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-conversation-thread__direction::after {\n\tcontent: ' ·';\n}\n\n.cn-conversation-thread__note {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-conversation-thread__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-conversation-thread__messages {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3.5 * var(--default-grid-baseline));\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-conversation-thread__message {\n\tdisplay: flex;\n\tgap: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__message--us {\n\tflex-direction: row-reverse;\n}\n\n.cn-conversation-thread__avatar {\n\talign-items: center;\n\tbackground: var(--color-background-dark);\n\tborder-radius: 50%;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: none;\n\tfont-size: 0.8em;\n\tfont-weight: 700;\n\theight: 34px;\n\tjustify-content: center;\n\twidth: 34px;\n}\n\n.cn-conversation-thread__bubble {\n\tbackground: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tborder-start-start-radius: var(--border-radius-small, 4px);\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: 1;\n\tflex-direction: column;\n\tgap: var(--default-grid-baseline);\n\tmin-width: 0;\n\tpadding: calc(3 * var(--default-grid-baseline)) calc(3.5 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__message--us .cn-conversation-thread__bubble {\n\tbackground: var(--color-primary-element-light);\n\tborder-start-end-radius: var(--border-radius-small, 4px);\n\tborder-start-start-radius: var(--border-radius-large);\n\tcolor: var(--color-primary-element-light-text);\n}\n\n.cn-conversation-thread__meta {\n\talign-items: center;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tfont-size: 0.9em;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__author {\n\tfont-weight: 600;\n}\n\n.cn-conversation-thread__text {\n\toverflow-wrap: anywhere;\n\twhite-space: pre-wrap;\n}\n\n.cn-conversation-thread__reply {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__reply-head {\n\talign-items: baseline;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tjustify-content: space-between;\n}\n\n.cn-conversation-thread__reply-label {\n\tfont-weight: 600;\n}\n\n.cn-conversation-thread__reply-hint {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-conversation-thread__reply-option {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-conversation-thread__input {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tresize: vertical;\n\twidth: 100%;\n}\n\n.cn-conversation-thread__error {\n\tcolor: var(--color-error-text, var(--color-error));\n\tmargin: 0;\n}\n\n.cn-conversation-thread__reply-foot {\n\talign-items: center;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2.5 * var(--default-grid-baseline));\n\tjustify-content: space-between;\n}\n\n.cn-conversation-thread__reply-actions {\n\tmargin-inline-start: auto;\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: calc(2.5 * var(--default-grid-baseline));\n\tjustify-content: flex-end;\n}\n\n.cn-conversation-thread__channel {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The channel's icon (`channelIcon`) stands before its name, 5px apart. */\n.cn-conversation-thread__channel:has(.cn-conversation-thread__channel-icon) {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 5px;\n}\n</style>\n"],"names":["CnIcon","CnQuickFilterBar","t","axios"],"mappings":"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"}
@@ -94,6 +94,7 @@ const _hoisted_31 = { class: "cn-conversation-thread__reply-actions" };
94
94
 
95
95
  function render(_ctx, _cache, $props, $setup, $data, $options) {
96
96
  const _component_CnQuickFilterBar = resolveComponent("CnQuickFilterBar");
97
+ const _component_CnIcon = resolveComponent("CnIcon");
97
98
  const _component_NcButton = resolveComponent("NcButton");
98
99
  const _component_Send = resolveComponent("Send");
99
100
 
@@ -158,7 +159,18 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
158
159
  createElementVNode("span", _hoisted_14, toDisplayString(row.directionLabel), 1 /* TEXT */),
159
160
  createElementVNode("span", _hoisted_15, toDisplayString(row.author), 1 /* TEXT */),
160
161
  (row.channel)
161
- ? (openBlock(), createElementBlock("span", _hoisted_16, toDisplayString(row.channel), 1 /* TEXT */))
162
+ ? (openBlock(), createElementBlock("span", _hoisted_16, [
163
+ (row.channelIcon)
164
+ ? (openBlock(), createBlock(_component_CnIcon, {
165
+ key: 0,
166
+ class: "cn-conversation-thread__channel-icon",
167
+ name: row.channelIcon,
168
+ size: 13,
169
+ "aria-hidden": "true"
170
+ }, null, 8 /* PROPS */, ["name"]))
171
+ : createCommentVNode("v-if", true),
172
+ createTextVNode(toDisplayString(row.channel), 1 /* TEXT */)
173
+ ]))
162
174
  : createCommentVNode("v-if", true),
163
175
  (row.status)
164
176
  ? (openBlock(), createElementBlock("span", _hoisted_17, toDisplayString(row.status), 1 /* TEXT */))