@conduction/nextcloud-vue 2.56.0-beta.11 → 2.56.0-beta.13
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 +154 -4
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +555 -480
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +9 -3
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.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 +187 -19
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +215 -135
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFkResolveCell/CnFkResolveCell.vue2.js +3 -1
- package/dist/esm/components/CnFkResolveCell/CnFkResolveCell.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +144 -4
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +157 -7
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +23 -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/CnSegmentedControl/CnSegmentedControl.vue2.js +11 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +2 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.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 +5 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +5 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/mixins/pageViews.js +400 -0
- package/dist/esm/mixins/pageViews.js.map +1 -0
- package/dist/esm/nextcloud-vue.css +513 -81
- package/dist/esm/store/useObjectStore.js +118 -33
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +25 -2
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +27 -3
- package/dist/esm/utils/columnFilters.js.map +1 -1
- package/dist/esm/utils/schema.js +40 -2
- package/dist/esm/utils/schema.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 +8858 -6566
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +513 -81
- package/l10n/en.json +5 -0
- package/l10n/nl.json +5 -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 +237 -12
- package/src/components/CnDataTable/CnColumnFilterPopover.vue +11 -5
- package/src/components/CnDataTable/CnDataTable.vue +45 -1
- package/src/components/CnDetailPage/CnDetailPage.md +8 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +263 -7
- package/src/components/CnFkResolveCell/CnFkResolveCell.vue +3 -1
- package/src/components/CnFormDialog/CnFormDialog.vue +144 -4
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +206 -9
- package/src/components/CnIndexPage/CnIndexPage.vue +32 -2
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +7 -1
- package/src/components/CnSegmentedControl/CnSegmentedControl.md +1 -1
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +12 -0
- 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 +37 -4
- package/src/css/kpi-card.css +71 -1
- package/src/css/table.css +18 -6
- package/src/mixins/pageViews.js +396 -0
- package/src/schemas/app-manifest-v2.schema.json +162 -3
- package/src/store/useObjectStore.js +118 -33
- package/src/utils/actionsDispatcher.js +25 -2
- package/src/utils/columnFilters.js +29 -2
- package/src/utils/schema.js +40 -2
- package/src/utils/schemaRefSlug.js +19 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -203,3 +203,7 @@ Static-manifest apps use `useAppManifest('myapp', bundledManifest)`, which fetch
|
|
|
203
203
|
| `softDependencyNotices` | Boolean | `false` | **Deprecated.** Restores the in-shell orange notices, one per unresolved optional dependency. Off by default: they stacked one per optional leaf and pushed the app's own content below the fold, for an audience who cannot act on them. Mount `CnLeafDependencySettings` in admin settings instead. Slated for removal. |
|
|
204
204
|
|
|
205
205
|
`unresolvedSoftDependencies` is unchanged and still exposed, so an app rendering its own surface from it keeps working.
|
|
206
|
+
|
|
207
|
+
### Without a navigation (`hideMenu`)
|
|
208
|
+
|
|
209
|
+
`hideMenu` (default `false`) renders neither the default CnAppNav nor the `#menu` slot, so the content starts at the left edge.
|
|
@@ -199,7 +199,13 @@
|
|
|
199
199
|
<!-- @binding {boolean} isOwner Whether the user owns this app (ADR-079 §3). -->
|
|
200
200
|
<!-- @binding {boolean} isAdmin Whether the user administers the instance; gates the Admin-settings link. -->
|
|
201
201
|
<!-- @binding {string} appId The app id used to build the Admin-settings target. -->
|
|
202
|
-
|
|
202
|
+
<!-- `hideMenu` renders no navigation at all, so the content starts
|
|
203
|
+
at the left edge. An EMPTY #menu override cannot do that: an
|
|
204
|
+
empty slot falls back to the default CnAppNav (ensureValidVNode),
|
|
205
|
+
which is why launchpad passed a hidden empty span. -->
|
|
206
|
+
<slot
|
|
207
|
+
v-if="!hideMenu"
|
|
208
|
+
name="menu"
|
|
203
209
|
:manifest="menuManifest"
|
|
204
210
|
:permissions="permissions"
|
|
205
211
|
:isOwner="isOwner"
|
|
@@ -700,6 +706,7 @@ import { BUILT_IN_FORMATTERS } from '../../utils/builtInFormatters.js'
|
|
|
700
706
|
import { DEFAULT_FORGE, resolveForge } from '../../utils/forge.js'
|
|
701
707
|
import { BUILT_IN_KB_PROVIDERS } from '../../utils/kbSearchProviders.js'
|
|
702
708
|
import { installModalStack, uninstallModalStack } from '../../utils/modalStack.js'
|
|
709
|
+
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
703
710
|
import { passesContextPredicates } from '../../utils/visibleIfContext.js'
|
|
704
711
|
|
|
705
712
|
/**
|
|
@@ -1049,6 +1056,13 @@ export default {
|
|
|
1049
1056
|
* the counts.
|
|
1050
1057
|
*/
|
|
1051
1058
|
cnMenuCounts: this.cnMenuCounts,
|
|
1059
|
+
/**
|
|
1060
|
+
* Reactive `{ [menuItemId]: number }` totals for menu entries
|
|
1061
|
+
* whose `count` is an object (`{ register, schema, filter? }`),
|
|
1062
|
+
* populated by `_hydrateMenuItemCounts()`. Read by
|
|
1063
|
+
* `CnAppNav.resolveCount()`.
|
|
1064
|
+
*/
|
|
1065
|
+
cnMenuItemCounts: this.cnMenuItemCounts,
|
|
1052
1066
|
}
|
|
1053
1067
|
},
|
|
1054
1068
|
|
|
@@ -1192,6 +1206,19 @@ export default {
|
|
|
1192
1206
|
required: true,
|
|
1193
1207
|
},
|
|
1194
1208
|
|
|
1209
|
+
/**
|
|
1210
|
+
* Render no app navigation at all: neither the default CnAppNav nor
|
|
1211
|
+
* the `#menu` slot, so the content starts at the left edge (a start
|
|
1212
|
+
* page without a menu). Off by default; the navigation renders as it
|
|
1213
|
+
* always has.
|
|
1214
|
+
*
|
|
1215
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-an-app-root-can-hide-its-menu
|
|
1216
|
+
*/
|
|
1217
|
+
hideMenu: {
|
|
1218
|
+
type: Boolean,
|
|
1219
|
+
default: false,
|
|
1220
|
+
},
|
|
1221
|
+
|
|
1195
1222
|
/**
|
|
1196
1223
|
* Human-readable name shown in the Nextcloud top bar. When set it
|
|
1197
1224
|
* overrides the technical `appId` so a virtual app shows its own name
|
|
@@ -1707,6 +1734,13 @@ export default {
|
|
|
1707
1734
|
* picked up by CnAppNav's reactive render.
|
|
1708
1735
|
*/
|
|
1709
1736
|
cnMenuCounts: reactive({}),
|
|
1737
|
+
/**
|
|
1738
|
+
* Reactive `{ [menuItemId]: number }` map of filtered totals, one
|
|
1739
|
+
* per menu entry whose `count` is an object. Provided to
|
|
1740
|
+
* descendants as `cnMenuItemCounts`. Populated by
|
|
1741
|
+
* `_hydrateMenuItemCounts()` at mount.
|
|
1742
|
+
*/
|
|
1743
|
+
cnMenuItemCounts: reactive({}),
|
|
1710
1744
|
/**
|
|
1711
1745
|
* Open state of the host NcAppSettingsDialog. Toggled
|
|
1712
1746
|
* to `true` by the provided `cnOpenUserSettings()`
|
|
@@ -2925,6 +2959,7 @@ export default {
|
|
|
2925
2959
|
this._validateRegistry()
|
|
2926
2960
|
this._warnCustomComponentsDeprecation()
|
|
2927
2961
|
this._hydrateMenuCounts()
|
|
2962
|
+
this._hydrateMenuItemCounts()
|
|
2928
2963
|
return
|
|
2929
2964
|
}
|
|
2930
2965
|
|
|
@@ -2953,6 +2988,7 @@ export default {
|
|
|
2953
2988
|
this._validateRegistry()
|
|
2954
2989
|
this._warnCustomComponentsDeprecation()
|
|
2955
2990
|
this._hydrateMenuCounts()
|
|
2991
|
+
this._hydrateMenuItemCounts()
|
|
2956
2992
|
},
|
|
2957
2993
|
|
|
2958
2994
|
beforeUnmount() {
|
|
@@ -3609,6 +3645,63 @@ export default {
|
|
|
3609
3645
|
})
|
|
3610
3646
|
},
|
|
3611
3647
|
|
|
3648
|
+
/**
|
|
3649
|
+
* Fetch the total of every menu entry (top level and children) whose
|
|
3650
|
+
* `count` is an object `{ register, schema, filter? }`: one
|
|
3651
|
+
* `GET /api/objects/{register}/{schema}?_limit=1` per entry with the
|
|
3652
|
+
* filter's tokens (`@me`, `@today`, ...) resolved at fetch time, the
|
|
3653
|
+
* response `total` written to `cnMenuItemCounts[item.id]`. The
|
|
3654
|
+
* request goes straight through axios rather than the object store,
|
|
3655
|
+
* so the index page's whole-schema total in the store is left alone.
|
|
3656
|
+
* Failures leave the badge unrendered.
|
|
3657
|
+
*
|
|
3658
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-menu-entry-counts-a-filtered-list
|
|
3659
|
+
* @return {void}
|
|
3660
|
+
* @private
|
|
3661
|
+
*/
|
|
3662
|
+
_hydrateMenuItemCounts() {
|
|
3663
|
+
const collect = (items) => {
|
|
3664
|
+
const out = []
|
|
3665
|
+
for (const item of items ?? []) {
|
|
3666
|
+
const count = item?.count
|
|
3667
|
+
if (count && typeof count === 'object' && typeof item.id === 'string' && count.register && count.schema) {
|
|
3668
|
+
out.push(item)
|
|
3669
|
+
}
|
|
3670
|
+
if (Array.isArray(item?.children)) {
|
|
3671
|
+
out.push(...collect(item.children))
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
return out
|
|
3675
|
+
}
|
|
3676
|
+
for (const item of collect(this.manifest?.menu)) {
|
|
3677
|
+
this._fetchMenuItemCount(item)
|
|
3678
|
+
}
|
|
3679
|
+
},
|
|
3680
|
+
|
|
3681
|
+
/**
|
|
3682
|
+
* One filtered-count request for a menu entry; see
|
|
3683
|
+
* `_hydrateMenuItemCounts`.
|
|
3684
|
+
*
|
|
3685
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-menu-entry-counts-a-filtered-list
|
|
3686
|
+
* @param {{ id: string, count: { register: string, schema: string, filter?: object } }} item The menu entry.
|
|
3687
|
+
* @return {Promise<void>}
|
|
3688
|
+
* @private
|
|
3689
|
+
*/
|
|
3690
|
+
async _fetchMenuItemCount(item) {
|
|
3691
|
+
try {
|
|
3692
|
+
const { register, schema, filter } = item.count
|
|
3693
|
+
const resolved = resolveFilterTokens(filter && typeof filter === 'object' ? filter : {}, {})
|
|
3694
|
+
const url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(register)}/${encodeURIComponent(schema)}`)
|
|
3695
|
+
const { data } = await axios.get(url, { params: { _limit: 1, ...resolved } })
|
|
3696
|
+
const total = data?.total
|
|
3697
|
+
if (typeof total === 'number' && total >= 0) {
|
|
3698
|
+
this.cnMenuItemCounts[item.id] = total
|
|
3699
|
+
}
|
|
3700
|
+
} catch {
|
|
3701
|
+
// Non-fatal: the badge stays unrendered.
|
|
3702
|
+
}
|
|
3703
|
+
},
|
|
3704
|
+
|
|
3612
3705
|
/**
|
|
3613
3706
|
* Hydrate all menu counts with a single `POST /api/objects/counts`
|
|
3614
3707
|
* (OpenRegister batched-counts endpoint). Distributes each returned
|
|
@@ -69,12 +69,27 @@ export default {
|
|
|
69
69
|
components: { CnDetailCard, NcLoadingIcon },
|
|
70
70
|
|
|
71
71
|
props: {
|
|
72
|
-
/** OpenRegister register id (slug or uuid). */
|
|
73
|
-
register: { type: String,
|
|
74
|
-
/** OpenRegister schema id (slug or uuid). */
|
|
75
|
-
schema: { type: String,
|
|
76
|
-
/** Parent object id. */
|
|
77
|
-
objectId: { type: String,
|
|
72
|
+
/** OpenRegister register id (slug or uuid). Optional for `scope: "app"`. */
|
|
73
|
+
register: { type: String, default: '' },
|
|
74
|
+
/** OpenRegister schema id (slug or uuid). Optional for `scope: "app"`. */
|
|
75
|
+
schema: { type: String, default: '' },
|
|
76
|
+
/** Parent object id. Required for `scope: "object"` (the default); unused for `scope: "app"`. */
|
|
77
|
+
objectId: { type: String, default: '' },
|
|
78
|
+
/**
|
|
79
|
+
* What the card lists. `object` (the default) is one object's trail
|
|
80
|
+
* and needs `register`, `schema` and `objectId`. `app` is the
|
|
81
|
+
* app-wide feed: the entries of every object the caller may read
|
|
82
|
+
* (OpenRegister's `/audit-trails/readable`), narrowed to `register`
|
|
83
|
+
* and `schema` when those are set.
|
|
84
|
+
*
|
|
85
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
86
|
+
*/
|
|
87
|
+
scope: {
|
|
88
|
+
type: String,
|
|
89
|
+
default: 'object',
|
|
90
|
+
validator: (value) => ['object', 'app'].includes(value),
|
|
91
|
+
},
|
|
92
|
+
|
|
78
93
|
/** Rendering surface — passed for AD-19 surface fallback consumers. */
|
|
79
94
|
surface: {
|
|
80
95
|
type: String,
|
|
@@ -116,13 +131,38 @@ export default {
|
|
|
116
131
|
displayedEntries() {
|
|
117
132
|
return this.entries.slice(0, this.maxDisplay)
|
|
118
133
|
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The path the card reads (prefixed at fetch time), or '' when it has nothing to read: the
|
|
137
|
+
* object's own trail, or the app-wide readable feed narrowed to the
|
|
138
|
+
* register and schema when set.
|
|
139
|
+
*
|
|
140
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
141
|
+
* @return {string}
|
|
142
|
+
*/
|
|
143
|
+
fetchUrl() {
|
|
144
|
+
const params = new URLSearchParams({ limit: String(this.maxDisplay) })
|
|
145
|
+
if (this.scope === 'app') {
|
|
146
|
+
if (this.register) {
|
|
147
|
+
params.set('register', this.register)
|
|
148
|
+
}
|
|
149
|
+
if (this.schema) {
|
|
150
|
+
params.set('schema', this.schema)
|
|
151
|
+
}
|
|
152
|
+
return `${this.apiBase}/audit-trails/readable?${params.toString()}`
|
|
153
|
+
}
|
|
154
|
+
if (!this.register || !this.schema || !this.objectId) {
|
|
155
|
+
return ''
|
|
156
|
+
}
|
|
157
|
+
return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trail?${params.toString()}`
|
|
158
|
+
},
|
|
119
159
|
},
|
|
120
160
|
|
|
121
161
|
watch: {
|
|
122
|
-
|
|
162
|
+
fetchUrl: {
|
|
123
163
|
immediate: true,
|
|
124
|
-
handler(
|
|
125
|
-
if (
|
|
164
|
+
handler(url) {
|
|
165
|
+
if (url) {
|
|
126
166
|
this.fetchEntries()
|
|
127
167
|
}
|
|
128
168
|
},
|
|
@@ -130,20 +170,25 @@ export default {
|
|
|
130
170
|
},
|
|
131
171
|
|
|
132
172
|
methods: {
|
|
173
|
+
/**
|
|
174
|
+
* Read the entries from `fetchUrl`. The readable feed answers
|
|
175
|
+
* `{ rows, nextCursor }`, the object trail `{ results }`.
|
|
176
|
+
*
|
|
177
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
178
|
+
* @return {Promise<void>}
|
|
179
|
+
*/
|
|
133
180
|
async fetchEntries() {
|
|
134
|
-
|
|
181
|
+
const url = this.fetchUrl
|
|
182
|
+
if (!url) {
|
|
135
183
|
return
|
|
136
184
|
}
|
|
137
185
|
this.loading = true
|
|
138
186
|
try {
|
|
139
|
-
const
|
|
140
|
-
const response = await fetch(
|
|
141
|
-
prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/audit-trail?${params.toString()}`),
|
|
142
|
-
{ headers: buildHeaders() },
|
|
143
|
-
)
|
|
187
|
+
const response = await fetch(prefixUrl(url), { headers: buildHeaders() })
|
|
144
188
|
if (response.ok) {
|
|
145
189
|
const data = await response.json()
|
|
146
|
-
|
|
190
|
+
const list = data.results || data.rows || data
|
|
191
|
+
this.entries = Array.isArray(list) ? list : []
|
|
147
192
|
} else {
|
|
148
193
|
this.entries = []
|
|
149
194
|
}
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
7
|
<CnAuditTrailCard
|
|
8
|
-
v-if="resolvedRegister && resolvedSchema && resolvedObjectId"
|
|
8
|
+
v-if="resolvedScope === 'app' || (resolvedRegister && resolvedSchema && resolvedObjectId)"
|
|
9
9
|
:register="resolvedRegister"
|
|
10
10
|
:schema="resolvedSchema"
|
|
11
11
|
:objectId="resolvedObjectId"
|
|
12
|
+
:scope="resolvedScope"
|
|
12
13
|
:title="resolvedTitle"
|
|
13
14
|
:maxDisplay="resolvedMaxDisplay" />
|
|
14
15
|
</template>
|
|
@@ -70,6 +71,15 @@ export default {
|
|
|
70
71
|
title: { type: String, default: '' },
|
|
71
72
|
/** Maximum audit rows to render (0 falls back to the card default). */
|
|
72
73
|
maxDisplay: { type: Number, default: 0 },
|
|
74
|
+
/**
|
|
75
|
+
* `app` lists the app-wide feed (every object the caller may read,
|
|
76
|
+
* narrowed to `register` / `schema` when set) and needs no object;
|
|
77
|
+
* `object` (the default) is one object's trail. Also read from
|
|
78
|
+
* `content.scope`.
|
|
79
|
+
*
|
|
80
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
81
|
+
*/
|
|
82
|
+
scope: { type: String, default: '' },
|
|
73
83
|
/** Stored widget content blob (CnDashboardPage registry branch): `{ title, maxDisplay, register?, schema?, objectId? }`. */
|
|
74
84
|
content: { type: Object, default: () => ({}) },
|
|
75
85
|
},
|
|
@@ -115,6 +125,17 @@ export default {
|
|
|
115
125
|
return typeof s === 'string' ? s : (s && (s.slug || s.name || s.id)) || ''
|
|
116
126
|
},
|
|
117
127
|
|
|
128
|
+
/**
|
|
129
|
+
* The feed scope: explicit prop → content → `object`.
|
|
130
|
+
*
|
|
131
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-the-audit-trail-widget-reads-the-app-wide-feed
|
|
132
|
+
* @return {('object'|'app')}
|
|
133
|
+
*/
|
|
134
|
+
resolvedScope() {
|
|
135
|
+
const s = this.scope || this.content.scope
|
|
136
|
+
return s === 'app' ? 'app' : 'object'
|
|
137
|
+
},
|
|
138
|
+
|
|
118
139
|
/**
|
|
119
140
|
* The card title: explicit prop → content → '' (card default).
|
|
120
141
|
*
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
<NcBreadcrumbs
|
|
19
19
|
v-if="crumbs.length > 0"
|
|
20
20
|
:aria-label="ariaLabel"
|
|
21
|
+
:rootIcon="rootText ? '' : undefined"
|
|
21
22
|
data-testid="cn-breadcrumbs">
|
|
22
23
|
<!-- `name` falls back to '' because NcBreadcrumb declares it a REQUIRED
|
|
23
24
|
String and warns per render on the label-less icon-only crumb. The
|
|
@@ -97,6 +98,19 @@ export default {
|
|
|
97
98
|
type: String,
|
|
98
99
|
default: () => t('nextcloud-vue', 'Breadcrumbs'),
|
|
99
100
|
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Draw the FIRST crumb's label as text. NcBreadcrumbs gives the root
|
|
104
|
+
* crumb a home icon and prints its `label` only as the accessible
|
|
105
|
+
* name, so "All cases" read as a house. Off by default, which keeps
|
|
106
|
+
* the home icon. A first crumb with its own `icon` keeps that icon.
|
|
107
|
+
*
|
|
108
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text
|
|
109
|
+
*/
|
|
110
|
+
rootText: {
|
|
111
|
+
type: Boolean,
|
|
112
|
+
default: false,
|
|
113
|
+
},
|
|
100
114
|
},
|
|
101
115
|
|
|
102
116
|
methods: {
|
|
@@ -177,6 +177,9 @@ import { safeHref } from '../../utils/safeHref.js'
|
|
|
177
177
|
import { formatValue } from '../../utils/schema.js'
|
|
178
178
|
import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
179
179
|
|
|
180
|
+
/** A uuid in any version, the shape OpenRegister ids take. */
|
|
181
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
182
|
+
|
|
180
183
|
/**
|
|
181
184
|
* Module-level set of column keys already warned about for a
|
|
182
185
|
* `widget:"link"` declaration with no resolvable target — guarantees
|
|
@@ -483,13 +486,20 @@ export default {
|
|
|
483
486
|
*
|
|
484
487
|
* @return {string}
|
|
485
488
|
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells
|
|
489
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-small-opt-ins-from-the-decidiq-and-learniq-lanes
|
|
486
490
|
*/
|
|
487
491
|
dateLabel() {
|
|
488
492
|
const date = parseDateValue(this.value)
|
|
489
493
|
if (date === null) {
|
|
490
494
|
return ''
|
|
491
495
|
}
|
|
492
|
-
|
|
496
|
+
// `widgetProps.showTime: true` adds the time of day ("5 Oct 2026,
|
|
497
|
+
// 08:30"); `widgetProps.timeOnly: true` shows just the time
|
|
498
|
+
// ("08:30"), for a timetable row. Default: the date alone.
|
|
499
|
+
const props = this.widgetProps || {}
|
|
500
|
+
const options = props.timeOnly === true
|
|
501
|
+
? { hour: '2-digit', minute: '2-digit' }
|
|
502
|
+
: { day: 'numeric', month: 'short', year: 'numeric', ...(props.showTime === true ? { hour: '2-digit', minute: '2-digit' } : {}) }
|
|
493
503
|
let locale
|
|
494
504
|
try {
|
|
495
505
|
locale = getCanonicalLocale()
|
|
@@ -738,6 +748,27 @@ export default {
|
|
|
738
748
|
return undefined
|
|
739
749
|
},
|
|
740
750
|
|
|
751
|
+
/**
|
|
752
|
+
* Whether the cell draws a uuid, and so takes the monospace uuid
|
|
753
|
+
* style. The property's `format: "uuid"` alone used to decide this,
|
|
754
|
+
* so a reference that a formatter or a widget had resolved to its
|
|
755
|
+
* name was still drawn as code. Now the value has to be a uuid AND
|
|
756
|
+
* nothing resolves it: a resolving widget or formatter, or a value
|
|
757
|
+
* that is not a uuid, renders in the normal text style.
|
|
758
|
+
*
|
|
759
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-resolved-reference-is-not-drawn-as-a-uuid
|
|
760
|
+
* @return {boolean}
|
|
761
|
+
*/
|
|
762
|
+
showsUuid() {
|
|
763
|
+
if (this.property?.format !== 'uuid') {
|
|
764
|
+
return false
|
|
765
|
+
}
|
|
766
|
+
if (this.widgetComponent || this.widget || this.hasFormatter) {
|
|
767
|
+
return false
|
|
768
|
+
}
|
|
769
|
+
return typeof this.value === 'string' && UUID_PATTERN.test(this.value)
|
|
770
|
+
},
|
|
771
|
+
|
|
741
772
|
cellClass() {
|
|
742
773
|
const classes = []
|
|
743
774
|
if (this.propertyType === 'boolean') {
|
|
@@ -749,7 +780,7 @@ export default {
|
|
|
749
780
|
if (this.property?.format === 'date-time' || this.property?.format === 'date') {
|
|
750
781
|
classes.push('cn-cell-renderer--date')
|
|
751
782
|
}
|
|
752
|
-
if (this.
|
|
783
|
+
if (this.showsUuid) {
|
|
753
784
|
classes.push('cn-cell-renderer--uuid')
|
|
754
785
|
}
|
|
755
786
|
if (this.propertyType === 'integer' || this.propertyType === 'number') {
|