@conduction/nextcloud-vue 2.103.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.
Files changed (154) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +23 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +94 -72
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  6. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +18 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -3
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  13. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  14. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +93 -1
  15. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  16. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  17. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  18. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +6 -3
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  20. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +3 -3
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +39 -2
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +14 -0
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +99 -3
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  29. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
  30. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +1 -1
  31. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  32. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  33. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
  34. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +2 -0
  35. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +32 -29
  37. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
  39. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +73 -4
  40. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +74 -27
  42. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +213 -4
  44. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +105 -85
  46. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  47. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +10 -4
  48. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  49. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js +9 -0
  50. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js.map +1 -0
  51. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js +88 -0
  52. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js.map +1 -0
  53. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js +39 -0
  54. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js.map +1 -0
  55. package/dist/esm/components/CnObjectRow/CnObjectRow.vue.js +1 -1
  56. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +57 -5
  57. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  58. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +37 -4
  59. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  60. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
  61. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
  62. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +3 -3
  63. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
  64. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  65. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  66. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +16 -4
  67. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  68. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
  69. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
  70. package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
  71. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  72. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +11 -0
  73. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -4
  75. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  77. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  78. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  79. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +18 -2
  80. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  81. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  82. package/dist/esm/composables/indexSources.js +7 -7
  83. package/dist/esm/composables/indexSources.js.map +1 -1
  84. package/dist/esm/index.js +1 -0
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/l10n/en.json.js +8 -1
  87. package/dist/esm/l10n/en.json.js.map +1 -1
  88. package/dist/esm/l10n/nl.json.js +8 -1
  89. package/dist/esm/l10n/nl.json.js.map +1 -1
  90. package/dist/esm/library-style.js +1 -1
  91. package/dist/esm/nextcloud-vue.css +680 -328
  92. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  93. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  94. package/dist/esm/utils/nextcloudApps.js +45 -0
  95. package/dist/esm/utils/nextcloudApps.js.map +1 -0
  96. package/dist/esm/utils/nextcloudDirectory.js +18 -1
  97. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  98. package/dist/esm/utils/scopeListLayout.js +9 -2
  99. package/dist/esm/utils/scopeListLayout.js.map +1 -1
  100. package/dist/esm/utils/sortOrder.js +61 -0
  101. package/dist/esm/utils/sortOrder.js.map +1 -0
  102. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  103. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  104. package/dist/esm/utils/visibleWhen.js +12 -4
  105. package/dist/esm/utils/visibleWhen.js.map +1 -1
  106. package/dist/nextcloud-vue.cjs.js +4046 -2999
  107. package/dist/nextcloud-vue.cjs.js.map +2 -2
  108. package/dist/nextcloud-vue.css +680 -328
  109. package/l10n/en.json +8 -1
  110. package/l10n/nl.json +8 -1
  111. package/package.json +1 -1
  112. package/src/components/CnActionsBar/CnActionsBar.md +1 -0
  113. package/src/components/CnActionsBar/CnActionsBar.vue +39 -2
  114. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -1
  115. package/src/components/CnAppRoot/CnAppRoot.md +1 -1
  116. package/src/components/CnAppRoot/CnAppRoot.vue +21 -4
  117. package/src/components/CnChartWidget/CnChartWidget.md +2 -0
  118. package/src/components/CnChartWidget/CnChartWidget.vue +93 -1
  119. package/src/components/CnDashboardPage/CnDashboardPage.vue +9 -6
  120. package/src/components/CnDataTable/CnDataTable.md +1 -0
  121. package/src/components/CnDataTable/CnDataTable.vue +49 -0
  122. package/src/components/CnDetailPage/CnDetailPage.md +2 -0
  123. package/src/components/CnDetailPage/CnDetailPage.vue +99 -3
  124. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -1
  125. package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +4 -1
  126. package/src/components/CnFlowsPage/CnFlowBoard.vue +132 -8
  127. package/src/components/CnIndexPage/CnIndexPage.md +5 -2
  128. package/src/components/CnIndexPage/CnIndexPage.vue +228 -9
  129. package/src/components/CnIndexPage/useSelfFetchList.js +9 -3
  130. package/src/components/CnNoticeWidget/CnNoticeWidget.vue +153 -0
  131. package/src/components/CnNoticeWidget/index.js +12 -0
  132. package/src/components/CnObjectRow/CnObjectRow.vue +95 -1
  133. package/src/components/CnSettingsField/CnSettingsField.vue +4 -1
  134. package/src/components/CnSetupWizard/CnSetupWizard.vue +9 -1
  135. package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +7 -2
  136. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +15 -4
  137. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  138. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +18 -2
  139. package/src/components/index.js +1 -0
  140. package/src/composables/indexSources.js +7 -7
  141. package/src/css/dialog.css +20 -2
  142. package/src/css/index-page.css +19 -0
  143. package/src/css/look-board-detail.css +77 -4
  144. package/src/css/look-board-index.css +84 -2
  145. package/src/css/look-board.css +97 -12
  146. package/src/index.js +1 -0
  147. package/src/schemas/app-manifest-v2.schema.json +58 -3
  148. package/src/utils/libraryWidgetKeys.js +1 -0
  149. package/src/utils/nextcloudApps.js +50 -0
  150. package/src/utils/nextcloudDirectory.js +17 -0
  151. package/src/utils/scopeListLayout.js +9 -2
  152. package/src/utils/sortOrder.js +58 -0
  153. package/src/utils/validateManifestV2.compiled.js +3 -3
  154. package/src/utils/visibleWhen.js +12 -4
@@ -1267,7 +1267,7 @@ import { isActivityWidget, resolveHeaderMeta } from '../../utils/headerMeta.js'
1267
1267
  import { patchStoredSelf } from '../../utils/patchStoredSelf.js'
1268
1268
  import { setReadState } from '../../utils/recordInteractions.js'
1269
1269
  import { slotRenders } from '../../utils/slotContent.js'
1270
- import { evaluateVisibleWhen, isLocallyDecidableVisibleWhen } from '../../utils/visibleWhen.js'
1270
+ import { evaluateVisibleWhen, isLocallyDecidableVisibleWhen, readVisibleWhenPath } from '../../utils/visibleWhen.js'
1271
1271
  import {
1272
1272
  isCardWidgetDef,
1273
1273
  isContentOnlyWidgetDef,
@@ -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
@@ -2582,6 +2598,22 @@ export default {
2582
2598
  default: () => [],
2583
2599
  },
2584
2600
 
2601
+ /**
2602
+ * A field path whose value picks the side column and its order for
2603
+ * this record (manifest `config.sideColumnFrom`), read like a
2604
+ * `visibleWhen` field: `@context.caseType.sideCards` reads the list a
2605
+ * case type keeps. A non-empty list of ids keeps those `sideColumn`
2606
+ * entries, in that order, and leaves the rest out; each still follows
2607
+ * its own `visibleWhen`. Any other value keeps `sideColumn` as
2608
+ * declared, so a record whose list is empty changes nothing.
2609
+ *
2610
+ * @spec openspec/changes/detail-side-column-from/specs/detail-page/spec.md#requirement-a-record-can-pick-the-side-column-and-its-order
2611
+ */
2612
+ sideColumnFrom: {
2613
+ type: String,
2614
+ default: '',
2615
+ },
2616
+
2585
2617
  /**
2586
2618
  * Draw the header as a card (manifest `config.headerCard: true`): the
2587
2619
  * pills, the title and the actions on the page's surface colour inside
@@ -3492,7 +3524,7 @@ export default {
3492
3524
  * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
3493
3525
  */
3494
3526
  sideColumnWidgets() {
3495
- const resolved = this.declaredSideWidgets.filter((widget) => this.sideWidgetVisible(widget))
3527
+ const resolved = this.pickedSideWidgets.filter((widget) => this.sideWidgetVisible(widget))
3496
3528
  if (!this.isBoard) {
3497
3529
  return resolved
3498
3530
  }
@@ -3504,6 +3536,28 @@ export default {
3504
3536
  .map(({ widget }) => widget)
3505
3537
  },
3506
3538
 
3539
+ /**
3540
+ * The side column's definitions as the record picks them
3541
+ * (`sideColumnFrom`): the ids it lists, in its order, or every
3542
+ * declared entry when it lists none. Edit mode shows them all.
3543
+ *
3544
+ * @return {Array<object>}
3545
+ * @spec openspec/changes/detail-side-column-from/specs/detail-page/spec.md#requirement-a-record-can-pick-the-side-column-and-its-order
3546
+ */
3547
+ pickedSideWidgets() {
3548
+ const declared = this.declaredSideWidgets
3549
+ if (!this.sideColumnFrom || this.editingBody || !this.widgetVisibilityData) {
3550
+ return declared
3551
+ }
3552
+ const ids = readVisibleWhenPath(this.widgetVisibilityData, this.sideColumnFrom)
3553
+ if (!Array.isArray(ids) || ids.length === 0) {
3554
+ return declared
3555
+ }
3556
+ return ids
3557
+ .map((id) => declared.find((widget) => widget.id === id) || null)
3558
+ .filter(Boolean)
3559
+ },
3560
+
3507
3561
  /**
3508
3562
  * The side column's entries resolved to definitions, before any
3509
3563
  * `visibleWhen` is read: a string names a widget declared in
@@ -4513,7 +4567,7 @@ export default {
4513
4567
  */
4514
4568
  bodyGridWidgets() {
4515
4569
  if (this.hasGridLayout) {
4516
- return this.widgets
4570
+ return this.pickTabs(this.widgets)
4517
4571
  }
4518
4572
  if (this.shouldRenderAutoBody) {
4519
4573
  return this.autoBodyWidgets || []
@@ -4521,6 +4575,21 @@ export default {
4521
4575
  return []
4522
4576
  },
4523
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
+
4524
4593
  /**
4525
4594
  * Whether the body renders as an adjustable widget grid (vs. the
4526
4595
  * hand-authored default slot). True for explicit grid pages and for the
@@ -4939,6 +5008,33 @@ export default {
4939
5008
  },
4940
5009
 
4941
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
+
4942
5038
  /**
4943
5039
  * The tag after an admin-only entry in the board look ("beheerder",
4944
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="$emit('run')">
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.app }}
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="$emit('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
- <CnFlowSidebar v-else embedded />
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
- /** @return {Array<object>} The palette grouped by owning app. */
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
- return Object.values(groups)
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;
@@ -389,6 +389,8 @@ export default {
389
389
  | `customComponents` | Object | `null` | Custom-component / handler registry. When set, takes precedence over the injected `cnCustomComponents` from CnAppRoot. Used to resolve `actions[].handler` registry names declared in the manifest (manifest-actions-dispatch). Named handlers resolve out of the v2 `registry` (a `kind: "handler"` entry) FIRST and fall back to this map. |
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
+ | `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. |
392
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`. |
393
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`. |
394
396
  | `searchPlaceholder` | String | `''` | Placeholder for the inline search field (manifest `config.searchPlaceholder`). |
@@ -878,8 +880,9 @@ The list view (`view-mode="list"`) and standalone sort dropdown add these props:
878
880
  | `listConfig` | Object | `{}` | Field mapping for the default list rows (`CnObjectRow`). |
879
881
  | `listComponent` | String | `''` | Custom row component, resolved against the v2 `registry` (any kind carrying a `component`) and then the legacy `customComponents` map. |
880
882
  | `showSortSelect` | Boolean | `false` | Show a standalone sort dropdown in the actions bar. |
881
- | `sortSelectOptions` | Array | `[]` | Options `{ value, label }` for the sort dropdown. |
882
- | `sortSelectValue` | String | `''` | Selected sort dropdown value (controlled). |
883
+ | `sortSelectOptions` | Array | `[]` | Options `{ value, label, order? }` for the sort dropdown. On a self-fetch page a choice orders the list as a header click does (`_order`, kept in the address): by the option's `order`, else its `value` read as an order (`"-createdAt"`, `"priority:desc,title"`). |
884
+ | `sortSelectValue` | String | `''` | Selected sort dropdown value. Empty lets a self-fetch page show the option matching its current order, so a header click that matches an option shows it. |
885
+ | `defaultOrder` | String, Object or Array | `''` | The order a self-fetch page opens on when neither the address nor `sortKeys`/`sortKey` gives one (manifest `config.defaultOrder`): `"-createdAt"`, `"priority:desc,title"`, an `_order` map `{ "priority": "desc" }` or a `[{ key, order }]` list. |
883
886
 
884
887
  The `#list-item`, `#row-icon`, `#row-badges`, and `#row-actions` slots override the list rows (see [CnObjectList](./cn-object-list.md)). Emits `@sort-change` with the chosen sort value.
885
888