@conduction/nextcloud-vue 2.78.0 → 2.80.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 (259) hide show
  1. package/dist/bundled-packages.json +0 -2
  2. package/dist/esm/bundled-packages.json +0 -2
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
  5. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  6. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
  7. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  8. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +115 -104
  9. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  13. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +39 -3
  14. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  15. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +158 -0
  17. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  18. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +157 -96
  19. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  20. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +139 -1
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +77 -0
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +5 -2
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +62 -4
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +85 -1
  32. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  33. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
  34. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  35. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
  36. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
  37. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +736 -0
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
  40. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
  41. package/dist/esm/components/CnFactsWidget/index.js +30 -0
  42. package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
  43. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
  44. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
  45. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +551 -0
  46. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
  47. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +183 -0
  48. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
  49. package/dist/esm/components/CnFieldListWidget/index.js +29 -0
  50. package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
  51. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
  52. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
  54. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  55. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
  56. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  57. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
  58. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
  59. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +66 -18
  60. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  61. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
  62. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  63. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +18 -3
  64. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  65. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  66. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +330 -74
  67. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  68. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +34 -15
  69. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  70. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
  71. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
  72. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
  73. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
  74. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
  75. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
  76. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
  77. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
  79. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  80. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
  81. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  82. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  83. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
  84. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  85. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  86. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
  87. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
  88. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +387 -0
  89. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
  90. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +169 -0
  91. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
  92. package/dist/esm/components/CnRecordListWidget/index.js +31 -0
  93. package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
  94. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  95. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
  96. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  97. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
  98. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
  99. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
  100. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
  101. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
  102. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
  103. package/dist/esm/components/CnSectionsWidget/index.js +35 -0
  104. package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
  105. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
  106. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  108. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  109. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
  110. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  111. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +152 -8
  112. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  113. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +84 -23
  114. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  115. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
  116. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  117. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
  118. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  119. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  120. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +106 -2
  121. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  122. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +105 -53
  123. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  124. package/dist/esm/composables/useAppManifest.js +8 -3
  125. package/dist/esm/composables/useAppManifest.js.map +1 -1
  126. package/dist/esm/composables/useFormDraft.js +140 -9
  127. package/dist/esm/composables/useFormDraft.js.map +1 -1
  128. package/dist/esm/index.js +5 -0
  129. package/dist/esm/index.js.map +1 -1
  130. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
  131. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  132. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
  133. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  134. package/dist/esm/integrations/builtin/field-inspection.js +21 -0
  135. package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
  136. package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
  137. package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
  138. package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
  139. package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
  140. package/dist/esm/integrations/offline/index.js +2 -1
  141. package/dist/esm/integrations/offline/index.js.map +1 -1
  142. package/dist/esm/integrations/offline/offlineDb.js +6 -1
  143. package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
  144. package/dist/esm/integrations/offline/syncReplayService.js +8 -2
  145. package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
  146. package/dist/esm/l10n/en.json.js +72 -1
  147. package/dist/esm/l10n/en.json.js.map +1 -1
  148. package/dist/esm/l10n/nl.json.js +120 -2
  149. package/dist/esm/l10n/nl.json.js.map +1 -1
  150. package/dist/esm/nextcloud-vue.css +1314 -74
  151. package/dist/esm/utils/boardDate.js +193 -0
  152. package/dist/esm/utils/boardDate.js.map +1 -0
  153. package/dist/esm/utils/detailActionModel.js +60 -3
  154. package/dist/esm/utils/detailActionModel.js.map +1 -1
  155. package/dist/esm/utils/dynamicProperties.js +5 -1
  156. package/dist/esm/utils/dynamicProperties.js.map +1 -1
  157. package/dist/esm/utils/fillTemplate.js +114 -0
  158. package/dist/esm/utils/fillTemplate.js.map +1 -0
  159. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  160. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  161. package/dist/esm/utils/queryPresets.js +96 -0
  162. package/dist/esm/utils/queryPresets.js.map +1 -0
  163. package/dist/esm/utils/resolveFilterTokens.js +11 -0
  164. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  165. package/dist/esm/utils/schema.js +21 -0
  166. package/dist/esm/utils/schema.js.map +1 -1
  167. package/dist/esm/utils/sentinelTokens.js +3 -3
  168. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  169. package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
  170. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  171. package/dist/esm/utils/widgetDispatch.js +7 -1
  172. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  173. package/dist/nextcloud-vue.cjs.js +14278 -8169
  174. package/dist/nextcloud-vue.cjs.js.map +2 -2
  175. package/dist/nextcloud-vue.css +1314 -74
  176. package/l10n/en.json +72 -1
  177. package/l10n/nl.json +120 -2
  178. package/package.json +1 -1
  179. package/scripts/build-validators.js +26 -0
  180. package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
  181. package/src/components/CnActionsBar/CnActionsBar.md +4 -0
  182. package/src/components/CnActionsBar/CnActionsBar.vue +83 -42
  183. package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
  184. package/src/components/CnBannerWidget/CnBannerWidget.vue +39 -3
  185. package/src/components/CnCellRenderer/CnCellRenderer.vue +306 -2
  186. package/src/components/CnDashboardPage/CnDashboardPage.vue +219 -4
  187. package/src/components/CnDataTable/CnDataTable.md +2 -0
  188. package/src/components/CnDataTable/CnDataTable.vue +82 -2
  189. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  190. package/src/components/CnDetailPage/CnDetailPage.vue +63 -3
  191. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +127 -1
  192. package/src/components/CnFactsWidget/CnFactsWidget.vue +1050 -0
  193. package/src/components/CnFactsWidget/index.js +29 -0
  194. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +746 -0
  195. package/src/components/CnFieldListWidget/index.js +28 -0
  196. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
  197. package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
  198. package/src/components/CnFilesBrowser/index.js +1 -0
  199. package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
  200. package/src/components/CnFormDialog/CnFormDialog.md +9 -3
  201. package/src/components/CnFormDialog/CnFormDialog.vue +70 -22
  202. package/src/components/CnFormPage/CnFormPage.vue +18 -3
  203. package/src/components/CnIndexPage/CnIndexPage.vue +281 -9
  204. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
  205. package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
  206. package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
  207. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
  208. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +603 -0
  209. package/src/components/CnRecordListWidget/index.js +30 -0
  210. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
  211. package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
  212. package/src/components/CnSectionsWidget/index.js +34 -0
  213. package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
  214. package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
  215. package/src/components/CnTabsWidget/CnTabsWidget.vue +214 -9
  216. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
  217. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
  218. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +188 -4
  219. package/src/components/index.js +5 -1
  220. package/src/composables/__tests__/useFormDraft.spec.js +69 -0
  221. package/src/composables/useAppManifest.js +8 -3
  222. package/src/composables/useFormDraft.js +139 -8
  223. package/src/css/form-field.css +14 -0
  224. package/src/css/index-page.css +71 -0
  225. package/src/css/kpi-card.css +32 -0
  226. package/src/css/look-board-detail.css +87 -0
  227. package/src/css/look-board-index.css +143 -14
  228. package/src/css/look-board.css +114 -2
  229. package/src/css/table.css +56 -0
  230. package/src/index.js +8 -0
  231. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
  232. package/src/integrations/builtin/field-inspection.js +21 -0
  233. package/src/integrations/offline/checklistSubmission.js +137 -0
  234. package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
  235. package/src/integrations/offline/index.js +3 -0
  236. package/src/integrations/offline/offlineDb.js +6 -1
  237. package/src/integrations/offline/syncReplayService.js +8 -2
  238. package/src/schemas/app-manifest-v2.schema.json +149 -5
  239. package/src/utils/boardDate.js +189 -0
  240. package/src/utils/detailActionModel.js +59 -2
  241. package/src/utils/dynamicProperties.js +5 -1
  242. package/src/utils/fillTemplate.js +110 -0
  243. package/src/utils/libraryWidgetKeys.js +4 -0
  244. package/src/utils/queryPresets.js +93 -0
  245. package/src/utils/resolveFilterTokens.js +11 -0
  246. package/src/utils/schema.js +21 -0
  247. package/src/utils/sentinelTokens.js +3 -3
  248. package/src/utils/validateManifestV2.compiled.js +57 -2
  249. package/src/utils/widgetDispatch.js +7 -1
  250. package/dist/esm/_virtual/equal.js +0 -4
  251. package/dist/esm/_virtual/equal.js.map +0 -1
  252. package/dist/esm/_virtual/ucs2length.js +0 -4
  253. package/dist/esm/_virtual/ucs2length.js.map +0 -1
  254. package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
  255. package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
  256. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
  257. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
  258. package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
  259. package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
@@ -0,0 +1,114 @@
1
+ import { formatBoardDate, boardDateLocale } from './boardDate.js';
2
+ import { parseDateValue } from './dateVariant.js';
3
+ import { readPath } from './readPath.js';
4
+
5
+ // SPDX-FileCopyrightText: 2026 Conduction B.V.
6
+ // SPDX-License-Identifier: EUPL-1.2
7
+
8
+
9
+ const TOKEN = /\{([^{}\s|]+)(?:\|([a-z]+))?\}/g;
10
+
11
+ /**
12
+ * A moment in the board's short form with the time: "4 okt, 10:52", or
13
+ * "14 feb 2024, 09:00" outside the current year.
14
+ *
15
+ * @param {unknown} value A Date, a timestamp or an ISO string.
16
+ * @param {Date} [now] The reference moment.
17
+ * @return {string} The text, or '' when the value is not a date.
18
+ * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
19
+ */
20
+ function formatBoardDateTime(value, now = new Date()) {
21
+ const day = formatBoardDate(value, now);
22
+ const date = parseDateValue(value);
23
+ if (day === '' || date === null) {
24
+ return ''
25
+ }
26
+ let time;
27
+ try {
28
+ time = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date);
29
+ } catch {
30
+ time = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date);
31
+ }
32
+ return `${day}, ${time}`
33
+ }
34
+
35
+ /**
36
+ * Format one token value.
37
+ *
38
+ * @param {unknown} value The raw value.
39
+ * @param {string} format `date`, `datetime` or '' (as is).
40
+ * @param {Date} now The reference moment.
41
+ * @return {string} The text, '' when nothing can be shown.
42
+ */
43
+ function formatValue(value, format, now) {
44
+ if (value === null || value === undefined || value === '' || typeof value === 'object') {
45
+ return ''
46
+ }
47
+ if (format === 'date') {
48
+ return formatBoardDate(value, now)
49
+ }
50
+ if (format === 'datetime') {
51
+ return formatBoardDateTime(value, now)
52
+ }
53
+ return String(value)
54
+ }
55
+
56
+ /**
57
+ * Fill a row template such as `"{identifier} · {caseTypeName}"` or
58
+ * `"Termijn {deadline|date}"` from an object.
59
+ *
60
+ * Every `{path}` token is a dot path into the object; `{path|date}` and
61
+ * `{path|datetime}` format it as the boards write dates. A token without a
62
+ * value empties the WHOLE template, because "Termijn " is worse than nothing.
63
+ * A template without tokens is returned as written (a constant label).
64
+ *
65
+ * @param {string} template The template, already translated.
66
+ * @param {object|null} object The object to read.
67
+ * @param {Date} [now] The reference moment for dates.
68
+ * @return {string} The filled text, or ''.
69
+ * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
70
+ */
71
+ function fillTemplate(template, object, now = new Date()) {
72
+ if (typeof template !== 'string' || template.trim() === '') {
73
+ return ''
74
+ }
75
+ let missing = false;
76
+ const text = template.replace(TOKEN, (_match, path, format) => {
77
+ const value = object && typeof object === 'object' ? readPath(object, path) : undefined;
78
+ const shown = formatValue(value, format || '', now);
79
+ if (shown === '') {
80
+ missing = true;
81
+ }
82
+ return shown
83
+ });
84
+ return missing ? '' : text.trim()
85
+ }
86
+
87
+ /**
88
+ * The first of several templates that fills: `["Klaar {closedAt|date}",
89
+ * "Termijn {deadline|date}"]` reads "Klaar ..." once the case is closed and
90
+ * "Termijn ..." while it is open. A single string is one candidate.
91
+ *
92
+ * @param {string|string[]} templates The candidates, in order.
93
+ * @param {object|null} object The object to read.
94
+ * @param {(key: string) => string} [translate] Applied to each template before filling.
95
+ * @param {Date} [now] The reference moment for dates.
96
+ * @return {string} The first filled text, or ''.
97
+ * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
98
+ */
99
+ function firstFilled(templates, object, translate = (key) => key, now = new Date()) {
100
+ const list = Array.isArray(templates) ? templates : [templates];
101
+ for (const template of list) {
102
+ if (typeof template !== 'string') {
103
+ continue
104
+ }
105
+ const text = fillTemplate(translate(template), object, now);
106
+ if (text !== '') {
107
+ return text
108
+ }
109
+ }
110
+ return ''
111
+ }
112
+
113
+ export { fillTemplate, firstFilled, formatBoardDateTime };
114
+ //# sourceMappingURL=fillTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fillTemplate.js","sources":["../../../src/utils/fillTemplate.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { boardDateLocale, formatBoardDate } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { readPath } from './readPath.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([a-z]+))?\\}/g\n\n/**\n * A moment in the board's short form with the time: \"4 okt, 10:52\", or\n * \"14 feb 2024, 09:00\" outside the current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function formatBoardDateTime(value, now = new Date()) {\n\tconst day = formatBoardDate(value, now)\n\tconst date = parseDateValue(value)\n\tif (day === '' || date === null) {\n\t\treturn ''\n\t}\n\tlet time\n\ttry {\n\t\ttime = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date)\n\t} catch {\n\t\ttime = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date)\n\t}\n\treturn `${day}, ${time}`\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime` or '' (as is).\n * @param {Date} now The reference moment.\n * @return {string} The text, '' when nothing can be shown.\n */\nfunction formatValue(value, format, now) {\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format === 'date') {\n\t\treturn formatBoardDate(value, now)\n\t}\n\tif (format === 'datetime') {\n\t\treturn formatBoardDateTime(value, now)\n\t}\n\treturn String(value)\n}\n\n/**\n * Fill a row template such as `\"{identifier} · {caseTypeName}\"` or\n * `\"Termijn {deadline|date}\"` from an object.\n *\n * Every `{path}` token is a dot path into the object; `{path|date}` and\n * `{path|datetime}` format it as the boards write dates. A token without a\n * value empties the WHOLE template, because \"Termijn \" is worse than nothing.\n * A template without tokens is returned as written (a constant label).\n *\n * @param {string} template The template, already translated.\n * @param {object|null} object The object to read.\n * @param {Date} [now] The reference moment for dates.\n * @return {string} The filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function fillTemplate(template, object, now = new Date()) {\n\tif (typeof template !== 'string' || template.trim() === '') {\n\t\treturn ''\n\t}\n\tlet missing = false\n\tconst text = template.replace(TOKEN, (_match, path, format) => {\n\t\tconst value = object && typeof object === 'object' ? readPath(object, path) : undefined\n\t\tconst shown = formatValue(value, format || '', now)\n\t\tif (shown === '') {\n\t\t\tmissing = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn missing ? '' : text.trim()\n}\n\n/**\n * The first of several templates that fills: `[\"Klaar {closedAt|date}\",\n * \"Termijn {deadline|date}\"]` reads \"Klaar ...\" once the case is closed and\n * \"Termijn ...\" while it is open. A single string is one candidate.\n *\n * @param {string|string[]} templates The candidates, in order.\n * @param {object|null} object The object to read.\n * @param {(key: string) => string} [translate] Applied to each template before filling.\n * @param {Date} [now] The reference moment for dates.\n * @return {string} The first filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function firstFilled(templates, object, translate = (key) => key, now = new Date()) {\n\tconst list = Array.isArray(templates) ? templates : [templates]\n\tfor (const template of list) {\n\t\tif (typeof template !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst text = fillTemplate(translate(template), object, now)\n\t\tif (text !== '') {\n\t\t\treturn text\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":[],"mappings":"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"}
@@ -101,6 +101,8 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
101
101
  'delta',
102
102
  'divider',
103
103
  'document-review',
104
+ 'facts',
105
+ 'field-list',
104
106
  'files',
105
107
  'flow-runs',
106
108
  'gauge',
@@ -124,8 +126,10 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
124
126
  'object-table',
125
127
  'people',
126
128
  'quicklinks',
129
+ 'record-list',
127
130
  'related',
128
131
  'saved-view',
132
+ 'sections',
129
133
  'spend-analytics',
130
134
  'stacked-bar',
131
135
  'stages',
@@ -1 +1 @@
1
- {"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'divider',\n\t'document-review',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'people',\n\t'quicklinks',\n\t'related',\n\t'saved-view',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'video',\n\t'week-strip',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;AAClD,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,SAAS;AACV,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,SAAS;AACV,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;AAC3D,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,cAAc;AACf,CAAC,WAAW;AACZ,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,SAAS;AACV,CAAC,iBAAiB;AAClB,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,kBAAkB;AACnB,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,KAAK;AACN,CAAC,UAAU;AACX,CAAC,MAAM;AACP,CAAC,WAAW;AACZ,CAAC,WAAW;AACZ,CAAC,SAAS;AACV;AACA;AACA,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,QAAQ;AACT,CAAC,YAAY;AACb,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,iBAAiB;AAClB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,aAAa;AACd,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE;AAC7B,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AACvE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;AAC9C,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAC;AAClB,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAC;AACtB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;AACjD,CAAC,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC;AAC1B,EAAE,GAAG,oBAAoB;AACzB,EAAE,GAAG,6BAA6B;AAClC,EAAE,CAAC,CAAC;AACJ,CAAC;;;;"}
1
+ {"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'divider',\n\t'document-review',\n\t'facts',\n\t'field-list',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'people',\n\t'quicklinks',\n\t'record-list',\n\t'related',\n\t'saved-view',\n\t'sections',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'video',\n\t'week-strip',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;AAClD,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,SAAS;AACV,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,SAAS;AACV,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;AAC3D,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,cAAc;AACf,CAAC,WAAW;AACZ,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,SAAS;AACV,CAAC,iBAAiB;AAClB,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,kBAAkB;AACnB,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,KAAK;AACN,CAAC,UAAU;AACX,CAAC,MAAM;AACP,CAAC,WAAW;AACZ,CAAC,WAAW;AACZ,CAAC,SAAS;AACV;AACA;AACA,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,QAAQ;AACT,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,UAAU;AACX,CAAC,iBAAiB;AAClB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,aAAa;AACd,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE;AAC7B,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AACvE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;AAC9C,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAC;AAClB,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAC;AACtB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;AACjD,CAAC,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC;AAC1B,EAAE,GAAG,oBAAoB;AACzB,EAAE,GAAG,6BAA6B;AAClC,EAAE,CAAC,CAAC;AACJ,CAAC;;;;"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * Query presets of an index page (`config.queryPresets`): a menu entry that
6
+ * deep-links to a list with a query (`{ caseType: '<uuid>' }`, the shape hydra
7
+ * gate-68 asks for instead of a second page) can bring its own lenses,
8
+ * columns and copy. The page names, per preset, the query it answers to and
9
+ * the keys it replaces; the first preset whose every `match` pair is in the
10
+ * current route query wins.
11
+ *
12
+ * @module utils/queryPresets
13
+ * @spec openspec/changes/screens-index-query-presets/specs/index-query-presets/spec.md#requirement-a-query-preset-overlays-lenses-columns-and-copy
14
+ */
15
+
16
+ /**
17
+ * The config keys a preset may replace. Anything else in a preset is ignored:
18
+ * a preset changes what the list shows, never which register or schema it
19
+ * reads or what it may write.
20
+ *
21
+ * @type {ReadonlyArray<string>}
22
+ */
23
+ const QUERY_PRESET_KEYS = Object.freeze([
24
+ 'title',
25
+ 'quickFilters',
26
+ 'columns',
27
+ 'cardFields',
28
+ 'countText',
29
+ 'searchPlaceholder',
30
+ 'footerNote',
31
+ 'defaultSort',
32
+ 'viewSwitch',
33
+ ]);
34
+
35
+ /**
36
+ * Whether one route query value satisfies one `match` value. A repeated query
37
+ * parameter (an array) matches when it holds the value.
38
+ *
39
+ * @param {unknown} actual The route query value.
40
+ * @param {string|number|boolean} expected The preset's value.
41
+ * @return {boolean} True when they match.
42
+ */
43
+ function queryValueMatches(actual, expected) {
44
+ const want = String(expected);
45
+ if (Array.isArray(actual)) {
46
+ return actual.some((v) => String(v) === want)
47
+ }
48
+ return actual !== undefined && actual !== null && String(actual) === want
49
+ }
50
+
51
+ /**
52
+ * The index of the first preset whose every `match` pair is in the query.
53
+ * A preset without a non-empty `match` never matches.
54
+ *
55
+ * @param {Array<object>} presets The page's `queryPresets`.
56
+ * @param {object} query The current route query.
57
+ * @return {number} The preset index, or -1.
58
+ */
59
+ function matchQueryPreset(presets, query) {
60
+ if (!Array.isArray(presets)) {
61
+ return -1
62
+ }
63
+ const q = query && typeof query === 'object' ? query : {};
64
+ return presets.findIndex((preset) => {
65
+ const match = preset && typeof preset.match === 'object' && preset.match !== null ? preset.match : null;
66
+ if (!match || Object.keys(match).length === 0) {
67
+ return false
68
+ }
69
+ return Object.entries(match).every(([key, value]) => queryValueMatches(q[key], value))
70
+ })
71
+ }
72
+
73
+ /**
74
+ * The page config with the preset's allowed keys laid over it, and without
75
+ * `queryPresets` itself (the page component has no such prop).
76
+ *
77
+ * @param {object} config The page config.
78
+ * @param {object|null} preset The matched preset, or null.
79
+ * @return {object} The effective config.
80
+ */
81
+ function applyQueryPreset(config, preset) {
82
+ const { queryPresets, ...rest } = config || {};
83
+ if (!preset || typeof preset !== 'object') {
84
+ return rest
85
+ }
86
+ const out = { ...rest };
87
+ for (const key of QUERY_PRESET_KEYS) {
88
+ if (key !== 'title' && preset[key] !== undefined) {
89
+ out[key] = preset[key];
90
+ }
91
+ }
92
+ return out
93
+ }
94
+
95
+ export { QUERY_PRESET_KEYS, applyQueryPreset, matchQueryPreset };
96
+ //# sourceMappingURL=queryPresets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryPresets.js","sources":["../../../src/utils/queryPresets.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Query presets of an index page (`config.queryPresets`): a menu entry that\n * deep-links to a list with a query (`{ caseType: '<uuid>' }`, the shape hydra\n * gate-68 asks for instead of a second page) can bring its own lenses,\n * columns and copy. The page names, per preset, the query it answers to and\n * the keys it replaces; the first preset whose every `match` pair is in the\n * current route query wins.\n *\n * @module utils/queryPresets\n * @spec openspec/changes/screens-index-query-presets/specs/index-query-presets/spec.md#requirement-a-query-preset-overlays-lenses-columns-and-copy\n */\n\n/**\n * The config keys a preset may replace. Anything else in a preset is ignored:\n * a preset changes what the list shows, never which register or schema it\n * reads or what it may write.\n *\n * @type {ReadonlyArray<string>}\n */\nexport const QUERY_PRESET_KEYS = Object.freeze([\n\t'title',\n\t'quickFilters',\n\t'columns',\n\t'cardFields',\n\t'countText',\n\t'searchPlaceholder',\n\t'footerNote',\n\t'defaultSort',\n\t'viewSwitch',\n])\n\n/**\n * Whether one route query value satisfies one `match` value. A repeated query\n * parameter (an array) matches when it holds the value.\n *\n * @param {unknown} actual The route query value.\n * @param {string|number|boolean} expected The preset's value.\n * @return {boolean} True when they match.\n */\nfunction queryValueMatches(actual, expected) {\n\tconst want = String(expected)\n\tif (Array.isArray(actual)) {\n\t\treturn actual.some((v) => String(v) === want)\n\t}\n\treturn actual !== undefined && actual !== null && String(actual) === want\n}\n\n/**\n * The index of the first preset whose every `match` pair is in the query.\n * A preset without a non-empty `match` never matches.\n *\n * @param {Array<object>} presets The page's `queryPresets`.\n * @param {object} query The current route query.\n * @return {number} The preset index, or -1.\n */\nexport function matchQueryPreset(presets, query) {\n\tif (!Array.isArray(presets)) {\n\t\treturn -1\n\t}\n\tconst q = query && typeof query === 'object' ? query : {}\n\treturn presets.findIndex((preset) => {\n\t\tconst match = preset && typeof preset.match === 'object' && preset.match !== null ? preset.match : null\n\t\tif (!match || Object.keys(match).length === 0) {\n\t\t\treturn false\n\t\t}\n\t\treturn Object.entries(match).every(([key, value]) => queryValueMatches(q[key], value))\n\t})\n}\n\n/**\n * The page config with the preset's allowed keys laid over it, and without\n * `queryPresets` itself (the page component has no such prop).\n *\n * @param {object} config The page config.\n * @param {object|null} preset The matched preset, or null.\n * @return {object} The effective config.\n */\nexport function applyQueryPreset(config, preset) {\n\tconst { queryPresets, ...rest } = config || {}\n\tif (!preset || typeof preset !== 'object') {\n\t\treturn rest\n\t}\n\tconst out = { ...rest }\n\tfor (const key of QUERY_PRESET_KEYS) {\n\t\tif (key !== 'title' && preset[key] !== undefined) {\n\t\t\tout[key] = preset[key]\n\t\t}\n\t}\n\treturn out\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;AAC/C,CAAC,OAAO;AACR,CAAC,cAAc;AACf,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,mBAAmB;AACpB,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,YAAY;AACb,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE;AAC7C,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAC;AAC9B,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AAC5B,EAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAC/C,EAAE;AACF,CAAC,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI;AAC1E,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE;AACjD,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC9B,EAAE,OAAO,CAAC,CAAC;AACX,EAAE;AACF,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,GAAE;AAC1D,CAAC,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK;AACtC,EAAE,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,KAAI;AACzG,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;AACjD,GAAG,OAAO,KAAK;AACf,GAAG;AACH,EAAE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;AACxF,EAAE,CAAC;AACH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE;AACjD,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,IAAI,GAAE;AAC/C,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AAC5C,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,GAAE;AACxB,CAAC,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE;AACtC,EAAE,IAAI,GAAG,KAAK,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;AACpD,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,EAAC;AACzB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,GAAG;AACX;;;;"}
@@ -26,6 +26,10 @@ import { peekCurrentUserGroups } from './widgetVisibility.js';
26
26
  * - `@today±Nd` → relative-date arithmetic at day granularity: N days
27
27
  * from today (e.g. `@today-30d`, `@today+7d`)
28
28
  * - `@monthStart` → first day of the current month, `YYYY-MM-DD`
29
+ * - `@monthEnd` → last day of the current month, `YYYY-MM-DD`; on a
30
+ * date-time field use `lt: '@nextMonthStart'` instead,
31
+ * since `lte` a date stops at that day's midnight
32
+ * - `@nextMonthStart` → first day of the next month, `YYYY-MM-DD`
29
33
  * - `@quarterStart` → first day of the current quarter, `YYYY-MM-DD`
30
34
  * - `@yearStart` → first day of the current year, `YYYY-MM-DD`
31
35
  * - `@currentFiscalYear` → the current calendar year as a number string, e.g. `2026`
@@ -215,6 +219,13 @@ function resolveFilterValue(v, ctx) {
215
219
  if (v === '@monthStart') {
216
220
  return ymd(new Date(now.getFullYear(), now.getMonth(), 1))
217
221
  }
222
+ if (v === '@monthEnd') {
223
+ // Day 0 of the next month is the last day of this one.
224
+ return ymd(new Date(now.getFullYear(), now.getMonth() + 1, 0))
225
+ }
226
+ if (v === '@nextMonthStart') {
227
+ return ymd(new Date(now.getFullYear(), now.getMonth() + 1, 1))
228
+ }
218
229
  if (v === '@quarterStart') {
219
230
  return ymd(new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3, 1))
220
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolveFilterTokens.js","sources":["../../../src/utils/resolveFilterTokens.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Resolve dynamic `@`-tokens inside a widget filter at FETCH time, so manifest\n * filters can express \"relative to now / the current user\" without baking a\n * fixed value. The widgets call this just before sending the filter to\n * OpenRegister.\n *\n * Supported tokens:\n * - `@me` → the current Nextcloud user id (`getCurrentUser()` from\n * `@nextcloud/auth`; `window.OC.currentUser` fallback)\n * - `@myGroups` → the ids of the Nextcloud groups the current user is\n * in, as an ARRAY (an IN filter). `ctx.myGroups` wins;\n * otherwise a reactive peek over `getCurrentUserGroups`.\n * Stays UNRESOLVED while loading and when the user is\n * in no group, so the caller waits instead of sending\n * an empty IN list (which OpenRegister reads as \"no\n * constraint\"). Spread in place inside an IN-list array.\n * - `@now` → the current instant, ISO-8601\n * - `@today` → today at 00:00, `YYYY-MM-DD` (date-prefix comparable)\n * - `@today±Nd` → relative-date arithmetic at day granularity: N days\n * from today (e.g. `@today-30d`, `@today+7d`)\n * - `@monthStart` → first day of the current month, `YYYY-MM-DD`\n * - `@quarterStart` → first day of the current quarter, `YYYY-MM-DD`\n * - `@yearStart` → first day of the current year, `YYYY-MM-DD`\n * - `@currentFiscalYear` → the current calendar year as a number string, e.g. `2026`\n * - `@range.from` / `@range.to` / `@range.preset` → the active date range a\n * widget passes in `ctx.range` (CnStatWidget: its own\n * picker, else the dashboard's). A trailing `?` marks it\n * optional, as for `@workspace.<key>?`.\n * - `@objectId` → the current detail-page object's id (needs `ctx`)\n * - `@object.<field>` → a field off the current detail-page object (needs `ctx`)\n * - `@workspace.<key>` → a value off the page-level workspace context (needs `ctx.workspace`)\n * - `@config.<key>` → a value off the page-level app config (needs `ctx.config`)\n *\n * `@objectId` / `@object.<field>` are OBJECT-CONTEXT tokens: they resolve only\n * when a `ctx` `{ objectId, object }` is supplied (a detail page provides it via\n * the `cnObjectContext` inject). `@workspace.<key>` is a WORKSPACE-CONTEXT token:\n * it resolves a key off `ctx.workspace` (the reactive `cnWorkspaceContext` a\n * dashboard/workspace page provides) so a list widget can react to page-level\n * state another widget writes (e.g. a selected client). Without the matching\n * context the tokens pass through unchanged — and an UNRESOLVED `@workspace.*`\n * token signals \"no selection yet\" to the caller (see {@link hasUnresolvedTokens}).\n *\n * `@config.<key>` is an APP-CONFIG token: it resolves a key off `ctx.config` (the\n * page-level app config a dashboard/detail page provides via `cnAppConfig` — e.g.\n * a reporting `currency` captured by the setup wizard). Like `@workspace.<key>`, a\n * trailing `?` marks it optional. An UNRESOLVED required `@config.<key>` passes\n * through unchanged; this lets `format: { currency: '@config.currency' }` fall\n * back to a literal default when the config is unset.\n *\n * IN-lists: a filter value MAY be an ARRAY (`{ status: ['open', 'pending'] }`,\n * or the explicit operator form `{ status: { in: [...] } }`). Tokens inside the\n * array are resolved item-by-item (`{ assignee: ['@me', 'shared'] }` works).\n * OpenRegister expects an IN-list as repeated bracket params\n * (`status[]=open&status[]=pending` — the same shape `buildQueryString` /\n * `useObjectStore` send), so widget fetchers serialize a resolved array as\n * repeated `field[]` params, never as a JSON blob or a single joined string.\n *\n * @module utils/resolveFilterTokens\n */\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { TODAY_DELTA_RE } from './sentinelTokens.js'\nimport { peekCurrentUserGroups } from './widgetVisibility.js'\n\n/** The filter token for the current user's group ids. */\nexport const MY_GROUPS_TOKEN = '@myGroups'\n\n/**\n * Resolve `@myGroups`: the current user's group ids, or the token itself while\n * they are loading or when there are none (an empty IN list would match every\n * row, so \"no team\" must mean \"wait\", never \"everything\").\n *\n * @spec openspec/changes/nextcloud-group-surfaces/specs/schema-utilities/spec.md#requirement-mygroups-resolves-to-the-current-users-group-ids\n * @param {{myGroups?: string[]}} [ctx] Optional context; `ctx.myGroups` wins.\n * @return {string[]|string} The group ids, or `'@myGroups'` when unresolved.\n */\nfunction resolveMyGroups(ctx) {\n\tlet groups = ctx && Array.isArray(ctx.myGroups) ? ctx.myGroups : null\n\tif (groups === null) {\n\t\ttry {\n\t\t\tgroups = peekCurrentUserGroups()\n\t\t} catch {\n\t\t\tgroups = null\n\t\t}\n\t}\n\tconst ids = Array.isArray(groups)\n\t\t? groups.filter((g) => typeof g === 'string' && g !== '')\n\t\t: []\n\treturn ids.length > 0 ? [...ids] : MY_GROUPS_TOKEN\n}\n\n/**\n * Format a Date as `YYYY-MM-DD` (local).\n *\n * @param {Date} d The date.\n * @return {string} The date-only string.\n */\nfunction ymd(d) {\n\tconst m = String(d.getMonth() + 1).padStart(2, '0')\n\tconst day = String(d.getDate()).padStart(2, '0')\n\treturn `${d.getFullYear()}-${m}-${day}`\n}\n\n/**\n * Resolve a single filter value if it is a dynamic `@`-token, else pass through.\n *\n * @param {unknown} v The candidate value.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object, range?: object, myGroups?: string[]}} [ctx] Optional\n * context for `@objectId` / `@object.<field>` (detail page),\n * `@workspace.<key>` (page-level workspace state), and `@config.<key>`\n * (page-level app config) tokens.\n * @return {unknown} The resolved value.\n */\nexport function resolveFilterValue(v, ctx) {\n\tif (typeof v !== 'string' || v.charAt(0) !== '@') {\n\t\treturn v\n\t}\n\tconst now = new Date()\n\tif (v === MY_GROUPS_TOKEN) {\n\t\treturn resolveMyGroups(ctx)\n\t}\n\tif (v === '@objectId') {\n\t\treturn (ctx && ctx.objectId !== undefined && ctx.objectId !== null) ? String(ctx.objectId) : v\n\t}\n\tif (v.startsWith('@object.')) {\n\t\tconst field = v.slice('@object.'.length)\n\t\tif (ctx && ctx.object && field && ctx.object[field] !== undefined) {\n\t\t\treturn ctx.object[field]\n\t\t}\n\t\treturn v\n\t}\n\tif (v.startsWith('@workspace.')) {\n\t\t// A trailing `?` marks the token OPTIONAL: when unset the caller drops the\n\t\t// filter key (show all) instead of waiting (see hasUnresolvedTokens /\n\t\t// CnObjectListWidget). The `?` is stripped from the key lookup.\n\t\tconst raw = v.slice('@workspace.'.length)\n\t\tconst key = raw.endsWith('?') ? raw.slice(0, -1) : raw\n\t\tif (ctx && ctx.workspace && key && ctx.workspace[key] !== undefined\n\t\t\t&& ctx.workspace[key] !== null && ctx.workspace[key] !== '') {\n\t\t\treturn ctx.workspace[key]\n\t\t}\n\t\treturn v\n\t}\n\tif (v.startsWith('@config.')) {\n\t\t// A trailing `?` marks the token OPTIONAL (same convention as\n\t\t// `@workspace.<key>?`): an unset value lets the caller drop the key. A\n\t\t// required `@config.<key>` that is unset passes through unchanged so a\n\t\t// downstream consumer can fall back to a literal default (e.g.\n\t\t// `format.currency` → `'EUR'`).\n\t\tconst raw = v.slice('@config.'.length)\n\t\tconst key = raw.endsWith('?') ? raw.slice(0, -1) : raw\n\t\tif (ctx && ctx.config && key && ctx.config[key] !== undefined\n\t\t\t&& ctx.config[key] !== null && ctx.config[key] !== '') {\n\t\t\treturn ctx.config[key]\n\t\t}\n\t\treturn v\n\t}\n\tif (v.startsWith('@range.')) {\n\t\t// `@range.from` / `@range.to` / `@range.preset`: the active date range the\n\t\t// caller put in `ctx.range`. CnStatWidget documented these tokens and put\n\t\t// `range` in its endpoint context, but nothing resolved them, so the\n\t\t// literal token stayed in the params, hasUnresolvedTokens blocked the\n\t\t// request, and a card that used its own period picker never loaded. A\n\t\t// trailing `?` marks the token OPTIONAL, the same convention as\n\t\t// `@workspace.<key>?`: unset, the caller drops the key.\n\t\tconst raw = v.slice('@range.'.length)\n\t\tconst key = raw.endsWith('?') ? raw.slice(0, -1) : raw\n\t\tif (ctx && ctx.range && key && ctx.range[key] !== undefined\n\t\t\t&& ctx.range[key] !== null && ctx.range[key] !== '') {\n\t\t\treturn ctx.range[key]\n\t\t}\n\t\treturn v\n\t}\n\tif (v === '@me.displayName' || v === '@me.email') {\n\t\t// The form's own context wins (`ctx.me`, e.g. an email fetched from the\n\t\t// profile); @nextcloud/auth only knows the uid and display name.\n\t\tconst key = v.slice('@me.'.length)\n\t\tif (ctx && ctx.me && ctx.me[key] !== undefined && ctx.me[key] !== null && ctx.me[key] !== '') {\n\t\t\treturn ctx.me[key]\n\t\t}\n\t\ttry {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (user && user[key]) {\n\t\t\t\treturn user[key]\n\t\t\t}\n\t\t} catch {\n\t\t\t// not signed in: leave the token unresolved\n\t\t}\n\t\treturn v\n\t}\n\tif (v === '@me') {\n\t\t// Canonical source is @nextcloud/auth; window.OC is the fallback for\n\t\t// environments where the auth package can't read the page state (jsdom).\n\t\tlet uid\n\t\ttry {\n\t\t\tconst user = getCurrentUser()\n\t\t\tuid = user && user.uid\n\t\t} catch {\n\t\t\tuid = null\n\t\t}\n\t\treturn uid || (typeof window !== 'undefined' && window.OC && window.OC.currentUser) || ''\n\t}\n\tif (v === '@now') {\n\t\treturn now.toISOString()\n\t}\n\tif (v === '@today') {\n\t\tconst d = new Date(now)\n\t\td.setHours(0, 0, 0, 0)\n\t\treturn ymd(d)\n\t}\n\tif (v === '@monthStart') {\n\t\treturn ymd(new Date(now.getFullYear(), now.getMonth(), 1))\n\t}\n\tif (v === '@quarterStart') {\n\t\treturn ymd(new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3, 1))\n\t}\n\tif (v === '@yearStart') {\n\t\treturn ymd(new Date(now.getFullYear(), 0, 1))\n\t}\n\tif (v === '@currentFiscalYear') {\n\t\treturn String(now.getFullYear())\n\t}\n\tconst m = v.match(TODAY_DELTA_RE)\n\tif (m) {\n\t\tconst d = new Date(now)\n\t\td.setHours(0, 0, 0, 0)\n\t\td.setDate(d.getDate() + Number(m[1]))\n\t\treturn ymd(d)\n\t}\n\treturn v\n}\n\n/**\n * Whether a value is an UNRESOLVED OPTIONAL token — a string still shaped like\n * `@workspace.<key>?` or `@config.<key>?` after token resolution (the `?` marks\n * it as \"drop me when unset\"). Callers strip such keys from the filter so the\n * list shows all rows rather than waiting for a selection / a config value.\n *\n * @param {unknown} v A (possibly resolved) filter value.\n * @return {boolean}\n */\nexport function isOptionalUnresolved(v) {\n\treturn typeof v === 'string'\n\t\t&& (v.startsWith('@workspace.') || v.startsWith('@config.') || v.startsWith('@range.'))\n\t\t&& v.endsWith('?')\n}\n\n/**\n * Return a copy of a resolved filter map with every UNRESOLVED OPTIONAL\n * workspace token (`@workspace.<key>?` left as-is) removed. Required tokens and\n * concrete values are kept untouched. Inside an IN-list array, unresolved\n * optional ITEMS are dropped; a key whose array empties out is dropped whole\n * (an empty IN-list means \"no constraint\", not \"match nothing\").\n *\n * @param {object} filter A filter map already passed through {@link resolveFilterTokens}.\n * @return {object} The filter without optional-unresolved keys.\n */\nexport function dropOptionalUnresolved(filter) {\n\tif (!filter || typeof filter !== 'object') {\n\t\treturn filter\n\t}\n\tconst out = {}\n\tfor (const [k, v] of Object.entries(filter)) {\n\t\tif (isOptionalUnresolved(v)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\tconst items = v.filter((item) => !isOptionalUnresolved(item))\n\t\t\tif (items.length > 0) {\n\t\t\t\tout[k] = items\n\t\t\t}\n\t\t} else if (v && typeof v === 'object') {\n\t\t\tconst inner = {}\n\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\tif (Array.isArray(ov)) {\n\t\t\t\t\tconst items = ov.filter((item) => !isOptionalUnresolved(item))\n\t\t\t\t\tif (items.length > 0) {\n\t\t\t\t\t\tinner[op] = items\n\t\t\t\t\t}\n\t\t\t\t} else if (!isOptionalUnresolved(ov)) {\n\t\t\t\t\tinner[op] = ov\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Object.keys(inner).length > 0) {\n\t\t\t\tout[k] = inner\n\t\t\t}\n\t\t} else {\n\t\t\tout[k] = v\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * Whether a resolved filter map still carries an unresolved `@`-token. Used by\n * context-bound widgets to detect \"the page state this widget depends on isn't\n * set yet\" (e.g. a client-overview list whose `client` filter is still the raw\n * `@workspace.selectedClient` because no client is selected) and skip the query\n * / render a prompt instead of fetching the whole register.\n *\n * OPTIONAL tokens (`@workspace.<key>?`) are NOT counted — they are meant to be\n * dropped, not waited on (see {@link dropOptionalUnresolved}).\n *\n * @param {object} filter A filter map already passed through {@link resolveFilterTokens}.\n * @return {boolean} True when any value is still a (non-optional) string beginning with `@`.\n */\nexport function hasUnresolvedTokens(filter) {\n\tif (!filter || typeof filter !== 'object') {\n\t\treturn false\n\t}\n\tconst blocking = (x) => typeof x === 'string' && x.charAt(0) === '@' && !isOptionalUnresolved(x)\n\tconst anyBlocking = (v) => {\n\t\tif (blocking(v)) {\n\t\t\treturn true\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\treturn v.some(blocking)\n\t\t}\n\t\treturn false\n\t}\n\tfor (const v of Object.values(filter)) {\n\t\tif (anyBlocking(v)) {\n\t\t\treturn true\n\t\t}\n\t\tif (v && typeof v === 'object' && !Array.isArray(v)) {\n\t\t\tfor (const ov of Object.values(v)) {\n\t\t\t\tif (anyBlocking(ov)) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n/**\n * Resolve every dynamic `@`-token in a filter map (equality, operator, and\n * IN-list array shapes). Array values resolve item-by-item so a list can mix\n * tokens and literals (`{ assignee: ['@me', 'shared'] }`).\n *\n * @param {object} filter The filter map (`{ field: value | value[] | { op: value | value[] } }`).\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object, range?: object}} [ctx] Optional\n * context forwarded to {@link resolveFilterValue} for `@objectId` /\n * `@object.<field>` / `@workspace.<key>` / `@config.<key>` tokens.\n * @return {object} A new filter map with tokens resolved.\n */\nexport function resolveFilterTokens(filter, ctx) {\n\tif (!filter || typeof filter !== 'object') {\n\t\treturn filter\n\t}\n\t// An item that resolves to a list (`@myGroups`) is spread in place, so\n\t// `['@myGroups', 'archive']` stays one flat IN list.\n\tconst resolveOne = (v) => (Array.isArray(v)\n\t\t? v.flatMap((item) => {\n\t\t\t\tconst r = resolveFilterValue(item, ctx)\n\t\t\t\treturn Array.isArray(r) ? r : [r]\n\t\t\t})\n\t\t: resolveFilterValue(v, ctx))\n\tconst out = {}\n\tfor (const [k, v] of Object.entries(filter)) {\n\t\tif (v && typeof v === 'object' && !Array.isArray(v)) {\n\t\t\tconst inner = {}\n\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\tinner[op] = resolveOne(ov)\n\t\t\t}\n\t\t\tout[k] = inner\n\t\t} else {\n\t\t\tout[k] = resolveOne(v)\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * Recursively resolve `@`-tokens inside an arbitrary JSON value — objects\n * and arrays at ANY depth, unlike {@link resolveFilterTokens} which only\n * understands the flat filter-map shape (equality / operator / IN-list, one\n * level of nesting). Used by the `api-call` action's `payload`: a\n * Filinq-style generation body like\n * `{ dataRefs: [{ register: 'crm', schema: 'lead', id: '@objectId' }] }`\n * needs the token resolved inside a nested array of objects, which the\n * shallow filter-map resolver can't reach.\n *\n * @param {unknown} value The value to resolve — object / array / primitive.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object, range?: object}} [ctx] Optional\n * context forwarded to {@link resolveFilterValue}.\n * @return {unknown} A new value with every string leaf token-resolved (structure preserved).\n */\nexport function resolveDeepTokens(value, ctx) {\n\tif (Array.isArray(value)) {\n\t\treturn value.map((v) => resolveDeepTokens(v, ctx))\n\t}\n\tif (value && typeof value === 'object') {\n\t\tconst out = {}\n\t\tfor (const [k, v] of Object.entries(value)) {\n\t\t\tout[k] = resolveDeepTokens(v, ctx)\n\t\t}\n\t\treturn out\n\t}\n\treturn resolveFilterValue(value, ctx)\n}\n\n/**\n * Deep-tree counterpart to {@link hasUnresolvedTokens}: whether an\n * already-{@link resolveDeepTokens}-resolved value still carries an\n * unresolved, non-optional `@`-token anywhere inside it (any depth). Used to\n * BLOCK an `api-call` whose `payload` has a required token (typically\n * `@objectId` / `@object.<field>`) that failed to resolve, instead of\n * silently sending the literal token string to the server.\n *\n * @param {unknown} value The (already deep-resolved) value.\n * @return {boolean} True when a required token is still unresolved.\n */\nexport function hasUnresolvedDeepTokens(value) {\n\tif (Array.isArray(value)) {\n\t\treturn value.some(hasUnresolvedDeepTokens)\n\t}\n\tif (value && typeof value === 'object') {\n\t\treturn Object.values(value).some(hasUnresolvedDeepTokens)\n\t}\n\treturn typeof value === 'string' && value.charAt(0) === '@' && !isOptionalUnresolved(value)\n}\n\n/**\n * Deep-tree counterpart to {@link dropOptionalUnresolved}: strips unresolved\n * OPTIONAL tokens (`@workspace.<key>?` / `@config.<key>?` left as-is after\n * resolution) out of an arbitrary JSON value at any depth. An object key\n * whose value is an unresolved optional token is dropped entirely; an array\n * item that resolves unresolved-optional is filtered out.\n *\n * @param {unknown} value The (already {@link resolveDeepTokens}-resolved) value.\n * @return {unknown} The value with unresolved-optional leaves removed.\n */\nexport function dropOptionalUnresolvedDeep(value) {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.filter((v) => !isOptionalUnresolved(v))\n\t\t\t.map((v) => dropOptionalUnresolvedDeep(v))\n\t}\n\tif (value && typeof value === 'object') {\n\t\tconst out = {}\n\t\tfor (const [k, v] of Object.entries(value)) {\n\t\t\tif (isOptionalUnresolved(v)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tout[k] = dropOptionalUnresolvedDeep(v)\n\t\t}\n\t\treturn out\n\t}\n\treturn value\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolveFilterTokens.js","sources":["../../../src/utils/resolveFilterTokens.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Resolve dynamic `@`-tokens inside a widget filter at FETCH time, so manifest\n * filters can express \"relative to now / the current user\" without baking a\n * fixed value. The widgets call this just before sending the filter to\n * OpenRegister.\n *\n * Supported tokens:\n * - `@me` → the current Nextcloud user id (`getCurrentUser()` from\n * `@nextcloud/auth`; `window.OC.currentUser` fallback)\n * - `@myGroups` → the ids of the Nextcloud groups the current user is\n * in, as an ARRAY (an IN filter). `ctx.myGroups` wins;\n * otherwise a reactive peek over `getCurrentUserGroups`.\n * Stays UNRESOLVED while loading and when the user is\n * in no group, so the caller waits instead of sending\n * an empty IN list (which OpenRegister reads as \"no\n * constraint\"). Spread in place inside an IN-list array.\n * - `@now` → the current instant, ISO-8601\n * - `@today` → today at 00:00, `YYYY-MM-DD` (date-prefix comparable)\n * - `@today±Nd` → relative-date arithmetic at day granularity: N days\n * from today (e.g. `@today-30d`, `@today+7d`)\n * - `@monthStart` → first day of the current month, `YYYY-MM-DD`\n * - `@monthEnd` → last day of the current month, `YYYY-MM-DD`; on a\n * date-time field use `lt: '@nextMonthStart'` instead,\n * since `lte` a date stops at that day's midnight\n * - `@nextMonthStart` → first day of the next month, `YYYY-MM-DD`\n * - `@quarterStart` → first day of the current quarter, `YYYY-MM-DD`\n * - `@yearStart` → first day of the current year, `YYYY-MM-DD`\n * - `@currentFiscalYear` → the current calendar year as a number string, e.g. `2026`\n * - `@range.from` / `@range.to` / `@range.preset` → the active date range a\n * widget passes in `ctx.range` (CnStatWidget: its own\n * picker, else the dashboard's). A trailing `?` marks it\n * optional, as for `@workspace.<key>?`.\n * - `@objectId` → the current detail-page object's id (needs `ctx`)\n * - `@object.<field>` → a field off the current detail-page object (needs `ctx`)\n * - `@workspace.<key>` → a value off the page-level workspace context (needs `ctx.workspace`)\n * - `@config.<key>` → a value off the page-level app config (needs `ctx.config`)\n *\n * `@objectId` / `@object.<field>` are OBJECT-CONTEXT tokens: they resolve only\n * when a `ctx` `{ objectId, object }` is supplied (a detail page provides it via\n * the `cnObjectContext` inject). `@workspace.<key>` is a WORKSPACE-CONTEXT token:\n * it resolves a key off `ctx.workspace` (the reactive `cnWorkspaceContext` a\n * dashboard/workspace page provides) so a list widget can react to page-level\n * state another widget writes (e.g. a selected client). Without the matching\n * context the tokens pass through unchanged — and an UNRESOLVED `@workspace.*`\n * token signals \"no selection yet\" to the caller (see {@link hasUnresolvedTokens}).\n *\n * `@config.<key>` is an APP-CONFIG token: it resolves a key off `ctx.config` (the\n * page-level app config a dashboard/detail page provides via `cnAppConfig` — e.g.\n * a reporting `currency` captured by the setup wizard). Like `@workspace.<key>`, a\n * trailing `?` marks it optional. An UNRESOLVED required `@config.<key>` passes\n * through unchanged; this lets `format: { currency: '@config.currency' }` fall\n * back to a literal default when the config is unset.\n *\n * IN-lists: a filter value MAY be an ARRAY (`{ status: ['open', 'pending'] }`,\n * or the explicit operator form `{ status: { in: [...] } }`). Tokens inside the\n * array are resolved item-by-item (`{ assignee: ['@me', 'shared'] }` works).\n * OpenRegister expects an IN-list as repeated bracket params\n * (`status[]=open&status[]=pending` — the same shape `buildQueryString` /\n * `useObjectStore` send), so widget fetchers serialize a resolved array as\n * repeated `field[]` params, never as a JSON blob or a single joined string.\n *\n * @module utils/resolveFilterTokens\n */\n\nimport { getCurrentUser } from '@nextcloud/auth'\nimport { TODAY_DELTA_RE } from './sentinelTokens.js'\nimport { peekCurrentUserGroups } from './widgetVisibility.js'\n\n/** The filter token for the current user's group ids. */\nexport const MY_GROUPS_TOKEN = '@myGroups'\n\n/**\n * Resolve `@myGroups`: the current user's group ids, or the token itself while\n * they are loading or when there are none (an empty IN list would match every\n * row, so \"no team\" must mean \"wait\", never \"everything\").\n *\n * @spec openspec/changes/nextcloud-group-surfaces/specs/schema-utilities/spec.md#requirement-mygroups-resolves-to-the-current-users-group-ids\n * @param {{myGroups?: string[]}} [ctx] Optional context; `ctx.myGroups` wins.\n * @return {string[]|string} The group ids, or `'@myGroups'` when unresolved.\n */\nfunction resolveMyGroups(ctx) {\n\tlet groups = ctx && Array.isArray(ctx.myGroups) ? ctx.myGroups : null\n\tif (groups === null) {\n\t\ttry {\n\t\t\tgroups = peekCurrentUserGroups()\n\t\t} catch {\n\t\t\tgroups = null\n\t\t}\n\t}\n\tconst ids = Array.isArray(groups)\n\t\t? groups.filter((g) => typeof g === 'string' && g !== '')\n\t\t: []\n\treturn ids.length > 0 ? [...ids] : MY_GROUPS_TOKEN\n}\n\n/**\n * Format a Date as `YYYY-MM-DD` (local).\n *\n * @param {Date} d The date.\n * @return {string} The date-only string.\n */\nfunction ymd(d) {\n\tconst m = String(d.getMonth() + 1).padStart(2, '0')\n\tconst day = String(d.getDate()).padStart(2, '0')\n\treturn `${d.getFullYear()}-${m}-${day}`\n}\n\n/**\n * Resolve a single filter value if it is a dynamic `@`-token, else pass through.\n *\n * @param {unknown} v The candidate value.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object, range?: object, myGroups?: string[]}} [ctx] Optional\n * context for `@objectId` / `@object.<field>` (detail page),\n * `@workspace.<key>` (page-level workspace state), and `@config.<key>`\n * (page-level app config) tokens.\n * @return {unknown} The resolved value.\n */\nexport function resolveFilterValue(v, ctx) {\n\tif (typeof v !== 'string' || v.charAt(0) !== '@') {\n\t\treturn v\n\t}\n\tconst now = new Date()\n\tif (v === MY_GROUPS_TOKEN) {\n\t\treturn resolveMyGroups(ctx)\n\t}\n\tif (v === '@objectId') {\n\t\treturn (ctx && ctx.objectId !== undefined && ctx.objectId !== null) ? String(ctx.objectId) : v\n\t}\n\tif (v.startsWith('@object.')) {\n\t\tconst field = v.slice('@object.'.length)\n\t\tif (ctx && ctx.object && field && ctx.object[field] !== undefined) {\n\t\t\treturn ctx.object[field]\n\t\t}\n\t\treturn v\n\t}\n\tif (v.startsWith('@workspace.')) {\n\t\t// A trailing `?` marks the token OPTIONAL: when unset the caller drops the\n\t\t// filter key (show all) instead of waiting (see hasUnresolvedTokens /\n\t\t// CnObjectListWidget). The `?` is stripped from the key lookup.\n\t\tconst raw = v.slice('@workspace.'.length)\n\t\tconst key = raw.endsWith('?') ? raw.slice(0, -1) : raw\n\t\tif (ctx && ctx.workspace && key && ctx.workspace[key] !== undefined\n\t\t\t&& ctx.workspace[key] !== null && ctx.workspace[key] !== '') {\n\t\t\treturn ctx.workspace[key]\n\t\t}\n\t\treturn v\n\t}\n\tif (v.startsWith('@config.')) {\n\t\t// A trailing `?` marks the token OPTIONAL (same convention as\n\t\t// `@workspace.<key>?`): an unset value lets the caller drop the key. A\n\t\t// required `@config.<key>` that is unset passes through unchanged so a\n\t\t// downstream consumer can fall back to a literal default (e.g.\n\t\t// `format.currency` → `'EUR'`).\n\t\tconst raw = v.slice('@config.'.length)\n\t\tconst key = raw.endsWith('?') ? raw.slice(0, -1) : raw\n\t\tif (ctx && ctx.config && key && ctx.config[key] !== undefined\n\t\t\t&& ctx.config[key] !== null && ctx.config[key] !== '') {\n\t\t\treturn ctx.config[key]\n\t\t}\n\t\treturn v\n\t}\n\tif (v.startsWith('@range.')) {\n\t\t// `@range.from` / `@range.to` / `@range.preset`: the active date range the\n\t\t// caller put in `ctx.range`. CnStatWidget documented these tokens and put\n\t\t// `range` in its endpoint context, but nothing resolved them, so the\n\t\t// literal token stayed in the params, hasUnresolvedTokens blocked the\n\t\t// request, and a card that used its own period picker never loaded. A\n\t\t// trailing `?` marks the token OPTIONAL, the same convention as\n\t\t// `@workspace.<key>?`: unset, the caller drops the key.\n\t\tconst raw = v.slice('@range.'.length)\n\t\tconst key = raw.endsWith('?') ? raw.slice(0, -1) : raw\n\t\tif (ctx && ctx.range && key && ctx.range[key] !== undefined\n\t\t\t&& ctx.range[key] !== null && ctx.range[key] !== '') {\n\t\t\treturn ctx.range[key]\n\t\t}\n\t\treturn v\n\t}\n\tif (v === '@me.displayName' || v === '@me.email') {\n\t\t// The form's own context wins (`ctx.me`, e.g. an email fetched from the\n\t\t// profile); @nextcloud/auth only knows the uid and display name.\n\t\tconst key = v.slice('@me.'.length)\n\t\tif (ctx && ctx.me && ctx.me[key] !== undefined && ctx.me[key] !== null && ctx.me[key] !== '') {\n\t\t\treturn ctx.me[key]\n\t\t}\n\t\ttry {\n\t\t\tconst user = getCurrentUser()\n\t\t\tif (user && user[key]) {\n\t\t\t\treturn user[key]\n\t\t\t}\n\t\t} catch {\n\t\t\t// not signed in: leave the token unresolved\n\t\t}\n\t\treturn v\n\t}\n\tif (v === '@me') {\n\t\t// Canonical source is @nextcloud/auth; window.OC is the fallback for\n\t\t// environments where the auth package can't read the page state (jsdom).\n\t\tlet uid\n\t\ttry {\n\t\t\tconst user = getCurrentUser()\n\t\t\tuid = user && user.uid\n\t\t} catch {\n\t\t\tuid = null\n\t\t}\n\t\treturn uid || (typeof window !== 'undefined' && window.OC && window.OC.currentUser) || ''\n\t}\n\tif (v === '@now') {\n\t\treturn now.toISOString()\n\t}\n\tif (v === '@today') {\n\t\tconst d = new Date(now)\n\t\td.setHours(0, 0, 0, 0)\n\t\treturn ymd(d)\n\t}\n\tif (v === '@monthStart') {\n\t\treturn ymd(new Date(now.getFullYear(), now.getMonth(), 1))\n\t}\n\tif (v === '@monthEnd') {\n\t\t// Day 0 of the next month is the last day of this one.\n\t\treturn ymd(new Date(now.getFullYear(), now.getMonth() + 1, 0))\n\t}\n\tif (v === '@nextMonthStart') {\n\t\treturn ymd(new Date(now.getFullYear(), now.getMonth() + 1, 1))\n\t}\n\tif (v === '@quarterStart') {\n\t\treturn ymd(new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3, 1))\n\t}\n\tif (v === '@yearStart') {\n\t\treturn ymd(new Date(now.getFullYear(), 0, 1))\n\t}\n\tif (v === '@currentFiscalYear') {\n\t\treturn String(now.getFullYear())\n\t}\n\tconst m = v.match(TODAY_DELTA_RE)\n\tif (m) {\n\t\tconst d = new Date(now)\n\t\td.setHours(0, 0, 0, 0)\n\t\td.setDate(d.getDate() + Number(m[1]))\n\t\treturn ymd(d)\n\t}\n\treturn v\n}\n\n/**\n * Whether a value is an UNRESOLVED OPTIONAL token — a string still shaped like\n * `@workspace.<key>?` or `@config.<key>?` after token resolution (the `?` marks\n * it as \"drop me when unset\"). Callers strip such keys from the filter so the\n * list shows all rows rather than waiting for a selection / a config value.\n *\n * @param {unknown} v A (possibly resolved) filter value.\n * @return {boolean}\n */\nexport function isOptionalUnresolved(v) {\n\treturn typeof v === 'string'\n\t\t&& (v.startsWith('@workspace.') || v.startsWith('@config.') || v.startsWith('@range.'))\n\t\t&& v.endsWith('?')\n}\n\n/**\n * Return a copy of a resolved filter map with every UNRESOLVED OPTIONAL\n * workspace token (`@workspace.<key>?` left as-is) removed. Required tokens and\n * concrete values are kept untouched. Inside an IN-list array, unresolved\n * optional ITEMS are dropped; a key whose array empties out is dropped whole\n * (an empty IN-list means \"no constraint\", not \"match nothing\").\n *\n * @param {object} filter A filter map already passed through {@link resolveFilterTokens}.\n * @return {object} The filter without optional-unresolved keys.\n */\nexport function dropOptionalUnresolved(filter) {\n\tif (!filter || typeof filter !== 'object') {\n\t\treturn filter\n\t}\n\tconst out = {}\n\tfor (const [k, v] of Object.entries(filter)) {\n\t\tif (isOptionalUnresolved(v)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\tconst items = v.filter((item) => !isOptionalUnresolved(item))\n\t\t\tif (items.length > 0) {\n\t\t\t\tout[k] = items\n\t\t\t}\n\t\t} else if (v && typeof v === 'object') {\n\t\t\tconst inner = {}\n\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\tif (Array.isArray(ov)) {\n\t\t\t\t\tconst items = ov.filter((item) => !isOptionalUnresolved(item))\n\t\t\t\t\tif (items.length > 0) {\n\t\t\t\t\t\tinner[op] = items\n\t\t\t\t\t}\n\t\t\t\t} else if (!isOptionalUnresolved(ov)) {\n\t\t\t\t\tinner[op] = ov\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (Object.keys(inner).length > 0) {\n\t\t\t\tout[k] = inner\n\t\t\t}\n\t\t} else {\n\t\t\tout[k] = v\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * Whether a resolved filter map still carries an unresolved `@`-token. Used by\n * context-bound widgets to detect \"the page state this widget depends on isn't\n * set yet\" (e.g. a client-overview list whose `client` filter is still the raw\n * `@workspace.selectedClient` because no client is selected) and skip the query\n * / render a prompt instead of fetching the whole register.\n *\n * OPTIONAL tokens (`@workspace.<key>?`) are NOT counted — they are meant to be\n * dropped, not waited on (see {@link dropOptionalUnresolved}).\n *\n * @param {object} filter A filter map already passed through {@link resolveFilterTokens}.\n * @return {boolean} True when any value is still a (non-optional) string beginning with `@`.\n */\nexport function hasUnresolvedTokens(filter) {\n\tif (!filter || typeof filter !== 'object') {\n\t\treturn false\n\t}\n\tconst blocking = (x) => typeof x === 'string' && x.charAt(0) === '@' && !isOptionalUnresolved(x)\n\tconst anyBlocking = (v) => {\n\t\tif (blocking(v)) {\n\t\t\treturn true\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\treturn v.some(blocking)\n\t\t}\n\t\treturn false\n\t}\n\tfor (const v of Object.values(filter)) {\n\t\tif (anyBlocking(v)) {\n\t\t\treturn true\n\t\t}\n\t\tif (v && typeof v === 'object' && !Array.isArray(v)) {\n\t\t\tfor (const ov of Object.values(v)) {\n\t\t\t\tif (anyBlocking(ov)) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n/**\n * Resolve every dynamic `@`-token in a filter map (equality, operator, and\n * IN-list array shapes). Array values resolve item-by-item so a list can mix\n * tokens and literals (`{ assignee: ['@me', 'shared'] }`).\n *\n * @param {object} filter The filter map (`{ field: value | value[] | { op: value | value[] } }`).\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object, range?: object}} [ctx] Optional\n * context forwarded to {@link resolveFilterValue} for `@objectId` /\n * `@object.<field>` / `@workspace.<key>` / `@config.<key>` tokens.\n * @return {object} A new filter map with tokens resolved.\n */\nexport function resolveFilterTokens(filter, ctx) {\n\tif (!filter || typeof filter !== 'object') {\n\t\treturn filter\n\t}\n\t// An item that resolves to a list (`@myGroups`) is spread in place, so\n\t// `['@myGroups', 'archive']` stays one flat IN list.\n\tconst resolveOne = (v) => (Array.isArray(v)\n\t\t? v.flatMap((item) => {\n\t\t\t\tconst r = resolveFilterValue(item, ctx)\n\t\t\t\treturn Array.isArray(r) ? r : [r]\n\t\t\t})\n\t\t: resolveFilterValue(v, ctx))\n\tconst out = {}\n\tfor (const [k, v] of Object.entries(filter)) {\n\t\tif (v && typeof v === 'object' && !Array.isArray(v)) {\n\t\t\tconst inner = {}\n\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\tinner[op] = resolveOne(ov)\n\t\t\t}\n\t\t\tout[k] = inner\n\t\t} else {\n\t\t\tout[k] = resolveOne(v)\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * Recursively resolve `@`-tokens inside an arbitrary JSON value — objects\n * and arrays at ANY depth, unlike {@link resolveFilterTokens} which only\n * understands the flat filter-map shape (equality / operator / IN-list, one\n * level of nesting). Used by the `api-call` action's `payload`: a\n * Filinq-style generation body like\n * `{ dataRefs: [{ register: 'crm', schema: 'lead', id: '@objectId' }] }`\n * needs the token resolved inside a nested array of objects, which the\n * shallow filter-map resolver can't reach.\n *\n * @param {unknown} value The value to resolve — object / array / primitive.\n * @param {{objectId?: (string|number), object?: object, workspace?: object, config?: object, range?: object}} [ctx] Optional\n * context forwarded to {@link resolveFilterValue}.\n * @return {unknown} A new value with every string leaf token-resolved (structure preserved).\n */\nexport function resolveDeepTokens(value, ctx) {\n\tif (Array.isArray(value)) {\n\t\treturn value.map((v) => resolveDeepTokens(v, ctx))\n\t}\n\tif (value && typeof value === 'object') {\n\t\tconst out = {}\n\t\tfor (const [k, v] of Object.entries(value)) {\n\t\t\tout[k] = resolveDeepTokens(v, ctx)\n\t\t}\n\t\treturn out\n\t}\n\treturn resolveFilterValue(value, ctx)\n}\n\n/**\n * Deep-tree counterpart to {@link hasUnresolvedTokens}: whether an\n * already-{@link resolveDeepTokens}-resolved value still carries an\n * unresolved, non-optional `@`-token anywhere inside it (any depth). Used to\n * BLOCK an `api-call` whose `payload` has a required token (typically\n * `@objectId` / `@object.<field>`) that failed to resolve, instead of\n * silently sending the literal token string to the server.\n *\n * @param {unknown} value The (already deep-resolved) value.\n * @return {boolean} True when a required token is still unresolved.\n */\nexport function hasUnresolvedDeepTokens(value) {\n\tif (Array.isArray(value)) {\n\t\treturn value.some(hasUnresolvedDeepTokens)\n\t}\n\tif (value && typeof value === 'object') {\n\t\treturn Object.values(value).some(hasUnresolvedDeepTokens)\n\t}\n\treturn typeof value === 'string' && value.charAt(0) === '@' && !isOptionalUnresolved(value)\n}\n\n/**\n * Deep-tree counterpart to {@link dropOptionalUnresolved}: strips unresolved\n * OPTIONAL tokens (`@workspace.<key>?` / `@config.<key>?` left as-is after\n * resolution) out of an arbitrary JSON value at any depth. An object key\n * whose value is an unresolved optional token is dropped entirely; an array\n * item that resolves unresolved-optional is filtered out.\n *\n * @param {unknown} value The (already {@link resolveDeepTokens}-resolved) value.\n * @return {unknown} The value with unresolved-optional leaves removed.\n */\nexport function dropOptionalUnresolvedDeep(value) {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.filter((v) => !isOptionalUnresolved(v))\n\t\t\t.map((v) => dropOptionalUnresolvedDeep(v))\n\t}\n\tif (value && typeof value === 'object') {\n\t\tconst out = {}\n\t\tfor (const [k, v] of Object.entries(value)) {\n\t\t\tif (isOptionalUnresolved(v)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tout[k] = dropOptionalUnresolvedDeep(v)\n\t\t}\n\t\treturn out\n\t}\n\treturn value\n}\n"],"names":[],"mappings":"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"}
@@ -1051,12 +1051,33 @@ function fieldsFromSchema(schema, options = {}) {
1051
1051
  if (overrides[key]) {
1052
1052
  Object.assign(field, overrides[key]);
1053
1053
  applyPickerOverride(field, overrides[key]);
1054
+ translateOverrideText(field, overrides[key], tr);
1054
1055
  }
1055
1056
 
1056
1057
  return field
1057
1058
  })
1058
1059
  }
1059
1060
 
1061
+ /**
1062
+ * Run an override's display text through the translate function, as the
1063
+ * schema's own title and description are. A manifest writes `label`,
1064
+ * `description` and `placeholder` in its source language; without this a
1065
+ * form showed "Requester" to a Dutch user whose catalogue holds "Aanvrager".
1066
+ *
1067
+ * @param {object} field The field, with the override already merged in.
1068
+ * @param {object} override The per-key override.
1069
+ * @param {(text: string) => string} tr The translate function (identity when none).
1070
+ * @return {void}
1071
+ * @spec openspec/changes/screens-form-override-labels-parity/specs/form-override-labels/spec.md#requirement-override-text-reads-in-the-users-language
1072
+ */
1073
+ function translateOverrideText(field, override, tr) {
1074
+ for (const name of ['label', 'description', 'placeholder']) {
1075
+ if (typeof override[name] === 'string' && override[name] !== '') {
1076
+ field[name] = tr(override[name]);
1077
+ }
1078
+ }
1079
+ }
1080
+
1060
1081
  /**
1061
1082
  * Generate faceted filter definitions from a schema's facetable properties.
1062
1083
  *