@conduction/nextcloud-vue 2.99.0 → 2.101.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 (180) hide show
  1. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  2. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +6 -1
  3. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  4. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +2 -1
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  6. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  7. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +22 -1
  8. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  9. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +26 -16
  10. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  11. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  12. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +51 -7
  13. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  14. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +104 -73
  15. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  16. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +25 -8
  17. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  18. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +5 -3
  19. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  20. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  21. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +11 -8
  22. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +13 -4
  24. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  25. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js +9 -0
  26. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js.map +1 -0
  27. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js +139 -0
  28. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js.map +1 -0
  29. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js +76 -0
  30. package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js.map +1 -0
  31. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  32. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +54 -3
  33. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  34. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +16 -2
  35. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  36. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  37. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +160 -2
  38. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +206 -94
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  41. package/dist/esm/components/CnFilesBrowser/filesReview.js +61 -0
  42. package/dist/esm/components/CnFilesBrowser/filesReview.js.map +1 -0
  43. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
  44. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +42 -7
  45. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
  46. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +121 -57
  47. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
  48. package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue2.js +10 -0
  49. package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue2.js.map +1 -1
  50. package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue3.js +2 -1
  51. package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue3.js.map +1 -1
  52. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +9 -0
  53. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js.map +1 -0
  54. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +279 -0
  55. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -0
  56. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +122 -0
  57. package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -0
  58. package/dist/esm/components/CnGroupedCardsWidget/index.js +29 -0
  59. package/dist/esm/components/CnGroupedCardsWidget/index.js.map +1 -0
  60. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +387 -78
  61. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  62. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +317 -209
  63. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  64. package/dist/esm/components/CnIndexPage/useNamedSource.js +4 -1
  65. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  66. package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
  67. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +15 -0
  68. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
  69. package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
  70. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  71. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +14 -1
  72. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  73. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +14 -6
  74. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  75. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  76. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +10 -0
  77. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +6 -1
  79. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  80. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
  81. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +91 -4
  82. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
  83. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +63 -38
  84. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
  85. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  86. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +68 -7
  87. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  88. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +256 -240
  89. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  90. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +9 -0
  91. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js.map +1 -0
  92. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js +175 -0
  93. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -0
  94. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js +77 -0
  95. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -0
  96. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +1 -1
  97. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +38 -9
  98. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -1
  99. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -1
  100. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  101. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +51 -4
  102. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  103. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +40 -25
  104. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  105. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +4 -0
  106. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  107. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  108. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  109. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue.js +1 -1
  110. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +7 -7
  111. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
  112. package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
  113. package/dist/esm/composables/indexSources.js +167 -20
  114. package/dist/esm/composables/indexSources.js.map +1 -1
  115. package/dist/esm/composables/useEndpointSource.js +28 -6
  116. package/dist/esm/composables/useEndpointSource.js.map +1 -1
  117. package/dist/esm/index.js +1 -0
  118. package/dist/esm/index.js.map +1 -1
  119. package/dist/esm/l10n/en.json.js +43 -1
  120. package/dist/esm/l10n/en.json.js.map +1 -1
  121. package/dist/esm/l10n/nl.json.js +41 -1
  122. package/dist/esm/l10n/nl.json.js.map +1 -1
  123. package/dist/esm/library-style.js +1 -1
  124. package/dist/esm/nextcloud-vue.css +1101 -396
  125. package/dist/esm/utils/languageMapText.js +50 -0
  126. package/dist/esm/utils/languageMapText.js.map +1 -0
  127. package/dist/esm/utils/libraryWidgetKeys.js +3 -0
  128. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  129. package/dist/esm/utils/mapList.js +71 -0
  130. package/dist/esm/utils/mapList.js.map +1 -0
  131. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  132. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  133. package/dist/nextcloud-vue.cjs.js +9996 -7378
  134. package/dist/nextcloud-vue.cjs.js.map +2 -2
  135. package/dist/nextcloud-vue.css +1101 -396
  136. package/l10n/en.json +43 -1
  137. package/l10n/nl.json +41 -1
  138. package/package.json +1 -1
  139. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +7 -1
  140. package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
  141. package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
  142. package/src/components/CnDashboardPage/CnDashboardPage.vue +89 -20
  143. package/src/components/CnDetailPage/CnDetailPage.vue +28 -9
  144. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +13 -4
  145. package/src/components/CnExportsWidget/CnExportsWidget.vue +233 -0
  146. package/src/components/CnExportsWidget/index.js +7 -0
  147. package/src/components/CnFactsWidget/CnFactsWidget.vue +65 -3
  148. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +324 -0
  149. package/src/components/CnFilesBrowser/filesReview.js +57 -0
  150. package/src/components/CnFlowsPage/CnFlowBoard.vue +77 -2
  151. package/src/components/CnFlowsPage/CnFlowEditorPage.vue +11 -0
  152. package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +433 -0
  153. package/src/components/CnGroupedCardsWidget/index.js +28 -0
  154. package/src/components/CnIndexPage/CnIndexPage.vue +376 -11
  155. package/src/components/CnIndexPage/useNamedSource.js +4 -1
  156. package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
  157. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +29 -1
  158. package/src/components/CnObjectSidebar/CnFilesTab.vue +15 -0
  159. package/src/components/CnOrderedListField/CnOrderedListField.vue +151 -5
  160. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +114 -7
  161. package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +272 -0
  162. package/src/components/CnSavedViewAlertWidget/index.js +7 -0
  163. package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +38 -9
  164. package/src/components/CnSetupWizard/CnSetupWizard.vue +72 -5
  165. package/src/components/CnWidgetGrid/builtInWidgets.js +4 -0
  166. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  167. package/src/components/CnWizardDialog/CnWizardDialog.vue +7 -7
  168. package/src/components/index.js +3 -0
  169. package/src/composables/indexSources.js +166 -19
  170. package/src/composables/useEndpointSource.js +28 -6
  171. package/src/css/detail-page.css +15 -0
  172. package/src/css/dialog.css +48 -0
  173. package/src/css/index-page.css +117 -0
  174. package/src/css/look-board.css +127 -1
  175. package/src/index.js +1 -0
  176. package/src/schemas/app-manifest-v2.schema.json +55 -1
  177. package/src/utils/languageMapText.js +46 -0
  178. package/src/utils/libraryWidgetKeys.js +3 -0
  179. package/src/utils/mapList.js +67 -0
  180. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -184,8 +184,14 @@
184
184
  <CnIcon :name="widget.icon" :size="20" />
185
185
  </template>
186
186
  <!-- A renderer that reports its own header (`header` event:
187
- `{ link, note }`, e.g. record-list's "3 in totaal"). -->
188
- <template v-if="rendererHeader.note || rendererHeader.action" #actions>
187
+ `{ link, note, action, pill }`, e.g. record-list's "3 in totaal",
188
+ a facts card's status pill). -->
189
+ <template v-if="rendererHeader.note || rendererHeader.action || rendererHeader.pill" #actions>
190
+ <CnStatusBadge
191
+ v-if="rendererHeader.pill"
192
+ :label="rendererHeader.pill.label"
193
+ :variant="rendererHeader.pill.variant"
194
+ data-testid="cn-detail-widget-header-pill" />
189
195
  <span
190
196
  v-if="rendererHeader.note"
191
197
  class="cn-detail-page__catalog-note"
@@ -298,6 +304,7 @@ import CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'
298
304
  import CnObjectFilesWidget from '../CnObjectFilesWidget/CnObjectFilesWidget.vue'
299
305
  import CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'
300
306
  import CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'
307
+ import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
301
308
  import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'
302
309
  import { normalizeLook } from '../../composables/useLook.js'
303
310
  import { isAppInstalled } from '../../utils/appInstalled.js'
@@ -376,6 +383,7 @@ export default {
376
383
  CnObjectDataWidget,
377
384
  CnObjectGeoWidget,
378
385
  CnRelatedObjectsWidget,
386
+ CnStatusBadge,
379
387
  CnWidgetWrapper,
380
388
  NcActionButton,
381
389
  CnEmptyContent,
@@ -631,7 +639,7 @@ export default {
631
639
  * `header` event (screens-relation-pages): a link and a note,
632
640
  * with the list's total already filled in.
633
641
  */
634
- rendererHeader: { link: null, note: '', action: null },
642
+ rendererHeader: { link: null, note: '', action: null, pill: null },
635
643
  }
636
644
  },
637
645
 
@@ -1218,7 +1226,8 @@ export default {
1218
1226
  const link = header && header.link && typeof header.link === 'object' ? header.link : null
1219
1227
  const note = header && typeof header.note === 'string' ? header.note : ''
1220
1228
  const action = header && header.action && typeof header.action.run === 'function' && header.action.label ? header.action : null
1221
- this.rendererHeader = { link, note, action }
1229
+ const pill = header && header.pill && typeof header.pill.label === 'string' && header.pill.label !== '' ? header.pill : null
1230
+ this.rendererHeader = { link, note, action, pill }
1222
1231
  },
1223
1232
 
1224
1233
  /**
@@ -0,0 +1,233 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+ <template>
6
+ <div class="cn-exports-widget" data-testid="cn-exports-widget">
7
+ <p v-if="loading && runs.length === 0" class="cn-exports-widget__muted">
8
+ {{ t('nextcloud-vue', 'Loading …') }}
9
+ </p>
10
+ <p v-else-if="failed" class="cn-exports-widget__muted" data-testid="cn-exports-error">
11
+ {{ t('nextcloud-vue', 'Your exports could not be loaded.') }}
12
+ </p>
13
+ <p v-else-if="runs.length === 0" class="cn-exports-widget__muted" data-testid="cn-exports-empty">
14
+ {{ emptyText }}
15
+ </p>
16
+ <ul v-else class="cn-exports-widget__list">
17
+ <li v-for="run in shownRuns"
18
+ :key="run.key"
19
+ class="cn-exports-widget__row"
20
+ data-testid="cn-exports-row">
21
+ <span class="cn-exports-widget__text">
22
+ <span class="cn-exports-widget__name">{{ run.filename }}</span>
23
+ <span class="cn-exports-widget__meta">{{ run.meta }}</span>
24
+ </span>
25
+ <span v-if="run.expiresToday" class="cn-exports-widget__tag" data-testid="cn-exports-expires-today">
26
+ {{ t('nextcloud-vue', 'Expires today') }}
27
+ </span>
28
+ <NcButton v-else-if="run.href"
29
+ :href="run.href"
30
+ data-testid="cn-exports-download">
31
+ {{ t('nextcloud-vue', 'Download') }}
32
+ </NcButton>
33
+ </li>
34
+ </ul>
35
+ </div>
36
+ </template>
37
+
38
+ <script>
39
+ import { getCurrentUser } from '@nextcloud/auth'
40
+ import axios from '@nextcloud/axios'
41
+ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
42
+ import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
43
+ import { NcButton } from '@nextcloud/vue'
44
+ import { formatBoardDate } from '../../utils/boardDate.js'
45
+
46
+ /**
47
+ * CnExportsWidget — the reader's own exports (DqZoekresultaten "Uw exports"):
48
+ * each file with when it was made, how often it was downloaded and when it
49
+ * expires, a Download button while the file is there, and "Expires today" on
50
+ * the day it goes.
51
+ *
52
+ * Reads OpenRegister's export runs (`GET /apps/openregister/api/exports`)
53
+ * with the reader as the actor, so an administrator sees their own here too.
54
+ * `content.register` and `content.schema` narrow it to one list's exports.
55
+ * A run whose file was swept, or that never produced one, is left out.
56
+ *
57
+ * Example (manifest, in an index page's `sidePanel`):
58
+ * ```json
59
+ * { "id": "exports", "type": "exports", "title": "Your exports", "content": { "register": "dossiq", "schema": "case", "limit": 5 } }
60
+ * ```
61
+ */
62
+ export default {
63
+ name: 'CnExportsWidget',
64
+
65
+ components: { NcButton },
66
+
67
+ inject: {
68
+ /** Host translate function provided by CnAppRoot. */
69
+ cnTranslate: { default: () => (key) => key },
70
+ },
71
+
72
+ props: {
73
+ /** `{ register?, schema?, limit?, emptyText? }`; `limit` defaults to 5. */
74
+ content: {
75
+ type: Object,
76
+ default: () => ({}),
77
+ },
78
+ },
79
+
80
+ data() {
81
+ return { loading: false, failed: false, runs: [], now: new Date() }
82
+ },
83
+
84
+ computed: {
85
+ /** @return {number} How many exports to show. */
86
+ limit() {
87
+ const limit = Number(this.content && this.content.limit)
88
+ return Number.isInteger(limit) && limit > 0 ? limit : 5
89
+ },
90
+
91
+ /** @return {Array<object>} The exports to draw, newest first. */
92
+ shownRuns() {
93
+ return this.runs
94
+ .filter((run) => run && run.filename && run.fileId !== null && run.fileId !== undefined && run.status !== 'expired')
95
+ .slice(0, this.limit)
96
+ .map((run) => this.view(run))
97
+ },
98
+
99
+ /** @return {string} The empty line. */
100
+ emptyText() {
101
+ const own = this.content && this.content.emptyText
102
+ return own ? this.cnTranslate(own) : t('nextcloud-vue', 'You have no exports here.')
103
+ },
104
+ },
105
+
106
+ mounted() {
107
+ this.load()
108
+ },
109
+
110
+ methods: {
111
+ t,
112
+
113
+ /**
114
+ * Read the reader's export runs.
115
+ *
116
+ * @return {Promise<void>}
117
+ */
118
+ async load() {
119
+ const user = getCurrentUser()
120
+ if (!user || !user.uid) {
121
+ return
122
+ }
123
+ this.loading = true
124
+ this.failed = false
125
+ try {
126
+ const params = { actor: user.uid, limit: Math.max(this.limit * 2, 10) }
127
+ for (const key of ['register', 'schema']) {
128
+ if (this.content && typeof this.content[key] === 'string' && this.content[key] !== '') {
129
+ params[key] = this.content[key]
130
+ }
131
+ }
132
+ const response = await axios.get(generateUrl('/apps/openregister/api/exports'), { params })
133
+ const results = response && response.data && Array.isArray(response.data.results) ? response.data.results : []
134
+ this.runs = [...results].sort((a, b) => String(b.producedAt || b.created || '').localeCompare(String(a.producedAt || a.created || '')))
135
+ } catch {
136
+ this.failed = true
137
+ this.runs = []
138
+ } finally {
139
+ this.loading = false
140
+ }
141
+ },
142
+
143
+ /**
144
+ * One export as the list draws it.
145
+ *
146
+ * @param {object} run An export run.
147
+ * @return {{key: string, filename: string, meta: string, href: string, expiresToday: boolean}}
148
+ */
149
+ view(run) {
150
+ const made = run.producedAt || run.created
151
+ const parts = []
152
+ if (made) {
153
+ parts.push(t('nextcloud-vue', 'Made {date}', { date: formatBoardDate(made, this.now) }))
154
+ }
155
+ if (typeof run.downloadCount === 'number') {
156
+ parts.push(n('nextcloud-vue', 'downloaded %n time', 'downloaded %n times', run.downloadCount))
157
+ }
158
+ if (run.expiresAt) {
159
+ parts.push(t('nextcloud-vue', 'expires {date}', { date: formatBoardDate(run.expiresAt, this.now) }))
160
+ }
161
+ const user = getCurrentUser()
162
+ const href = run.filePath && user && user.uid
163
+ ? generateRemoteUrl(`dav/files/${encodeURIComponent(user.uid)}`) + String(run.filePath).split('/').map(encodeURIComponent).join('/')
164
+ : ''
165
+ return {
166
+ key: String(run.uuid || run.id),
167
+ filename: run.filename,
168
+ meta: parts.join(' · '),
169
+ href,
170
+ expiresToday: Boolean(run.expiresAt) && new Date(run.expiresAt).toDateString() === this.now.toDateString(),
171
+ }
172
+ },
173
+ },
174
+ }
175
+ </script>
176
+
177
+ <style scoped>
178
+ .cn-exports-widget__list {
179
+ display: flex;
180
+ flex-direction: column;
181
+ margin: 0;
182
+ padding: 0;
183
+ list-style: none;
184
+ }
185
+
186
+ .cn-exports-widget__row {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 12px;
190
+ padding: 10px 0;
191
+ }
192
+
193
+ .cn-exports-widget__row + .cn-exports-widget__row {
194
+ border-top: 1px solid var(--color-border);
195
+ }
196
+
197
+ .cn-exports-widget__text {
198
+ display: flex;
199
+ flex: 1;
200
+ flex-direction: column;
201
+ gap: 2px;
202
+ min-width: 0;
203
+ }
204
+
205
+ .cn-exports-widget__name {
206
+ overflow: hidden;
207
+ font-size: 14px;
208
+ font-weight: 600;
209
+ text-overflow: ellipsis;
210
+ white-space: nowrap;
211
+ }
212
+
213
+ .cn-exports-widget__meta,
214
+ .cn-exports-widget__muted {
215
+ color: var(--color-text-maxcontrast);
216
+ font-size: 13px;
217
+ }
218
+
219
+ .cn-exports-widget__muted {
220
+ margin: 0;
221
+ }
222
+
223
+ .cn-exports-widget__tag {
224
+ flex: none;
225
+ padding: 3px 10px;
226
+ border-radius: 12px;
227
+ background: var(--color-warning-light, color-mix(in srgb, var(--color-warning) 18%, transparent));
228
+ color: var(--color-warning-text, var(--color-main-text));
229
+ font-size: 13px;
230
+ font-weight: 600;
231
+ white-space: nowrap;
232
+ }
233
+ </style>
@@ -0,0 +1,7 @@
1
+ // SPDX-FileCopyrightText: 2026 Conduction B.V.
2
+ // SPDX-License-Identifier: EUPL-1.2
3
+
4
+ import CnExportsWidget from './CnExportsWidget.vue'
5
+
6
+ export default CnExportsWidget
7
+ export { CnExportsWidget }
@@ -132,6 +132,13 @@
132
132
  </div>
133
133
  </template>
134
134
  </template>
135
+ <NcNoteCard
136
+ v-if="notice && record"
137
+ :type="notice.type"
138
+ class="cn-facts-widget__notice"
139
+ data-testid="cn-facts-notice">
140
+ {{ notice.text }}
141
+ </NcNoteCard>
135
142
  <component
136
143
  :is="link.to ? 'router-link' : 'a'"
137
144
  v-if="link && record"
@@ -157,7 +164,7 @@
157
164
  import { emit } from '@nextcloud/event-bus'
158
165
  import { formatFileSize } from '@nextcloud/files'
159
166
  import { translate as t } from '@nextcloud/l10n'
160
- import { NcLoadingIcon } from '@nextcloud/vue'
167
+ import { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
161
168
  import Check from 'vue-material-design-icons/Check.vue'
162
169
  import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
163
170
  import EyeOutline from 'vue-material-design-icons/EyeOutline.vue'
@@ -266,7 +273,7 @@ export const FACTS_REVEAL_CHANNEL = 'cn:facts:reveal'
266
273
  export default {
267
274
  name: 'CnFactsWidget',
268
275
 
269
- components: { Check, CnIcon, CnStatusBadge, ContentCopy, EyeOutline, NcLoadingIcon },
276
+ components: { Check, CnIcon, CnStatusBadge, ContentCopy, EyeOutline, NcLoadingIcon, NcNoteCard },
270
277
 
271
278
  inject: {
272
279
  cnTranslate: { default: () => (key) => key },
@@ -290,6 +297,8 @@ export default {
290
297
  * `resolve: { register, schema, labelField? }` on an entry or a part turns a value that is a reference (an id, or a list of ids) into the label of the referenced object (`labelField`, else `title`, then `name`): the case type or status of a task shows its name, not its uuid. The ids are fetched once per register and schema through the object store and shared by every facts widget on the page; the entry stays empty until its label is known, and when no label can be read. A resolved value works as a pill too: `map` is applied to the resolved label, and `variantMap` / `colorMap` look the tone up by the value of `resolve.toneField` on the referenced object when that is set (a status named by its `name`, coloured by its `state`), then by the resolved label, then by the stored value.
291
298
  * `format: 'user'` shows a Nextcloud user id as the user's display name, read once per id; the entry stays empty until the name is known.
292
299
  * - `action` — an action drawn as a button under the facts; `headerAction` — one drawn in the card header (reported through the `header` event).
300
+ * - `headerPill` — a status pill in the card header (reported through the `header` event): `{ path?, text?, map?, variant?, variantMap?, colorMap? }`, read from the record like a `pill` entry (DqBezwaar "Advies ontvangen"); nothing shows while the value is empty.
301
+ * - `notice` — `{ text, variant? }`, a note band inside the card under the facts (`info` by default; `success`, `warning` or `error`); the text is translated.
293
302
  * `format` is `text` (default), `date` (board short date), `date-long` (always with the year), `list` (an array joined by ", ", each item read at `itemPath`), `bytes` (a size, "1.5 MB") or `currency` (a money amount in the reader's language, "€ 1.234,50": the code in `currency`, else the record's `currencyField`, else the app's reporting currency, else EUR; `decimals` defaults to 2).
294
303
  * `map` turns a raw value into a label (translated). `parts` (on a `line`) lists paths, or `{ path, label?, format? }`, joined by `separator` (default " · "); empty parts are dropped.
295
304
  * - `source` — `{ register, schema, id }` or `{ url }`; omitted = the current object. An entry with `from: "object"` reads the current object even then (a client card that also shows the ticket's channel).
@@ -508,6 +517,47 @@ export default {
508
517
  return this.actionView(this.content?.headerAction)
509
518
  },
510
519
 
520
+ /**
521
+ * The card header's status pill, read from the record like a `pill`
522
+ * entry, or null when it has no text.
523
+ *
524
+ * @return {{label: string, variant: string}|null}
525
+ */
526
+ headerPill() {
527
+ const pill = this.content?.headerPill
528
+ if (!pill || typeof pill !== 'object' || !this.record) {
529
+ return null
530
+ }
531
+ const raw = typeof pill.path === 'string' && pill.path !== '' ? readPath(this.record, pill.path) : undefined
532
+ const mapped = pill.map && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(pill.map, raw) ? pill.map[raw] : undefined
533
+ const text = mapped ?? pill.text ?? raw
534
+ if (typeof text !== 'string' && typeof text !== 'number') {
535
+ return null
536
+ }
537
+ const label = this.tr(String(text))
538
+ if (label === '') {
539
+ return null
540
+ }
541
+ const colours = pill.variantMap || pill.colorMap
542
+ const hit = colours && (typeof raw === 'string' || typeof raw === 'number') && Object.hasOwn(colours, raw) ? colours[raw] : pill.variant
543
+ return { label, variant: isBadgeVariant(hit) ? hit : 'default' }
544
+ },
545
+
546
+ /**
547
+ * The note band under the facts: its text translated, its NcNoteCard
548
+ * type from `variant` (info by default), or null without text.
549
+ *
550
+ * @return {{text: string, type: string}|null}
551
+ */
552
+ notice() {
553
+ const notice = this.content?.notice
554
+ const text = notice && typeof notice.text === 'string' ? this.tr(notice.text) : ''
555
+ if (text === '') {
556
+ return null
557
+ }
558
+ return { text, type: ['success', 'warning', 'error'].includes(notice.variant) ? notice.variant : 'info' }
559
+ },
560
+
511
561
  /** @return {string} The reveal button's text. */
512
562
  showLabel() {
513
563
  return t('nextcloud-vue', 'Show')
@@ -544,6 +594,13 @@ export default {
544
594
  this.emitHeader()
545
595
  },
546
596
  },
597
+
598
+ headerPill: {
599
+ deep: true,
600
+ handler() {
601
+ this.emitHeader()
602
+ },
603
+ },
547
604
  },
548
605
 
549
606
  methods: {
@@ -803,12 +860,13 @@ export default {
803
860
  const view = this.headerAction
804
861
  /**
805
862
  * @event header Emitted with the card header's parts: the header action, if any.
806
- * @type {{link: null, note: string, action: (object|null)}} `action` is `{ label, icon, run }`.
863
+ * @type {{link: null, note: string, action: (object|null), pill: (object|null)}} `action` is `{ label, icon, run }`, `pill` is `{ label, variant }`.
807
864
  */
808
865
  this.$emit('header', {
809
866
  link: null,
810
867
  note: '',
811
868
  action: view ? { ...view, run: () => this.runAction(this.content.headerAction) } : null,
869
+ pill: this.headerPill,
812
870
  })
813
871
  },
814
872
 
@@ -1368,6 +1426,10 @@ export default {
1368
1426
  background: var(--color-primary-element-light-hover, var(--color-primary-element-light));
1369
1427
  }
1370
1428
 
1429
+ .cn-facts-widget__notice {
1430
+ margin: 4px 0 0;
1431
+ }
1432
+
1371
1433
  .cn-facts-widget__link {
1372
1434
  font-size: 14px;
1373
1435
  font-weight: 600;