@conduction/nextcloud-vue 2.100.0 → 2.102.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +22 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +8 -6
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  6. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +29 -4
  7. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  8. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +9 -1
  9. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  10. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  11. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +22 -1
  12. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  13. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +26 -16
  14. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  15. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  16. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +70 -6
  17. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  18. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +105 -74
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  20. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js +9 -0
  21. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js.map +1 -0
  22. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js +139 -0
  23. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js.map +1 -0
  24. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js +76 -0
  25. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js.map +1 -0
  26. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  27. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +160 -2
  28. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  29. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +206 -94
  30. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  31. package/dist/esm/components/CnFilesBrowser/filesReview.js +61 -0
  32. package/dist/esm/components/CnFilesBrowser/filesReview.js.map +1 -0
  33. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +9 -0
  34. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js.map +1 -0
  35. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +279 -0
  36. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -0
  37. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +122 -0
  38. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -0
  39. package/dist/esm/components/CnGroupedCardsWidget/index.js +29 -0
  40. package/dist/esm/components/CnGroupedCardsWidget/index.js.map +1 -0
  41. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +320 -74
  42. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +323 -213
  44. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/useNamedSource.js +30 -3
  46. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  47. package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
  48. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +15 -0
  49. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
  50. package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
  51. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue.js +1 -1
  52. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js +25 -1
  53. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js.map +1 -1
  54. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue3.js.map +1 -1
  55. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  56. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +14 -1
  57. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  58. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +14 -6
  59. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  60. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  61. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +10 -0
  62. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  63. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +6 -1
  64. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  65. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
  66. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +91 -4
  67. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
  68. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +63 -38
  69. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
  70. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue.js +1 -1
  71. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +106 -4
  72. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  73. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +70 -25
  74. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  75. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +9 -0
  76. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js.map +1 -0
  77. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js +175 -0
  78. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -0
  79. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js +77 -0
  80. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -0
  81. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  82. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +35 -2
  83. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  84. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  85. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +4 -0
  86. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  87. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  88. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  89. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  90. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +2 -23
  91. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  92. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  93. package/dist/esm/composables/indexSources.js +176 -20
  94. package/dist/esm/composables/indexSources.js.map +1 -1
  95. package/dist/esm/composables/useEndpointSource.js +28 -6
  96. package/dist/esm/composables/useEndpointSource.js.map +1 -1
  97. package/dist/esm/index.js +1 -0
  98. package/dist/esm/index.js.map +1 -1
  99. package/dist/esm/l10n/en.json.js +41 -1
  100. package/dist/esm/l10n/en.json.js.map +1 -1
  101. package/dist/esm/l10n/nl.json.js +39 -1
  102. package/dist/esm/l10n/nl.json.js.map +1 -1
  103. package/dist/esm/library-style.js +1 -1
  104. package/dist/esm/nextcloud-vue.css +939 -376
  105. package/dist/esm/utils/builtInFormatters.js +24 -4
  106. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  107. package/dist/esm/utils/languageMapText.js +50 -0
  108. package/dist/esm/utils/languageMapText.js.map +1 -0
  109. package/dist/esm/utils/libraryWidgetKeys.js +3 -0
  110. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  111. package/dist/esm/utils/mapList.js +71 -0
  112. package/dist/esm/utils/mapList.js.map +1 -0
  113. package/dist/esm/utils/rowClassRules.js +43 -0
  114. package/dist/esm/utils/rowClassRules.js.map +1 -0
  115. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  116. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  117. package/dist/esm/utils/visibleWhen.js +19 -2
  118. package/dist/esm/utils/visibleWhen.js.map +1 -1
  119. package/dist/nextcloud-vue.cjs.js +9681 -7136
  120. package/dist/nextcloud-vue.cjs.js.map +2 -2
  121. package/dist/nextcloud-vue.css +939 -376
  122. package/l10n/en.json +41 -1
  123. package/l10n/nl.json +39 -1
  124. package/package.json +1 -1
  125. package/src/components/CnActionsBar/CnActionsBar.md +1 -0
  126. package/src/components/CnActionsBar/CnActionsBar.vue +31 -4
  127. package/src/components/CnChartWidget/CnChartWidget.md +3 -2
  128. package/src/components/CnChartWidget/CnChartWidget.vue +42 -4
  129. package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
  130. package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
  131. package/src/components/CnDashboardPage/CnDashboardPage.vue +110 -21
  132. package/src/components/CnExportsWidget/CnExportsWidget.vue +233 -0
  133. package/src/components/CnExportsWidget/index.js +7 -0
  134. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +324 -0
  135. package/src/components/CnFilesBrowser/filesReview.js +57 -0
  136. package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +433 -0
  137. package/src/components/CnGroupedCardsWidget/index.js +28 -0
  138. package/src/components/CnIndexPage/CnIndexPage.md +5 -1
  139. package/src/components/CnIndexPage/CnIndexPage.vue +313 -9
  140. package/src/components/CnIndexPage/useNamedSource.js +30 -3
  141. package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
  142. package/src/components/CnMenuWidget/CnMenuWidget.vue +25 -1
  143. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +29 -1
  144. package/src/components/CnObjectSidebar/CnFilesTab.vue +15 -0
  145. package/src/components/CnOrderedListField/CnOrderedListField.vue +151 -5
  146. package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +1 -0
  147. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +159 -5
  148. package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +272 -0
  149. package/src/components/CnSavedViewAlertWidget/index.js +7 -0
  150. package/src/components/CnSetupWizard/CnSetupWizard.vue +35 -2
  151. package/src/components/CnWidgetGrid/builtInWidgets.js +4 -0
  152. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  153. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +2 -23
  154. package/src/components/index.js +3 -0
  155. package/src/composables/indexSources.js +175 -19
  156. package/src/composables/useEndpointSource.js +28 -6
  157. package/src/css/index-page.css +117 -0
  158. package/src/css/look-board-index.css +10 -0
  159. package/src/css/look-board.css +44 -0
  160. package/src/index.js +1 -0
  161. package/src/schemas/app-manifest-v2.schema.json +85 -3
  162. package/src/utils/builtInFormatters.js +24 -4
  163. package/src/utils/languageMapText.js +46 -0
  164. package/src/utils/libraryWidgetKeys.js +3 -0
  165. package/src/utils/mapList.js +67 -0
  166. package/src/utils/rowClassRules.js +39 -0
  167. package/src/utils/validateManifestV2.compiled.js +3 -3
  168. package/src/utils/visibleWhen.js +19 -2
@@ -0,0 +1,272 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+ <template>
6
+ <div class="cn-saved-view-alert" data-testid="cn-saved-view-alert">
7
+ <p v-if="!view" class="cn-saved-view-alert__empty" data-testid="cn-saved-view-alert-empty">
8
+ {{ emptyText }}
9
+ </p>
10
+ <template v-else>
11
+ <dl class="cn-saved-view-alert__facts">
12
+ <dt>{{ t('nextcloud-vue', 'Name') }}</dt>
13
+ <dd data-testid="cn-saved-view-alert-name">
14
+ {{ view.name }}
15
+ </dd>
16
+ <template v-if="countText">
17
+ <dt>{{ t('nextcloud-vue', 'Now') }}</dt>
18
+ <dd data-testid="cn-saved-view-alert-count">
19
+ {{ countText }}
20
+ </dd>
21
+ </template>
22
+ </dl>
23
+ <NcCheckboxRadioSwitch
24
+ type="switch"
25
+ :modelValue="enabled"
26
+ :disabled="saving || !canEdit"
27
+ data-testid="cn-saved-view-alert-switch"
28
+ @update:modelValue="onToggle">
29
+ <span class="cn-saved-view-alert__switch-text">
30
+ <span>{{ t('nextcloud-vue', 'Tell me above a number') }}</span>
31
+ <span class="cn-saved-view-alert__hint">{{ hintText }}</span>
32
+ </span>
33
+ </NcCheckboxRadioSwitch>
34
+ <NcTextField v-if="enabled"
35
+ :modelValue="String(threshold)"
36
+ type="number"
37
+ min="0"
38
+ :label="t('nextcloud-vue', 'Threshold')"
39
+ :disabled="saving || !canEdit"
40
+ data-testid="cn-saved-view-alert-threshold"
41
+ @update:modelValue="onThreshold" />
42
+ <p v-if="error"
43
+ class="cn-saved-view-alert__error"
44
+ role="alert"
45
+ data-testid="cn-saved-view-alert-error">
46
+ {{ error }}
47
+ </p>
48
+ </template>
49
+ </div>
50
+ </template>
51
+
52
+ <script>
53
+ import { getCurrentUser } from '@nextcloud/auth'
54
+ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
55
+ import { NcCheckboxRadioSwitch, NcTextField } from '@nextcloud/vue'
56
+ import { useSavedViewsApi } from '../../composables/useSavedViewsApi.js'
57
+
58
+ /**
59
+ * CnSavedViewAlertWidget — the saved view an index page has applied, with its
60
+ * count alert (DqZoekresultaten "Opgeslagen zoekopdracht"): its name, how many
61
+ * rows it holds now, and a switch that asks OpenRegister to tell the reader
62
+ * when the count reaches a threshold.
63
+ *
64
+ * The alert is the view's own `alert` (`{ operator: 'gte', threshold,
65
+ * recipients }`), saved with a PATCH on the view; OpenRegister evaluates it on
66
+ * its sweep and fires on the crossing, not on the state. Switching it off
67
+ * clears it. The widget reads the page through the `cnIndexList` CnIndexPage
68
+ * provides, so it belongs in an index page's `sidePanel`
69
+ * (`type: "saved-view-alert"`).
70
+ *
71
+ * Example (manifest):
72
+ * ```json
73
+ * { "id": "alert", "type": "saved-view-alert", "title": "Saved search", "content": { "noun": { "one": "case", "other": "cases" } } }
74
+ * ```
75
+ */
76
+ export default {
77
+ name: 'CnSavedViewAlertWidget',
78
+
79
+ components: { NcCheckboxRadioSwitch, NcTextField },
80
+
81
+ inject: {
82
+ /** The index page's list: its applied saved view and its total. */
83
+ cnIndexList: { default: null },
84
+ /** Host translate function provided by CnAppRoot. */
85
+ cnTranslate: { default: () => (key) => key },
86
+ },
87
+
88
+ props: {
89
+ /**
90
+ * The widget content: `{ noun?: { one, other }, emptyText? }`. `noun`
91
+ * names what is counted ("9 zaken"); `emptyText` shows while no saved
92
+ * view is applied. Both are translated with the app's catalogue.
93
+ */
94
+ content: {
95
+ type: Object,
96
+ default: () => ({}),
97
+ },
98
+ },
99
+
100
+ data() {
101
+ return { saving: false, error: '', draft: null }
102
+ },
103
+
104
+ computed: {
105
+ /** @return {object|null} The applied saved view, or null. */
106
+ view() {
107
+ return (this.cnIndexList && this.cnIndexList.appliedView) || null
108
+ },
109
+
110
+ /** @return {boolean} Whether the view carries an alert. */
111
+ enabled() {
112
+ const alert = this.view && this.view.alert
113
+ return Boolean(alert && typeof alert === 'object' && Number.isFinite(Number(alert.threshold)))
114
+ },
115
+
116
+ /** @return {number} The threshold being edited, else the view's, else 10. */
117
+ threshold() {
118
+ if (this.draft !== null) {
119
+ return this.draft
120
+ }
121
+ const value = this.enabled ? Number(this.view.alert.threshold) : 10
122
+ return Number.isFinite(value) ? value : 10
123
+ },
124
+
125
+ /** @return {boolean} Whether the reader may change the view: its owner, or when the view says nothing. */
126
+ canEdit() {
127
+ const owner = this.view && this.view.owner
128
+ if (!owner) {
129
+ return true
130
+ }
131
+ const user = getCurrentUser()
132
+ return Boolean(user && user.uid === owner)
133
+ },
134
+
135
+ /** @return {string} "9 zaken", or '' while the total is unknown. */
136
+ countText() {
137
+ const total = this.cnIndexList ? this.cnIndexList.total : undefined
138
+ if (typeof total !== 'number') {
139
+ return ''
140
+ }
141
+ const noun = this.content && this.content.noun
142
+ if (noun && typeof noun === 'object' && noun.one && noun.other) {
143
+ return `${total} ${this.cnTranslate(total === 1 ? noun.one : noun.other)}`
144
+ }
145
+ return n('nextcloud-vue', '%n result', '%n results', total)
146
+ },
147
+
148
+ /** @return {string} The line under the switch. */
149
+ hintText() {
150
+ return t('nextcloud-vue', 'A message to you when there are {threshold} or more', { threshold: this.threshold })
151
+ },
152
+
153
+ /** @return {string} What shows while no saved view is applied. */
154
+ emptyText() {
155
+ const own = this.content && this.content.emptyText
156
+ return own ? this.cnTranslate(own) : t('nextcloud-vue', 'Save this search or open a saved one to set an alert on it.')
157
+ },
158
+ },
159
+
160
+ methods: {
161
+ t,
162
+
163
+ /**
164
+ * Switch the alert on (at the shown threshold) or off.
165
+ *
166
+ * @param {boolean} on The new state.
167
+ * @return {Promise<void>}
168
+ */
169
+ async onToggle(on) {
170
+ await this.save(on ? this.alertFor(this.threshold) : null)
171
+ },
172
+
173
+ /**
174
+ * Save a new threshold, when the alert is on.
175
+ *
176
+ * @param {string} value The typed value.
177
+ * @return {Promise<void>}
178
+ */
179
+ async onThreshold(value) {
180
+ const number = Math.max(0, Math.floor(Number(value)))
181
+ if (!Number.isFinite(number)) {
182
+ return
183
+ }
184
+ this.draft = number
185
+ await this.save(this.alertFor(number))
186
+ this.draft = null
187
+ },
188
+
189
+ /**
190
+ * The alert the view stores: reaching the threshold tells the reader.
191
+ *
192
+ * @param {number} threshold The threshold.
193
+ * @return {{operator: string, threshold: number, recipients: Array<string>}}
194
+ */
195
+ alertFor(threshold) {
196
+ const user = getCurrentUser()
197
+ return { operator: 'gte', threshold, recipients: user && user.uid ? [user.uid] : [] }
198
+ },
199
+
200
+ /**
201
+ * PATCH the view's alert and hand the saved view back to the page.
202
+ *
203
+ * @param {object|null} alert The alert, or null to clear it.
204
+ * @return {Promise<void>}
205
+ */
206
+ async save(alert) {
207
+ if (!this.view) {
208
+ return
209
+ }
210
+ this.saving = true
211
+ this.error = ''
212
+ try {
213
+ const saved = await useSavedViewsApi().patchView(this.view.uuid || this.view.id, { alert })
214
+ if (saved && this.cnIndexList && typeof this.cnIndexList.replaceSavedView === 'function') {
215
+ this.cnIndexList.replaceSavedView(saved)
216
+ }
217
+ } catch (error) {
218
+ this.error = (error && error.response && error.response.data && error.response.data.error) || t('nextcloud-vue', 'The alert could not be saved.')
219
+ } finally {
220
+ this.saving = false
221
+ }
222
+ },
223
+ },
224
+ }
225
+ </script>
226
+
227
+ <style scoped>
228
+ .cn-saved-view-alert {
229
+ display: flex;
230
+ flex-direction: column;
231
+ gap: 12px;
232
+ font-size: 14px;
233
+ }
234
+
235
+ .cn-saved-view-alert__facts {
236
+ display: grid;
237
+ grid-template-columns: max-content 1fr;
238
+ gap: 6px 16px;
239
+ margin: 0;
240
+ }
241
+
242
+ .cn-saved-view-alert__facts dt {
243
+ color: var(--color-text-maxcontrast);
244
+ }
245
+
246
+ .cn-saved-view-alert__facts dd {
247
+ margin: 0;
248
+ font-weight: 500;
249
+ }
250
+
251
+ .cn-saved-view-alert__switch-text {
252
+ display: flex;
253
+ flex-direction: column;
254
+ gap: 2px;
255
+ }
256
+
257
+ .cn-saved-view-alert__hint,
258
+ .cn-saved-view-alert__empty {
259
+ color: var(--color-text-maxcontrast);
260
+ font-size: 13px;
261
+ }
262
+
263
+ .cn-saved-view-alert__empty,
264
+ .cn-saved-view-alert__error {
265
+ margin: 0;
266
+ }
267
+
268
+ .cn-saved-view-alert__error {
269
+ color: var(--color-error-text, var(--color-error));
270
+ font-size: 13px;
271
+ }
272
+ </style>
@@ -0,0 +1,7 @@
1
+ // SPDX-FileCopyrightText: 2026 Conduction B.V.
2
+ // SPDX-License-Identifier: EUPL-1.2
3
+
4
+ import CnSavedViewAlertWidget from './CnSavedViewAlertWidget.vue'
5
+
6
+ export default CnSavedViewAlertWidget
7
+ export { CnSavedViewAlertWidget }
@@ -438,7 +438,7 @@ export default {
438
438
  */
439
439
  setup(props) {
440
440
  const { isBoard } = useLook()
441
- const wantsSource = (props.steps || []).some((s) => s && s.optionsSource)
441
+ const wantsSource = (props.steps || []).some((s) => s && (s.optionsSource || s.visibleWhen))
442
442
  if (!wantsSource) {
443
443
  return { isBoard }
444
444
  }
@@ -506,7 +506,7 @@ export default {
506
506
 
507
507
  setupSteps() {
508
508
  const skipped = new Set(this.skippedSteps.map((entry) => entry.step.id))
509
- return this.declaredSteps.filter((s) => !skipped.has(s.id))
509
+ return this.declaredSteps.filter((s) => !skipped.has(s.id) && this.stepConditionHolds(s))
510
510
  },
511
511
 
512
512
  /**
@@ -869,6 +869,39 @@ export default {
869
869
  })
870
870
  },
871
871
 
872
+ /**
873
+ * Whether a step's `visibleWhen` holds: the value at that path in the
874
+ * setup status (`"flags.hasPayouts"`, dots walk into objects) is truthy,
875
+ * or equals `equals` when the step gives `{ path, equals }`. A step
876
+ * without `visibleWhen` always shows; one with it stays hidden until the
877
+ * status says so, also while the status loads (DqInstellen: the dwangsom
878
+ * step only when the app pays penalties).
879
+ *
880
+ * @param {object} step The step declaration.
881
+ * @return {boolean}
882
+ */
883
+ stepConditionHolds(step) {
884
+ const when = step && step.visibleWhen
885
+ if (when === undefined || when === null || when === '') {
886
+ return true
887
+ }
888
+ const path = typeof when === 'string' ? when : when.path
889
+ if (typeof path !== 'string' || path === '') {
890
+ return true
891
+ }
892
+ let node = this.setupStatus
893
+ for (const key of path.split('.')) {
894
+ if (node === null || node === undefined || typeof node !== 'object') {
895
+ return false
896
+ }
897
+ node = node[key]
898
+ }
899
+ if (typeof when === 'object' && Object.hasOwn(when, 'equals')) {
900
+ return node === when.equals
901
+ }
902
+ return Boolean(node)
903
+ },
904
+
872
905
  /**
873
906
  * The option list a `choice` step declares `optionsSource` for, read
874
907
  * from the setup status document the app already serves.
@@ -17,6 +17,7 @@
17
17
  import CnAuditTrailWidget from '../CnAuditTrailWidget/CnAuditTrailWidget.vue'
18
18
  import CnBannerWidget from '../CnBannerWidget/CnBannerWidget.vue'
19
19
  import CnDividerWidget from '../CnDividerWidget/CnDividerWidget.vue'
20
+ import CnExportsWidget from '../CnExportsWidget/CnExportsWidget.vue'
20
21
  import CnHeaderWidget from '../CnHeaderWidget/CnHeaderWidget.vue'
21
22
  import CnIntegrationWidget from '../CnIntegrationWidget/CnIntegrationWidget.vue'
22
23
  import CnNavCardGrid from '../CnNavCardGrid/CnNavCardGrid.vue'
@@ -24,6 +25,7 @@ import CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'
24
25
  import CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'
25
26
  import CnObjectMetadataWidget from '../CnObjectMetadataWidget/CnObjectMetadataWidget.vue'
26
27
  import CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'
28
+ import CnSavedViewAlertWidget from '../CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue'
27
29
  import CnTextWidget from '../CnTextWidget/CnTextWidget.vue'
28
30
  import CnTimelineWidget from '../CnTimelineWidget/CnTimelineWidget.vue'
29
31
  import CnWidgetCardGrid from '../CnWidgetCardGrid/CnWidgetCardGrid.vue'
@@ -89,4 +91,6 @@ export const BUILT_IN_WIDGETS = {
89
91
  header: CnHeaderWidget,
90
92
  text: CnTextWidget,
91
93
  divider: CnDividerWidget,
94
+ 'saved-view-alert': CnSavedViewAlertWidget,
95
+ exports: CnExportsWidget,
92
96
  }
@@ -75,6 +75,7 @@ import '../CnTasksWidget/index.js'
75
75
  import '../CnSectionsWidget/index.js'
76
76
  import '../CnFactsWidget/index.js'
77
77
  import '../CnDeviceFramesWidget/index.js'
78
+ import '../CnGroupedCardsWidget/index.js'
78
79
  import '../CnChecklistRunWidget/index.js'
79
80
  import '../CnWorkingCalendarWidget/index.js'
80
81
  import '../CnPartyActionsWidget/index.js'
@@ -88,8 +88,8 @@ import { dispatchAction, resolveObjectOpType } from '../../utils/actionsDispatch
88
88
  import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'
89
89
  import { pageFilter, pageSort } from '../../utils/pageControls.js'
90
90
  import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
91
+ import { compileRowClass } from '../../utils/rowClassRules.js'
91
92
  import { fieldsFromSchema } from '../../utils/schema.js'
92
- import { compareVisibleWhen, readVisibleWhenPath } from '../../utils/visibleWhen.js'
93
93
  import { CnIcon } from '../CnIcon/index.js'
94
94
  import { CnRowActions } from '../CnRowActions/index.js'
95
95
  import { CnWidgetWrapper } from '../CnWidgetWrapper/index.js'
@@ -762,28 +762,7 @@ export default {
762
762
  * @return {?(row: object) => string} The row-class function, or null.
763
763
  */
764
764
  compiledRowClass() {
765
- const rc = this.rowClass
766
- if (typeof rc === 'function') {
767
- return rc
768
- }
769
- if (!Array.isArray(rc) || rc.length === 0) {
770
- return null
771
- }
772
- const rules = rc
773
- return (row) => {
774
- const classes = []
775
- for (const rule of rules) {
776
- if (!rule || !rule.class) {
777
- continue
778
- }
779
- const when = rule.when || {}
780
- const actual = readVisibleWhenPath(row, when.field)
781
- if (compareVisibleWhen(actual, when.op || 'eq', when.value)) {
782
- classes.push(rule.class)
783
- }
784
- }
785
- return classes.join(' ')
786
- }
765
+ return compileRowClass(this.rowClass)
787
766
  },
788
767
 
789
768
  /**
@@ -30,6 +30,8 @@ export { CnDateAxisView } from './CnDateAxisView/index.js'
30
30
  export { CnObjectRow } from './CnObjectRow/index.js'
31
31
  export { CnObjectList } from './CnObjectList/index.js'
32
32
  export { CnOrderedListField } from './CnOrderedListField/index.js'
33
+ export { CnExportsWidget } from './CnExportsWidget/index.js'
34
+ export { CnSavedViewAlertWidget } from './CnSavedViewAlertWidget/index.js'
33
35
  export { CnObjectKanban } from './CnObjectKanban/index.js'
34
36
  export { CnObjectCalendar } from './CnObjectCalendar/index.js'
35
37
  export { CnFolderTree } from './CnFolderTree/index.js'
@@ -224,6 +226,7 @@ export { CnTasksWidgetForm } from './CnTasksWidgetForm/index.js'
224
226
  export { CnSectionsWidget } from './CnSectionsWidget/index.js'
225
227
  export { CnFactsWidget } from './CnFactsWidget/index.js'
226
228
  export { CnDeviceFramesWidget } from './CnDeviceFramesWidget/index.js'
229
+ export { CnGroupedCardsWidget } from './CnGroupedCardsWidget/index.js'
227
230
  export { CnChecklistRunWidget } from './CnChecklistRunWidget/index.js'
228
231
  export { CnWorkingCalendarWidget } from './CnWorkingCalendarWidget/index.js'
229
232
  export { CnPartyActionsWidget } from './CnPartyActionsWidget/index.js'