@conduction/nextcloud-vue 2.100.0 → 2.102.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/CnActionsBar/CnActionsBar.vue2.js +22 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +8 -6
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +29 -4
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +9 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +22 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +26 -16
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +70 -6
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +105 -74
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js +9 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js +139 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js +76 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +160 -2
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +206 -94
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesReview.js +61 -0
- package/dist/esm/components/CnFilesBrowser/filesReview.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +9 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +279 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +122 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/index.js +29 -0
- package/dist/esm/components/CnGroupedCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +320 -74
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +323 -213
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +30 -3
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +15 -0
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue.js +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js +25 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +14 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +14 -6
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +10 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +6 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +91 -4
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +63 -38
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue.js +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +106 -4
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +70 -25
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +9 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js +175 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js +77 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +35 -2
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +4 -0
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +2 -23
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +176 -20
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useEndpointSource.js +28 -6
- package/dist/esm/composables/useEndpointSource.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +41 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +39 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +939 -376
- package/dist/esm/utils/builtInFormatters.js +24 -4
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/languageMapText.js +50 -0
- package/dist/esm/utils/languageMapText.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +3 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/mapList.js +71 -0
- package/dist/esm/utils/mapList.js.map +1 -0
- package/dist/esm/utils/rowClassRules.js +43 -0
- package/dist/esm/utils/rowClassRules.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +19 -2
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +9681 -7136
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +939 -376
- package/l10n/en.json +41 -1
- package/l10n/nl.json +39 -1
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +1 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +31 -4
- package/src/components/CnChartWidget/CnChartWidget.md +3 -2
- package/src/components/CnChartWidget/CnChartWidget.vue +42 -4
- package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
- package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
- package/src/components/CnDashboardPage/CnDashboardPage.vue +110 -21
- package/src/components/CnExportsWidget/CnExportsWidget.vue +233 -0
- package/src/components/CnExportsWidget/index.js +7 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +324 -0
- package/src/components/CnFilesBrowser/filesReview.js +57 -0
- package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +433 -0
- package/src/components/CnGroupedCardsWidget/index.js +28 -0
- package/src/components/CnIndexPage/CnIndexPage.md +5 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +313 -9
- package/src/components/CnIndexPage/useNamedSource.js +30 -3
- package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
- package/src/components/CnMenuWidget/CnMenuWidget.vue +25 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +29 -1
- package/src/components/CnObjectSidebar/CnFilesTab.vue +15 -0
- package/src/components/CnOrderedListField/CnOrderedListField.vue +151 -5
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +1 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +159 -5
- package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +272 -0
- package/src/components/CnSavedViewAlertWidget/index.js +7 -0
- package/src/components/CnSetupWizard/CnSetupWizard.vue +35 -2
- package/src/components/CnWidgetGrid/builtInWidgets.js +4 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +2 -23
- package/src/components/index.js +3 -0
- package/src/composables/indexSources.js +175 -19
- package/src/composables/useEndpointSource.js +28 -6
- package/src/css/index-page.css +117 -0
- package/src/css/look-board-index.css +10 -0
- package/src/css/look-board.css +44 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +85 -3
- package/src/utils/builtInFormatters.js +24 -4
- package/src/utils/languageMapText.js +46 -0
- package/src/utils/libraryWidgetKeys.js +3 -0
- package/src/utils/mapList.js +67 -0
- package/src/utils/rowClassRules.js +39 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +19 -2
|
@@ -42,12 +42,13 @@
|
|
|
42
42
|
group ends the description line ("Week 41 · alle teams ·
|
|
43
43
|
[Deze week | Dit kwartaal | Dit jaar]"). -->
|
|
44
44
|
<div
|
|
45
|
-
v-if="datePeriodInDescription"
|
|
45
|
+
v-if="datePeriodInDescription || descriptionPageFilters.length > 0"
|
|
46
46
|
class="cn-dashboard-page__description cn-dashboard-page__description--with-period"
|
|
47
47
|
data-testid="cn-dashboard-page-subtitle">
|
|
48
48
|
<span v-if="subtitleText">{{ subtitleText }}</span>
|
|
49
49
|
<span v-if="subtitleText" aria-hidden="true">·</span>
|
|
50
50
|
<CnSegmentedControl
|
|
51
|
+
v-if="datePeriodInDescription"
|
|
51
52
|
size="compact"
|
|
52
53
|
mode="toggle"
|
|
53
54
|
:options="segmentedOptions"
|
|
@@ -55,6 +56,20 @@
|
|
|
55
56
|
:ariaLabel="datePeriodLabel"
|
|
56
57
|
data-testid="cn-dashboard-page-date-segmented"
|
|
57
58
|
@update:modelValue="onSegmentPick" />
|
|
59
|
+
<!-- A page filter with `control: "segmented"` and
|
|
60
|
+
`placement: "description"` (DqProcesanalyse
|
|
61
|
+
"Gemiddeld | Mediaan"): a segmented group on the
|
|
62
|
+
description line that writes the workspace context. -->
|
|
63
|
+
<CnSegmentedControl
|
|
64
|
+
v-for="pf in descriptionPageFilters"
|
|
65
|
+
:key="pf.key"
|
|
66
|
+
size="compact"
|
|
67
|
+
mode="toggle"
|
|
68
|
+
:options="pageFilterSegments(pf)"
|
|
69
|
+
:modelValue="workspaceContext[pf.key]"
|
|
70
|
+
:ariaLabel="pf.label ? effectiveTranslate(pf.label) : pf.key"
|
|
71
|
+
:data-testid="'cn-page-filter-' + pf.key"
|
|
72
|
+
@update:modelValue="onPageFilterChange(pf, $event)" />
|
|
58
73
|
</div>
|
|
59
74
|
<p v-else-if="subtitleText" class="cn-dashboard-page__description" data-testid="cn-dashboard-page-subtitle">
|
|
60
75
|
{{ subtitleText }}
|
|
@@ -96,7 +111,7 @@
|
|
|
96
111
|
<Pencil v-if="!isEditing" :size="20" />
|
|
97
112
|
<Check v-else :size="20" />
|
|
98
113
|
</template>
|
|
99
|
-
{{ isEditing ?
|
|
114
|
+
{{ isEditing ? shownDoneLabel : shownEditLabel }}
|
|
100
115
|
</NcButton>
|
|
101
116
|
<!-- With `userLayout`, edit mode offers a way back to the
|
|
102
117
|
manifest: the page's own grid and the chosen view return
|
|
@@ -316,23 +331,33 @@
|
|
|
316
331
|
`@page.<key>` / `@workspace.<key>` token (e.g. an endpoint KPI's URL
|
|
317
332
|
interpolating a chosen period). -->
|
|
318
333
|
<div
|
|
319
|
-
v-if="
|
|
334
|
+
v-if="rowPageFilters.length > 0"
|
|
320
335
|
class="cn-dashboard-page__page-filters"
|
|
321
336
|
data-testid="cn-dashboard-page-page-filters">
|
|
322
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
:modelValue="
|
|
329
|
-
:
|
|
330
|
-
:clearable="false"
|
|
331
|
-
:inputLabel="pf.label || pf.key"
|
|
332
|
-
label="label"
|
|
337
|
+
<template v-for="pf in rowPageFilters" :key="pf.key">
|
|
338
|
+
<CnSegmentedControl
|
|
339
|
+
v-if="pf.control === 'segmented'"
|
|
340
|
+
size="compact"
|
|
341
|
+
mode="toggle"
|
|
342
|
+
:options="pageFilterSegments(pf)"
|
|
343
|
+
:modelValue="workspaceContext[pf.key]"
|
|
344
|
+
:ariaLabel="pf.label ? effectiveTranslate(pf.label) : pf.key"
|
|
333
345
|
:data-testid="'cn-page-filter-' + pf.key"
|
|
334
346
|
@update:modelValue="onPageFilterChange(pf, $event)" />
|
|
335
|
-
|
|
347
|
+
<label
|
|
348
|
+
v-else
|
|
349
|
+
class="cn-dashboard-page__page-filter">
|
|
350
|
+
<span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ pf.label }}</span>
|
|
351
|
+
<NcSelect
|
|
352
|
+
:modelValue="selectedPageFilterOption(pf)"
|
|
353
|
+
:options="pf.options || []"
|
|
354
|
+
:clearable="false"
|
|
355
|
+
:inputLabel="pf.label || pf.key"
|
|
356
|
+
label="label"
|
|
357
|
+
:data-testid="'cn-page-filter-' + pf.key"
|
|
358
|
+
@update:modelValue="onPageFilterChange(pf, $event)" />
|
|
359
|
+
</label>
|
|
360
|
+
</template>
|
|
336
361
|
</div>
|
|
337
362
|
|
|
338
363
|
<!-- Declarative in-body sections, `placement: "before-grid"` — host-app
|
|
@@ -1080,6 +1105,7 @@ const CHART_PROP_KEYS = [
|
|
|
1080
1105
|
'footnote',
|
|
1081
1106
|
'highlight',
|
|
1082
1107
|
'cellSource',
|
|
1108
|
+
'emptyCellLabel',
|
|
1083
1109
|
]
|
|
1084
1110
|
|
|
1085
1111
|
/**
|
|
@@ -1678,13 +1704,18 @@ export default {
|
|
|
1678
1704
|
* read it via a `@page.<key>` / `@workspace.<key>` token — e.g. a period
|
|
1679
1705
|
* selector that every endpoint-bound KPI's URL interpolates.
|
|
1680
1706
|
*
|
|
1681
|
-
* Each entry: `{ key, label?, type?: 'select',
|
|
1682
|
-
* default? }`. `key` is the workspace-context
|
|
1683
|
-
* `default` (or the first option) seeds it on
|
|
1684
|
-
*
|
|
1685
|
-
*
|
|
1707
|
+
* Each entry: `{ key, label?, type?: 'select', control?, placement?,
|
|
1708
|
+
* options: [{ value, label }], default? }`. `key` is the workspace-context
|
|
1709
|
+
* key written on change; `default` (or the first option) seeds it on
|
|
1710
|
+
* mount. The control is a select, or with `control: 'segmented'` a
|
|
1711
|
+
* compact segmented group; `placement: 'description'` puts a segmented
|
|
1712
|
+
* group on the header's description line, after the period group
|
|
1713
|
+
* (DqProcesanalyse "Gemiddeld | Mediaan"). An endpoint widget can read
|
|
1714
|
+
* the value in its `responsePath` too (`phaseDays.@workspace.measure`).
|
|
1715
|
+
* Empty (the default) renders no controls and leaves the context
|
|
1716
|
+
* untouched.
|
|
1686
1717
|
*
|
|
1687
|
-
* @type {Array<{key: string, label?: string, type?: string, options: Array<{value: (string|number), label: string}>, default?: (string|number)}>}
|
|
1718
|
+
* @type {Array<{key: string, label?: string, type?: string, control?: ('select'|'segmented'), placement?: ('row'|'description'), options: Array<{value: (string|number), label: string}>, default?: (string|number)}>}
|
|
1688
1719
|
*/
|
|
1689
1720
|
pageFilters: {
|
|
1690
1721
|
type: Array,
|
|
@@ -2343,6 +2374,52 @@ export default {
|
|
|
2343
2374
|
&& this.dateRange.placement === 'description' && !this.hasCustomPreset
|
|
2344
2375
|
},
|
|
2345
2376
|
|
|
2377
|
+
/**
|
|
2378
|
+
* The page filters drawn as a segmented group on the description
|
|
2379
|
+
* line (`control: "segmented"`, `placement: "description"`).
|
|
2380
|
+
* Without a header row they fall back to the filter row.
|
|
2381
|
+
*
|
|
2382
|
+
* @spec openspec/changes/dashboard-board-termijnen/specs/dashboard-page/spec.md#requirement-a-page-filter-as-a-segmented-group-on-the-description-line
|
|
2383
|
+
* @return {Array<object>}
|
|
2384
|
+
*/
|
|
2385
|
+
descriptionPageFilters() {
|
|
2386
|
+
if (!this.showHeader) {
|
|
2387
|
+
return []
|
|
2388
|
+
}
|
|
2389
|
+
return (this.pageFilters || []).filter((pf) => pf && pf.key && pf.control === 'segmented' && pf.placement === 'description')
|
|
2390
|
+
},
|
|
2391
|
+
|
|
2392
|
+
/**
|
|
2393
|
+
* The page filters drawn in the filter row under the header: every
|
|
2394
|
+
* filter the description line does not draw.
|
|
2395
|
+
*
|
|
2396
|
+
* @spec openspec/changes/dashboard-board-termijnen/specs/dashboard-page/spec.md#requirement-a-page-filter-as-a-segmented-group-on-the-description-line
|
|
2397
|
+
* @return {Array<object>}
|
|
2398
|
+
*/
|
|
2399
|
+
rowPageFilters() {
|
|
2400
|
+
const inDescription = this.descriptionPageFilters
|
|
2401
|
+
return (this.pageFilters || []).filter((pf) => pf && pf.key && !inDescription.includes(pf))
|
|
2402
|
+
},
|
|
2403
|
+
|
|
2404
|
+
/**
|
|
2405
|
+
* The edit button's label through the app's translate: a manifest's
|
|
2406
|
+
* `editLabel` ("Edit layout", DqDashboard) reads in the user's language.
|
|
2407
|
+
*
|
|
2408
|
+
* @return {string}
|
|
2409
|
+
*/
|
|
2410
|
+
shownEditLabel() {
|
|
2411
|
+
return this.effectiveTranslate(this.editLabel)
|
|
2412
|
+
},
|
|
2413
|
+
|
|
2414
|
+
/**
|
|
2415
|
+
* The done button's label through the app's translate.
|
|
2416
|
+
*
|
|
2417
|
+
* @return {string}
|
|
2418
|
+
*/
|
|
2419
|
+
shownDoneLabel() {
|
|
2420
|
+
return this.effectiveTranslate(this.doneLabel)
|
|
2421
|
+
},
|
|
2422
|
+
|
|
2346
2423
|
/**
|
|
2347
2424
|
* The preset list passed to the picker. Falls back to the
|
|
2348
2425
|
* `DEFAULT_DATE_RANGE_PRESETS` constant when the consumer
|
|
@@ -2795,6 +2872,18 @@ export default {
|
|
|
2795
2872
|
return (pf.options || []).find((o) => o.value === current) || null
|
|
2796
2873
|
},
|
|
2797
2874
|
|
|
2875
|
+
/**
|
|
2876
|
+
* A page filter's options as segments, labels through the host
|
|
2877
|
+
* translate.
|
|
2878
|
+
*
|
|
2879
|
+
* @spec openspec/changes/dashboard-board-termijnen/specs/dashboard-page/spec.md#requirement-a-page-filter-as-a-segmented-group-on-the-description-line
|
|
2880
|
+
* @param {object} pf The page-filter descriptor.
|
|
2881
|
+
* @return {Array<{value: (string|number), label: string}>}
|
|
2882
|
+
*/
|
|
2883
|
+
pageFilterSegments(pf) {
|
|
2884
|
+
return (pf.options || []).map((o) => ({ value: o.value, label: o.label ? this.effectiveTranslate(o.label) : String(o.value) }))
|
|
2885
|
+
},
|
|
2886
|
+
|
|
2798
2887
|
/**
|
|
2799
2888
|
* Write a page filter's new selection into the reactive workspace
|
|
2800
2889
|
* context so every `@page.<key>` / `@workspace.<key>` token re-resolves.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<div class="cn-exports-widget" data-testid="cn-exports-widget">
|
|
7
|
+
<p v-if="loading && runs.length === 0" class="cn-exports-widget__muted">
|
|
8
|
+
{{ t('nextcloud-vue', 'Loading …') }}
|
|
9
|
+
</p>
|
|
10
|
+
<p v-else-if="failed" class="cn-exports-widget__muted" data-testid="cn-exports-error">
|
|
11
|
+
{{ t('nextcloud-vue', 'Your exports could not be loaded.') }}
|
|
12
|
+
</p>
|
|
13
|
+
<p v-else-if="runs.length === 0" class="cn-exports-widget__muted" data-testid="cn-exports-empty">
|
|
14
|
+
{{ emptyText }}
|
|
15
|
+
</p>
|
|
16
|
+
<ul v-else class="cn-exports-widget__list">
|
|
17
|
+
<li v-for="run in shownRuns"
|
|
18
|
+
:key="run.key"
|
|
19
|
+
class="cn-exports-widget__row"
|
|
20
|
+
data-testid="cn-exports-row">
|
|
21
|
+
<span class="cn-exports-widget__text">
|
|
22
|
+
<span class="cn-exports-widget__name">{{ run.filename }}</span>
|
|
23
|
+
<span class="cn-exports-widget__meta">{{ run.meta }}</span>
|
|
24
|
+
</span>
|
|
25
|
+
<span v-if="run.expiresToday" class="cn-exports-widget__tag" data-testid="cn-exports-expires-today">
|
|
26
|
+
{{ t('nextcloud-vue', 'Expires today') }}
|
|
27
|
+
</span>
|
|
28
|
+
<NcButton v-else-if="run.href"
|
|
29
|
+
:href="run.href"
|
|
30
|
+
data-testid="cn-exports-download">
|
|
31
|
+
{{ t('nextcloud-vue', 'Download') }}
|
|
32
|
+
</NcButton>
|
|
33
|
+
</li>
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script>
|
|
39
|
+
import { getCurrentUser } from '@nextcloud/auth'
|
|
40
|
+
import axios from '@nextcloud/axios'
|
|
41
|
+
import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
42
|
+
import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
|
|
43
|
+
import { NcButton } from '@nextcloud/vue'
|
|
44
|
+
import { formatBoardDate } from '../../utils/boardDate.js'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* CnExportsWidget — the reader's own exports (DqZoekresultaten "Uw exports"):
|
|
48
|
+
* each file with when it was made, how often it was downloaded and when it
|
|
49
|
+
* expires, a Download button while the file is there, and "Expires today" on
|
|
50
|
+
* the day it goes.
|
|
51
|
+
*
|
|
52
|
+
* Reads OpenRegister's export runs (`GET /apps/openregister/api/exports`)
|
|
53
|
+
* with the reader as the actor, so an administrator sees their own here too.
|
|
54
|
+
* `content.register` and `content.schema` narrow it to one list's exports.
|
|
55
|
+
* A run whose file was swept, or that never produced one, is left out.
|
|
56
|
+
*
|
|
57
|
+
* Example (manifest, in an index page's `sidePanel`):
|
|
58
|
+
* ```json
|
|
59
|
+
* { "id": "exports", "type": "exports", "title": "Your exports", "content": { "register": "dossiq", "schema": "case", "limit": 5 } }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export default {
|
|
63
|
+
name: 'CnExportsWidget',
|
|
64
|
+
|
|
65
|
+
components: { NcButton },
|
|
66
|
+
|
|
67
|
+
inject: {
|
|
68
|
+
/** Host translate function provided by CnAppRoot. */
|
|
69
|
+
cnTranslate: { default: () => (key) => key },
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
props: {
|
|
73
|
+
/** `{ register?, schema?, limit?, emptyText? }`; `limit` defaults to 5. */
|
|
74
|
+
content: {
|
|
75
|
+
type: Object,
|
|
76
|
+
default: () => ({}),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
data() {
|
|
81
|
+
return { loading: false, failed: false, runs: [], now: new Date() }
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
computed: {
|
|
85
|
+
/** @return {number} How many exports to show. */
|
|
86
|
+
limit() {
|
|
87
|
+
const limit = Number(this.content && this.content.limit)
|
|
88
|
+
return Number.isInteger(limit) && limit > 0 ? limit : 5
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/** @return {Array<object>} The exports to draw, newest first. */
|
|
92
|
+
shownRuns() {
|
|
93
|
+
return this.runs
|
|
94
|
+
.filter((run) => run && run.filename && run.fileId !== null && run.fileId !== undefined && run.status !== 'expired')
|
|
95
|
+
.slice(0, this.limit)
|
|
96
|
+
.map((run) => this.view(run))
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/** @return {string} The empty line. */
|
|
100
|
+
emptyText() {
|
|
101
|
+
const own = this.content && this.content.emptyText
|
|
102
|
+
return own ? this.cnTranslate(own) : t('nextcloud-vue', 'You have no exports here.')
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
mounted() {
|
|
107
|
+
this.load()
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
methods: {
|
|
111
|
+
t,
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Read the reader's export runs.
|
|
115
|
+
*
|
|
116
|
+
* @return {Promise<void>}
|
|
117
|
+
*/
|
|
118
|
+
async load() {
|
|
119
|
+
const user = getCurrentUser()
|
|
120
|
+
if (!user || !user.uid) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
this.loading = true
|
|
124
|
+
this.failed = false
|
|
125
|
+
try {
|
|
126
|
+
const params = { actor: user.uid, limit: Math.max(this.limit * 2, 10) }
|
|
127
|
+
for (const key of ['register', 'schema']) {
|
|
128
|
+
if (this.content && typeof this.content[key] === 'string' && this.content[key] !== '') {
|
|
129
|
+
params[key] = this.content[key]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const response = await axios.get(generateUrl('/apps/openregister/api/exports'), { params })
|
|
133
|
+
const results = response && response.data && Array.isArray(response.data.results) ? response.data.results : []
|
|
134
|
+
this.runs = [...results].sort((a, b) => String(b.producedAt || b.created || '').localeCompare(String(a.producedAt || a.created || '')))
|
|
135
|
+
} catch {
|
|
136
|
+
this.failed = true
|
|
137
|
+
this.runs = []
|
|
138
|
+
} finally {
|
|
139
|
+
this.loading = false
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* One export as the list draws it.
|
|
145
|
+
*
|
|
146
|
+
* @param {object} run An export run.
|
|
147
|
+
* @return {{key: string, filename: string, meta: string, href: string, expiresToday: boolean}}
|
|
148
|
+
*/
|
|
149
|
+
view(run) {
|
|
150
|
+
const made = run.producedAt || run.created
|
|
151
|
+
const parts = []
|
|
152
|
+
if (made) {
|
|
153
|
+
parts.push(t('nextcloud-vue', 'Made {date}', { date: formatBoardDate(made, this.now) }))
|
|
154
|
+
}
|
|
155
|
+
if (typeof run.downloadCount === 'number') {
|
|
156
|
+
parts.push(n('nextcloud-vue', 'downloaded %n time', 'downloaded %n times', run.downloadCount))
|
|
157
|
+
}
|
|
158
|
+
if (run.expiresAt) {
|
|
159
|
+
parts.push(t('nextcloud-vue', 'expires {date}', { date: formatBoardDate(run.expiresAt, this.now) }))
|
|
160
|
+
}
|
|
161
|
+
const user = getCurrentUser()
|
|
162
|
+
const href = run.filePath && user && user.uid
|
|
163
|
+
? generateRemoteUrl(`dav/files/${encodeURIComponent(user.uid)}`) + String(run.filePath).split('/').map(encodeURIComponent).join('/')
|
|
164
|
+
: ''
|
|
165
|
+
return {
|
|
166
|
+
key: String(run.uuid || run.id),
|
|
167
|
+
filename: run.filename,
|
|
168
|
+
meta: parts.join(' · '),
|
|
169
|
+
href,
|
|
170
|
+
expiresToday: Boolean(run.expiresAt) && new Date(run.expiresAt).toDateString() === this.now.toDateString(),
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<style scoped>
|
|
178
|
+
.cn-exports-widget__list {
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
margin: 0;
|
|
182
|
+
padding: 0;
|
|
183
|
+
list-style: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.cn-exports-widget__row {
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
gap: 12px;
|
|
190
|
+
padding: 10px 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.cn-exports-widget__row + .cn-exports-widget__row {
|
|
194
|
+
border-top: 1px solid var(--color-border);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.cn-exports-widget__text {
|
|
198
|
+
display: flex;
|
|
199
|
+
flex: 1;
|
|
200
|
+
flex-direction: column;
|
|
201
|
+
gap: 2px;
|
|
202
|
+
min-width: 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.cn-exports-widget__name {
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
font-size: 14px;
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
text-overflow: ellipsis;
|
|
210
|
+
white-space: nowrap;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.cn-exports-widget__meta,
|
|
214
|
+
.cn-exports-widget__muted {
|
|
215
|
+
color: var(--color-text-maxcontrast);
|
|
216
|
+
font-size: 13px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.cn-exports-widget__muted {
|
|
220
|
+
margin: 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.cn-exports-widget__tag {
|
|
224
|
+
flex: none;
|
|
225
|
+
padding: 3px 10px;
|
|
226
|
+
border-radius: 12px;
|
|
227
|
+
background: var(--color-warning-light, color-mix(in srgb, var(--color-warning) 18%, transparent));
|
|
228
|
+
color: var(--color-warning-text, var(--color-main-text));
|
|
229
|
+
font-size: 13px;
|
|
230
|
+
font-weight: 600;
|
|
231
|
+
white-space: nowrap;
|
|
232
|
+
}
|
|
233
|
+
</style>
|