@conduction/nextcloud-vue 2.56.0-beta.31 → 2.56.0-beta.32
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/CnBannerWidget/CnBannerWidget.vue2.js +55 -4
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +51 -15
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +13 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +63 -52
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +25 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +3 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +20 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +7 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +77 -3
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +282 -69
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +0 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +18 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +34 -25
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +47 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +16 -14
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
- package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +4 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +1 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +1 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +843 -4
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.js.map +1 -0
- package/dist/esm/utils/fetchFilterCounts.js +40 -5
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +7 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectWrite.js +59 -0
- package/dist/esm/utils/objectWrite.js.map +1 -0
- package/dist/esm/utils/widgetDispatch.js +1 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +10510 -6830
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +843 -4
- package/l10n/en.json +1 -0
- package/l10n/nl.json +1 -0
- package/package.json +1 -1
- package/src/components/CnBannerWidget/CnBannerWidget.vue +93 -13
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +43 -3
- package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
- package/src/components/CnChipsFieldWidget/index.js +25 -0
- package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
- package/src/components/CnChoicePickerWidget/index.js +26 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +38 -2
- package/src/components/CnDataTable/CnDataTable.vue +2 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +45 -2
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -3
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +265 -4
- package/src/components/CnIndexPage/CnIndexPage.vue +0 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
- package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +43 -1
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
- package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
- package/src/components/CnSettingsFormWidget/index.js +26 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +87 -6
- package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
- package/src/components/CnSwitchRowsWidget/index.js +25 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +82 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
- package/src/components/CnToggleTableWidget/index.js +26 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +28 -1
- package/src/components/index.js +7 -0
- package/src/css/look-board-detail.css +36 -2
- package/src/css/look-board-index.css +23 -0
- package/src/css/look-board.css +72 -0
- package/src/index.js +7 -0
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +7 -0
- package/src/utils/objectWrite.js +55 -0
- package/src/utils/widgetDispatch.js +1 -1
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div class="cn-saved-views-widget" data-testid="cn-saved-views-widget">
|
|
8
|
+
<p v-if="error" class="cn-saved-views-widget__note" data-testid="cn-saved-views-error">
|
|
9
|
+
{{ tr(content.errorText) || t('nextcloud-vue', 'Your saved views could not be loaded.') }}
|
|
10
|
+
</p>
|
|
11
|
+
<div v-else-if="loading" class="cn-saved-views-widget__loading">
|
|
12
|
+
<NcLoadingIcon :size="20" :name="t('nextcloud-vue', 'Loading …')" />
|
|
13
|
+
</div>
|
|
14
|
+
<p v-else-if="items.length === 0" class="cn-saved-views-widget__note" data-testid="cn-saved-views-empty">
|
|
15
|
+
{{ tr(content.emptyText) || t('nextcloud-vue', 'You have no saved views yet.') }}
|
|
16
|
+
</p>
|
|
17
|
+
<ul v-else class="cn-saved-views-widget__list">
|
|
18
|
+
<li v-for="item in items" :key="item.key" class="cn-saved-views-widget__item">
|
|
19
|
+
<component
|
|
20
|
+
:is="item.to ? 'router-link' : 'span'"
|
|
21
|
+
class="cn-saved-views-widget__link"
|
|
22
|
+
:to="item.to || undefined"
|
|
23
|
+
data-testid="cn-saved-views-link">
|
|
24
|
+
{{ item.label }}
|
|
25
|
+
</component>
|
|
26
|
+
</li>
|
|
27
|
+
</ul>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script>
|
|
32
|
+
import { getCurrentUser } from '@nextcloud/auth'
|
|
33
|
+
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
34
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
35
|
+
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
36
|
+
import { useSavedViewsApi } from '../../composables/useSavedViewsApi.js'
|
|
37
|
+
import { fetchListTotal } from '../../utils/fetchFilterCounts.js'
|
|
38
|
+
import { buildRouteQueryFromViewState, extractViewState, isOwnView, viewMatchesScope } from '../../utils/savedViewHelpers.js'
|
|
39
|
+
|
|
40
|
+
const PAGE_REFRESH_CHANNEL = 'cn:page:refresh'
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* CnSavedViewsWidget — the reader's own saved views as a list of links
|
|
44
|
+
* (`saved-views`), the "Opgeslagen weergaven" card of a board dashboard.
|
|
45
|
+
*
|
|
46
|
+
* `saved-view` draws the rows of ONE view that the manifest names by id. This
|
|
47
|
+
* one lists the views themselves and has no id to configure: it asks the Views
|
|
48
|
+
* API for the signed-in user's views (their own, or with `own: false` also the
|
|
49
|
+
* ones shared with them), keeps those that belong to the configured source,
|
|
50
|
+
* and links each to the index page `route` with the view's filters, search and
|
|
51
|
+
* sort in the query, which the index page applies on load. A view saved or
|
|
52
|
+
* deleted later shows up on the next load or page refresh.
|
|
53
|
+
*
|
|
54
|
+
* ```js
|
|
55
|
+
* { type: 'saved-views', content: { register: 'pipelinq', schema: 'ticket', route: 'Tickets', showCounts: true } }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export default {
|
|
59
|
+
name: 'CnSavedViewsWidget',
|
|
60
|
+
|
|
61
|
+
components: { NcLoadingIcon },
|
|
62
|
+
|
|
63
|
+
inject: {
|
|
64
|
+
/** The host app's translate function. */
|
|
65
|
+
cnTranslate: { default: () => (key) => key },
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
props: {
|
|
69
|
+
/**
|
|
70
|
+
* The configuration: `{ register, schema, scope?, route, own?, showCounts?,
|
|
71
|
+
* limit?, emptyText?, errorText? }`. `register` and `schema` (or `scope`,
|
|
72
|
+
* the index page's `savedViewsScope`) pick the views of one list page;
|
|
73
|
+
* `route` is the name of that index page's route; `own` (default true)
|
|
74
|
+
* keeps the signed-in user's views only; `showCounts` adds how many records
|
|
75
|
+
* each view matches; `limit` caps the list (default 10).
|
|
76
|
+
*
|
|
77
|
+
* @type {{register?: string, schema?: string, scope?: string, route?: (string|object), own?: boolean, showCounts?: boolean, limit?: number, emptyText?: string, errorText?: string}}
|
|
78
|
+
*/
|
|
79
|
+
content: {
|
|
80
|
+
type: Object,
|
|
81
|
+
default: () => ({}),
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Injected views API, for tests and hosts that stub the transport.
|
|
86
|
+
*
|
|
87
|
+
* @type {{fetchViews: Function}|null}
|
|
88
|
+
*/
|
|
89
|
+
api: {
|
|
90
|
+
type: Object,
|
|
91
|
+
default: null,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
data() {
|
|
96
|
+
return {
|
|
97
|
+
views: [],
|
|
98
|
+
counts: {},
|
|
99
|
+
loading: true,
|
|
100
|
+
error: false,
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
computed: {
|
|
105
|
+
/**
|
|
106
|
+
* The views to show: the reader's own (unless `own: false`) that belong to
|
|
107
|
+
* the configured source, newest first, capped at `limit`.
|
|
108
|
+
*
|
|
109
|
+
* @return {Array<object>} The View API objects.
|
|
110
|
+
*/
|
|
111
|
+
shown() {
|
|
112
|
+
const c = this.content || {}
|
|
113
|
+
const uid = this.userId()
|
|
114
|
+
const page = { scope: c.scope, register: c.register, schema: c.schema }
|
|
115
|
+
const limit = Number(c.limit)
|
|
116
|
+
return this.views
|
|
117
|
+
.filter((view) => (c.own === false ? true : isOwnView(view, uid)))
|
|
118
|
+
.filter((view) => viewMatchesScope(view, page))
|
|
119
|
+
.slice(0, limit > 0 ? Math.floor(limit) : 10)
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The rows: a label (the view's name and its count) and the location that
|
|
124
|
+
* applies the view on the index page.
|
|
125
|
+
*
|
|
126
|
+
* @return {Array<{key: string, label: string, to: (object|null)}>} The rows.
|
|
127
|
+
*/
|
|
128
|
+
items() {
|
|
129
|
+
const route = this.content?.route
|
|
130
|
+
return this.shown.map((view) => {
|
|
131
|
+
const count = this.counts[this.keyOf(view)]
|
|
132
|
+
const query = buildRouteQueryFromViewState(extractViewState(view))
|
|
133
|
+
const location = typeof route === 'string' && route !== '' ? { name: route, query } : (route && typeof route === 'object' && route.name ? { ...route, query } : null)
|
|
134
|
+
return {
|
|
135
|
+
key: this.keyOf(view),
|
|
136
|
+
label: Number.isFinite(count) ? `${view.name} (${count})` : String(view.name ?? ''),
|
|
137
|
+
to: location && this.$router ? location : null,
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
watch: {
|
|
144
|
+
'content.register': 'load',
|
|
145
|
+
'content.schema': 'load',
|
|
146
|
+
'content.scope': 'load',
|
|
147
|
+
'content.own': 'load',
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
created() {
|
|
151
|
+
this.load()
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
mounted() {
|
|
155
|
+
this.onRefresh = () => this.load()
|
|
156
|
+
subscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
beforeUnmount() {
|
|
160
|
+
unsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
methods: {
|
|
164
|
+
t,
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Translate a manifest string through the host app.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} text The text.
|
|
170
|
+
* @return {string} The translation, or '' for no text.
|
|
171
|
+
*/
|
|
172
|
+
tr(text) {
|
|
173
|
+
return typeof text === 'string' && text !== '' && typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : ''
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The signed-in user's id, or '' when it cannot be read.
|
|
178
|
+
*
|
|
179
|
+
* @return {string} The id.
|
|
180
|
+
*/
|
|
181
|
+
userId() {
|
|
182
|
+
try {
|
|
183
|
+
return getCurrentUser()?.uid || ''
|
|
184
|
+
} catch {
|
|
185
|
+
return ''
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* A view's key: its id, else its uuid.
|
|
191
|
+
*
|
|
192
|
+
* @param {object} view The View API object.
|
|
193
|
+
* @return {string} The key.
|
|
194
|
+
*/
|
|
195
|
+
keyOf(view) {
|
|
196
|
+
return String(view?.id ?? view?.uuid ?? view?.name ?? '')
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Read the views, and when asked how many records each matches. A failed
|
|
201
|
+
* read says so; a failed count only leaves that view without one.
|
|
202
|
+
*
|
|
203
|
+
* @return {Promise<void>}
|
|
204
|
+
* @spec openspec/changes/screens-own-saved-views/specs/own-saved-views/spec.md#requirement-a-dashboard-lists-the-readers-own-saved-views
|
|
205
|
+
*/
|
|
206
|
+
async load() {
|
|
207
|
+
this.loading = true
|
|
208
|
+
this.error = false
|
|
209
|
+
try {
|
|
210
|
+
const api = this.api || useSavedViewsApi()
|
|
211
|
+
this.views = await api.fetchViews()
|
|
212
|
+
} catch (e) {
|
|
213
|
+
// eslint-disable-next-line no-console
|
|
214
|
+
console.warn('[CnSavedViewsWidget] failed to load saved views:', e)
|
|
215
|
+
this.views = []
|
|
216
|
+
this.error = true
|
|
217
|
+
this.loading = false
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
this.loading = false
|
|
221
|
+
await this.loadCounts()
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* One list request per shown view (`_limit=1`, the `total`), with the
|
|
226
|
+
* view's filters.
|
|
227
|
+
*
|
|
228
|
+
* @return {Promise<void>}
|
|
229
|
+
*/
|
|
230
|
+
async loadCounts() {
|
|
231
|
+
const c = this.content || {}
|
|
232
|
+
if (c.showCounts !== true || !c.register || !c.schema) {
|
|
233
|
+
this.counts = {}
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
const next = {}
|
|
237
|
+
await Promise.all(this.shown.map(async (view) => {
|
|
238
|
+
try {
|
|
239
|
+
const total = await fetchListTotal(c.register, c.schema, extractViewState(view).filters, {})
|
|
240
|
+
if (total !== null) {
|
|
241
|
+
next[this.keyOf(view)] = total
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
// No count for this view.
|
|
245
|
+
}
|
|
246
|
+
}))
|
|
247
|
+
this.counts = next
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
</script>
|
|
252
|
+
|
|
253
|
+
<style scoped>
|
|
254
|
+
.cn-saved-views-widget__list {
|
|
255
|
+
display: flex;
|
|
256
|
+
flex-direction: column;
|
|
257
|
+
gap: 10px;
|
|
258
|
+
list-style: none;
|
|
259
|
+
margin: 0;
|
|
260
|
+
padding: 0;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.cn-saved-views-widget__link {
|
|
264
|
+
color: var(--color-primary-element);
|
|
265
|
+
display: inline-block;
|
|
266
|
+
font-size: 14px;
|
|
267
|
+
font-weight: 600;
|
|
268
|
+
min-height: 24px;
|
|
269
|
+
text-decoration: none;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.cn-saved-views-widget__link:hover,
|
|
273
|
+
.cn-saved-views-widget__link:focus-visible {
|
|
274
|
+
text-decoration: underline;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.cn-saved-views-widget__note {
|
|
278
|
+
color: var(--color-text-maxcontrast);
|
|
279
|
+
margin: 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.cn-saved-views-widget__loading {
|
|
283
|
+
display: flex;
|
|
284
|
+
justify-content: center;
|
|
285
|
+
padding: 8px 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* A phone: each link is a 44px touch target. */
|
|
289
|
+
@media (max-width: 640px) {
|
|
290
|
+
.cn-saved-views-widget__link {
|
|
291
|
+
align-items: center;
|
|
292
|
+
display: inline-flex;
|
|
293
|
+
min-height: 44px;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* CnSavedViewsWidget — self-registers the `saved-views` widget type (a list of
|
|
6
|
+
* the reader's own saved views, each a link into the index page that applies
|
|
7
|
+
* it) into the shared dashboardWidgetRegistry at module load.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import CnSavedViewsWidget from './CnSavedViewsWidget.vue'
|
|
11
|
+
import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'
|
|
12
|
+
|
|
13
|
+
registerDashboardWidget('saved-views', {
|
|
14
|
+
renderer: CnSavedViewsWidget,
|
|
15
|
+
form: null,
|
|
16
|
+
defaultContent: {
|
|
17
|
+
register: '',
|
|
18
|
+
schema: '',
|
|
19
|
+
route: '',
|
|
20
|
+
own: true,
|
|
21
|
+
limit: 10,
|
|
22
|
+
},
|
|
23
|
+
displayName: 'Saved views',
|
|
24
|
+
icon: 'ViewList',
|
|
25
|
+
surfaces: ['app-dashboard'],
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export { CnSavedViewsWidget }
|
|
29
|
+
export default CnSavedViewsWidget
|
|
@@ -367,4 +367,17 @@ export default {
|
|
|
367
367
|
border-color: transparent;
|
|
368
368
|
box-shadow: 0 1px 2px var(--color-box-shadow);
|
|
369
369
|
}
|
|
370
|
+
|
|
371
|
+
/* On a phone (screens-dashboard-phone) the compact group's segments are 44px
|
|
372
|
+
high touch targets, and the group wraps onto a second row instead of
|
|
373
|
+
running off the screen. */
|
|
374
|
+
@media (max-width: 640px) {
|
|
375
|
+
.cn-segmented-control.cn-segmented-control--compact {
|
|
376
|
+
flex-wrap: wrap;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.cn-segmented-control.cn-segmented-control--compact .cn-segmented-control__option {
|
|
380
|
+
min-height: 44px;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
370
383
|
</style>
|