@conduction/nextcloud-vue 2.56.0-beta.6 → 2.56.0-beta.7

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 (235) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +14 -1
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +39 -0
  5. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +46 -4
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +224 -9
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +79 -25
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js +42 -3
  13. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js +39 -6
  15. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js.map +1 -1
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +46 -0
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +11 -4
  19. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  20. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js +10 -0
  21. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js.map +1 -0
  22. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +39 -0
  23. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -0
  24. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +16 -0
  25. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -0
  26. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -1
  27. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  28. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +147 -89
  29. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  30. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +10 -0
  31. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js.map +1 -0
  32. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +259 -0
  33. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -0
  34. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +131 -0
  35. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -0
  36. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +10 -0
  37. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js.map +1 -0
  38. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +116 -0
  39. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -0
  40. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +24 -0
  41. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -0
  42. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +503 -28
  43. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +300 -142
  45. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js +10 -0
  47. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js.map +1 -0
  48. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js +237 -0
  49. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js.map +1 -0
  50. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js +120 -0
  51. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js.map +1 -0
  52. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js +10 -0
  53. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js.map +1 -0
  54. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +77 -0
  55. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -0
  56. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js +27 -0
  57. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -0
  58. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +169 -12
  59. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  60. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +15 -7
  61. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  62. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js +37 -3
  63. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js.map +1 -1
  64. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js +44 -16
  65. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js.map +1 -1
  66. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +132 -3
  67. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  68. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -3
  69. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  70. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js +10 -0
  71. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js.map +1 -0
  72. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js +150 -0
  73. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -0
  74. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js +106 -0
  75. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -0
  76. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js +9 -0
  77. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js.map +1 -0
  78. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +47 -0
  79. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -0
  80. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js +17 -0
  81. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js.map +1 -0
  82. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +49 -0
  83. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  84. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  85. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js +6 -0
  86. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js.map +1 -1
  87. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue3.js.map +1 -1
  88. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js +46 -0
  89. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js.map +1 -1
  90. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js +15 -3
  91. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js.map +1 -1
  92. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +37 -1
  93. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  94. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +29 -13
  95. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  96. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +50 -3
  97. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  98. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  99. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +10 -0
  100. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js.map +1 -0
  101. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +186 -0
  102. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -0
  103. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +46 -0
  104. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -0
  105. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js +11 -0
  106. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js.map +1 -0
  107. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +298 -0
  108. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -0
  109. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +77 -0
  110. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -0
  111. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js +10 -0
  112. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js.map +1 -0
  113. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js +169 -0
  114. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js.map +1 -0
  115. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js +48 -0
  116. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js.map +1 -0
  117. package/dist/esm/components/CnTabs/CnTab.vue2.js +35 -0
  118. package/dist/esm/components/CnTabs/CnTab.vue2.js.map +1 -1
  119. package/dist/esm/components/CnTabs/CnTab.vue3.js.map +1 -1
  120. package/dist/esm/components/CnTabs/CnTabs.vue2.js +120 -7
  121. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  122. package/dist/esm/components/CnTabs/CnTabs.vue3.js +57 -5
  123. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  124. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +59 -4
  125. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  126. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +6 -2
  127. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  128. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +10 -0
  129. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js.map +1 -0
  130. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +484 -0
  131. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -0
  132. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +115 -0
  133. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -0
  134. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js +10 -0
  135. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js.map +1 -0
  136. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js +189 -0
  137. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js.map +1 -0
  138. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js +74 -0
  139. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js.map +1 -0
  140. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +82 -8
  141. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  142. package/dist/esm/index.js +10 -0
  143. package/dist/esm/index.js.map +1 -1
  144. package/dist/esm/l10n/en.json.js +54 -1
  145. package/dist/esm/l10n/en.json.js.map +1 -1
  146. package/dist/esm/l10n/nl.json.js +54 -1
  147. package/dist/esm/l10n/nl.json.js.map +1 -1
  148. package/dist/esm/nextcloud-vue.css +931 -4
  149. package/dist/esm/utils/dateVariant.js +124 -0
  150. package/dist/esm/utils/dateVariant.js.map +1 -0
  151. package/dist/esm/utils/detailActionModel.js +260 -0
  152. package/dist/esm/utils/detailActionModel.js.map +1 -0
  153. package/dist/esm/utils/dueRule.js +84 -0
  154. package/dist/esm/utils/dueRule.js.map +1 -0
  155. package/dist/esm/utils/fetchAggregate.js +37 -1
  156. package/dist/esm/utils/fetchAggregate.js.map +1 -1
  157. package/dist/esm/utils/fetchFilterCounts.js +186 -0
  158. package/dist/esm/utils/fetchFilterCounts.js.map +1 -0
  159. package/dist/esm/utils/libraryWidgetKeys.js +5 -0
  160. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  161. package/dist/esm/utils/readPath.js +30 -0
  162. package/dist/esm/utils/readPath.js.map +1 -0
  163. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  164. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  165. package/dist/nextcloud-vue.cjs.js +14970 -8991
  166. package/dist/nextcloud-vue.cjs.js.map +2 -2
  167. package/dist/nextcloud-vue.css +931 -4
  168. package/l10n/en.json +54 -1
  169. package/l10n/nl.json +54 -1
  170. package/package.json +1 -1
  171. package/scripts/.jsdoc-baselines.json +3 -0
  172. package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
  173. package/src/components/CnAppNav/CnAppNav.md +26 -0
  174. package/src/components/CnAppNav/CnAppNav.vue +104 -1
  175. package/src/components/CnBannerWidget/CnBannerWidget.vue +365 -10
  176. package/src/components/CnBannerWidgetForm/CnBannerWidgetForm.vue +65 -3
  177. package/src/components/CnBoardView/CnBoardView.vue +77 -0
  178. package/src/components/CnBrandStripe/CnBrandStripe.md +22 -0
  179. package/src/components/CnBrandStripe/CnBrandStripe.vue +100 -0
  180. package/src/components/CnBrandStripe/index.js +4 -0
  181. package/src/components/CnCellRenderer/CnCellRenderer.vue +207 -1
  182. package/src/components/CnConversationThread/CnConversationThread.md +51 -0
  183. package/src/components/CnConversationThread/CnConversationThread.vue +457 -0
  184. package/src/components/CnConversationThread/CnConversationWidget.vue +144 -0
  185. package/src/components/CnConversationThread/index.js +4 -0
  186. package/src/components/CnDetailPage/CnDetailPage.md +16 -0
  187. package/src/components/CnDetailPage/CnDetailPage.vue +683 -48
  188. package/src/components/CnDocumentReviewList/CnDocumentReviewList.md +55 -0
  189. package/src/components/CnDocumentReviewList/CnDocumentReviewList.vue +384 -0
  190. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +108 -0
  191. package/src/components/CnDocumentReviewList/index.js +4 -0
  192. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +189 -14
  193. package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
  194. package/src/components/CnIndexPage/CnIndexPage.vue +135 -3
  195. package/src/components/CnNextStepCard/CnNextStepCard.md +40 -0
  196. package/src/components/CnNextStepCard/CnNextStepCard.vue +319 -0
  197. package/src/components/CnNextStepCard/CnNextStepWidget.vue +59 -0
  198. package/src/components/CnNextStepCard/index.js +4 -0
  199. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +49 -0
  200. package/src/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue +6 -0
  201. package/src/components/CnObjectKanban/CnObjectKanban.vue +86 -2
  202. package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +3 -0
  203. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +60 -1
  204. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +50 -3
  205. package/src/components/CnSegmentedControl/CnSegmentedControl.md +46 -0
  206. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +291 -0
  207. package/src/components/CnSegmentedControl/index.js +4 -0
  208. package/src/components/CnStackedBarWidget/CnStackedBarWidget.md +15 -0
  209. package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +414 -0
  210. package/src/components/CnStackedBarWidget/index.js +14 -0
  211. package/src/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue +224 -0
  212. package/src/components/CnStackedBarWidgetForm/index.js +4 -0
  213. package/src/components/CnTabs/CnTab.vue +35 -0
  214. package/src/components/CnTabs/CnTabs.vue +244 -10
  215. package/src/components/CnTabsWidget/CnTabsWidget.vue +63 -4
  216. package/src/components/CnWeekStripWidget/CnWeekStripWidget.md +31 -0
  217. package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +668 -0
  218. package/src/components/CnWeekStripWidget/index.js +14 -0
  219. package/src/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue +265 -0
  220. package/src/components/CnWeekStripWidgetForm/index.js +4 -0
  221. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +74 -0
  222. package/src/components/index.js +11 -0
  223. package/src/css/detail-page.css +47 -0
  224. package/src/index.js +9 -0
  225. package/src/schemas/app-manifest-v2.schema.json +389 -1
  226. package/src/schemas/app-manifest.schema.json +2 -1
  227. package/src/utils/dateVariant.js +120 -0
  228. package/src/utils/detailActionModel.js +256 -0
  229. package/src/utils/dueRule.js +94 -0
  230. package/src/utils/fetchAggregate.js +36 -0
  231. package/src/utils/fetchFilterCounts.js +182 -0
  232. package/src/utils/index.js +1 -0
  233. package/src/utils/libraryWidgetKeys.js +5 -0
  234. package/src/utils/readPath.js +27 -0
  235. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -0,0 +1,55 @@
1
+ Five documents, two reviewed:
2
+
3
+ ```vue
4
+ <CnDocumentReviewList
5
+ title="Review documents"
6
+ :documents="[
7
+ { id: 1, name: 'Advice on street lighting', meta: 'PDF, from the resident file', reviewStatus: 'public' },
8
+ { id: 2, name: 'Mail from the contractor about planning', meta: 'E-mail, 12 March 2026', reviewStatus: 'partly-public' },
9
+ { id: 3, name: 'Council proposal', meta: 'PDF, already published', reviewStatus: 'to-review' },
10
+ { id: 4, name: 'Internal budget note', meta: 'Word, 2 February 2026' },
11
+ { id: 5, name: 'Quote for lamp posts', meta: 'PDF, 20 January 2026' },
12
+ ]" />
13
+ ```
14
+
15
+ Your own status field and values:
16
+
17
+ ```vue
18
+ <CnDocumentReviewList
19
+ title="Check attachments"
20
+ status-field="verdict"
21
+ row-action-label="Open"
22
+ :statuses="[
23
+ { value: 'approved', label: 'Approved', variant: 'success', reviewed: true },
24
+ { value: 'rejected', label: 'Rejected', variant: 'error', reviewed: true },
25
+ { value: 'open', label: 'Not checked' },
26
+ ]"
27
+ :documents="[
28
+ { id: 1, name: 'Passport copy', verdict: 'approved' },
29
+ { id: 2, name: 'Bank statement', verdict: 'open' },
30
+ ]" />
31
+ ```
32
+
33
+ Nothing to review:
34
+
35
+ ```vue
36
+ <CnDocumentReviewList title="Review documents" :documents="[]" />
37
+ ```
38
+
39
+ Records that name their fields differently. `name-field`, `meta-field`, `href-field` and `row-key` say where to look, `default-status` is the status of a document that carries none, and `title-tag` and `empty-text` adjust the heading and the empty state:
40
+
41
+ ```vue
42
+ <CnDocumentReviewList
43
+ title="Review documents"
44
+ title-tag="h2"
45
+ name-field="filename"
46
+ meta-field="description"
47
+ href-field="downloadUrl"
48
+ row-key="uuid"
49
+ default-status="to-review"
50
+ empty-text="This request has no documents yet."
51
+ :documents="[
52
+ { uuid: 'a1', filename: 'advice.pdf', description: 'PDF, 240 kB', downloadUrl: '#advice', reviewStatus: 'public' },
53
+ { uuid: 'b2', filename: 'note.docx', description: 'Word, 18 kB' },
54
+ ]" />
55
+ ```
@@ -0,0 +1,384 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+
6
+ <template>
7
+ <section
8
+ class="cn-document-review-list"
9
+ data-testid="cn-document-review-list"
10
+ :aria-labelledby="title ? titleId : null"
11
+ :aria-label="title ? null : fallbackLabel">
12
+ <div v-if="title || rows.length > 0" class="cn-document-review-list__header">
13
+ <component
14
+ :is="titleTag"
15
+ v-if="title"
16
+ :id="titleId"
17
+ class="cn-document-review-list__title">
18
+ {{ title }}
19
+ </component>
20
+ <span
21
+ v-if="rows.length > 0"
22
+ class="cn-document-review-list__summary"
23
+ data-testid="cn-document-review-summary">
24
+ {{ summary }}
25
+ </span>
26
+ </div>
27
+
28
+ <p v-if="rows.length === 0" class="cn-document-review-list__empty" data-testid="cn-document-review-empty">
29
+ {{ emptyText }}
30
+ </p>
31
+
32
+ <ul v-else class="cn-document-review-list__rows">
33
+ <li
34
+ v-for="row in rows"
35
+ :key="row.key"
36
+ class="cn-document-review-list__row"
37
+ data-testid="cn-document-review-row">
38
+ <FileDocumentOutline class="cn-document-review-list__icon" :size="22" aria-hidden="true" />
39
+ <span class="cn-document-review-list__text">
40
+ <a
41
+ v-if="row.href"
42
+ class="cn-document-review-list__name"
43
+ :href="row.href">
44
+ {{ row.name }}
45
+ </a>
46
+ <span v-else class="cn-document-review-list__name">{{ row.name }}</span>
47
+ <span v-if="row.meta" class="cn-document-review-list__meta">{{ row.meta }}</span>
48
+ </span>
49
+ <CnStatusBadge
50
+ class="cn-document-review-list__status"
51
+ :label="row.status.label"
52
+ :variant="row.status.variant" />
53
+ <!-- @slot row-action Replace the row's action button. -->
54
+ <!-- @binding {object} document The row's document. -->
55
+ <!-- @binding {object} status The row's resolved status ({ value, label, variant, reviewed }). -->
56
+ <slot name="row-action" :document="row.document" :status="row.status">
57
+ <NcButton
58
+ v-if="rowActionLabel"
59
+ variant="tertiary"
60
+ :aria-label="`${rowActionLabel}: ${row.name}`"
61
+ data-testid="cn-document-review-action"
62
+ @click="$emit('row-action', row.document)">
63
+ {{ rowActionLabel }}
64
+ </NcButton>
65
+ </slot>
66
+ </li>
67
+ </ul>
68
+ </section>
69
+ </template>
70
+
71
+ <script>
72
+ import { translate as t } from '@nextcloud/l10n'
73
+ import { NcButton } from '@nextcloud/vue'
74
+ import FileDocumentOutline from 'vue-material-design-icons/FileDocumentOutline.vue'
75
+ import { readPath } from '../../utils/readPath.js'
76
+ import { CnStatusBadge } from '../CnStatusBadge/index.js'
77
+
78
+ let documentReviewListUid = 0
79
+
80
+ /**
81
+ * CnDocumentReviewList: files that each need a verdict, and how far along you are.
82
+ *
83
+ * One row per file with its review status, a progress line ("2 of 5 reviewed")
84
+ * and one action per row. Built for a disclosure request, where every document
85
+ * is judged public, partly public or still to review, but the status field and
86
+ * its values are yours to configure.
87
+ *
88
+ * ```vue
89
+ * <CnDocumentReviewList
90
+ * title="Review documents"
91
+ * :documents="[
92
+ * { id: 1, name: 'Advice on street lighting', meta: 'PDF', reviewStatus: 'public' },
93
+ * { id: 2, name: 'Budget note', meta: 'Word, 2 February 2026' },
94
+ * ]"
95
+ * @row-action="openDocument" />
96
+ * ```
97
+ *
98
+ * The `document-review` detail widget type renders this list from a field on
99
+ * the record.
100
+ */
101
+ export default {
102
+ name: 'CnDocumentReviewList',
103
+
104
+ components: {
105
+ CnStatusBadge,
106
+ FileDocumentOutline,
107
+ NcButton,
108
+ },
109
+
110
+ props: {
111
+ /** The documents, one object per row. */
112
+ documents: {
113
+ type: Array,
114
+ default: () => [],
115
+ },
116
+
117
+ /** Heading of the list. */
118
+ title: {
119
+ type: String,
120
+ default: '',
121
+ },
122
+
123
+ /** Element the heading renders as. */
124
+ titleTag: {
125
+ type: String,
126
+ default: 'h3',
127
+ },
128
+
129
+ /** Dot-path to a document's display name. Falls back to `title`, then `filename`. */
130
+ nameField: {
131
+ type: String,
132
+ default: 'name',
133
+ },
134
+
135
+ /** Dot-path to the line under the name (type, date, origin). */
136
+ metaField: {
137
+ type: String,
138
+ default: 'meta',
139
+ },
140
+
141
+ /** Dot-path to a document's review status. */
142
+ statusField: {
143
+ type: String,
144
+ default: 'reviewStatus',
145
+ },
146
+
147
+ /** Dot-path to a URL that opens the document. When a row has one, its name is a link. */
148
+ hrefField: {
149
+ type: String,
150
+ default: 'href',
151
+ },
152
+
153
+ /** Dot-path to the value that identifies a row. */
154
+ rowKey: {
155
+ type: String,
156
+ default: 'id',
157
+ },
158
+
159
+ /**
160
+ * The review statuses: `{ value, label, variant?, reviewed? }` each.
161
+ * `variant` is a CnStatusBadge variant. `reviewed: true` counts the
162
+ * row in the progress line. Empty uses the built-in three: public,
163
+ * partly public and to review.
164
+ */
165
+ statuses: {
166
+ type: Array,
167
+ default: () => [],
168
+ },
169
+
170
+ /**
171
+ * The status of a document that carries none, or one this list does
172
+ * not know. Defaults to the first status that is not `reviewed`.
173
+ */
174
+ defaultStatus: {
175
+ type: String,
176
+ default: '',
177
+ },
178
+
179
+ /** Label of the per-row action button. An empty string renders no button. */
180
+ rowActionLabel: {
181
+ type: String,
182
+ default: () => t('nextcloud-vue', 'Review'),
183
+ },
184
+
185
+ /** Shown when there are no documents. */
186
+ emptyText: {
187
+ type: String,
188
+ default: () => t('nextcloud-vue', 'No documents to review.'),
189
+ },
190
+ },
191
+
192
+ emits: [
193
+ /** A row's action button was pressed. Payload: the document. */
194
+ 'row-action',
195
+ ],
196
+
197
+ data() {
198
+ documentReviewListUid += 1
199
+ return {
200
+ titleId: `cn-document-review-title-${documentReviewListUid}`,
201
+ }
202
+ },
203
+
204
+ computed: {
205
+ /**
206
+ * The statuses in effect: the configured ones, or the built-in three.
207
+ *
208
+ * @return {Array<{value: string, label: string, variant: string, reviewed: boolean}>} The statuses.
209
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
210
+ */
211
+ effectiveStatuses() {
212
+ const configured = (Array.isArray(this.statuses) ? this.statuses : [])
213
+ .filter((status) => status && status.value !== undefined && status.value !== null)
214
+ .map((status) => ({
215
+ value: String(status.value),
216
+ label: typeof status.label === 'string' && status.label !== '' ? status.label : String(status.value),
217
+ variant: typeof status.variant === 'string' && status.variant !== '' ? status.variant : 'default',
218
+ reviewed: status.reviewed === true,
219
+ }))
220
+ if (configured.length > 0) {
221
+ return configured
222
+ }
223
+ return [
224
+ { value: 'public', label: t('nextcloud-vue', 'Public'), variant: 'success', reviewed: true },
225
+ { value: 'partly-public', label: t('nextcloud-vue', 'Partly public'), variant: 'warning', reviewed: true },
226
+ { value: 'to-review', label: t('nextcloud-vue', 'To review'), variant: 'default', reviewed: false },
227
+ ]
228
+ },
229
+
230
+ /**
231
+ * The status a document without a known one falls back to.
232
+ *
233
+ * @return {{value: string, label: string, variant: string, reviewed: boolean}} The fallback status.
234
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
235
+ */
236
+ fallbackStatus() {
237
+ const statuses = this.effectiveStatuses
238
+ return statuses.find((status) => status.value === this.defaultStatus)
239
+ || statuses.find((status) => !status.reviewed)
240
+ || statuses[0]
241
+ },
242
+
243
+ /**
244
+ * The rows, with everything the template needs resolved.
245
+ *
246
+ * @return {Array<{key: string, name: string, meta: string, href: string, status: object, document: object}>} The rows.
247
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
248
+ */
249
+ rows() {
250
+ return (Array.isArray(this.documents) ? this.documents : [])
251
+ .filter((document) => document && typeof document === 'object')
252
+ .map((document, index) => {
253
+ const raw = readPath(document, this.statusField)
254
+ const wanted = raw === null || raw === undefined ? '' : String(raw).toLowerCase()
255
+ const status = this.effectiveStatuses.find((candidate) => candidate.value.toLowerCase() === wanted)
256
+ || this.fallbackStatus
257
+ const name = readPath(document, this.nameField) || document.title || document.filename || ''
258
+ const key = readPath(document, this.rowKey)
259
+ const href = readPath(document, this.hrefField)
260
+ const meta = readPath(document, this.metaField)
261
+ return {
262
+ key: key === undefined || key === null ? `row-${index}` : String(key),
263
+ name: String(name),
264
+ meta: typeof meta === 'string' ? meta : '',
265
+ href: typeof href === 'string' ? href : '',
266
+ status,
267
+ document,
268
+ }
269
+ })
270
+ },
271
+
272
+ /**
273
+ * How many rows carry a reviewed status.
274
+ *
275
+ * @return {number} The count.
276
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
277
+ */
278
+ reviewedCount() {
279
+ return this.rows.filter((row) => row.status.reviewed).length
280
+ },
281
+
282
+ /**
283
+ * The progress line, for example "2 of 5 reviewed".
284
+ *
285
+ * @return {string} The summary.
286
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
287
+ */
288
+ summary() {
289
+ return t('nextcloud-vue', '{reviewed} of {total} reviewed', {
290
+ reviewed: this.reviewedCount,
291
+ total: this.rows.length,
292
+ })
293
+ },
294
+
295
+ /**
296
+ * Accessible name for a list without a title.
297
+ *
298
+ * @return {string} The label.
299
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
300
+ */
301
+ fallbackLabel() {
302
+ return t('nextcloud-vue', 'Documents to review')
303
+ },
304
+ },
305
+ }
306
+ </script>
307
+
308
+ <style scoped>
309
+ .cn-document-review-list {
310
+ display: flex;
311
+ flex-direction: column;
312
+ gap: calc(1.5 * var(--default-grid-baseline));
313
+ }
314
+
315
+ .cn-document-review-list__header {
316
+ align-items: center;
317
+ display: flex;
318
+ flex-wrap: wrap;
319
+ gap: calc(2 * var(--default-grid-baseline));
320
+ justify-content: space-between;
321
+ margin-bottom: calc(2 * var(--default-grid-baseline));
322
+ }
323
+
324
+ .cn-document-review-list__title {
325
+ font-size: 1.1em;
326
+ font-weight: 700;
327
+ margin: 0;
328
+ }
329
+
330
+ .cn-document-review-list__summary,
331
+ .cn-document-review-list__meta,
332
+ .cn-document-review-list__empty {
333
+ color: var(--color-text-maxcontrast);
334
+ }
335
+
336
+ .cn-document-review-list__empty {
337
+ margin: 0;
338
+ }
339
+
340
+ .cn-document-review-list__rows {
341
+ list-style: none;
342
+ margin: 0;
343
+ padding: 0;
344
+ }
345
+
346
+ .cn-document-review-list__row {
347
+ align-items: center;
348
+ border-top: 1px solid var(--color-border);
349
+ display: flex;
350
+ flex-wrap: wrap;
351
+ gap: calc(3 * var(--default-grid-baseline));
352
+ padding: calc(2.5 * var(--default-grid-baseline)) 0;
353
+ }
354
+
355
+ .cn-document-review-list__icon {
356
+ color: var(--color-text-maxcontrast);
357
+ flex: none;
358
+ }
359
+
360
+ .cn-document-review-list__text {
361
+ display: flex;
362
+ flex: 1 1 220px;
363
+ flex-direction: column;
364
+ min-width: 0;
365
+ }
366
+
367
+ .cn-document-review-list__name {
368
+ font-weight: 500;
369
+ overflow-wrap: anywhere;
370
+ }
371
+
372
+ a.cn-document-review-list__name {
373
+ color: var(--color-primary-element);
374
+ text-decoration: underline;
375
+ }
376
+
377
+ .cn-document-review-list__meta {
378
+ font-size: 0.9em;
379
+ }
380
+
381
+ .cn-document-review-list__status {
382
+ flex: none;
383
+ }
384
+ </style>
@@ -0,0 +1,108 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+
6
+ <template>
7
+ <div class="cn-case-widget-card">
8
+ <CnDocumentReviewList
9
+ :documents="documents"
10
+ :title="content.title || ''"
11
+ :nameField="content.nameField || 'name'"
12
+ :metaField="content.metaField || 'meta'"
13
+ :statusField="content.statusField || 'reviewStatus'"
14
+ :hrefField="content.hrefField || 'href'"
15
+ :rowKey="content.rowKey || 'id'"
16
+ :statuses="content.statuses || []"
17
+ :defaultStatus="content.defaultStatus || ''"
18
+ :rowActionLabel="rowActionLabel"
19
+ :emptyText="content.emptyText || undefined"
20
+ @rowAction="onRowAction" />
21
+ </div>
22
+ </template>
23
+
24
+ <script>
25
+ import CnDocumentReviewList from './CnDocumentReviewList.vue'
26
+ import { readPath } from '../../utils/readPath.js'
27
+
28
+ /**
29
+ * CnDocumentReviewWidget: the `document-review` detail widget type.
30
+ *
31
+ * Renders CnDocumentReviewList from a list on the bound record:
32
+ * `content: { field, title?, statusField?, statuses?, rowAction?: { label, route } }`.
33
+ * A row action with a `route` opens that route with the row's key as `id`.
34
+ * Resolved by its registry key, not exported.
35
+ */
36
+ export default {
37
+ name: 'CnDocumentReviewWidget',
38
+
39
+ components: {
40
+ CnDocumentReviewList,
41
+ },
42
+
43
+ props: {
44
+ /** The widget's content. `field` is the dot-path to the documents on the record. */
45
+ content: {
46
+ type: Object,
47
+ default: () => ({}),
48
+ },
49
+
50
+ /** The bound record. */
51
+ objectData: {
52
+ type: Object,
53
+ default: null,
54
+ },
55
+ },
56
+
57
+ computed: {
58
+ /**
59
+ * The documents read off the record.
60
+ *
61
+ * @return {Array<object>} The documents, or an empty list.
62
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
63
+ */
64
+ documents() {
65
+ const value = readPath(this.objectData || {}, this.content.field || 'documents')
66
+ return Array.isArray(value) ? value : []
67
+ },
68
+
69
+ /**
70
+ * The row action's label. Empty, and so no button, unless the content declares one.
71
+ *
72
+ * @return {string} The label.
73
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
74
+ */
75
+ rowActionLabel() {
76
+ const label = this.content.rowAction?.label
77
+ return typeof label === 'string' ? label : ''
78
+ },
79
+ },
80
+
81
+ methods: {
82
+ /**
83
+ * Open the route the row action names, with the row's key as `id`.
84
+ *
85
+ * @param {object} document The row's document.
86
+ * @return {void}
87
+ * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-document-review-list
88
+ */
89
+ onRowAction(document) {
90
+ const route = this.content.rowAction?.route
91
+ const id = readPath(document, this.content.rowKey || 'id')
92
+ if (typeof route !== 'string' || route === '' || !this.$router || id === undefined || id === null) {
93
+ return
94
+ }
95
+ this.$router.push({ name: route, params: { id: String(id) } })
96
+ },
97
+ },
98
+ }
99
+ </script>
100
+
101
+ <style scoped>
102
+ .cn-case-widget-card {
103
+ background: var(--color-main-background);
104
+ border: 1px solid var(--color-border);
105
+ border-radius: var(--border-radius-large);
106
+ padding: calc(5 * var(--default-grid-baseline));
107
+ }
108
+ </style>
@@ -0,0 +1,4 @@
1
+ import CnDocumentReviewList from './CnDocumentReviewList.vue'
2
+
3
+ export default CnDocumentReviewList
4
+ export { CnDocumentReviewList }