@conduction/nextcloud-vue 2.108.0 → 2.109.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 (155) hide show
  1. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js +10 -0
  2. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js.map +1 -0
  3. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js +305 -0
  4. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js +38 -0
  6. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +10 -0
  8. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js.map +1 -0
  9. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +398 -0
  10. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -0
  11. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +155 -0
  12. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -0
  13. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js +9 -0
  14. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js.map +1 -0
  15. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js +281 -0
  16. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js.map +1 -0
  17. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js +137 -0
  18. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js.map +1 -0
  19. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js +9 -0
  20. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js.map +1 -0
  21. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js +225 -0
  22. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js.map +1 -0
  23. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js +110 -0
  24. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js.map +1 -0
  25. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  26. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +19 -3
  27. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  28. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  29. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
  30. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +513 -481
  32. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  33. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +9 -0
  34. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js.map +1 -0
  35. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +181 -0
  36. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -0
  37. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js +50 -0
  38. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -0
  39. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  40. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +102 -1
  41. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  42. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -6
  43. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  44. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js +9 -0
  45. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js.map +1 -0
  46. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js +190 -0
  47. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js.map +1 -0
  48. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js +91 -0
  49. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js.map +1 -0
  50. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js +9 -0
  51. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js.map +1 -0
  52. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js +64 -0
  53. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js.map +1 -0
  54. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js +49 -0
  55. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js.map +1 -0
  56. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js +9 -0
  57. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js.map +1 -0
  58. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js +382 -0
  59. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js.map +1 -0
  60. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js +285 -0
  61. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js.map +1 -0
  62. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +9 -0
  63. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js.map +1 -0
  64. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +277 -0
  65. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -0
  66. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +183 -0
  67. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -0
  68. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  69. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  70. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +53 -22
  71. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  72. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js +9 -0
  73. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js.map +1 -0
  74. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js +246 -0
  75. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js.map +1 -0
  76. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js +197 -0
  77. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js.map +1 -0
  78. package/dist/esm/index.js +12 -0
  79. package/dist/esm/index.js.map +1 -1
  80. package/dist/esm/l10n/en.json.js +106 -1
  81. package/dist/esm/l10n/en.json.js.map +1 -1
  82. package/dist/esm/l10n/nl.json.js +105 -1
  83. package/dist/esm/l10n/nl.json.js.map +1 -1
  84. package/dist/esm/library-style.js +1 -1
  85. package/dist/esm/nextcloud-vue.css +495 -61
  86. package/dist/esm/utils/actionsDispatcher.js +6 -1
  87. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  88. package/dist/esm/utils/builtInModals.js +3 -1
  89. package/dist/esm/utils/builtInModals.js.map +1 -1
  90. package/dist/esm/utils/duplicateWarning.js +62 -0
  91. package/dist/esm/utils/duplicateWarning.js.map +1 -0
  92. package/dist/esm/utils/errors.js +4 -0
  93. package/dist/esm/utils/errors.js.map +1 -1
  94. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  95. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  96. package/dist/esm/utils/openDuplicateWarning.js +39 -0
  97. package/dist/esm/utils/openDuplicateWarning.js.map +1 -0
  98. package/dist/esm/utils/optionsFrom.js +69 -2
  99. package/dist/esm/utils/optionsFrom.js.map +1 -1
  100. package/dist/esm/utils/planTree.js +57 -0
  101. package/dist/esm/utils/planTree.js.map +1 -0
  102. package/dist/esm/utils/redactionSegments.js +53 -0
  103. package/dist/esm/utils/redactionSegments.js.map +1 -0
  104. package/dist/esm/utils/registerNcDashboardWidgets.js +61 -0
  105. package/dist/esm/utils/registerNcDashboardWidgets.js.map +1 -0
  106. package/dist/esm/utils/searchRefusal.js +62 -0
  107. package/dist/esm/utils/searchRefusal.js.map +1 -0
  108. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  109. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  110. package/dist/nextcloud-vue.cjs.js +17409 -12832
  111. package/dist/nextcloud-vue.cjs.js.map +2 -2
  112. package/dist/nextcloud-vue.css +495 -61
  113. package/l10n/en.json +106 -1
  114. package/l10n/nl.json +105 -1
  115. package/package.json +1 -1
  116. package/src/components/CnAddressSearch/CnAddressSearch.vue +343 -0
  117. package/src/components/CnAddressSearch/index.js +12 -0
  118. package/src/components/CnAssignDialog/CnAssignDialog.vue +525 -0
  119. package/src/components/CnAssignDialog/index.js +12 -0
  120. package/src/components/CnDecisionListWidget/CnDecisionListWidget.vue +388 -0
  121. package/src/components/CnDecisionListWidget/index.js +12 -0
  122. package/src/components/CnDuplicateWarning/CnDuplicateWarning.vue +318 -0
  123. package/src/components/CnDuplicateWarning/index.js +12 -0
  124. package/src/components/CnFormDialog/CnFormDialog.vue +19 -3
  125. package/src/components/CnIndexPage/CnIndexPage.vue +52 -0
  126. package/src/components/CnNcDashboardList/CnNcDashboardList.vue +218 -0
  127. package/src/components/CnNcDashboardList/index.js +12 -0
  128. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +107 -6
  129. package/src/components/CnPlanWidget/CnPlanWidget.vue +246 -0
  130. package/src/components/CnPlanWidget/CnPlanWidgetRow.vue +117 -0
  131. package/src/components/CnPlanWidget/index.js +12 -0
  132. package/src/components/CnRedactionEditor/CnRedactionEditor.vue +655 -0
  133. package/src/components/CnRedactionEditor/index.js +13 -0
  134. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +413 -0
  135. package/src/components/CnRetentionWidget/index.js +12 -0
  136. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  137. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +31 -0
  138. package/src/components/CnWorkQueue/CnWorkQueue.vue +456 -0
  139. package/src/components/CnWorkQueue/index.js +12 -0
  140. package/src/components/index.js +9 -0
  141. package/src/css/index-page.css +24 -0
  142. package/src/index.js +12 -0
  143. package/src/schemas/app-manifest-v2.schema.json +119 -3
  144. package/src/utils/actionsDispatcher.js +6 -1
  145. package/src/utils/builtInModals.js +3 -1
  146. package/src/utils/duplicateWarning.js +59 -0
  147. package/src/utils/errors.js +4 -0
  148. package/src/utils/libraryWidgetKeys.js +4 -0
  149. package/src/utils/openDuplicateWarning.js +35 -0
  150. package/src/utils/optionsFrom.js +68 -1
  151. package/src/utils/planTree.js +54 -0
  152. package/src/utils/redactionSegments.js +50 -0
  153. package/src/utils/registerNcDashboardWidgets.js +57 -0
  154. package/src/utils/searchRefusal.js +59 -0
  155. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -0,0 +1,456 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+ <template>
6
+ <div class="cn-work-queue" data-testid="cn-work-queue">
7
+ <div v-if="title || $slots.actions" class="cn-work-queue__head">
8
+ <h2 v-if="title" class="cn-work-queue__title">
9
+ {{ title }}
10
+ </h2>
11
+ <div class="cn-work-queue__actions">
12
+ <!-- @slot Buttons beside the title ("Plan an item"). -->
13
+ <slot name="actions" />
14
+ </div>
15
+ </div>
16
+
17
+ <NcNoteCard v-for="source in unavailable"
18
+ :key="source.source"
19
+ type="warning"
20
+ :data-testid="`cn-work-queue-unavailable-${source.source}`">
21
+ {{ t('nextcloud-vue', '{source} could not be read, so this list is incomplete.', { source: source.label || source.source }) }}
22
+ </NcNoteCard>
23
+
24
+ <NcLoadingIcon v-if="loading" :size="32" />
25
+ <NcEmptyContent v-else-if="error"
26
+ :name="t('nextcloud-vue', 'This queue could not be read')"
27
+ :description="error"
28
+ data-testid="cn-work-queue-error">
29
+ <template #action>
30
+ <NcButton @click="load">
31
+ {{ t('nextcloud-vue', 'Try again') }}
32
+ </NcButton>
33
+ </template>
34
+ </NcEmptyContent>
35
+ <template v-else>
36
+ <p v-if="shownGroups.length === 0" class="cn-work-queue__muted" data-testid="cn-work-queue-empty">
37
+ {{ text(content.emptyText) || t('nextcloud-vue', 'Nothing is waiting on you.') }}
38
+ </p>
39
+ <section v-for="group in shownGroups"
40
+ :key="group.key"
41
+ class="cn-work-queue__group"
42
+ :data-testid="`cn-work-queue-group-${group.key}`">
43
+ <div class="cn-work-queue__group-head">
44
+ <h3 class="cn-work-queue__group-title">
45
+ {{ group.label }}
46
+ <span class="cn-work-queue__count">{{ group.items.length }}</span>
47
+ </h3>
48
+ <NcButton v-if="content.hide"
49
+ variant="secondary"
50
+ :aria-label="t('nextcloud-vue', 'Hide this group until tomorrow')"
51
+ :data-testid="`cn-work-queue-hide-${group.key}`"
52
+ @click="setHidden(group.key, true)">
53
+ {{ t('nextcloud-vue', 'Hide until tomorrow') }}
54
+ </NcButton>
55
+ </div>
56
+ <p v-if="group.closesWhen" class="cn-work-queue__muted">
57
+ {{ group.closesWhen }}
58
+ </p>
59
+ <ul class="cn-work-queue__items">
60
+ <li v-for="item in group.items"
61
+ :key="item.id"
62
+ class="cn-work-queue__item"
63
+ :class="{ 'cn-work-queue__item--late': item.deadlineTier === 'overdue' }"
64
+ :data-testid="`cn-work-queue-item-${item.id}`">
65
+ <span class="cn-work-queue__avatar" aria-hidden="true">{{ initials(item.title) }}</span>
66
+ <span class="cn-work-queue__body">
67
+ <a :href="hrefOf(item) || '#'" class="cn-work-queue__link" @click="onOpen(item, $event)">{{ item.title }}</a>
68
+ <span v-if="item.covered || waitingText(item)" class="cn-work-queue__sub">
69
+ <span v-if="item.covered">{{ t('nextcloud-vue', 'Covering for {person}', { person: String(item.coveredFor || '') }) }}</span>
70
+ <span v-if="waitingText(item)" :data-testid="`cn-work-queue-waiting-${item.id}`">{{ waitingText(item) }}</span>
71
+ </span>
72
+ </span>
73
+ <span v-if="item.dueAt" class="cn-work-queue__muted">{{ item.dueAt }}</span>
74
+ </li>
75
+ </ul>
76
+ </section>
77
+ </template>
78
+
79
+ <section v-if="content.hide && hidden.length" class="cn-work-queue__group" data-testid="cn-work-queue-hidden">
80
+ <h3 class="cn-work-queue__group-title">
81
+ {{ t('nextcloud-vue', 'Hidden until tomorrow') }}
82
+ </h3>
83
+ <NcButton v-for="key in hidden"
84
+ :key="key"
85
+ variant="tertiary"
86
+ :data-testid="`cn-work-queue-show-${key}`"
87
+ @click="setHidden(key, false)">
88
+ {{ t('nextcloud-vue', 'Show {group} again', { group: labelOf(key) }) }}
89
+ </NcButton>
90
+ </section>
91
+ </div>
92
+ </template>
93
+
94
+ <script>
95
+ import axios from '@nextcloud/axios'
96
+ import { translate as t } from '@nextcloud/l10n'
97
+ import { generateUrl } from '@nextcloud/router'
98
+ import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
99
+ import { fetchEndpointSource } from '../../composables/useEndpointSource.js'
100
+ import { fillTemplate } from '../../utils/fillTemplate.js'
101
+ import { openRowTarget } from '../../utils/linkNavigation.js'
102
+
103
+ /**
104
+ * CnWorkQueue — one list of everything waiting on the reader: tasks,
105
+ * consultations, mentions, calendar items, work they cover for a colleague,
106
+ * from as many sources as the server declares. The component names no
107
+ * source: the server answers the groups (`key`, `label`, `closesWhen`,
108
+ * `items`), so a new kind of work needs no change here. A source that could
109
+ * not be read is named at the top rather than folded into "nothing is
110
+ * waiting". A group can be hidden until tomorrow, through the server, so the
111
+ * choice follows the reader to another device. dossiq's personal queue is
112
+ * the first; pipelinq's queue has the same shape.
113
+ *
114
+ * `content`:
115
+ * - `source`: an endpointSource answering `{ groups, hiddenGroups?, unavailable? }`.
116
+ * An item is `{ id, title, dueAt?, deadlineTier?, covered?, coveredFor?,
117
+ * waiting?: { on, days, chases }, route?, href? }`.
118
+ * - `hide`, `show`: url templates with `{group}` that hide a group until
119
+ * tomorrow and show it again, each answering `{ hiddenGroups }`; without
120
+ * `hide` no group can be hidden.
121
+ * - `itemHref`: a template filled from an item, when items carry no `route`/`href`.
122
+ * - `waitingParties`: words for `waiting.on` (`{ applicant: "the applicant" }`).
123
+ * - `emptyText`: through the host translate.
124
+ */
125
+ export default {
126
+ name: 'CnWorkQueue',
127
+
128
+ components: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard },
129
+
130
+ inject: {
131
+ /** The host app's translate (CnAppRoot `cnTranslate`). */
132
+ cnTranslate: { default: () => (key) => key },
133
+ },
134
+
135
+ props: {
136
+ /**
137
+ * The queue's configuration (see the component docs).
138
+ *
139
+ * @type {object}
140
+ */
141
+ content: {
142
+ type: Object,
143
+ default: () => ({}),
144
+ },
145
+
146
+ /** The heading above the queue; none when empty. */
147
+ title: {
148
+ type: String,
149
+ default: '',
150
+ },
151
+ },
152
+
153
+ emits: ['open'],
154
+
155
+ data() {
156
+ return {
157
+ loading: true,
158
+ error: '',
159
+ groups: [],
160
+ hidden: [],
161
+ unavailable: [],
162
+ }
163
+ },
164
+
165
+ computed: {
166
+ /**
167
+ * The groups not hidden today.
168
+ *
169
+ * @spec openspec/changes/work-queue/specs/work-queue/spec.md#requirement-one-queue-over-every-source
170
+ * @return {Array<object>}
171
+ */
172
+ shownGroups() {
173
+ const hidden = new Set(this.hidden.map(String))
174
+ return this.groups.filter((group) => !hidden.has(String(group.key)))
175
+ },
176
+ },
177
+
178
+ mounted() {
179
+ this.load()
180
+ },
181
+
182
+ methods: {
183
+ t,
184
+
185
+ /**
186
+ * A config text through the host translate.
187
+ *
188
+ * @param {unknown} value The text.
189
+ * @return {string}
190
+ */
191
+ text(value) {
192
+ return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''
193
+ },
194
+
195
+ /**
196
+ * Read the queue; a failed read is an error with a retry.
197
+ *
198
+ * @spec openspec/changes/work-queue/specs/work-queue/spec.md#requirement-one-queue-over-every-source
199
+ * @return {Promise<void>}
200
+ */
201
+ async load() {
202
+ this.loading = true
203
+ this.error = ''
204
+ try {
205
+ const data = (await fetchEndpointSource(this.content.source, {}, { fresh: true })) || {}
206
+ this.groups = (Array.isArray(data.groups) ? data.groups : [])
207
+ .filter((g) => g && g.key !== undefined)
208
+ .map((g) => ({ ...g, items: Array.isArray(g.items) ? g.items : [] }))
209
+ this.hidden = Array.isArray(data.hiddenGroups) ? data.hiddenGroups.map(String) : []
210
+ this.unavailable = Array.isArray(data.unavailable) ? data.unavailable.filter((s) => s && s.source) : []
211
+ } catch (failure) {
212
+ const body = failure && failure.response && failure.response.data
213
+ this.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)
214
+ } finally {
215
+ this.loading = false
216
+ }
217
+ },
218
+
219
+ /**
220
+ * Hide a group until tomorrow, or show it again, through the server.
221
+ *
222
+ * @spec openspec/changes/work-queue/specs/work-queue/spec.md#requirement-one-queue-over-every-source
223
+ * @param {string} key The group key.
224
+ * @param {boolean} hide Hide (true) or show (false).
225
+ * @return {Promise<void>}
226
+ */
227
+ async setHidden(key, hide) {
228
+ const template = hide ? this.content.hide : this.content.show
229
+ if (!template) {
230
+ return
231
+ }
232
+ try {
233
+ const { data } = await axios.post(generateUrl(fillTemplate(template, { group: encodeURIComponent(key) })), {})
234
+ this.hidden = Array.isArray(data && data.hiddenGroups) ? data.hiddenGroups.map(String) : this.hidden
235
+ } catch {
236
+ // The group stays as it was; the server decides.
237
+ }
238
+ },
239
+
240
+ /**
241
+ * A group's label by key, for a hidden group.
242
+ *
243
+ * @param {string} key The key.
244
+ * @return {string}
245
+ */
246
+ labelOf(key) {
247
+ const group = this.groups.find((g) => String(g.key) === String(key))
248
+ return group && group.label ? group.label : String(key)
249
+ },
250
+
251
+ /**
252
+ * An item's page as an href.
253
+ *
254
+ * @param {object} item The item.
255
+ * @return {string}
256
+ */
257
+ hrefOf(item) {
258
+ if (item.route && this.$router) {
259
+ try {
260
+ return this.$router.resolve(item.route).href
261
+ } catch {
262
+ return ''
263
+ }
264
+ }
265
+ if (item.href) {
266
+ return String(item.href)
267
+ }
268
+ return this.content.itemHref ? generateUrl(fillTemplate(this.content.itemHref, item)) : ''
269
+ },
270
+
271
+ /**
272
+ * Open an item: its route through the router, else its href like a link, else tell the host.
273
+ *
274
+ * @param {object} item The item.
275
+ * @param {MouseEvent} event The click.
276
+ */
277
+ onOpen(item, event) {
278
+ if (item.route && this.$router && event && !(event.ctrlKey || event.metaKey || event.shiftKey || event.button === 1)) {
279
+ event.preventDefault()
280
+ this.$router.push(item.route).catch(() => {})
281
+ return
282
+ }
283
+ const href = this.hrefOf(item)
284
+ if (href) {
285
+ if (event) {
286
+ event.preventDefault()
287
+ }
288
+ openRowTarget(event, href)
289
+ return
290
+ }
291
+ if (event) {
292
+ event.preventDefault()
293
+ }
294
+ /** @event open An item without a route or href was opened. Payload: the item. */
295
+ this.$emit('open', item)
296
+ },
297
+
298
+ /**
299
+ * Who the item waits on, for how long and how often chased.
300
+ *
301
+ * @param {object} item The item.
302
+ * @return {string}
303
+ */
304
+ waitingText(item) {
305
+ const waiting = item && item.waiting
306
+ if (!waiting || typeof waiting !== 'object' || !waiting.on) {
307
+ return ''
308
+ }
309
+ const parties = {
310
+ applicant: t('nextcloud-vue', 'the applicant'),
311
+ thirdParty: t('nextcloud-vue', 'a third party'),
312
+ us: t('nextcloud-vue', 'us'),
313
+ }
314
+ const own = this.content.waitingParties && this.content.waitingParties[waiting.on]
315
+ const party = own ? this.cnTranslate(own) : (parties[waiting.on] || String(waiting.on))
316
+ const days = Number.isFinite(waiting.days) ? waiting.days : 0
317
+ const chases = Number.isFinite(waiting.chases) ? waiting.chases : 0
318
+ if (chases === 0) {
319
+ return t('nextcloud-vue', 'Waiting on {party} for {days} days', { party, days })
320
+ }
321
+ if (chases === 1) {
322
+ return t('nextcloud-vue', 'Waiting on {party} for {days} days, chased once', { party, days })
323
+ }
324
+ return t('nextcloud-vue', 'Waiting on {party} for {days} days, chased {chases} times', { party, days, chases })
325
+ },
326
+
327
+ /**
328
+ * Up to two initials of a title.
329
+ *
330
+ * @param {string} title The title.
331
+ * @return {string}
332
+ */
333
+ initials(title) {
334
+ return String(title ?? '').trim().split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0]).join('').toUpperCase()
335
+ },
336
+ },
337
+ }
338
+ </script>
339
+
340
+ <style scoped>
341
+ .cn-work-queue {
342
+ display: flex;
343
+ flex-direction: column;
344
+ gap: 24px;
345
+ }
346
+
347
+ .cn-work-queue__head,
348
+ .cn-work-queue__group-head {
349
+ display: flex;
350
+ flex-wrap: wrap;
351
+ align-items: center;
352
+ justify-content: space-between;
353
+ gap: 12px;
354
+ }
355
+
356
+ .cn-work-queue__title {
357
+ margin: 0;
358
+ font-size: 28px;
359
+ font-weight: 700;
360
+ }
361
+
362
+ .cn-work-queue__actions {
363
+ display: flex;
364
+ gap: 8px;
365
+ }
366
+
367
+ .cn-work-queue__group {
368
+ padding: 20px 24px;
369
+ border: 1px solid var(--color-border);
370
+ border-radius: var(--border-radius-large);
371
+ background: var(--color-main-background);
372
+ }
373
+
374
+ .cn-work-queue__group-title {
375
+ display: flex;
376
+ align-items: center;
377
+ gap: 8px;
378
+ margin: 0;
379
+ font-size: 18px;
380
+ font-weight: 700;
381
+ }
382
+
383
+ .cn-work-queue__count {
384
+ padding: 0 8px;
385
+ border-radius: 10px;
386
+ background: var(--color-background-dark);
387
+ font-size: 12px;
388
+ font-weight: 600;
389
+ }
390
+
391
+ .cn-work-queue__muted {
392
+ margin: 4px 0 8px;
393
+ color: var(--color-text-maxcontrast);
394
+ }
395
+
396
+ .cn-work-queue__items {
397
+ margin: 0;
398
+ padding: 0;
399
+ list-style: none;
400
+ }
401
+
402
+ .cn-work-queue__item {
403
+ display: flex;
404
+ align-items: center;
405
+ gap: 16px;
406
+ padding: 12px 0;
407
+ border-block-end: 1px solid var(--color-border);
408
+ }
409
+
410
+ .cn-work-queue__item:last-child {
411
+ border-block-end: none;
412
+ }
413
+
414
+ .cn-work-queue__avatar {
415
+ display: inline-flex;
416
+ flex: none;
417
+ align-items: center;
418
+ justify-content: center;
419
+ width: 32px;
420
+ height: 32px;
421
+ border-radius: 50%;
422
+ background: var(--color-background-dark);
423
+ color: var(--color-text-maxcontrast);
424
+ font-size: 12px;
425
+ font-weight: 700;
426
+ }
427
+
428
+ .cn-work-queue__body {
429
+ display: flex;
430
+ flex: 1 1 auto;
431
+ flex-direction: column;
432
+ min-width: 0;
433
+ }
434
+
435
+ .cn-work-queue__link {
436
+ color: var(--color-main-text);
437
+ font-weight: 700;
438
+ text-decoration: none;
439
+ }
440
+
441
+ .cn-work-queue__link:hover {
442
+ text-decoration: underline;
443
+ }
444
+
445
+ .cn-work-queue__item--late .cn-work-queue__link {
446
+ color: var(--color-error);
447
+ }
448
+
449
+ .cn-work-queue__sub {
450
+ display: flex;
451
+ flex-wrap: wrap;
452
+ gap: 12px;
453
+ color: var(--color-text-maxcontrast);
454
+ font-size: 0.9em;
455
+ }
456
+ </style>
@@ -0,0 +1,12 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * CnWorkQueue — one list of everything waiting on the reader, in the groups
6
+ * the server declares.
7
+ */
8
+
9
+ import CnWorkQueue from './CnWorkQueue.vue'
10
+
11
+ export { CnWorkQueue }
12
+ export default CnWorkQueue
@@ -59,6 +59,7 @@ export { CnMassExportDialog } from './CnMassExportDialog/index.js'
59
59
  export { CnMassImportDialog } from './CnMassImportDialog/index.js'
60
60
  export { CnExportWizard } from './CnExportWizard/index.js'
61
61
  export { CnWizardDialog } from './CnWizardDialog/index.js'
62
+ export { CnWorkQueue } from './CnWorkQueue/index.js'
62
63
  export { CnSetupWizard } from './CnSetupWizard/index.js'
63
64
  export { CnWalkthrough } from './CnWalkthrough/index.js'
64
65
  export { CnRichSubmitDialog } from './CnRichSubmitDialog/index.js'
@@ -84,6 +85,7 @@ export {
84
85
  export { CnPageHeader } from './CnPageHeader/index.js'
85
86
  export { CnBreadcrumbs } from './CnBreadcrumbs/index.js'
86
87
  export { CnActionDialog } from './CnActionDialog/index.js'
88
+ export { CnAssignDialog } from './CnAssignDialog/index.js'
87
89
  export { CnActionsBar } from './CnActionsBar/index.js'
88
90
  export { CnActionsMenu } from './CnActionsMenu/index.js'
89
91
  export { CnActionButtons } from './CnActionButtons/index.js'
@@ -131,9 +133,12 @@ export { default as CnContactsTab } from '../integrations/builtin/contacts/CnCon
131
133
  export { CnContactPicker } from './CnContactPicker/index.js'
132
134
  export { CnContactCreate } from './CnContactCreate/index.js'
133
135
  export { CnResourceSelect } from './CnResourceSelect/index.js'
136
+ export { CnRetentionWidget } from './CnRetentionWidget/index.js'
134
137
  export { CnSearchSelect } from './CnSearchSelect/index.js'
135
138
  export { CnPartyPicker } from './CnPartyPicker/index.js'
139
+ export { CnAddressSearch } from './CnAddressSearch/index.js'
136
140
  export { CnNoticeWidget } from './CnNoticeWidget/index.js'
141
+ export { CnDecisionListWidget } from './CnDecisionListWidget/index.js'
137
142
  export { CnPropertySourceField } from './CnPropertySourceField/index.js'
138
143
  export { CnDurationField } from './CnDurationField/index.js'
139
144
  export { CnFavouriteToggle } from './CnFavouriteToggle/index.js'
@@ -193,6 +198,7 @@ export { CnDividerWidgetForm } from './CnDividerWidgetForm/index.js'
193
198
  export { CnFilesWidget } from './CnFilesWidget/index.js'
194
199
  export { CnFilesWidgetForm } from './CnFilesWidgetForm/index.js'
195
200
  export { CnPeopleWidget } from './CnPeopleWidget/index.js'
201
+ export { CnPlanWidget } from './CnPlanWidget/index.js'
196
202
  export { CnPeopleWidgetForm } from './CnPeopleWidgetForm/index.js'
197
203
  export { CnQuicklinksWidget } from './CnQuicklinksWidget/index.js'
198
204
  export { CnQuicklinksWidgetForm } from './CnQuicklinksWidgetForm/index.js'
@@ -209,6 +215,7 @@ export { CnCalendarWidgetForm } from './CnCalendarWidgetForm/index.js'
209
215
  export { CnSpendAnalyticsWidget } from './CnSpendAnalyticsWidget/index.js'
210
216
  export { CnSpendAnalyticsWidgetForm } from './CnSpendAnalyticsWidgetForm/index.js'
211
217
  export { CnNcWidgetWidget } from './CnNcWidgetWidget/index.js'
218
+ export { CnNcDashboardList } from './CnNcDashboardList/index.js'
212
219
  export { CnNcDashboardWidgetForm } from './CnNcDashboardWidgetForm/index.js'
213
220
  export { CnDashTileWidget } from './CnDashTileWidget/index.js'
214
221
  export { CnDashTileWidgetForm } from './CnDashTileWidgetForm/index.js'
@@ -278,6 +285,7 @@ export { CnObjectDataWidget } from './CnObjectDataWidget/index.js'
278
285
  export { CnObjectMetadataWidget } from './CnObjectMetadataWidget/index.js'
279
286
  export { CnObjectMetadataModal } from './CnObjectMetadataModal/index.js'
280
287
  export { CnRelatedObjectsWidget } from './CnRelatedObjectsWidget/index.js'
288
+ export { CnRedactionEditor } from './CnRedactionEditor/index.js'
281
289
  export { CnObjectGeoWidget } from './CnObjectGeoWidget/index.js'
282
290
  export { CnLogsPage } from './CnLogsPage/index.js'
283
291
  export { CnSettingsPage } from './CnSettingsPage/index.js'
@@ -435,3 +443,4 @@ export { CnTeamWorkloadWidget } from './CnTeamWorkloadWidget/index.js'
435
443
  export { CnStackedBarWidgetForm } from './CnStackedBarWidgetForm/index.js'
436
444
  export { CnSegmentedControl } from './CnSegmentedControl/index.js'
437
445
  export { CnBrandStripe } from './CnBrandStripe/index.js'
446
+ export { CnDuplicateWarning } from './CnDuplicateWarning/index.js'
@@ -236,6 +236,30 @@
236
236
  text-align: center;
237
237
  }
238
238
 
239
+ .cn-index-page__search-refusal {
240
+ padding: calc(3 * var(--default-grid-baseline)) 0;
241
+ }
242
+
243
+ .cn-index-page__search-refusal-headline {
244
+ font-weight: bold;
245
+ }
246
+
247
+ .cn-index-page__search-refusal-term {
248
+ font-family: var(--font-face-monospace, monospace);
249
+ overflow-wrap: anywhere;
250
+ white-space: pre-wrap;
251
+ }
252
+
253
+ .cn-index-page__search-refusal-term mark {
254
+ background-color: var(--color-warning);
255
+ color: var(--color-main-text);
256
+ font-weight: bold;
257
+ }
258
+
259
+ .cn-index-page__search-refusal-muted {
260
+ color: var(--color-text-maxcontrast);
261
+ }
262
+
239
263
  /* Explicit `0` rather than dropping the declaration: this element carries both
240
264
  `.cn-pagination` and `.cn-index-page__pagination`, and `.cn-pagination` sets
241
265
  `margin-top: calc(8 * baseline)` at the same (0,1,0) specificity. Only the
package/src/index.js CHANGED
@@ -35,6 +35,7 @@ export {
35
35
  CnActionDialog,
36
36
  CnActionsBar,
37
37
  CnActionsMenu,
38
+ CnAddressSearch,
38
39
  CnAddWidgetModal,
39
40
  CnAdminActionCard,
40
41
  CnAdminSettingsShell,
@@ -43,6 +44,7 @@ export {
43
44
  CnAppLoading,
44
45
  CnAppNav,
45
46
  CnAppRoot,
47
+ CnAssignDialog,
46
48
  CnAuditTrailCard,
47
49
  CnAuditTrailWidget,
48
50
  CnBannerWidget,
@@ -91,6 +93,7 @@ export {
91
93
  CnDataTable,
92
94
  CnDateAxisView,
93
95
  CnDateRangePicker,
96
+ CnDecisionListWidget,
94
97
  CnDeckCardCreate,
95
98
  CnDeckCardPicker,
96
99
  CnDeleteDialog,
@@ -108,6 +111,7 @@ export {
108
111
  CnDividerWidgetForm,
109
112
  CnDocumentReviewList,
110
113
  CnDocumentReviewWidget,
114
+ CnDuplicateWarning,
111
115
  CnDurationField,
112
116
  CnEditActionsModal,
113
117
  CnEditDataModal,
@@ -211,6 +215,7 @@ export {
211
215
  CnMenuWidgetForm,
212
216
  CnMetadataTab,
213
217
  CnNavCardGrid,
218
+ CnNcDashboardList,
214
219
  CnNcDashboardWidgetForm,
215
220
  CnNcWidgetGridPicker,
216
221
  CnNcWidgetWidget,
@@ -249,6 +254,7 @@ export {
249
254
  CnPartyPicker,
250
255
  CnPeopleWidget,
251
256
  CnPeopleWidgetForm,
257
+ CnPlanWidget,
252
258
  CnPresenceAvatars,
253
259
  CnProcessSteps,
254
260
  CnProgressBar,
@@ -260,6 +266,7 @@ export {
260
266
  CnQuicklinksWidgetForm,
261
267
  CnRadioCardsWidget,
262
268
  CnRecordListWidget,
269
+ CnRedactionEditor,
263
270
  CnReferencePreview,
264
271
  CnRegisterMapping,
265
272
  CnRegisterSchemaSelect,
@@ -269,6 +276,7 @@ export {
269
276
  CnRelationLinkModal,
270
277
  CnRelationshipGraph,
271
278
  CnResourceSelect,
279
+ CnRetentionWidget,
272
280
  CnRichSubmitDialog,
273
281
  CnRoadmapItem,
274
282
  CnRoadmapTab,
@@ -355,6 +363,7 @@ export {
355
363
  CnWidgetWrapper,
356
364
  CnWizardDialog,
357
365
  CnWorkingCalendarWidget,
366
+ CnWorkQueue,
358
367
  CnWorkspaceFilterWidget,
359
368
  DEFAULT_DATE_RANGE_PRESETS,
360
369
  defaultPageTypes,
@@ -618,6 +627,9 @@ export { followLinkClick, isModifiedClick, isNewTabHandled, isRowMiddleClick, op
618
627
  export { resolveImageUrl } from './utils/index.js'
619
628
  export { resolveDateVariant } from './utils/index.js'
620
629
  export { actionLink, dispatchAction } from './utils/actionsDispatcher.js'
630
+ export { candidateWithoutBlanks, duplicateAffordances } from './utils/duplicateWarning.js'
631
+ export { openDuplicateWarning } from './utils/openDuplicateWarning.js'
632
+ export { registerNcDashboardWidgets } from './utils/registerNcDashboardWidgets.js'
621
633
  // Nested-modal stacking. `CnAppRoot` installs this itself; apps that do not
622
634
  // mount `CnAppRoot` should call `installModalStack()` once from `main.js`, or
623
635
  // two open dialogs tie on z-index and the lower one intercepts the clicks.