@conduction/nextcloud-vue 2.144.0 → 2.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +1 -0
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue3.js.map +1 -1
  7. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  8. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +55 -2
  9. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  10. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  11. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
  12. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +16 -0
  13. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
  14. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -1
  15. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +1 -1
  17. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -1
  18. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -1
  19. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +12 -2
  20. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  21. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +42 -30
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  23. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +22 -0
  24. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  25. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +1 -1
  28. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +1 -1
  29. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -1
  30. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +3 -3
  31. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -1
  32. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  33. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +46 -8
  34. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  35. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +3 -3
  36. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  37. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
  38. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +2 -2
  39. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  40. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  41. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +6 -3
  42. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  44. package/dist/esm/components/CnMailTab/CnMailTab.vue.js +1 -1
  45. package/dist/esm/components/CnMailTab/CnMailTab.vue2.js.map +1 -1
  46. package/dist/esm/components/CnMailTab/CnMailTab.vue3.js +2 -2
  47. package/dist/esm/components/CnMailTab/CnMailTab.vue3.js.map +1 -1
  48. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  49. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  50. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +1 -0
  51. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  52. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue.js +1 -1
  53. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js +21 -2
  54. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue2.js.map +1 -1
  55. package/dist/esm/components/CnNotificationMatrix/CnNotificationMatrix.vue3.js.map +1 -1
  56. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  57. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +73 -12
  58. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +165 -126
  60. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  62. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  64. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
  65. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +14 -0
  66. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
  67. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +2 -1
  68. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
  69. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  70. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  71. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  72. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  73. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -8
  74. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  75. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue.js +1 -1
  77. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +10 -2
  78. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  79. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  80. package/dist/esm/composables/indexSources.js +35 -9
  81. package/dist/esm/composables/indexSources.js.map +1 -1
  82. package/dist/esm/l10n/nl.json.js +2 -0
  83. package/dist/esm/l10n/nl.json.js.map +1 -1
  84. package/dist/esm/library-style.js +1 -1
  85. package/dist/esm/nextcloud-vue.css +1204 -467
  86. package/dist/esm/utils/builtInFormatters.js +18 -1
  87. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  88. package/dist/esm/utils/detailObjectContext.js +7 -2
  89. package/dist/esm/utils/detailObjectContext.js.map +1 -1
  90. package/dist/esm/utils/localHeaderRows.js +95 -0
  91. package/dist/esm/utils/localHeaderRows.js.map +1 -0
  92. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  93. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  94. package/dist/esm/utils/visibleWhen.js +6 -0
  95. package/dist/esm/utils/visibleWhen.js.map +1 -1
  96. package/dist/nextcloud-vue.cjs.js +686 -239
  97. package/dist/nextcloud-vue.cjs.js.map +2 -2
  98. package/dist/nextcloud-vue.css +1204 -467
  99. package/l10n/nl.json +2 -0
  100. package/package.json +1 -1
  101. package/src/components/CnActionDialog/CnActionDialog.vue +8 -1
  102. package/src/components/CnAiCompanion/CnAiChatPanel.vue +3 -0
  103. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +55 -2
  104. package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +18 -0
  105. package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +4 -2
  106. package/src/components/CnDataTable/CnDataTable.vue +19 -2
  107. package/src/components/CnDetailPage/CnDetailPage.md +1 -0
  108. package/src/components/CnDetailPage/CnDetailPage.vue +23 -0
  109. package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +3 -1
  110. package/src/components/CnFactsWidget/CnFactsWidget.vue +74 -9
  111. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +2 -2
  112. package/src/components/CnIndexPage/CnIndexPage.vue +6 -3
  113. package/src/components/CnMailTab/CnMailTab.vue +6 -4
  114. package/src/components/CnNotesCard/CnNotesCard.vue +7 -2
  115. package/src/components/CnNotificationMatrix/CnNotificationMatrix.vue +25 -3
  116. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +132 -20
  117. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +2 -0
  118. package/src/components/CnSettingsField/CnSettingsField.vue +15 -0
  119. package/src/components/CnTabsWidget/CnTabsWidget.vue +6 -1
  120. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -8
  121. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +10 -2
  122. package/src/composables/indexSources.js +35 -9
  123. package/src/css/dialog.css +25 -9
  124. package/src/css/look-board-detail.css +370 -30
  125. package/src/css/look-board-index.css +116 -4
  126. package/src/css/look-board.css +168 -8
  127. package/src/css/table.css +17 -1
  128. package/src/schemas/app-manifest-v2.schema.json +2 -2
  129. package/src/utils/builtInFormatters.js +17 -0
  130. package/src/utils/detailObjectContext.js +7 -2
  131. package/src/utils/localHeaderRows.js +91 -0
  132. package/src/utils/validateManifestV2.compiled.js +2 -2
  133. package/src/utils/visibleWhen.js +6 -0
@@ -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:class=\"{ 'cn-audit-card__row--stacked': layout === 'stacked' }\">\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 v-if=\"layout !== 'stacked'\" 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 v-if=\"layout === 'stacked'\" class=\"cn-audit-card__when\" data-testid=\"cn-audit-card-when-below\">{{ stackedLine(entry) }}</span>\n\t\t\t\t\t<span v-else 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 { formatBoardDate, formatTimeOfDay } from '../../utils/boardDate.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { userDisplayName } from '../../utils/userDisplayName.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 * `stacked` (DqTaak \"Historie\"): the action, then one muted line under\n\t\t * it with the moment (\"6 okt 09:05\") and who, when the action does not\n\t\t * already name them. Default: the moment beside the action, who under it.\n\t\t */\n\t\tlayout: { type: String, default: '' },\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\tif (entry.actorDisplayName) {\n\t\t\t\treturn entry.actorDisplayName\n\t\t\t}\n\t\t\tconst id = entry.actor || entry.userId || entry.user || ''\n\t\t\t// A person's user id reads as their display name once it is known.\n\t\t\treturn (id && (!entry.performerType || entry.performerType === 'user') && userDisplayName(String(id))) || id\n\t\t},\n\n\t\t/**\n\t\t * The muted line of a stacked row: the moment in the board's short\n\t\t * form (\"6 okt 09:05\") and who, unless the action names them already.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t * @spec openspec/changes/audit-card-stacked-rows/specs/audit-trail/spec.md#requirement-an-audit-row-can-put-its-moment-under-the-action\n\t\t */\n\t\tstackedLine(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tconst date = raw ? new Date(raw) : null\n\t\t\tconst when = date && !Number.isNaN(date.getTime()) ? `${formatBoardDate(date)} ${formatTimeOfDay(date)}` : ''\n\t\t\treturn [when, this.formatActor(entry)].filter((part) => part !== '').join(' · ')\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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"}
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:class=\"{ 'cn-audit-card__row--stacked': layout === 'stacked' }\">\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 v-if=\"layout !== 'stacked'\" 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 v-if=\"layout === 'stacked'\" class=\"cn-audit-card__when\" data-testid=\"cn-audit-card-when-below\">{{ stackedLine(entry) }}</span>\n\t\t\t\t\t<span v-else 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 { formatBoardDate, formatTimeOfDay } from '../../utils/boardDate.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { userDisplayName } from '../../utils/userDisplayName.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * A changed value as an entry records it: OpenRegister writes `{ old, new }`,\n * other sources the value itself.\n *\n * @param {unknown} value The recorded change.\n * @return {unknown} The value it changed to.\n */\nfunction newValueOf(value) {\n\treturn value !== null && typeof value === 'object' && Object.hasOwn(value, 'new') ? value.new : value\n}\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 * `stacked` (DqTaak \"Historie\"): the action, then one muted line under\n\t\t * it with the moment (\"6 okt 09:05\") and who, when the action does not\n\t\t * already name them. Default: the moment beside the action, who under it.\n\t\t */\n\t\tlayout: { type: String, default: '' },\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 * What happened, in words, by the field an entry changed: rules\n\t\t * `[{ action?, field, to?, label }]`, the first whose `field` the entry\n\t\t * changed (to `to`, when given) wins over `actionLabels`. A label fills\n\t\t * the same tokens, plus `{object.<path>}` from `object`\n\t\t * (\"Version {object.version} published\" for `isDraft` set to false).\n\t\t *\n\t\t * @type {Array<{action?: string, field: string, to?: unknown, label: string}>}\n\t\t */\n\t\tchangeLabels: { type: Array, default: () => [] },\n\t\t/** The audited record, for `{object.<path>}` in a label; null without one. */\n\t\tobject: { 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.changeLabelFor(entry) ?? 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\t\t.replace(/\\{object\\.([\\w.-]+)\\}/g, (match, path) => {\n\t\t\t\t\t\tconst value = this.object ? readPath(this.object, path) : undefined\n\t\t\t\t\t\treturn value === null || value === undefined || typeof value === 'object' ? '' : String(value)\n\t\t\t\t\t})\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 = newValueOf(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 `changeLabels` rule an entry matches: the first whose field the\n\t\t * entry changed, for its action and to its value when the rule names them.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string|null} The label, or null when no rule matches.\n\t\t */\n\t\tchangeLabelFor(entry) {\n\t\t\tconst changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}\n\t\t\tconst action = entry.action || entry.event || ''\n\t\t\tfor (const rule of Array.isArray(this.changeLabels) ? this.changeLabels : []) {\n\t\t\t\tif (!rule || typeof rule.label !== 'string' || rule.label === '' || typeof rule.field !== 'string' || !Object.hasOwn(changed, rule.field)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (rule.action && rule.action !== action) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (Object.hasOwn(rule, 'to') && String(newValueOf(changed[rule.field])) !== String(rule.to)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn rule.label\n\t\t\t}\n\t\t\treturn null\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\tif (entry.actorDisplayName) {\n\t\t\t\treturn entry.actorDisplayName\n\t\t\t}\n\t\t\tconst id = entry.actor || entry.userId || entry.user || ''\n\t\t\t// A person's user id reads as their display name once it is known.\n\t\t\treturn (id && (!entry.performerType || entry.performerType === 'user') && userDisplayName(String(id))) || id\n\t\t},\n\n\t\t/**\n\t\t * The muted line of a stacked row: the moment in the board's short\n\t\t * form (\"6 okt 09:05\") and who, unless the action names them already.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t * @spec openspec/changes/audit-card-stacked-rows/specs/audit-trail/spec.md#requirement-an-audit-row-can-put-its-moment-under-the-action\n\t\t */\n\t\tstackedLine(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tconst date = raw ? new Date(raw) : null\n\t\t\tconst when = date && !Number.isNaN(date.getTime()) ? `${formatBoardDate(date)} ${formatTimeOfDay(date)}` : ''\n\t\t\treturn [when, this.formatActor(entry)].filter((part) => part !== '').join(' · ')\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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnAuditTrailCard.vue3.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue?vue&type=template&id=401394d1&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:class=\"{ 'cn-audit-card__row--stacked': layout === 'stacked' }\">\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 v-if=\"layout !== 'stacked'\" 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 v-if=\"layout === 'stacked'\" class=\"cn-audit-card__when\" data-testid=\"cn-audit-card-when-below\">{{ stackedLine(entry) }}</span>\n\t\t\t\t\t<span v-else 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 { formatBoardDate, formatTimeOfDay } from '../../utils/boardDate.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { userDisplayName } from '../../utils/userDisplayName.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 * `stacked` (DqTaak \"Historie\"): the action, then one muted line under\n\t\t * it with the moment (\"6 okt 09:05\") and who, when the action does not\n\t\t * already name them. Default: the moment beside the action, who under it.\n\t\t */\n\t\tlayout: { type: String, default: '' },\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\tif (entry.actorDisplayName) {\n\t\t\t\treturn entry.actorDisplayName\n\t\t\t}\n\t\t\tconst id = entry.actor || entry.userId || entry.user || ''\n\t\t\t// A person's user id reads as their display name once it is known.\n\t\t\treturn (id && (!entry.performerType || entry.performerType === 'user') && userDisplayName(String(id))) || id\n\t\t},\n\n\t\t/**\n\t\t * The muted line of a stacked row: the moment in the board's short\n\t\t * form (\"6 okt 09:05\") and who, unless the action names them already.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t * @spec openspec/changes/audit-card-stacked-rows/specs/audit-trail/spec.md#requirement-an-audit-row-can-put-its-moment-under-the-action\n\t\t */\n\t\tstackedLine(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tconst date = raw ? new Date(raw) : null\n\t\t\tconst when = date && !Number.isNaN(date.getTime()) ? `${formatBoardDate(date)} ${formatTimeOfDay(date)}` : ''\n\t\t\treturn [when, this.formatActor(entry)].filter((part) => part !== '').join(' · ')\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","_normalizeClass","_createElementVNode"],"mappings":"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"}
1
+ {"version":3,"file":"CnAuditTrailCard.vue3.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue?vue&type=template&id=7e58faa4&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:class=\"{ 'cn-audit-card__row--stacked': layout === 'stacked' }\">\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 v-if=\"layout !== 'stacked'\" 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 v-if=\"layout === 'stacked'\" class=\"cn-audit-card__when\" data-testid=\"cn-audit-card-when-below\">{{ stackedLine(entry) }}</span>\n\t\t\t\t\t<span v-else 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 { formatBoardDate, formatTimeOfDay } from '../../utils/boardDate.js'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { leafSourceUrl } from '../../utils/leafSourceUrl.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { userDisplayName } from '../../utils/userDisplayName.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\n/**\n * A changed value as an entry records it: OpenRegister writes `{ old, new }`,\n * other sources the value itself.\n *\n * @param {unknown} value The recorded change.\n * @return {unknown} The value it changed to.\n */\nfunction newValueOf(value) {\n\treturn value !== null && typeof value === 'object' && Object.hasOwn(value, 'new') ? value.new : value\n}\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 * `stacked` (DqTaak \"Historie\"): the action, then one muted line under\n\t\t * it with the moment (\"6 okt 09:05\") and who, when the action does not\n\t\t * already name them. Default: the moment beside the action, who under it.\n\t\t */\n\t\tlayout: { type: String, default: '' },\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 * What happened, in words, by the field an entry changed: rules\n\t\t * `[{ action?, field, to?, label }]`, the first whose `field` the entry\n\t\t * changed (to `to`, when given) wins over `actionLabels`. A label fills\n\t\t * the same tokens, plus `{object.<path>}` from `object`\n\t\t * (\"Version {object.version} published\" for `isDraft` set to false).\n\t\t *\n\t\t * @type {Array<{action?: string, field: string, to?: unknown, label: string}>}\n\t\t */\n\t\tchangeLabels: { type: Array, default: () => [] },\n\t\t/** The audited record, for `{object.<path>}` in a label; null without one. */\n\t\tobject: { 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.changeLabelFor(entry) ?? 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\t\t.replace(/\\{object\\.([\\w.-]+)\\}/g, (match, path) => {\n\t\t\t\t\t\tconst value = this.object ? readPath(this.object, path) : undefined\n\t\t\t\t\t\treturn value === null || value === undefined || typeof value === 'object' ? '' : String(value)\n\t\t\t\t\t})\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 = newValueOf(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 `changeLabels` rule an entry matches: the first whose field the\n\t\t * entry changed, for its action and to its value when the rule names them.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string|null} The label, or null when no rule matches.\n\t\t */\n\t\tchangeLabelFor(entry) {\n\t\t\tconst changed = entry && typeof entry.changed === 'object' && entry.changed !== null ? entry.changed : {}\n\t\t\tconst action = entry.action || entry.event || ''\n\t\t\tfor (const rule of Array.isArray(this.changeLabels) ? this.changeLabels : []) {\n\t\t\t\tif (!rule || typeof rule.label !== 'string' || rule.label === '' || typeof rule.field !== 'string' || !Object.hasOwn(changed, rule.field)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (rule.action && rule.action !== action) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (Object.hasOwn(rule, 'to') && String(newValueOf(changed[rule.field])) !== String(rule.to)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn rule.label\n\t\t\t}\n\t\t\treturn null\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\tif (entry.actorDisplayName) {\n\t\t\t\treturn entry.actorDisplayName\n\t\t\t}\n\t\t\tconst id = entry.actor || entry.userId || entry.user || ''\n\t\t\t// A person's user id reads as their display name once it is known.\n\t\t\treturn (id && (!entry.performerType || entry.performerType === 'user') && userDisplayName(String(id))) || id\n\t\t},\n\n\t\t/**\n\t\t * The muted line of a stacked row: the moment in the board's short\n\t\t * form (\"6 okt 09:05\") and who, unless the action names them already.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {string} The line.\n\t\t * @spec openspec/changes/audit-card-stacked-rows/specs/audit-trail/spec.md#requirement-an-audit-row-can-put-its-moment-under-the-action\n\t\t */\n\t\tstackedLine(entry) {\n\t\t\tconst raw = entry.creationDateTime || entry.created || entry.timestamp\n\t\t\tconst date = raw ? new Date(raw) : null\n\t\t\tconst when = date && !Number.isNaN(date.getTime()) ? `${formatBoardDate(date)} ${formatTimeOfDay(date)}` : ''\n\t\t\treturn [when, this.formatActor(entry)].filter((part) => part !== '').join(' · ')\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","_normalizeClass","_createElementVNode"],"mappings":"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"}
@@ -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-6387051e";
5
+ script.__scopeId = "data-v-87ba40b4";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -164,6 +164,22 @@ var script = {
164
164
  .map(([key, text]) => [key, translate(text)]))
165
165
  },
166
166
 
167
+ /**
168
+ * `content.changeLabels`, each rule's label through the app's translate.
169
+ *
170
+ * @return {Array<object>}
171
+ */
172
+ changeLabels() {
173
+ const rules = this.content.changeLabels;
174
+ if (!Array.isArray(rules)) {
175
+ return []
176
+ }
177
+ const translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text;
178
+ return rules
179
+ .filter((rule) => rule && typeof rule.label === 'string' && rule.label !== '')
180
+ .map((rule) => ({ ...rule, label: translate(rule.label) }))
181
+ },
182
+
167
183
  /**
168
184
  * `content.footnote`, a muted line under the trail with the record's
169
185
  * fields filled in ("Reference {identifier}"); '' when a field is empty.
@@ -1 +1 @@
1
- {"version":3,"file":"CnAuditTrailWidget.vue2.js","sources":["../../../../src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tv-if=\"resolvedScope === 'app' || resolvedSource || resolvedEndpoint || (resolvedRegister && resolvedSchema && resolvedObjectId)\"\n\t\tclass=\"cn-audit-trail-widget\">\n\t\t<CnAuditTrailCard\n\t\t\t:source=\"resolvedSource\"\n\t\t\t:endpoint=\"resolvedEndpoint\"\n\t\t\t:register=\"resolvedRegister\"\n\t\t\t:schema=\"resolvedSchema\"\n\t\t\t:objectId=\"resolvedObjectId\"\n\t\t\t:scope=\"resolvedScope\"\n\t\t\t:title=\"resolvedTitle\"\n\t\t\t:maxDisplay=\"resolvedMaxDisplay\"\n\t\t\t:layout=\"content && typeof content.layout === 'string' ? content.layout : ''\"\n\t\t\t:excludeActions=\"resolvedExcludeActions\"\n\t\t\t:actionLabels=\"actionLabels\"\n\t\t\t:valueLabels=\"valueLabels\"\n\t\t\t:relatedSources=\"relatedSources\"\n\t\t\t:footnote=\"footnote\"\n\t\t\tv-bind=\"emptyText ? { noEntriesLabel: emptyText } : {}\" />\n\t</div>\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\n\n/**\n * CnAuditTrailWidget — thin built-in widget wrapping CnAuditTrailCard with\n * unified object-context resolution (`audit-trail` widget key, Wave 1 of\n * nextcloud-vue#91).\n *\n * CnAuditTrailCard needs explicit `register` / `schema` / `objectId` props,\n * but the detail render paths surface the current object differently:\n * CnDetailPage's config grid provides a `cnObjectContext` inject, while\n * CnPageRenderer's slot CnWidgetGrid spreads the detail object context as\n * props, and CnDashboardPage's registry branch hands a stored `content`\n * blob. This widget reads whichever is present — explicit props win, then\n * the injects, then `content` — so the single `audit-trail` key works on\n * every surface. It replaces the three identical per-app adapters\n * (procest / zaakafhandelapp / scholiq).\n *\n * Registered in BOTH the v2 grid's BUILT_IN_WIDGETS and (detail-page\n * surface, like the `data` widget) the shared dashboardWidgetRegistry —\n * see `dashboardRegistration.js`.\n */\nexport default {\n\tname: 'CnAuditTrailWidget',\n\n\tcomponents: { CnAuditTrailCard },\n\n\tinject: {\n\t\t/** The host app's translate function, for `content.emptyText`. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/**\n\t\t * Detail-page object context (`{ objectId, object, register, schema }`)\n\t\t * provided by CnDetailPage (raw object or `{ value }` holder).\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * Detail object context holder (`{ value: { objectData, schema,\n\t\t * objectType, objectId, register, store } | null }`) provided by\n\t\t * CnPageRenderer for the v2 slot grid.\n\t\t */\n\t\tcnDetailObjectContext: { default: null },\n\t},\n\n\tprops: {\n\t\t/** OpenRegister register slug/id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tregister: { type: String, default: '' },\n\t\t/**\n\t\t * OpenRegister schema — a slug string, or the schema OBJECT the\n\t\t * detail-context merge supplies (its slug/name/id is used).\n\t\t *\n\t\t * @type {string|object}\n\t\t */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The audited object's id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Optional card title override (defaults to the card's translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Maximum audit rows to render (0 falls back to the card default). */\n\t\tmaxDisplay: { type: Number, default: 0 },\n\t\t/**\n\t\t * `app` lists the app-wide feed (every object the caller may read,\n\t\t * narrowed to `register` / `schema` when set) and needs no object;\n\t\t * `object` (the default) is one object's trail. Also read from\n\t\t * `content.scope`.\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: { type: String, default: '' },\n\t\t/** Read the trail of something that is not an object (`{ kind: 'flow-task', id? }`); also read from `content.source`. */\n\t\tsource: { type: Object, default: null },\n\t\t/** A trail URL of its own; also read from `content.endpoint`. */\n\t\tendpoint: { type: String, default: '' },\n\t\t/** Actions to leave out (`['read']`); also read from `content.excludeActions`. */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, footnote? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels?, relatedSources? }`. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The resolved object-context bag from either inject shape, or {}.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tctx() {\n\t\t\tconst inj = this.cnObjectContext && (this.cnObjectContext.value || this.cnObjectContext)\n\t\t\tconst holder = this.cnDetailObjectContext && this.cnDetailObjectContext.value\n\t\t\treturn inj || holder || {}\n\t\t},\n\n\t\t/**\n\t\t * The audited object's id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedObjectId() {\n\t\t\treturn this.objectId || this.ctx.objectId || this.content.objectId || ''\n\t\t},\n\n\t\t/**\n\t\t * The register slug/id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedRegister() {\n\t\t\treturn this.register || this.ctx.register || this.content.register || ''\n\t\t},\n\n\t\t/**\n\t\t * The schema SLUG: explicit prop → inject context → content; a\n\t\t * schema object collapses to its slug/name/id.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSchema() {\n\t\t\tconst s = this.schema || this.ctx.schema || this.content.schema || ''\n\t\t\treturn typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''\n\t\t},\n\n\t\t/**\n\t\t * The feed scope: explicit prop → content → `object`.\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 {('object'|'app')}\n\t\t */\n\t\tresolvedScope() {\n\t\t\tconst s = this.scope || this.content.scope\n\t\t\treturn s === 'app' ? 'app' : 'object'\n\t\t},\n\n\t\t/**\n\t\t * The card title: explicit prop → content → '' (card default).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\t/** @return {object|null} The non-object anchor, from the prop or the content. */\n\t\tresolvedSource() {\n\t\t\tconst source = this.source || this.content.source\n\t\t\treturn source && typeof source === 'object' ? source : null\n\t\t},\n\n\t\t/** @return {string} The trail URL of its own, from the prop or the content. */\n\t\tresolvedEndpoint() {\n\t\t\treturn this.endpoint || (typeof this.content.endpoint === 'string' ? this.content.endpoint : '')\n\t\t},\n\n\t\t/** @return {string} The empty line the content names, through the host's translate, or ''. */\n\t\t/**\n\t\t * `content.actionLabels`, each label through the app's translate.\n\t\t *\n\t\t * @return {Object<string, string>|null}\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\tactionLabels() {\n\t\t\tconst labels = this.content.actionLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t.map(([key, text]) => [key, translate(text)]))\n\t\t},\n\n\t\t/**\n\t\t * `content.footnote`, a muted line under the trail with the record's\n\t\t * fields filled in (\"Reference {identifier}\"); '' when a field is empty.\n\t\t *\n\t\t * @return {string}\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() {\n\t\t\tconst note = this.content.footnote\n\t\t\tif (typeof note !== 'string' || note === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn fillTemplate(translate(note), this.ctx.object || this.ctx.objectData || null)\n\t\t},\n\n\t\t/**\n\t\t * `content.valueLabels`, each word through the app's translate.\n\t\t *\n\t\t * @return {{[key: string]: {[value: string]: string}}|null}\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\tvalueLabels() {\n\t\t\tconst labels = this.content.valueLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, words]) => words && typeof words === 'object')\n\t\t\t\t.map(([key, words]) => [key, Object.fromEntries(Object.entries(words)\n\t\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t\t.map(([value, text]) => [value, translate(text)]))]))\n\t\t},\n\n\t\t/**\n\t\t * `content.relatedSources`: records beside the object whose events\n\t\t * join its history (see CnAuditTrailCard).\n\t\t *\n\t\t * @return {Array<object>}\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() {\n\t\t\treturn Array.isArray(this.content.relatedSources) ? this.content.relatedSources : []\n\t\t},\n\n\t\temptyText() {\n\t\t\tconst text = this.content.emptyText\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The actions to leave out: explicit prop → content → none.\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 * @return {Array<string>}\n\t\t */\n\t\tresolvedExcludeActions() {\n\t\t\tif (this.excludeActions.length > 0) {\n\t\t\t\treturn this.excludeActions\n\t\t\t}\n\t\t\tconst fromContent = this.content.excludeActions\n\t\t\treturn Array.isArray(fromContent) ? fromContent.filter((action) => typeof action === 'string' && action !== '') : []\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || this.content.title || ''\n\t\t},\n\n\t\t/**\n\t\t * The row cap: explicit prop → content → the card's own default (5).\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tresolvedMaxDisplay() {\n\t\t\tif (this.maxDisplay > 0) {\n\t\t\t\treturn this.maxDisplay\n\t\t\t}\n\t\t\tconst fromContent = Number(this.content.maxDisplay)\n\t\t\treturn Number.isFinite(fromContent) && fromContent > 0 ? fromContent : 5\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* The card keeps its own box, footnote included. */\n.cn-audit-trail-widget {\n\tdisplay: contents;\n}\n</style>\n"],"names":["CnAuditTrailCard"],"mappings":"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"}
1
+ {"version":3,"file":"CnAuditTrailWidget.vue2.js","sources":["../../../../src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tv-if=\"resolvedScope === 'app' || resolvedSource || resolvedEndpoint || (resolvedRegister && resolvedSchema && resolvedObjectId)\"\n\t\tclass=\"cn-audit-trail-widget\">\n\t\t<CnAuditTrailCard\n\t\t\t:source=\"resolvedSource\"\n\t\t\t:endpoint=\"resolvedEndpoint\"\n\t\t\t:register=\"resolvedRegister\"\n\t\t\t:schema=\"resolvedSchema\"\n\t\t\t:objectId=\"resolvedObjectId\"\n\t\t\t:scope=\"resolvedScope\"\n\t\t\t:title=\"resolvedTitle\"\n\t\t\t:maxDisplay=\"resolvedMaxDisplay\"\n\t\t\t:layout=\"content && typeof content.layout === 'string' ? content.layout : ''\"\n\t\t\t:excludeActions=\"resolvedExcludeActions\"\n\t\t\t:actionLabels=\"actionLabels\"\n\t\t\t:valueLabels=\"valueLabels\"\n\t\t\t:changeLabels=\"changeLabels\"\n\t\t\t:object=\"ctx.object || ctx.objectData || null\"\n\t\t\t:relatedSources=\"relatedSources\"\n\t\t\t:footnote=\"footnote\"\n\t\t\tv-bind=\"emptyText ? { noEntriesLabel: emptyText } : {}\" />\n\t</div>\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\n\n/**\n * CnAuditTrailWidget — thin built-in widget wrapping CnAuditTrailCard with\n * unified object-context resolution (`audit-trail` widget key, Wave 1 of\n * nextcloud-vue#91).\n *\n * CnAuditTrailCard needs explicit `register` / `schema` / `objectId` props,\n * but the detail render paths surface the current object differently:\n * CnDetailPage's config grid provides a `cnObjectContext` inject, while\n * CnPageRenderer's slot CnWidgetGrid spreads the detail object context as\n * props, and CnDashboardPage's registry branch hands a stored `content`\n * blob. This widget reads whichever is present — explicit props win, then\n * the injects, then `content` — so the single `audit-trail` key works on\n * every surface. It replaces the three identical per-app adapters\n * (procest / zaakafhandelapp / scholiq).\n *\n * Registered in BOTH the v2 grid's BUILT_IN_WIDGETS and (detail-page\n * surface, like the `data` widget) the shared dashboardWidgetRegistry —\n * see `dashboardRegistration.js`.\n */\nexport default {\n\tname: 'CnAuditTrailWidget',\n\n\tcomponents: { CnAuditTrailCard },\n\n\tinject: {\n\t\t/** The host app's translate function, for `content.emptyText`. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/**\n\t\t * Detail-page object context (`{ objectId, object, register, schema }`)\n\t\t * provided by CnDetailPage (raw object or `{ value }` holder).\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * Detail object context holder (`{ value: { objectData, schema,\n\t\t * objectType, objectId, register, store } | null }`) provided by\n\t\t * CnPageRenderer for the v2 slot grid.\n\t\t */\n\t\tcnDetailObjectContext: { default: null },\n\t},\n\n\tprops: {\n\t\t/** OpenRegister register slug/id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tregister: { type: String, default: '' },\n\t\t/**\n\t\t * OpenRegister schema — a slug string, or the schema OBJECT the\n\t\t * detail-context merge supplies (its slug/name/id is used).\n\t\t *\n\t\t * @type {string|object}\n\t\t */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The audited object's id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Optional card title override (defaults to the card's translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Maximum audit rows to render (0 falls back to the card default). */\n\t\tmaxDisplay: { type: Number, default: 0 },\n\t\t/**\n\t\t * `app` lists the app-wide feed (every object the caller may read,\n\t\t * narrowed to `register` / `schema` when set) and needs no object;\n\t\t * `object` (the default) is one object's trail. Also read from\n\t\t * `content.scope`.\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: { type: String, default: '' },\n\t\t/** Read the trail of something that is not an object (`{ kind: 'flow-task', id? }`); also read from `content.source`. */\n\t\tsource: { type: Object, default: null },\n\t\t/** A trail URL of its own; also read from `content.endpoint`. */\n\t\tendpoint: { type: String, default: '' },\n\t\t/** Actions to leave out (`['read']`); also read from `content.excludeActions`. */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, footnote? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels?, relatedSources? }`. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The resolved object-context bag from either inject shape, or {}.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tctx() {\n\t\t\tconst inj = this.cnObjectContext && (this.cnObjectContext.value || this.cnObjectContext)\n\t\t\tconst holder = this.cnDetailObjectContext && this.cnDetailObjectContext.value\n\t\t\treturn inj || holder || {}\n\t\t},\n\n\t\t/**\n\t\t * The audited object's id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedObjectId() {\n\t\t\treturn this.objectId || this.ctx.objectId || this.content.objectId || ''\n\t\t},\n\n\t\t/**\n\t\t * The register slug/id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedRegister() {\n\t\t\treturn this.register || this.ctx.register || this.content.register || ''\n\t\t},\n\n\t\t/**\n\t\t * The schema SLUG: explicit prop → inject context → content; a\n\t\t * schema object collapses to its slug/name/id.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSchema() {\n\t\t\tconst s = this.schema || this.ctx.schema || this.content.schema || ''\n\t\t\treturn typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''\n\t\t},\n\n\t\t/**\n\t\t * The feed scope: explicit prop → content → `object`.\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 {('object'|'app')}\n\t\t */\n\t\tresolvedScope() {\n\t\t\tconst s = this.scope || this.content.scope\n\t\t\treturn s === 'app' ? 'app' : 'object'\n\t\t},\n\n\t\t/**\n\t\t * The card title: explicit prop → content → '' (card default).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\t/** @return {object|null} The non-object anchor, from the prop or the content. */\n\t\tresolvedSource() {\n\t\t\tconst source = this.source || this.content.source\n\t\t\treturn source && typeof source === 'object' ? source : null\n\t\t},\n\n\t\t/** @return {string} The trail URL of its own, from the prop or the content. */\n\t\tresolvedEndpoint() {\n\t\t\treturn this.endpoint || (typeof this.content.endpoint === 'string' ? this.content.endpoint : '')\n\t\t},\n\n\t\t/** @return {string} The empty line the content names, through the host's translate, or ''. */\n\t\t/**\n\t\t * `content.actionLabels`, each label through the app's translate.\n\t\t *\n\t\t * @return {Object<string, string>|null}\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\tactionLabels() {\n\t\t\tconst labels = this.content.actionLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t.map(([key, text]) => [key, translate(text)]))\n\t\t},\n\n\t\t/**\n\t\t * `content.changeLabels`, each rule's label through the app's translate.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tchangeLabels() {\n\t\t\tconst rules = this.content.changeLabels\n\t\t\tif (!Array.isArray(rules)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn rules\n\t\t\t\t.filter((rule) => rule && typeof rule.label === 'string' && rule.label !== '')\n\t\t\t\t.map((rule) => ({ ...rule, label: translate(rule.label) }))\n\t\t},\n\n\t\t/**\n\t\t * `content.footnote`, a muted line under the trail with the record's\n\t\t * fields filled in (\"Reference {identifier}\"); '' when a field is empty.\n\t\t *\n\t\t * @return {string}\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() {\n\t\t\tconst note = this.content.footnote\n\t\t\tif (typeof note !== 'string' || note === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn fillTemplate(translate(note), this.ctx.object || this.ctx.objectData || null)\n\t\t},\n\n\t\t/**\n\t\t * `content.valueLabels`, each word through the app's translate.\n\t\t *\n\t\t * @return {{[key: string]: {[value: string]: string}}|null}\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\tvalueLabels() {\n\t\t\tconst labels = this.content.valueLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, words]) => words && typeof words === 'object')\n\t\t\t\t.map(([key, words]) => [key, Object.fromEntries(Object.entries(words)\n\t\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t\t.map(([value, text]) => [value, translate(text)]))]))\n\t\t},\n\n\t\t/**\n\t\t * `content.relatedSources`: records beside the object whose events\n\t\t * join its history (see CnAuditTrailCard).\n\t\t *\n\t\t * @return {Array<object>}\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() {\n\t\t\treturn Array.isArray(this.content.relatedSources) ? this.content.relatedSources : []\n\t\t},\n\n\t\temptyText() {\n\t\t\tconst text = this.content.emptyText\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The actions to leave out: explicit prop → content → none.\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 * @return {Array<string>}\n\t\t */\n\t\tresolvedExcludeActions() {\n\t\t\tif (this.excludeActions.length > 0) {\n\t\t\t\treturn this.excludeActions\n\t\t\t}\n\t\t\tconst fromContent = this.content.excludeActions\n\t\t\treturn Array.isArray(fromContent) ? fromContent.filter((action) => typeof action === 'string' && action !== '') : []\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || this.content.title || ''\n\t\t},\n\n\t\t/**\n\t\t * The row cap: explicit prop → content → the card's own default (5).\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tresolvedMaxDisplay() {\n\t\t\tif (this.maxDisplay > 0) {\n\t\t\t\treturn this.maxDisplay\n\t\t\t}\n\t\t\tconst fromContent = Number(this.content.maxDisplay)\n\t\t\treturn Number.isFinite(fromContent) && fromContent > 0 ? fromContent : 5\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* The card keeps its own box, footnote included. */\n.cn-audit-trail-widget {\n\tdisplay: contents;\n}\n</style>\n"],"names":["CnAuditTrailCard"],"mappings":";;;AAkCA;AACC;AACA;AACA;AACA;AACA;;AAEA;AACA;;;;;AAKA;AACA;;;AAGA;;AAED,aAAe;CACd,IAAI,EAAE,oBAAoB;;CAE1B,UAAU,EAAE,oBAAEA,UAAe,EAAG;;AAEhC,CAAA,MAAM,EAAE;;AAEP,EAAA,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,KAAK,KAAK;AAC5C;;;;AAIA,EAAA,eAAe,EAAE,EAAE,OAAO,EAAE,IAAG,EAAG;AAClC;;;AAGC;;AAED,EAAA,qBAAqB,EAAE,EAAE,OAAO,EAAE,MAAM;EACxC;;AAED,CAAA,KAAK,EAAE;AACN;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;;;AAGC;AACA;;AAED,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI;AAC/C;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;AACA,EAAA,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAC,EAAG;;AAEpC,EAAA,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;AACxC;;;;AAIC;AACA;AACA;;AAED,EAAA,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAC,EAAG;;AAEpC,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;AAEvC,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;AACA,EAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI;AAClD;AACA;;AAEA,EAAA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG;EAC9C;;AAED,CAAA,QAAQ,EAAE;AACT;;AAEC;AACA;;EAED,GAAG,GAAG;AACL,GAAA,MAAM,GAAI,GAAE,IAAI,CAAC,eAAc,KAAM,IAAI,CAAC,eAAe,CAAC,SAAS,IAAI,CAAC,eAAe,EAAA;AACvF,GAAA,MAAM,MAAO,GAAE,IAAI,CAAC,qBAAsB,IAAG,IAAI,CAAC,qBAAqB,CAAC,MAAI;AAC5E,GAAA,OAAO,GAAI,IAAG,MAAO,IAAG,EAAC;GACzB;;AAED;;AAEC;AACA;;AAED,EAAA,gBAAgB,GAAG;AAClB,GAAA,OAAO,IAAI,CAAC,QAAO,IAAK,IAAI,CAAC,GAAG,CAAC,QAAO,IAAK,IAAI,CAAC,OAAO,CAAC,QAAS,IAAG,EAAC;GACvE;;AAED;;AAEC;AACA;;AAED,EAAA,gBAAgB,GAAG;AAClB,GAAA,OAAO,IAAI,CAAC,QAAO,IAAK,IAAI,CAAC,GAAG,CAAC,QAAO,IAAK,IAAI,CAAC,OAAO,CAAC,QAAS,IAAG,EAAC;GACvE;;AAED;;AAEC;AACA;AACA;;AAED,EAAA,cAAc,GAAG;GAChB,MAAM,CAAA,GAAI,IAAI,CAAC,MAAO,IAAG,IAAI,CAAC,GAAG,CAAC,MAAO,IAAG,IAAI,CAAC,OAAO,CAAC,MAAK,IAAK,GAAC;GACpE,OAAO,OAAO,CAAA,KAAM,QAAO,GAAI,CAAA,GAAI,CAAC,MAAM,CAAC,CAAC,IAAK,IAAG,CAAC,CAAC,IAAG,IAAK,CAAC,CAAC,EAAE,CAAC,KAAK,EAAC;GACzE;;AAED;AACC;AACA;AACA;;;AAGD,EAAA,aAAa,GAAG;AACf,GAAA,MAAM,IAAI,IAAI,CAAC,KAAM,IAAG,IAAI,CAAC,OAAO,CAAC,MAAI;GACzC,OAAO,CAAE,KAAI,KAAM,GAAE,KAAM,GAAE,QAAO;GACpC;;AAED;;AAEC;AACA;;AAED;AACA,EAAA,cAAc,GAAG;AAChB,GAAA,MAAM,MAAO,GAAE,IAAI,CAAC,MAAK,IAAK,IAAI,CAAC,OAAO,CAAC,OAAK;GAChD,OAAO,UAAU,OAAO,MAAO,KAAI,WAAW,MAAO,GAAE,IAAG;GAC1D;;AAED;AACA,EAAA,gBAAgB,GAAG;AAClB,GAAA,OAAO,IAAI,CAAC,QAAS,KAAI,OAAO,IAAI,CAAC,OAAO,CAAC,QAAS,KAAI,QAAS,GAAE,IAAI,CAAC,OAAO,CAAC,QAAS,GAAE,EAAE,CAAA;GAC/F;;AAED;AACA;;AAEC;AACA;;;AAGD,EAAA,YAAY,GAAG;AACd,GAAA,MAAM,SAAS,IAAI,CAAC,OAAO,CAAC,aAAW;AACvC,GAAA,IAAI,CAAC,MAAK,IAAK,OAAO,MAAK,KAAM,QAAQ,EAAE;AAC1C,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,SAAQ,GAAI,OAAO,IAAI,CAAC,gBAAgB,UAAW,GAAE,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,KAAG;GAC3F,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAA;KAC7C,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,OAAO,SAAS,QAAS,IAAG,SAAS,EAAE,CAAA;AAC7D,KAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;GAC9C;;AAED;AACC;AACA;;;AAGD,EAAA,YAAY,GAAG;AACd,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,OAAO,CAAC,aAAW;GACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AAC1B,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,SAAQ,GAAI,OAAO,IAAI,CAAC,gBAAgB,UAAW,GAAE,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,KAAG;GAC3F,OAAO,KAAI;AACV,KAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAG,IAAK,OAAO,IAAI,CAAC,KAAM,KAAI,QAAS,IAAG,IAAI,CAAC,KAAM,KAAI,EAAE,CAAA;KAC5E,GAAG,CAAC,CAAC,IAAI,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAE,EAAC,CAAC,CAAA;GAC3D;;AAED;;;AAGC;AACA;AACA;;EAED,QAAQ,GAAG;AACV,GAAA,MAAM,IAAG,GAAI,IAAI,CAAC,OAAO,CAAC,SAAO;AACjC,GAAA,IAAI,OAAO,IAAG,KAAM,QAAO,IAAK,SAAS,EAAE,EAAE;AAC5C,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,SAAQ,GAAI,OAAO,IAAI,CAAC,gBAAgB,UAAW,GAAE,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,KAAG;AAC3F,GAAA,OAAO,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAK,IAAK,IAAI,CAAC,GAAG,CAAC,cAAc,IAAI,CAAA;GACnF;;AAED;;AAEC;;AAEA;;EAED,WAAW,GAAG;AACb,GAAA,MAAM,MAAO,GAAE,IAAI,CAAC,OAAO,CAAC,YAAU;AACtC,GAAA,IAAI,CAAC,MAAK,IAAK,OAAO,MAAK,KAAM,QAAQ,EAAE;AAC1C,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,SAAQ,GAAI,OAAO,IAAI,CAAC,gBAAgB,UAAW,GAAE,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,KAAG;GAC3F,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAA;KAC7C,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,SAAS,OAAO,UAAU,QAAQ,CAAA;AACzD,KAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAA;MAClE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,OAAO,SAAS,QAAS,IAAG,SAAS,EAAE,CAAA;MAC5D,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;GACtD;;AAED;;AAEC;AACA;;AAEA;;AAED,EAAA,cAAc,GAAG;AAChB,GAAA,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAE,GAAE,IAAI,CAAC,OAAO,CAAC,cAAa,GAAI,EAAC;GACnF;;EAED,SAAS,GAAG;AACX,GAAA,MAAM,IAAG,GAAI,IAAI,CAAC,OAAO,CAAC,UAAQ;AAClC,GAAA,IAAI,OAAO,IAAG,KAAM,QAAO,IAAK,SAAS,EAAE,EAAE;AAC5C,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,OAAO,OAAO,IAAI,CAAC,WAAY,KAAI,UAAW,GAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA,GAAI,IAAG;GAC5E;;AAED;;AAEC;AACA;;;EAGD,sBAAsB,GAAG;AACxB,GAAA,IAAI,IAAI,CAAC,cAAc,CAAC,MAAO,GAAE,CAAC,EAAE;IACnC,OAAO,IAAI,CAAC,cAAa;AAC1B,IAAA;AACA,GAAA,MAAM,cAAc,IAAI,CAAC,OAAO,CAAC,eAAa;GAC9C,OAAO,KAAK,CAAC,OAAO,CAAC,WAAW,CAAE,GAAE,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,OAAO,MAAK,KAAM,QAAO,IAAK,WAAW,EAAE,CAAE,GAAE,EAAC;GACnH;;AAED,EAAA,aAAa,GAAG;AACf,GAAA,OAAO,IAAI,CAAC,KAAI,IAAK,IAAI,CAAC,OAAO,CAAC,KAAM,IAAG,EAAC;GAC5C;;AAED;;AAEC;AACA;;AAED,EAAA,kBAAkB,GAAG;AACpB,GAAA,IAAI,IAAI,CAAC,UAAW,GAAE,CAAC,EAAE;AACxB,IAAA,OAAO,IAAI,CAAC,UAAS;AACtB,IAAA;GACA,MAAM,WAAY,GAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAA;AAClD,GAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAE,IAAG,WAAU,GAAI,CAAA,GAAI,WAAU,GAAI,CAAA;GACvE;EACD;AACF;;;;"}
@@ -23,9 +23,11 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
23
23
  excludeActions: $options.resolvedExcludeActions,
24
24
  actionLabels: $options.actionLabels,
25
25
  valueLabels: $options.valueLabels,
26
+ changeLabels: $options.changeLabels,
27
+ object: $options.ctx.object || $options.ctx.objectData || null,
26
28
  relatedSources: $options.relatedSources,
27
29
  footnote: $options.footnote
28
- }, $options.emptyText ? { noEntriesLabel: $options.emptyText } : {}), null, 16 /* FULL_PROPS */, ["source", "endpoint", "register", "schema", "objectId", "scope", "title", "maxDisplay", "layout", "excludeActions", "actionLabels", "valueLabels", "relatedSources", "footnote"])
30
+ }, $options.emptyText ? { noEntriesLabel: $options.emptyText } : {}), null, 16 /* FULL_PROPS */, ["source", "endpoint", "register", "schema", "objectId", "scope", "title", "maxDisplay", "layout", "excludeActions", "actionLabels", "valueLabels", "changeLabels", "object", "relatedSources", "footnote"])
29
31
  ]))
30
32
  : createCommentVNode("v-if", true)
31
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CnAuditTrailWidget.vue3.js","sources":["../../../../src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue?vue&type=template&id=6387051e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tv-if=\"resolvedScope === 'app' || resolvedSource || resolvedEndpoint || (resolvedRegister && resolvedSchema && resolvedObjectId)\"\n\t\tclass=\"cn-audit-trail-widget\">\n\t\t<CnAuditTrailCard\n\t\t\t:source=\"resolvedSource\"\n\t\t\t:endpoint=\"resolvedEndpoint\"\n\t\t\t:register=\"resolvedRegister\"\n\t\t\t:schema=\"resolvedSchema\"\n\t\t\t:objectId=\"resolvedObjectId\"\n\t\t\t:scope=\"resolvedScope\"\n\t\t\t:title=\"resolvedTitle\"\n\t\t\t:maxDisplay=\"resolvedMaxDisplay\"\n\t\t\t:layout=\"content && typeof content.layout === 'string' ? content.layout : ''\"\n\t\t\t:excludeActions=\"resolvedExcludeActions\"\n\t\t\t:actionLabels=\"actionLabels\"\n\t\t\t:valueLabels=\"valueLabels\"\n\t\t\t:relatedSources=\"relatedSources\"\n\t\t\t:footnote=\"footnote\"\n\t\t\tv-bind=\"emptyText ? { noEntriesLabel: emptyText } : {}\" />\n\t</div>\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\n\n/**\n * CnAuditTrailWidget — thin built-in widget wrapping CnAuditTrailCard with\n * unified object-context resolution (`audit-trail` widget key, Wave 1 of\n * nextcloud-vue#91).\n *\n * CnAuditTrailCard needs explicit `register` / `schema` / `objectId` props,\n * but the detail render paths surface the current object differently:\n * CnDetailPage's config grid provides a `cnObjectContext` inject, while\n * CnPageRenderer's slot CnWidgetGrid spreads the detail object context as\n * props, and CnDashboardPage's registry branch hands a stored `content`\n * blob. This widget reads whichever is present — explicit props win, then\n * the injects, then `content` — so the single `audit-trail` key works on\n * every surface. It replaces the three identical per-app adapters\n * (procest / zaakafhandelapp / scholiq).\n *\n * Registered in BOTH the v2 grid's BUILT_IN_WIDGETS and (detail-page\n * surface, like the `data` widget) the shared dashboardWidgetRegistry —\n * see `dashboardRegistration.js`.\n */\nexport default {\n\tname: 'CnAuditTrailWidget',\n\n\tcomponents: { CnAuditTrailCard },\n\n\tinject: {\n\t\t/** The host app's translate function, for `content.emptyText`. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/**\n\t\t * Detail-page object context (`{ objectId, object, register, schema }`)\n\t\t * provided by CnDetailPage (raw object or `{ value }` holder).\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * Detail object context holder (`{ value: { objectData, schema,\n\t\t * objectType, objectId, register, store } | null }`) provided by\n\t\t * CnPageRenderer for the v2 slot grid.\n\t\t */\n\t\tcnDetailObjectContext: { default: null },\n\t},\n\n\tprops: {\n\t\t/** OpenRegister register slug/id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tregister: { type: String, default: '' },\n\t\t/**\n\t\t * OpenRegister schema — a slug string, or the schema OBJECT the\n\t\t * detail-context merge supplies (its slug/name/id is used).\n\t\t *\n\t\t * @type {string|object}\n\t\t */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The audited object's id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Optional card title override (defaults to the card's translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Maximum audit rows to render (0 falls back to the card default). */\n\t\tmaxDisplay: { type: Number, default: 0 },\n\t\t/**\n\t\t * `app` lists the app-wide feed (every object the caller may read,\n\t\t * narrowed to `register` / `schema` when set) and needs no object;\n\t\t * `object` (the default) is one object's trail. Also read from\n\t\t * `content.scope`.\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: { type: String, default: '' },\n\t\t/** Read the trail of something that is not an object (`{ kind: 'flow-task', id? }`); also read from `content.source`. */\n\t\tsource: { type: Object, default: null },\n\t\t/** A trail URL of its own; also read from `content.endpoint`. */\n\t\tendpoint: { type: String, default: '' },\n\t\t/** Actions to leave out (`['read']`); also read from `content.excludeActions`. */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, footnote? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels?, relatedSources? }`. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The resolved object-context bag from either inject shape, or {}.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tctx() {\n\t\t\tconst inj = this.cnObjectContext && (this.cnObjectContext.value || this.cnObjectContext)\n\t\t\tconst holder = this.cnDetailObjectContext && this.cnDetailObjectContext.value\n\t\t\treturn inj || holder || {}\n\t\t},\n\n\t\t/**\n\t\t * The audited object's id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedObjectId() {\n\t\t\treturn this.objectId || this.ctx.objectId || this.content.objectId || ''\n\t\t},\n\n\t\t/**\n\t\t * The register slug/id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedRegister() {\n\t\t\treturn this.register || this.ctx.register || this.content.register || ''\n\t\t},\n\n\t\t/**\n\t\t * The schema SLUG: explicit prop → inject context → content; a\n\t\t * schema object collapses to its slug/name/id.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSchema() {\n\t\t\tconst s = this.schema || this.ctx.schema || this.content.schema || ''\n\t\t\treturn typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''\n\t\t},\n\n\t\t/**\n\t\t * The feed scope: explicit prop → content → `object`.\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 {('object'|'app')}\n\t\t */\n\t\tresolvedScope() {\n\t\t\tconst s = this.scope || this.content.scope\n\t\t\treturn s === 'app' ? 'app' : 'object'\n\t\t},\n\n\t\t/**\n\t\t * The card title: explicit prop → content → '' (card default).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\t/** @return {object|null} The non-object anchor, from the prop or the content. */\n\t\tresolvedSource() {\n\t\t\tconst source = this.source || this.content.source\n\t\t\treturn source && typeof source === 'object' ? source : null\n\t\t},\n\n\t\t/** @return {string} The trail URL of its own, from the prop or the content. */\n\t\tresolvedEndpoint() {\n\t\t\treturn this.endpoint || (typeof this.content.endpoint === 'string' ? this.content.endpoint : '')\n\t\t},\n\n\t\t/** @return {string} The empty line the content names, through the host's translate, or ''. */\n\t\t/**\n\t\t * `content.actionLabels`, each label through the app's translate.\n\t\t *\n\t\t * @return {Object<string, string>|null}\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\tactionLabels() {\n\t\t\tconst labels = this.content.actionLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t.map(([key, text]) => [key, translate(text)]))\n\t\t},\n\n\t\t/**\n\t\t * `content.footnote`, a muted line under the trail with the record's\n\t\t * fields filled in (\"Reference {identifier}\"); '' when a field is empty.\n\t\t *\n\t\t * @return {string}\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() {\n\t\t\tconst note = this.content.footnote\n\t\t\tif (typeof note !== 'string' || note === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn fillTemplate(translate(note), this.ctx.object || this.ctx.objectData || null)\n\t\t},\n\n\t\t/**\n\t\t * `content.valueLabels`, each word through the app's translate.\n\t\t *\n\t\t * @return {{[key: string]: {[value: string]: string}}|null}\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\tvalueLabels() {\n\t\t\tconst labels = this.content.valueLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, words]) => words && typeof words === 'object')\n\t\t\t\t.map(([key, words]) => [key, Object.fromEntries(Object.entries(words)\n\t\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t\t.map(([value, text]) => [value, translate(text)]))]))\n\t\t},\n\n\t\t/**\n\t\t * `content.relatedSources`: records beside the object whose events\n\t\t * join its history (see CnAuditTrailCard).\n\t\t *\n\t\t * @return {Array<object>}\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() {\n\t\t\treturn Array.isArray(this.content.relatedSources) ? this.content.relatedSources : []\n\t\t},\n\n\t\temptyText() {\n\t\t\tconst text = this.content.emptyText\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The actions to leave out: explicit prop → content → none.\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 * @return {Array<string>}\n\t\t */\n\t\tresolvedExcludeActions() {\n\t\t\tif (this.excludeActions.length > 0) {\n\t\t\t\treturn this.excludeActions\n\t\t\t}\n\t\t\tconst fromContent = this.content.excludeActions\n\t\t\treturn Array.isArray(fromContent) ? fromContent.filter((action) => typeof action === 'string' && action !== '') : []\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || this.content.title || ''\n\t\t},\n\n\t\t/**\n\t\t * The row cap: explicit prop → content → the card's own default (5).\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tresolvedMaxDisplay() {\n\t\t\tif (this.maxDisplay > 0) {\n\t\t\t\treturn this.maxDisplay\n\t\t\t}\n\t\t\tconst fromContent = Number(this.content.maxDisplay)\n\t\t\treturn Number.isFinite(fromContent) && fromContent > 0 ? fromContent : 5\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* The card keeps its own box, footnote included. */\n.cn-audit-trail-widget {\n\tdisplay: contents;\n}\n</style>\n"],"names":[],"mappings":";;;;OAaO,EAAC,uBAAkB;;;;;;UADlB,QAAa,CAAA,aAAA,KAAA,KAAA,IAAS,uBAAA,IAAA,QAAA,CAAA,gBAAA,KAAA,QAAA,CAAA,gBAAA,IAAA,QAAA,CAAA,cAAA,IAAA,QAAA,CAAA,gBAAA,CAAA;;;AAG1B,UAAA,MAAM,EAAE,QAAY,CAAA,cAAA;AACpB,UAAA,QAAQ,EAAE,QAAU,CAAA,gBAAA;AACpB,UAAA,QAAQ,EAAE,QAAgB,CAAA,gBAAA;AAC1B,UAAA,MAAM,EAAE,QAAc,CAAA,cAAA;AACtB,UAAA,QAAQ,EAAE,QAAgB,CAAA,gBAAA;AAC1B,UAAA,KAAK,EAAE,QAAa,CAAA,aAAA;AACpB,UAAA,KAAK,EAAE,QAAa,CAAA,aAAA;AACpB,UAAA,UAAU,EAAE,QAAkB,CAAA,kBAAA;UAC9B,MAAM,EAAE,cAAO,IAAG,OAAA,MAAA,CAAA,OAAA,CAAA,MAAA,KAAA,QAAA,GAAA,MAAA,CAAA,OAAA,CAAA,MAAA,GAAA,EAAA;AAClB,UAAA,cAAc,EAAC,QAAuB,CAAA,sBAAA;AACtC,UAAA,YAAE,EAAA,QAAA,CAAA,YAAA;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnAuditTrailWidget.vue3.js","sources":["../../../../src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue?vue&type=template&id=87ba40b4&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tv-if=\"resolvedScope === 'app' || resolvedSource || resolvedEndpoint || (resolvedRegister && resolvedSchema && resolvedObjectId)\"\n\t\tclass=\"cn-audit-trail-widget\">\n\t\t<CnAuditTrailCard\n\t\t\t:source=\"resolvedSource\"\n\t\t\t:endpoint=\"resolvedEndpoint\"\n\t\t\t:register=\"resolvedRegister\"\n\t\t\t:schema=\"resolvedSchema\"\n\t\t\t:objectId=\"resolvedObjectId\"\n\t\t\t:scope=\"resolvedScope\"\n\t\t\t:title=\"resolvedTitle\"\n\t\t\t:maxDisplay=\"resolvedMaxDisplay\"\n\t\t\t:layout=\"content && typeof content.layout === 'string' ? content.layout : ''\"\n\t\t\t:excludeActions=\"resolvedExcludeActions\"\n\t\t\t:actionLabels=\"actionLabels\"\n\t\t\t:valueLabels=\"valueLabels\"\n\t\t\t:changeLabels=\"changeLabels\"\n\t\t\t:object=\"ctx.object || ctx.objectData || null\"\n\t\t\t:relatedSources=\"relatedSources\"\n\t\t\t:footnote=\"footnote\"\n\t\t\tv-bind=\"emptyText ? { noEntriesLabel: emptyText } : {}\" />\n\t</div>\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\n\n/**\n * CnAuditTrailWidget — thin built-in widget wrapping CnAuditTrailCard with\n * unified object-context resolution (`audit-trail` widget key, Wave 1 of\n * nextcloud-vue#91).\n *\n * CnAuditTrailCard needs explicit `register` / `schema` / `objectId` props,\n * but the detail render paths surface the current object differently:\n * CnDetailPage's config grid provides a `cnObjectContext` inject, while\n * CnPageRenderer's slot CnWidgetGrid spreads the detail object context as\n * props, and CnDashboardPage's registry branch hands a stored `content`\n * blob. This widget reads whichever is present — explicit props win, then\n * the injects, then `content` — so the single `audit-trail` key works on\n * every surface. It replaces the three identical per-app adapters\n * (procest / zaakafhandelapp / scholiq).\n *\n * Registered in BOTH the v2 grid's BUILT_IN_WIDGETS and (detail-page\n * surface, like the `data` widget) the shared dashboardWidgetRegistry —\n * see `dashboardRegistration.js`.\n */\nexport default {\n\tname: 'CnAuditTrailWidget',\n\n\tcomponents: { CnAuditTrailCard },\n\n\tinject: {\n\t\t/** The host app's translate function, for `content.emptyText`. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/**\n\t\t * Detail-page object context (`{ objectId, object, register, schema }`)\n\t\t * provided by CnDetailPage (raw object or `{ value }` holder).\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * Detail object context holder (`{ value: { objectData, schema,\n\t\t * objectType, objectId, register, store } | null }`) provided by\n\t\t * CnPageRenderer for the v2 slot grid.\n\t\t */\n\t\tcnDetailObjectContext: { default: null },\n\t},\n\n\tprops: {\n\t\t/** OpenRegister register slug/id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tregister: { type: String, default: '' },\n\t\t/**\n\t\t * OpenRegister schema — a slug string, or the schema OBJECT the\n\t\t * detail-context merge supplies (its slug/name/id is used).\n\t\t *\n\t\t * @type {string|object}\n\t\t */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The audited object's id — object context spread as props by CnWidgetGrid (slot path). */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Optional card title override (defaults to the card's translated label). */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Maximum audit rows to render (0 falls back to the card default). */\n\t\tmaxDisplay: { type: Number, default: 0 },\n\t\t/**\n\t\t * `app` lists the app-wide feed (every object the caller may read,\n\t\t * narrowed to `register` / `schema` when set) and needs no object;\n\t\t * `object` (the default) is one object's trail. Also read from\n\t\t * `content.scope`.\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: { type: String, default: '' },\n\t\t/** Read the trail of something that is not an object (`{ kind: 'flow-task', id? }`); also read from `content.source`. */\n\t\tsource: { type: Object, default: null },\n\t\t/** A trail URL of its own; also read from `content.endpoint`. */\n\t\tendpoint: { type: String, default: '' },\n\t\t/** Actions to leave out (`['read']`); also read from `content.excludeActions`. */\n\t\texcludeActions: { type: Array, default: () => [] },\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, footnote? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels? }`. */\n\t\t/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId?, source?, endpoint?, emptyText?, excludeActions?, actionLabels?, valueLabels?, relatedSources? }`. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The resolved object-context bag from either inject shape, or {}.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tctx() {\n\t\t\tconst inj = this.cnObjectContext && (this.cnObjectContext.value || this.cnObjectContext)\n\t\t\tconst holder = this.cnDetailObjectContext && this.cnDetailObjectContext.value\n\t\t\treturn inj || holder || {}\n\t\t},\n\n\t\t/**\n\t\t * The audited object's id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedObjectId() {\n\t\t\treturn this.objectId || this.ctx.objectId || this.content.objectId || ''\n\t\t},\n\n\t\t/**\n\t\t * The register slug/id: explicit prop → inject context → content.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedRegister() {\n\t\t\treturn this.register || this.ctx.register || this.content.register || ''\n\t\t},\n\n\t\t/**\n\t\t * The schema SLUG: explicit prop → inject context → content; a\n\t\t * schema object collapses to its slug/name/id.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedSchema() {\n\t\t\tconst s = this.schema || this.ctx.schema || this.content.schema || ''\n\t\t\treturn typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''\n\t\t},\n\n\t\t/**\n\t\t * The feed scope: explicit prop → content → `object`.\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 {('object'|'app')}\n\t\t */\n\t\tresolvedScope() {\n\t\t\tconst s = this.scope || this.content.scope\n\t\t\treturn s === 'app' ? 'app' : 'object'\n\t\t},\n\n\t\t/**\n\t\t * The card title: explicit prop → content → '' (card default).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\t/** @return {object|null} The non-object anchor, from the prop or the content. */\n\t\tresolvedSource() {\n\t\t\tconst source = this.source || this.content.source\n\t\t\treturn source && typeof source === 'object' ? source : null\n\t\t},\n\n\t\t/** @return {string} The trail URL of its own, from the prop or the content. */\n\t\tresolvedEndpoint() {\n\t\t\treturn this.endpoint || (typeof this.content.endpoint === 'string' ? this.content.endpoint : '')\n\t\t},\n\n\t\t/** @return {string} The empty line the content names, through the host's translate, or ''. */\n\t\t/**\n\t\t * `content.actionLabels`, each label through the app's translate.\n\t\t *\n\t\t * @return {Object<string, string>|null}\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\tactionLabels() {\n\t\t\tconst labels = this.content.actionLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t.map(([key, text]) => [key, translate(text)]))\n\t\t},\n\n\t\t/**\n\t\t * `content.changeLabels`, each rule's label through the app's translate.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tchangeLabels() {\n\t\t\tconst rules = this.content.changeLabels\n\t\t\tif (!Array.isArray(rules)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn rules\n\t\t\t\t.filter((rule) => rule && typeof rule.label === 'string' && rule.label !== '')\n\t\t\t\t.map((rule) => ({ ...rule, label: translate(rule.label) }))\n\t\t},\n\n\t\t/**\n\t\t * `content.footnote`, a muted line under the trail with the record's\n\t\t * fields filled in (\"Reference {identifier}\"); '' when a field is empty.\n\t\t *\n\t\t * @return {string}\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() {\n\t\t\tconst note = this.content.footnote\n\t\t\tif (typeof note !== 'string' || note === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn fillTemplate(translate(note), this.ctx.object || this.ctx.objectData || null)\n\t\t},\n\n\t\t/**\n\t\t * `content.valueLabels`, each word through the app's translate.\n\t\t *\n\t\t * @return {{[key: string]: {[value: string]: string}}|null}\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\tvalueLabels() {\n\t\t\tconst labels = this.content.valueLabels\n\t\t\tif (!labels || typeof labels !== 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (text) => text\n\t\t\treturn Object.fromEntries(Object.entries(labels)\n\t\t\t\t.filter(([, words]) => words && typeof words === 'object')\n\t\t\t\t.map(([key, words]) => [key, Object.fromEntries(Object.entries(words)\n\t\t\t\t\t.filter(([, text]) => typeof text === 'string' && text !== '')\n\t\t\t\t\t.map(([value, text]) => [value, translate(text)]))]))\n\t\t},\n\n\t\t/**\n\t\t * `content.relatedSources`: records beside the object whose events\n\t\t * join its history (see CnAuditTrailCard).\n\t\t *\n\t\t * @return {Array<object>}\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() {\n\t\t\treturn Array.isArray(this.content.relatedSources) ? this.content.relatedSources : []\n\t\t},\n\n\t\temptyText() {\n\t\t\tconst text = this.content.emptyText\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The actions to leave out: explicit prop → content → none.\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 * @return {Array<string>}\n\t\t */\n\t\tresolvedExcludeActions() {\n\t\t\tif (this.excludeActions.length > 0) {\n\t\t\t\treturn this.excludeActions\n\t\t\t}\n\t\t\tconst fromContent = this.content.excludeActions\n\t\t\treturn Array.isArray(fromContent) ? fromContent.filter((action) => typeof action === 'string' && action !== '') : []\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || this.content.title || ''\n\t\t},\n\n\t\t/**\n\t\t * The row cap: explicit prop → content → the card's own default (5).\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tresolvedMaxDisplay() {\n\t\t\tif (this.maxDisplay > 0) {\n\t\t\t\treturn this.maxDisplay\n\t\t\t}\n\t\t\tconst fromContent = Number(this.content.maxDisplay)\n\t\t\treturn Number.isFinite(fromContent) && fromContent > 0 ? fromContent : 5\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* The card keeps its own box, footnote included. */\n.cn-audit-trail-widget {\n\tdisplay: contents;\n}\n</style>\n"],"names":[],"mappings":";;;;OAaO,EAAC,uBAAkB;;;;;;UADlB,QAAa,CAAA,aAAA,KAAA,KAAA,IAAS,uBAAA,IAAA,QAAA,CAAA,gBAAA,KAAA,QAAA,CAAA,gBAAA,IAAA,QAAA,CAAA,cAAA,IAAA,QAAA,CAAA,gBAAA,CAAA;;;AAG1B,UAAA,MAAM,EAAE,QAAY,CAAA,cAAA;AACpB,UAAA,QAAQ,EAAE,QAAU,CAAA,gBAAA;AACpB,UAAA,QAAQ,EAAE,QAAgB,CAAA,gBAAA;AAC1B,UAAA,MAAM,EAAE,QAAc,CAAA,cAAA;AACtB,UAAA,QAAQ,EAAE,QAAgB,CAAA,gBAAA;AAC1B,UAAA,KAAK,EAAE,QAAa,CAAA,aAAA;AACpB,UAAA,KAAK,EAAE,QAAa,CAAA,aAAA;AACpB,UAAA,UAAU,EAAE,QAAc,CAAA,kBAAA;UAC1B,MAAM,EAAE,cAAO,IAAW,OAAA,MAAA,CAAA,OAAO,CAAC,MAAM,KAAA,QAAA,GAAI,MAAA,CAAA,OAAA,CAAA,MAAA,GAAA,EAAA;AAC5C,UAAA,cAAc,EAAE,QAAc,CAAA,sBAAA;AAC9B,UAAA,YAAY,EAAE,QAAI,CAAA,YAAA;AAClB,UAAA,WAAW,EAAE,QAAW,CAAA,WAAA;AACxB,UAAA,YAAE,EAAA,QAAA,CAAA,YAAA;;;;;;;;;;;"}
@@ -2,7 +2,7 @@ import script from './CnChecklistRunWidget.vue2.js';
2
2
  import { render } from './CnChecklistRunWidget.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-dd87442e";
5
+ script.__scopeId = "data-v-f3678d20";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };