@conduction/nextcloud-vue 2.135.0 → 2.136.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 +2 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +65 -50
- 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 +6 -0
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js +9 -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.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +4 -11
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +12 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +11 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +3 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +18 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +38 -23
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +1 -1
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +69 -3
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/l10n/en.json.js +20 -20
- package/dist/esm/l10n/nl.json.js +21 -21
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +322 -164
- package/dist/esm/utils/widgetDispatch.js +3 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +739 -589
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +322 -164
- package/l10n/en.json +20 -20
- package/l10n/nl.json +21 -21
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +45 -4
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +15 -0
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +2 -10
- package/src/components/CnDataTable/CnDataTable.md +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +13 -1
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +12 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +4 -1
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +38 -2
- package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +7 -0
- package/src/composables/indexSources.js +69 -3
- package/src/css/look-board-detail.css +47 -1
- package/src/css/look-board-index.css +26 -1
- package/src/css/look-board.css +46 -3
- package/src/utils/widgetDispatch.js +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnActionDialog.vue3.js","sources":["../../../../src/components/CnActionDialog/CnActionDialog.vue?vue&type=template&id=7d4eaf3b&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Confirm')\"\n\t\t:eyebrow=\"eyebrowText\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-action-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-action-dialog\">\n\t\t\t<!-- The record the action is about: its title, a meta line, a badge. -->\n\t\t\t<div v-if=\"summary\" class=\"cn-action-dialog__summary\" data-testid=\"cn-action-dialog-summary\">\n\t\t\t\t<CnIcon :name=\"config.summary.icon || 'FolderOutline'\" :size=\"20\" class=\"cn-action-dialog__summary-icon\" />\n\t\t\t\t<span class=\"cn-action-dialog__summary-text\">\n\t\t\t\t\t<strong>{{ summary.title }}</strong>\n\t\t\t\t\t<span v-if=\"summary.meta\" class=\"cn-action-dialog__muted\">{{ summary.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"summary.badge\" class=\"cn-action-dialog__badge\">{{ summary.badge }}</span>\n\t\t\t</div>\n\n\t\t\t<!-- Facts about the record, in titled groups (\"Retention under the case type\"). -->\n\t\t\t<section v-for=\"(section, index) in factSections\"\n\t\t\t\t:key=\"`facts-${index}`\"\n\t\t\t\tclass=\"cn-action-dialog__section\"\n\t\t\t\tdata-testid=\"cn-action-dialog-facts\">\n\t\t\t\t<h3 v-if=\"section.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ section.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-action-dialog__facts\">\n\t\t\t\t\t<div v-for=\"fact in section.facts\" :key=\"fact.label\" class=\"cn-action-dialog__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\n\t\t\t<!-- The checklist, read from an endpoint: each item met, not met or\n\t\t\t unknown. Items named in `required` decide whether the action may\n\t\t\t run; the others inform. -->\n\t\t\t<section v-if=\"config.checklist\" class=\"cn-action-dialog__section\" data-testid=\"cn-action-dialog-checklist\">\n\t\t\t\t<h3 v-if=\"config.checklist.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ text(config.checklist.title) }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"checklistError\" class=\"cn-action-dialog__muted\" role=\"status\">\n\t\t\t\t\t{{ checklistError }}\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cn-action-dialog__checks\">\n\t\t\t\t\t<li v-for=\"item in checklist\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-action-dialog__check\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-dialog-check-${item.id}`\"\n\t\t\t\t\t\t:data-met=\"String(item.met)\">\n\t\t\t\t\t\t<CheckCircle v-if=\"item.met === true\" :size=\"20\" class=\"cn-action-dialog__check-icon cn-action-dialog__check-icon--met\" />\n\t\t\t\t\t\t<HelpCircleOutline v-else-if=\"item.met === null\" :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<CircleOutline v-else :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__check-label\">{{ item.label }}</strong>\n\t\t\t\t\t\t<span v-if=\"item.detail\" class=\"cn-action-dialog__muted\">{{ item.detail }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\n\t\t\t<!-- Fields the action asks for, sent beside the reason. -->\n\t\t\t<div v-for=\"field in fields\"\n\t\t\t\t:key=\"field.field\"\n\t\t\t\tclass=\"cn-action-dialog__field\"\n\t\t\t\t:data-testid=\"`cn-action-dialog-field-${field.field}`\">\n\t\t\t\t<!-- A checkbox field: a tinted box, the label filled from the\n\t\t\t\t record, a hint under it (DqArchiveren \"Sanne de Vries laten\n\t\t\t\t weten...\"). -->\n\t\t\t\t<NcCheckboxRadioSwitch v-if=\"field.type === 'checkbox'\"\n\t\t\t\t\tclass=\"cn-action-dialog__checkbox\"\n\t\t\t\t\t:modelValue=\"checkboxValue(field)\"\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t@update:modelValue=\"(on) => setChecked(field.field, on)\">\n\t\t\t\t\t<span class=\"cn-action-dialog__checkbox-text\">\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__checkbox-label\">{{ fill(field.label) || field.field }}</strong>\n\t\t\t\t\t\t<span v-if=\"field.hint\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<!-- A search field: records of `source` found as the reader\n\t\t\t\t types (DqZaakKoppelen \"Zaak koppelen\"). -->\n\t\t\t\t<CnSearchSelect v-else-if=\"field.type === 'search'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || null\"\n\t\t\t\t\t:options=\"searchOptions[field.field] || []\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"true\"\n\t\t\t\t\t:loading=\"searching === field.field\"\n\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t@search=\"(typed) => onSearch(field, typed)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value && typeof value === 'object' ? value.id : value)\" />\n\t\t\t\t<!-- An optional select or text field: its label above the control,\n\t\t\t\t \"(optional)\" in grey after it (DqUwWachtrij \"Sjabloon (niet\n\t\t\t\t verplicht)\"). -->\n\t\t\t\t<CnFormField v-if=\"labelsOutside(field)\"\n\t\t\t\t\t:controlId=\"controlId(field)\"\n\t\t\t\t\t:text=\"fill(field.label) || field.field\"\n\t\t\t\t\t:optional=\"true\" />\n\t\t\t\t<NcSelect v-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsOf(field)\"\n\t\t\t\t\t:inputId=\"controlId(field)\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t:clearable=\"field.required === false\"\n\t\t\t\t\t@update:modelValue=\"(option) => pick(field, option)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type === 'secret'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\ttype=\"password\"\n\t\t\t\t\tautocomplete=\"new-password\"\n\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t:placeholder=\"secretStored(field) ? t('nextcloud-vue', 'Stored. Type a new value to replace it.') : ''\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type !== 'checkbox' && field.type !== 'search'\"\n\t\t\t\t\t:id=\"controlId(field)\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\t:type=\"field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')\"\n\t\t\t\t\t:label=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<span v-if=\"field.hint && field.type !== 'checkbox'\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.reason\" class=\"cn-action-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"reasonId\"\n\t\t\t\t\tv-model=\"reason\"\n\t\t\t\t\t:label=\"reasonLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:rows=\"config.reason.rows || 2\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-action-dialog-reason\" />\n\t\t\t\t<span v-if=\"config.reason.hint\" class=\"cn-action-dialog__muted\">{{ text(config.reason.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"config.note\"\n\t\t\t\t:type=\"noteType\"\n\t\t\t\t:heading=\"text(config.note.title)\"\n\t\t\t\tdata-testid=\"cn-action-dialog-note\">\n\t\t\t\t{{ text(config.note.text) }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-action-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-action-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-action-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-action-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-action-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template v-if=\"confirmConfig.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Confirm') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CheckCircle from 'vue-material-design-icons/CheckCircle.vue'\nimport CircleOutline from 'vue-material-design-icons/CircleOutline.vue'\nimport HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'\nimport CnFormField from '../CnFormField/CnFormField.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** How long typing rests before a search field searches, in ms. */\nconst SEARCH_DELAY = 250\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** Gives each dialog's reason field its own id. */\nlet reasonSeq = 0\n\n/**\n * CnActionDialog — the generic \"summary, checklist, reason, confirm\" dialog\n * for an act on a record, configured from the manifest (an `open-modal`\n * action's props), so an app does not hand-build each one. dossiq's archive\n * dialog (board DqArchiveren) is the first: the case, what its case type\n * keeps, a readiness checklist read from an endpoint, a reason, a read-only\n * note and Archive it.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record (\"Close off · {identifier}\").\n * - `summary`: `{ source?, title, meta?, badge?, icon? }`: templates filled from\n * the record (`source` an endpointSource to read it from, else `object`).\n * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template.\n * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items\n * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an\n * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be\n * met before the action can run. `met: null` reads as unknown.\n * - `permission`: `{ source, listPath?, match?, allowedPath, reasonPath? }`:\n * the action runs only while `allowedPath` reads true; `reasonPath` says\n * why not. With `listPath` the paths read the entry of that list whose\n * fields equal `match` (`{ act: \"archive\" }`).\n * - `fields`: `[{ field, type?: \"text\"|\"date\"|\"datetime\"|\"select\"|\"checkbox\", label?,\n * required?, options?: [{ value, label, fills? }], hint?, default? }]`: inputs\n * sent beside the reason, required by default; a select option's `fills`\n * sets other fields that are still empty. A `checkbox` is sent as a boolean,\n * starts at `default` (false), fills its `label` and `hint` from the record,\n * and is optional unless `required: true` (then it must be ticked). A\n * `search` field finds records of `source: { register, schema }` as the\n * reader types, shows each as `optionLabel` with `optionSecondary` under\n * it (templates on the found record), leaves the record in hand out with\n * `excludeSelf`, and sends the chosen record's id. Every field's `label`,\n * `hint` and `placeholder` (search and select) are templates filled from the record.\n * An optional select or text field (`required: false`) draws its label above\n * the control with \"(optional)\" after it.\n * - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by\n * default; sent as `field` (default `reason`). `rows` is its height in lines\n * (default 2; board DqBesluit draws 4).\n * - `note`: `{ variant?, title?, text }`: a read-only note.\n * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.\n * `request` posts itself (`body` templates filled with the record and\n * `{reason}`); `action` dispatches a manifest action through the page.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url.\n */\nexport default {\n\tname: 'CnActionDialog',\n\n\tcomponents: { CheckCircle, CircleOutline, CnDialog, CnFormField, CnIcon, CnSearchSelect, HelpCircleOutline, NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it: the summary and facts read it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\titems: [],\n\t\t\tchecklistError: '',\n\t\t\tallowed: true,\n\t\t\treason: '',\n\t\t\tvalues: {},\n\t\t\t/** A search field's found options, by field name. */\n\t\t\tsearchOptions: {},\n\t\t\t/** The search field whose search runs, or ''. */\n\t\t\tsearching: '',\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\treasonId: `cn-action-dialog-reason-${++reasonSeq}`,\n\t\t\tfieldIdPrefix: `cn-action-dialog-field-${reasonSeq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/**\n\t\t * The summary row, or null without one or before the record is known.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {{title: string, meta: string, badge: string}|null}\n\t\t */\n\t\tsummary() {\n\t\t\tconst def = this.config.summary\n\t\t\tif (!def || !this.record) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst title = this.fill(def.title)\n\t\t\treturn title ? { title, meta: this.fill(def.meta), badge: this.fill(def.badge) } : null\n\t\t},\n\n\t\t/** @return {Array<{title: string, facts: Array<{label: string, value: string}>}>} The fact groups. */\n\t\tfactSections() {\n\t\t\tif (!Array.isArray(this.config.facts) || !this.record) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst empty = t('nextcloud-vue', 'Not set')\n\t\t\treturn this.config.facts.map((section) => ({\n\t\t\t\ttitle: this.text(section.title),\n\t\t\t\tfacts: (Array.isArray(section.items) ? section.items : []).map((fact) => ({ label: this.text(fact.label), value: this.fill(fact.value) || empty })),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The checklist rows: label and detail from `labels` by id, else the item's own.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Array<{id: string, met: (boolean|null), label: string, detail: string}>}\n\t\t */\n\t\tchecklist() {\n\t\t\tconst labels = (this.config.checklist && this.config.checklist.labels) || {}\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst met = item.met === null || item.met === undefined ? null : item.met === true\n\t\t\t\tconst own = labels[item.id] || {}\n\t\t\t\tconst detail = met === false && own.unmetDetail ? own.unmetDetail : (own.detail || item.detail || '')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tmet,\n\t\t\t\t\tlabel: this.fill(own.label || item.label || String(item.id), item),\n\t\t\t\t\tdetail: met === null && !item.detail ? t('nextcloud-vue', 'Could not be read') : this.fill(detail, item),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether every required item is met. */\n\t\trequiredMet() {\n\t\t\tconst required = (this.config.checklist && Array.isArray(this.config.checklist.required)) ? this.config.checklist.required.map(String) : []\n\t\t\treturn required.every((id) => this.items.some((item) => String(item.id) === id && item.met === true))\n\t\t},\n\n\t\t/** @return {boolean} Whether a reason is asked and needed. */\n\t\treasonNeeded() {\n\t\t\treturn Boolean(this.config.reason) && this.config.reason.required !== false\n\t\t},\n\n\t\t/** @return {string} The reason field's label. */\n\t\treasonLabel() {\n\t\t\treturn this.text(this.config.reason && this.config.reason.label) || t('nextcloud-vue', 'Reason')\n\t\t},\n\n\t\t/** @return {string} The note's NcNoteCard type. */\n\t\tnoteType() {\n\t\t\tconst variant = this.config.note && this.config.note.variant\n\t\t\treturn ['info', 'warning', 'error', 'success'].includes(variant) ? variant : 'info'\n\t\t},\n\n\t\t/**\n\t\t * Whether the action may run: allowed, required items met, a reason\n\t\t * when one is needed, and nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.allowed && this.requiredMet && this.fieldsFilled && (!this.reasonNeeded || this.reason.trim() !== '')\n\t\t},\n\n\t\t/** @return {string} The eyebrow, filled from the record once it is known. */\n\t\teyebrowText() {\n\t\t\treturn this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)\n\t\t},\n\n\t\t/**\n\t\t * The value of every field as sent: a checkbox as a boolean, the\n\t\t * others as entered ('' when empty).\n\t\t *\n\t\t * @return {object} Values by field name.\n\t\t */\n\t\tfieldValues() {\n\t\t\tconst out = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tout[field.field] = field.type === 'checkbox' ? this.checkboxValue(field) : (this.values[field.field] ?? '')\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/** @return {Array<object>} The fields with a name. */\n\t\tfields() {\n\t\t\treturn (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)\n\t\t},\n\n\t\t/**\n\t\t * Whether every required field has a value.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-fields-and-acts/specs/action-dialog/spec.md#requirement-an-action-dialog-asks-for-fields-and-reads-a-permission-from-a-list\n\t\t * @return {boolean}\n\t\t */\n\t\tfieldsFilled() {\n\t\t\treturn this.fields.every((f) => {\n\t\t\t\tif (f.type === 'checkbox') {\n\t\t\t\t\treturn f.required !== true || this.checkboxValue(f)\n\t\t\t\t}\n\t\t\t\treturn f.required === false || this.secretStored(f) || String(this.values[f.field] ?? '').trim() !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\trecord: {\n\t\t\thandler() {\n\t\t\t\tthis.startValues()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether a field's label sits above its control with \"(optional)\" after\n\t\t * it: a select or a text field the action does not require.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-optional-fields/specs/action-dialog/spec.md#requirement-an-optional-field-says-so-beside-its-label\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tlabelsOutside(field) {\n\t\t\treturn field.required === false && !['checkbox', 'search', 'secret'].includes(field.type)\n\t\t},\n\n\t\t/**\n\t\t * The id of a field's control, which its outside label points at.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {string}\n\t\t */\n\t\tcontrolId(field) {\n\t\t\treturn `${this.fieldIdPrefix}-${field.field}`\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\t/**\n\t\t * Search a search field's records once typing rests.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} typed What the reader typed.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/action-dialog-search-field/specs/action-dialog-search-field/spec.md#requirement-an-action-dialog-field-can-search-records\n\t\t */\n\t\tonSearch(field, typed) {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.searchRecords(field, String(typed || '').trim()), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Read the records of a search field's `source` that match the term.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} term The term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync searchRecords(field, term) {\n\t\t\tconst source = field.source || {}\n\t\t\tif (!term || !source.register || !source.schema) {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.searching = field.field\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(source.register)}/${encodeURIComponent(source.schema)}`)\n\t\t\t\tconst { data } = await axios.get(url, { params: { _search: term, _limit: 10 } })\n\t\t\t\tconst rows = Array.isArray(data) ? data : (data && Array.isArray(data.results) ? data.results : [])\n\t\t\t\tconst self = String(this.recordId || '')\n\t\t\t\tconst options = rows\n\t\t\t\t\t.map((row) => ({ row, id: String(row.id || (row['@self'] && row['@self'].id) || '') }))\n\t\t\t\t\t.filter(({ id }) => id !== '' && !(field.excludeSelf && id === self))\n\t\t\t\t\t.map(({ row, id }) => {\n\t\t\t\t\t\tconst option = { id, label: this.fill(field.optionLabel || '{title}', row) || id }\n\t\t\t\t\t\tconst secondary = this.fill(field.optionSecondary, row)\n\t\t\t\t\t\treturn secondary ? { ...option, secondary } : option\n\t\t\t\t\t})\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: options }\n\t\t\t} catch {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t} finally {\n\t\t\t\tthis.searching = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A checkbox field's state: what was ticked, else its `default`.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/action-dialog-checkbox-field/specs/action-dialog-checkbox-field/spec.md#requirement-an-action-dialog-field-can-be-a-checkbox\n\t\t */\n\t\tcheckboxValue(field) {\n\t\t\treturn field.field in this.values ? this.values[field.field] === true : field.default === true\n\t\t},\n\n\t\t/**\n\t\t * Tick or untick a checkbox field.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {boolean} on Ticked.\n\t\t * @return {void}\n\t\t */\n\t\tsetChecked(name, on) {\n\t\t\tthis.values = { ...this.values, [name]: on === true }\n\t\t},\n\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row (the record by default), translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} [row] The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, row) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn fillTemplate(this.cnTranslate(template), row || this.record || {}).trim()\n\t\t},\n\n\t\t/**\n\t\t * Read an endpoint, fresh.\n\t\t *\n\t\t * @param {object} source An endpointSource block.\n\t\t * @return {Promise<unknown>}\n\t\t */\n\t\tread(source) {\n\t\t\treturn fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadRecord() {\n\t\t\tconst source = this.config.summary && this.config.summary.source\n\t\t\tif (!source || !source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(source)\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The summary stays empty; the checklist and the action still work.\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadChecklist() {\n\t\t\tconst def = this.config.checklist\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst list = def.itemsPath ? readPath(data, def.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && item.id !== undefined) : []\n\t\t\t} catch {\n\t\t\t\tthis.items = []\n\t\t\t\tthis.checklistError = t('nextcloud-vue', 'Whether this is ready could not be read.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entry of a list whose fields equal `match`, or null.\n\t\t *\n\t\t * @param {unknown} list The list.\n\t\t * @param {object} [match] The fields to match.\n\t\t * @return {object|null}\n\t\t */\n\t\tmatchIn(list, match) {\n\t\t\tconst wanted = Object.entries(match || {})\n\t\t\treturn (Array.isArray(list) ? list : []).find((entry) => entry && wanted.every(([key, value]) => String(entry[key]) === String(value))) || null\n\t\t},\n\n\t\t/**\n\t\t * A select field's options, labels translated.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {Array<{id: string, label: string, fills?: object}>}\n\t\t */\n\t\toptionsOf(field) {\n\t\t\treturn (Array.isArray(field.options) ? field.options : []).map((o) => ({ id: String(o.value), label: this.text(o.label) || String(o.value), fills: o.fills }))\n\t\t},\n\n\t\t/**\n\t\t * A select field's chosen option.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\treturn this.optionsOf(field).find((o) => o.id === String(this.values[field.field] ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Set a field's value.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetValue(name, value) {\n\t\t\tthis.values = { ...this.values, [name]: value === null || value === undefined ? '' : String(value) }\n\t\t},\n\n\t\t/**\n\t\t * Choose a select option; its `fills` set the fields still empty.\n\t\t *\n\t\t * @param {object} field The select field.\n\t\t * @param {{id: string, fills?: object}|null} option The option.\n\t\t */\n\t\tpick(field, option) {\n\t\t\tthis.setValue(field.field, option ? option.id : '')\n\t\t\tfor (const [name, value] of Object.entries((option && option.fills) || {})) {\n\t\t\t\tif (String(this.values[name] ?? '').trim() === '') {\n\t\t\t\t\tthis.setValue(name, this.text(value) || value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadPermission() {\n\t\t\tconst def = this.config.permission\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst entry = def.listPath ? this.matchIn(readPath(data, def.listPath), def.match) : data\n\t\t\t\tthis.allowed = readPath(entry, def.allowedPath || 'allowed') === true\n\t\t\t\tthis.refusal = this.allowed || !def.reasonPath ? '' : String(readPath(entry, def.reasonPath) || '')\n\t\t\t} catch {\n\t\t\t\tthis.allowed = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run the action once: post `confirm.request`, or dispatch\n\t\t * `confirm.action`, then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst reason = this.reason.trim()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, { ...(this.record || {}), reason }) : value\n\t\t\t\t\t}\n\t\t\t\t\tif (this.config.reason) {\n\t\t\t\t\t\tbody[this.config.reason.field || 'reason'] = reason\n\t\t\t\t\t}\n\t\t\t\t\tconst values = { ...this.fieldValues }\n\t\t\t\t\tfor (const field of this.fields) {\n\t\t\t\t\t\tif (field.type === 'secret' && String(this.values[field.field] ?? '') === '') {\n\t\t\t\t\t\t\tdelete values[field.field]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tObject.assign(body, values)\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.fieldValues, reason }] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The action ran. Payload: the field values and `reason`. */\n\t\t\t\tthis.$emit('done', { ...this.fieldValues, reason })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start each field with `valuePath` from the record, unless it was\n\t\t * already edited.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-secret-field/specs/action-dialog/spec.md#requirement-a-field-can-start-from-the-record-and-a-secret-is-write-only\n\t\t * @return {void}\n\t\t */\n\t\tstartValues() {\n\t\t\tif (!this.record) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst values = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tif (field.valuePath && field.type !== 'secret' && !(field.field in values)) {\n\t\t\t\t\tconst value = readPath(this.record, field.valuePath)\n\t\t\t\t\tif (value !== null && value !== undefined) {\n\t\t\t\t\t\tvalues[field.field] = String(value)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.values = values\n\t\t},\n\n\t\t/**\n\t\t * Whether a secret field has a value stored: `storedPath` reads true on the record.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tsecretStored(field) {\n\t\t\treturn field.type === 'secret' && Boolean(field.storedPath && this.record && readPath(this.record, field.storedPath))\n\t\t},\n\n\t\t/** Close the dialog, unless the action is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-action-dialog__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-action-dialog__summary-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tmin-width: 0;\n}\n\n.cn-action-dialog__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-dark);\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n\n.cn-action-dialog__title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n/* Facts in columns of at least 200px, each a label over its value above a\n hairline (DqArchiveren \"Bewaring volgens het zaaktype\"). */\n.cn-action-dialog__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-action-dialog__fact {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n\tpadding-bottom: 10px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-action-dialog__facts dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__facts dd {\n\tmargin: 0;\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n/* Checklist rows divided by hairlines, the detail at the row's end\n (DqArchiveren \"Klaar om te archiveren\"). */\n.cn-action-dialog__checks {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-action-dialog__check {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 2px 12px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 15px;\n}\n\n.cn-action-dialog__check-label {\n\tflex: 1 1 auto;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__check-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-action-dialog__check-icon--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-action-dialog__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n/* A checkbox field is a tinted box with a 2px edge, its label bold and its\n hint under it (DqArchiveren). */\n.cn-action-dialog__checkbox {\n\tpadding: 12px 14px;\n\tborder: 2px solid var(--color-primary-element);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-action-dialog__checkbox-text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-action-dialog__checkbox-label {\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__checkbox .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-action-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createVNode","_createBlock","_createTextVNode","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnActionDialog.vue3.js","sources":["../../../../src/components/CnActionDialog/CnActionDialog.vue?vue&type=template&id=a3810b54&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Confirm')\"\n\t\t:eyebrow=\"eyebrowText\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-action-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-action-dialog\">\n\t\t\t<!-- The record the action is about: its title, a meta line, a badge. -->\n\t\t\t<div v-if=\"summary\" class=\"cn-action-dialog__summary\" data-testid=\"cn-action-dialog-summary\">\n\t\t\t\t<span class=\"cn-action-dialog__summary-icon\">\n\t\t\t\t\t<CnIcon :name=\"config.summary.icon || 'FolderOutline'\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-action-dialog__summary-text\">\n\t\t\t\t\t<strong>{{ summary.title }}</strong>\n\t\t\t\t\t<span v-if=\"summary.meta\" class=\"cn-action-dialog__muted\">{{ summary.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"summary.badge\" class=\"cn-action-dialog__badge\">{{ summary.badge }}</span>\n\t\t\t</div>\n\n\t\t\t<!-- Facts about the record, in titled groups (\"Retention under the case type\"). -->\n\t\t\t<section v-for=\"(section, index) in factSections\"\n\t\t\t\t:key=\"`facts-${index}`\"\n\t\t\t\tclass=\"cn-action-dialog__section\"\n\t\t\t\tdata-testid=\"cn-action-dialog-facts\">\n\t\t\t\t<h3 v-if=\"section.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ section.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-action-dialog__facts\">\n\t\t\t\t\t<div v-for=\"fact in section.facts\" :key=\"fact.label\" class=\"cn-action-dialog__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\n\t\t\t<!-- The checklist, read from an endpoint: each item met, not met or\n\t\t\t unknown. Items named in `required` decide whether the action may\n\t\t\t run; the others inform. -->\n\t\t\t<section v-if=\"config.checklist\" class=\"cn-action-dialog__section\" data-testid=\"cn-action-dialog-checklist\">\n\t\t\t\t<h3 v-if=\"config.checklist.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ text(config.checklist.title) }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"checklistError\" class=\"cn-action-dialog__muted\" role=\"status\">\n\t\t\t\t\t{{ checklistError }}\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cn-action-dialog__checks\">\n\t\t\t\t\t<li v-for=\"item in checklist\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-action-dialog__check\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-dialog-check-${item.id}`\"\n\t\t\t\t\t\t:data-met=\"String(item.met)\">\n\t\t\t\t\t\t<CheckCircle v-if=\"item.met === true\" :size=\"20\" class=\"cn-action-dialog__check-icon cn-action-dialog__check-icon--met\" />\n\t\t\t\t\t\t<HelpCircleOutline v-else-if=\"item.met === null\" :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<CircleOutline v-else :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__check-label\">{{ item.label }}</strong>\n\t\t\t\t\t\t<span v-if=\"item.detail\" class=\"cn-action-dialog__muted\">{{ item.detail }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\n\t\t\t<!-- Fields the action asks for, sent beside the reason. -->\n\t\t\t<div v-for=\"field in fields\"\n\t\t\t\t:key=\"field.field\"\n\t\t\t\tclass=\"cn-action-dialog__field\"\n\t\t\t\t:data-testid=\"`cn-action-dialog-field-${field.field}`\">\n\t\t\t\t<!-- A checkbox field: a tinted box, the label filled from the\n\t\t\t\t record, a hint under it (DqArchiveren \"Sanne de Vries laten\n\t\t\t\t weten...\"). -->\n\t\t\t\t<NcCheckboxRadioSwitch v-if=\"field.type === 'checkbox'\"\n\t\t\t\t\tclass=\"cn-action-dialog__checkbox\"\n\t\t\t\t\t:modelValue=\"checkboxValue(field)\"\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t@update:modelValue=\"(on) => setChecked(field.field, on)\">\n\t\t\t\t\t<span class=\"cn-action-dialog__checkbox-text\">\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__checkbox-label\">{{ fill(field.label) || field.field }}</strong>\n\t\t\t\t\t\t<span v-if=\"field.hint\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<!-- A search field: records of `source` found as the reader\n\t\t\t\t types (DqZaakKoppelen \"Zaak koppelen\"). -->\n\t\t\t\t<CnSearchSelect v-else-if=\"field.type === 'search'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || null\"\n\t\t\t\t\t:options=\"searchOptions[field.field] || []\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"true\"\n\t\t\t\t\t:loading=\"searching === field.field\"\n\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t@search=\"(typed) => onSearch(field, typed)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value && typeof value === 'object' ? value.id : value)\" />\n\t\t\t\t<!-- An optional select or text field: its label above the control,\n\t\t\t\t \"(optional)\" in grey after it (DqUwWachtrij \"Sjabloon (niet\n\t\t\t\t verplicht)\"). -->\n\t\t\t\t<CnFormField v-if=\"labelsOutside(field)\"\n\t\t\t\t\t:controlId=\"controlId(field)\"\n\t\t\t\t\t:text=\"fill(field.label) || field.field\"\n\t\t\t\t\t:optional=\"true\" />\n\t\t\t\t<NcSelect v-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsOf(field)\"\n\t\t\t\t\t:inputId=\"controlId(field)\"\n\t\t\t\t\t:inputLabel=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t:placeholder=\"fill(field.placeholder)\"\n\t\t\t\t\t:clearable=\"field.required === false\"\n\t\t\t\t\t@update:modelValue=\"(option) => pick(field, option)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type === 'secret'\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\ttype=\"password\"\n\t\t\t\t\tautocomplete=\"new-password\"\n\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t:placeholder=\"secretStored(field) ? t('nextcloud-vue', 'Stored. Type a new value to replace it.') : ''\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<NcTextField v-else-if=\"field.type !== 'checkbox' && field.type !== 'search'\"\n\t\t\t\t\t:id=\"controlId(field)\"\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\t:type=\"field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')\"\n\t\t\t\t\t:label=\"fill(field.label) || field.field\"\n\t\t\t\t\t:labelOutside=\"labelsOutside(field)\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t\t<span v-if=\"field.hint && field.type !== 'checkbox'\" class=\"cn-action-dialog__muted\">{{ fill(field.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.reason\" class=\"cn-action-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"reasonId\"\n\t\t\t\t\tv-model=\"reason\"\n\t\t\t\t\t:label=\"reasonLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:rows=\"config.reason.rows || 2\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-action-dialog-reason\" />\n\t\t\t\t<span v-if=\"config.reason.hint\" class=\"cn-action-dialog__muted\">{{ text(config.reason.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"config.note\"\n\t\t\t\t:type=\"noteType\"\n\t\t\t\t:heading=\"text(config.note.title)\"\n\t\t\t\tdata-testid=\"cn-action-dialog-note\">\n\t\t\t\t<template v-if=\"config.note.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"config.note.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(config.note.text) }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-action-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-action-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-action-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-action-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-action-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template v-if=\"confirmConfig.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Confirm') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CheckCircle from 'vue-material-design-icons/CheckCircle.vue'\nimport CircleOutline from 'vue-material-design-icons/CircleOutline.vue'\nimport HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'\nimport CnFormField from '../CnFormField/CnFormField.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** How long typing rests before a search field searches, in ms. */\nconst SEARCH_DELAY = 250\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** Gives each dialog's reason field its own id. */\nlet reasonSeq = 0\n\n/**\n * CnActionDialog — the generic \"summary, checklist, reason, confirm\" dialog\n * for an act on a record, configured from the manifest (an `open-modal`\n * action's props), so an app does not hand-build each one. dossiq's archive\n * dialog (board DqArchiveren) is the first: the case, what its case type\n * keeps, a readiness checklist read from an endpoint, a reason, a read-only\n * note and Archive it.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record (\"Close off · {identifier}\").\n * - `summary`: `{ source?, title, meta?, badge?, icon? }`: templates filled from\n * the record (`source` an endpointSource to read it from, else `object`).\n * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template.\n * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items\n * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an\n * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be\n * met before the action can run. `met: null` reads as unknown.\n * - `permission`: `{ source, listPath?, match?, allowedPath, reasonPath? }`:\n * the action runs only while `allowedPath` reads true; `reasonPath` says\n * why not. With `listPath` the paths read the entry of that list whose\n * fields equal `match` (`{ act: \"archive\" }`).\n * - `fields`: `[{ field, type?: \"text\"|\"date\"|\"datetime\"|\"select\"|\"checkbox\", label?,\n * required?, options?: [{ value, label, fills? }], hint?, default? }]`: inputs\n * sent beside the reason, required by default; a select option's `fills`\n * sets other fields that are still empty. A `checkbox` is sent as a boolean,\n * starts at `default` (false), fills its `label` and `hint` from the record,\n * and is optional unless `required: true` (then it must be ticked). A\n * `search` field finds records of `source: { register, schema }` as the\n * reader types, shows each as `optionLabel` with `optionSecondary` under\n * it (templates on the found record), leaves the record in hand out with\n * `excludeSelf`, and sends the chosen record's id. Every field's `label`,\n * `hint` and `placeholder` (search and select) are templates filled from the record.\n * An optional select or text field (`required: false`) draws its label above\n * the control with \"(optional)\" after it.\n * - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by\n * default; sent as `field` (default `reason`). `rows` is its height in lines\n * (default 2; board DqBesluit draws 4).\n * - `note`: `{ variant?, title?, text, icon? }`: a read-only note; `icon` an\n * icon name in place of the variant's own (\"LockOutline\").\n * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.\n * `request` posts itself (`body` templates filled with the record and\n * `{reason}`); `action` dispatches a manifest action through the page.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url.\n */\nexport default {\n\tname: 'CnActionDialog',\n\n\tcomponents: { CheckCircle, CircleOutline, CnDialog, CnFormField, CnIcon, CnSearchSelect, HelpCircleOutline, NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it: the summary and facts read it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\titems: [],\n\t\t\tchecklistError: '',\n\t\t\tallowed: true,\n\t\t\treason: '',\n\t\t\tvalues: {},\n\t\t\t/** A search field's found options, by field name. */\n\t\t\tsearchOptions: {},\n\t\t\t/** The search field whose search runs, or ''. */\n\t\t\tsearching: '',\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\treasonId: `cn-action-dialog-reason-${++reasonSeq}`,\n\t\t\tfieldIdPrefix: `cn-action-dialog-field-${reasonSeq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/**\n\t\t * The summary row, or null without one or before the record is known.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {{title: string, meta: string, badge: string}|null}\n\t\t */\n\t\tsummary() {\n\t\t\tconst def = this.config.summary\n\t\t\tif (!def || !this.record) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst title = this.fill(def.title)\n\t\t\treturn title ? { title, meta: this.fill(def.meta), badge: this.fill(def.badge) } : null\n\t\t},\n\n\t\t/** @return {Array<{title: string, facts: Array<{label: string, value: string}>}>} The fact groups. */\n\t\tfactSections() {\n\t\t\tif (!Array.isArray(this.config.facts) || !this.record) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst empty = t('nextcloud-vue', 'Not set')\n\t\t\treturn this.config.facts.map((section) => ({\n\t\t\t\ttitle: this.text(section.title),\n\t\t\t\tfacts: (Array.isArray(section.items) ? section.items : []).map((fact) => ({ label: this.text(fact.label), value: this.fill(fact.value) || empty })),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The checklist rows: label and detail from `labels` by id, else the item's own.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Array<{id: string, met: (boolean|null), label: string, detail: string}>}\n\t\t */\n\t\tchecklist() {\n\t\t\tconst labels = (this.config.checklist && this.config.checklist.labels) || {}\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst met = item.met === null || item.met === undefined ? null : item.met === true\n\t\t\t\tconst own = labels[item.id] || {}\n\t\t\t\tconst detail = met === false && own.unmetDetail ? own.unmetDetail : (own.detail || item.detail || '')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tmet,\n\t\t\t\t\tlabel: this.fill(own.label || item.label || String(item.id), item),\n\t\t\t\t\tdetail: met === null && !item.detail ? t('nextcloud-vue', 'Could not be read') : this.fill(detail, item),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether every required item is met. */\n\t\trequiredMet() {\n\t\t\tconst required = (this.config.checklist && Array.isArray(this.config.checklist.required)) ? this.config.checklist.required.map(String) : []\n\t\t\treturn required.every((id) => this.items.some((item) => String(item.id) === id && item.met === true))\n\t\t},\n\n\t\t/** @return {boolean} Whether a reason is asked and needed. */\n\t\treasonNeeded() {\n\t\t\treturn Boolean(this.config.reason) && this.config.reason.required !== false\n\t\t},\n\n\t\t/** @return {string} The reason field's label. */\n\t\treasonLabel() {\n\t\t\treturn this.text(this.config.reason && this.config.reason.label) || t('nextcloud-vue', 'Reason')\n\t\t},\n\n\t\t/** @return {string} The note's NcNoteCard type. */\n\t\tnoteType() {\n\t\t\tconst variant = this.config.note && this.config.note.variant\n\t\t\treturn ['info', 'warning', 'error', 'success'].includes(variant) ? variant : 'info'\n\t\t},\n\n\t\t/**\n\t\t * Whether the action may run: allowed, required items met, a reason\n\t\t * when one is needed, and nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.allowed && this.requiredMet && this.fieldsFilled && (!this.reasonNeeded || this.reason.trim() !== '')\n\t\t},\n\n\t\t/** @return {string} The eyebrow, filled from the record once it is known. */\n\t\teyebrowText() {\n\t\t\treturn this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)\n\t\t},\n\n\t\t/**\n\t\t * The value of every field as sent: a checkbox as a boolean, the\n\t\t * others as entered ('' when empty).\n\t\t *\n\t\t * @return {object} Values by field name.\n\t\t */\n\t\tfieldValues() {\n\t\t\tconst out = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tout[field.field] = field.type === 'checkbox' ? this.checkboxValue(field) : (this.values[field.field] ?? '')\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/** @return {Array<object>} The fields with a name. */\n\t\tfields() {\n\t\t\treturn (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)\n\t\t},\n\n\t\t/**\n\t\t * Whether every required field has a value.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-fields-and-acts/specs/action-dialog/spec.md#requirement-an-action-dialog-asks-for-fields-and-reads-a-permission-from-a-list\n\t\t * @return {boolean}\n\t\t */\n\t\tfieldsFilled() {\n\t\t\treturn this.fields.every((f) => {\n\t\t\t\tif (f.type === 'checkbox') {\n\t\t\t\t\treturn f.required !== true || this.checkboxValue(f)\n\t\t\t\t}\n\t\t\t\treturn f.required === false || this.secretStored(f) || String(this.values[f.field] ?? '').trim() !== ''\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\trecord: {\n\t\t\thandler() {\n\t\t\t\tthis.startValues()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.searchTimer)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether a field's label sits above its control with \"(optional)\" after\n\t\t * it: a select or a text field the action does not require.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-optional-fields/specs/action-dialog/spec.md#requirement-an-optional-field-says-so-beside-its-label\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tlabelsOutside(field) {\n\t\t\treturn field.required === false && !['checkbox', 'search', 'secret'].includes(field.type)\n\t\t},\n\n\t\t/**\n\t\t * The id of a field's control, which its outside label points at.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {string}\n\t\t */\n\t\tcontrolId(field) {\n\t\t\treturn `${this.fieldIdPrefix}-${field.field}`\n\t\t},\n\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\t/**\n\t\t * Search a search field's records once typing rests.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} typed What the reader typed.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/action-dialog-search-field/specs/action-dialog-search-field/spec.md#requirement-an-action-dialog-field-can-search-records\n\t\t */\n\t\tonSearch(field, typed) {\n\t\t\tclearTimeout(this.searchTimer)\n\t\t\tthis.searchTimer = setTimeout(() => this.searchRecords(field, String(typed || '').trim()), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Read the records of a search field's `source` that match the term.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} term The term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync searchRecords(field, term) {\n\t\t\tconst source = field.source || {}\n\t\t\tif (!term || !source.register || !source.schema) {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.searching = field.field\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(source.register)}/${encodeURIComponent(source.schema)}`)\n\t\t\t\tconst { data } = await axios.get(url, { params: { _search: term, _limit: 10 } })\n\t\t\t\tconst rows = Array.isArray(data) ? data : (data && Array.isArray(data.results) ? data.results : [])\n\t\t\t\tconst self = String(this.recordId || '')\n\t\t\t\tconst options = rows\n\t\t\t\t\t.map((row) => ({ row, id: String(row.id || (row['@self'] && row['@self'].id) || '') }))\n\t\t\t\t\t.filter(({ id }) => id !== '' && !(field.excludeSelf && id === self))\n\t\t\t\t\t.map(({ row, id }) => {\n\t\t\t\t\t\tconst option = { id, label: this.fill(field.optionLabel || '{title}', row) || id }\n\t\t\t\t\t\tconst secondary = this.fill(field.optionSecondary, row)\n\t\t\t\t\t\treturn secondary ? { ...option, secondary } : option\n\t\t\t\t\t})\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: options }\n\t\t\t} catch {\n\t\t\t\tthis.searchOptions = { ...this.searchOptions, [field.field]: [] }\n\t\t\t} finally {\n\t\t\t\tthis.searching = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A checkbox field's state: what was ticked, else its `default`.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/action-dialog-checkbox-field/specs/action-dialog-checkbox-field/spec.md#requirement-an-action-dialog-field-can-be-a-checkbox\n\t\t */\n\t\tcheckboxValue(field) {\n\t\t\treturn field.field in this.values ? this.values[field.field] === true : field.default === true\n\t\t},\n\n\t\t/**\n\t\t * Tick or untick a checkbox field.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {boolean} on Ticked.\n\t\t * @return {void}\n\t\t */\n\t\tsetChecked(name, on) {\n\t\t\tthis.values = { ...this.values, [name]: on === true }\n\t\t},\n\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row (the record by default), translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} [row] The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, row) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn fillTemplate(this.cnTranslate(template), row || this.record || {}).trim()\n\t\t},\n\n\t\t/**\n\t\t * Read an endpoint, fresh.\n\t\t *\n\t\t * @param {object} source An endpointSource block.\n\t\t * @return {Promise<unknown>}\n\t\t */\n\t\tread(source) {\n\t\t\treturn fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadRecord() {\n\t\t\tconst source = this.config.summary && this.config.summary.source\n\t\t\tif (!source || !source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(source)\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The summary stays empty; the checklist and the action still work.\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadChecklist() {\n\t\t\tconst def = this.config.checklist\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst list = def.itemsPath ? readPath(data, def.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && item.id !== undefined) : []\n\t\t\t} catch {\n\t\t\t\tthis.items = []\n\t\t\t\tthis.checklistError = t('nextcloud-vue', 'Whether this is ready could not be read.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entry of a list whose fields equal `match`, or null.\n\t\t *\n\t\t * @param {unknown} list The list.\n\t\t * @param {object} [match] The fields to match.\n\t\t * @return {object|null}\n\t\t */\n\t\tmatchIn(list, match) {\n\t\t\tconst wanted = Object.entries(match || {})\n\t\t\treturn (Array.isArray(list) ? list : []).find((entry) => entry && wanted.every(([key, value]) => String(entry[key]) === String(value))) || null\n\t\t},\n\n\t\t/**\n\t\t * A select field's options, labels translated.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {Array<{id: string, label: string, fills?: object}>}\n\t\t */\n\t\toptionsOf(field) {\n\t\t\treturn (Array.isArray(field.options) ? field.options : []).map((o) => ({ id: String(o.value), label: this.text(o.label) || String(o.value), fills: o.fills }))\n\t\t},\n\n\t\t/**\n\t\t * A select field's chosen option.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\treturn this.optionsOf(field).find((o) => o.id === String(this.values[field.field] ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Set a field's value.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetValue(name, value) {\n\t\t\tthis.values = { ...this.values, [name]: value === null || value === undefined ? '' : String(value) }\n\t\t},\n\n\t\t/**\n\t\t * Choose a select option; its `fills` set the fields still empty.\n\t\t *\n\t\t * @param {object} field The select field.\n\t\t * @param {{id: string, fills?: object}|null} option The option.\n\t\t */\n\t\tpick(field, option) {\n\t\t\tthis.setValue(field.field, option ? option.id : '')\n\t\t\tfor (const [name, value] of Object.entries((option && option.fills) || {})) {\n\t\t\t\tif (String(this.values[name] ?? '').trim() === '') {\n\t\t\t\t\tthis.setValue(name, this.text(value) || value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadPermission() {\n\t\t\tconst def = this.config.permission\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst entry = def.listPath ? this.matchIn(readPath(data, def.listPath), def.match) : data\n\t\t\t\tthis.allowed = readPath(entry, def.allowedPath || 'allowed') === true\n\t\t\t\tthis.refusal = this.allowed || !def.reasonPath ? '' : String(readPath(entry, def.reasonPath) || '')\n\t\t\t} catch {\n\t\t\t\tthis.allowed = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run the action once: post `confirm.request`, or dispatch\n\t\t * `confirm.action`, then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst reason = this.reason.trim()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, { ...(this.record || {}), reason }) : value\n\t\t\t\t\t}\n\t\t\t\t\tif (this.config.reason) {\n\t\t\t\t\t\tbody[this.config.reason.field || 'reason'] = reason\n\t\t\t\t\t}\n\t\t\t\t\tconst values = { ...this.fieldValues }\n\t\t\t\t\tfor (const field of this.fields) {\n\t\t\t\t\t\tif (field.type === 'secret' && String(this.values[field.field] ?? '') === '') {\n\t\t\t\t\t\t\tdelete values[field.field]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tObject.assign(body, values)\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.fieldValues, reason }] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The action ran. Payload: the field values and `reason`. */\n\t\t\t\tthis.$emit('done', { ...this.fieldValues, reason })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start each field with `valuePath` from the record, unless it was\n\t\t * already edited.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-secret-field/specs/action-dialog/spec.md#requirement-a-field-can-start-from-the-record-and-a-secret-is-write-only\n\t\t * @return {void}\n\t\t */\n\t\tstartValues() {\n\t\t\tif (!this.record) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst values = { ...this.values }\n\t\t\tfor (const field of this.fields) {\n\t\t\t\tif (field.valuePath && field.type !== 'secret' && !(field.field in values)) {\n\t\t\t\t\tconst value = readPath(this.record, field.valuePath)\n\t\t\t\t\tif (value !== null && value !== undefined) {\n\t\t\t\t\t\tvalues[field.field] = String(value)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.values = values\n\t\t},\n\n\t\t/**\n\t\t * Whether a secret field has a value stored: `storedPath` reads true on the record.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {boolean}\n\t\t */\n\t\tsecretStored(field) {\n\t\t\treturn field.type === 'secret' && Boolean(field.storedPath && this.record && readPath(this.record, field.storedPath))\n\t\t},\n\n\t\t/** Close the dialog, unless the action is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-action-dialog__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n}\n\n/* The record's icon on a tinted tile (DqArchiveren). */\n.cn-action-dialog__summary-icon {\n\tdisplay: flex;\n\tflex: none;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 36px;\n\theight: 36px;\n\tborder-radius: 8px;\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text);\n}\n\n.cn-action-dialog__summary-text strong {\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__summary-text .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__summary-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tmin-width: 0;\n}\n\n.cn-action-dialog__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-dark);\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n\n.cn-action-dialog__title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n/* Facts in columns of at least 200px, each a label over its value above a\n hairline (DqArchiveren \"Bewaring volgens het zaaktype\"). */\n.cn-action-dialog__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-action-dialog__fact {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n\tpadding-bottom: 10px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n/* The server draws every dt as a 130px right-aligned inline block and pads\n dt and dd 12px; a fact is a label over its value. */\n.cn-action-dialog__facts dt,\n.cn-action-dialog__facts dd {\n\tdisplay: block;\n\twidth: auto;\n\twhite-space: normal;\n\ttext-align: start;\n}\n\n.cn-action-dialog__facts dt {\n\tmargin: 0;\n\tpadding: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__facts dd {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n/* Checklist rows divided by hairlines, the detail at the row's end\n (DqArchiveren \"Klaar om te archiveren\"). */\n.cn-action-dialog__checks {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-action-dialog__check {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 2px 12px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 15px;\n}\n\n.cn-action-dialog__check-label {\n\tflex: 1 1 auto;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__check-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-action-dialog__check-icon--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-action-dialog__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n/* A checkbox field is a tinted box with a 2px edge, its label bold and its\n hint under it (DqArchiveren). */\n.cn-action-dialog__checkbox {\n\tpadding: 12px 14px;\n\tborder: 2px solid var(--color-primary-element);\n\tborder-radius: var(--border-radius-element, 8px);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-action-dialog__checkbox-text {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-action-dialog__checkbox-label {\n\tfont-size: 15px;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__checkbox .cn-action-dialog__muted {\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-action-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createVNode","_createBlock","_createTextVNode","_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnAuditTrailCard.vue2.js';
|
|
|
2
2
|
import { render } from './CnAuditTrailCard.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-834a0068";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -35,6 +35,12 @@ var script = {
|
|
|
35
35
|
components: { CnDetailCard: __sfc_main, NcLoadingIcon },
|
|
36
36
|
|
|
37
37
|
props: {
|
|
38
|
+
/**
|
|
39
|
+
* A muted line under the trail, inside the card ("Kenmerk ZT-OMV-REG").
|
|
40
|
+
*
|
|
41
|
+
* @spec openspec/changes/audit-trail-footnote/specs/audit-trail/spec.md#requirement-a-history-card-can-end-in-a-line-about-the-record
|
|
42
|
+
*/
|
|
43
|
+
footnote: { type: String, default: '' },
|
|
38
44
|
/** OpenRegister register id (slug or uuid). Optional for `scope: "app"`. */
|
|
39
45
|
register: { type: String, default: '' },
|
|
40
46
|
/** OpenRegister schema id (slug or uuid). Optional for `scope: "app"`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnAuditTrailCard.vue2.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue"],"sourcesContent":["<!--\n CnAuditTrailCard — compact audit-trail widget for the integration\n registry.\n\n Surface-aware shell around the `audit-trail` integration: fetches\n the object's recent audit-trail entries (query-time storage strategy\n per AD-22) and renders them in a CnDetailCard.\n\n Per umbrella change `pluggable-integration-registry` AD-19 (surface\n fallback), one component handles all surfaces — `surface` is\n forwarded so the component can branch internally if desired.\n-->\n<template>\n\t<CnDetailCard\n\t\t:title=\"resolvedTitle\"\n\t\t:icon=\"History\"\n\t\t:collapsible=\"collapsible\"\n\t\t:data-surface=\"surface\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<p v-else-if=\"failed\"\n\t\t\tclass=\"cn-audit-card__empty\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-audit-card-failed\">\n\t\t\t{{ failedLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"shownEntries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ formatAction(entry) }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entry.authorized === false\"\n\t\t\t\t\t\tclass=\"cn-audit-card__refused\"\n\t\t\t\t\t\tdata-testid=\"cn-audit-card-refused\">{{ refusedLabel }}</span>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"entry.reason\" class=\"cn-audit-card__reason\">\n\t\t\t\t\t{{ entry.reason }}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t</ul>\n\t\t<template v-if=\"shownEntries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ shownEntries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * @param {object} entry An audit entry.\n * @return {number} When it happened, in ms; 0 when unreadable.\n */\nfunction entryTime(entry) {\n\tconst time = new Date(entry.creationDateTime || entry.created || entry.timestamp || 0).getTime()\n\treturn Number.isNaN(time) ? 0 : time\n}\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Required for `scope: \"object\"` (the default); unused for `scope: \"app\"`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/**\n\t\t * What the card lists. `object` (the default) is one object's trail\n\t\t * and needs `register`, `schema` and `objectId`. `app` is the\n\t\t * app-wide feed: the entries of every object the caller may read\n\t\t * (OpenRegister's `/audit-trails/readable`), narrowed to `register`\n\t\t * and `schema` when those are set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t */\n\t\tscope: {\n\t\t\ttype: String,\n\t\t\tdefault: 'object',\n\t\t\tvalidator: (value) => ['object', 'app'].includes(value),\n\t\t},\n\n\t\t/** Rendering surface — passed for AD-19 surface fallback consumers. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (value) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(value),\n\t\t},\n\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t/**\n\t\t * Read the trail of something that is not an OpenRegister object:\n\t\t * `{ kind: 'flow-task', id }` reads `${apiBase}/flow-tasks/{id}/audit`,\n\t\t * oldest first. `id` may be left out or written `@objectId`.\n\t\t *\n\t\t * @type {{kind: string, id?: (string|number)}|null}\n\t\t */\n\t\tsource: { type: Object, default: null },\n\n\t\t/** A trail URL that replaces the object's own; `{id}` is filled with the source's id or `objectId`. */\n\t\tendpoint: { type: String, default: '' },\n\n\t\t/** `newest` first (the object trail's order) or `oldest` first; empty takes `oldest` for a source and `newest` otherwise. */\n\t\torder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (value) => ['', 'newest', 'oldest'].includes(value),\n\t\t},\n\n\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/**\n\t\t * Actions the card leaves out (case-insensitive), such as `['read']`\n\t\t * for a history of what changed. It then reads more entries than it\n\t\t * shows, so the rows it leaves out do not crowd the rest away.\n\t\t *\n\t\t * @spec openspec/changes/audit-trail-exclude-actions/specs/audit-trail/spec.md#requirement-a-history-card-can-leave-out-events-by-action\n\t\t */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Whether the card collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Pre-translated empty label. */\n\t\tnoEntriesLabel: { type: String, default: () => t('nextcloud-vue', 'No audit entries yet') },\n\t\t/** Pre-translated overflow label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/** Pre-translated fallback action label. */\n\t\tactionLabel: { type: String, default: () => t('nextcloud-vue', 'Change') },\n\t\t/** Shown on an entry the server refused (`authorized: false`). */\n\t\trefusedLabel: { type: String, default: () => t('nextcloud-vue', '(refused)') },\n\t\t/**\n\t\t * What happened, in words, by action (`claim`) or by action and the\n\t\t * state it led to (`complete:completed`, tried first). `{actor}`,\n\t\t * `{state}` and `{changed.<key>}` (a value the entry recorded, in the\n\t\t * words of `valueLabels`) are filled in; a label that names the actor leaves it off\n\t\t * the line under it. An action without a label shows as written.\n\t\t *\n\t\t * @type {Object<string, string>|null}\n\t\t */\n\t\tactionLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Words for the values an action label quotes as `{changed.<key>}`:\n\t\t * `{ purpose: { handling: 'Handling' } }`. A value without an entry\n\t\t * shows as written.\n\t\t *\n\t\t * @type {{[key: string]: {[value: string]: string}}|null}\n\t\t */\n\t\tvalueLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Records beside the object whose events belong in its history:\n\t\t * `[{ as, register, schema, filter, limit? }]`. The card lists the\n\t\t * objects matching `filter` (`@objectId` is this object; at most\n\t\t * `limit`, default 5), reads their trails and merges the entries with\n\t\t * the object's own, newest first. A related entry is labelled by\n\t\t * `actionLabels[\"<as>.<action>\"]` first. Object scope only.\n\t\t *\n\t\t * @type {Array<{as: string, register: string, schema: string, filter?: object, limit?: number}>}\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\trelatedSources: { type: Array, default: () => [] },\n\t\t/** Shown when a `source` or `endpoint` read failed. */\n\t\tfailedLabel: { type: String, default: () => t('nextcloud-vue', 'The history could not be loaded') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tHistory: markRaw(History),\n\t\t\tentries: [],\n\t\t\tloading: false,\n\t\t\t/** A `source` or `endpoint` read failed: said, rather than shown as an empty trail. */\n\t\t\tfailed: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Audit trail')\n\t\t},\n\n\t\t/** @return {Array<object>} The entries without the excluded actions. */\n\t\tshownEntries() {\n\t\t\tconst excluded = this.excludeActions.map((action) => String(action).toLowerCase())\n\t\t\tif (excluded.length === 0) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.filter((entry) => !excluded.includes(String(entry.action || entry.event || '').toLowerCase()))\n\t\t},\n\n\t\tdisplayedEntries() {\n\t\t\tconst entries = this.oldestFirst ? [...this.shownEntries].sort((a, b) => entryTime(a) - entryTime(b)) : this.shownEntries\n\t\t\treturn entries.slice(0, this.maxDisplay)\n\t\t},\n\n\t\t/**\n\t\t * The trail URL a `source` or `endpoint` names, '' while its record id\n\t\t * is unknown, or null when the card reads an object.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/leaf-cards-read-a-source/specs/leaf-cards-read-a-source/spec.md#requirement-a-leaf-card-reads-a-source-or-an-endpoint\n\t\t */\n\t\tsourceUrl() {\n\t\t\treturn leafSourceUrl({ source: this.source, endpoint: this.endpoint, apiBase: this.apiBase, objectId: this.objectId }, 'audit')\n\t\t},\n\n\t\t/** @return {boolean} Whether the entries read oldest first. */\n\t\toldestFirst() {\n\t\t\treturn this.order === 'oldest' || (this.order === '' && this.sourceUrl !== null)\n\t\t},\n\n\t\t/**\n\t\t * The path the card reads (prefixed at fetch time), or '' when it has nothing to read: the\n\t\t * object's own trail, or the app-wide readable feed narrowed to the\n\t\t * register and schema when set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {string}\n\t\t */\n\t\tfetchUrl() {\n\t\t\tif (this.sourceUrl !== null) {\n\t\t\t\treturn this.sourceUrl\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst params = new URLSearchParams({ limit: String(limit) })\n\t\t\tif (this.scope === 'app') {\n\t\t\t\tif (this.register) {\n\t\t\t\t\tparams.set('register', this.register)\n\t\t\t\t}\n\t\t\t\tif (this.schema) {\n\t\t\t\t\tparams.set('schema', this.schema)\n\t\t\t\t}\n\t\t\t\treturn `${this.apiBase}/audit-trails/readable?${params.toString()}`\n\t\t\t}\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails?${params.toString()}`\n\t\t},\n\t},\n\n\twatch: {\n\t\tfetchUrl: {\n\t\t\timmediate: true,\n\t\t\thandler(url) {\n\t\t\t\tif (url) {\n\t\t\t\t\tthis.fetchEntries()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the entries from `fetchUrl`. The readable feed answers\n\t\t * `{ rows, nextCursor }`, the object trail `{ results }`.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEntries() {\n\t\t\tconst url = this.fetchUrl\n\t\t\tif (!url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\t\tconst own = Array.isArray(list) ? list : []\n\t\t\t\t\tconst related = await this.fetchRelatedEntries()\n\t\t\t\t\tthis.entries = related.length > 0\n\t\t\t\t\t\t? [...own, ...related].sort((a, b) => entryTime(b) - entryTime(a))\n\t\t\t\t\t\t: own\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\n\t\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnAuditTrailCard] failed to fetch audit trail', err)\n\t\t\t\tthis.entries = []\n\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entries of the records beside the object (`relatedSources`),\n\t\t * each tagged with its source's `as`. A read that fails leaves that\n\t\t * source out.\n\t\t *\n\t\t * @return {Promise<Array<object>>} The entries.\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\tasync fetchRelatedEntries() {\n\t\t\tif (this.scope !== 'object' || this.sourceUrl !== null || !this.objectId || !Array.isArray(this.relatedSources)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst read = async (url) => {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(String(response.status))\n\t\t\t\t}\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\treturn Array.isArray(list) ? list : []\n\t\t\t}\n\t\t\tconst perSource = await Promise.all(this.relatedSources\n\t\t\t\t.filter((source) => source && source.register && source.schema)\n\t\t\t\t.map(async (source) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst params = new URLSearchParams({ _limit: String(Number(source.limit) > 0 ? Number(source.limit) : 5), _order: JSON.stringify({ '@self.created': 'DESC' }) })\n\t\t\t\t\t\tfor (const [key, value] of Object.entries(source.filter || {})) {\n\t\t\t\t\t\t\tparams.set(key, value === '@objectId' ? this.objectId : String(value))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst objects = await read(`${this.apiBase}/objects/${source.register}/${source.schema}?${params.toString()}`)\n\t\t\t\t\t\tconst ids = objects.map((object) => object?.id || object?.['@self']?.id || object?.uuid).filter(Boolean)\n\t\t\t\t\t\tconst trails = await Promise.all(ids.map((id) => read(`${this.apiBase}/objects/${source.register}/${source.schema}/${id}/audit-trails?limit=${limit}`).catch(() => [])))\n\t\t\t\t\t\treturn trails.flat().map((entry) => ({ ...entry, relatedAs: source.as || source.schema }))\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\treturn perSource.flat()\n\t\t},\n\n\t\t/**\n\t\t * What happened: the action, with the state it led to when the entry\n\t\t * names one (a flow task's `stateAfter`).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatAction(entry) {\n\t\t\tconst label = this.labelFor(entry)\n\t\t\tif (label !== null) {\n\t\t\t\treturn label\n\t\t\t\t\t.replace('{actor}', this.actorName(entry))\n\t\t\t\t\t.replace('{state}', entry.stateAfter || '')\n\t\t\t\t\t.replace(/\\{changed\\.([\\w-]+)\\}/g, (match, key) => this.changedValue(entry, key))\n\t\t\t}\n\t\t\tconst action = entry.action || entry.event || this.actionLabel\n\t\t\treturn entry.stateAfter ? `${action} (${entry.stateAfter})` : action\n\t\t},\n\n\t\t/**\n\t\t * A value the entry recorded under `changed` (an audit entry's context),\n\t\t * in the words of `valueLabels`; '' when it recorded none.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {string} key The recorded key.\n\t\t * @return {string} The value in words.\n\t\t * @spec openspec/changes/audit-label-changed-values/specs/audit-trail/spec.md#requirement-a-trail-label-can-quote-what-the-entry-recorded\n\t\t */\n\t\tchangedValue(entry, key) {\n\t\t\tconst changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}\n\t\t\tconst value = changed[key]\n\t\t\tif (value === null || value === undefined || typeof value === 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst words = this.valueLabels && this.valueLabels[key]\n\t\t\treturn words && typeof words[value] === 'string' ? words[value] : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The `actionLabels` entry for an entry: by action and state first,\n\t\t * then by action.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string|null} The label, or null when none is declared.\n\t\t * @spec openspec/changes/audit-trail-action-labels/specs/audit-trail/spec.md#requirement-a-trail-can-say-what-happened-in-words\n\t\t */\n\t\tlabelFor(entry) {\n\t\t\tconst labels = this.actionLabels\n\t\t\tconst action = entry.action || entry.event\n\t\t\tif (!labels || !action) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst state = entry.stateAfter || ''\n\t\t\tconst prefix = entry.relatedAs ? `${entry.relatedAs}.` : ''\n\t\t\tconst label = (prefix ? (labels[`${prefix}${action}:${state}`] ?? labels[`${prefix}${action}`]) : undefined)\n\t\t\t\t?? labels[`${action}:${state}`] ?? labels[action]\n\t\t\treturn typeof label === 'string' && label !== '' ? label : null\n\t\t},\n\n\t\t/**\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} Who did it, by name where the entry carries one.\n\t\t */\n\t\tactorName(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\n\t\t},\n\n\t\t/**\n\t\t * Who did it: the actor, the kind of performer when it is not a\n\t\t * person (a flow, the system), and on whose behalf.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatActor(entry) {\n\t\t\tconst named = (this.labelFor(entry) || '').includes('{actor}')\n\t\t\tconst parts = [named ? '' : this.actorName(entry)]\n\t\t\tif (entry.performerType && entry.performerType !== 'user') {\n\t\t\t\tparts.push(`(${entry.performerType})`)\n\t\t\t}\n\t\t\tif (entry.onBehalfOf) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'on behalf of {name}', { name: entry.onBehalfOf }))\n\t\t\t}\n\t\t\treturn parts.filter((part) => part !== '').join(' ')\n\t\t},\n\n\t\tformatWhen(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tif (raw === undefined || raw === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst d = new Date(raw)\n\t\t\tif (Number.isNaN(d.getTime()) === true) {\n\t\t\t\treturn String(raw)\n\t\t\t}\n\t\t\treturn d.toLocaleString(userLocale())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-audit-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-audit-card__row {\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-audit-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-audit-card__row-head {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-audit-card__action {\n\tcolor: var(--color-main-text);\n}\n\n.cn-audit-card__when {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\twhite-space: nowrap;\n}\n\n.cn-audit-card__row-body {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-audit-card__refused {\n\tmargin-inline-start: 6px;\n\tfont-weight: 700;\n\tcolor: var(--color-error-text, var(--color-main-text));\n}\n\n.cn-audit-card__reason {\n\tmargin: 2px 0 0;\n\tfont-size: 0.9em;\n\toverflow-wrap: anywhere;\n}\n\n.cn-audit-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t"],"mappings":";;;;;;;;;AAmEA;AACC;;;AAGD,SAAS,SAAS,CAAC,KAAK,EAAE;AACzB,CAAA,MAAM,IAAG,GAAI,IAAI,IAAI,CAAC,KAAK,CAAC,oBAAoB,KAAK,CAAC,OAAM,IAAK,KAAK,CAAC,SAAQ,IAAK,CAAC,CAAC,CAAC,OAAO,GAAC;AAC/F,CAAA,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAE,GAAE,CAAE,GAAE,IAAG;AACpC,CAAA;;AAEA;;;AAGC;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAED,aAAe;CACd,IAAI,EAAE,kBAAkB;;AAExB,CAAA,UAAU,EAAE,gBAAEA,UAAY,EAAE,eAAe;;AAE3C,CAAA,KAAK,EAAE;AACN;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;AACA,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACrC;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;;;;;;AAMC;AACA;;AAED,EAAA,KAAK,EAAE;GACN,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,QAAQ;GACjB,SAAS,EAAE,CAAC,KAAK,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;GACvD;;;AAGD,EAAA,OAAO,EAAE;GACR,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,aAAa;GACtB,SAAS,EAAE,CAAC,KAAK,KAAK,CAAC,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;GACzG;;AAED;EACA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAuB,EAAG;;AAE5D;;AAEC;;AAEA;AACA;;AAED,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;AAEvC;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;;;AAGvC,EAAA,KAAK,EAAE;GACN,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,EAAE;GACX,SAAS,EAAE,CAAC,KAAK,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;GAC9D;;;AAGD,EAAA,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;AACxC;;;;AAIC;AACA;;AAED,EAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI;;AAElD,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO;;AAE9C,EAAA,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAC,EAAG;;AAEpC,EAAA,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,sBAAsB,GAAG;AAC3F;AACA,EAAA,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,UAAU,GAAG;AAC7E;AACA,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,QAAQ,GAAG;;AAE1E,EAAA,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,WAAW,GAAG;AAC9E;;;;AAIC;;AAEA;AACA;;AAED,EAAA,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;AAC7C;;;AAGC;AACA;;;AAGD,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;AAC5C;;;;;;;AAOC;AACA;AACA;;AAED,EAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI;AAClD;AACA,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,iCAAiC,GAAG;EACnG;;AAED,CAAA,KAAK,EAAE,CAAC,UAAU,CAAC;;AAEnB,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;GACN,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;GACzB,OAAO,EAAE,EAAE;GACX,OAAO,EAAE,KAAK;AACd;GACA,MAAM,EAAE,KAAK;AACd,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;AACT,EAAA,aAAa,GAAG;GACf,OAAO,IAAI,CAAC,KAAM,IAAGA,SAAC,CAAC,eAAe,EAAE,aAAa,CAAA;GACrD;;AAED;AACA,EAAA,YAAY,GAAG;AACd,GAAA,MAAM,QAAO,GAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAA;GACjF,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE;AAC1B,IAAA,OAAO,IAAI,CAAC,OAAM;AACnB,IAAA;GACA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAK,IAAK,KAAK,CAAC,KAAI,IAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;GACjH;;AAED,EAAA,gBAAgB,GAAG;GAClB,MAAM,OAAM,GAAI,IAAI,CAAC,WAAU,GAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAA,GAAI,SAAS,CAAC,CAAC,CAAC,CAAA,GAAI,IAAI,CAAC,aAAW;AACxH,GAAA,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;GACvC;;AAED;;AAEC;AACA;AACA;AACA;;EAED,SAAS,GAAG;GACX,OAAO,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAO,EAAG,EAAE,OAAO,CAAA;GAC9H;;;EAGD,WAAW,GAAG;AACb,GAAA,OAAO,IAAI,CAAC,KAAI,KAAM,QAAO,KAAM,IAAI,CAAC,KAAI,KAAM,EAAG,IAAG,IAAI,CAAC,SAAU,KAAI,IAAI,CAAA;GAC/E;;AAED;AACC;;;AAGA;AACA;AACA;;EAED,QAAQ,GAAG;GACV,IAAI,IAAI,CAAC,SAAQ,KAAM,IAAI,EAAE;AAC5B,IAAA,OAAO,IAAI,CAAC,SAAQ;AACrB,IAAA;AACA,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,cAAc,CAAC,MAAK,GAAI,CAAE,GAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,CAAE,GAAE,IAAI,CAAC,WAAS;GACnG,MAAM,MAAO,GAAE,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAA,EAAG,EAAA;GAC3D,IAAI,IAAI,CAAC,KAAM,KAAI,KAAK,EAAE;AACzB,IAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AAClB,KAAA,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAA;AACrC,KAAA;AACA,IAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AAChB,KAAA,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAA;AACjC,KAAA;AACA,IAAA,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;AACnE,IAAA;GACA,IAAI,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE;AACrD,IAAA,OAAO,EAAC;AACT,IAAA;GACA,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;GAClH;EACD;;AAED,CAAA,KAAK,EAAE;AACN,EAAA,QAAQ,EAAE;AACT,GAAA,SAAS,EAAE,IAAI;GACf,OAAO,CAAC,GAAG,EAAE;AACZ,IAAA,IAAI,GAAG,EAAE;AACR,KAAA,IAAI,CAAC,YAAY,GAAC;AACnB,KAAA;IACA;GACD;EACD;;AAED,CAAA,OAAO,EAAE;AACR;;;AAGC;AACA;;;AAGD,EAAA,MAAM,YAAY,GAAG;AACpB,GAAA,MAAM,MAAM,IAAI,CAAC,SAAO;AACxB,GAAA,IAAI,CAAC,GAAG,EAAE;IACT,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;AAClB,GAAA,IAAI,CAAC,MAAO,GAAE,MAAI;AAClB,GAAA,IAAI;AACH,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,IAAI,EAAA;AACxE,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,KAAA,MAAM,IAAG,GAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAK,IAAG,KAAG;AAC7C,KAAA,MAAM,GAAE,GAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAA,GAAI,IAAG,GAAI,GAAC;AAC1C,KAAA,MAAM,OAAM,GAAI,MAAM,IAAI,CAAC,mBAAmB,GAAC;KAC/C,IAAI,CAAC,OAAM,GAAI,OAAO,CAAC,MAAK,GAAI,CAAA;QAC7B,CAAC,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAA;AACjE,QAAE,IAAE;KACJ,MAAK;AACN,KAAA,IAAI,CAAC,OAAQ,GAAE,GAAC;AAChB,KAAA,IAAI,CAAC,MAAK,GAAI,IAAI,CAAC,SAAQ,KAAM,KAAG;AACrC,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,gDAAgD,EAAE,GAAG,EAAA;AACnE,IAAA,IAAI,CAAC,OAAQ,GAAE,GAAC;AAChB,IAAA,IAAI,CAAC,MAAK,GAAI,IAAI,CAAC,SAAQ,KAAM,KAAG;AACrC,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;;AAED;;;;AAIC;AACA;AACA;;EAED,MAAM,mBAAmB,GAAG;GAC3B,IAAI,IAAI,CAAC,KAAM,KAAI,QAAO,IAAK,IAAI,CAAC,SAAQ,KAAM,IAAK,IAAG,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE;AAChH,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,cAAc,CAAC,MAAK,GAAI,CAAE,GAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,CAAE,GAAE,IAAI,CAAC,WAAS;GACnG,MAAM,OAAO,OAAO,GAAG,KAAK;AAC3B,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,IAAI,EAAA;AACxE,IAAA,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;AACjB,KAAA,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AACxC,KAAA;IACA,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,IAAA,MAAM,IAAG,GAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAK,IAAG,KAAG;AAC7C,IAAA,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAE,GAAE,IAAG,GAAI,EAAC;AACtC,KAAA;GACA,MAAM,SAAU,GAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,cAAa;KACpD,MAAM,CAAC,CAAC,MAAM,KAAK,MAAK,IAAK,MAAM,CAAC,QAAS,IAAG,MAAM,CAAC,MAAM,CAAA;AAC9D,KAAC,GAAG,CAAC,OAAO,MAAM,KAAK;AACtB,KAAA,IAAI;AACH,MAAA,MAAM,MAAK,GAAI,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAE,GAAE,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAE,GAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,MAAO,EAAC,GAAG,EAAA;MAC/J,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAA,IAAK,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAO,IAAG,EAAE,CAAC,EAAE;AAC/D,OAAA,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAM,KAAI,WAAY,GAAE,IAAI,CAAC,QAAS,GAAE,MAAM,CAAC,KAAK,CAAC,EAAA;AACtE,OAAA;MACA,MAAM,OAAM,GAAI,MAAM,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAA;MAC7G,MAAM,GAAE,GAAI,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,EAAE,EAAC,IAAK,MAAM,GAAG,OAAO,CAAC,EAAE,EAAG,IAAG,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,EAAA;AACvG,MAAA,MAAM,MAAK,GAAI,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,EAAA;AACvK,MAAA,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,MAAM,CAAC,MAAK,EAAG,CAAC,CAAA;AAC1F,MAAA,CAAE,MAAM;AACP,MAAA,OAAO,EAAC;AACT,MAAA;AACD,KAAC,CAAC,EAAA;AACH,GAAA,OAAO,SAAS,CAAC,IAAI,EAAC;GACtB;;AAED;;AAEC;AACA;;;;AAID,EAAA,YAAY,CAAC,KAAK,EAAE;AACnB,GAAA,MAAM,KAAM,GAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAA;AACjC,GAAA,IAAI,KAAI,KAAM,IAAI,EAAE;IACnB,OAAO,KAAI;AACV,MAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AACzC,MAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,cAAc,EAAE,CAAA;AAC1C,MAAC,OAAO,CAAC,wBAAwB,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AAClF,IAAA;GACA,MAAM,MAAK,GAAI,KAAK,CAAC,MAAO,IAAG,KAAK,CAAC,KAAI,IAAK,IAAI,CAAC,YAAU;GAC7D,OAAO,KAAK,CAAC,UAAS,GAAI,CAAC,EAAE,MAAM,CAAE,EAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAE,GAAE,MAAK;GACnE;;AAED;;;AAGC;;AAEA;AACA;AACA;;EAED,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE;AACxB,GAAA,MAAM,OAAM,GAAI,KAAI,IAAK,OAAO,KAAK,CAAC,YAAY,YAAY,KAAK,CAAC,OAAQ,KAAI,OAAO,KAAK,CAAC,OAAM,GAAI,GAAC;GACxG,MAAM,KAAM,GAAE,OAAO,CAAC,GAAG,EAAA;GACzB,IAAI,KAAI,KAAM,QAAQ,KAAM,KAAI,SAAQ,IAAK,OAAO,KAAM,KAAI,QAAQ,EAAE;AACvE,IAAA,OAAO,EAAC;AACT,IAAA;GACA,MAAM,KAAM,GAAE,IAAI,CAAC,WAAU,IAAK,IAAI,CAAC,WAAW,CAAC,GAAG,EAAA;AACtD,GAAA,OAAO,KAAI,IAAK,OAAO,KAAK,CAAC,KAAK,CAAA,KAAM,QAAO,GAAI,KAAK,CAAC,KAAK,CAAA,GAAI,MAAM,CAAC,KAAK,CAAA;GAC9E;;AAED;;;AAGC;;;;;AAKD,EAAA,QAAQ,CAAC,KAAK,EAAE;GACf,MAAM,MAAO,GAAE,IAAI,CAAC,aAAW;AAC/B,GAAA,MAAM,SAAS,KAAK,CAAC,MAAK,IAAK,KAAK,CAAC,MAAI;AACzC,GAAA,IAAI,CAAC,MAAK,IAAK,CAAC,MAAM,EAAE;AACvB,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,KAAM,GAAE,KAAK,CAAC,UAAS,IAAK,GAAC;GACnC,MAAM,MAAO,GAAE,KAAK,CAAC,SAAU,GAAE,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAE,GAAE,GAAC;GAC1D,MAAM,KAAM,GAAE,CAAC,UAAU,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAE,IAAG,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,SAAS;AAC1G,OAAG,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAE,IAAG,MAAM,CAAC,MAAM,EAAA;GACjD,OAAO,OAAO,KAAM,KAAI,QAAO,IAAK,KAAM,KAAI,EAAG,GAAE,KAAM,GAAE,IAAG;GAC9D;;AAED;;;;AAIA,EAAA,SAAS,CAAC,KAAK,EAAE;AAChB,GAAA,OAAO,KAAK,CAAC,gBAAe,IAAK,KAAK,CAAC,KAAI,IAAK,KAAK,CAAC,MAAK,IAAK,KAAK,CAAC,IAAK,IAAG,EAAC;GAC/E;;AAED;;AAEC;AACA;;;;AAID,EAAA,WAAW,CAAC,KAAK,EAAE;GAClB,MAAM,KAAM,GAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAE,IAAG,EAAE,EAAE,QAAQ,CAAC,SAAS,EAAA;AAC7D,GAAA,MAAM,KAAI,GAAI,CAAC,KAAM,GAAE,EAAG,GAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAA;GACjD,IAAI,KAAK,CAAC,aAAY,IAAK,KAAK,CAAC,aAAY,KAAM,MAAM,EAAE;AAC1D,IAAA,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAAA;AACtC,IAAA;AACA,GAAA,IAAI,KAAK,CAAC,UAAU,EAAE;AACrB,IAAA,KAAK,CAAC,IAAI,CAACA,SAAC,CAAC,eAAe,EAAE,qBAAqB,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,UAAS,EAAG,CAAC,EAAA;AACjF,IAAA;AACA,GAAA,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAG,KAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAA;GACnD;;AAED,EAAA,UAAU,CAAC,KAAK,EAAE;AACjB,GAAA,MAAM,GAAI,GAAE,KAAK,CAAC,gBAAe,IAAK,KAAK,CAAC,OAAQ,IAAG,KAAK,CAAC,UAAQ;AACrE,GAAA,IAAI,GAAI,KAAI,SAAU,IAAG,GAAE,KAAM,IAAI,EAAE;AACtC,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,CAAA,GAAI,IAAI,IAAI,CAAC,GAAG,EAAA;AACtB,GAAA,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAE,KAAI,IAAI,EAAE;AACvC,IAAA,OAAO,MAAM,CAAC,GAAG,CAAA;AAClB,IAAA;AACA,GAAA,OAAO,CAAC,CAAC,cAAc,CAAC,UAAU,EAAE,CAAA;GACpC;EACD;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"CnAuditTrailCard.vue2.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue"],"sourcesContent":["<!--\n CnAuditTrailCard — compact audit-trail widget for the integration\n registry.\n\n Surface-aware shell around the `audit-trail` integration: fetches\n the object's recent audit-trail entries (query-time storage strategy\n per AD-22) and renders them in a CnDetailCard.\n\n Per umbrella change `pluggable-integration-registry` AD-19 (surface\n fallback), one component handles all surfaces — `surface` is\n forwarded so the component can branch internally if desired.\n-->\n<template>\n\t<CnDetailCard\n\t\t:title=\"resolvedTitle\"\n\t\t:icon=\"History\"\n\t\t:collapsible=\"collapsible\"\n\t\t:data-surface=\"surface\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<p v-else-if=\"failed\"\n\t\t\tclass=\"cn-audit-card__empty\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-audit-card-failed\">\n\t\t\t{{ failedLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"shownEntries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ formatAction(entry) }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entry.authorized === false\"\n\t\t\t\t\t\tclass=\"cn-audit-card__refused\"\n\t\t\t\t\t\tdata-testid=\"cn-audit-card-refused\">{{ refusedLabel }}</span>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"entry.reason\" class=\"cn-audit-card__reason\">\n\t\t\t\t\t{{ entry.reason }}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t</ul>\n\t\t<p v-if=\"footnote\" class=\"cn-audit-card__footnote\" data-testid=\"cn-audit-trail-widget-footnote\">\n\t\t\t{{ footnote }}\n\t\t</p>\n\t\t<template v-if=\"shownEntries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ shownEntries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * @param {object} entry An audit entry.\n * @return {number} When it happened, in ms; 0 when unreadable.\n */\nfunction entryTime(entry) {\n\tconst time = new Date(entry.creationDateTime || entry.created || entry.timestamp || 0).getTime()\n\treturn Number.isNaN(time) ? 0 : time\n}\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/**\n\t\t * A muted line under the trail, inside the card (\"Kenmerk ZT-OMV-REG\").\n\t\t *\n\t\t * @spec openspec/changes/audit-trail-footnote/specs/audit-trail/spec.md#requirement-a-history-card-can-end-in-a-line-about-the-record\n\t\t */\n\t\tfootnote: { type: String, default: '' },\n\t\t/** OpenRegister register id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Required for `scope: \"object\"` (the default); unused for `scope: \"app\"`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/**\n\t\t * What the card lists. `object` (the default) is one object's trail\n\t\t * and needs `register`, `schema` and `objectId`. `app` is the\n\t\t * app-wide feed: the entries of every object the caller may read\n\t\t * (OpenRegister's `/audit-trails/readable`), narrowed to `register`\n\t\t * and `schema` when those are set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t */\n\t\tscope: {\n\t\t\ttype: String,\n\t\t\tdefault: 'object',\n\t\t\tvalidator: (value) => ['object', 'app'].includes(value),\n\t\t},\n\n\t\t/** Rendering surface — passed for AD-19 surface fallback consumers. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (value) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(value),\n\t\t},\n\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t/**\n\t\t * Read the trail of something that is not an OpenRegister object:\n\t\t * `{ kind: 'flow-task', id }` reads `${apiBase}/flow-tasks/{id}/audit`,\n\t\t * oldest first. `id` may be left out or written `@objectId`.\n\t\t *\n\t\t * @type {{kind: string, id?: (string|number)}|null}\n\t\t */\n\t\tsource: { type: Object, default: null },\n\n\t\t/** A trail URL that replaces the object's own; `{id}` is filled with the source's id or `objectId`. */\n\t\tendpoint: { type: String, default: '' },\n\n\t\t/** `newest` first (the object trail's order) or `oldest` first; empty takes `oldest` for a source and `newest` otherwise. */\n\t\torder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (value) => ['', 'newest', 'oldest'].includes(value),\n\t\t},\n\n\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/**\n\t\t * Actions the card leaves out (case-insensitive), such as `['read']`\n\t\t * for a history of what changed. It then reads more entries than it\n\t\t * shows, so the rows it leaves out do not crowd the rest away.\n\t\t *\n\t\t * @spec openspec/changes/audit-trail-exclude-actions/specs/audit-trail/spec.md#requirement-a-history-card-can-leave-out-events-by-action\n\t\t */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Whether the card collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Pre-translated empty label. */\n\t\tnoEntriesLabel: { type: String, default: () => t('nextcloud-vue', 'No audit entries yet') },\n\t\t/** Pre-translated overflow label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/** Pre-translated fallback action label. */\n\t\tactionLabel: { type: String, default: () => t('nextcloud-vue', 'Change') },\n\t\t/** Shown on an entry the server refused (`authorized: false`). */\n\t\trefusedLabel: { type: String, default: () => t('nextcloud-vue', '(refused)') },\n\t\t/**\n\t\t * What happened, in words, by action (`claim`) or by action and the\n\t\t * state it led to (`complete:completed`, tried first). `{actor}`,\n\t\t * `{state}` and `{changed.<key>}` (a value the entry recorded, in the\n\t\t * words of `valueLabels`) are filled in; a label that names the actor leaves it off\n\t\t * the line under it. An action without a label shows as written.\n\t\t *\n\t\t * @type {Object<string, string>|null}\n\t\t */\n\t\tactionLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Words for the values an action label quotes as `{changed.<key>}`:\n\t\t * `{ purpose: { handling: 'Handling' } }`. A value without an entry\n\t\t * shows as written.\n\t\t *\n\t\t * @type {{[key: string]: {[value: string]: string}}|null}\n\t\t */\n\t\tvalueLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Records beside the object whose events belong in its history:\n\t\t * `[{ as, register, schema, filter, limit? }]`. The card lists the\n\t\t * objects matching `filter` (`@objectId` is this object; at most\n\t\t * `limit`, default 5), reads their trails and merges the entries with\n\t\t * the object's own, newest first. A related entry is labelled by\n\t\t * `actionLabels[\"<as>.<action>\"]` first. Object scope only.\n\t\t *\n\t\t * @type {Array<{as: string, register: string, schema: string, filter?: object, limit?: number}>}\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\trelatedSources: { type: Array, default: () => [] },\n\t\t/** Shown when a `source` or `endpoint` read failed. */\n\t\tfailedLabel: { type: String, default: () => t('nextcloud-vue', 'The history could not be loaded') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tHistory: markRaw(History),\n\t\t\tentries: [],\n\t\t\tloading: false,\n\t\t\t/** A `source` or `endpoint` read failed: said, rather than shown as an empty trail. */\n\t\t\tfailed: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Audit trail')\n\t\t},\n\n\t\t/** @return {Array<object>} The entries without the excluded actions. */\n\t\tshownEntries() {\n\t\t\tconst excluded = this.excludeActions.map((action) => String(action).toLowerCase())\n\t\t\tif (excluded.length === 0) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.filter((entry) => !excluded.includes(String(entry.action || entry.event || '').toLowerCase()))\n\t\t},\n\n\t\tdisplayedEntries() {\n\t\t\tconst entries = this.oldestFirst ? [...this.shownEntries].sort((a, b) => entryTime(a) - entryTime(b)) : this.shownEntries\n\t\t\treturn entries.slice(0, this.maxDisplay)\n\t\t},\n\n\t\t/**\n\t\t * The trail URL a `source` or `endpoint` names, '' while its record id\n\t\t * is unknown, or null when the card reads an object.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/leaf-cards-read-a-source/specs/leaf-cards-read-a-source/spec.md#requirement-a-leaf-card-reads-a-source-or-an-endpoint\n\t\t */\n\t\tsourceUrl() {\n\t\t\treturn leafSourceUrl({ source: this.source, endpoint: this.endpoint, apiBase: this.apiBase, objectId: this.objectId }, 'audit')\n\t\t},\n\n\t\t/** @return {boolean} Whether the entries read oldest first. */\n\t\toldestFirst() {\n\t\t\treturn this.order === 'oldest' || (this.order === '' && this.sourceUrl !== null)\n\t\t},\n\n\t\t/**\n\t\t * The path the card reads (prefixed at fetch time), or '' when it has nothing to read: the\n\t\t * object's own trail, or the app-wide readable feed narrowed to the\n\t\t * register and schema when set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {string}\n\t\t */\n\t\tfetchUrl() {\n\t\t\tif (this.sourceUrl !== null) {\n\t\t\t\treturn this.sourceUrl\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst params = new URLSearchParams({ limit: String(limit) })\n\t\t\tif (this.scope === 'app') {\n\t\t\t\tif (this.register) {\n\t\t\t\t\tparams.set('register', this.register)\n\t\t\t\t}\n\t\t\t\tif (this.schema) {\n\t\t\t\t\tparams.set('schema', this.schema)\n\t\t\t\t}\n\t\t\t\treturn `${this.apiBase}/audit-trails/readable?${params.toString()}`\n\t\t\t}\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails?${params.toString()}`\n\t\t},\n\t},\n\n\twatch: {\n\t\tfetchUrl: {\n\t\t\timmediate: true,\n\t\t\thandler(url) {\n\t\t\t\tif (url) {\n\t\t\t\t\tthis.fetchEntries()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the entries from `fetchUrl`. The readable feed answers\n\t\t * `{ rows, nextCursor }`, the object trail `{ results }`.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEntries() {\n\t\t\tconst url = this.fetchUrl\n\t\t\tif (!url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\t\tconst own = Array.isArray(list) ? list : []\n\t\t\t\t\tconst related = await this.fetchRelatedEntries()\n\t\t\t\t\tthis.entries = related.length > 0\n\t\t\t\t\t\t? [...own, ...related].sort((a, b) => entryTime(b) - entryTime(a))\n\t\t\t\t\t\t: own\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\n\t\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnAuditTrailCard] failed to fetch audit trail', err)\n\t\t\t\tthis.entries = []\n\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entries of the records beside the object (`relatedSources`),\n\t\t * each tagged with its source's `as`. A read that fails leaves that\n\t\t * source out.\n\t\t *\n\t\t * @return {Promise<Array<object>>} The entries.\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\tasync fetchRelatedEntries() {\n\t\t\tif (this.scope !== 'object' || this.sourceUrl !== null || !this.objectId || !Array.isArray(this.relatedSources)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst read = async (url) => {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(String(response.status))\n\t\t\t\t}\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\treturn Array.isArray(list) ? list : []\n\t\t\t}\n\t\t\tconst perSource = await Promise.all(this.relatedSources\n\t\t\t\t.filter((source) => source && source.register && source.schema)\n\t\t\t\t.map(async (source) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst params = new URLSearchParams({ _limit: String(Number(source.limit) > 0 ? Number(source.limit) : 5), _order: JSON.stringify({ '@self.created': 'DESC' }) })\n\t\t\t\t\t\tfor (const [key, value] of Object.entries(source.filter || {})) {\n\t\t\t\t\t\t\tparams.set(key, value === '@objectId' ? this.objectId : String(value))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst objects = await read(`${this.apiBase}/objects/${source.register}/${source.schema}?${params.toString()}`)\n\t\t\t\t\t\tconst ids = objects.map((object) => object?.id || object?.['@self']?.id || object?.uuid).filter(Boolean)\n\t\t\t\t\t\tconst trails = await Promise.all(ids.map((id) => read(`${this.apiBase}/objects/${source.register}/${source.schema}/${id}/audit-trails?limit=${limit}`).catch(() => [])))\n\t\t\t\t\t\treturn trails.flat().map((entry) => ({ ...entry, relatedAs: source.as || source.schema }))\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\treturn perSource.flat()\n\t\t},\n\n\t\t/**\n\t\t * What happened: the action, with the state it led to when the entry\n\t\t * names one (a flow task's `stateAfter`).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatAction(entry) {\n\t\t\tconst label = this.labelFor(entry)\n\t\t\tif (label !== null) {\n\t\t\t\treturn label\n\t\t\t\t\t.replace('{actor}', this.actorName(entry))\n\t\t\t\t\t.replace('{state}', entry.stateAfter || '')\n\t\t\t\t\t.replace(/\\{changed\\.([\\w-]+)\\}/g, (match, key) => this.changedValue(entry, key))\n\t\t\t}\n\t\t\tconst action = entry.action || entry.event || this.actionLabel\n\t\t\treturn entry.stateAfter ? `${action} (${entry.stateAfter})` : action\n\t\t},\n\n\t\t/**\n\t\t * A value the entry recorded under `changed` (an audit entry's context),\n\t\t * in the words of `valueLabels`; '' when it recorded none.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {string} key The recorded key.\n\t\t * @return {string} The value in words.\n\t\t * @spec openspec/changes/audit-label-changed-values/specs/audit-trail/spec.md#requirement-a-trail-label-can-quote-what-the-entry-recorded\n\t\t */\n\t\tchangedValue(entry, key) {\n\t\t\tconst changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}\n\t\t\tconst value = changed[key]\n\t\t\tif (value === null || value === undefined || typeof value === 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst words = this.valueLabels && this.valueLabels[key]\n\t\t\treturn words && typeof words[value] === 'string' ? words[value] : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The `actionLabels` entry for an entry: by action and state first,\n\t\t * then by action.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string|null} The label, or null when none is declared.\n\t\t * @spec openspec/changes/audit-trail-action-labels/specs/audit-trail/spec.md#requirement-a-trail-can-say-what-happened-in-words\n\t\t */\n\t\tlabelFor(entry) {\n\t\t\tconst labels = this.actionLabels\n\t\t\tconst action = entry.action || entry.event\n\t\t\tif (!labels || !action) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst state = entry.stateAfter || ''\n\t\t\tconst prefix = entry.relatedAs ? `${entry.relatedAs}.` : ''\n\t\t\tconst label = (prefix ? (labels[`${prefix}${action}:${state}`] ?? labels[`${prefix}${action}`]) : undefined)\n\t\t\t\t?? labels[`${action}:${state}`] ?? labels[action]\n\t\t\treturn typeof label === 'string' && label !== '' ? label : null\n\t\t},\n\n\t\t/**\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} Who did it, by name where the entry carries one.\n\t\t */\n\t\tactorName(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\n\t\t},\n\n\t\t/**\n\t\t * Who did it: the actor, the kind of performer when it is not a\n\t\t * person (a flow, the system), and on whose behalf.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatActor(entry) {\n\t\t\tconst named = (this.labelFor(entry) || '').includes('{actor}')\n\t\t\tconst parts = [named ? '' : this.actorName(entry)]\n\t\t\tif (entry.performerType && entry.performerType !== 'user') {\n\t\t\t\tparts.push(`(${entry.performerType})`)\n\t\t\t}\n\t\t\tif (entry.onBehalfOf) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'on behalf of {name}', { name: entry.onBehalfOf }))\n\t\t\t}\n\t\t\treturn parts.filter((part) => part !== '').join(' ')\n\t\t},\n\n\t\tformatWhen(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tif (raw === undefined || raw === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst d = new Date(raw)\n\t\t\tif (Number.isNaN(d.getTime()) === true) {\n\t\t\t\treturn String(raw)\n\t\t\t}\n\t\t\treturn d.toLocaleString(userLocale())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-audit-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-audit-card__row {\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-audit-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-audit-card__row-head {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-audit-card__action {\n\tcolor: var(--color-main-text);\n}\n\n.cn-audit-card__when {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\twhite-space: nowrap;\n}\n\n.cn-audit-card__row-body {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-audit-card__refused {\n\tmargin-inline-start: 6px;\n\tfont-weight: 700;\n\tcolor: var(--color-error-text, var(--color-main-text));\n}\n\n.cn-audit-card__reason {\n\tmargin: 2px 0 0;\n\tfont-size: 0.9em;\n\toverflow-wrap: anywhere;\n}\n\n.cn-audit-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__footnote {\n\tmargin: 12px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t"],"mappings":"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"}
|
|
@@ -28,6 +28,11 @@ const _hoisted_10 = {
|
|
|
28
28
|
key: 0,
|
|
29
29
|
class: "cn-audit-card__reason"
|
|
30
30
|
};
|
|
31
|
+
const _hoisted_11 = {
|
|
32
|
+
key: 4,
|
|
33
|
+
class: "cn-audit-card__footnote",
|
|
34
|
+
"data-testid": "cn-audit-trail-widget-footnote"
|
|
35
|
+
};
|
|
31
36
|
|
|
32
37
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
33
38
|
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
|
|
@@ -67,7 +72,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
67
72
|
: createCommentVNode("v-if", true)
|
|
68
73
|
]))
|
|
69
74
|
}), 128 /* KEYED_FRAGMENT */))
|
|
70
|
-
]))
|
|
75
|
+
])),
|
|
76
|
+
($props.footnote)
|
|
77
|
+
? (openBlock(), createElementBlock("p", _hoisted_11, toDisplayString($props.footnote), 1 /* TEXT */))
|
|
78
|
+
: createCommentVNode("v-if", true)
|
|
71
79
|
]),
|
|
72
80
|
_: 2 /* DYNAMIC */
|
|
73
81
|
}, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnAuditTrailCard.vue3.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue?vue&type=template&id=841ccbc2&lang.js"],"sourcesContent":["<!--\n CnAuditTrailCard — compact audit-trail widget for the integration\n registry.\n\n Surface-aware shell around the `audit-trail` integration: fetches\n the object's recent audit-trail entries (query-time storage strategy\n per AD-22) and renders them in a CnDetailCard.\n\n Per umbrella change `pluggable-integration-registry` AD-19 (surface\n fallback), one component handles all surfaces — `surface` is\n forwarded so the component can branch internally if desired.\n-->\n<template>\n\t<CnDetailCard\n\t\t:title=\"resolvedTitle\"\n\t\t:icon=\"History\"\n\t\t:collapsible=\"collapsible\"\n\t\t:data-surface=\"surface\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<p v-else-if=\"failed\"\n\t\t\tclass=\"cn-audit-card__empty\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-audit-card-failed\">\n\t\t\t{{ failedLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"shownEntries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ formatAction(entry) }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entry.authorized === false\"\n\t\t\t\t\t\tclass=\"cn-audit-card__refused\"\n\t\t\t\t\t\tdata-testid=\"cn-audit-card-refused\">{{ refusedLabel }}</span>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"entry.reason\" class=\"cn-audit-card__reason\">\n\t\t\t\t\t{{ entry.reason }}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t</ul>\n\t\t<template v-if=\"shownEntries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ shownEntries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * @param {object} entry An audit entry.\n * @return {number} When it happened, in ms; 0 when unreadable.\n */\nfunction entryTime(entry) {\n\tconst time = new Date(entry.creationDateTime || entry.created || entry.timestamp || 0).getTime()\n\treturn Number.isNaN(time) ? 0 : time\n}\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Required for `scope: \"object\"` (the default); unused for `scope: \"app\"`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/**\n\t\t * What the card lists. `object` (the default) is one object's trail\n\t\t * and needs `register`, `schema` and `objectId`. `app` is the\n\t\t * app-wide feed: the entries of every object the caller may read\n\t\t * (OpenRegister's `/audit-trails/readable`), narrowed to `register`\n\t\t * and `schema` when those are set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t */\n\t\tscope: {\n\t\t\ttype: String,\n\t\t\tdefault: 'object',\n\t\t\tvalidator: (value) => ['object', 'app'].includes(value),\n\t\t},\n\n\t\t/** Rendering surface — passed for AD-19 surface fallback consumers. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (value) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(value),\n\t\t},\n\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t/**\n\t\t * Read the trail of something that is not an OpenRegister object:\n\t\t * `{ kind: 'flow-task', id }` reads `${apiBase}/flow-tasks/{id}/audit`,\n\t\t * oldest first. `id` may be left out or written `@objectId`.\n\t\t *\n\t\t * @type {{kind: string, id?: (string|number)}|null}\n\t\t */\n\t\tsource: { type: Object, default: null },\n\n\t\t/** A trail URL that replaces the object's own; `{id}` is filled with the source's id or `objectId`. */\n\t\tendpoint: { type: String, default: '' },\n\n\t\t/** `newest` first (the object trail's order) or `oldest` first; empty takes `oldest` for a source and `newest` otherwise. */\n\t\torder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (value) => ['', 'newest', 'oldest'].includes(value),\n\t\t},\n\n\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/**\n\t\t * Actions the card leaves out (case-insensitive), such as `['read']`\n\t\t * for a history of what changed. It then reads more entries than it\n\t\t * shows, so the rows it leaves out do not crowd the rest away.\n\t\t *\n\t\t * @spec openspec/changes/audit-trail-exclude-actions/specs/audit-trail/spec.md#requirement-a-history-card-can-leave-out-events-by-action\n\t\t */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Whether the card collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Pre-translated empty label. */\n\t\tnoEntriesLabel: { type: String, default: () => t('nextcloud-vue', 'No audit entries yet') },\n\t\t/** Pre-translated overflow label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/** Pre-translated fallback action label. */\n\t\tactionLabel: { type: String, default: () => t('nextcloud-vue', 'Change') },\n\t\t/** Shown on an entry the server refused (`authorized: false`). */\n\t\trefusedLabel: { type: String, default: () => t('nextcloud-vue', '(refused)') },\n\t\t/**\n\t\t * What happened, in words, by action (`claim`) or by action and the\n\t\t * state it led to (`complete:completed`, tried first). `{actor}`,\n\t\t * `{state}` and `{changed.<key>}` (a value the entry recorded, in the\n\t\t * words of `valueLabels`) are filled in; a label that names the actor leaves it off\n\t\t * the line under it. An action without a label shows as written.\n\t\t *\n\t\t * @type {Object<string, string>|null}\n\t\t */\n\t\tactionLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Words for the values an action label quotes as `{changed.<key>}`:\n\t\t * `{ purpose: { handling: 'Handling' } }`. A value without an entry\n\t\t * shows as written.\n\t\t *\n\t\t * @type {{[key: string]: {[value: string]: string}}|null}\n\t\t */\n\t\tvalueLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Records beside the object whose events belong in its history:\n\t\t * `[{ as, register, schema, filter, limit? }]`. The card lists the\n\t\t * objects matching `filter` (`@objectId` is this object; at most\n\t\t * `limit`, default 5), reads their trails and merges the entries with\n\t\t * the object's own, newest first. A related entry is labelled by\n\t\t * `actionLabels[\"<as>.<action>\"]` first. Object scope only.\n\t\t *\n\t\t * @type {Array<{as: string, register: string, schema: string, filter?: object, limit?: number}>}\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\trelatedSources: { type: Array, default: () => [] },\n\t\t/** Shown when a `source` or `endpoint` read failed. */\n\t\tfailedLabel: { type: String, default: () => t('nextcloud-vue', 'The history could not be loaded') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tHistory: markRaw(History),\n\t\t\tentries: [],\n\t\t\tloading: false,\n\t\t\t/** A `source` or `endpoint` read failed: said, rather than shown as an empty trail. */\n\t\t\tfailed: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Audit trail')\n\t\t},\n\n\t\t/** @return {Array<object>} The entries without the excluded actions. */\n\t\tshownEntries() {\n\t\t\tconst excluded = this.excludeActions.map((action) => String(action).toLowerCase())\n\t\t\tif (excluded.length === 0) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.filter((entry) => !excluded.includes(String(entry.action || entry.event || '').toLowerCase()))\n\t\t},\n\n\t\tdisplayedEntries() {\n\t\t\tconst entries = this.oldestFirst ? [...this.shownEntries].sort((a, b) => entryTime(a) - entryTime(b)) : this.shownEntries\n\t\t\treturn entries.slice(0, this.maxDisplay)\n\t\t},\n\n\t\t/**\n\t\t * The trail URL a `source` or `endpoint` names, '' while its record id\n\t\t * is unknown, or null when the card reads an object.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/leaf-cards-read-a-source/specs/leaf-cards-read-a-source/spec.md#requirement-a-leaf-card-reads-a-source-or-an-endpoint\n\t\t */\n\t\tsourceUrl() {\n\t\t\treturn leafSourceUrl({ source: this.source, endpoint: this.endpoint, apiBase: this.apiBase, objectId: this.objectId }, 'audit')\n\t\t},\n\n\t\t/** @return {boolean} Whether the entries read oldest first. */\n\t\toldestFirst() {\n\t\t\treturn this.order === 'oldest' || (this.order === '' && this.sourceUrl !== null)\n\t\t},\n\n\t\t/**\n\t\t * The path the card reads (prefixed at fetch time), or '' when it has nothing to read: the\n\t\t * object's own trail, or the app-wide readable feed narrowed to the\n\t\t * register and schema when set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {string}\n\t\t */\n\t\tfetchUrl() {\n\t\t\tif (this.sourceUrl !== null) {\n\t\t\t\treturn this.sourceUrl\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst params = new URLSearchParams({ limit: String(limit) })\n\t\t\tif (this.scope === 'app') {\n\t\t\t\tif (this.register) {\n\t\t\t\t\tparams.set('register', this.register)\n\t\t\t\t}\n\t\t\t\tif (this.schema) {\n\t\t\t\t\tparams.set('schema', this.schema)\n\t\t\t\t}\n\t\t\t\treturn `${this.apiBase}/audit-trails/readable?${params.toString()}`\n\t\t\t}\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails?${params.toString()}`\n\t\t},\n\t},\n\n\twatch: {\n\t\tfetchUrl: {\n\t\t\timmediate: true,\n\t\t\thandler(url) {\n\t\t\t\tif (url) {\n\t\t\t\t\tthis.fetchEntries()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the entries from `fetchUrl`. The readable feed answers\n\t\t * `{ rows, nextCursor }`, the object trail `{ results }`.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEntries() {\n\t\t\tconst url = this.fetchUrl\n\t\t\tif (!url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\t\tconst own = Array.isArray(list) ? list : []\n\t\t\t\t\tconst related = await this.fetchRelatedEntries()\n\t\t\t\t\tthis.entries = related.length > 0\n\t\t\t\t\t\t? [...own, ...related].sort((a, b) => entryTime(b) - entryTime(a))\n\t\t\t\t\t\t: own\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\n\t\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnAuditTrailCard] failed to fetch audit trail', err)\n\t\t\t\tthis.entries = []\n\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entries of the records beside the object (`relatedSources`),\n\t\t * each tagged with its source's `as`. A read that fails leaves that\n\t\t * source out.\n\t\t *\n\t\t * @return {Promise<Array<object>>} The entries.\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\tasync fetchRelatedEntries() {\n\t\t\tif (this.scope !== 'object' || this.sourceUrl !== null || !this.objectId || !Array.isArray(this.relatedSources)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst read = async (url) => {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(String(response.status))\n\t\t\t\t}\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\treturn Array.isArray(list) ? list : []\n\t\t\t}\n\t\t\tconst perSource = await Promise.all(this.relatedSources\n\t\t\t\t.filter((source) => source && source.register && source.schema)\n\t\t\t\t.map(async (source) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst params = new URLSearchParams({ _limit: String(Number(source.limit) > 0 ? Number(source.limit) : 5), _order: JSON.stringify({ '@self.created': 'DESC' }) })\n\t\t\t\t\t\tfor (const [key, value] of Object.entries(source.filter || {})) {\n\t\t\t\t\t\t\tparams.set(key, value === '@objectId' ? this.objectId : String(value))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst objects = await read(`${this.apiBase}/objects/${source.register}/${source.schema}?${params.toString()}`)\n\t\t\t\t\t\tconst ids = objects.map((object) => object?.id || object?.['@self']?.id || object?.uuid).filter(Boolean)\n\t\t\t\t\t\tconst trails = await Promise.all(ids.map((id) => read(`${this.apiBase}/objects/${source.register}/${source.schema}/${id}/audit-trails?limit=${limit}`).catch(() => [])))\n\t\t\t\t\t\treturn trails.flat().map((entry) => ({ ...entry, relatedAs: source.as || source.schema }))\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\treturn perSource.flat()\n\t\t},\n\n\t\t/**\n\t\t * What happened: the action, with the state it led to when the entry\n\t\t * names one (a flow task's `stateAfter`).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatAction(entry) {\n\t\t\tconst label = this.labelFor(entry)\n\t\t\tif (label !== null) {\n\t\t\t\treturn label\n\t\t\t\t\t.replace('{actor}', this.actorName(entry))\n\t\t\t\t\t.replace('{state}', entry.stateAfter || '')\n\t\t\t\t\t.replace(/\\{changed\\.([\\w-]+)\\}/g, (match, key) => this.changedValue(entry, key))\n\t\t\t}\n\t\t\tconst action = entry.action || entry.event || this.actionLabel\n\t\t\treturn entry.stateAfter ? `${action} (${entry.stateAfter})` : action\n\t\t},\n\n\t\t/**\n\t\t * A value the entry recorded under `changed` (an audit entry's context),\n\t\t * in the words of `valueLabels`; '' when it recorded none.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {string} key The recorded key.\n\t\t * @return {string} The value in words.\n\t\t * @spec openspec/changes/audit-label-changed-values/specs/audit-trail/spec.md#requirement-a-trail-label-can-quote-what-the-entry-recorded\n\t\t */\n\t\tchangedValue(entry, key) {\n\t\t\tconst changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}\n\t\t\tconst value = changed[key]\n\t\t\tif (value === null || value === undefined || typeof value === 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst words = this.valueLabels && this.valueLabels[key]\n\t\t\treturn words && typeof words[value] === 'string' ? words[value] : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The `actionLabels` entry for an entry: by action and state first,\n\t\t * then by action.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string|null} The label, or null when none is declared.\n\t\t * @spec openspec/changes/audit-trail-action-labels/specs/audit-trail/spec.md#requirement-a-trail-can-say-what-happened-in-words\n\t\t */\n\t\tlabelFor(entry) {\n\t\t\tconst labels = this.actionLabels\n\t\t\tconst action = entry.action || entry.event\n\t\t\tif (!labels || !action) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst state = entry.stateAfter || ''\n\t\t\tconst prefix = entry.relatedAs ? `${entry.relatedAs}.` : ''\n\t\t\tconst label = (prefix ? (labels[`${prefix}${action}:${state}`] ?? labels[`${prefix}${action}`]) : undefined)\n\t\t\t\t?? labels[`${action}:${state}`] ?? labels[action]\n\t\t\treturn typeof label === 'string' && label !== '' ? label : null\n\t\t},\n\n\t\t/**\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} Who did it, by name where the entry carries one.\n\t\t */\n\t\tactorName(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\n\t\t},\n\n\t\t/**\n\t\t * Who did it: the actor, the kind of performer when it is not a\n\t\t * person (a flow, the system), and on whose behalf.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatActor(entry) {\n\t\t\tconst named = (this.labelFor(entry) || '').includes('{actor}')\n\t\t\tconst parts = [named ? '' : this.actorName(entry)]\n\t\t\tif (entry.performerType && entry.performerType !== 'user') {\n\t\t\t\tparts.push(`(${entry.performerType})`)\n\t\t\t}\n\t\t\tif (entry.onBehalfOf) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'on behalf of {name}', { name: entry.onBehalfOf }))\n\t\t\t}\n\t\t\treturn parts.filter((part) => part !== '').join(' ')\n\t\t},\n\n\t\tformatWhen(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tif (raw === undefined || raw === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst d = new Date(raw)\n\t\t\tif (Number.isNaN(d.getTime()) === true) {\n\t\t\t\treturn String(raw)\n\t\t\t}\n\t\t\treturn d.toLocaleString(userLocale())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-audit-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-audit-card__row {\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-audit-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-audit-card__row-head {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-audit-card__action {\n\tcolor: var(--color-main-text);\n}\n\n.cn-audit-card__when {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\twhite-space: nowrap;\n}\n\n.cn-audit-card__row-body {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-audit-card__refused {\n\tmargin-inline-start: 6px;\n\tfont-weight: 700;\n\tcolor: var(--color-error-text, var(--color-main-text));\n}\n\n.cn-audit-card__reason {\n\tmargin: 2px 0 0;\n\tfont-size: 0.9em;\n\toverflow-wrap: anywhere;\n}\n\n.cn-audit-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_toDisplayString","_openBlock","_createElementVNode"],"mappings":";;;;OAgCQ,EAAC,sBAAqB;AAC1B,EAAA,IAAG,EAAE,OAAM;AACV,EAAA,aAAS,EAAC,sBAAsB;;;;AAGG,EAAA,KAAA,EAAA,sBAAA;;;;AAG3B,EAAA,KAAK,EAAC,qBAAmB;;AAK7B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAO,GAAA;AACf,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,uBAAA,GAAA;AACA,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,qBAAA,GAAA;AAEE,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAyB,GAAA;AAC7B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,sBAAsB,GAAA;;;AAGjC,EAAA,KAAK,EAAC,wBAAA;AACN,EAAA,aAAS,EAAA,uBAAA;;;;;;;;;;;uBA7BbA;AACC,IAAA,KAAK,EAAE,QAAY,CAAA,aAAA;AACnB,IAAA,IAAI,EAAA,KAAA,CAAA,OAAA;AACJ,IAAA,WAAW,EAAE,MAAW,CAAA,WAAA;AACxB,IAAA,cAAE,EAAA,MAAA,CAAA,OAAA;;qBACD,MAA8B;OAAX,KAAO,CAAA,OAAA;wBAA1BA,WAA8B,CAAA,wBAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,CAAA;WAClB,KAAE,CAAA,MAAA;0BAAdC,kBAKE,CAAA,GAAA,EALF,UAKE,EAAAC,eAAA,CADA,MAAW,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;AAEC,YAAA,CAAA,QAAA,CAAA,YAAY,CAAC,MAAM,KAAA,CAAA;4BAAjCD,kBAEI,CAAA,KAAA,EAFJ,UAEI,EAAAC,eAAA,CADF,MAAc,CAAA,cAAA,CAAA,EAAA,CAAA,YAAA;AAEd,eAAAC,SAAA,EAAA,EAAAF,kBAAA,OAAA;gFAEc,QAAgB,CAAA,gBAAA,EAAA,CAAzB,KAAK,KAAA;;oBACX,GAAG,EAAC,KAAA,CAAA,EAAA;AACL,oBAAA,KAAK,EAAC,oBAAoB;;AACzB,oBAAAG,kBAAA,CAGC,OAHD,UAGC,EAAA;AAFD,sBAAAA,kBAAA,CAAE,QAAF,EAAA,UAAE,EAAAF,eAAA,CAAA,QAAA,CAAA,YAAA,CAAA,KAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AACF,sBAAAE,kBAAA,CAAE,MAAF,EAAA,UAAE,EAAAF,eAAA,CAAA,QAAA,CAAA,UAAA,CAAA,KAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;AAEH,oBAAAE,kBAAA,QAAA;AACC,sBAAAA,kBAAA,CAAgE,MAAhE,EAAA,UAAgE,EAA1BF,eAAA,CAAA,QAAA,CAAA,WAAW,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;AAEjD,sBAAA,CAAA,KAAA,CAAA,UAAA,KAAA,KAAA;wCADND,kBAGU,CAAA,MAAA,EAHV,UAGU,EAAAC,eAAA,CAAA,MAAA,CAAA,YAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"CnAuditTrailCard.vue3.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue?vue&type=template&id=834a0068&lang.js"],"sourcesContent":["<!--\n CnAuditTrailCard — compact audit-trail widget for the integration\n registry.\n\n Surface-aware shell around the `audit-trail` integration: fetches\n the object's recent audit-trail entries (query-time storage strategy\n per AD-22) and renders them in a CnDetailCard.\n\n Per umbrella change `pluggable-integration-registry` AD-19 (surface\n fallback), one component handles all surfaces — `surface` is\n forwarded so the component can branch internally if desired.\n-->\n<template>\n\t<CnDetailCard\n\t\t:title=\"resolvedTitle\"\n\t\t:icon=\"History\"\n\t\t:collapsible=\"collapsible\"\n\t\t:data-surface=\"surface\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<p v-else-if=\"failed\"\n\t\t\tclass=\"cn-audit-card__empty\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-audit-card-failed\">\n\t\t\t{{ failedLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"shownEntries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ formatAction(entry) }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"entry.authorized === false\"\n\t\t\t\t\t\tclass=\"cn-audit-card__refused\"\n\t\t\t\t\t\tdata-testid=\"cn-audit-card-refused\">{{ refusedLabel }}</span>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"entry.reason\" class=\"cn-audit-card__reason\">\n\t\t\t\t\t{{ entry.reason }}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t</ul>\n\t\t<p v-if=\"footnote\" class=\"cn-audit-card__footnote\" data-testid=\"cn-audit-trail-widget-footnote\">\n\t\t\t{{ footnote }}\n\t\t</p>\n\t\t<template v-if=\"shownEntries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ shownEntries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * @param {object} entry An audit entry.\n * @return {number} When it happened, in ms; 0 when unreadable.\n */\nfunction entryTime(entry) {\n\tconst time = new Date(entry.creationDateTime || entry.created || entry.timestamp || 0).getTime()\n\treturn Number.isNaN(time) ? 0 : time\n}\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/**\n\t\t * A muted line under the trail, inside the card (\"Kenmerk ZT-OMV-REG\").\n\t\t *\n\t\t * @spec openspec/changes/audit-trail-footnote/specs/audit-trail/spec.md#requirement-a-history-card-can-end-in-a-line-about-the-record\n\t\t */\n\t\tfootnote: { type: String, default: '' },\n\t\t/** OpenRegister register id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Required for `scope: \"object\"` (the default); unused for `scope: \"app\"`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/**\n\t\t * What the card lists. `object` (the default) is one object's trail\n\t\t * and needs `register`, `schema` and `objectId`. `app` is the\n\t\t * app-wide feed: the entries of every object the caller may read\n\t\t * (OpenRegister's `/audit-trails/readable`), narrowed to `register`\n\t\t * and `schema` when those are set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t */\n\t\tscope: {\n\t\t\ttype: String,\n\t\t\tdefault: 'object',\n\t\t\tvalidator: (value) => ['object', 'app'].includes(value),\n\t\t},\n\n\t\t/** Rendering surface — passed for AD-19 surface fallback consumers. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (value) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(value),\n\t\t},\n\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t/**\n\t\t * Read the trail of something that is not an OpenRegister object:\n\t\t * `{ kind: 'flow-task', id }` reads `${apiBase}/flow-tasks/{id}/audit`,\n\t\t * oldest first. `id` may be left out or written `@objectId`.\n\t\t *\n\t\t * @type {{kind: string, id?: (string|number)}|null}\n\t\t */\n\t\tsource: { type: Object, default: null },\n\n\t\t/** A trail URL that replaces the object's own; `{id}` is filled with the source's id or `objectId`. */\n\t\tendpoint: { type: String, default: '' },\n\n\t\t/** `newest` first (the object trail's order) or `oldest` first; empty takes `oldest` for a source and `newest` otherwise. */\n\t\torder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t\tvalidator: (value) => ['', 'newest', 'oldest'].includes(value),\n\t\t},\n\n\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/**\n\t\t * Actions the card leaves out (case-insensitive), such as `['read']`\n\t\t * for a history of what changed. It then reads more entries than it\n\t\t * shows, so the rows it leaves out do not crowd the rest away.\n\t\t *\n\t\t * @spec openspec/changes/audit-trail-exclude-actions/specs/audit-trail/spec.md#requirement-a-history-card-can-leave-out-events-by-action\n\t\t */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Whether the card collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Pre-translated empty label. */\n\t\tnoEntriesLabel: { type: String, default: () => t('nextcloud-vue', 'No audit entries yet') },\n\t\t/** Pre-translated overflow label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/** Pre-translated fallback action label. */\n\t\tactionLabel: { type: String, default: () => t('nextcloud-vue', 'Change') },\n\t\t/** Shown on an entry the server refused (`authorized: false`). */\n\t\trefusedLabel: { type: String, default: () => t('nextcloud-vue', '(refused)') },\n\t\t/**\n\t\t * What happened, in words, by action (`claim`) or by action and the\n\t\t * state it led to (`complete:completed`, tried first). `{actor}`,\n\t\t * `{state}` and `{changed.<key>}` (a value the entry recorded, in the\n\t\t * words of `valueLabels`) are filled in; a label that names the actor leaves it off\n\t\t * the line under it. An action without a label shows as written.\n\t\t *\n\t\t * @type {Object<string, string>|null}\n\t\t */\n\t\tactionLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Words for the values an action label quotes as `{changed.<key>}`:\n\t\t * `{ purpose: { handling: 'Handling' } }`. A value without an entry\n\t\t * shows as written.\n\t\t *\n\t\t * @type {{[key: string]: {[value: string]: string}}|null}\n\t\t */\n\t\tvalueLabels: { type: Object, default: null },\n\t\t/**\n\t\t * Records beside the object whose events belong in its history:\n\t\t * `[{ as, register, schema, filter, limit? }]`. The card lists the\n\t\t * objects matching `filter` (`@objectId` is this object; at most\n\t\t * `limit`, default 5), reads their trails and merges the entries with\n\t\t * the object's own, newest first. A related entry is labelled by\n\t\t * `actionLabels[\"<as>.<action>\"]` first. Object scope only.\n\t\t *\n\t\t * @type {Array<{as: string, register: string, schema: string, filter?: object, limit?: number}>}\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\trelatedSources: { type: Array, default: () => [] },\n\t\t/** Shown when a `source` or `endpoint` read failed. */\n\t\tfailedLabel: { type: String, default: () => t('nextcloud-vue', 'The history could not be loaded') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tHistory: markRaw(History),\n\t\t\tentries: [],\n\t\t\tloading: false,\n\t\t\t/** A `source` or `endpoint` read failed: said, rather than shown as an empty trail. */\n\t\t\tfailed: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Audit trail')\n\t\t},\n\n\t\t/** @return {Array<object>} The entries without the excluded actions. */\n\t\tshownEntries() {\n\t\t\tconst excluded = this.excludeActions.map((action) => String(action).toLowerCase())\n\t\t\tif (excluded.length === 0) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.filter((entry) => !excluded.includes(String(entry.action || entry.event || '').toLowerCase()))\n\t\t},\n\n\t\tdisplayedEntries() {\n\t\t\tconst entries = this.oldestFirst ? [...this.shownEntries].sort((a, b) => entryTime(a) - entryTime(b)) : this.shownEntries\n\t\t\treturn entries.slice(0, this.maxDisplay)\n\t\t},\n\n\t\t/**\n\t\t * The trail URL a `source` or `endpoint` names, '' while its record id\n\t\t * is unknown, or null when the card reads an object.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/leaf-cards-read-a-source/specs/leaf-cards-read-a-source/spec.md#requirement-a-leaf-card-reads-a-source-or-an-endpoint\n\t\t */\n\t\tsourceUrl() {\n\t\t\treturn leafSourceUrl({ source: this.source, endpoint: this.endpoint, apiBase: this.apiBase, objectId: this.objectId }, 'audit')\n\t\t},\n\n\t\t/** @return {boolean} Whether the entries read oldest first. */\n\t\toldestFirst() {\n\t\t\treturn this.order === 'oldest' || (this.order === '' && this.sourceUrl !== null)\n\t\t},\n\n\t\t/**\n\t\t * The path the card reads (prefixed at fetch time), or '' when it has nothing to read: the\n\t\t * object's own trail, or the app-wide readable feed narrowed to the\n\t\t * register and schema when set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {string}\n\t\t */\n\t\tfetchUrl() {\n\t\t\tif (this.sourceUrl !== null) {\n\t\t\t\treturn this.sourceUrl\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst params = new URLSearchParams({ limit: String(limit) })\n\t\t\tif (this.scope === 'app') {\n\t\t\t\tif (this.register) {\n\t\t\t\t\tparams.set('register', this.register)\n\t\t\t\t}\n\t\t\t\tif (this.schema) {\n\t\t\t\t\tparams.set('schema', this.schema)\n\t\t\t\t}\n\t\t\t\treturn `${this.apiBase}/audit-trails/readable?${params.toString()}`\n\t\t\t}\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trails?${params.toString()}`\n\t\t},\n\t},\n\n\twatch: {\n\t\tfetchUrl: {\n\t\t\timmediate: true,\n\t\t\thandler(url) {\n\t\t\t\tif (url) {\n\t\t\t\t\tthis.fetchEntries()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the entries from `fetchUrl`. The readable feed answers\n\t\t * `{ rows, nextCursor }`, the object trail `{ results }`.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEntries() {\n\t\t\tconst url = this.fetchUrl\n\t\t\tif (!url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.failed = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\t\tconst own = Array.isArray(list) ? list : []\n\t\t\t\t\tconst related = await this.fetchRelatedEntries()\n\t\t\t\t\tthis.entries = related.length > 0\n\t\t\t\t\t\t? [...own, ...related].sort((a, b) => entryTime(b) - entryTime(a))\n\t\t\t\t\t\t: own\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\n\t\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnAuditTrailCard] failed to fetch audit trail', err)\n\t\t\t\tthis.entries = []\n\t\t\t\tthis.failed = this.sourceUrl !== null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entries of the records beside the object (`relatedSources`),\n\t\t * each tagged with its source's `as`. A read that fails leaves that\n\t\t * source out.\n\t\t *\n\t\t * @return {Promise<Array<object>>} The entries.\n\t\t * @spec openspec/changes/audit-trail-related-sources/specs/audit-trail/spec.md#requirement-an-objects-history-can-include-the-events-of-records-beside-it\n\t\t */\n\t\tasync fetchRelatedEntries() {\n\t\t\tif (this.scope !== 'object' || this.sourceUrl !== null || !this.objectId || !Array.isArray(this.relatedSources)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst limit = this.excludeActions.length > 0 ? Math.min(100, this.maxDisplay * 10) : this.maxDisplay\n\t\t\tconst read = async (url) => {\n\t\t\t\tconst response = await fetch(prefixUrl(url), { headers: buildHeaders() })\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(String(response.status))\n\t\t\t\t}\n\t\t\t\tconst data = await response.json()\n\t\t\t\tconst list = data.results || data.rows || data\n\t\t\t\treturn Array.isArray(list) ? list : []\n\t\t\t}\n\t\t\tconst perSource = await Promise.all(this.relatedSources\n\t\t\t\t.filter((source) => source && source.register && source.schema)\n\t\t\t\t.map(async (source) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst params = new URLSearchParams({ _limit: String(Number(source.limit) > 0 ? Number(source.limit) : 5), _order: JSON.stringify({ '@self.created': 'DESC' }) })\n\t\t\t\t\t\tfor (const [key, value] of Object.entries(source.filter || {})) {\n\t\t\t\t\t\t\tparams.set(key, value === '@objectId' ? this.objectId : String(value))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst objects = await read(`${this.apiBase}/objects/${source.register}/${source.schema}?${params.toString()}`)\n\t\t\t\t\t\tconst ids = objects.map((object) => object?.id || object?.['@self']?.id || object?.uuid).filter(Boolean)\n\t\t\t\t\t\tconst trails = await Promise.all(ids.map((id) => read(`${this.apiBase}/objects/${source.register}/${source.schema}/${id}/audit-trails?limit=${limit}`).catch(() => [])))\n\t\t\t\t\t\treturn trails.flat().map((entry) => ({ ...entry, relatedAs: source.as || source.schema }))\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn []\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\treturn perSource.flat()\n\t\t},\n\n\t\t/**\n\t\t * What happened: the action, with the state it led to when the entry\n\t\t * names one (a flow task's `stateAfter`).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatAction(entry) {\n\t\t\tconst label = this.labelFor(entry)\n\t\t\tif (label !== null) {\n\t\t\t\treturn label\n\t\t\t\t\t.replace('{actor}', this.actorName(entry))\n\t\t\t\t\t.replace('{state}', entry.stateAfter || '')\n\t\t\t\t\t.replace(/\\{changed\\.([\\w-]+)\\}/g, (match, key) => this.changedValue(entry, key))\n\t\t\t}\n\t\t\tconst action = entry.action || entry.event || this.actionLabel\n\t\t\treturn entry.stateAfter ? `${action} (${entry.stateAfter})` : action\n\t\t},\n\n\t\t/**\n\t\t * A value the entry recorded under `changed` (an audit entry's context),\n\t\t * in the words of `valueLabels`; '' when it recorded none.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {string} key The recorded key.\n\t\t * @return {string} The value in words.\n\t\t * @spec openspec/changes/audit-label-changed-values/specs/audit-trail/spec.md#requirement-a-trail-label-can-quote-what-the-entry-recorded\n\t\t */\n\t\tchangedValue(entry, key) {\n\t\t\tconst changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}\n\t\t\tconst value = changed[key]\n\t\t\tif (value === null || value === undefined || typeof value === 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst words = this.valueLabels && this.valueLabels[key]\n\t\t\treturn words && typeof words[value] === 'string' ? words[value] : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The `actionLabels` entry for an entry: by action and state first,\n\t\t * then by action.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string|null} The label, or null when none is declared.\n\t\t * @spec openspec/changes/audit-trail-action-labels/specs/audit-trail/spec.md#requirement-a-trail-can-say-what-happened-in-words\n\t\t */\n\t\tlabelFor(entry) {\n\t\t\tconst labels = this.actionLabels\n\t\t\tconst action = entry.action || entry.event\n\t\t\tif (!labels || !action) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst state = entry.stateAfter || ''\n\t\t\tconst prefix = entry.relatedAs ? `${entry.relatedAs}.` : ''\n\t\t\tconst label = (prefix ? (labels[`${prefix}${action}:${state}`] ?? labels[`${prefix}${action}`]) : undefined)\n\t\t\t\t?? labels[`${action}:${state}`] ?? labels[action]\n\t\t\treturn typeof label === 'string' && label !== '' ? label : null\n\t\t},\n\n\t\t/**\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} Who did it, by name where the entry carries one.\n\t\t */\n\t\tactorName(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\n\t\t},\n\n\t\t/**\n\t\t * Who did it: the actor, the kind of performer when it is not a\n\t\t * person (a flow, the system), and on whose behalf.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t */\n\t\tformatActor(entry) {\n\t\t\tconst named = (this.labelFor(entry) || '').includes('{actor}')\n\t\t\tconst parts = [named ? '' : this.actorName(entry)]\n\t\t\tif (entry.performerType && entry.performerType !== 'user') {\n\t\t\t\tparts.push(`(${entry.performerType})`)\n\t\t\t}\n\t\t\tif (entry.onBehalfOf) {\n\t\t\t\tparts.push(t('nextcloud-vue', 'on behalf of {name}', { name: entry.onBehalfOf }))\n\t\t\t}\n\t\t\treturn parts.filter((part) => part !== '').join(' ')\n\t\t},\n\n\t\tformatWhen(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tif (raw === undefined || raw === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst d = new Date(raw)\n\t\t\tif (Number.isNaN(d.getTime()) === true) {\n\t\t\t\treturn String(raw)\n\t\t\t}\n\t\t\treturn d.toLocaleString(userLocale())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-audit-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-audit-card__row {\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-audit-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-audit-card__row-head {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-audit-card__action {\n\tcolor: var(--color-main-text);\n}\n\n.cn-audit-card__when {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\twhite-space: nowrap;\n}\n\n.cn-audit-card__row-body {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-audit-card__refused {\n\tmargin-inline-start: 6px;\n\tfont-weight: 700;\n\tcolor: var(--color-error-text, var(--color-main-text));\n}\n\n.cn-audit-card__reason {\n\tmargin: 2px 0 0;\n\tfont-size: 0.9em;\n\toverflow-wrap: anywhere;\n}\n\n.cn-audit-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__footnote {\n\tmargin: 12px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_toDisplayString","_openBlock","_createElementVNode"],"mappings":";;;;OAgCQ,EAAC,sBAAqB;AAC1B,EAAA,IAAG,EAAE,OAAM;AACV,EAAA,aAAS,EAAC,sBAAsB;;;;AAGG,EAAA,KAAA,EAAA,sBAAA;;;;AAG3B,EAAA,KAAK,EAAC,qBAAmB;;AAK7B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAO,GAAA;AACf,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,uBAAA,GAAA;AACA,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,qBAAA,GAAA;AAEE,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,yBAAyB,GAAA;AAC7B,MAAA,UAAA,GAAA,EAAA,KAAK,EAAA,sBAAA,GAAA;;;AAGV,EAAA,KAAK,EAAC,wBAAwB;eACnB,EAAC,uBAAuB;;;;AAE7B,EAAA,KAAA,EAAA,uBAAA;;;;;;;;;;;;uBA/BVA;AACC,IAAA,KAAK,EAAE,QAAY,CAAA,aAAA;AACnB,IAAA,IAAI,EAAA,KAAA,CAAA,OAAA;AACJ,IAAA,WAAW,EAAE,MAAW,CAAA,WAAA;AACxB,IAAA,cAAE,EAAA,MAAA,CAAA,OAAA;;qBACD,MAA8B;OAAX,KAAO,CAAA,OAAA;wBAA1BA,WAA8B,CAAA,wBAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,CAAA;WAClB,KAAE,CAAA,MAAA;0BAAdC,kBAKE,CAAA,GAAA,EALF,UAKE,EAAAC,eAAA,CADA,MAAW,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;AAEC,YAAA,CAAA,QAAA,CAAA,YAAY,CAAC,MAAM,KAAA,CAAA;4BAAjCD,kBAEI,CAAA,KAAA,EAFJ,UAEI,EAAAC,eAAA,CADF,MAAc,CAAA,cAAA,CAAA,EAAA,CAAA,YAAA;AAEd,eAAAC,SAAA,EAAA,EAAAF,kBAAA,OAAA;gFAEc,QAAgB,CAAA,gBAAA,EAAA,CAAzB,KAAK,KAAA;;oBACX,GAAG,EAAC,KAAA,CAAA,EAAA;AACL,oBAAA,KAAK,EAAC,oBAAoB;;AACzB,oBAAAG,kBAAA,CAGC,OAHD,UAGC,EAAA;AAFD,sBAAAA,kBAAA,CAAE,QAAF,EAAA,UAAE,EAAAF,eAAA,CAAA,QAAA,CAAA,YAAA,CAAA,KAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AACF,sBAAAE,kBAAA,CAAE,MAAF,EAAA,UAAE,EAAAF,eAAA,CAAA,QAAA,CAAA,UAAA,CAAA,KAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;uCAQG,iBAAA,EAAA;AALN,sBAAAE,kBAAA,CAAY,MAAZ,EAAA,UAAY,EAAAF,eAAA,CAAA,QAAA,CAAA,WAAA,CAAA,KAAA,CAAA,CAAA,EAAA,CAAA,YAAA;AAEJ,sBAAA,CAAA,KAAK,CAAC,UAAU,KAAA,KAAA;wCADvBD,kBAG4C,CAAA,MAAA,EAH5C,UAG4C,EAAAC,eAAA,CAAJ,MAAI,CAAA,YAAA,CAAA,EAAA,CAAA,YAAA;;;AAErC,oBAAA,CAAA,KAAA,CAAA,MAAA;AAAR,yBAAAC,SAAA,EAAA,EAAAF,kBAAA,IAAA,EAAA,aACIC,eAAA,CAAA,KAAK,CAAC,MAAC,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -2,7 +2,7 @@ import script from './CnAuditTrailWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnAuditTrailWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-3f7bbf0c";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|