@conduction/nextcloud-vue 2.144.0 → 2.145.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +1 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +55 -2
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +16 -0
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +3 -3
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +2 -2
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +6 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue.js +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue2.js.map +1 -1
- package/dist/esm/components/CnMailTab/CnMailTab.vue3.js +2 -2
- package/dist/esm/components/CnMailTab/CnMailTab.vue3.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
- package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +73 -12
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +165 -126
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +14 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +2 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -8
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue.js +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +10 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +35 -9
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +742 -326
- package/dist/esm/utils/builtInFormatters.js +18 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/localHeaderRows.js +95 -0
- package/dist/esm/utils/localHeaderRows.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +514 -179
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +742 -326
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +8 -1
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +55 -2
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +18 -0
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +2 -1
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +2 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +6 -3
- package/src/components/CnMailTab/CnMailTab.vue +6 -4
- package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +4 -1
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +131 -20
- package/src/components/CnSettingsField/CnSettingsField.vue +15 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -8
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +10 -2
- package/src/composables/indexSources.js +35 -9
- package/src/css/dialog.css +18 -9
- package/src/css/look-board-detail.css +219 -23
- package/src/css/look-board-index.css +38 -4
- package/src/css/look-board.css +138 -8
- package/src/css/table.css +8 -1
- package/src/utils/builtInFormatters.js +17 -0
- package/src/utils/localHeaderRows.js +91 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMailTab.vue2.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</p>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["CnEmailTab","CnEmptyContent","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMailTab.vue2.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<!-- Divs, not <p>: a theme that sizes every paragraph with !important\n\t\t\t\t\t would override the card's 14px description. -->\n\t\t\t\t\t<div v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<div v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</div>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["CnEmailTab","CnEmptyContent","t"],"mappings":"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"}
|
|
@@ -63,7 +63,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
63
63
|
? (openBlock(), createElementBlock("h3", _hoisted_4, toDisplayString($options.heading), 1 /* TEXT */))
|
|
64
64
|
: createCommentVNode("v-if", true),
|
|
65
65
|
($options.description)
|
|
66
|
-
? (openBlock(), createElementBlock("
|
|
66
|
+
? (openBlock(), createElementBlock("div", _hoisted_5, toDisplayString($options.description), 1 /* TEXT */))
|
|
67
67
|
: createCommentVNode("v-if", true)
|
|
68
68
|
]),
|
|
69
69
|
($options.hasHeaderActions)
|
|
@@ -153,7 +153,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
153
153
|
]))
|
|
154
154
|
: createCommentVNode("v-if", true),
|
|
155
155
|
($options.hint)
|
|
156
|
-
? (openBlock(), createElementBlock("
|
|
156
|
+
? (openBlock(), createElementBlock("div", _hoisted_8, toDisplayString($options.hint), 1 /* TEXT */))
|
|
157
157
|
: createCommentVNode("v-if", true),
|
|
158
158
|
($data.error)
|
|
159
159
|
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnMailTab.vue3.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue?vue&type=template&id=12407f1c&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</p>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_createVNode","_toDisplayString","_createTextVNode","_createElementVNode","_Fragment","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnMailTab.vue3.js","sources":["../../../../src/components/CnMailTab/CnMailTab.vue?vue&type=template&id=0fa12b0e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-mail-tab\" data-testid=\"cn-mail-tab\">\n\t\t<CnEmptyContent\n\t\t\tv-if=\"unavailable\"\n\t\t\t:name=\"t('nextcloud-vue', 'Mail is not available')\"\n\t\t\t:description=\"t('nextcloud-vue', 'Install Nextcloud Mail to draft and link mail here.')\">\n\t\t\t<template #icon>\n\t\t\t\t<EmailOffOutline :size=\"48\" />\n\t\t\t</template>\n\t\t</CnEmptyContent>\n\n\t\t<template v-else>\n\t\t\t<div v-if=\"heading || description || hasHeaderActions\" class=\"cn-mail-tab__header\" data-testid=\"cn-mail-tab-header\">\n\t\t\t\t<div class=\"cn-mail-tab__heading\">\n\t\t\t\t\t<h3 v-if=\"heading\" class=\"cn-mail-tab__title\">\n\t\t\t\t\t\t{{ heading }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<!-- Divs, not <p>: a theme that sizes every paragraph with !important\n\t\t\t\t\t would override the card's 14px description. -->\n\t\t\t\t\t<div v-if=\"description\" class=\"cn-mail-tab__subtitle\">\n\t\t\t\t\t\t{{ description }}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"hasHeaderActions\" class=\"cn-mail-tab__header-actions\">\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showLinkAction\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-link\"\n\t\t\t\t\t\t@click=\"openLinkPicker\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Link mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"showComposeAction\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t:disabled=\"draftEnabled && (closed || drafting)\"\n\t\t\t\t\t\t:href=\"draftEnabled ? undefined : composeUrl\"\n\t\t\t\t\t\t:target=\"draftEnabled ? undefined : '_blank'\"\n\t\t\t\t\t\tdata-testid=\"cn-mail-tab-header-compose\"\n\t\t\t\t\t\t@click=\"onCompose\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<EmailEditOutline :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Compose mail') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"draftEnabled\" class=\"cn-mail-tab__template-row\" data-testid=\"cn-mail-tab-template-row\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\tv-model=\"selectedTemplate\"\n\t\t\t\t\tclass=\"cn-mail-tab__template-select\"\n\t\t\t\t\t:options=\"templates\"\n\t\t\t\t\t:disabled=\"templates.length === 0\"\n\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Email template')\"\n\t\t\t\t\t:label=\"templateLabelField\"\n\t\t\t\t\t:trackBy=\"templateIdField\"\n\t\t\t\t\t:placeholder=\"templatePlaceholder\"\n\t\t\t\t\t:clearable=\"true\" />\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"templatesEnabled\"\n\t\t\t\t\t:disabled=\"closed || drafting || !selectedTemplate\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-template\"\n\t\t\t\t\t@click=\"draftFromTemplate\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Open draft from template') }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"!showComposeAction || draftSource.blankButton === true\"\n\t\t\t\t\t:disabled=\"closed || drafting\"\n\t\t\t\t\tdata-testid=\"cn-mail-tab-draft-blank\"\n\t\t\t\t\t@click=\"draftBlank\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Empty draft') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<div v-if=\"hint\" class=\"cn-mail-tab__hint\">\n\t\t\t\t{{ hint }}\n\t\t\t</div>\n\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"error\"\n\t\t\t\ttype=\"error\"\n\t\t\t\t:heading=\"t('nextcloud-vue', 'Could not open draft')\"\n\t\t\t\trole=\"alert\">\n\t\t\t\t{{ error }}\n\t\t\t</NcNoteCard>\n\t\t\t<NcNoteCard\n\t\t\t\tv-if=\"unresolved.length > 0\"\n\t\t\t\ttype=\"warning\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-mail-tab-unresolved\">\n\t\t\t\t<p>{{ t('nextcloud-vue', 'Some template fields could not be filled. Fill these in the draft yourself:') }}</p>\n\t\t\t\t<ul class=\"cn-mail-tab__unresolved\">\n\t\t\t\t\t<li v-for=\"name in unresolved\" :key=\"name\">\n\t\t\t\t\t\t<code>{{ placeholderOf(name) }}</code>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</NcNoteCard>\n\n\t\t\t<CnEmailTab\n\t\t\t\tv-if=\"linkedMail && objectKey\"\n\t\t\t\tref=\"linkedMail\"\n\t\t\t\tclass=\"cn-mail-tab__linked\"\n\t\t\t\t:showActions=\"!hasHeaderActions\"\n\t\t\t\t:objectId=\"objectKey\"\n\t\t\t\t:register=\"registerKey\"\n\t\t\t\t:schema=\"schemaKey\" />\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport EmailEditOutline from 'vue-material-design-icons/EmailEditOutline.vue'\nimport EmailOffOutline from 'vue-material-design-icons/EmailOffOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport CnEmailTab from '../../integrations/builtin/email/CnEmailTab.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { evaluateVisibleWhenLocal } from '../../utils/visibleWhen.js'\n\n/** The actions `content.headerActions` can put in the tab's header. */\nconst HEADER_ACTIONS = ['compose', 'link']\n\n/**\n * Read a reference that may be a slug, an id or an object carrying either.\n *\n * @param {string|number|object} value The reference.\n * @return {string} The slug or id, or ''.\n */\nfunction keyOf(value) {\n\tif (value === null || value === undefined) {\n\t\treturn ''\n\t}\n\tif (typeof value === 'object') {\n\t\treturn String(value.slug || value.id || value.uuid || '')\n\t}\n\treturn String(value)\n}\n\n/**\n * CnMailTab — the mail of one record: drafts in Nextcloud Mail, from a\n * template or empty, above the mail linked to the record.\n *\n * Registered as the `mail` widget type. The app owns the endpoints and the\n * texts; the tab never sends mail. A draft is a POST to `draft.url`, whose\n * response may carry `draftUrl` (opened in a new tab) and `unresolved` (the\n * template fields the server could not fill). A 404 there means the app has\n * no Mail to draft into, and the tab says so instead of the controls.\n *\n * ```json\n * {\n * \"type\": \"mail\",\n * \"content\": {\n * \"heading\": \"Email\",\n * \"description\": \"Mail linked to this case\",\n * \"hint\": \"The draft opens in Nextcloud Mail.\",\n * \"templates\": { \"url\": \"/apps/app/api/types/@object.type/templates\", \"responsePath\": \"results\", \"labelField\": \"name\", \"idField\": \"id\" },\n * \"noTemplatesText\": \"No templates for this type yet\",\n * \"draft\": { \"url\": \"/apps/app/api/records/@objectId/templates/{templateId}/draft\", \"blankTemplateId\": \"blank\" },\n * \"closedWhen\": { \"field\": \"endDate\", \"op\": \"notEmpty\" },\n * \"linkedMail\": true,\n * \"headerActions\": [\"link\", \"compose\"]\n * }\n * }\n * ```\n *\n * URLs take the `@objectId` / `@object.<field>` tokens of `endpointSource`;\n * `{templateId}` in the draft URL is the chosen template's id, or\n * `blankTemplateId` for an empty draft. Without `templates` the row offers\n * the empty draft only; without `draft` there is no row. `closedWhen` is a\n * local visibleWhen condition on the record that disables drafting.\n * `linkedMail: false` leaves the linked mail to a separate `email`\n * integration card. `headerActions` puts buttons beside the heading:\n * `compose` opens the empty draft (or Nextcloud Mail's composer when there\n * is no `draft`) and replaces the row's empty-draft button, unless\n * `draft.blankButton` is true (DqZaakEmail draws both); `link` opens the\n * linked mail's picker and needs the linked mail. The linked mail then drops\n * its own buttons. Texts go through the host's translate.\n */\nexport default {\n\tname: 'CnMailTab',\n\n\tcomponents: {\n\t\tCnEmailTab,\n\t\tCnEmptyContent,\n\t\tEmailEditOutline,\n\t\tEmailOffOutline,\n\t\tLinkVariant,\n\t\tNcButton,\n\t\tNcNoteCard,\n\t\tNcSelect,\n\t},\n\n\tinject: {\n\t\t/** The detail page's object context (`{ objectId, object }`), for tokens. */\n\t\tcnObjectContext: { default: null },\n\t\t/** The host's translate, for the manifest texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ heading?, description?, hint?, templates?,\n\t\t * noTemplatesText?, draft?, closedWhen?, linkedMail?, headerActions? }`, as in the\n\t\t * component description and docs/components/cn-mail-tab.md.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` and the linked mail. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` and `closedWhen`. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The bound record's register, for the linked mail. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record's schema, for the linked mail. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['drafted'],\n\n\tdata() {\n\t\treturn {\n\t\t\ttemplates: [],\n\t\t\tselectedTemplate: null,\n\t\t\tdrafting: false,\n\t\t\terror: '',\n\t\t\tunresolved: [],\n\t\t\tunavailable: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The record, from the prop or the page context. */\n\t\trecord() {\n\t\t\tif (this.objectData) {\n\t\t\t\treturn this.objectData\n\t\t\t}\n\t\t\tconst ctx = this.cnObjectContext\n\t\t\tconst value = ctx && typeof ctx === 'object' && 'value' in ctx ? ctx.value : ctx\n\t\t\treturn (value && value.object) || {}\n\t\t},\n\n\t\t/** @return {string} The record id. */\n\t\tobjectKey() {\n\t\t\treturn keyOf(this.objectId) || keyOf(this.record.id) || keyOf(this.record['@self'] && this.record['@self'].id)\n\t\t},\n\n\t\t/** @return {string} The register slug or id. */\n\t\tregisterKey() {\n\t\t\treturn keyOf(this.register)\n\t\t},\n\n\t\t/** @return {string} The schema slug or id. */\n\t\tschemaKey() {\n\t\t\treturn keyOf(this.schema)\n\t\t},\n\n\t\t/** @return {object} The token context for the URLs. */\n\t\ttokenContext() {\n\t\t\treturn { objectId: this.objectKey, object: this.record }\n\t\t},\n\n\t\t/** @return {string} The heading, translated, or ''. */\n\t\theading() {\n\t\t\treturn this.tr(this.content.heading)\n\t\t},\n\n\t\t/** @return {string} The line under the heading, translated, or ''. */\n\t\tdescription() {\n\t\t\treturn this.tr(this.content.description)\n\t\t},\n\n\t\t/** @return {string} The line under the template row, translated, or ''. */\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint)\n\t\t},\n\n\t\t/** @return {object|null} The templates endpoint, or null. */\n\t\ttemplatesSource() {\n\t\t\tconst source = this.content.templates\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {object|null} The draft endpoint, or null. */\n\t\tdraftSource() {\n\t\t\tconst source = this.content.draft\n\t\t\treturn source && typeof source === 'object' && source.url ? source : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template select and its button show. */\n\t\ttemplatesEnabled() {\n\t\t\treturn this.templatesSource !== null\n\t\t},\n\n\t\t/** @return {boolean} Whether the template row shows. */\n\t\tdraftEnabled() {\n\t\t\treturn this.draftSource !== null\n\t\t},\n\n\t\t/** @return {string} The template field shown in the select. */\n\t\ttemplateLabelField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.labelField) || 'name'\n\t\t},\n\n\t\t/** @return {string} The template field that identifies it. */\n\t\ttemplateIdField() {\n\t\t\treturn (this.templatesSource && this.templatesSource.idField) || 'id'\n\t\t},\n\n\t\t/** @return {string} The select's placeholder. */\n\t\ttemplatePlaceholder() {\n\t\t\tif (this.templates.length === 0) {\n\t\t\t\treturn this.tr(this.content.noTemplatesText) || t('nextcloud-vue', 'No templates yet')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Select a template (optional)…')\n\t\t},\n\n\t\t/** @return {boolean} Whether `closedWhen` holds for the record. */\n\t\tclosed() {\n\t\t\tconst cond = this.content.closedWhen\n\t\t\tif (!cond) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn evaluateVisibleWhenLocal(cond, this.record)\n\t\t},\n\n\t\t/** @return {string} The templates URL after tokens, to refetch on change. */\n\t\ttemplatesUrl() {\n\t\t\treturn this.templatesSource ? interpolateUrlTokens(this.templatesSource.url, this.tokenContext) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the linked mail list shows. */\n\t\tlinkedMail() {\n\t\t\treturn this.content.linkedMail !== false\n\t\t},\n\n\t\t/**\n\t\t * The known header actions the config asks for.\n\t\t *\n\t\t * @return {string[]} `compose` and or `link`.\n\t\t * @spec openspec/changes/mail-tab-compose-and-link/specs/mail-tab-compose-and-link/spec.md#requirement-the-mail-tab-can-carry-compose-mail-and-link-mail-in-its-header\n\t\t */\n\t\theaderActions() {\n\t\t\tconst list = Array.isArray(this.content.headerActions) ? this.content.headerActions : []\n\t\t\treturn list.filter((key) => HEADER_ACTIONS.includes(key))\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Compose mail\". */\n\t\tshowComposeAction() {\n\t\t\treturn this.headerActions.includes('compose')\n\t\t},\n\n\t\t/** @return {boolean} Whether the header shows \"Link mail\": it opens the linked mail's picker. */\n\t\tshowLinkAction() {\n\t\t\treturn this.headerActions.includes('link') && this.linkedMail && this.objectKey !== ''\n\t\t},\n\n\t\t/** @return {boolean} Whether the header holds any button. */\n\t\thasHeaderActions() {\n\t\t\treturn this.showComposeAction || this.showLinkAction\n\t\t},\n\n\t\t/** @return {string} Nextcloud Mail's composer, for an app without a draft endpoint. */\n\t\tcomposeUrl() {\n\t\t\treturn (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function')\n\t\t\t\t? OC.generateUrl('/apps/mail/box/draft')\n\t\t\t\t: '/index.php/apps/mail/box/draft'\n\t\t},\n\t},\n\n\twatch: {\n\t\ttemplatesUrl: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.loadTemplates()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Translate a manifest text through the host.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation, or '' for no text.\n\t\t */\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.cnTranslate(text)\n\t\t},\n\n\t\t/**\n\t\t * Read the templates. A failed read leaves the empty draft usable.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadTemplates() {\n\t\t\tif (!this.templatesSource) {\n\t\t\t\tthis.templates = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = await fetchEndpointSource(this.templatesSource, this.tokenContext)\n\t\t\t\tconst list = Array.isArray(payload) ? payload : (payload && Array.isArray(payload.results) ? payload.results : [])\n\t\t\t\tthis.templates = list\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnMailTab] could not read the templates', err)\n\t\t\t\tthis.templates = []\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftFromTemplate() {\n\t\t\treturn this.openDraft(this.selectedTemplate)\n\t\t},\n\n\t\t/**\n\t\t * \"Compose mail\": the empty draft through the app's endpoint. Without\n\t\t * one the button is a link to Mail's composer and needs no handler.\n\t\t *\n\t\t * @return {Promise<void>|undefined}\n\t\t */\n\t\tonCompose() {\n\t\t\treturn this.draftEnabled ? this.draftBlank() : undefined\n\t\t},\n\n\t\t/**\n\t\t * \"Link mail\": the linked mail's picker, which links the chosen\n\t\t * message to the record through its own endpoint.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\topenLinkPicker() {\n\t\t\tconst linked = this.$refs.linkedMail\n\t\t\tif (linked && typeof linked.openPicker === 'function') {\n\t\t\t\tlinked.openPicker()\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tdraftBlank() {\n\t\t\tthis.selectedTemplate = null\n\t\t\treturn this.openDraft(null)\n\t\t},\n\n\t\t/**\n\t\t * Ask the app to create a Nextcloud Mail draft and open it.\n\t\t *\n\t\t * @param {object|null} template The chosen template, or null for an empty draft.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openDraft(template) {\n\t\t\tif (this.closed || !this.draftSource) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.drafting = true\n\t\t\tthis.error = ''\n\t\t\tthis.unresolved = []\n\t\t\ttry {\n\t\t\t\tconst id = template ? template[this.templateIdField] : (this.draftSource.blankTemplateId || 'blank')\n\t\t\t\tconst path = interpolateUrlTokens(this.draftSource.url, this.tokenContext)\n\t\t\t\t\t.replace('{templateId}', encodeURIComponent(String(id)))\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst { data } = await axios.post(generateUrl(path), {})\n\t\t\t\tthis.unresolved = Array.isArray(data && data.unresolved) ? data.unresolved : []\n\t\t\t\tif (data && data.draftUrl) {\n\t\t\t\t\twindow.open(data.draftUrl, '_blank', 'noopener')\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event drafted A draft was made from a template. Carries the\n\t\t\t\t * draft endpoint's response, including `draftUrl` and\n\t\t\t\t * `unresolved` when the endpoint returns them.\n\t\t\t\t * @type {object}\n\t\t\t\t */\n\t\t\t\tthis.$emit('drafted', data)\n\t\t\t} catch (err) {\n\t\t\t\tconst response = err && err.response\n\t\t\t\tif (response && response.status === 404) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\tthis.error = (response && response.data && response.data.message)\n\t\t\t\t\t|| (err && err.message)\n\t\t\t\t\t|| t('nextcloud-vue', 'The draft could not be opened.')\n\t\t\t} finally {\n\t\t\t\tthis.drafting = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a template field the way the template spells it.\n\t\t *\n\t\t * @param {string} name The field name.\n\t\t * @return {string} `{{name}}`.\n\t\t */\n\t\tplaceholderOf(name) {\n\t\t\treturn `{{${name}}}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-mail-tab {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tpadding: 12px;\n}\n\n.cn-mail-tab__header {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 10px 16px;\n}\n\n.cn-mail-tab__heading {\n\tdisplay: flex;\n\tflex: 1 1 260px;\n\tflex-direction: column;\n\tgap: 3px;\n\tmin-width: 0;\n}\n\n.cn-mail-tab__header-actions {\n\tdisplay: flex;\n\tflex: none;\n\tflex-wrap: wrap;\n\tgap: 8px;\n}\n\n.cn-mail-tab__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: bold;\n}\n\n.cn-mail-tab__subtitle,\n.cn-mail-tab__hint {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-mail-tab__template-row {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px;\n}\n\n.cn-mail-tab__template-select {\n\tflex: 1 1 260px;\n}\n\n.cn-mail-tab__unresolved {\n\tmargin: 4px 0 0;\n\tpadding-inline-start: 20px;\n}\n\n.cn-mail-tab__unresolved code {\n\tbackground: var(--color-background-dark);\n\tpadding: 2px 6px;\n\tborder-radius: var(--border-radius);\n\tfont-family: var(--font-face-mono, monospace);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_createVNode","_toDisplayString","_createTextVNode","_createElementVNode","_Fragment","_renderList"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnNotificationMatrix.vue2.js';
|
|
|
2
2
|
import { render } from './CnNotificationMatrix.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-93dc585a";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnNotificationMatrix.vue2.js","sources":["../../../../src/components/CnNotificationMatrix/CnNotificationMatrix.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-notification-matrix\" :class=\"{ 'cn-notification-matrix--board': isBoardLook }\" data-testid=\"cn-notification-matrix\">\n\t\t<p v-if=\"adminMode\" class=\"cn-notification-matrix__note\" data-testid=\"cn-nm-admin-note\">\n\t\t\t{{ adminNote }}\n\t\t</p>\n\n\t\t<table class=\"cn-notification-matrix__table\" data-testid=\"cn-nm-table\">\n\t\t\t<caption class=\"cn-notification-matrix__caption\">\n\t\t\t\t{{ tableCaption }}\n\t\t\t</caption>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ eventHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`ch-${channel.id}`\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-channel-header\"\n\t\t\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t\t\t{{ channel.label }}\n\t\t\t\t\t\t<template v-if=\"!isBoardLook\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody v-for=\"group in groups\" :key=\"`grp-${group.key}`\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th :colspan=\"channels.length + 1\" scope=\"colgroup\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__group\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\"\n\t\t\t\t\t\t\t:aria-expanded=\"String(!isCollapsed(group.key))\"\n\t\t\t\t\t\t\t@click=\"toggleGroup(group.key)\">\n\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rowsFor(group)\"\n\t\t\t\t\tv-show=\"!isCollapsed(group.key)\"\n\t\t\t\t\t:key=\"`ev-${row.event.id}-${row.scope}`\"\n\t\t\t\t\tdata-testid=\"cn-nm-row\"\n\t\t\t\t\t:class=\"{ 'cn-notification-matrix__row--scoped': !row.isGlobal }\"\n\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t:data-scope=\"row.scope\">\n\t\t\t\t\t<th scope=\"row\" :class=\"{ 'cn-notification-matrix__scope': !row.isGlobal }\">\n\t\t\t\t\t\t<template v-if=\"row.isGlobal\">\n\t\t\t\t\t\t\t{{ row.event.label }}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"row.event.immediate\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-immediate\">\n\t\t\t\t\t\t\t\t{{ immediateText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tv-if=\"addableScopes(row.event).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-add-scope\"\n\t\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t\t:aria-label=\"addScopeLabel(row.event)\"\n\t\t\t\t\t\t\t\tvalue=\"\"\n\t\t\t\t\t\t\t\t@change=\"onAddScope(row.event, $event)\">\n\t\t\t\t\t\t\t\t<option value=\"\">\n\t\t\t\t\t\t\t\t\t{{ addScopeText }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\tv-for=\"choice in addableScopes(row.event)\"\n\t\t\t\t\t\t\t\t\t:key=\"`sc-${row.event.id}-${choice.id}`\"\n\t\t\t\t\t\t\t\t\t:value=\"choice.id\">\n\t\t\t\t\t\t\t\t\t{{ choice.label }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<span data-testid=\"cn-nm-scope-label\">{{ scopeLabel(row.scope) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`c-${row.event.id}-${row.scope}-${channel.id}`\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-cell\"\n\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-level=\"cellFor(row.event, channel, row.scope).level\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-toggle\"\n\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:checked=\"cellFor(row.event, channel, row.scope).value\"\n\t\t\t\t\t\t\t:disabled=\"isLockedFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-label=\"cellLabelFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-describedby=\"isBoardLook ? sourceIdFor(row.event, channel, row.scope) : null\"\n\t\t\t\t\t\t\t@change=\"onToggle(row.event, channel, $event, row.scope)\">\n\t\t\t\t\t\t<!-- The board look draws only the checkbox; why it is set stays said, and stays visible when the cell is locked. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t:id=\"sourceIdFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t:class=\"{ 'hidden-visually': isBoardLook && !isLockedFor(row.event, channel, row.scope) }\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-source\">\n\t\t\t\t\t\t\t{{ sourceTextFor(row.event, channel, row.scope) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<!-- The board look keeps the grid to its checkboxes; each channel's bundling, its time and the test send live here. -->\n\t\t<details\n\t\t\tv-if=\"isBoardLook\"\n\t\t\tclass=\"cn-notification-matrix__channels\"\n\t\t\tdata-testid=\"cn-nm-channel-settings\">\n\t\t\t<summary class=\"cn-notification-matrix__channels-summary\">\n\t\t\t\t{{ channelSettingsTitle }}\n\t\t\t</summary>\n\t\t\t<div\n\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t:key=\"`cs-${channel.id}`\"\n\t\t\t\tclass=\"cn-notification-matrix__channel\"\n\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t<h4 class=\"cn-notification-matrix__channel-name\">\n\t\t\t\t\t{{ channel.label }}\n\t\t\t\t</h4>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t</span>\n\n\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<button\n\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t</button>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</details>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport {\n\tchannelAvailability,\n\tPREFERENCE_LEVELS,\n\tresolvePreference,\n} from '../../utils/notificationPreference.js'\nimport {\n\tGLOBAL_SCOPE,\n\tscopeRowsFor,\n\tvalueForScope,\n} from '../../utils/preferenceScopes.js'\n\n/**\n * CnNotificationMatrix — one screen where a person chooses what notifies them,\n * and finds out why they are getting what they are getting.\n *\n * 🔴 IT DOES NOT REPLACE `CnNotificationPreferences`, IT SITS BESIDE IT. That\n * component is a self-contained settings pane: it takes no props, fetches for\n * itself and scopes to the app whose settings modal is open, and `CnAppRoot`\n * mounts it as the default of its `#user-settings` slot. This one takes a\n * catalogue from its host, knows four levels rather than two and has a channel\n * axis. Replacing the older one in place, which an earlier revision of this\n * change did, left every app that mounts it through `CnAppRoot` rendering an\n * empty table with no error: a silent regression, not a breakage anybody would\n * see in a test.\n *\n * Events down, channels across. The catalogue is the host's, so this component\n * never knows what a \"term expires\" event is.\n *\n * 🔴 IT NEVER SHOWS A SETTING THAT DOES NOT APPLY. That is the whole point of\n * the screen and it is the one way it can fail while looking perfect. Three\n * cases, each handled rather than hidden:\n *\n * - A FORCED cell is locked, says who forced it and why. An administrator\n * has taken the choice away; a toggle the person can move which then does\n * nothing is worse than a locked one, because they believe they acted.\n * - A channel the instance has not configured is disabled with the reason.\n * - A channel REFUSED for this kind and recipient says the rule. An internal\n * notice to an outside recipient is a rule working, not a gap to fix, and\n * \"no channels available\" would send somebody to the wrong place.\n *\n * 🔴 IT IS A REAL TABLE. A grid of checkboxes with no row and column headers\n * is unreadable to a screen reader: every cell is \"checkbox, checked\" with no\n * way to know which event or which channel. The header cells are `th` with\n * scopes, and each toggle carries a name that says both.\n *\n * 🔴 A PREFERENCE IS SET GLOBALLY OR FOR ONE CASE DOMAIN. \"Tell me when a term\n * expires\" is usually too much and \"never\" is too little, so the event row is\n * the global one and scoped rows sit indented under it. The narrower row wins\n * only where it applies: everywhere else the global row still stands, because\n * a scope that replaced it would silence every other domain the moment\n * somebody narrowed one.\n *\n * 🔴 ONLY EVENTS IN THE CATALOGUE ARE LISTED. A stored preference for an event\n * that no longer exists is not rendered, because a switch for something that\n * can no longer happen is a lie; the store drops it on the next write.\n *\n * 🔴 A TEST SEND REPORTS WHAT HAPPENED, INCLUDING A REFUSAL. Nobody should have\n * to wait for a real event to find out a channel is broken, and a button that\n * silently did nothing would read as proof the channel works.\n *\n * @event {object} change — A cell was set. Payload: `{ eventId, channelId, scope, value }`.\n * @event {object} add-scope — A scope was added to a row. Payload: `{ eventId, scope }`.\n * @event {object} digest-change — A channel's digest was set. Payload: `{ channelId, mode, timeOfDay }`.\n * @event {object} test-send — A test was asked for. Payload: `{ channelId }`.\n */\nexport default {\n\tname: 'CnNotificationMatrix',\n\n\tinject: {\n\t\t/** The look CnAppRoot (or the page) draws in. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The events, each `{ id, label, group, groupLabel, immediate,\n\t\t * appDefault }`. From the host's catalogue.\n\t\t */\n\t\tevents: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The channels, each `{ id, label, configured, unconfiguredReason }`. */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The group's values, as `{ [eventId]: { [channelId]: boolean } }`. */\n\t\tgroupValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** This person's values, same shape. */\n\t\tpersonalValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What an administrator has forced, as\n\t\t * `{ [eventId]: { [channelId]: { value, by, reason } } }`.\n\t\t *\n\t\t * A level ABOVE the person's own rather than another default: it is\n\t\t * the administrator removing the choice, not setting it.\n\t\t */\n\t\tforcedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What the platform refuses for this recipient, same shape, each\n\t\t * `{ reason }`. A refusal is not an absence: it is a rule, and the\n\t\t * screen says which.\n\t\t */\n\t\trefusals: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether this is the admin screen writing the group's defaults. The\n\t\t * screen says a person's own value wins, so an administrator is not\n\t\t * surprised when somebody does not get what they set.\n\t\t */\n\t\tadminMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * This person's narrower values, as\n\t\t * `{ [eventId]: { [channelId]: { [scope]: boolean } } }`.\n\t\t *\n\t\t * Separate from `personalValues` rather than replacing it, so the\n\t\t * global row keeps the flat shape every existing caller passes. A row\n\t\t * left unset here falls through to the global value, which is what\n\t\t * makes an absent scoped row an unanswered question rather than a no.\n\t\t */\n\t\tscopedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The scopes a row can be narrowed to, each `{ id, label }`. Case\n\t\t * domains or record types, named by whoever configured the app.\n\t\t */\n\t\tscopeChoices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The digest per channel, as `{ [channelId]: { mode, timeOfDay } }`\n\t\t * where mode is `off`, `daily` or `weekly`.\n\t\t */\n\t\tdigest: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The last test send per channel, as\n\t\t * `{ [channelId]: { ok, message } }`. A refusal belongs here as much\n\t\t * as a success: both are results.\n\t\t */\n\t\ttestResults: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['change', 'add-scope', 'digest-change', 'test-send'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Groups the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the matrix draws the board's grid.\n\t\t *\n\t\t * @return {boolean} True in the board look.\n\t\t * @spec openspec/changes/notification-matrix-board-grid/specs/notification-matrix/spec.md\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/** @return {string} The disclosure under the board grid. */\n\t\tchannelSettingsTitle() {\n\t\t\treturn t('nextcloud-vue', 'Bundling and test message')\n\t\t},\n\n\t\t/** @return {Array<object>} The events, in their groups. */\n\t\tgroups() {\n\t\t\tconst byKey = new Map()\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst key = String(event?.group ?? '')\n\t\t\t\tif (byKey.has(key) === false) {\n\t\t\t\t\tbyKey.set(key, {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: String(event?.groupLabel ?? key) || this.ungroupedText,\n\t\t\t\t\t\tevents: [],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tbyKey.get(key).events.push(event)\n\t\t\t}\n\t\t\treturn [...byKey.values()]\n\t\t},\n\n\t\t/** @return {string} The table's caption. */\n\t\ttableCaption() {\n\t\t\treturn t('nextcloud-vue', 'What notifies you, and over which channel')\n\t\t},\n\n\t\t/** @return {string} The events column header. */\n\t\teventHeader() {\n\t\t\treturn t('nextcloud-vue', 'Event')\n\t\t},\n\n\t\t/** @return {string} What an ungrouped event group is called. */\n\t\tungroupedText() {\n\t\t\treturn t('nextcloud-vue', 'Other')\n\t\t},\n\n\t\t/** @return {string} What an unusable channel says with no reason given. */\n\t\tunavailableText() {\n\t\t\treturn t('nextcloud-vue', 'Not available on this instance')\n\t\t},\n\n\t\t/** @return {string} What an immediate event says. */\n\t\timmediateText() {\n\t\t\treturn t('nextcloud-vue', 'Always sent straight away, never held for a digest')\n\t\t},\n\n\t\t/** @return {Array<object>} The digest choices, in the order they escalate. */\n\t\tdigestModes() {\n\t\t\treturn [\n\t\t\t\t{ id: 'off', label: t('nextcloud-vue', 'Send each one as it happens') },\n\t\t\t\t{ id: 'daily', label: t('nextcloud-vue', 'Once a day') },\n\t\t\t\t{ id: 'weekly', label: t('nextcloud-vue', 'Once a week') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} What the digest control is called. */\n\t\tdigestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Bundle these')\n\t\t},\n\n\t\t/** @return {string} What the time control is called. */\n\t\tdigestTimeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Time of day')\n\t\t},\n\n\t\t/** @return {string} What a digest says about the events it cannot hold. */\n\t\tdigestImmediateNote() {\n\t\t\treturn t('nextcloud-vue', 'Events marked as urgent are still sent straight away.')\n\t\t},\n\n\t\t/** @return {string} What the test button says. */\n\t\ttestSendText() {\n\t\t\treturn t('nextcloud-vue', 'Send a test')\n\t\t},\n\n\t\t/** @return {string} What the add-a-scope control says when nothing is chosen. */\n\t\taddScopeText() {\n\t\t\treturn t('nextcloud-vue', 'Set this for one kind of case')\n\t\t},\n\n\t\t/** @return {string} What the admin screen says about a person's own value. */\n\t\tadminNote() {\n\t\t\treturn t('nextcloud-vue', \"These are the group's defaults. Somebody who has set their own value keeps it, unless a channel is forced.\")\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The id of a cell's source line, which describes its checkbox.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The row's scope.\n\t\t * @return {string} The id.\n\t\t */\n\t\tsourceIdFor(event, channel, scope) {\n\t\t\treturn `cn-nm-src-${this.$?.uid ?? 0}-${event.id}-${scope}-${channel.id}`.replace(/[^A-Za-z0-9_-]/g, '_')\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * Whether a channel can be used at all, for any event.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} Its availability.\n\t\t */\n\t\tchannelState(channel) {\n\t\t\treturn channelAvailability({ channel })\n\t\t},\n\n\t\t/**\n\t\t * One cell, resolved.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcell(event, channel) {\n\t\t\treturn this.cellFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * One cell on one row, resolved.\n\t\t *\n\t\t * The scoped value is a NARROWER ANSWER TO THE SAME QUESTION, not a\n\t\t * separate preference, so an unset scoped cell falls back to the\n\t\t * person's global value and from there to the group and the default.\n\t\t * Reading an unset scoped cell as false would override both.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcellFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\n\t\t\treturn resolvePreference({\n\t\t\t\tappDefault: event?.appDefault === true,\n\t\t\t\tgroupValue: this.at(this.groupValues, event, channel),\n\t\t\t\tpersonalValue: narrow.value === null\n\t\t\t\t\t? this.at(this.personalValues, event, channel)\n\t\t\t\t\t: narrow.value,\n\t\t\t\tforced: this.at(this.forcedValues, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows one group renders: each event, then its scopes.\n\t\t *\n\t\t * Only events in the catalogue. A stored preference for an event the\n\t\t * catalogue no longer has is not listed, because a switch for\n\t\t * something that can no longer happen is a lie.\n\t\t *\n\t\t * @param {object} group The group.\n\t\t * @return {Array<object>} `{ event, scope, isGlobal }`.\n\t\t */\n\t\trowsFor(group) {\n\t\t\tconst rows = []\n\t\t\tfor (const event of group.events) {\n\t\t\t\tfor (const row of scopeRowsFor({ values: this.scopedValues, eventId: event.id })) {\n\t\t\t\t\trows.push({ event, scope: row.scope, isGlobal: row.isGlobal })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn rows\n\t\t},\n\n\t\t/**\n\t\t * The scopes this row can still be narrowed to.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {Array<object>} The remaining choices.\n\t\t */\n\t\taddableScopes(event) {\n\t\t\tconst taken = new Set(scopeRowsFor({ values: this.scopedValues, eventId: event?.id })\n\t\t\t\t.map((row) => row.scope))\n\t\t\treturn this.scopeChoices.filter((choice) => taken.has(String(choice?.id)) === false)\n\t\t},\n\n\t\t/**\n\t\t * What a scope is called on screen.\n\t\t *\n\t\t * @param {string} scope The scope.\n\t\t * @return {string} Its label, or the id when nobody named it.\n\t\t */\n\t\tscopeLabel(scope) {\n\t\t\tconst choice = this.scopeChoices.find((entry) => String(entry?.id) === scope)\n\t\t\treturn String(choice?.label ?? scope)\n\t\t},\n\n\t\t/**\n\t\t * Whether the platform refuses this channel for this event.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The availability.\n\t\t */\n\t\tcellAvailability(event, channel) {\n\t\t\treturn channelAvailability({\n\t\t\t\tchannel,\n\t\t\t\trefusal: this.at(this.refusals, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A value out of one of the nested maps.\n\t\t *\n\t\t * @param {object} map The map.\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {(boolean|object|null)} The value, or null.\n\t\t */\n\t\tat(map, event, channel) {\n\t\t\tconst row = map?.[event?.id]\n\t\t\tconst value = row?.[channel?.id]\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLocked(event, channel) {\n\t\t\treturn this.isLockedFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell on one row cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLockedFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\treturn this.cellFor(event, channel, scope).editable === false\n\t\t\t\t|| this.cellAvailability(event, channel).usable === false\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called to somebody who cannot see the table.\n\t\t *\n\t\t * Says the event, the channel and why it is locked when it is. A\n\t\t * disabled checkbox with no name is a dead end for a screen reader.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabel(event, channel) {\n\t\t\treturn this.cellLabelFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called, saying which row it is on.\n\t\t *\n\t\t * A scoped row is indented, and indentation is invisible to a screen\n\t\t * reader: without the scope in the name, two checkboxes for the same\n\t\t * event and channel are indistinguishable.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabelFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst base = scope === GLOBAL_SCOPE\n\t\t\t\t? t('nextcloud-vue', '{event} over {channel}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t})\n\t\t\t\t: t('nextcloud-vue', '{event} over {channel}, for {scope}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t\tscope: this.scopeLabel(scope),\n\t\t\t\t\t})\n\t\t\tconst reason = this.lockReasonFor(event, channel, scope)\n\t\t\treturn reason === '' ? base : `${base}. ${reason}`\n\t\t},\n\n\t\t/**\n\t\t * Why a cell is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReason(event, channel) {\n\t\t\treturn this.lockReasonFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Why a cell on one row is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReasonFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst availability = this.cellAvailability(event, channel)\n\t\t\tif (availability.usable === false) {\n\t\t\t\treturn availability.reason || this.unavailableText\n\t\t\t}\n\n\t\t\tconst resolved = this.cellFor(event, channel, scope)\n\t\t\tif (resolved.editable === true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\tif (resolved.forcedBy !== '' && resolved.reason !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}: {reason}', {\n\t\t\t\t\tby: resolved.forcedBy,\n\t\t\t\t\treason: resolved.reason,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (resolved.forcedBy !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}, and cannot be changed here', { by: resolved.forcedBy })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Set by an administrator, and cannot be changed here')\n\t\t},\n\n\t\t/**\n\t\t * What a cell says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceText(event, channel) {\n\t\t\treturn this.sourceTextFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell on one row says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceTextFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst locked = this.lockReasonFor(event, channel, scope)\n\t\t\tif (locked !== '') {\n\t\t\t\treturn locked\n\t\t\t}\n\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\t\t\tif (scope !== GLOBAL_SCOPE && narrow.scope === GLOBAL_SCOPE) {\n\t\t\t\t// Said out loud, because an unanswered narrower question looks\n\t\t\t\t// exactly like an answered one: the checkbox shows the same\n\t\t\t\t// state either way.\n\t\t\t\treturn t('nextcloud-vue', 'Follows the row above')\n\t\t\t}\n\n\t\t\tconst level = this.cellFor(event, channel, scope).level\n\t\t\tif (level === PREFERENCE_LEVELS.PERSONAL) {\n\t\t\t\treturn t('nextcloud-vue', 'Your choice')\n\t\t\t}\n\t\t\tif (level === PREFERENCE_LEVELS.GROUP) {\n\t\t\t\treturn t('nextcloud-vue', 'Follows your group')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Follows the app default')\n\t\t},\n\n\t\t/**\n\t\t * Whether a group is collapsed.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a group, or open it again.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t */\n\t\ttoggleGroup(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * A cell was set.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t * @param {string} scope The scope of the row it is on.\n\t\t */\n\t\tonToggle(event, channel, domEvent, scope = GLOBAL_SCOPE) {\n\t\t\t// Refuse from here too, not only in the template. A disabled\n\t\t\t// attribute is a rendering; this is the rule.\n\t\t\tif (this.isLockedFor(event, channel, scope)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event change A cell was set.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('change', {\n\t\t\t\teventId: event.id,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tscope,\n\t\t\t\tvalue: domEvent?.target?.checked === true,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A scope was added to a row, from the row itself.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonAddScope(event, domEvent) {\n\t\t\tconst scope = String(domEvent?.target?.value ?? '')\n\t\t\tif (scope === '') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Back to the placeholder, so the control reads as an action\n\t\t\t// rather than as a setting that now says something.\n\t\t\tif (domEvent?.target) {\n\t\t\t\tdomEvent.target.value = ''\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event add-scope A row was narrowed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('add-scope', { eventId: event.id, scope })\n\t\t},\n\n\t\t/**\n\t\t * What a channel's digest is set to.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} `{ mode, timeOfDay }`.\n\t\t */\n\t\tdigestFor(channel) {\n\t\t\tconst set = this.digest?.[channel?.id]\n\t\t\treturn {\n\t\t\t\tmode: String(set?.mode ?? 'off'),\n\t\t\t\ttimeOfDay: String(set?.timeOfDay ?? ''),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The digest mode was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestMode(channel, domEvent) {\n\t\t\t/**\n\t\t\t * @event digest-change Emitted when a reader changes a channel's digest mode or its time of day. Payload: `{ channelId, mode, timeOfDay }`.\n\t\t\t */\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: String(domEvent?.target?.value ?? 'off'),\n\t\t\t\ttimeOfDay: this.digestFor(channel).timeOfDay,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The digest time was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestTime(channel, domEvent) {\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: this.digestFor(channel).mode,\n\t\t\t\ttimeOfDay: String(domEvent?.target?.value ?? ''),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The last test send on a channel, or null when there has been none.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {?object} `{ ok, message }`.\n\t\t */\n\t\ttestResultFor(channel) {\n\t\t\tconst result = this.testResults?.[channel?.id]\n\t\t\treturn result === undefined || result === null ? null : result\n\t\t},\n\n\t\t/**\n\t\t * What a test send reported.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\ttestResultText(channel) {\n\t\t\tconst result = this.testResultFor(channel)\n\t\t\tif (result === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (String(result.message ?? '') !== '') {\n\t\t\t\treturn String(result.message)\n\t\t\t}\n\t\t\t// Only when the server sent no sentence of its own. A refusal\n\t\t\t// without a reason still has to say it was a refusal.\n\t\t\treturn result.ok === true\n\t\t\t\t? t('nextcloud-vue', 'The test arrived.')\n\t\t\t\t: t('nextcloud-vue', 'The test did not arrive.')\n\t\t},\n\n\t\t/**\n\t\t * What the test button is called.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\ttestSendLabel(channel) {\n\t\t\treturn t('nextcloud-vue', 'Send a test over {channel}', { channel: channel.label })\n\t\t},\n\n\t\t/**\n\t\t * What the add-a-scope control is called.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\taddScopeLabel(event) {\n\t\t\treturn t('nextcloud-vue', 'Set {event} for one kind of case', { event: event.label })\n\t\t},\n\n\t\t/**\n\t\t * A test send was asked for.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t */\n\t\tonTestSend(channel) {\n\t\t\t/**\n\t\t\t * @event test-send Emitted when a reader asks for a test notification on one channel. Payload: `{ channelId }`.\n\t\t\t */\n\t\t\tthis.$emit('test-send', { channelId: channel.id })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-notification-matrix__table {\n\tinline-size: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-notification-matrix__caption {\n\ttext-align: start;\n\tpadding-block-end: 8px;\n}\n\n.cn-notification-matrix__table th,\n.cn-notification-matrix__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-block-end: 1px solid var(--color-border);\n\tvertical-align: top;\n}\n\n.cn-notification-matrix__muted {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85rem;\n}\n\n.cn-notification-matrix__group {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-notification-matrix__scope {\n\tpadding-inline-start: 24px !important;\n\tfont-weight: normal;\n}\n\n.cn-notification-matrix__row--scoped th,\n.cn-notification-matrix__row--scoped td {\n\tborder-block-end-style: dotted;\n}\n\n.cn-notification-matrix__test {\n\tmargin-block-start: 4px;\n}\n\n.cn-notification-matrix__note {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The board look (DqPersoonlijkeInstellingen \"Meldingen\"): muted 13px/600\n headers over a hairline, the channel columns 96px with a centred 20px\n checkbox, the event rows 10px high with the soft rule. The caption stays for\n assistive technology. */\n.cn-notification-matrix--board {\n\t.cn-notification-matrix__caption {\n\t\tposition: absolute;\n\t\tinline-size: 1px;\n\t\tblock-size: 1px;\n\t\toverflow: hidden;\n\t\tclip-path: inset(50%);\n\t\twhite-space: nowrap;\n\t}\n\n\t/* A theme that colours every th (thematiq's table header token) leaves\n\t the board grid's head white. */\n\t.cn-notification-matrix__table {\n\t\t--nldesign-component-table-header-background-color: transparent;\n\t\tfont-size: 14px;\n\t}\n\n\t.cn-notification-matrix__table thead th {\n\t\tpadding: 8px 10px 8px 0;\n\t\tline-height: 1.2;\n\t\tborder-block-end: 1px solid var(--color-border);\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tvertical-align: bottom;\n\t}\n\n\t.cn-notification-matrix__table thead th[data-channel] {\n\t\tinline-size: 96px;\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody th,\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding: 10px 10px 10px 0;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t\tfont-weight: 400;\n\t\tline-height: 1.4;\n\t\tvertical-align: middle;\n\t}\n\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody td input[type='checkbox'] {\n\t\tinline-size: 20px;\n\t\tmin-inline-size: 20px;\n\t\tblock-size: 20px;\n\t\tmin-block-size: 20px;\n\t\tmargin: 0;\n\t}\n\n\t.cn-notification-matrix__group {\n\t\tpadding: 4px 0;\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t.cn-notification-matrix__channels {\n\t\tmargin-block-start: 16px;\n\t}\n\n\t.cn-notification-matrix__channels-summary {\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t\tcursor: pointer;\n\t}\n\n\t.cn-notification-matrix__channel {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\talign-items: flex-end;\n\t\tgap: 8px 16px;\n\t\tpadding-block: 10px;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t}\n\n\t.cn-notification-matrix__channel-name {\n\t\tflex: 1 0 100%;\n\t\tmargin: 0;\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t}\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnNotificationMatrix.vue2.js","sources":["../../../../src/components/CnNotificationMatrix/CnNotificationMatrix.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-notification-matrix\" :class=\"{ 'cn-notification-matrix--board': isBoardLook }\" data-testid=\"cn-notification-matrix\">\n\t\t<p v-if=\"adminMode\" class=\"cn-notification-matrix__note\" data-testid=\"cn-nm-admin-note\">\n\t\t\t{{ adminNote }}\n\t\t</p>\n\n\t\t<table class=\"cn-notification-matrix__table\" data-testid=\"cn-nm-table\">\n\t\t\t<caption class=\"cn-notification-matrix__caption\">\n\t\t\t\t{{ tableCaption }}\n\t\t\t</caption>\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ eventHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`ch-${channel.id}`\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-channel-header\"\n\t\t\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t\t\t{{ channel.label }}\n\t\t\t\t\t\t<template v-if=\"!isBoardLook\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\n\t\t\t<tbody v-for=\"group in groups\" :key=\"`grp-${group.key}`\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th :colspan=\"channels.length + 1\" scope=\"colgroup\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__group\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-group\"\n\t\t\t\t\t\t\t:data-group=\"group.key\"\n\t\t\t\t\t\t\t:aria-expanded=\"String(!isCollapsed(group.key))\"\n\t\t\t\t\t\t\t@click=\"toggleGroup(group.key)\">\n\t\t\t\t\t\t\t{{ group.label }}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rowsFor(group)\"\n\t\t\t\t\tv-show=\"!isCollapsed(group.key)\"\n\t\t\t\t\t:key=\"`ev-${row.event.id}-${row.scope}`\"\n\t\t\t\t\tdata-testid=\"cn-nm-row\"\n\t\t\t\t\t:class=\"{ 'cn-notification-matrix__row--scoped': !row.isGlobal }\"\n\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t:data-scope=\"row.scope\">\n\t\t\t\t\t<th scope=\"row\" :class=\"{ 'cn-notification-matrix__scope': !row.isGlobal }\">\n\t\t\t\t\t\t<template v-if=\"row.isGlobal\">\n\t\t\t\t\t\t\t{{ row.event.label }}\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"row.event.immediate\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-immediate\">\n\t\t\t\t\t\t\t\t{{ immediateText }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tv-if=\"addableScopes(row.event).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-nm-add-scope\"\n\t\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t\t:aria-label=\"addScopeLabel(row.event)\"\n\t\t\t\t\t\t\t\tvalue=\"\"\n\t\t\t\t\t\t\t\t@change=\"onAddScope(row.event, $event)\">\n\t\t\t\t\t\t\t\t<option value=\"\">\n\t\t\t\t\t\t\t\t\t{{ addScopeText }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\tv-for=\"choice in addableScopes(row.event)\"\n\t\t\t\t\t\t\t\t\t:key=\"`sc-${row.event.id}-${choice.id}`\"\n\t\t\t\t\t\t\t\t\t:value=\"choice.id\">\n\t\t\t\t\t\t\t\t\t{{ choice.label }}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t<span data-testid=\"cn-nm-scope-label\">{{ scopeLabel(row.scope) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</th>\n\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t\t\t:key=\"`c-${row.event.id}-${row.scope}-${channel.id}`\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-cell\"\n\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-level=\"cellFor(row.event, channel, row.scope).level\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-toggle\"\n\t\t\t\t\t\t\t:data-event=\"row.event.id\"\n\t\t\t\t\t\t\t:data-scope=\"row.scope\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:checked=\"cellFor(row.event, channel, row.scope).value\"\n\t\t\t\t\t\t\t:disabled=\"isLockedFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-label=\"cellLabelFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\t:aria-describedby=\"isBoardLook ? sourceIdFor(row.event, channel, row.scope) : null\"\n\t\t\t\t\t\t\t@change=\"onToggle(row.event, channel, $event, row.scope)\">\n\t\t\t\t\t\t<!-- The board look draws only the checkbox; why it is set stays said, and stays visible when the cell is locked. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t:id=\"sourceIdFor(row.event, channel, row.scope)\"\n\t\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\t\t:class=\"{ 'hidden-visually': isBoardLook && !isLockedFor(row.event, channel, row.scope) }\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-source\">\n\t\t\t\t\t\t\t{{ sourceTextFor(row.event, channel, row.scope) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<!-- The board look keeps the grid to its checkboxes; each channel's bundling, its time and the test send live here. -->\n\t\t<details\n\t\t\tv-if=\"isBoardLook\"\n\t\t\tclass=\"cn-notification-matrix__channels\"\n\t\t\tdata-testid=\"cn-nm-channel-settings\">\n\t\t\t<summary class=\"cn-notification-matrix__channels-summary\">\n\t\t\t\t{{ channelSettingsTitle }}\n\t\t\t</summary>\n\t\t\t<div\n\t\t\t\tv-for=\"channel in channels\"\n\t\t\t\t:key=\"`cs-${channel.id}`\"\n\t\t\t\tclass=\"cn-notification-matrix__channel\"\n\t\t\t\t:data-channel=\"channel.id\">\n\t\t\t\t<h4 class=\"cn-notification-matrix__channel-name\">\n\t\t\t\t\t{{ channel.label }}\n\t\t\t\t</h4>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"channelState(channel).usable === false\"\n\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\tdata-testid=\"cn-nm-channel-unusable\">\n\t\t\t\t\t{{ channelState(channel).reason || unavailableText }}\n\t\t\t\t</span>\n\n\t\t\t\t<template v-if=\"channelState(channel).usable !== false\">\n\t\t\t\t\t<label class=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestLabel }}\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-mode\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).mode\"\n\t\t\t\t\t\t\t@change=\"onDigestMode(channel, $event)\">\n\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\tv-for=\"mode in digestModes\"\n\t\t\t\t\t\t\t\t:key=\"`dm-${channel.id}-${mode.id}`\"\n\t\t\t\t\t\t\t\t:value=\"mode.id\">\n\t\t\t\t\t\t\t\t{{ mode.label }}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<label\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\">\n\t\t\t\t\t\t{{ digestTimeLabel }}\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"time\"\n\t\t\t\t\t\t\tdata-testid=\"cn-nm-digest-time\"\n\t\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t\t:value=\"digestFor(channel).timeOfDay\"\n\t\t\t\t\t\t\t@change=\"onDigestTime(channel, $event)\">\n\t\t\t\t\t</label>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"digestFor(channel).mode !== 'off'\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-digest-immediate-note\">\n\t\t\t\t\t\t{{ digestImmediateNote }}\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<button\n\t\t\t\t\t\tclass=\"cn-notification-matrix__test\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-send\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:aria-label=\"testSendLabel(channel)\"\n\t\t\t\t\t\t@click=\"onTestSend(channel)\">\n\t\t\t\t\t\t{{ testSendText }}\n\t\t\t\t\t</button>\n\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"testResultFor(channel) !== null\"\n\t\t\t\t\t\tclass=\"cn-notification-matrix__muted\"\n\t\t\t\t\t\tdata-testid=\"cn-nm-test-result\"\n\t\t\t\t\t\t:data-channel=\"channel.id\"\n\t\t\t\t\t\t:data-ok=\"String(testResultFor(channel).ok === true)\"\n\t\t\t\t\t\trole=\"status\">\n\t\t\t\t\t\t{{ testResultText(channel) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</details>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport {\n\tchannelAvailability,\n\tPREFERENCE_LEVELS,\n\tresolvePreference,\n} from '../../utils/notificationPreference.js'\nimport {\n\tGLOBAL_SCOPE,\n\tscopeRowsFor,\n\tvalueForScope,\n} from '../../utils/preferenceScopes.js'\n\n/**\n * CnNotificationMatrix — one screen where a person chooses what notifies them,\n * and finds out why they are getting what they are getting.\n *\n * 🔴 IT DOES NOT REPLACE `CnNotificationPreferences`, IT SITS BESIDE IT. That\n * component is a self-contained settings pane: it takes no props, fetches for\n * itself and scopes to the app whose settings modal is open, and `CnAppRoot`\n * mounts it as the default of its `#user-settings` slot. This one takes a\n * catalogue from its host, knows four levels rather than two and has a channel\n * axis. Replacing the older one in place, which an earlier revision of this\n * change did, left every app that mounts it through `CnAppRoot` rendering an\n * empty table with no error: a silent regression, not a breakage anybody would\n * see in a test.\n *\n * Events down, channels across. The catalogue is the host's, so this component\n * never knows what a \"term expires\" event is.\n *\n * 🔴 IT NEVER SHOWS A SETTING THAT DOES NOT APPLY. That is the whole point of\n * the screen and it is the one way it can fail while looking perfect. Three\n * cases, each handled rather than hidden:\n *\n * - A FORCED cell is locked, says who forced it and why. An administrator\n * has taken the choice away; a toggle the person can move which then does\n * nothing is worse than a locked one, because they believe they acted.\n * - A channel the instance has not configured is disabled with the reason.\n * - A channel REFUSED for this kind and recipient says the rule. An internal\n * notice to an outside recipient is a rule working, not a gap to fix, and\n * \"no channels available\" would send somebody to the wrong place.\n *\n * 🔴 IT IS A REAL TABLE. A grid of checkboxes with no row and column headers\n * is unreadable to a screen reader: every cell is \"checkbox, checked\" with no\n * way to know which event or which channel. The header cells are `th` with\n * scopes, and each toggle carries a name that says both.\n *\n * 🔴 A PREFERENCE IS SET GLOBALLY OR FOR ONE CASE DOMAIN. \"Tell me when a term\n * expires\" is usually too much and \"never\" is too little, so the event row is\n * the global one and scoped rows sit indented under it. The narrower row wins\n * only where it applies: everywhere else the global row still stands, because\n * a scope that replaced it would silence every other domain the moment\n * somebody narrowed one.\n *\n * 🔴 ONLY EVENTS IN THE CATALOGUE ARE LISTED. A stored preference for an event\n * that no longer exists is not rendered, because a switch for something that\n * can no longer happen is a lie; the store drops it on the next write.\n *\n * 🔴 A TEST SEND REPORTS WHAT HAPPENED, INCLUDING A REFUSAL. Nobody should have\n * to wait for a real event to find out a channel is broken, and a button that\n * silently did nothing would read as proof the channel works.\n *\n * @event {object} change — A cell was set. Payload: `{ eventId, channelId, scope, value }`.\n * @event {object} add-scope — A scope was added to a row. Payload: `{ eventId, scope }`.\n * @event {object} digest-change — A channel's digest was set. Payload: `{ channelId, mode, timeOfDay }`.\n * @event {object} test-send — A test was asked for. Payload: `{ channelId }`.\n */\nexport default {\n\tname: 'CnNotificationMatrix',\n\n\tinject: {\n\t\t/** The look CnAppRoot (or the page) draws in. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The events, each `{ id, label, group, groupLabel, immediate,\n\t\t * appDefault }`. From the host's catalogue.\n\t\t */\n\t\tevents: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The channels, each `{ id, label, configured, unconfiguredReason }`. */\n\t\tchannels: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The group's values, as `{ [eventId]: { [channelId]: boolean } }`. */\n\t\tgroupValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** This person's values, same shape. */\n\t\tpersonalValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What an administrator has forced, as\n\t\t * `{ [eventId]: { [channelId]: { value, by, reason } } }`.\n\t\t *\n\t\t * A level ABOVE the person's own rather than another default: it is\n\t\t * the administrator removing the choice, not setting it.\n\t\t */\n\t\tforcedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * What the platform refuses for this recipient, same shape, each\n\t\t * `{ reason }`. A refusal is not an absence: it is a rule, and the\n\t\t * screen says which.\n\t\t */\n\t\trefusals: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether this is the admin screen writing the group's defaults. The\n\t\t * screen says a person's own value wins, so an administrator is not\n\t\t * surprised when somebody does not get what they set.\n\t\t */\n\t\tadminMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * This person's narrower values, as\n\t\t * `{ [eventId]: { [channelId]: { [scope]: boolean } } }`.\n\t\t *\n\t\t * Separate from `personalValues` rather than replacing it, so the\n\t\t * global row keeps the flat shape every existing caller passes. A row\n\t\t * left unset here falls through to the global value, which is what\n\t\t * makes an absent scoped row an unanswered question rather than a no.\n\t\t */\n\t\tscopedValues: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The scopes a row can be narrowed to, each `{ id, label }`. Case\n\t\t * domains or record types, named by whoever configured the app.\n\t\t */\n\t\tscopeChoices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The digest per channel, as `{ [channelId]: { mode, timeOfDay } }`\n\t\t * where mode is `off`, `daily` or `weekly`.\n\t\t */\n\t\tdigest: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The last test send per channel, as\n\t\t * `{ [channelId]: { ok, message } }`. A refusal belongs here as much\n\t\t * as a success: both are results.\n\t\t */\n\t\ttestResults: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['change', 'add-scope', 'digest-change', 'test-send'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Groups the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the matrix draws the board's grid.\n\t\t *\n\t\t * @return {boolean} True in the board look.\n\t\t * @spec openspec/changes/notification-matrix-board-grid/specs/notification-matrix/spec.md\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/** @return {string} The disclosure under the board grid. */\n\t\tchannelSettingsTitle() {\n\t\t\treturn t('nextcloud-vue', 'Bundling and test message')\n\t\t},\n\n\t\t/** @return {Array<object>} The events, in their groups. */\n\t\tgroups() {\n\t\t\tconst byKey = new Map()\n\t\t\tfor (const event of this.events) {\n\t\t\t\tconst key = String(event?.group ?? '')\n\t\t\t\tif (byKey.has(key) === false) {\n\t\t\t\t\tbyKey.set(key, {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: String(event?.groupLabel ?? key) || this.ungroupedText,\n\t\t\t\t\t\tevents: [],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tbyKey.get(key).events.push(event)\n\t\t\t}\n\t\t\treturn [...byKey.values()]\n\t\t},\n\n\t\t/** @return {string} The table's caption. */\n\t\ttableCaption() {\n\t\t\treturn t('nextcloud-vue', 'What notifies you, and over which channel')\n\t\t},\n\n\t\t/** @return {string} The events column header. */\n\t\teventHeader() {\n\t\t\treturn t('nextcloud-vue', 'Event')\n\t\t},\n\n\t\t/** @return {string} What an ungrouped event group is called. */\n\t\tungroupedText() {\n\t\t\treturn t('nextcloud-vue', 'Other')\n\t\t},\n\n\t\t/** @return {string} What an unusable channel says with no reason given. */\n\t\tunavailableText() {\n\t\t\treturn t('nextcloud-vue', 'Not available on this instance')\n\t\t},\n\n\t\t/** @return {string} What an immediate event says. */\n\t\timmediateText() {\n\t\t\treturn t('nextcloud-vue', 'Always sent straight away, never held for a digest')\n\t\t},\n\n\t\t/** @return {Array<object>} The digest choices, in the order they escalate. */\n\t\tdigestModes() {\n\t\t\treturn [\n\t\t\t\t{ id: 'off', label: t('nextcloud-vue', 'Send each one as it happens') },\n\t\t\t\t{ id: 'daily', label: t('nextcloud-vue', 'Once a day') },\n\t\t\t\t{ id: 'weekly', label: t('nextcloud-vue', 'Once a week') },\n\t\t\t]\n\t\t},\n\n\t\t/** @return {string} What the digest control is called. */\n\t\tdigestLabel() {\n\t\t\treturn t('nextcloud-vue', 'Bundle these')\n\t\t},\n\n\t\t/** @return {string} What the time control is called. */\n\t\tdigestTimeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Time of day')\n\t\t},\n\n\t\t/** @return {string} What a digest says about the events it cannot hold. */\n\t\tdigestImmediateNote() {\n\t\t\treturn t('nextcloud-vue', 'Events marked as urgent are still sent straight away.')\n\t\t},\n\n\t\t/** @return {string} What the test button says. */\n\t\ttestSendText() {\n\t\t\treturn t('nextcloud-vue', 'Send a test')\n\t\t},\n\n\t\t/** @return {string} What the add-a-scope control says when nothing is chosen. */\n\t\taddScopeText() {\n\t\t\treturn t('nextcloud-vue', 'Set this for one kind of case')\n\t\t},\n\n\t\t/** @return {string} What the admin screen says about a person's own value. */\n\t\tadminNote() {\n\t\t\treturn t('nextcloud-vue', \"These are the group's defaults. Somebody who has set their own value keeps it, unless a channel is forced.\")\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The id of a cell's source line, which describes its checkbox.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The row's scope.\n\t\t * @return {string} The id.\n\t\t */\n\t\tsourceIdFor(event, channel, scope) {\n\t\t\treturn `cn-nm-src-${this.$?.uid ?? 0}-${event.id}-${scope}-${channel.id}`.replace(/[^A-Za-z0-9_-]/g, '_')\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * Whether a channel can be used at all, for any event.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} Its availability.\n\t\t */\n\t\tchannelState(channel) {\n\t\t\treturn channelAvailability({ channel })\n\t\t},\n\n\t\t/**\n\t\t * One cell, resolved.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcell(event, channel) {\n\t\t\treturn this.cellFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * One cell on one row, resolved.\n\t\t *\n\t\t * The scoped value is a NARROWER ANSWER TO THE SAME QUESTION, not a\n\t\t * separate preference, so an unset scoped cell falls back to the\n\t\t * person's global value and from there to the group and the default.\n\t\t * Reading an unset scoped cell as false would override both.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {object} The resolution.\n\t\t */\n\t\tcellFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\n\t\t\treturn resolvePreference({\n\t\t\t\tappDefault: event?.appDefault === true,\n\t\t\t\tgroupValue: this.at(this.groupValues, event, channel),\n\t\t\t\tpersonalValue: narrow.value === null\n\t\t\t\t\t? this.at(this.personalValues, event, channel)\n\t\t\t\t\t: narrow.value,\n\t\t\t\tforced: this.at(this.forcedValues, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The rows one group renders: each event, then its scopes.\n\t\t *\n\t\t * Only events in the catalogue. A stored preference for an event the\n\t\t * catalogue no longer has is not listed, because a switch for\n\t\t * something that can no longer happen is a lie.\n\t\t *\n\t\t * @param {object} group The group.\n\t\t * @return {Array<object>} `{ event, scope, isGlobal }`.\n\t\t */\n\t\trowsFor(group) {\n\t\t\tconst rows = []\n\t\t\tfor (const event of group.events) {\n\t\t\t\tfor (const row of scopeRowsFor({ values: this.scopedValues, eventId: event.id })) {\n\t\t\t\t\trows.push({ event, scope: row.scope, isGlobal: row.isGlobal })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn rows\n\t\t},\n\n\t\t/**\n\t\t * The scopes this row can still be narrowed to.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {Array<object>} The remaining choices.\n\t\t */\n\t\taddableScopes(event) {\n\t\t\tconst taken = new Set(scopeRowsFor({ values: this.scopedValues, eventId: event?.id })\n\t\t\t\t.map((row) => row.scope))\n\t\t\treturn this.scopeChoices.filter((choice) => taken.has(String(choice?.id)) === false)\n\t\t},\n\n\t\t/**\n\t\t * What a scope is called on screen.\n\t\t *\n\t\t * @param {string} scope The scope.\n\t\t * @return {string} Its label, or the id when nobody named it.\n\t\t */\n\t\tscopeLabel(scope) {\n\t\t\tconst choice = this.scopeChoices.find((entry) => String(entry?.id) === scope)\n\t\t\treturn String(choice?.label ?? scope)\n\t\t},\n\n\t\t/**\n\t\t * Whether the platform refuses this channel for this event.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} The availability.\n\t\t */\n\t\tcellAvailability(event, channel) {\n\t\t\treturn channelAvailability({\n\t\t\t\tchannel,\n\t\t\t\trefusal: this.at(this.refusals, event, channel),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A value out of one of the nested maps.\n\t\t *\n\t\t * @param {object} map The map.\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {(boolean|object|null)} The value, or null.\n\t\t */\n\t\tat(map, event, channel) {\n\t\t\tconst row = map?.[event?.id]\n\t\t\tconst value = row?.[channel?.id]\n\t\t\treturn value === undefined ? null : value\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLocked(event, channel) {\n\t\t\treturn this.isLockedFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Whether a cell on one row cannot be changed.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {boolean} True when it is locked.\n\t\t */\n\t\tisLockedFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\treturn this.cellFor(event, channel, scope).editable === false\n\t\t\t\t|| this.cellAvailability(event, channel).usable === false\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called to somebody who cannot see the table.\n\t\t *\n\t\t * Says the event, the channel and why it is locked when it is. A\n\t\t * disabled checkbox with no name is a dead end for a screen reader.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabel(event, channel) {\n\t\t\treturn this.cellLabelFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell is called, saying which row it is on.\n\t\t *\n\t\t * A scoped row is indented, and indentation is invisible to a screen\n\t\t * reader: without the scope in the name, two checkboxes for the same\n\t\t * event and channel are indistinguishable.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcellLabelFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst base = scope === GLOBAL_SCOPE\n\t\t\t\t? t('nextcloud-vue', '{event} over {channel}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t})\n\t\t\t\t: t('nextcloud-vue', '{event} over {channel}, for {scope}', {\n\t\t\t\t\t\tevent: event.label,\n\t\t\t\t\t\tchannel: channel.label,\n\t\t\t\t\t\tscope: this.scopeLabel(scope),\n\t\t\t\t\t})\n\t\t\tconst reason = this.lockReasonFor(event, channel, scope)\n\t\t\treturn reason === '' ? base : `${base}. ${reason}`\n\t\t},\n\n\t\t/**\n\t\t * Why a cell is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReason(event, channel) {\n\t\t\treturn this.lockReasonFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * Why a cell on one row is locked, in words.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The reason, or the empty string.\n\t\t */\n\t\tlockReasonFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst availability = this.cellAvailability(event, channel)\n\t\t\tif (availability.usable === false) {\n\t\t\t\treturn availability.reason || this.unavailableText\n\t\t\t}\n\n\t\t\tconst resolved = this.cellFor(event, channel, scope)\n\t\t\tif (resolved.editable === true) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\tif (resolved.forcedBy !== '' && resolved.reason !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}: {reason}', {\n\t\t\t\t\tby: resolved.forcedBy,\n\t\t\t\t\treason: resolved.reason,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif (resolved.forcedBy !== '') {\n\t\t\t\treturn t('nextcloud-vue', 'Set by {by}, and cannot be changed here', { by: resolved.forcedBy })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Set by an administrator, and cannot be changed here')\n\t\t},\n\n\t\t/**\n\t\t * What a cell says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceText(event, channel) {\n\t\t\treturn this.sourceTextFor(event, channel, GLOBAL_SCOPE)\n\t\t},\n\n\t\t/**\n\t\t * What a cell on one row says under its toggle.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {string} scope The scope of this row.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\tsourceTextFor(event, channel, scope = GLOBAL_SCOPE) {\n\t\t\tconst locked = this.lockReasonFor(event, channel, scope)\n\t\t\tif (locked !== '') {\n\t\t\t\treturn locked\n\t\t\t}\n\n\t\t\tconst narrow = valueForScope({\n\t\t\t\tvalues: this.scopedValues,\n\t\t\t\teventId: event?.id,\n\t\t\t\tchannelId: channel?.id,\n\t\t\t\tscope,\n\t\t\t})\n\t\t\tif (scope !== GLOBAL_SCOPE && narrow.scope === GLOBAL_SCOPE) {\n\t\t\t\t// Said out loud, because an unanswered narrower question looks\n\t\t\t\t// exactly like an answered one: the checkbox shows the same\n\t\t\t\t// state either way.\n\t\t\t\treturn t('nextcloud-vue', 'Follows the row above')\n\t\t\t}\n\n\t\t\tconst level = this.cellFor(event, channel, scope).level\n\t\t\tif (level === PREFERENCE_LEVELS.PERSONAL) {\n\t\t\t\treturn t('nextcloud-vue', 'Your choice')\n\t\t\t}\n\t\t\tif (level === PREFERENCE_LEVELS.GROUP) {\n\t\t\t\treturn t('nextcloud-vue', 'Follows your group')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Follows the app default')\n\t\t},\n\n\t\t/**\n\t\t * Whether a group is collapsed.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a group, or open it again.\n\t\t *\n\t\t * @param {string} key The group.\n\t\t */\n\t\ttoggleGroup(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * A cell was set.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t * @param {string} scope The scope of the row it is on.\n\t\t */\n\t\tonToggle(event, channel, domEvent, scope = GLOBAL_SCOPE) {\n\t\t\t// Refuse from here too, not only in the template. A disabled\n\t\t\t// attribute is a rendering; this is the rule.\n\t\t\tif (this.isLockedFor(event, channel, scope)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event change A cell was set.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('change', {\n\t\t\t\teventId: event.id,\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tscope,\n\t\t\t\tvalue: domEvent?.target?.checked === true,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * A scope was added to a row, from the row itself.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonAddScope(event, domEvent) {\n\t\t\tconst scope = String(domEvent?.target?.value ?? '')\n\t\t\tif (scope === '') {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Back to the placeholder, so the control reads as an action\n\t\t\t// rather than as a setting that now says something.\n\t\t\tif (domEvent?.target) {\n\t\t\t\tdomEvent.target.value = ''\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * @event add-scope A row was narrowed.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('add-scope', { eventId: event.id, scope })\n\t\t},\n\n\t\t/**\n\t\t * What a channel's digest is set to.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {object} `{ mode, timeOfDay }`.\n\t\t */\n\t\tdigestFor(channel) {\n\t\t\tconst set = this.digest?.[channel?.id]\n\t\t\treturn {\n\t\t\t\tmode: String(set?.mode ?? 'off'),\n\t\t\t\ttimeOfDay: String(set?.timeOfDay ?? ''),\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The digest mode was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestMode(channel, domEvent) {\n\t\t\t/**\n\t\t\t * @event digest-change Emitted when a reader changes a channel's digest mode or its time of day. Payload: `{ channelId, mode, timeOfDay }`.\n\t\t\t */\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: String(domEvent?.target?.value ?? 'off'),\n\t\t\t\ttimeOfDay: this.digestFor(channel).timeOfDay,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The digest time was changed.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @param {Event} domEvent The change event.\n\t\t */\n\t\tonDigestTime(channel, domEvent) {\n\t\t\tthis.$emit('digest-change', {\n\t\t\t\tchannelId: channel.id,\n\t\t\t\tmode: this.digestFor(channel).mode,\n\t\t\t\ttimeOfDay: String(domEvent?.target?.value ?? ''),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The last test send on a channel, or null when there has been none.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {?object} `{ ok, message }`.\n\t\t */\n\t\ttestResultFor(channel) {\n\t\t\tconst result = this.testResults?.[channel?.id]\n\t\t\treturn result === undefined || result === null ? null : result\n\t\t},\n\n\t\t/**\n\t\t * What a test send reported.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The sentence.\n\t\t */\n\t\ttestResultText(channel) {\n\t\t\tconst result = this.testResultFor(channel)\n\t\t\tif (result === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (String(result.message ?? '') !== '') {\n\t\t\t\treturn String(result.message)\n\t\t\t}\n\t\t\t// Only when the server sent no sentence of its own. A refusal\n\t\t\t// without a reason still has to say it was a refusal.\n\t\t\treturn result.ok === true\n\t\t\t\t? t('nextcloud-vue', 'The test arrived.')\n\t\t\t\t: t('nextcloud-vue', 'The test did not arrive.')\n\t\t},\n\n\t\t/**\n\t\t * What the test button is called.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\ttestSendLabel(channel) {\n\t\t\treturn t('nextcloud-vue', 'Send a test over {channel}', { channel: channel.label })\n\t\t},\n\n\t\t/**\n\t\t * What the add-a-scope control is called.\n\t\t *\n\t\t * @param {object} event The event.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\taddScopeLabel(event) {\n\t\t\treturn t('nextcloud-vue', 'Set {event} for one kind of case', { event: event.label })\n\t\t},\n\n\t\t/**\n\t\t * A test send was asked for.\n\t\t *\n\t\t * @param {object} channel The channel.\n\t\t */\n\t\tonTestSend(channel) {\n\t\t\t/**\n\t\t\t * @event test-send Emitted when a reader asks for a test notification on one channel. Payload: `{ channelId }`.\n\t\t\t */\n\t\t\tthis.$emit('test-send', { channelId: channel.id })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-notification-matrix__table {\n\tinline-size: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-notification-matrix__caption {\n\ttext-align: start;\n\tpadding-block-end: 8px;\n}\n\n.cn-notification-matrix__table th,\n.cn-notification-matrix__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-block-end: 1px solid var(--color-border);\n\tvertical-align: top;\n}\n\n.cn-notification-matrix__muted {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85rem;\n}\n\n.cn-notification-matrix__group {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-notification-matrix__scope {\n\tpadding-inline-start: 24px !important;\n\tfont-weight: normal;\n}\n\n.cn-notification-matrix__row--scoped th,\n.cn-notification-matrix__row--scoped td {\n\tborder-block-end-style: dotted;\n}\n\n.cn-notification-matrix__test {\n\tmargin-block-start: 4px;\n}\n\n.cn-notification-matrix__note {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The board look (DqPersoonlijkeInstellingen \"Meldingen\"): muted 13px/600\n headers over a hairline, the channel columns 96px with a centred 20px\n checkbox, the event rows 10px high with the soft rule. The caption stays for\n assistive technology. */\n.cn-notification-matrix--board {\n\t.cn-notification-matrix__caption {\n\t\tposition: absolute;\n\t\tinline-size: 1px;\n\t\tblock-size: 1px;\n\t\toverflow: hidden;\n\t\tclip-path: inset(50%);\n\t\twhite-space: nowrap;\n\t}\n\n\t/* A theme that colours every th (thematiq's table header token) leaves\n\t the board grid's head white. */\n\t.cn-notification-matrix__table {\n\t\t--nldesign-component-table-header-background-color: transparent;\n\t\tfont-size: 14px;\n\t}\n\n\t.cn-notification-matrix__table thead th {\n\t\tpadding: 8px 10px 8px 0;\n\t\tline-height: 1.2;\n\t\tborder-block-end: 1px solid var(--color-border);\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tvertical-align: bottom;\n\t}\n\n\t.cn-notification-matrix__table thead th[data-channel] {\n\t\tinline-size: 96px;\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody th,\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding: 10px 10px 10px 0;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t\tfont-weight: 400;\n\t\tline-height: 1.4;\n\t\tvertical-align: middle;\n\t}\n\n\t.cn-notification-matrix__table tbody td {\n\t\tpadding-inline: 4px;\n\t\ttext-align: center;\n\t}\n\n\t.cn-notification-matrix__table tbody td input[type='checkbox'] {\n\t\tinline-size: 20px;\n\t\tmin-inline-size: 20px;\n\t\tblock-size: 20px;\n\t\tmin-block-size: 20px;\n\t\t/* A block box, so the line's baseline adds no height: the row is\n\t\t 42px as on the board (10px, the 1px-low checkbox, 10px, the rule). */\n\t\tdisplay: block;\n\t\tmargin: 1px auto 0;\n\t}\n\n\t.cn-notification-matrix__group {\n\t\tpadding: 4px 0;\n\t\tfont-size: 13px;\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text-maxcontrast);\n\t}\n\n\t.cn-notification-matrix__channels {\n\t\tmargin-block-start: 16px;\n\t}\n\n\t.cn-notification-matrix__channels-summary {\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t\tcursor: pointer;\n\t}\n\n\t.cn-notification-matrix__channel {\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\talign-items: flex-end;\n\t\tgap: 8px 16px;\n\t\tpadding-block: 10px;\n\t\tborder-block-end: 1px solid var(--color-background-dark);\n\t}\n\n\t.cn-notification-matrix__channel-name {\n\t\tflex: 1 0 100%;\n\t\tmargin: 0;\n\t\tfont-size: 14px;\n\t\tfont-weight: 600;\n\t}\n}\n</style>\n"],"names":["t"],"mappings":"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"}
|