@conduction/nextcloud-vue 2.63.0 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +253 -10
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +139 -46
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +87 -0
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +26 -24
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +61 -16
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +20 -0
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -2
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js +13 -0
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +2 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +55 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +153 -133
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +37 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +12 -4
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +125 -14
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +87 -40
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +101 -7
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +22 -5
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +5 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +63 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +96 -37
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/stagesModel.js +5 -2
- package/dist/esm/components/CnStagesWidget/stagesModel.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +74 -5
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +15 -8
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +14 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +2 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +73 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +14 -2
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +495 -81
- package/dist/esm/store/useObjectStore.js +118 -33
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +14 -0
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/schemaRefSlug.js +19 -3
- package/dist/esm/utils/schemaRefSlug.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +7388 -6021
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +495 -81
- package/l10n/en.json +2 -0
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnAppNav/CnAppNav.vue +441 -14
- package/src/components/CnAppRoot/CnAppRoot.md +4 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +94 -1
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +61 -16
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +22 -1
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +14 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +33 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +72 -4
- package/src/components/CnDataTable/CnDataTable.vue +45 -1
- package/src/components/CnDetailPage/CnDetailPage.md +8 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +169 -2
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +147 -9
- package/src/components/CnIndexPage/CnIndexPage.vue +32 -2
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +7 -1
- package/src/components/CnStagesWidget/CnStagesWidget.vue +189 -2
- package/src/components/CnStagesWidget/stagesModel.js +5 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +89 -7
- package/src/components/CnTabsWidget/CnTabsWidget.vue +15 -2
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +4 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +107 -1
- package/src/css/detail-page.css +32 -4
- package/src/css/kpi-card.css +71 -1
- package/src/css/table.css +18 -6
- package/src/schemas/app-manifest-v2.schema.json +134 -3
- package/src/store/useObjectStore.js +118 -33
- package/src/utils/actionsDispatcher.js +14 -0
- package/src/utils/schemaRefSlug.js +19 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -24,12 +24,27 @@ var script = {
|
|
|
24
24
|
components: { CnDetailCard: __sfc_main, NcLoadingIcon },
|
|
25
25
|
|
|
26
26
|
props: {
|
|
27
|
-
/** OpenRegister register id (slug or uuid). */
|
|
28
|
-
register: { type: String,
|
|
29
|
-
/** OpenRegister schema id (slug or uuid). */
|
|
30
|
-
schema: { type: String,
|
|
31
|
-
/** Parent object id. */
|
|
32
|
-
objectId: { type: String,
|
|
27
|
+
/** OpenRegister register id (slug or uuid). Optional for `scope: "app"`. */
|
|
28
|
+
register: { type: String, default: '' },
|
|
29
|
+
/** OpenRegister schema id (slug or uuid). Optional for `scope: "app"`. */
|
|
30
|
+
schema: { type: String, default: '' },
|
|
31
|
+
/** Parent object id. Required for `scope: "object"` (the default); unused for `scope: "app"`. */
|
|
32
|
+
objectId: { type: String, default: '' },
|
|
33
|
+
/**
|
|
34
|
+
* What the card lists. `object` (the default) is one object's trail
|
|
35
|
+
* and needs `register`, `schema` and `objectId`. `app` is the
|
|
36
|
+
* app-wide feed: the entries of every object the caller may read
|
|
37
|
+
* (OpenRegister's `/audit-trails/readable`), narrowed to `register`
|
|
38
|
+
* and `schema` when those are set.
|
|
39
|
+
*
|
|
40
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
41
|
+
*/
|
|
42
|
+
scope: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: 'object',
|
|
45
|
+
validator: (value) => ['object', 'app'].includes(value),
|
|
46
|
+
},
|
|
47
|
+
|
|
33
48
|
/** Rendering surface — passed for AD-19 surface fallback consumers. */
|
|
34
49
|
surface: {
|
|
35
50
|
type: String,
|
|
@@ -71,13 +86,38 @@ var script = {
|
|
|
71
86
|
displayedEntries() {
|
|
72
87
|
return this.entries.slice(0, this.maxDisplay)
|
|
73
88
|
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The path the card reads (prefixed at fetch time), or '' when it has nothing to read: the
|
|
92
|
+
* object's own trail, or the app-wide readable feed narrowed to the
|
|
93
|
+
* register and schema when set.
|
|
94
|
+
*
|
|
95
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
96
|
+
* @return {string}
|
|
97
|
+
*/
|
|
98
|
+
fetchUrl() {
|
|
99
|
+
const params = new URLSearchParams({ limit: String(this.maxDisplay) });
|
|
100
|
+
if (this.scope === 'app') {
|
|
101
|
+
if (this.register) {
|
|
102
|
+
params.set('register', this.register);
|
|
103
|
+
}
|
|
104
|
+
if (this.schema) {
|
|
105
|
+
params.set('schema', this.schema);
|
|
106
|
+
}
|
|
107
|
+
return `${this.apiBase}/audit-trails/readable?${params.toString()}`
|
|
108
|
+
}
|
|
109
|
+
if (!this.register || !this.schema || !this.objectId) {
|
|
110
|
+
return ''
|
|
111
|
+
}
|
|
112
|
+
return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trail?${params.toString()}`
|
|
113
|
+
},
|
|
74
114
|
},
|
|
75
115
|
|
|
76
116
|
watch: {
|
|
77
|
-
|
|
117
|
+
fetchUrl: {
|
|
78
118
|
immediate: true,
|
|
79
|
-
handler(
|
|
80
|
-
if (
|
|
119
|
+
handler(url) {
|
|
120
|
+
if (url) {
|
|
81
121
|
this.fetchEntries();
|
|
82
122
|
}
|
|
83
123
|
},
|
|
@@ -85,20 +125,25 @@ var script = {
|
|
|
85
125
|
},
|
|
86
126
|
|
|
87
127
|
methods: {
|
|
128
|
+
/**
|
|
129
|
+
* Read the entries from `fetchUrl`. The readable feed answers
|
|
130
|
+
* `{ rows, nextCursor }`, the object trail `{ results }`.
|
|
131
|
+
*
|
|
132
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
133
|
+
* @return {Promise<void>}
|
|
134
|
+
*/
|
|
88
135
|
async fetchEntries() {
|
|
89
|
-
|
|
136
|
+
const url = this.fetchUrl;
|
|
137
|
+
if (!url) {
|
|
90
138
|
return
|
|
91
139
|
}
|
|
92
140
|
this.loading = true;
|
|
93
141
|
try {
|
|
94
|
-
const
|
|
95
|
-
const response = await fetch(
|
|
96
|
-
prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trail?${params.toString()}`),
|
|
97
|
-
{ headers: buildHeaders() },
|
|
98
|
-
);
|
|
142
|
+
const response = await fetch(prefixUrl(url), { headers: buildHeaders() });
|
|
99
143
|
if (response.ok) {
|
|
100
144
|
const data = await response.json();
|
|
101
|
-
|
|
145
|
+
const list = data.results || data.rows || data;
|
|
146
|
+
this.entries = Array.isArray(list) ? list : [];
|
|
102
147
|
} else {
|
|
103
148
|
this.entries = [];
|
|
104
149
|
}
|
|
@@ -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<div v-else-if=\"entries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ entry.action || entry.event || actionLabel }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t\t<template v-if=\"entries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ entries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\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\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\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},\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}\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\tdisplayedEntries() {\n\t\t\treturn this.entries.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\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\tasync fetchEntries() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst params = new URLSearchParams({ limit: String(this.maxDisplay) })\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trail?${params.toString()}`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.entries = data.results || data || []\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\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} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatActor(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\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()\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__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t"],"mappings":"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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<div v-else-if=\"entries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ entry.action || entry.event || actionLabel }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t\t<template v-if=\"entries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ entries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Required for `scope: \"object\"` (the default); unused for `scope: \"app\"`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/**\n\t\t * What the card lists. `object` (the default) is one object's trail\n\t\t * and needs `register`, `schema` and `objectId`. `app` is the\n\t\t * app-wide feed: the entries of every object the caller may read\n\t\t * (OpenRegister's `/audit-trails/readable`), narrowed to `register`\n\t\t * and `schema` when those are set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t */\n\t\tscope: {\n\t\t\ttype: String,\n\t\t\tdefault: 'object',\n\t\t\tvalidator: (value) => ['object', 'app'].includes(value),\n\t\t},\n\n\t\t/** Rendering surface — passed for AD-19 surface fallback consumers. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (value) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(value),\n\t\t},\n\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\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},\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}\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\tdisplayedEntries() {\n\t\t\treturn this.entries.slice(0, this.maxDisplay)\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\tconst params = new URLSearchParams({ limit: String(this.maxDisplay) })\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-trail?${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\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\tthis.entries = Array.isArray(list) ? list : []\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\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} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatActor(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\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()\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__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnAuditTrailCard.vue3.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue?vue&type=template&id=d4cd20e2&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<div v-else-if=\"entries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ entry.action || entry.event || actionLabel }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t\t<template v-if=\"entries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ entries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String,
|
|
1
|
+
{"version":3,"file":"CnAuditTrailCard.vue3.js","sources":["../../../../src/components/CnAuditTrailCard/CnAuditTrailCard.vue?vue&type=template&id=d4cd20e2&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<div v-else-if=\"entries.length === 0\" class=\"cn-audit-card__empty\">\n\t\t\t{{ noEntriesLabel }}\n\t\t</div>\n\t\t<ul v-else class=\"cn-audit-card__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"entry in displayedEntries\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\tclass=\"cn-audit-card__row\">\n\t\t\t\t<div class=\"cn-audit-card__row-head\">\n\t\t\t\t\t<strong class=\"cn-audit-card__action\">{{ entry.action || entry.event || actionLabel }}</strong>\n\t\t\t\t\t<span class=\"cn-audit-card__when\">{{ formatWhen(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-audit-card__row-body\">\n\t\t\t\t\t<span class=\"cn-audit-card__actor\">{{ formatActor(entry) }}</span>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t\t<template v-if=\"entries.length > maxDisplay\" #footer>\n\t\t\t<button class=\"cn-audit-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ entries.length }})\n\t\t\t</button>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport History from 'vue-material-design-icons/History.vue'\nimport CnDetailCard from '../CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\n\n/**\n * CnAuditTrailCard — compact audit-trail widget rendered by the\n * integration registry on dashboard and detail surfaces.\n *\n * Basic usage\n * ```vue\n * <CnAuditTrailCard\n * :register=\"registerId\"\n * :schema=\"schemaId\"\n * :object-id=\"objectId\"\n * surface=\"detail-page\" />\n * ```\n */\nexport default {\n\tname: 'CnAuditTrailCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for `scope: \"app\"`. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Required for `scope: \"object\"` (the default); unused for `scope: \"app\"`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/**\n\t\t * What the card lists. `object` (the default) is one object's trail\n\t\t * and needs `register`, `schema` and `objectId`. `app` is the\n\t\t * app-wide feed: the entries of every object the caller may read\n\t\t * (OpenRegister's `/audit-trails/readable`), narrowed to `register`\n\t\t * and `schema` when those are set.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed\n\t\t */\n\t\tscope: {\n\t\t\ttype: String,\n\t\t\tdefault: 'object',\n\t\t\tvalidator: (value) => ['object', 'app'].includes(value),\n\t\t},\n\n\t\t/** Rendering surface — passed for AD-19 surface fallback consumers. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (value) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(value),\n\t\t},\n\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Maximum rows to render. */\n\t\tmaxDisplay: { type: Number, default: 5 },\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},\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}\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\tdisplayedEntries() {\n\t\t\treturn this.entries.slice(0, this.maxDisplay)\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\tconst params = new URLSearchParams({ limit: String(this.maxDisplay) })\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-trail?${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\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\tthis.entries = Array.isArray(list) ? list : []\n\t\t\t\t} else {\n\t\t\t\t\tthis.entries = []\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} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatActor(entry) {\n\t\t\treturn entry.actorDisplayName || entry.actor || entry.userId || entry.user || ''\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()\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__empty {\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n\tpadding: 12px 0;\n}\n\n.cn-audit-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tcursor: pointer;\n\tpadding: 4px 0;\n\tfont: inherit;\n}\n\n.cn-audit-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_toDisplayString","_createElementVNode"],"mappings":";;;;AA+BwC,EAAA,KAAA,EAAA,sBAAA;;;;AAGjC,EAAA,KAAA,EAAA,qBAAA;;AAKE,MAAA,UAAA,GAAA,EAAA,KAAE,EAAA,yBAAA,GAAA;AACC,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,uBAAA,GAAA;AACD,MAAA,UAAA,GAAA,EAAA,KAAI,EAAA,qBAAA,GAAA;;;;;;;;uBAhBXA;SACI,EAAE,QAAa,CAAA,aAAA;AACnB,IAAA,IAAG,EAAE,KAAO,CAAA,OAAA;AACX,IAAA,WAAS,EAAE,MAAW,CAAA,WAAA;kBACZ,EAAE,MAAO,CAAA,OAAA;;2BACf;OAAA,KAAA,CAAA,OAAA;mCAAA,CAAA,wBAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,CAAA;AACS,UAAA,CAAA,KAAA,CAAA,OAAO,CAAC,MAAM,KAAA,CAAA;0BAA5BC,kBAEI,CAAA,KAAA,EAFJ,UAEI,EAAAC,eAAA,CADF,MAAc,CAAA,cAAA,CAAA,EAAA,CAAA,YAAA;;gCAGjBD,8CACiB,QAAgB,CAAA,gBAAA,EAAA,MAApB,KAAA;qCADbA;kBAEE,GAAG,EAAC,KAAM,CAAC,EAAE;AACd,kBAAA,KAAK,EAAC,oBAAoB;;AAC1B,kBAAAE,kBAAA,QAAA;oBACCA,kBAAO,CAAA,QAAA,EAAP,UAAO,EAAAD,eAAA,CAAA,KAAA,CAAA,MAAA,IAAA,KAAA,CAAA,KAAA,IAAA,MAAA,CAAA,WAAA,CAAA,EAAA,CAAA,YAAA;AACP,oBAAAC,kBAAA,CAAU,MAAV,EAAA,UAAU,EAAAD,eAAA,CAAA,QAAA,CAAA,UAAA,CAAA,KAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -54,6 +54,15 @@ var script = {
|
|
|
54
54
|
title: { type: String, default: '' },
|
|
55
55
|
/** Maximum audit rows to render (0 falls back to the card default). */
|
|
56
56
|
maxDisplay: { type: Number, default: 0 },
|
|
57
|
+
/**
|
|
58
|
+
* `app` lists the app-wide feed (every object the caller may read,
|
|
59
|
+
* narrowed to `register` / `schema` when set) and needs no object;
|
|
60
|
+
* `object` (the default) is one object's trail. Also read from
|
|
61
|
+
* `content.scope`.
|
|
62
|
+
*
|
|
63
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
64
|
+
*/
|
|
65
|
+
scope: { type: String, default: '' },
|
|
57
66
|
/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId? }`. */
|
|
58
67
|
content: { type: Object, default: () => ({}) },
|
|
59
68
|
},
|
|
@@ -99,6 +108,17 @@ var script = {
|
|
|
99
108
|
return typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''
|
|
100
109
|
},
|
|
101
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The feed scope: explicit prop → content → `object`.
|
|
113
|
+
*
|
|
114
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
115
|
+
* @return {('object'|'app')}
|
|
116
|
+
*/
|
|
117
|
+
resolvedScope() {
|
|
118
|
+
const s = this.scope || this.content.scope;
|
|
119
|
+
return s === 'app' ? 'app' : 'object'
|
|
120
|
+
},
|
|
121
|
+
|
|
102
122
|
/**
|
|
103
123
|
* The card title: explicit prop → content → '' (card default).
|
|
104
124
|
*
|
|
@@ -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<CnAuditTrailCard\n\t\tv-if=\"resolvedRegister && resolvedSchema && resolvedObjectId\"\n\t\t:register=\"resolvedRegister\"\n\t\t:schema=\"resolvedSchema\"\n\t\t:objectId=\"resolvedObjectId\"\n\t\t:title=\"resolvedTitle\"\n\t\t:maxDisplay=\"resolvedMaxDisplay\" />\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\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/**\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/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId? }`. */\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 card title: explicit prop → content → '' (card default).\n\t\t *\n\t\t * @return {string}\n\t\t */\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"],"names":["CnAuditTrailCard"],"mappings":";;
|
|
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<CnAuditTrailCard\n\t\tv-if=\"resolvedScope === 'app' || (resolvedRegister && resolvedSchema && resolvedObjectId)\"\n\t\t:register=\"resolvedRegister\"\n\t\t:schema=\"resolvedSchema\"\n\t\t:objectId=\"resolvedObjectId\"\n\t\t:scope=\"resolvedScope\"\n\t\t:title=\"resolvedTitle\"\n\t\t:maxDisplay=\"resolvedMaxDisplay\" />\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\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/**\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/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId? }`. */\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\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"],"names":["CnAuditTrailCard"],"mappings":"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"}
|
|
@@ -3,15 +3,16 @@ import { resolveComponent, openBlock, createBlock, createCommentVNode } from 'vu
|
|
|
3
3
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
4
4
|
const _component_CnAuditTrailCard = resolveComponent("CnAuditTrailCard");
|
|
5
5
|
|
|
6
|
-
return ($options.resolvedRegister && $options.resolvedSchema && $options.resolvedObjectId)
|
|
6
|
+
return ($options.resolvedScope === 'app' || ($options.resolvedRegister && $options.resolvedSchema && $options.resolvedObjectId))
|
|
7
7
|
? (openBlock(), createBlock(_component_CnAuditTrailCard, {
|
|
8
8
|
key: 0,
|
|
9
9
|
register: $options.resolvedRegister,
|
|
10
10
|
schema: $options.resolvedSchema,
|
|
11
11
|
objectId: $options.resolvedObjectId,
|
|
12
|
+
scope: $options.resolvedScope,
|
|
12
13
|
title: $options.resolvedTitle,
|
|
13
14
|
maxDisplay: $options.resolvedMaxDisplay
|
|
14
|
-
}, null, 8 /* PROPS */, ["register", "schema", "objectId", "title", "maxDisplay"]))
|
|
15
|
+
}, null, 8 /* PROPS */, ["register", "schema", "objectId", "scope", "title", "maxDisplay"]))
|
|
15
16
|
: createCommentVNode("v-if", true)
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnAuditTrailWidget.vue3.js","sources":["../../../../src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue?vue&type=template&id=0d7050cf&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<CnAuditTrailCard\n\t\tv-if=\"resolvedRegister && resolvedSchema && resolvedObjectId\"\n\t\t:register=\"resolvedRegister\"\n\t\t:schema=\"resolvedSchema\"\n\t\t:objectId=\"resolvedObjectId\"\n\t\t:title=\"resolvedTitle\"\n\t\t:maxDisplay=\"resolvedMaxDisplay\" />\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\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/**\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/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId? }`. */\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 card title: explicit prop → content → '' (card default).\n\t\t *\n\t\t * @return {string}\n\t\t */\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"],"names":["_createBlock"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"CnAuditTrailWidget.vue3.js","sources":["../../../../src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue?vue&type=template&id=0d7050cf&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<CnAuditTrailCard\n\t\tv-if=\"resolvedScope === 'app' || (resolvedRegister && resolvedSchema && resolvedObjectId)\"\n\t\t:register=\"resolvedRegister\"\n\t\t:schema=\"resolvedSchema\"\n\t\t:objectId=\"resolvedObjectId\"\n\t\t:scope=\"resolvedScope\"\n\t\t:title=\"resolvedTitle\"\n\t\t:maxDisplay=\"resolvedMaxDisplay\" />\n</template>\n\n<script>\nimport CnAuditTrailCard from '../CnAuditTrailCard/CnAuditTrailCard.vue'\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/**\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/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId? }`. */\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\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"],"names":["_createBlock"],"mappings":";;;;;AAYQ,EAAA,OAAA,CAAA,QAAA,CAAA,aAAa,KAAE,KAAA,KAAA,QAAA,CAAA,gBAAA,IAAA,QAAA,CAAA,cAAA,IAAA,QAAA,CAAA,gBAAA,CAAA;oBADrBA;;AAEC,QAAA,QAAQ,EAAE,QAAgB,CAAA,gBAAA;;;;;;;;;;;;"}
|
|
@@ -56,6 +56,19 @@ var script = {
|
|
|
56
56
|
type: String,
|
|
57
57
|
default: () => translate('nextcloud-vue', 'Breadcrumbs'),
|
|
58
58
|
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Draw the FIRST crumb's label as text. NcBreadcrumbs gives the root
|
|
62
|
+
* crumb a home icon and prints its `label` only as the accessible
|
|
63
|
+
* name, so "All cases" read as a house. Off by default, which keeps
|
|
64
|
+
* the home icon. A first crumb with its own `icon` keeps that icon.
|
|
65
|
+
*
|
|
66
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text
|
|
67
|
+
*/
|
|
68
|
+
rootText: {
|
|
69
|
+
type: Boolean,
|
|
70
|
+
default: false,
|
|
71
|
+
},
|
|
59
72
|
},
|
|
60
73
|
|
|
61
74
|
methods: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnBreadcrumbs.vue2.js","sources":["../../../../src/components/CnBreadcrumbs/CnBreadcrumbs.vue"],"sourcesContent":["<!--\n CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n\n Renders a `crumbs` array as NcBreadcrumb entries: every crumb but the\n last navigates via `to` (router target) or `href` (plain anchor); the\n LAST crumb is the current location — rendered without a link target and\n carrying aria-current=\"page\" so assistive tech announces the position\n in the trail. Crumb icons resolve by PascalCase MDI name through the\n shared CnIcon registry, so a home crumb is `{ icon: 'Home', to: … }`\n without the consumer importing an icon component.\n\n NcBreadcrumbs supplies the nav landmark, the chevron separators and the\n responsive collapse of middle crumbs into an overflow menu; this wrapper\n only owns the array-in API and the last-crumb semantics, keeping fleet\n apps from hand-rolling the same v-for.\n-->\n<template>\n\t<NcBreadcrumbs\n\t\tv-if=\"crumbs.length > 0\"\n\t\t:aria-label=\"ariaLabel\"\n\t\tdata-testid=\"cn-breadcrumbs\">\n\t\t<!-- `name` falls back to '' because NcBreadcrumb declares it a REQUIRED\n\t\t String and warns per render on the label-less icon-only crumb. The\n\t\t empty string never shows: NcBreadcrumb prints `name` only for\n\t\t crumbs without an icon slot. -->\n\t\t<NcBreadcrumb\n\t\t\tv-for=\"(crumb, index) in crumbs\"\n\t\t\t:key=\"`${index}-${crumb.label ?? crumb.icon}`\"\n\t\t\t:name=\"crumb.label ?? ''\"\n\t\t\t:to=\"isCurrent(index) ? undefined : crumb.to\"\n\t\t\t:href=\"isCurrent(index) ? undefined : crumb.href\"\n\t\t\t:aria-current=\"isCurrent(index) ? 'page' : undefined\"\n\t\t\t:data-testid=\"`cn-breadcrumbs-crumb-${index}`\">\n\t\t\t<template v-if=\"crumb.icon\" #icon>\n\t\t\t\t<CnIcon :name=\"crumb.icon\" :size=\"20\" />\n\t\t\t</template>\n\t\t</NcBreadcrumb>\n\t</NcBreadcrumbs>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcBreadcrumb, NcBreadcrumbs } from '@nextcloud/vue'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n *\n * Feed it the whole trail as one `crumbs` array — first entry is the root\n * (typically a Home crumb), last entry is the CURRENT location. Every crumb\n * but the last links via its `to` (router) or `href` (anchor); the last one\n * is rendered unlinked with `aria-current=\"page\"`, per the WAI-ARIA\n * breadcrumb pattern.\n *\n * ```vue\n * <CnBreadcrumbs\n * :crumbs=\"[\n * { icon: 'Home', to: { name: 'SecretList' } },\n * { label: 'Team vault', to: { name: 'SecretListFolder', params: { folderId: '7' } } },\n * { label: 'Production' },\n * ]\" />\n * ```\n */\nexport default {\n\tname: 'CnBreadcrumbs',\n\n\tcomponents: {\n\t\tNcBreadcrumbs,\n\t\tNcBreadcrumb,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The trail, root first, current location LAST. Each crumb:\n\t\t * `{ label, to?, href?, icon? }` — `label` is the visible (already\n\t\t * translated) text, `to` a vue-router target, `href` a plain URL\n\t\t * (use one or the other; both are ignored on the last crumb), and\n\t\t * `icon` a PascalCase MDI name resolved via the CnIcon registry\n\t\t * (register it with `registerIcons()` at boot).\n\t\t *\n\t\t * @type {Array<{label: string, to?: (object|string), href?: string, icon?: string}>}\n\t\t */\n\t\tcrumbs: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (value) => value.every((crumb) => crumb && typeof crumb === 'object'\n\t\t\t\t&& (typeof crumb.label === 'string' || typeof crumb.icon === 'string')),\n\t\t},\n\n\t\t/**\n\t\t * Accessible name of the breadcrumb nav landmark. Defaults to the\n\t\t * lib's translation of \"Breadcrumbs\"; override per surface when a\n\t\t * page carries more than one trail.\n\t\t */\n\t\tariaLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Breadcrumbs'),\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the crumb at `index` is the trail's last entry — the\n\t\t * current location, which renders unlinked with aria-current.\n\t\t *\n\t\t * @param {number} index Crumb position in `crumbs`.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCurrent(index) {\n\t\t\treturn index === this.crumbs.length - 1\n\t\t},\n\t},\n}\n</script>\n"],"names":["CnIcon","t"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"CnBreadcrumbs.vue2.js","sources":["../../../../src/components/CnBreadcrumbs/CnBreadcrumbs.vue"],"sourcesContent":["<!--\n CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n\n Renders a `crumbs` array as NcBreadcrumb entries: every crumb but the\n last navigates via `to` (router target) or `href` (plain anchor); the\n LAST crumb is the current location — rendered without a link target and\n carrying aria-current=\"page\" so assistive tech announces the position\n in the trail. Crumb icons resolve by PascalCase MDI name through the\n shared CnIcon registry, so a home crumb is `{ icon: 'Home', to: … }`\n without the consumer importing an icon component.\n\n NcBreadcrumbs supplies the nav landmark, the chevron separators and the\n responsive collapse of middle crumbs into an overflow menu; this wrapper\n only owns the array-in API and the last-crumb semantics, keeping fleet\n apps from hand-rolling the same v-for.\n-->\n<template>\n\t<NcBreadcrumbs\n\t\tv-if=\"crumbs.length > 0\"\n\t\t:aria-label=\"ariaLabel\"\n\t\t:rootIcon=\"rootText ? '' : undefined\"\n\t\tdata-testid=\"cn-breadcrumbs\">\n\t\t<!-- `name` falls back to '' because NcBreadcrumb declares it a REQUIRED\n\t\t String and warns per render on the label-less icon-only crumb. The\n\t\t empty string never shows: NcBreadcrumb prints `name` only for\n\t\t crumbs without an icon slot. -->\n\t\t<NcBreadcrumb\n\t\t\tv-for=\"(crumb, index) in crumbs\"\n\t\t\t:key=\"`${index}-${crumb.label ?? crumb.icon}`\"\n\t\t\t:name=\"crumb.label ?? ''\"\n\t\t\t:to=\"isCurrent(index) ? undefined : crumb.to\"\n\t\t\t:href=\"isCurrent(index) ? undefined : crumb.href\"\n\t\t\t:aria-current=\"isCurrent(index) ? 'page' : undefined\"\n\t\t\t:data-testid=\"`cn-breadcrumbs-crumb-${index}`\">\n\t\t\t<template v-if=\"crumb.icon\" #icon>\n\t\t\t\t<CnIcon :name=\"crumb.icon\" :size=\"20\" />\n\t\t\t</template>\n\t\t</NcBreadcrumb>\n\t</NcBreadcrumbs>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcBreadcrumb, NcBreadcrumbs } from '@nextcloud/vue'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n *\n * Feed it the whole trail as one `crumbs` array — first entry is the root\n * (typically a Home crumb), last entry is the CURRENT location. Every crumb\n * but the last links via its `to` (router) or `href` (anchor); the last one\n * is rendered unlinked with `aria-current=\"page\"`, per the WAI-ARIA\n * breadcrumb pattern.\n *\n * ```vue\n * <CnBreadcrumbs\n * :crumbs=\"[\n * { icon: 'Home', to: { name: 'SecretList' } },\n * { label: 'Team vault', to: { name: 'SecretListFolder', params: { folderId: '7' } } },\n * { label: 'Production' },\n * ]\" />\n * ```\n */\nexport default {\n\tname: 'CnBreadcrumbs',\n\n\tcomponents: {\n\t\tNcBreadcrumbs,\n\t\tNcBreadcrumb,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The trail, root first, current location LAST. Each crumb:\n\t\t * `{ label, to?, href?, icon? }` — `label` is the visible (already\n\t\t * translated) text, `to` a vue-router target, `href` a plain URL\n\t\t * (use one or the other; both are ignored on the last crumb), and\n\t\t * `icon` a PascalCase MDI name resolved via the CnIcon registry\n\t\t * (register it with `registerIcons()` at boot).\n\t\t *\n\t\t * @type {Array<{label: string, to?: (object|string), href?: string, icon?: string}>}\n\t\t */\n\t\tcrumbs: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (value) => value.every((crumb) => crumb && typeof crumb === 'object'\n\t\t\t\t&& (typeof crumb.label === 'string' || typeof crumb.icon === 'string')),\n\t\t},\n\n\t\t/**\n\t\t * Accessible name of the breadcrumb nav landmark. Defaults to the\n\t\t * lib's translation of \"Breadcrumbs\"; override per surface when a\n\t\t * page carries more than one trail.\n\t\t */\n\t\tariaLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Breadcrumbs'),\n\t\t},\n\n\t\t/**\n\t\t * Draw the FIRST crumb's label as text. NcBreadcrumbs gives the root\n\t\t * crumb a home icon and prints its `label` only as the accessible\n\t\t * name, so \"All cases\" read as a house. Off by default, which keeps\n\t\t * the home icon. A first crumb with its own `icon` keeps that icon.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text\n\t\t */\n\t\trootText: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the crumb at `index` is the trail's last entry — the\n\t\t * current location, which renders unlinked with aria-current.\n\t\t *\n\t\t * @param {number} index Crumb position in `crumbs`.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCurrent(index) {\n\t\t\treturn index === this.crumbs.length - 1\n\t\t},\n\t},\n}\n</script>\n"],"names":["CnIcon","t"],"mappings":";;;;AA8CA;AACC;AACA;;AAEA;;;AAGA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAED,aAAe;AACd,CAAA,IAAI,EAAE,eAAe;;CAErB,UAAU,EAAE;EACX,aAAa;EACb,YAAY;AACZ,UAAAA,UAAM;EACN;;AAED,CAAA,KAAK,EAAE;AACN;;;;;;AAMC;AACA;AACA;;AAED,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,KAAK;AACX,GAAA,OAAO,EAAE,MAAM,EAAE;AACjB,GAAA,SAAS,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,KAAK,KAAM,IAAG,OAAO,UAAU,QAAO;AAC7E,QAAI,OAAO,KAAK,CAAC,KAAM,KAAI,QAAO,IAAK,OAAO,KAAK,CAAC,IAAG,KAAM,QAAQ,CAAC,CAAC;GACxE;;AAED;;;;;EAKA,SAAS,EAAE;GACV,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,aAAa,CAAC;GAChD;;AAED;;;;;AAKC;AACA;;AAED,EAAA,QAAQ,EAAE;GACT,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,KAAK;GACd;EACD;;AAED,CAAA,OAAO,EAAE;AACR;;;AAGC;AACA;AACA;;AAED,EAAA,SAAS,CAAC,KAAK,EAAE;AAChB,GAAA,OAAO,KAAI,KAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA;GACtC;EACD;AACF;;;;"}
|
|
@@ -9,6 +9,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
9
9
|
? (openBlock(), createBlock(_component_NcBreadcrumbs, {
|
|
10
10
|
key: 0,
|
|
11
11
|
"aria-label": $props.ariaLabel,
|
|
12
|
+
rootIcon: $props.rootText ? '' : undefined,
|
|
12
13
|
"data-testid": "cn-breadcrumbs"
|
|
13
14
|
}, {
|
|
14
15
|
default: withCtx(() => [
|
|
@@ -37,7 +38,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
37
38
|
}), 128 /* KEYED_FRAGMENT */))
|
|
38
39
|
]),
|
|
39
40
|
_: 1 /* STABLE */
|
|
40
|
-
}, 8 /* PROPS */, ["aria-label"]))
|
|
41
|
+
}, 8 /* PROPS */, ["aria-label", "rootIcon"]))
|
|
41
42
|
: createCommentVNode("v-if", true)
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnBreadcrumbs.vue3.js","sources":["../../../../src/components/CnBreadcrumbs/CnBreadcrumbs.vue?vue&type=template&id=296fde9b&lang.js"],"sourcesContent":["<!--\n CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n\n Renders a `crumbs` array as NcBreadcrumb entries: every crumb but the\n last navigates via `to` (router target) or `href` (plain anchor); the\n LAST crumb is the current location — rendered without a link target and\n carrying aria-current=\"page\" so assistive tech announces the position\n in the trail. Crumb icons resolve by PascalCase MDI name through the\n shared CnIcon registry, so a home crumb is `{ icon: 'Home', to: … }`\n without the consumer importing an icon component.\n\n NcBreadcrumbs supplies the nav landmark, the chevron separators and the\n responsive collapse of middle crumbs into an overflow menu; this wrapper\n only owns the array-in API and the last-crumb semantics, keeping fleet\n apps from hand-rolling the same v-for.\n-->\n<template>\n\t<NcBreadcrumbs\n\t\tv-if=\"crumbs.length > 0\"\n\t\t:aria-label=\"ariaLabel\"\n\t\tdata-testid=\"cn-breadcrumbs\">\n\t\t<!-- `name` falls back to '' because NcBreadcrumb declares it a REQUIRED\n\t\t String and warns per render on the label-less icon-only crumb. The\n\t\t empty string never shows: NcBreadcrumb prints `name` only for\n\t\t crumbs without an icon slot. -->\n\t\t<NcBreadcrumb\n\t\t\tv-for=\"(crumb, index) in crumbs\"\n\t\t\t:key=\"`${index}-${crumb.label ?? crumb.icon}`\"\n\t\t\t:name=\"crumb.label ?? ''\"\n\t\t\t:to=\"isCurrent(index) ? undefined : crumb.to\"\n\t\t\t:href=\"isCurrent(index) ? undefined : crumb.href\"\n\t\t\t:aria-current=\"isCurrent(index) ? 'page' : undefined\"\n\t\t\t:data-testid=\"`cn-breadcrumbs-crumb-${index}`\">\n\t\t\t<template v-if=\"crumb.icon\" #icon>\n\t\t\t\t<CnIcon :name=\"crumb.icon\" :size=\"20\" />\n\t\t\t</template>\n\t\t</NcBreadcrumb>\n\t</NcBreadcrumbs>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcBreadcrumb, NcBreadcrumbs } from '@nextcloud/vue'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n *\n * Feed it the whole trail as one `crumbs` array — first entry is the root\n * (typically a Home crumb), last entry is the CURRENT location. Every crumb\n * but the last links via its `to` (router) or `href` (anchor); the last one\n * is rendered unlinked with `aria-current=\"page\"`, per the WAI-ARIA\n * breadcrumb pattern.\n *\n * ```vue\n * <CnBreadcrumbs\n * :crumbs=\"[\n * { icon: 'Home', to: { name: 'SecretList' } },\n * { label: 'Team vault', to: { name: 'SecretListFolder', params: { folderId: '7' } } },\n * { label: 'Production' },\n * ]\" />\n * ```\n */\nexport default {\n\tname: 'CnBreadcrumbs',\n\n\tcomponents: {\n\t\tNcBreadcrumbs,\n\t\tNcBreadcrumb,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The trail, root first, current location LAST. Each crumb:\n\t\t * `{ label, to?, href?, icon? }` — `label` is the visible (already\n\t\t * translated) text, `to` a vue-router target, `href` a plain URL\n\t\t * (use one or the other; both are ignored on the last crumb), and\n\t\t * `icon` a PascalCase MDI name resolved via the CnIcon registry\n\t\t * (register it with `registerIcons()` at boot).\n\t\t *\n\t\t * @type {Array<{label: string, to?: (object|string), href?: string, icon?: string}>}\n\t\t */\n\t\tcrumbs: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (value) => value.every((crumb) => crumb && typeof crumb === 'object'\n\t\t\t\t&& (typeof crumb.label === 'string' || typeof crumb.icon === 'string')),\n\t\t},\n\n\t\t/**\n\t\t * Accessible name of the breadcrumb nav landmark. Defaults to the\n\t\t * lib's translation of \"Breadcrumbs\"; override per surface when a\n\t\t * page carries more than one trail.\n\t\t */\n\t\tariaLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Breadcrumbs'),\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the crumb at `index` is the trail's last entry — the\n\t\t * current location, which renders unlinked with aria-current.\n\t\t *\n\t\t * @param {number} index Crumb position in `crumbs`.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCurrent(index) {\n\t\t\treturn index === this.crumbs.length - 1\n\t\t},\n\t},\n}\n</script>\n"],"names":[
|
|
1
|
+
{"version":3,"file":"CnBreadcrumbs.vue3.js","sources":["../../../../src/components/CnBreadcrumbs/CnBreadcrumbs.vue?vue&type=template&id=296fde9b&lang.js"],"sourcesContent":["<!--\n CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n\n Renders a `crumbs` array as NcBreadcrumb entries: every crumb but the\n last navigates via `to` (router target) or `href` (plain anchor); the\n LAST crumb is the current location — rendered without a link target and\n carrying aria-current=\"page\" so assistive tech announces the position\n in the trail. Crumb icons resolve by PascalCase MDI name through the\n shared CnIcon registry, so a home crumb is `{ icon: 'Home', to: … }`\n without the consumer importing an icon component.\n\n NcBreadcrumbs supplies the nav landmark, the chevron separators and the\n responsive collapse of middle crumbs into an overflow menu; this wrapper\n only owns the array-in API and the last-crumb semantics, keeping fleet\n apps from hand-rolling the same v-for.\n-->\n<template>\n\t<NcBreadcrumbs\n\t\tv-if=\"crumbs.length > 0\"\n\t\t:aria-label=\"ariaLabel\"\n\t\t:rootIcon=\"rootText ? '' : undefined\"\n\t\tdata-testid=\"cn-breadcrumbs\">\n\t\t<!-- `name` falls back to '' because NcBreadcrumb declares it a REQUIRED\n\t\t String and warns per render on the label-less icon-only crumb. The\n\t\t empty string never shows: NcBreadcrumb prints `name` only for\n\t\t crumbs without an icon slot. -->\n\t\t<NcBreadcrumb\n\t\t\tv-for=\"(crumb, index) in crumbs\"\n\t\t\t:key=\"`${index}-${crumb.label ?? crumb.icon}`\"\n\t\t\t:name=\"crumb.label ?? ''\"\n\t\t\t:to=\"isCurrent(index) ? undefined : crumb.to\"\n\t\t\t:href=\"isCurrent(index) ? undefined : crumb.href\"\n\t\t\t:aria-current=\"isCurrent(index) ? 'page' : undefined\"\n\t\t\t:data-testid=\"`cn-breadcrumbs-crumb-${index}`\">\n\t\t\t<template v-if=\"crumb.icon\" #icon>\n\t\t\t\t<CnIcon :name=\"crumb.icon\" :size=\"20\" />\n\t\t\t</template>\n\t\t</NcBreadcrumb>\n\t</NcBreadcrumbs>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcBreadcrumb, NcBreadcrumbs } from '@nextcloud/vue'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnBreadcrumbs — declarative breadcrumb trail wrapping NcBreadcrumbs.\n *\n * Feed it the whole trail as one `crumbs` array — first entry is the root\n * (typically a Home crumb), last entry is the CURRENT location. Every crumb\n * but the last links via its `to` (router) or `href` (anchor); the last one\n * is rendered unlinked with `aria-current=\"page\"`, per the WAI-ARIA\n * breadcrumb pattern.\n *\n * ```vue\n * <CnBreadcrumbs\n * :crumbs=\"[\n * { icon: 'Home', to: { name: 'SecretList' } },\n * { label: 'Team vault', to: { name: 'SecretListFolder', params: { folderId: '7' } } },\n * { label: 'Production' },\n * ]\" />\n * ```\n */\nexport default {\n\tname: 'CnBreadcrumbs',\n\n\tcomponents: {\n\t\tNcBreadcrumbs,\n\t\tNcBreadcrumb,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The trail, root first, current location LAST. Each crumb:\n\t\t * `{ label, to?, href?, icon? }` — `label` is the visible (already\n\t\t * translated) text, `to` a vue-router target, `href` a plain URL\n\t\t * (use one or the other; both are ignored on the last crumb), and\n\t\t * `icon` a PascalCase MDI name resolved via the CnIcon registry\n\t\t * (register it with `registerIcons()` at boot).\n\t\t *\n\t\t * @type {Array<{label: string, to?: (object|string), href?: string, icon?: string}>}\n\t\t */\n\t\tcrumbs: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (value) => value.every((crumb) => crumb && typeof crumb === 'object'\n\t\t\t\t&& (typeof crumb.label === 'string' || typeof crumb.icon === 'string')),\n\t\t},\n\n\t\t/**\n\t\t * Accessible name of the breadcrumb nav landmark. Defaults to the\n\t\t * lib's translation of \"Breadcrumbs\"; override per surface when a\n\t\t * page carries more than one trail.\n\t\t */\n\t\tariaLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Breadcrumbs'),\n\t\t},\n\n\t\t/**\n\t\t * Draw the FIRST crumb's label as text. NcBreadcrumbs gives the root\n\t\t * crumb a home icon and prints its `label` only as the accessible\n\t\t * name, so \"All cases\" read as a house. Off by default, which keeps\n\t\t * the home icon. A first crumb with its own `icon` keeps that icon.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text\n\t\t */\n\t\trootText: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the crumb at `index` is the trail's last entry — the\n\t\t * current location, which renders unlinked with aria-current.\n\t\t *\n\t\t * @param {number} index Crumb position in `crumbs`.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCurrent(index) {\n\t\t\treturn index === this.crumbs.length - 1\n\t\t},\n\t},\n}\n</script>\n"],"names":[],"mappings":";;;;;;;AAkCQ,EAAA,OAAA,CAAA,MAAA,CAAA,MAAM,CAAC,MAAM,GAAA,CAAA;;;AACjB,QAAA,YAAS,EAAE,MAAS,CAAA,SAAA;QACrB,QAAQ,EAAE,MAAA,CAAA,QAAA,GAAA,EAAA,GAAA,SAAA;AACX,QAAA,aAAW,EAAC,gBAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -9,6 +9,9 @@ import { safeHref } from '../../utils/safeHref.js';
|
|
|
9
9
|
import { formatValue } from '../../utils/schema.js';
|
|
10
10
|
import __sfc_main from '../CnStatusBadge/CnStatusBadge.vue.js';
|
|
11
11
|
|
|
12
|
+
/** A uuid in any version, the shape OpenRegister ids take. */
|
|
13
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
16
|
* Module-level set of column keys already warned about for a
|
|
14
17
|
* `widget:"link"` declaration with no resolvable target — guarantees
|
|
@@ -315,13 +318,20 @@ var script = {
|
|
|
315
318
|
*
|
|
316
319
|
* @return {string}
|
|
317
320
|
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
321
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-small-opt-ins-from-the-decidiq-and-learniq-lanes
|
|
318
322
|
*/
|
|
319
323
|
dateLabel() {
|
|
320
324
|
const date = parseDateValue(this.value);
|
|
321
325
|
if (date === null) {
|
|
322
326
|
return ''
|
|
323
327
|
}
|
|
324
|
-
|
|
328
|
+
// `widgetProps.showTime: true` adds the time of day ("5 Oct 2026,
|
|
329
|
+
// 08:30"); `widgetProps.timeOnly: true` shows just the time
|
|
330
|
+
// ("08:30"), for a timetable row. Default: the date alone.
|
|
331
|
+
const props = this.widgetProps || {};
|
|
332
|
+
const options = props.timeOnly === true
|
|
333
|
+
? { hour: '2-digit', minute: '2-digit' }
|
|
334
|
+
: { day: 'numeric', month: 'short', year: 'numeric', ...(props.showTime === true ? { hour: '2-digit', minute: '2-digit' } : {}) };
|
|
325
335
|
let locale;
|
|
326
336
|
try {
|
|
327
337
|
locale = getCanonicalLocale();
|
|
@@ -570,6 +580,27 @@ var script = {
|
|
|
570
580
|
return undefined
|
|
571
581
|
},
|
|
572
582
|
|
|
583
|
+
/**
|
|
584
|
+
* Whether the cell draws a uuid, and so takes the monospace uuid
|
|
585
|
+
* style. The property's `format: "uuid"` alone used to decide this,
|
|
586
|
+
* so a reference that a formatter or a widget had resolved to its
|
|
587
|
+
* name was still drawn as code. Now the value has to be a uuid AND
|
|
588
|
+
* nothing resolves it: a resolving widget or formatter, or a value
|
|
589
|
+
* that is not a uuid, renders in the normal text style.
|
|
590
|
+
*
|
|
591
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-resolved-reference-is-not-drawn-as-a-uuid
|
|
592
|
+
* @return {boolean}
|
|
593
|
+
*/
|
|
594
|
+
showsUuid() {
|
|
595
|
+
if (this.property?.format !== 'uuid') {
|
|
596
|
+
return false
|
|
597
|
+
}
|
|
598
|
+
if (this.widgetComponent || this.widget || this.hasFormatter) {
|
|
599
|
+
return false
|
|
600
|
+
}
|
|
601
|
+
return typeof this.value === 'string' && UUID_PATTERN.test(this.value)
|
|
602
|
+
},
|
|
603
|
+
|
|
573
604
|
cellClass() {
|
|
574
605
|
const classes = [];
|
|
575
606
|
if (this.propertyType === 'boolean') {
|
|
@@ -581,7 +612,7 @@ var script = {
|
|
|
581
612
|
if (this.property?.format === 'date-time' || this.property?.format === 'date') {
|
|
582
613
|
classes.push('cn-cell-renderer--date');
|
|
583
614
|
}
|
|
584
|
-
if (this.
|
|
615
|
+
if (this.showsUuid) {
|
|
585
616
|
classes.push('cn-cell-renderer--uuid');
|
|
586
617
|
}
|
|
587
618
|
if (this.propertyType === 'integer' || this.propertyType === 'number') {
|