@conduction/nextcloud-vue 2.99.0 → 2.100.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 +6 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +2 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +25 -8
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +5 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +11 -8
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +13 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +54 -3
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +16 -2
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +42 -7
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +121 -57
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue2.js +10 -0
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue3.js +2 -1
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +136 -7
- 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/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +68 -7
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +256 -240
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +1 -1
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +38 -9
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +16 -2
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +40 -25
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue.js +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +7 -7
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +7 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +7 -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 +348 -161
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +1482 -998
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +348 -161
- package/l10n/en.json +7 -1
- package/l10n/nl.json +7 -1
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +7 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +28 -9
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +13 -4
- package/src/components/CnFactsWidget/CnFactsWidget.vue +65 -3
- package/src/components/CnFlowsPage/CnFlowBoard.vue +77 -2
- package/src/components/CnFlowsPage/CnFlowEditorPage.vue +11 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +136 -7
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +114 -7
- package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +38 -9
- package/src/components/CnSetupWizard/CnSetupWizard.vue +37 -3
- package/src/components/CnWizardDialog/CnWizardDialog.vue +7 -7
- package/src/css/detail-page.css +15 -0
- package/src/css/dialog.css +48 -0
- package/src/css/look-board.css +83 -1
- package/src/schemas/app-manifest-v2.schema.json +25 -1
- package/src/utils/validateManifestV2.compiled.js +3 -3
package/l10n/en.json
CHANGED
|
@@ -1694,7 +1694,13 @@
|
|
|
1694
1694
|
"{fields}: {reason}": "{fields}: {reason}",
|
|
1695
1695
|
"Current {name}": "Current {name}",
|
|
1696
1696
|
"Upload {name}": "Upload {name}",
|
|
1697
|
-
"The image could not be uploaded: {reason}": "The image could not be uploaded: {reason}"
|
|
1697
|
+
"The image could not be uploaded: {reason}": "The image could not be uploaded: {reason}",
|
|
1698
|
+
"The saved flow runs once with the record you pick as its subject.": "The saved flow runs once with the record you pick as its subject.",
|
|
1699
|
+
"Record": "Record",
|
|
1700
|
+
"Run test": "Run test",
|
|
1701
|
+
"Test with a record": "Test with a record",
|
|
1702
|
+
"admin": "admin",
|
|
1703
|
+
"Wizard": "Wizard"
|
|
1698
1704
|
},
|
|
1699
1705
|
"plurals": {
|
|
1700
1706
|
"{count} selected": [
|
package/l10n/nl.json
CHANGED
|
@@ -1909,7 +1909,13 @@
|
|
|
1909
1909
|
"Day added.": "Dag toegevoegd.",
|
|
1910
1910
|
"Only an administrator can change the working calendar.": "Alleen een beheerder kan de werkkalender wijzigen.",
|
|
1911
1911
|
"The day could not be added. Try again.": "De dag kon niet worden toegevoegd. Probeer het opnieuw.",
|
|
1912
|
-
"Loading …": "Laden …"
|
|
1912
|
+
"Loading …": "Laden …",
|
|
1913
|
+
"The saved flow runs once with the record you pick as its subject.": "De opgeslagen flow draait één keer met het record dat u kiest als onderwerp.",
|
|
1914
|
+
"Record": "Record",
|
|
1915
|
+
"Run test": "Test uitvoeren",
|
|
1916
|
+
"Test with a record": "Testen met een record",
|
|
1917
|
+
"admin": "beheerder",
|
|
1918
|
+
"Wizard": "Assistent"
|
|
1913
1919
|
},
|
|
1914
1920
|
"plurals": {
|
|
1915
1921
|
"{count} selected": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduction/nextcloud-vue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.100.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": {
|
|
@@ -218,6 +218,7 @@
|
|
|
218
218
|
<CnSetupWizard
|
|
219
219
|
v-if="showSetup && setupWizardOpen"
|
|
220
220
|
:appId="appId"
|
|
221
|
+
:appName="appName"
|
|
221
222
|
:steps="setupSteps"
|
|
222
223
|
@complete="setupWizardOpen = false"
|
|
223
224
|
@close="setupWizardOpen = false" />
|
|
@@ -231,7 +232,7 @@ import { loadState } from '@nextcloud/initial-state'
|
|
|
231
232
|
import { translate as t } from '@nextcloud/l10n'
|
|
232
233
|
import { generateUrl } from '@nextcloud/router'
|
|
233
234
|
import { NcButton, NcLoadingIcon, NcSettingsSection } from '@nextcloud/vue'
|
|
234
|
-
import { computed, provide } from 'vue'
|
|
235
|
+
import { computed, inject, provide } from 'vue'
|
|
235
236
|
import AutoFix from 'vue-material-design-icons/AutoFix.vue'
|
|
236
237
|
import BookOpenVariant from 'vue-material-design-icons/BookOpenVariant.vue'
|
|
237
238
|
import HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'
|
|
@@ -772,6 +773,11 @@ export default {
|
|
|
772
773
|
// Everything inside the shell, its own version card and the app's
|
|
773
774
|
// sections alike, follows the shell's look without the app providing it.
|
|
774
775
|
provide('cnLook', look)
|
|
776
|
+
// The setup wizard translates its step titles and bodies through
|
|
777
|
+
// `cnTranslate`. On an admin page no CnAppRoot provides it, so the
|
|
778
|
+
// shell hands down the app's own catalog, keyed by its app id.
|
|
779
|
+
const hostTranslate = inject('cnTranslate', null)
|
|
780
|
+
provide('cnTranslate', hostTranslate || ((text, vars) => t(props.appId, text, vars)))
|
|
775
781
|
return { environmentName, isBoard, ownLookClass }
|
|
776
782
|
},
|
|
777
783
|
|
|
@@ -414,7 +414,8 @@
|
|
|
414
414
|
:key="`${entry.id}-link`"
|
|
415
415
|
:href="entry.href"
|
|
416
416
|
:target="entry.linkTarget || undefined"
|
|
417
|
-
:data-testid="entry.testid"
|
|
417
|
+
:data-testid="entry.testid"
|
|
418
|
+
:data-cn-admin-tag="adminTagFor(group)">
|
|
418
419
|
<template v-if="entry.iconName || entry.iconClass" #icon>
|
|
419
420
|
<CnIcon v-if="entry.iconName" :name="entry.iconName" :size="20" />
|
|
420
421
|
<span v-else :class="entry.iconClass" />
|
|
@@ -428,6 +429,7 @@
|
|
|
428
429
|
:disabled="entry.disabled"
|
|
429
430
|
:aria-pressed="entry.pressed === null ? null : String(entry.pressed)"
|
|
430
431
|
:closeAfterClick="true"
|
|
432
|
+
:data-cn-admin-tag="adminTagFor(group)"
|
|
431
433
|
@click="entry.run()">
|
|
432
434
|
<template v-if="entry.iconName || entry.iconClass" #icon>
|
|
433
435
|
<CnIcon v-if="entry.iconName" :name="entry.iconName" :size="20" />
|
|
@@ -2443,13 +2445,15 @@ export default {
|
|
|
2443
2445
|
|
|
2444
2446
|
/**
|
|
2445
2447
|
* What the Actions menu itself shows (manifest `config.actionsMenu`):
|
|
2446
|
-
* `{ showRefresh?, showHelpLinks?, label? }`.
|
|
2447
|
-
* removes Refresh. `showHelpLinks: false` removes
|
|
2448
|
-
* Report a bug and Documentation, for an app that
|
|
2449
|
-
*
|
|
2450
|
-
*
|
|
2448
|
+
* `{ showRefresh?, showHelpLinks?, showMarkUnread?, showOpenSidebar?, label? }`.
|
|
2449
|
+
* `showRefresh: false` removes Refresh. `showHelpLinks: false` removes
|
|
2450
|
+
* Request a feature, Report a bug and Documentation, for an app that
|
|
2451
|
+
* offers them in its own help menu. `showMarkUnread: false` and
|
|
2452
|
+
* `showOpenSidebar: false` remove those two built-in entries, for a
|
|
2453
|
+
* menu the board draws without them (DqZaakMeer). `label` names the
|
|
2454
|
+
* menu, for example "More". `null` (the default) changes nothing.
|
|
2451
2455
|
*
|
|
2452
|
-
* @type {{showRefresh?: boolean, showHelpLinks?: boolean, label?: string}|null}
|
|
2456
|
+
* @type {{showRefresh?: boolean, showHelpLinks?: boolean, showMarkUnread?: boolean, showOpenSidebar?: boolean, label?: string}|null}
|
|
2453
2457
|
*/
|
|
2454
2458
|
actionsMenu: {
|
|
2455
2459
|
type: Object,
|
|
@@ -4173,7 +4177,8 @@ export default {
|
|
|
4173
4177
|
* @spec openspec/changes/record-unread-markers/tasks.md#task-2
|
|
4174
4178
|
*/
|
|
4175
4179
|
showMarkUnread() {
|
|
4176
|
-
return
|
|
4180
|
+
return this.actionsMenu?.showMarkUnread !== false
|
|
4181
|
+
&& typeof this.resolvedSelf.unread === 'boolean'
|
|
4177
4182
|
&& this.register !== '' && this.schema !== '' && this.objectId !== ''
|
|
4178
4183
|
},
|
|
4179
4184
|
|
|
@@ -4185,7 +4190,8 @@ export default {
|
|
|
4185
4190
|
* @return {boolean}
|
|
4186
4191
|
*/
|
|
4187
4192
|
showOpenSidebar() {
|
|
4188
|
-
return this.
|
|
4193
|
+
return this.actionsMenu?.showOpenSidebar !== false
|
|
4194
|
+
&& this.isBoard && this.hasExternalSidebar && this.sidebarActive && this.objectSidebarState.open !== true
|
|
4189
4195
|
},
|
|
4190
4196
|
|
|
4191
4197
|
/** @return {string} Label of the Open sidebar entry. */
|
|
@@ -4933,6 +4939,19 @@ export default {
|
|
|
4933
4939
|
},
|
|
4934
4940
|
|
|
4935
4941
|
methods: {
|
|
4942
|
+
/**
|
|
4943
|
+
* The tag after an admin-only entry in the board look ("beheerder",
|
|
4944
|
+
* DqZaakMeer), or null for any other entry or look. It is drawn by
|
|
4945
|
+
* detail-page.css from the attribute, since the menu renders its
|
|
4946
|
+
* entries' text as plain text.
|
|
4947
|
+
*
|
|
4948
|
+
* @param {{key: string}} group The menu group the entry is in.
|
|
4949
|
+
* @return {string|null} The tag, or null.
|
|
4950
|
+
*/
|
|
4951
|
+
adminTagFor(group) {
|
|
4952
|
+
return this.isBoard && group && group.key === 'cn-admin' ? t('nextcloud-vue', 'admin') : null
|
|
4953
|
+
},
|
|
4954
|
+
|
|
4936
4955
|
/**
|
|
4937
4956
|
* Open the object sidebar through the shared channel.
|
|
4938
4957
|
*
|
|
@@ -184,8 +184,14 @@
|
|
|
184
184
|
<CnIcon :name="widget.icon" :size="20" />
|
|
185
185
|
</template>
|
|
186
186
|
<!-- A renderer that reports its own header (`header` event:
|
|
187
|
-
`{ link, note }`, e.g. record-list's "3 in totaal"
|
|
188
|
-
|
|
187
|
+
`{ link, note, action, pill }`, e.g. record-list's "3 in totaal",
|
|
188
|
+
a facts card's status pill). -->
|
|
189
|
+
<template v-if="rendererHeader.note || rendererHeader.action || rendererHeader.pill" #actions>
|
|
190
|
+
<CnStatusBadge
|
|
191
|
+
v-if="rendererHeader.pill"
|
|
192
|
+
:label="rendererHeader.pill.label"
|
|
193
|
+
:variant="rendererHeader.pill.variant"
|
|
194
|
+
data-testid="cn-detail-widget-header-pill" />
|
|
189
195
|
<span
|
|
190
196
|
v-if="rendererHeader.note"
|
|
191
197
|
class="cn-detail-page__catalog-note"
|
|
@@ -298,6 +304,7 @@ import CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'
|
|
|
298
304
|
import CnObjectFilesWidget from '../CnObjectFilesWidget/CnObjectFilesWidget.vue'
|
|
299
305
|
import CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'
|
|
300
306
|
import CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'
|
|
307
|
+
import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
|
|
301
308
|
import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'
|
|
302
309
|
import { normalizeLook } from '../../composables/useLook.js'
|
|
303
310
|
import { isAppInstalled } from '../../utils/appInstalled.js'
|
|
@@ -376,6 +383,7 @@ export default {
|
|
|
376
383
|
CnObjectDataWidget,
|
|
377
384
|
CnObjectGeoWidget,
|
|
378
385
|
CnRelatedObjectsWidget,
|
|
386
|
+
CnStatusBadge,
|
|
379
387
|
CnWidgetWrapper,
|
|
380
388
|
NcActionButton,
|
|
381
389
|
CnEmptyContent,
|
|
@@ -631,7 +639,7 @@ export default {
|
|
|
631
639
|
* `header` event (screens-relation-pages): a link and a note,
|
|
632
640
|
* with the list's total already filled in.
|
|
633
641
|
*/
|
|
634
|
-
rendererHeader: { link: null, note: '', action: null },
|
|
642
|
+
rendererHeader: { link: null, note: '', action: null, pill: null },
|
|
635
643
|
}
|
|
636
644
|
},
|
|
637
645
|
|
|
@@ -1218,7 +1226,8 @@ export default {
|
|
|
1218
1226
|
const link = header && header.link && typeof header.link === 'object' ? header.link : null
|
|
1219
1227
|
const note = header && typeof header.note === 'string' ? header.note : ''
|
|
1220
1228
|
const action = header && header.action && typeof header.action.run === 'function' && header.action.label ? header.action : null
|
|
1221
|
-
|
|
1229
|
+
const pill = header && header.pill && typeof header.pill.label === 'string' && header.pill.label !== '' ? header.pill : null
|
|
1230
|
+
this.rendererHeader = { link, note, action, pill }
|
|
1222
1231
|
},
|
|
1223
1232
|
|
|
1224
1233
|
/**
|
|
@@ -132,6 +132,13 @@
|
|
|
132
132
|
</div>
|
|
133
133
|
</template>
|
|
134
134
|
</template>
|
|
135
|
+
<NcNoteCard
|
|
136
|
+
v-if="notice && record"
|
|
137
|
+
:type="notice.type"
|
|
138
|
+
class="cn-facts-widget__notice"
|
|
139
|
+
data-testid="cn-facts-notice">
|
|
140
|
+
{{ notice.text }}
|
|
141
|
+
</NcNoteCard>
|
|
135
142
|
<component
|
|
136
143
|
:is="link.to ? 'router-link' : 'a'"
|
|
137
144
|
v-if="link && record"
|
|
@@ -157,7 +164,7 @@
|
|
|
157
164
|
import { emit } from '@nextcloud/event-bus'
|
|
158
165
|
import { formatFileSize } from '@nextcloud/files'
|
|
159
166
|
import { translate as t } from '@nextcloud/l10n'
|
|
160
|
-
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
167
|
+
import { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
|
|
161
168
|
import Check from 'vue-material-design-icons/Check.vue'
|
|
162
169
|
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
|
|
163
170
|
import EyeOutline from 'vue-material-design-icons/EyeOutline.vue'
|
|
@@ -266,7 +273,7 @@ export const FACTS_REVEAL_CHANNEL = 'cn:facts:reveal'
|
|
|
266
273
|
export default {
|
|
267
274
|
name: 'CnFactsWidget',
|
|
268
275
|
|
|
269
|
-
components: { Check, CnIcon, CnStatusBadge, ContentCopy, EyeOutline, NcLoadingIcon },
|
|
276
|
+
components: { Check, CnIcon, CnStatusBadge, ContentCopy, EyeOutline, NcLoadingIcon, NcNoteCard },
|
|
270
277
|
|
|
271
278
|
inject: {
|
|
272
279
|
cnTranslate: { default: () => (key) => key },
|
|
@@ -290,6 +297,8 @@ export default {
|
|
|
290
297
|
* `resolve: { register, schema, labelField? }` on an entry or a part turns a value that is a reference (an id, or a list of ids) into the label of the referenced object (`labelField`, else `title`, then `name`): the case type or status of a task shows its name, not its uuid. The ids are fetched once per register and schema through the object store and shared by every facts widget on the page; the entry stays empty until its label is known, and when no label can be read. A resolved value works as a pill too: `map` is applied to the resolved label, and `variantMap` / `colorMap` look the tone up by the value of `resolve.toneField` on the referenced object when that is set (a status named by its `name`, coloured by its `state`), then by the resolved label, then by the stored value.
|
|
291
298
|
* `format: 'user'` shows a Nextcloud user id as the user's display name, read once per id; the entry stays empty until the name is known.
|
|
292
299
|
* - `action` — an action drawn as a button under the facts; `headerAction` — one drawn in the card header (reported through the `header` event).
|
|
300
|
+
* - `headerPill` — a status pill in the card header (reported through the `header` event): `{ path?, text?, map?, variant?, variantMap?, colorMap? }`, read from the record like a `pill` entry (DqBezwaar "Advies ontvangen"); nothing shows while the value is empty.
|
|
301
|
+
* - `notice` — `{ text, variant? }`, a note band inside the card under the facts (`info` by default; `success`, `warning` or `error`); the text is translated.
|
|
293
302
|
* `format` is `text` (default), `date` (board short date), `date-long` (always with the year), `list` (an array joined by ", ", each item read at `itemPath`), `bytes` (a size, "1.5 MB") or `currency` (a money amount in the reader's language, "€ 1.234,50": the code in `currency`, else the record's `currencyField`, else the app's reporting currency, else EUR; `decimals` defaults to 2).
|
|
294
303
|
* `map` turns a raw value into a label (translated). `parts` (on a `line`) lists paths, or `{ path, label?, format? }`, joined by `separator` (default " · "); empty parts are dropped.
|
|
295
304
|
* - `source` — `{ register, schema, id }` or `{ url }`; omitted = the current object. An entry with `from: "object"` reads the current object even then (a client card that also shows the ticket's channel).
|
|
@@ -508,6 +517,47 @@ export default {
|
|
|
508
517
|
return this.actionView(this.content?.headerAction)
|
|
509
518
|
},
|
|
510
519
|
|
|
520
|
+
/**
|
|
521
|
+
* The card header's status pill, read from the record like a `pill`
|
|
522
|
+
* entry, or null when it has no text.
|
|
523
|
+
*
|
|
524
|
+
* @return {{label: string, variant: string}|null}
|
|
525
|
+
*/
|
|
526
|
+
headerPill() {
|
|
527
|
+
const pill = this.content?.headerPill
|
|
528
|
+
if (!pill || typeof pill !== 'object' || !this.record) {
|
|
529
|
+
return null
|
|
530
|
+
}
|
|
531
|
+
const raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(this.record, pill.path) : undefined
|
|
532
|
+
const mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : undefined
|
|
533
|
+
const text = mapped ?? pill.text ?? raw
|
|
534
|
+
if (typeof text !== 'string' && typeof text !== 'number') {
|
|
535
|
+
return null
|
|
536
|
+
}
|
|
537
|
+
const label = this.tr(String(text))
|
|
538
|
+
if (label === '') {
|
|
539
|
+
return null
|
|
540
|
+
}
|
|
541
|
+
const colours = pill.variantMap || pill.colorMap
|
|
542
|
+
const hit = colours && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(colours, raw) ? colours[raw] : pill.variant
|
|
543
|
+
return { label, variant: isBadgeVariant(hit) ? hit : 'default' }
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* The note band under the facts: its text translated, its NcNoteCard
|
|
548
|
+
* type from `variant` (info by default), or null without text.
|
|
549
|
+
*
|
|
550
|
+
* @return {{text: string, type: string}|null}
|
|
551
|
+
*/
|
|
552
|
+
notice() {
|
|
553
|
+
const notice = this.content?.notice
|
|
554
|
+
const text = notice && typeof notice.text === 'string' ? this.tr(notice.text) : ''
|
|
555
|
+
if (text === '') {
|
|
556
|
+
return null
|
|
557
|
+
}
|
|
558
|
+
return { text, type: ['success', 'warning', 'error'].includes(notice.variant) ? notice.variant : 'info' }
|
|
559
|
+
},
|
|
560
|
+
|
|
511
561
|
/** @return {string} The reveal button's text. */
|
|
512
562
|
showLabel() {
|
|
513
563
|
return t('nextcloud-vue', 'Show')
|
|
@@ -544,6 +594,13 @@ export default {
|
|
|
544
594
|
this.emitHeader()
|
|
545
595
|
},
|
|
546
596
|
},
|
|
597
|
+
|
|
598
|
+
headerPill: {
|
|
599
|
+
deep: true,
|
|
600
|
+
handler() {
|
|
601
|
+
this.emitHeader()
|
|
602
|
+
},
|
|
603
|
+
},
|
|
547
604
|
},
|
|
548
605
|
|
|
549
606
|
methods: {
|
|
@@ -803,12 +860,13 @@ export default {
|
|
|
803
860
|
const view = this.headerAction
|
|
804
861
|
/**
|
|
805
862
|
* @event header Emitted with the card header's parts: the header action, if any.
|
|
806
|
-
* @type {{link: null, note: string, action: (object|null)}} `action` is `{ label, icon, run }`.
|
|
863
|
+
* @type {{link: null, note: string, action: (object|null), pill: (object|null)}} `action` is `{ label, icon, run }`, `pill` is `{ label, variant }`.
|
|
807
864
|
*/
|
|
808
865
|
this.$emit('header', {
|
|
809
866
|
link: null,
|
|
810
867
|
note: '',
|
|
811
868
|
action: view ? { ...view, run: () => this.runAction(this.content.headerAction) } : null,
|
|
869
|
+
pill: this.headerPill,
|
|
812
870
|
})
|
|
813
871
|
},
|
|
814
872
|
|
|
@@ -1368,6 +1426,10 @@ export default {
|
|
|
1368
1426
|
background: var(--color-primary-element-light-hover, var(--color-primary-element-light));
|
|
1369
1427
|
}
|
|
1370
1428
|
|
|
1429
|
+
.cn-facts-widget__notice {
|
|
1430
|
+
margin: 4px 0 0;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1371
1433
|
.cn-facts-widget__link {
|
|
1372
1434
|
font-size: 14px;
|
|
1373
1435
|
font-weight: 600;
|
|
@@ -39,6 +39,15 @@
|
|
|
39
39
|
@click="$emit('run')">
|
|
40
40
|
{{ t('nextcloud-vue', 'Run') }}
|
|
41
41
|
</NcButton>
|
|
42
|
+
<NcButton v-if="testSubjectReady"
|
|
43
|
+
:disabled="store.running || !store.flow.id"
|
|
44
|
+
data-testid="flow-board-test"
|
|
45
|
+
@click="testOpen = true">
|
|
46
|
+
<template #icon>
|
|
47
|
+
<TestTube :size="16" />
|
|
48
|
+
</template>
|
|
49
|
+
{{ testLabel }}
|
|
50
|
+
</NcButton>
|
|
42
51
|
<CnBuildiqEditButton />
|
|
43
52
|
<NcButton variant="primary"
|
|
44
53
|
:disabled="store.saving || !store.flow.name || store.graphLocked"
|
|
@@ -49,6 +58,32 @@
|
|
|
49
58
|
</div>
|
|
50
59
|
</header>
|
|
51
60
|
|
|
61
|
+
<CnDialog v-if="testOpen"
|
|
62
|
+
:name="testLabel"
|
|
63
|
+
data-testid="flow-board-test-dialog"
|
|
64
|
+
@closing="testOpen = false">
|
|
65
|
+
<p class="cn-flow-board__test-intro">
|
|
66
|
+
{{ t('nextcloud-vue', 'The saved flow runs once with the record you pick as its subject.') }}
|
|
67
|
+
</p>
|
|
68
|
+
<CnResourceSelect v-model="testRecord"
|
|
69
|
+
:register="testSubject.register"
|
|
70
|
+
:schema="testSubject.schema"
|
|
71
|
+
:labelField="testSubject.labelField || 'title'"
|
|
72
|
+
:inputLabel="t('nextcloud-vue', 'Record')"
|
|
73
|
+
data-testid="flow-board-test-record" />
|
|
74
|
+
<template #actions>
|
|
75
|
+
<NcButton data-testid="flow-board-test-cancel" @click="testOpen = false">
|
|
76
|
+
{{ t('nextcloud-vue', 'Cancel') }}
|
|
77
|
+
</NcButton>
|
|
78
|
+
<NcButton variant="primary"
|
|
79
|
+
:disabled="!testRecord || store.running"
|
|
80
|
+
data-testid="flow-board-test-run"
|
|
81
|
+
@click="runTest">
|
|
82
|
+
{{ t('nextcloud-vue', 'Run test') }}
|
|
83
|
+
</NcButton>
|
|
84
|
+
</template>
|
|
85
|
+
</CnDialog>
|
|
86
|
+
|
|
52
87
|
<p class="cn-flow-board__path" data-testid="flow-board-path">
|
|
53
88
|
<a :href="listHref" class="cn-flow-board__crumb" @click="onCrumbClick($event, detailRoute)">{{ listText }}</a>
|
|
54
89
|
<span aria-hidden="true"> / </span>
|
|
@@ -186,11 +221,14 @@
|
|
|
186
221
|
<script>
|
|
187
222
|
import { translate as t } from '@nextcloud/l10n'
|
|
188
223
|
import { NcButton, NcCheckboxRadioSwitch, NcNoteCard } from '@nextcloud/vue'
|
|
224
|
+
import TestTube from 'vue-material-design-icons/TestTube.vue'
|
|
189
225
|
import CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'
|
|
226
|
+
import CnDialog from '../CnDialog/CnDialog.vue'
|
|
190
227
|
import CnFlowDetail from '../CnFlowDetail/CnFlowDetail.vue'
|
|
191
228
|
import CnFlowRunSummary from '../CnFlowDetail/CnFlowRunSummary.vue'
|
|
192
229
|
import CnFlowSidebar from '../CnFlowDetail/CnFlowSidebar.vue'
|
|
193
230
|
import CnFlowStepLog from '../CnFlowDetail/CnFlowStepLog.vue'
|
|
231
|
+
import CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'
|
|
194
232
|
import { FLOW_RUN_ACTIVE_STATUSES, useFlowStore } from '../../composables/useFlowStore.js'
|
|
195
233
|
import { formatRelativeDate } from '../../utils/boardDate.js'
|
|
196
234
|
import { flowOutcomeLevels, flowRunDurationMs, flowRunLogLines, flowRunName, formatFlowDuration } from '../../utils/flowRunLog.js'
|
|
@@ -204,7 +242,7 @@ import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
|
204
242
|
export default {
|
|
205
243
|
name: 'CnFlowBoard',
|
|
206
244
|
|
|
207
|
-
components: { CnBuildiqEditButton, CnFlowDetail, CnFlowRunSummary, CnFlowSidebar, CnFlowStepLog, CnStatusBadge, NcButton, NcCheckboxRadioSwitch, NcNoteCard },
|
|
245
|
+
components: { CnBuildiqEditButton, CnDialog, CnFlowDetail, CnResourceSelect, TestTube, CnFlowRunSummary, CnFlowSidebar, CnFlowStepLog, CnStatusBadge, NcButton, NcCheckboxRadioSwitch, NcNoteCard },
|
|
208
246
|
|
|
209
247
|
props: {
|
|
210
248
|
/** The flow uuid, or `new`. */
|
|
@@ -225,6 +263,12 @@ export default {
|
|
|
225
263
|
notice: { type: String, default: '' },
|
|
226
264
|
/** The pill beside the title ("Case type flow"). */
|
|
227
265
|
pill: { type: String, default: '' },
|
|
266
|
+
/**
|
|
267
|
+
* The records a test run can take as its subject: `{ register, schema,
|
|
268
|
+
* labelField?, label? }`. Draws a header button ("Testen met een zaak",
|
|
269
|
+
* DqFlow) that runs the saved flow once with a picked record.
|
|
270
|
+
*/
|
|
271
|
+
testSubject: { type: Object, default: null },
|
|
228
272
|
},
|
|
229
273
|
|
|
230
274
|
emits: ['save', 'run', 'retried'],
|
|
@@ -234,10 +278,20 @@ export default {
|
|
|
234
278
|
},
|
|
235
279
|
|
|
236
280
|
data() {
|
|
237
|
-
return { runTab: 'step' }
|
|
281
|
+
return { runTab: 'step', testOpen: false, testRecord: null }
|
|
238
282
|
},
|
|
239
283
|
|
|
240
284
|
computed: {
|
|
285
|
+
/** @return {boolean} Whether a test subject is declared well enough to pick one. */
|
|
286
|
+
testSubjectReady() {
|
|
287
|
+
return Boolean(this.testSubject && this.testSubject.register && this.testSubject.schema)
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
/** @return {string} The test button's label: the page's own, else the library's. */
|
|
291
|
+
testLabel() {
|
|
292
|
+
return (this.testSubject && this.testSubject.label) || t('nextcloud-vue', 'Test with a record')
|
|
293
|
+
},
|
|
294
|
+
|
|
241
295
|
/** @return {boolean} Whether the palette card is drawn. */
|
|
242
296
|
showPalette() {
|
|
243
297
|
return this.palette === 'column' && !this.store.inRunView
|
|
@@ -454,6 +508,22 @@ export default {
|
|
|
454
508
|
},
|
|
455
509
|
|
|
456
510
|
methods: {
|
|
511
|
+
/**
|
|
512
|
+
* Run the saved flow once with the picked record as its subject, then
|
|
513
|
+
* close the dialog; the run shows on the canvas as any run does.
|
|
514
|
+
*
|
|
515
|
+
* @return {Promise<void>}
|
|
516
|
+
*/
|
|
517
|
+
async runTest() {
|
|
518
|
+
const uuid = Array.isArray(this.testRecord) ? this.testRecord[0] : this.testRecord
|
|
519
|
+
if (!uuid) {
|
|
520
|
+
return
|
|
521
|
+
}
|
|
522
|
+
await this.store.run({ uuid, register: this.testSubject.register, schema: this.testSubject.schema })
|
|
523
|
+
this.testOpen = false
|
|
524
|
+
this.testRecord = null
|
|
525
|
+
},
|
|
526
|
+
|
|
457
527
|
t,
|
|
458
528
|
|
|
459
529
|
/**
|
|
@@ -581,6 +651,11 @@ export default {
|
|
|
581
651
|
gap: 8px;
|
|
582
652
|
}
|
|
583
653
|
|
|
654
|
+
.cn-flow-board__test-intro {
|
|
655
|
+
margin: 0 0 12px;
|
|
656
|
+
color: var(--color-text-maxcontrast);
|
|
657
|
+
}
|
|
658
|
+
|
|
584
659
|
.cn-flow-board__path {
|
|
585
660
|
margin: 0;
|
|
586
661
|
color: var(--color-text-maxcontrast);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
:paletteFootnote="paletteFootnote ? cnTranslate(paletteFootnote) : ''"
|
|
12
12
|
:notice="notice ? cnTranslate(notice) : ''"
|
|
13
13
|
:pill="pill ? cnTranslate(pill) : ''"
|
|
14
|
+
:testSubject="testSubject ? { ...testSubject, label: testSubject.label ? cnTranslate(testSubject.label) : '' } : null"
|
|
14
15
|
@save="onSave"
|
|
15
16
|
@run="onRun" />
|
|
16
17
|
<CnFlowDetail v-else
|
|
@@ -122,6 +123,16 @@ export default {
|
|
|
122
123
|
default: '',
|
|
123
124
|
},
|
|
124
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Board look: the records a test run can take as its subject,
|
|
128
|
+
* `{ register, schema, labelField?, label? }`. Manifest key
|
|
129
|
+
* `config.testSubject`; `label` is translated with the app's catalogue.
|
|
130
|
+
*/
|
|
131
|
+
testSubject: {
|
|
132
|
+
type: Object,
|
|
133
|
+
default: null,
|
|
134
|
+
},
|
|
135
|
+
|
|
125
136
|
/**
|
|
126
137
|
* Board look: an info notice under the path. Manifest key
|
|
127
138
|
* `config.notice`, translated with the app's catalogue.
|