@conduction/nextcloud-vue 2.104.0 → 2.105.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/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +93 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +6 -3
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +3 -3
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +59 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +2 -0
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +32 -29
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +73 -4
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +74 -27
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +54 -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/CnIndexPage/useSelfFetchList.js +3 -2
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js +9 -0
- package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js.map +1 -0
- package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js +88 -0
- package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js +39 -0
- package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +3 -3
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +16 -4
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +11 -0
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -4
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.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 +4 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +4 -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 +566 -295
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/nextcloudApps.js +45 -0
- package/dist/esm/utils/nextcloudApps.js.map +1 -0
- package/dist/esm/utils/scopeListLayout.js +9 -2
- package/dist/esm/utils/scopeListLayout.js.map +1 -1
- 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 +12 -4
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +3054 -2517
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +566 -295
- package/l10n/en.json +4 -1
- package/l10n/nl.json +4 -1
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -1
- package/src/components/CnChartWidget/CnChartWidget.md +2 -0
- package/src/components/CnChartWidget/CnChartWidget.vue +93 -1
- package/src/components/CnDashboardPage/CnDashboardPage.vue +9 -6
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +59 -1
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -1
- package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +4 -1
- package/src/components/CnFlowsPage/CnFlowBoard.vue +132 -8
- package/src/components/CnIndexPage/CnIndexPage.md +1 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +54 -1
- package/src/components/CnIndexPage/useSelfFetchList.js +3 -2
- package/src/components/CnNoticeWidget/CnNoticeWidget.vue +153 -0
- package/src/components/CnNoticeWidget/index.js +12 -0
- package/src/components/CnSettingsField/CnSettingsField.vue +4 -1
- package/src/components/CnSetupWizard/CnSetupWizard.vue +9 -1
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +15 -4
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/index.js +1 -0
- package/src/css/dialog.css +20 -2
- package/src/css/look-board-detail.css +77 -4
- package/src/css/look-board-index.css +36 -2
- package/src/css/look-board.css +97 -12
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +36 -3
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/nextcloudApps.js +50 -0
- package/src/utils/scopeListLayout.js +9 -2
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +12 -4
package/l10n/en.json
CHANGED
|
@@ -1724,7 +1724,10 @@
|
|
|
1724
1724
|
"No contacts linked": "No contacts linked",
|
|
1725
1725
|
"No relations yet": "No relations yet",
|
|
1726
1726
|
"Period": "Period",
|
|
1727
|
-
"On": "On"
|
|
1727
|
+
"On": "On",
|
|
1728
|
+
"Select a step on the canvas to see its settings.": "Select a step on the canvas to see its settings.",
|
|
1729
|
+
"Show runs": "Show runs",
|
|
1730
|
+
"Back to the step settings": "Back to the step settings"
|
|
1728
1731
|
},
|
|
1729
1732
|
"plurals": {
|
|
1730
1733
|
"{count} member": [
|
package/l10n/nl.json
CHANGED
|
@@ -1937,7 +1937,10 @@
|
|
|
1937
1937
|
"No contacts linked": "Geen contacten gekoppeld",
|
|
1938
1938
|
"No relations yet": "Nog geen relaties",
|
|
1939
1939
|
"Period": "Periode",
|
|
1940
|
-
"On": "Aan"
|
|
1940
|
+
"On": "Aan",
|
|
1941
|
+
"Select a step on the canvas to see its settings.": "Kies een stap op het canvas om de instellingen te zien.",
|
|
1942
|
+
"Show runs": "Uitvoeringen tonen",
|
|
1943
|
+
"Back to the step settings": "Terug naar de stapinstellingen"
|
|
1941
1944
|
},
|
|
1942
1945
|
"plurals": {
|
|
1943
1946
|
"{count} member": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduction/nextcloud-vue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.105.0",
|
|
4
4
|
"description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
|
|
5
5
|
"homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
<template #additional-items>
|
|
161
161
|
<!-- The board card: support as one fact row, the SLA address on the same line. -->
|
|
162
162
|
<div v-if="compactCard && showSupport && !supportAtEnd && !$slots.footer" class="cn-version-info__item" data-testid="cn-admin-settings-support-row">
|
|
163
|
-
<span class="cn-version-info__label">{{ supportTitle }}
|
|
163
|
+
<span class="cn-version-info__label">{{ supportTitle }}</span>
|
|
164
164
|
<span class="cn-version-info__value">
|
|
165
165
|
<a :href="`mailto:${supportEmail}`">{{ supportEmail }}</a><template v-if="slaEmail">, {{ slaInline }} <a :href="`mailto:${slaEmail}`">{{ slaEmail }}</a></template>
|
|
166
166
|
</span>
|
|
@@ -108,6 +108,8 @@ Fallback slot — shown when ApexCharts is not available:
|
|
|
108
108
|
| `legendPosition` | String | `''` | Legend placement override (`top`/`bottom`/`left`/`right`); empty keeps the automatic placement |
|
|
109
109
|
| `barMax` | Number | `null` | `bar-list`: the value a full track stands for (100 for percentages); null takes the largest value |
|
|
110
110
|
| `footnote` | String | `''` | A line under the chart, through the host's translate: under the rows of a `bar-list` or `matrix`, under the plot of a `line`, `area` or `bar` chart |
|
|
111
|
+
| `referenceLines` | Array | `[]` | Dashed horizontal reference lines on the value axis of a line, area or bar chart, such as a norm: `[{ value, label?, color? }]`. The label goes through the host's translate; the colour defaults to the theme's error colour (board DqDoorlooptijd). |
|
|
112
|
+
| `categoryFormat` | String | `''` | How date categories read on the x-axis: `month` ("jan") or `month-year` ("jan 2026"), in the user's language. Categories that are not dates are left as is. |
|
|
111
113
|
| `emptyCellLabel` | String | `''` | `matrix`: the text of a cell without a value ("geen"), through the host's translate. Empty leaves the cell blank |
|
|
112
114
|
| `highlight` | Object | `null` | `matrix`: the cell to outline, `{ row, column, columnKeys? }`, tokens such as `@object.behaviour` resolved |
|
|
113
115
|
| `cellSource` | Object | `null` | `matrix`: read the cells from records that each hold one cell (`{ register, schema, filter?, rowField, columnField, valueField, … }`), or with `cellsPath` from the cells inside one record, see [Bar list and matrix](#bar-list-and-matrix) |
|
|
@@ -220,6 +220,22 @@ function parseBucketKey(key) {
|
|
|
220
220
|
)
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
/**
|
|
224
|
+
* A theme colour by CSS variable name, as a literal: ApexCharts writes colours
|
|
225
|
+
* into SVG attributes, where `var()` does not resolve.
|
|
226
|
+
*
|
|
227
|
+
* @param {string} name The variable, e.g. `--color-error`.
|
|
228
|
+
* @param {string} fallback The colour when the variable is unset (or no DOM).
|
|
229
|
+
* @return {string} The colour.
|
|
230
|
+
*/
|
|
231
|
+
function themeColor(name, fallback) {
|
|
232
|
+
if (typeof document === 'undefined' || typeof getComputedStyle !== 'function') {
|
|
233
|
+
return fallback
|
|
234
|
+
}
|
|
235
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
|
|
236
|
+
return value || fallback
|
|
237
|
+
}
|
|
238
|
+
|
|
223
239
|
/** Event-bus channel CnWidgetWrapper's Refresh action broadcasts on. */
|
|
224
240
|
const REFRESH_BUS_CHANNEL = 'cn:widget:refresh'
|
|
225
241
|
|
|
@@ -385,6 +401,31 @@ export default {
|
|
|
385
401
|
default: '',
|
|
386
402
|
},
|
|
387
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Horizontal reference lines on the value axis of a line, area or bar
|
|
406
|
+
* chart, such as a norm: `[{ value, label?, color? }]`. Drawn dashed;
|
|
407
|
+
* the label goes through the host's translate. The colour defaults to
|
|
408
|
+
* the theme's error colour.
|
|
409
|
+
*
|
|
410
|
+
* @type {Array<{value: number, label?: string, color?: string}>}
|
|
411
|
+
*/
|
|
412
|
+
referenceLines: {
|
|
413
|
+
type: Array,
|
|
414
|
+
default: () => [],
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* How date categories read on the x-axis: `month` ("jan") or
|
|
419
|
+
* `month-year` ("jan 2026"), in the user's language. A category that
|
|
420
|
+
* is not a date is shown as is. Empty shows the categories unchanged.
|
|
421
|
+
*
|
|
422
|
+
* @type {''|'month'|'month-year'}
|
|
423
|
+
*/
|
|
424
|
+
categoryFormat: {
|
|
425
|
+
type: String,
|
|
426
|
+
default: '',
|
|
427
|
+
},
|
|
428
|
+
|
|
388
429
|
/**
|
|
389
430
|
* The `matrix` cell to outline, usually the record's on a detail page:
|
|
390
431
|
* `{ row, column, columnKeys? }`, where `row` and `column` take tokens
|
|
@@ -1505,6 +1546,51 @@ export default {
|
|
|
1505
1546
|
return this.footnote ? this.hostTranslate(this.footnote) : t('nextcloud-vue', 'Chart data')
|
|
1506
1547
|
},
|
|
1507
1548
|
|
|
1549
|
+
/**
|
|
1550
|
+
* The x-axis categories as shown: with `categoryFormat`, date
|
|
1551
|
+
* categories as short months in the user's language.
|
|
1552
|
+
*
|
|
1553
|
+
* @return {Array<string|number>} The categories.
|
|
1554
|
+
*/
|
|
1555
|
+
displayCategories() {
|
|
1556
|
+
const categories = Array.isArray(this.resolvedCategories) ? this.resolvedCategories : []
|
|
1557
|
+
if (this.categoryFormat !== 'month' && this.categoryFormat !== 'month-year') {
|
|
1558
|
+
return categories
|
|
1559
|
+
}
|
|
1560
|
+
const options = this.categoryFormat === 'month' ? { month: 'short' } : { month: 'short', year: 'numeric' }
|
|
1561
|
+
return categories.map((category) => {
|
|
1562
|
+
const date = typeof category === 'string' || typeof category === 'number' ? parseBucketKey(category) : null
|
|
1563
|
+
return date ? date.toLocaleDateString(userLocale(), options) : category
|
|
1564
|
+
})
|
|
1565
|
+
},
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* The `referenceLines` as ApexCharts annotations: dashed, labelled,
|
|
1569
|
+
* in the theme's error colour unless a line names its own.
|
|
1570
|
+
*
|
|
1571
|
+
* @return {Array<object>} The annotations, possibly empty.
|
|
1572
|
+
*/
|
|
1573
|
+
referenceAnnotations() {
|
|
1574
|
+
const lines = (Array.isArray(this.referenceLines) ? this.referenceLines : [])
|
|
1575
|
+
.filter((line) => line && Number.isFinite(Number(line.value)))
|
|
1576
|
+
if (lines.length === 0) {
|
|
1577
|
+
return []
|
|
1578
|
+
}
|
|
1579
|
+
const fallback = themeColor('--color-error', '#e04224')
|
|
1580
|
+
return lines.map((line) => {
|
|
1581
|
+
const color = line.color || fallback
|
|
1582
|
+
return {
|
|
1583
|
+
y: Number(line.value),
|
|
1584
|
+
x: Number(line.value),
|
|
1585
|
+
borderColor: color,
|
|
1586
|
+
strokeDashArray: 6,
|
|
1587
|
+
label: line.label
|
|
1588
|
+
? { text: this.hostTranslate(line.label), borderColor: color, style: { color: '#fff', background: color } }
|
|
1589
|
+
: { text: '' },
|
|
1590
|
+
}
|
|
1591
|
+
})
|
|
1592
|
+
},
|
|
1593
|
+
|
|
1508
1594
|
mergedOptions() {
|
|
1509
1595
|
const isPieType = ['pie', 'donut', 'radialBar'].includes(this.type)
|
|
1510
1596
|
|
|
@@ -1592,7 +1678,7 @@ export default {
|
|
|
1592
1678
|
if (!isPieType) {
|
|
1593
1679
|
if (this.resolvedCategories.length > 0) {
|
|
1594
1680
|
defaults.xaxis = {
|
|
1595
|
-
categories: this.
|
|
1681
|
+
categories: this.displayCategories,
|
|
1596
1682
|
labels: {
|
|
1597
1683
|
style: {
|
|
1598
1684
|
colors: 'var(--color-text-maxcontrast, #767676)',
|
|
@@ -1634,6 +1720,12 @@ export default {
|
|
|
1634
1720
|
}
|
|
1635
1721
|
}
|
|
1636
1722
|
|
|
1723
|
+
// Reference lines, such as a norm, on the value axis.
|
|
1724
|
+
if (!isPieType && this.referenceAnnotations.length > 0) {
|
|
1725
|
+
const axisKey = (this.type === 'bar' && this.horizontal) ? 'xaxis' : 'yaxis'
|
|
1726
|
+
defaults.annotations = { [axisKey]: this.referenceAnnotations }
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1637
1729
|
// Add labels for pie/donut
|
|
1638
1730
|
if (isPieType && this.resolvedLabels.length > 0) {
|
|
1639
1731
|
defaults.labels = this.resolvedLabels
|
|
@@ -347,12 +347,12 @@
|
|
|
347
347
|
<label
|
|
348
348
|
v-else
|
|
349
349
|
class="cn-dashboard-page__page-filter">
|
|
350
|
-
<span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ pf.label }}</span>
|
|
350
|
+
<span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ effectiveTranslate(pf.label) }}</span>
|
|
351
351
|
<NcSelect
|
|
352
352
|
:modelValue="selectedPageFilterOption(pf)"
|
|
353
|
-
:options="pf
|
|
353
|
+
:options="pageFilterSegments(pf)"
|
|
354
354
|
:clearable="false"
|
|
355
|
-
:inputLabel="pf.label
|
|
355
|
+
:inputLabel="pf.label ? effectiveTranslate(pf.label) : pf.key"
|
|
356
356
|
label="label"
|
|
357
357
|
:data-testid="'cn-page-filter-' + pf.key"
|
|
358
358
|
@update:modelValue="onPageFilterChange(pf, $event)" />
|
|
@@ -1106,6 +1106,9 @@ const CHART_PROP_KEYS = [
|
|
|
1106
1106
|
'highlight',
|
|
1107
1107
|
'cellSource',
|
|
1108
1108
|
'emptyCellLabel',
|
|
1109
|
+
// A norm line and month categories on a line chart (DqDoorlooptijd).
|
|
1110
|
+
'referenceLines',
|
|
1111
|
+
'categoryFormat',
|
|
1109
1112
|
]
|
|
1110
1113
|
|
|
1111
1114
|
/**
|
|
@@ -2886,12 +2889,12 @@ export default {
|
|
|
2886
2889
|
return null
|
|
2887
2890
|
}
|
|
2888
2891
|
const current = this.workspaceContext[pf.key]
|
|
2889
|
-
return (pf
|
|
2892
|
+
return this.pageFilterSegments(pf).find((o) => o.value === current) || null
|
|
2890
2893
|
},
|
|
2891
2894
|
|
|
2892
2895
|
/**
|
|
2893
|
-
* A page filter's options
|
|
2894
|
-
*
|
|
2896
|
+
* A page filter's options, labels through the host translate: the
|
|
2897
|
+
* segments of a segmented filter and the options of a select.
|
|
2895
2898
|
*
|
|
2896
2899
|
* @spec openspec/changes/dashboard-board-termijnen/specs/dashboard-page/spec.md#requirement-a-page-filter-as-a-segmented-group-on-the-description-line
|
|
2897
2900
|
* @param {object} pf The page-filter descriptor.
|
|
@@ -427,6 +427,7 @@ Four levels of action and the case surfaces around them. All opt in. See [the re
|
|
|
427
427
|
| `headerFields` | Array | `[]` | Fields shown as chips under the title (manifest `config.headerFields`). Each entry is a property key, or `{ key, format, labelField, colorField, warnWhenPast }`; `format` is `text` (default), `mono`, `badge`, `user` or `date`. A `$ref` value shows `labelField` of the referenced object and falls back to the raw id in mono; a badge maps `colorField` (`success`, `warning`, `error`, `info`, `neutral`) to a variant; `warnWhenPast` gives a past date the error variant. An empty value shows no chip, a page without the key shows no row. Each chip reads "Title: value" to a screen reader, and the row prints as plain text. |
|
|
428
428
|
| `sideColumn` | Array | `[]` | Widget definitions or widget ids, rendered as a column of cards beside the body. |
|
|
429
429
|
| `sideColumnFrom` | String | `''` | A field path, read like a `visibleWhen` field (e.g. `@context.caseType.sideCards`), whose list of ids picks the `sideColumn` entries this record shows and their order. Any other value keeps `sideColumn` as declared. |
|
|
430
|
+
| `tabsFrom` | String | `''` | A field path, read like a `visibleWhen` field, whose list of tab ids picks the tabs of the page's `tabs` widgets and their order for this record: `@context.caseType.tabs` reads the list a case type keeps (DqAanbesteding: Inschrijvingen, Publicaties, Bestanden, Historie). A tab's id is its `id`, else its `widgetId`, or the string entry. An empty or missing list keeps the declared tabs; edit mode shows them all. |
|
|
430
431
|
| `isAdmin` | Boolean \| null | `null` | Shows `adminOnly` header actions. `null` reads it from Nextcloud. |
|
|
431
432
|
|
|
432
433
|
## The record as a place
|
|
@@ -2567,6 +2567,22 @@ export default {
|
|
|
2567
2567
|
default: null,
|
|
2568
2568
|
},
|
|
2569
2569
|
|
|
2570
|
+
/**
|
|
2571
|
+
* A field path whose value picks the tabs of the page's `tabs` widgets
|
|
2572
|
+
* and their order for this record (manifest `config.tabsFrom`), read
|
|
2573
|
+
* like a `visibleWhen` field: `@context.caseType.tabs` reads the list a
|
|
2574
|
+
* case type keeps. A non-empty list of tab ids (a tab's `id`, else its
|
|
2575
|
+
* `widgetId`, or the string entry itself) keeps those tabs in that
|
|
2576
|
+
* order and leaves the rest out. Any other value keeps the declared
|
|
2577
|
+
* tabs. Edit mode shows them all.
|
|
2578
|
+
*
|
|
2579
|
+
* @spec openspec/changes/detail-tabs-from/specs/detail-page/spec.md#requirement-a-record-can-pick-its-tabs-and-their-order
|
|
2580
|
+
*/
|
|
2581
|
+
tabsFrom: {
|
|
2582
|
+
type: String,
|
|
2583
|
+
default: '',
|
|
2584
|
+
},
|
|
2585
|
+
|
|
2570
2586
|
/**
|
|
2571
2587
|
* A column of cards beside the body (manifest `config.sideColumn`),
|
|
2572
2588
|
* for the record's facts: deadline, requester, handler, links. Each
|
|
@@ -4551,7 +4567,7 @@ export default {
|
|
|
4551
4567
|
*/
|
|
4552
4568
|
bodyGridWidgets() {
|
|
4553
4569
|
if (this.hasGridLayout) {
|
|
4554
|
-
return this.widgets
|
|
4570
|
+
return this.pickTabs(this.widgets)
|
|
4555
4571
|
}
|
|
4556
4572
|
if (this.shouldRenderAutoBody) {
|
|
4557
4573
|
return this.autoBodyWidgets || []
|
|
@@ -4559,6 +4575,21 @@ export default {
|
|
|
4559
4575
|
return []
|
|
4560
4576
|
},
|
|
4561
4577
|
|
|
4578
|
+
/**
|
|
4579
|
+
* The tab ids `tabsFrom` reads off this record, or null when it reads
|
|
4580
|
+
* no non-empty list (or the body is being edited).
|
|
4581
|
+
*
|
|
4582
|
+
* @spec openspec/changes/detail-tabs-from/specs/detail-page/spec.md#requirement-a-record-can-pick-its-tabs-and-their-order
|
|
4583
|
+
* @return {Array<string>|null}
|
|
4584
|
+
*/
|
|
4585
|
+
pickedTabIds() {
|
|
4586
|
+
if (!this.tabsFrom || this.editingBody || !this.widgetVisibilityData) {
|
|
4587
|
+
return null
|
|
4588
|
+
}
|
|
4589
|
+
const ids = readVisibleWhenPath(this.widgetVisibilityData, this.tabsFrom)
|
|
4590
|
+
return Array.isArray(ids) && ids.length > 0 ? ids.map((id) => String(id)) : null
|
|
4591
|
+
},
|
|
4592
|
+
|
|
4562
4593
|
/**
|
|
4563
4594
|
* Whether the body renders as an adjustable widget grid (vs. the
|
|
4564
4595
|
* hand-authored default slot). True for explicit grid pages and for the
|
|
@@ -4977,6 +5008,33 @@ export default {
|
|
|
4977
5008
|
},
|
|
4978
5009
|
|
|
4979
5010
|
methods: {
|
|
5011
|
+
/**
|
|
5012
|
+
* The widget definitions with each `tabs` widget's tabs narrowed and
|
|
5013
|
+
* ordered by `pickedTabIds`; the definitions as given without a pick.
|
|
5014
|
+
* A new definition is returned for a changed widget; the declared one
|
|
5015
|
+
* is never mutated.
|
|
5016
|
+
*
|
|
5017
|
+
* @spec openspec/changes/detail-tabs-from/specs/detail-page/spec.md#requirement-a-record-can-pick-its-tabs-and-their-order
|
|
5018
|
+
* @param {Array<object>} widgets The widget definitions.
|
|
5019
|
+
* @return {Array<object>}
|
|
5020
|
+
*/
|
|
5021
|
+
pickTabs(widgets) {
|
|
5022
|
+
const ids = this.pickedTabIds
|
|
5023
|
+
if (!ids || !Array.isArray(widgets)) {
|
|
5024
|
+
return widgets
|
|
5025
|
+
}
|
|
5026
|
+
const idOf = (tab) => String(typeof tab === 'string' ? tab : (tab && (tab.id ?? tab.widgetId)) ?? '')
|
|
5027
|
+
return widgets.map((widget) => {
|
|
5028
|
+
if (!widget || widget.type !== 'tabs' || !Array.isArray(widget.content?.tabs)) {
|
|
5029
|
+
return widget
|
|
5030
|
+
}
|
|
5031
|
+
const tabs = ids
|
|
5032
|
+
.map((id) => widget.content.tabs.find((tab) => idOf(tab) === id))
|
|
5033
|
+
.filter(Boolean)
|
|
5034
|
+
return tabs.length > 0 ? { ...widget, content: { ...widget.content, tabs } } : widget
|
|
5035
|
+
})
|
|
5036
|
+
},
|
|
5037
|
+
|
|
4980
5038
|
/**
|
|
4981
5039
|
* The tag after an admin-only entry in the board look ("beheerder",
|
|
4982
5040
|
* DqZaakMeer), or null for any other entry or look. It is drawn by
|
|
@@ -460,7 +460,7 @@ export default {
|
|
|
460
460
|
* @return {object}
|
|
461
461
|
*/
|
|
462
462
|
boardOptions() {
|
|
463
|
-
const keys = ['layout', 'tabs', 'filter', 'statusLabels', 'next', 'issueLabels', 'subtitle', 'notice', 'noticeLink', 'summaries', 'documentationButton', 'suggestButton']
|
|
463
|
+
const keys = ['layout', 'tabs', 'filter', 'statusLabels', 'next', 'issueLabels', 'subtitle', 'notice', 'noticeLink', 'summaries', 'documentationButton', 'suggestButton', 'actionsMenu']
|
|
464
464
|
const options = {}
|
|
465
465
|
for (const key of keys) {
|
|
466
466
|
if (this.board && this.board[key] !== undefined && this.board[key] !== null) {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
{{ t('nextcloud-vue', 'Suggest feature') }}
|
|
37
37
|
</NcButton>
|
|
38
38
|
<NcActions
|
|
39
|
-
v-if="!tabs"
|
|
39
|
+
v-if="!tabs && actionsMenu"
|
|
40
40
|
:menuName="t('nextcloud-vue', 'Actions')"
|
|
41
41
|
:forceName="true"
|
|
42
42
|
data-testid="cn-features-board-actions">
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
</button>
|
|
86
86
|
</div>
|
|
87
87
|
<NcActions
|
|
88
|
+
v-if="actionsMenu"
|
|
88
89
|
:menuName="t('nextcloud-vue', 'Actions')"
|
|
89
90
|
:forceName="true"
|
|
90
91
|
class="cn-features-board__tab-actions"
|
|
@@ -375,6 +376,8 @@ export default {
|
|
|
375
376
|
|
|
376
377
|
/** Show the Features and Roadmap folder tabs. */
|
|
377
378
|
tabs: { type: Boolean, default: true },
|
|
379
|
+
/** Show the Actions menu (suggest, buildiq, AI skills, support), in the header or the tab row; false draws none. */
|
|
380
|
+
actionsMenu: { type: Boolean, default: true },
|
|
378
381
|
/** `chips`: pill buttons with a count. `segmented`: a segmented control without counts. */
|
|
379
382
|
filter: {
|
|
380
383
|
type: String,
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<NcButton v-if="showRun"
|
|
37
37
|
:disabled="store.running || !store.flow.id"
|
|
38
38
|
data-testid="flow-board-run"
|
|
39
|
-
@click="
|
|
39
|
+
@click="onRun">
|
|
40
40
|
{{ t('nextcloud-vue', 'Run') }}
|
|
41
41
|
</NcButton>
|
|
42
42
|
<NcButton v-if="testSubjectReady"
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
:key="group.app"
|
|
110
110
|
class="cn-flow-board__palette-group">
|
|
111
111
|
<h4 class="cn-flow-board__palette-app">
|
|
112
|
-
{{ group.
|
|
112
|
+
{{ group.name }}
|
|
113
113
|
</h4>
|
|
114
114
|
<ul class="cn-flow-board__palette-list">
|
|
115
115
|
<li v-for="entry in group.entries" :key="entry.id">
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
:toolbarActions="false"
|
|
136
136
|
:addStep="!showPalette"
|
|
137
137
|
@save="$emit('save')"
|
|
138
|
-
@run="
|
|
138
|
+
@run="onRun" />
|
|
139
139
|
<div v-if="store.inRunView"
|
|
140
140
|
class="cn-flow-board__legend"
|
|
141
141
|
data-testid="flow-board-legend">
|
|
@@ -183,6 +183,15 @@
|
|
|
183
183
|
<h3 class="cn-flow-board__card-title" data-testid="flow-board-step-name">
|
|
184
184
|
{{ stepName }}
|
|
185
185
|
</h3>
|
|
186
|
+
<dl v-if="stepSettings.length > 0" class="cn-flow-board__settings">
|
|
187
|
+
<div v-for="setting in stepSettings"
|
|
188
|
+
:key="setting.key"
|
|
189
|
+
class="cn-flow-board__setting"
|
|
190
|
+
data-testid="flow-board-step-setting">
|
|
191
|
+
<dt>{{ setting.label }}</dt>
|
|
192
|
+
<dd>{{ setting.value }}</dd>
|
|
193
|
+
</div>
|
|
194
|
+
</dl>
|
|
186
195
|
<div class="cn-flow-board__step-actions">
|
|
187
196
|
<NcButton data-testid="flow-board-edit-step" @click="store.editingNodeId = store.selectedNodeId">
|
|
188
197
|
{{ t('nextcloud-vue', 'Edit step') }}
|
|
@@ -212,7 +221,23 @@
|
|
|
212
221
|
</NcButton>
|
|
213
222
|
</div>
|
|
214
223
|
</template>
|
|
215
|
-
<
|
|
224
|
+
<template v-else-if="showRuns">
|
|
225
|
+
<NcButton variant="tertiary"
|
|
226
|
+
class="cn-flow-board__runs-back"
|
|
227
|
+
data-testid="flow-board-hide-runs"
|
|
228
|
+
@click="showRuns = false">
|
|
229
|
+
{{ t('nextcloud-vue', 'Back to the step settings') }}
|
|
230
|
+
</NcButton>
|
|
231
|
+
<CnFlowSidebar embedded />
|
|
232
|
+
</template>
|
|
233
|
+
<div v-else class="cn-flow-board__inspector-empty" data-testid="flow-board-inspector-empty">
|
|
234
|
+
<p>{{ t('nextcloud-vue', 'Select a step on the canvas to see its settings.') }}</p>
|
|
235
|
+
<NcButton variant="secondary"
|
|
236
|
+
data-testid="flow-board-show-runs"
|
|
237
|
+
@click="showRuns = true">
|
|
238
|
+
{{ t('nextcloud-vue', 'Show runs') }}
|
|
239
|
+
</NcButton>
|
|
240
|
+
</div>
|
|
216
241
|
</aside>
|
|
217
242
|
</div>
|
|
218
243
|
</div>
|
|
@@ -232,6 +257,7 @@ import CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'
|
|
|
232
257
|
import { FLOW_RUN_ACTIVE_STATUSES, useFlowStore } from '../../composables/useFlowStore.js'
|
|
233
258
|
import { formatRelativeDate } from '../../utils/boardDate.js'
|
|
234
259
|
import { flowOutcomeLevels, flowRunDurationMs, flowRunLogLines, flowRunName, formatFlowDuration } from '../../utils/flowRunLog.js'
|
|
260
|
+
import { appDisplayNames } from '../../utils/nextcloudApps.js'
|
|
235
261
|
import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
236
262
|
|
|
237
263
|
/**
|
|
@@ -278,7 +304,7 @@ export default {
|
|
|
278
304
|
},
|
|
279
305
|
|
|
280
306
|
data() {
|
|
281
|
-
return { runTab: 'step', testOpen: false, testRecord: null }
|
|
307
|
+
return { runTab: 'step', testOpen: false, testRecord: null, appNames: {}, showRuns: false }
|
|
282
308
|
},
|
|
283
309
|
|
|
284
310
|
computed: {
|
|
@@ -414,17 +440,25 @@ export default {
|
|
|
414
440
|
return { tone: 'success', text: this.t('nextcloud-vue', 'The flow is valid.') }
|
|
415
441
|
},
|
|
416
442
|
|
|
417
|
-
/**
|
|
443
|
+
/**
|
|
444
|
+
* The palette grouped by owning app, each group named by the app's
|
|
445
|
+
* display name (its id when Nextcloud names none), the app that owns
|
|
446
|
+
* this flow first.
|
|
447
|
+
*
|
|
448
|
+
* @return {Array<{app: string, name: string, entries: Array<object>}>} The groups.
|
|
449
|
+
*/
|
|
418
450
|
paletteGroups() {
|
|
419
451
|
const groups = {}
|
|
420
452
|
;(this.store.nodeCatalog || []).forEach((entry) => {
|
|
421
453
|
const app = String(entry?.app || String(entry?.id || '').split('.')[0] || '')
|
|
422
454
|
if (!groups[app]) {
|
|
423
|
-
groups[app] = { app, entries: [] }
|
|
455
|
+
groups[app] = { app, name: this.appNames[app] || app, entries: [] }
|
|
424
456
|
}
|
|
425
457
|
groups[app].entries.push(entry)
|
|
426
458
|
})
|
|
427
|
-
|
|
459
|
+
const list = Object.values(groups)
|
|
460
|
+
const host = list.findIndex((group) => group.app === this.app)
|
|
461
|
+
return host > 0 ? [list[host], ...list.slice(0, host), ...list.slice(host + 1)] : list
|
|
428
462
|
},
|
|
429
463
|
|
|
430
464
|
/** @return {object|null} The run on screen. */
|
|
@@ -477,6 +511,37 @@ export default {
|
|
|
477
511
|
return entry ? (entry.displayName || entry.id) : String(node?.type || '')
|
|
478
512
|
},
|
|
479
513
|
|
|
514
|
+
/**
|
|
515
|
+
* The selected step's settings as its owner declares them in the
|
|
516
|
+
* catalogue's `configForm`: label and value, a select's option label,
|
|
517
|
+
* Yes or No for a switch. Unset settings are left out.
|
|
518
|
+
*
|
|
519
|
+
* @return {Array<{key: string, label: string, value: string}>} The settings.
|
|
520
|
+
*/
|
|
521
|
+
stepSettings() {
|
|
522
|
+
const node = this.store.selectedNode
|
|
523
|
+
const entry = node ? this.store.catalogEntry(node.type) : null
|
|
524
|
+
const config = (node && node.config) || {}
|
|
525
|
+
const form = (entry && Array.isArray(entry.configForm)) ? entry.configForm : []
|
|
526
|
+
return form
|
|
527
|
+
.filter((field) => field && field.key && config[field.key] !== undefined && config[field.key] !== null && config[field.key] !== '')
|
|
528
|
+
.map((field) => {
|
|
529
|
+
const raw = config[field.key]
|
|
530
|
+
const option = Array.isArray(field.options) ? field.options.find((o) => o && o.value === raw) : null
|
|
531
|
+
let value
|
|
532
|
+
if (option) {
|
|
533
|
+
value = String(option.label ?? option.value)
|
|
534
|
+
} else if (typeof raw === 'boolean') {
|
|
535
|
+
value = raw ? this.t('nextcloud-vue', 'Yes') : this.t('nextcloud-vue', 'No')
|
|
536
|
+
} else if (typeof raw === 'object') {
|
|
537
|
+
value = JSON.stringify(raw)
|
|
538
|
+
} else {
|
|
539
|
+
value = String(raw)
|
|
540
|
+
}
|
|
541
|
+
return { key: field.key, label: field.label || field.key, value }
|
|
542
|
+
})
|
|
543
|
+
},
|
|
544
|
+
|
|
480
545
|
/** @return {string} The selected step's name. */
|
|
481
546
|
stepName() {
|
|
482
547
|
const node = this.store.selectedNode
|
|
@@ -507,7 +572,36 @@ export default {
|
|
|
507
572
|
},
|
|
508
573
|
},
|
|
509
574
|
|
|
575
|
+
watch: {
|
|
576
|
+
// A selected step takes the panel back from the run history.
|
|
577
|
+
'store.selectedNodeId': function(id) {
|
|
578
|
+
if (id) {
|
|
579
|
+
this.showRuns = false
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
|
|
584
|
+
async created() {
|
|
585
|
+
// The palette names its groups by app display name; only the column
|
|
586
|
+
// palette draws them, so only it reads the names.
|
|
587
|
+
if (this.palette !== 'column') {
|
|
588
|
+
return
|
|
589
|
+
}
|
|
590
|
+
const { default: axios } = await import('@nextcloud/axios')
|
|
591
|
+
this.appNames = await appDisplayNames(axios)
|
|
592
|
+
},
|
|
593
|
+
|
|
510
594
|
methods: {
|
|
595
|
+
/**
|
|
596
|
+
* Run the flow, with the run history in the panel to follow it.
|
|
597
|
+
*
|
|
598
|
+
* @return {void}
|
|
599
|
+
*/
|
|
600
|
+
onRun() {
|
|
601
|
+
this.showRuns = true
|
|
602
|
+
this.$emit('run')
|
|
603
|
+
},
|
|
604
|
+
|
|
511
605
|
/**
|
|
512
606
|
* Run the saved flow once with the picked record as its subject, then
|
|
513
607
|
* close the dialog; the run shows on the canvas as any run does.
|
|
@@ -822,6 +916,36 @@ export default {
|
|
|
822
916
|
text-align: center;
|
|
823
917
|
}
|
|
824
918
|
|
|
919
|
+
.cn-flow-board__settings {
|
|
920
|
+
display: flex;
|
|
921
|
+
flex-direction: column;
|
|
922
|
+
gap: 10px;
|
|
923
|
+
margin: 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.cn-flow-board__setting dt {
|
|
927
|
+
font-size: 13px;
|
|
928
|
+
color: var(--color-text-maxcontrast);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.cn-flow-board__setting dd {
|
|
932
|
+
margin: 2px 0 0;
|
|
933
|
+
font-weight: 600;
|
|
934
|
+
overflow-wrap: anywhere;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.cn-flow-board__inspector-empty {
|
|
938
|
+
display: flex;
|
|
939
|
+
flex-direction: column;
|
|
940
|
+
align-items: flex-start;
|
|
941
|
+
gap: 12px;
|
|
942
|
+
color: var(--color-text-maxcontrast);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
.cn-flow-board__inspector-empty p {
|
|
946
|
+
margin: 0;
|
|
947
|
+
}
|
|
948
|
+
|
|
825
949
|
.cn-flow-board__step-actions,
|
|
826
950
|
.cn-flow-board__step-footer {
|
|
827
951
|
display: flex;
|
|
@@ -390,6 +390,7 @@ export default {
|
|
|
390
390
|
| `showViewToggle` | Boolean | `true` | Whether to show the Cards/Table view toggle |
|
|
391
391
|
| `inlineSearch` | Boolean | `false` | Show an inline search field in the actions bar (in addition to / instead of the sidebar search). Fed from the manifest as `pages[].config.inlineSearch`. The field takes the place of the "Showing X of Y" counter unless `showCountWithSearch` is set. |
|
|
392
392
|
| `search` | Boolean | `true` | `false` draws no inline search in the actions bar, also in the board look, which otherwise draws one on every index page (manifest: `config.search`). Wins over `inlineSearch`. |
|
|
393
|
+
| `appConfig` | Object | `{}` | The app config `@config.<key>` tokens resolve against (manifest: `config.appConfig`), as on the dashboard and detail pages, merged over a config an ancestor provides on `cnAppConfig`. A quick filter's `filter` and its `label` / `activeLabel` take the token: `"Residence @config.municipality"` reads "Residence Zuiddrecht" (board DqContacten); the label is translated first, and a key the config does not hold leaves the token as written. |
|
|
393
394
|
| `showCountWithSearch` | Boolean | `false` | Keep the "Showing X of Y" counter visible beside the inline search field, after the search and any `#after-search` controls (only relevant with `inlineSearch`, which otherwise takes the counter's place). Fed from the manifest as `pages[].config.showCountWithSearch`. |
|
|
394
395
|
| `searchInFiles` | Boolean | `false` | "Also search inside files" switch beside the search box (manifest `config.searchInFiles`). On, a search with a term sends `_content_search=true`; rows found through a file show "Found in {file}" (`@self.matchedFile`). State kept in the route as `contentSearch=1`. Emits `content-search`. |
|
|
395
396
|
| `searchPlaceholder` | String | `''` | Placeholder for the inline search field (manifest `config.searchPlaceholder`). |
|