@conduction/nextcloud-vue 2.59.0 → 2.60.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 (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
@@ -4,15 +4,22 @@
4
4
  -->
5
5
 
6
6
  <template>
7
- <div class="cn-header-widget" :style="wrapperStyle">
7
+ <div class="cn-header-widget" :class="{ 'cn-header-widget--plain': isPlain }" :style="wrapperStyle">
8
8
  <div
9
9
  v-if="hasOverlay"
10
10
  class="cn-header-widget__overlay"
11
11
  :style="overlayStyle"
12
12
  aria-hidden="true" />
13
13
  <div class="cn-header-widget__content" :style="contentStyle">
14
+ <p
15
+ v-if="dateLine"
16
+ class="cn-header-widget__date"
17
+ :style="dateStyle"
18
+ data-testid="cn-header-widget-date">
19
+ {{ dateLine }}
20
+ </p>
14
21
  <h2 v-if="hasTitle" class="cn-header-widget__title" :style="textStyle">
15
- {{ title }}
22
+ {{ headingText }}
16
23
  </h2>
17
24
  <p v-if="hasSubtitle" class="cn-header-widget__subtitle" :style="textStyle">
18
25
  {{ subtitle }}
@@ -38,7 +45,8 @@
38
45
  </template>
39
46
 
40
47
  <script>
41
- import { translate as t } from '@nextcloud/l10n'
48
+ import { getCurrentUser } from '@nextcloud/auth'
49
+ import { getCanonicalLocale, translate as t } from '@nextcloud/l10n'
42
50
  import { resolveImageUrl } from '../../utils/resolveImageUrl.js'
43
51
 
44
52
  const ALLOWED_OVERLAY_MODES = ['none', 'tint', 'gradient-bottom']
@@ -75,6 +83,16 @@ const VERTICAL_ALIGN_FLEX = Object.freeze({
75
83
  * External CTA URLs (http/https) open in a new tab with
76
84
  * `rel="noopener noreferrer"`; relative / anchor URLs stay in the current tab.
77
85
  *
86
+ * Optional greeting: `greeting: true` makes the heading greet the signed-in
87
+ * user by time of day and first name ("Good afternoon, Pieter"; `"full"` uses
88
+ * the whole display name), and `showDate: true` adds a line with today's
89
+ * date above it. `plain: true` drops the coloured background and aligns the
90
+ * text to the start, which is how a greeting usually sits on a dashboard.
91
+ *
92
+ * ```json
93
+ * { "widgetKey": "header", "props": { "content": { "greeting": true, "showDate": true, "plain": true } } }
94
+ * ```
95
+ *
78
96
  * Registered as the `header` dashboard widget type via the renderer's
79
97
  * `index.js`.
80
98
  */
@@ -96,9 +114,10 @@ export default {
96
114
  /**
97
115
  * Persisted widget content: `{title, subtitle, backgroundImageUrl,
98
116
  * backgroundImageFileId, backgroundColor, overlayMode, overlayColor,
99
- * overlayOpacity, textColor, textAlign, verticalAlign, height, cta}`.
100
- * All fields are optional except `title`; unknown enum values collapse
101
- * to documented defaults and the renderer never throws.
117
+ * overlayOpacity, textColor, textAlign, verticalAlign, height, cta,
118
+ * greeting, showDate, plain}`. All fields are optional except `title`
119
+ * (or `greeting`); unknown enum values collapse to documented defaults
120
+ * and the renderer never throws.
102
121
  *
103
122
  * @type {object}
104
123
  */
@@ -118,6 +137,17 @@ export default {
118
137
  default: null,
119
138
  },
120
139
  /* eslint-enable vue/no-unused-properties */
140
+
141
+ /**
142
+ * The moment the greeting and the date line are computed for. Leave
143
+ * empty for the current time.
144
+ *
145
+ * @type {Date|null}
146
+ */
147
+ now: {
148
+ type: Date,
149
+ default: null,
150
+ },
121
151
  },
122
152
 
123
153
  data() {
@@ -153,9 +183,121 @@ export default {
153
183
  return typeof value === 'string' && value !== '' ? this.effectiveTranslate(value) : ''
154
184
  },
155
185
 
156
- /** Whether a non-empty title is set. */
186
+ /** Whether a heading renders: a non-empty title or a greeting. */
157
187
  hasTitle() {
158
- return this.title !== ''
188
+ return this.headingText !== ''
189
+ },
190
+
191
+ /**
192
+ * The moment the greeting and date line are computed for.
193
+ *
194
+ * @return {Date}
195
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
196
+ */
197
+ reference() {
198
+ return this.now instanceof Date ? this.now : new Date()
199
+ },
200
+
201
+ /**
202
+ * Whether the plain presentation is on (no coloured background).
203
+ *
204
+ * @return {boolean}
205
+ */
206
+ isPlain() {
207
+ return Boolean(this.content && this.content.plain === true)
208
+ },
209
+
210
+ /**
211
+ * The name the greeting uses: the first word of the display name, or
212
+ * the whole display name for `greeting: "full"`. '' when unknown.
213
+ *
214
+ * @return {string}
215
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
216
+ */
217
+ greetingName() {
218
+ let user
219
+ try {
220
+ user = getCurrentUser()
221
+ } catch {
222
+ user = null
223
+ }
224
+ const displayName = (user && typeof user.displayName === 'string') ? user.displayName.trim() : ''
225
+ if (displayName === '') {
226
+ return ''
227
+ }
228
+ return this.content.greeting === 'full' ? displayName : displayName.split(/\s+/)[0]
229
+ },
230
+
231
+ /**
232
+ * The greeting for the time of day, with the user's name when known,
233
+ * or '' when the greeting is off.
234
+ *
235
+ * @return {string}
236
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
237
+ */
238
+ greetingText() {
239
+ const greeting = this.content && this.content.greeting
240
+ if (greeting !== true && greeting !== 'full' && greeting !== 'first') {
241
+ return ''
242
+ }
243
+ const hour = this.reference.getHours()
244
+ const name = this.greetingName
245
+ if (hour < 12) {
246
+ return name ? t('nextcloud-vue', 'Good morning, {name}', { name }) : t('nextcloud-vue', 'Good morning')
247
+ }
248
+ if (hour < 18) {
249
+ return name ? t('nextcloud-vue', 'Good afternoon, {name}', { name }) : t('nextcloud-vue', 'Good afternoon')
250
+ }
251
+ return name ? t('nextcloud-vue', 'Good evening, {name}', { name }) : t('nextcloud-vue', 'Good evening')
252
+ },
253
+
254
+ /**
255
+ * The heading: the greeting when it is on, else the title.
256
+ *
257
+ * @return {string}
258
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
259
+ */
260
+ headingText() {
261
+ return this.greetingText || this.title
262
+ },
263
+
264
+ /**
265
+ * Today's date written out ("Monday 5 October 2026"), or '' when
266
+ * `showDate` is off.
267
+ *
268
+ * @return {string}
269
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
270
+ */
271
+ dateLine() {
272
+ if (!this.content || this.content.showDate !== true) {
273
+ return ''
274
+ }
275
+ const options = { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }
276
+ let locale
277
+ try {
278
+ locale = getCanonicalLocale()
279
+ } catch {
280
+ locale = undefined
281
+ }
282
+ try {
283
+ return new Intl.DateTimeFormat(locale || undefined, options).format(this.reference)
284
+ } catch {
285
+ return new Intl.DateTimeFormat(undefined, options).format(this.reference)
286
+ }
287
+ },
288
+
289
+ /**
290
+ * Inline style of the date line: the heading's colour, or the muted
291
+ * text colour on a plain header.
292
+ *
293
+ * @return {object}
294
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
295
+ */
296
+ dateStyle() {
297
+ return {
298
+ color: this.isPlain && !(this.content && this.content.textColor) ? 'var(--color-text-maxcontrast)' : this.textColor,
299
+ margin: 0,
300
+ }
159
301
  },
160
302
 
161
303
  /** Whether a non-empty subtitle is set. */
@@ -187,12 +329,19 @@ export default {
187
329
  return this.backgroundImageUrl !== '' && this.imageFailed === false
188
330
  },
189
331
 
190
- /** Resolved background colour (default theme primary). */
332
+ /**
333
+ * Resolved background colour (default theme primary).
334
+ *
335
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
336
+ */
191
337
  backgroundColor() {
192
338
  const value = this.content && this.content.backgroundColor
193
339
  if (typeof value === 'string' && value !== '') {
194
340
  return value
195
341
  }
342
+ if (this.isPlain) {
343
+ return 'transparent'
344
+ }
196
345
  return 'var(--color-primary, #0070c0)'
197
346
  },
198
347
 
@@ -250,13 +399,17 @@ export default {
250
399
  return HEIGHT_PIXELS[this.height]
251
400
  },
252
401
 
253
- /** Resolved text alignment (default `center`). */
402
+ /**
403
+ * Resolved text alignment (default `center`).
404
+ *
405
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
406
+ */
254
407
  textAlign() {
255
408
  const declared = this.content && this.content.textAlign
256
409
  if (typeof declared === 'string' && ALLOWED_TEXT_ALIGN.includes(declared)) {
257
410
  return declared
258
411
  }
259
- return 'center'
412
+ return this.isPlain ? 'left' : 'center'
260
413
  },
261
414
 
262
415
  /** Resolved vertical alignment (default `middle`). */
@@ -268,7 +421,11 @@ export default {
268
421
  return 'middle'
269
422
  },
270
423
 
271
- /** Resolved text colour with an auto-contrast fallback. */
424
+ /**
425
+ * Resolved text colour with an auto-contrast fallback.
426
+ *
427
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
428
+ */
272
429
  textColor() {
273
430
  const value = this.content && this.content.textColor
274
431
  if (typeof value === 'string' && value !== '') {
@@ -277,6 +434,9 @@ export default {
277
434
  if (this.hasBackgroundImage) {
278
435
  return '#ffffff'
279
436
  }
437
+ if (this.isPlain && !(this.content && this.content.backgroundColor)) {
438
+ return 'var(--color-main-text)'
439
+ }
280
440
  return this.isLightColor(this.backgroundColor) ? '#000000' : '#ffffff'
281
441
  },
282
442
 
@@ -321,7 +481,11 @@ export default {
321
481
  }
322
482
  },
323
483
 
324
- /** Inline style for the content flex container. */
484
+ /**
485
+ * Inline style for the content flex container.
486
+ *
487
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
488
+ */
325
489
  contentStyle() {
326
490
  return {
327
491
  position: 'relative',
@@ -332,7 +496,7 @@ export default {
332
496
  'flex-direction': 'column',
333
497
  'align-items': this.flexAlignFromTextAlign,
334
498
  'justify-content': VERTICAL_ALIGN_FLEX[this.verticalAlign],
335
- padding: '16px 24px',
499
+ padding: this.isPlain ? '0' : '16px 24px',
336
500
  'box-sizing': 'border-box',
337
501
  gap: '8px',
338
502
  'text-align': this.textAlign,
@@ -484,6 +648,17 @@ export default {
484
648
  overflow: hidden;
485
649
  }
486
650
 
651
+ .cn-header-widget--plain {
652
+ min-height: 0;
653
+ border-radius: 0;
654
+ }
655
+
656
+ .cn-header-widget__date {
657
+ font-size: 14px;
658
+ line-height: 1.4;
659
+ margin: 0;
660
+ }
661
+
487
662
  .cn-header-widget__overlay {
488
663
  position: absolute;
489
664
  inset: 0;
@@ -18,6 +18,22 @@
18
18
  :placeholder="t('nextcloud-vue', 'Optional subtitle')"
19
19
  @update:modelValue="updateField('subtitle', $event)" />
20
20
 
21
+ <NcCheckboxRadioSwitch
22
+ :modelValue="greeting"
23
+ @update:modelValue="updateField('greeting', $event)">
24
+ {{ t('nextcloud-vue', 'Greet the user by name instead of showing the title') }}
25
+ </NcCheckboxRadioSwitch>
26
+ <NcCheckboxRadioSwitch
27
+ :modelValue="showDate"
28
+ @update:modelValue="updateField('showDate', $event)">
29
+ {{ t('nextcloud-vue', 'Show the date of today above the heading') }}
30
+ </NcCheckboxRadioSwitch>
31
+ <NcCheckboxRadioSwitch
32
+ :modelValue="plain"
33
+ @update:modelValue="updateField('plain', $event)">
34
+ {{ t('nextcloud-vue', 'Plain header without a coloured background') }}
35
+ </NcCheckboxRadioSwitch>
36
+
21
37
  <!-- Pick a background image. Selection does NOT upload — the file is held
22
38
  and only uploaded when the host modal calls commit() on submit, so
23
39
  re-picking or cancelling writes nothing. Uploads are the reliable
@@ -162,7 +178,7 @@
162
178
 
163
179
  <script>
164
180
  import { translate as t } from '@nextcloud/l10n'
165
- import { NcButton, NcSelect, NcTextField } from '@nextcloud/vue'
181
+ import { NcButton, NcCheckboxRadioSwitch, NcSelect, NcTextField } from '@nextcloud/vue'
166
182
  import CnColorPicker from '../CnColorPicker/CnColorPicker.vue'
167
183
  import { embedAsDataUrl, extractTransportUrl, readFileAsDataUrl, warnUploadFnDeprecated } from '../../utils/widgetUpload.js'
168
184
 
@@ -206,6 +222,7 @@ export default {
206
222
  NcTextField,
207
223
  NcSelect,
208
224
  NcButton,
225
+ NcCheckboxRadioSwitch,
209
226
  CnColorPicker,
210
227
  },
211
228
 
@@ -282,6 +299,11 @@ export default {
282
299
  return {
283
300
  title: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,
284
301
  subtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,
302
+ greeting: initial.greeting === true || initial.greeting === 'full' || initial.greeting === 'first',
303
+ // `"full"` (whole display name) is a manifest choice; kept as written.
304
+ greetingMode: initial.greeting === 'full' ? 'full' : true,
305
+ showDate: initial.showDate === true,
306
+ plain: initial.plain === true,
285
307
  backgroundImageUrl: typeof initial.backgroundImageUrl === 'string'
286
308
  ? initial.backgroundImageUrl
287
309
  : DEFAULT_CONTENT.backgroundImageUrl,
@@ -394,9 +416,35 @@ export default {
394
416
  }
395
417
  },
396
418
 
397
- /** The full content blob assembled from the current field values. */
419
+ /**
420
+ * The greeting keys, written only when they are on, so a header that
421
+ * never used them keeps the content blob it always had.
422
+ *
423
+ * @return {object}
424
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
425
+ */
426
+ greetingContent() {
427
+ const content = {}
428
+ if (this.greeting) {
429
+ content.greeting = this.greetingMode
430
+ }
431
+ if (this.showDate) {
432
+ content.showDate = true
433
+ }
434
+ if (this.plain) {
435
+ content.plain = true
436
+ }
437
+ return content
438
+ },
439
+
440
+ /**
441
+ * The full content blob assembled from the current field values.
442
+ *
443
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
444
+ */
398
445
  assembledContent() {
399
446
  return {
447
+ ...this.greetingContent,
400
448
  title: this.title,
401
449
  subtitle: this.subtitle,
402
450
  backgroundImageUrl: this.backgroundImageUrl,
@@ -538,10 +586,12 @@ export default {
538
586
  * when either CTA field is filled.
539
587
  *
540
588
  * @return {string[]} the validation errors.
589
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
541
590
  */
542
591
  validate() {
543
592
  const errors = []
544
- if (typeof this.title !== 'string' || this.title.trim() === '') {
593
+ // A greeting is the heading, so a greeting header needs no title.
594
+ if (!this.greeting && (typeof this.title !== 'string' || this.title.trim() === '')) {
545
595
  errors.push(t('nextcloud-vue', 'Title is required'))
546
596
  }
547
597
  if (typeof this.backgroundImageUrl === 'string'
@@ -119,6 +119,7 @@
119
119
  :views="viewsForControl"
120
120
  :loading="savedViewsLoading"
121
121
  :currentUserId="currentSavedViewsUserId"
122
+ :counts="savedViewCounts"
122
123
  @apply="onApplySavedView"
123
124
  @saveRequest="showSaveViewDialog = true"
124
125
  @deleteRequest="onDeleteViewRequest" />
@@ -171,6 +172,7 @@
171
172
  :multiple="quickFilterMultiple"
172
173
  :activeIndex="activeQuickFilterIndex"
173
174
  :selectedIndices="selectedQuickFilterIndices"
175
+ :counts="tabCounts"
174
176
  @update:activeIndex="onQuickFilterChange"
175
177
  @update:selectedIndices="onQuickFilterMultiChange" />
176
178
  </template>
@@ -545,6 +547,7 @@
545
547
  :statusField="board.statusField || 'status'"
546
548
  :cardFields="board.cardFields || []"
547
549
  :swimlaneField="board.swimlaneField || ''"
550
+ :dueRule="board.dueRule || null"
548
551
  :rowKey="rowKey"
549
552
  :runTransition="runTransition"
550
553
  :paged="isPaged"
@@ -781,12 +784,14 @@ import { useSavedViewsApi } from '../../composables/useSavedViewsApi.js'
781
784
  import { METADATA_COLUMNS } from '../../constants/metadata.js'
782
785
  import { routeHref } from '../../utils/actionLink.js'
783
786
  import { buildOnSuccessRoute, resolveRegisteredHandler } from '../../utils/actionsDispatcher.js'
787
+ import { fetchFilterCounts } from '../../utils/fetchFilterCounts.js'
784
788
  import { buildExportUrl } from '../../utils/indexExportHelpers.js'
785
789
  import { openRowTarget } from '../../utils/linkNavigation.js'
786
- import { resolveClaimedTeams, resolveClaimTokens, splitViewsIntoTabs, viewAsTab } from '../../utils/listLenses.js'
790
+ import { resolveClaimedTeams, resolveClaimTokens, splitViewsIntoTabs, viewAsTab, viewIdOf } from '../../utils/listLenses.js'
787
791
  import { LIST_SHORTCUTS, listPaletteCommands, shortcutFor } from '../../utils/listShortcuts.js'
788
792
  import { multiKeySort } from '../../utils/multiKeySort.js'
789
793
  import { resolveDeepTokens, resolveFilterValue } from '../../utils/resolveFilterTokens.js'
794
+ import { resolveFilterMap } from '../../utils/routeFilters.js'
790
795
  import { availableRowActions, DEFAULT_ROW_ACTION_FIELD, refusalReasonFor, undeclaredRowActions } from '../../utils/rowActionAvailability.js'
791
796
  import { isNewTabClick, isNewTabHandled, isRowMiddleClick, markNewTabHandled, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
792
797
  import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js'
@@ -1199,7 +1204,6 @@ export default {
1199
1204
  default: null,
1200
1205
  },
1201
1206
 
1202
- /* eslint-disable vue/no-unused-properties -- used in useSelfFetchList */
1203
1207
  /**
1204
1208
  * Base filter for the self-fetch path. String values of the form
1205
1209
  * `"@route.<name>"` / `":<name>"` are interpolated from `$route.params`.
@@ -1208,7 +1212,6 @@ export default {
1208
1212
  type: Object,
1209
1213
  default: null,
1210
1214
  },
1211
- /* eslint-enable vue/no-unused-properties */
1212
1215
 
1213
1216
  /**
1214
1217
  * Self-fetch mode only — an array of clickable filter tabs rendered as
@@ -2211,6 +2214,19 @@ export default {
2211
2214
  default: () => [],
2212
2215
  },
2213
2216
 
2217
+ /**
2218
+ * Which saved views show how many records they match: `true` for all
2219
+ * of them (tabs and the entries in the views control), or a list of
2220
+ * view ids. Off by default, so no count request is made. A quick
2221
+ * filter opts in on its own entry with `showCount: true`.
2222
+ *
2223
+ * @type {boolean|string[]}
2224
+ */
2225
+ viewCounts: {
2226
+ type: [Boolean, Array],
2227
+ default: false,
2228
+ },
2229
+
2214
2230
  /**
2215
2231
  * The teams this person may claim. The instance decides this list; it
2216
2232
  * is the membership side of `claimedTeams`.
@@ -2649,6 +2665,10 @@ export default {
2649
2665
  data() {
2650
2666
  return {
2651
2667
  currentViewMode: this.viewMode,
2668
+ /** Count per tab-strip index, for the entries that asked for one; null = none. */
2669
+ tabCounts: null,
2670
+ /** Count per saved-view id, for the views control; null = none. */
2671
+ savedViewCounts: null,
2652
2672
  /**
2653
2673
  * The non-`_` query keys this page owns, i.e. may clear on the next
2654
2674
  * persist. Seeded with what it adopted from the query on load, and
@@ -3241,6 +3261,55 @@ export default {
3241
3261
  return this.splitSavedViews.control
3242
3262
  },
3243
3263
 
3264
+ /**
3265
+ * The filters whose record count this page shows: quick filters that
3266
+ * set `showCount`, and the saved views `viewCounts` names. Empty when
3267
+ * nothing opted in, which is what keeps the page from making a count
3268
+ * request nobody asked for.
3269
+ *
3270
+ * @return {Array<{key: string, filter: object}>} The filters to count.
3271
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
3272
+ */
3273
+ countEntries() {
3274
+ const entries = []
3275
+ const tabs = this.tabStripEntries || []
3276
+ const lens = this.lensTabs.length > 0
3277
+ tabs.forEach((tab, index) => {
3278
+ const wants = lens ? this.viewWantsCount(tab.id) : (tab && tab.showCount === true)
3279
+ if (wants && tab.narrowsToNothing !== true) {
3280
+ entries.push({ key: `tab:${index}`, filter: tab.filter || {} })
3281
+ }
3282
+ })
3283
+ for (const view of this.viewsForControl) {
3284
+ const id = viewIdOf(view)
3285
+ if (this.viewWantsCount(id)) {
3286
+ entries.push({ key: `view:${id}`, filter: viewAsTab(view).filter })
3287
+ }
3288
+ }
3289
+ return entries
3290
+ },
3291
+
3292
+ /**
3293
+ * Changes whenever the counts have to be fetched again: another set
3294
+ * of filters, another register or schema, or a list whose total moved
3295
+ * (a record was added, removed, or left a filter).
3296
+ *
3297
+ * @return {string} The signature.
3298
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
3299
+ */
3300
+ countRequestKey() {
3301
+ if (this.countEntries.length === 0) {
3302
+ return ''
3303
+ }
3304
+ return JSON.stringify({
3305
+ r: this.register,
3306
+ s: this.exportSchemaSlug,
3307
+ f: this.filter || null,
3308
+ e: this.countEntries,
3309
+ t: Number(this.effectivePagination?.total ?? 0),
3310
+ })
3311
+ },
3312
+
3244
3313
  /**
3245
3314
  * What the tab strip shows: the page's lens tabs when it named any,
3246
3315
  * else its quick filters. One strip, never two, so a person is not
@@ -4279,6 +4348,14 @@ export default {
4279
4348
  },
4280
4349
 
4281
4350
  watch: {
4351
+ countRequestKey: {
4352
+ immediate: true,
4353
+ /** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views */
4354
+ handler() {
4355
+ this.loadFilterCounts()
4356
+ },
4357
+ },
4358
+
4282
4359
  // Remember the whole folder set whenever no folder narrows the query.
4283
4360
  folderSidebarLiveFacetValues: {
4284
4361
  immediate: true,
@@ -4488,6 +4565,61 @@ export default {
4488
4565
  },
4489
4566
 
4490
4567
  methods: {
4568
+ /**
4569
+ * Whether a saved view shows its record count (`viewCounts`).
4570
+ *
4571
+ * @param {string} id The view id.
4572
+ * @return {boolean} True when the view opted in.
4573
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
4574
+ */
4575
+ viewWantsCount(id) {
4576
+ if (this.viewCounts === true) {
4577
+ return true
4578
+ }
4579
+ return Array.isArray(this.viewCounts) && this.viewCounts.map(String).includes(String(id))
4580
+ },
4581
+
4582
+ /**
4583
+ * Fetch the record counts for the filters and views that asked for
4584
+ * one. Filters that narrow the same single field share one grouped
4585
+ * request. A failed count leaves that entry without a number.
4586
+ *
4587
+ * @return {Promise<void>}
4588
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
4589
+ */
4590
+ async loadFilterCounts() {
4591
+ const requestKey = this.countRequestKey
4592
+ const schema = this.exportSchemaSlug
4593
+ if (requestKey === '' || !this.register || !schema) {
4594
+ this.tabCounts = null
4595
+ this.savedViewCounts = null
4596
+ return
4597
+ }
4598
+ const params = (this.$route && this.$route.params) || {}
4599
+ const ctx = typeof this.selfFetchTokenCtx === 'function' ? this.selfFetchTokenCtx() : {}
4600
+ const counts = await fetchFilterCounts({
4601
+ register: this.register,
4602
+ schema,
4603
+ entries: this.countEntries.map((entry) => ({ key: entry.key, filter: resolveFilterMap(entry.filter, params, ctx) })),
4604
+ baseFilter: resolveFilterMap(this.filter, params, ctx),
4605
+ ctx,
4606
+ })
4607
+ if (requestKey !== this.countRequestKey) {
4608
+ return
4609
+ }
4610
+ const tabCounts = {}
4611
+ const viewCounts = {}
4612
+ for (const [key, value] of Object.entries(counts)) {
4613
+ if (key.startsWith('tab:')) {
4614
+ tabCounts[Number(key.slice(4))] = value
4615
+ } else {
4616
+ viewCounts[key.slice(5)] = value
4617
+ }
4618
+ }
4619
+ this.tabCounts = tabCounts
4620
+ this.savedViewCounts = viewCounts
4621
+ },
4622
+
4491
4623
  // ── Split view (case-page-and-list-as-a-place) ──────────────────
4492
4624
 
4493
4625
  /**
@@ -0,0 +1,40 @@
1
+ A step with one thing done and two to go:
2
+
3
+ ```vue
4
+ <CnNextStepCard
5
+ title="What now? Step 2: handling"
6
+ :items="[
7
+ { label: 'Receipt confirmed to the resident', done: true },
8
+ { label: 'Review the 3 documents that are still open', hint: '2 of 5 done' },
9
+ { label: 'Draft the decision' },
10
+ ]"
11
+ action-label="Continue reviewing"
12
+ after="Then: step 3, decision" />
13
+ ```
14
+
15
+ A checklist only, with no button:
16
+
17
+ ```vue
18
+ <CnNextStepCard
19
+ :items="[
20
+ { label: 'Inform the resident', done: true },
21
+ { label: 'Archive the case', done: true },
22
+ ]" />
23
+ ```
24
+
25
+ As a page section, with the button waiting for its request. `title-tag` sets the heading level, `action-id` gives the button an id a skip link can target, and `done-label` and `todo-label` are the states a screen reader hears:
26
+
27
+ ```vue
28
+ <CnNextStepCard
29
+ title="What now?"
30
+ title-tag="h2"
31
+ action-label="Publish"
32
+ action-id="case-primary-action"
33
+ :action-disabled="true"
34
+ done-label="Klaar"
35
+ todo-label="Nog doen"
36
+ :items="[
37
+ { label: 'Check what can be made public', done: true },
38
+ { label: 'Create the publication' },
39
+ ]" />
40
+ ```