@conduction/nextcloud-vue 2.83.0 → 2.85.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 (246) hide show
  1. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  2. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +131 -3
  3. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  4. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +54 -18
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  7. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +1 -1
  8. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  10. package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
  11. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +232 -4
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +95 -51
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  15. package/dist/esm/components/CnBodySections/CnBodySections.vue.js +1 -1
  16. package/dist/esm/components/CnBodySections/CnBodySections.vue2.js +22 -1
  17. package/dist/esm/components/CnBodySections/CnBodySections.vue2.js.map +1 -1
  18. package/dist/esm/components/CnBodySections/CnBodySections.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
  20. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +47 -4
  21. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
  22. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
  23. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
  24. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +239 -3
  25. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +5 -5
  27. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  28. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  29. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +50 -0
  30. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -12
  32. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue.js +1 -1
  34. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js +1 -1
  35. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js.map +1 -1
  36. package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue3.js.map +1 -1
  37. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +226 -8
  38. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  39. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  40. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  41. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +3 -1
  42. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  43. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  44. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  45. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +18 -5
  46. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  48. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +1 -1
  49. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +38 -1
  50. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +45 -5
  52. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  53. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue.js +1 -1
  54. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js +1 -1
  55. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js.map +1 -1
  56. package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue3.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +548 -77
  58. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  59. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +191 -82
  60. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  61. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +11 -2
  62. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  63. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +29 -6
  64. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  65. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  66. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
  67. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +126 -2
  68. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  69. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +24 -2
  70. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  71. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  72. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +106 -3
  73. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  74. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  75. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
  76. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +67 -5
  77. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
  78. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +181 -124
  79. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
  80. package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
  81. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +135 -2
  82. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
  83. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +95 -43
  84. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
  85. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
  86. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +5 -1
  87. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
  88. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
  89. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue.js +1 -1
  90. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +72 -5
  91. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  92. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +33 -8
  93. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  94. package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
  95. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +135 -11
  96. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  97. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -2
  98. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  99. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue.js +1 -1
  100. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +17 -2
  101. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  102. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +1 -1
  103. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  104. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  105. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +39 -6
  106. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  108. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +1 -1
  109. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +140 -7
  110. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -1
  111. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -1
  112. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  113. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +37 -9
  114. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  115. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +6 -5
  116. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  117. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue.js +1 -1
  118. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +16 -0
  119. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
  120. package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
  121. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +1 -1
  122. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +2 -1
  123. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
  124. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
  125. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js +53 -1
  126. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js.map +1 -1
  127. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js +3 -2
  128. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js.map +1 -1
  129. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  130. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +135 -0
  131. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  132. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  133. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
  134. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +19 -0
  135. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  136. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +37 -19
  137. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  138. package/dist/esm/composables/useListView.js +21 -0
  139. package/dist/esm/composables/useListView.js.map +1 -1
  140. package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
  141. package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +68 -6
  142. package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
  143. package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +133 -105
  144. package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
  145. package/dist/esm/index.js +1 -0
  146. package/dist/esm/index.js.map +1 -1
  147. package/dist/esm/l10n/en.json.js +38 -1
  148. package/dist/esm/l10n/en.json.js.map +1 -1
  149. package/dist/esm/l10n/nl.json.js +78 -1
  150. package/dist/esm/l10n/nl.json.js.map +1 -1
  151. package/dist/esm/library-style.js +1 -1
  152. package/dist/esm/nextcloud-vue.css +1030 -533
  153. package/dist/esm/store/useObjectStore.js +16 -2
  154. package/dist/esm/store/useObjectStore.js.map +1 -1
  155. package/dist/esm/utils/boardColumns.js +8 -1
  156. package/dist/esm/utils/boardColumns.js.map +1 -1
  157. package/dist/esm/utils/boardReferenceColumns.js +278 -0
  158. package/dist/esm/utils/boardReferenceColumns.js.map +1 -0
  159. package/dist/esm/utils/buildManifest.js +41 -4
  160. package/dist/esm/utils/buildManifest.js.map +1 -1
  161. package/dist/esm/utils/detailWidgetVisibility.js +149 -0
  162. package/dist/esm/utils/detailWidgetVisibility.js.map +1 -0
  163. package/dist/esm/utils/formatMetric.js +39 -3
  164. package/dist/esm/utils/formatMetric.js.map +1 -1
  165. package/dist/esm/utils/gridStack.js +40 -1
  166. package/dist/esm/utils/gridStack.js.map +1 -1
  167. package/dist/esm/utils/humaniseKey.js +41 -0
  168. package/dist/esm/utils/humaniseKey.js.map +1 -0
  169. package/dist/esm/utils/notificationRuleLabel.js +6 -23
  170. package/dist/esm/utils/notificationRuleLabel.js.map +1 -1
  171. package/dist/esm/utils/resolveManifestTokens.js +1 -0
  172. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  173. package/dist/esm/utils/sentinelTokens.js +9 -0
  174. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  175. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  176. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  177. package/dist/esm/utils/widgetDispatch.js +8 -2
  178. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  179. package/dist/nextcloud-vue.cjs.js +17159 -13422
  180. package/dist/nextcloud-vue.cjs.js.map +2 -2
  181. package/dist/nextcloud-vue.css +1030 -533
  182. package/l10n/en.json +38 -1
  183. package/l10n/nl.json +78 -1
  184. package/package.json +1 -1
  185. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +160 -8
  186. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +1 -1
  187. package/src/components/CnBoardView/CnBoardView.vue +262 -6
  188. package/src/components/CnBodySections/CnBodySections.vue +22 -1
  189. package/src/components/CnCountdownWidget/CnCountdownWidget.vue +47 -4
  190. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +244 -8
  191. package/src/components/CnDashboardPage/CnDashboardPage.vue +62 -2
  192. package/src/components/CnDeltaWidget/CnDeltaWidget.vue +1 -1
  193. package/src/components/CnDetailPage/CnDetailPage.md +3 -1
  194. package/src/components/CnDetailPage/CnDetailPage.vue +226 -8
  195. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +3 -1
  196. package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -5
  197. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +63 -1
  198. package/src/components/CnGaugeWidget/CnGaugeWidget.vue +1 -1
  199. package/src/components/CnIndexPage/CnIndexPage.vue +586 -25
  200. package/src/components/CnIndexPage/useSelfFetchList.js +10 -1
  201. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +29 -6
  202. package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
  203. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +154 -5
  204. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +106 -3
  205. package/src/components/CnRegisterMapping/CnRegisterMapping.md +5 -0
  206. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +231 -96
  207. package/src/components/CnReportsPage/CnReportsPage.vue +179 -1
  208. package/src/components/CnSettingsPage/CnSettingsPage.vue +5 -1
  209. package/src/components/CnStagesWidget/CnStagesWidget.vue +103 -6
  210. package/src/components/CnStatWidget/CnStatWidget.vue +137 -12
  211. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +34 -3
  212. package/src/components/CnTabsWidget/CnTabsWidget.vue +39 -6
  213. package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +140 -7
  214. package/src/components/CnVersionInfoCard/CnVersionInfoCard.md +2 -1
  215. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +42 -13
  216. package/src/components/CnWalkthrough/CnWalkthrough.vue +16 -0
  217. package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +2 -1
  218. package/src/components/CnWidgetCardGrid/CnWidgetCardGrid.vue +55 -2
  219. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +135 -0
  220. package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +10 -0
  221. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +44 -1
  222. package/src/composables/useListView.js +21 -0
  223. package/src/css/board.css +65 -4
  224. package/src/css/dialog.css +4 -2
  225. package/src/css/form-field.css +14 -0
  226. package/src/css/form-widget.css +11 -0
  227. package/src/css/index-page.css +49 -0
  228. package/src/css/kpi-card.css +13 -0
  229. package/src/css/look-board-reports.css +58 -0
  230. package/src/css/look-board.css +108 -7
  231. package/src/dialogs/CnTransitionInputDialog.vue +87 -7
  232. package/src/index.js +2 -0
  233. package/src/schemas/app-manifest-v2.schema.json +137 -6
  234. package/src/store/useObjectStore.js +16 -2
  235. package/src/utils/boardColumns.js +8 -1
  236. package/src/utils/boardReferenceColumns.js +274 -0
  237. package/src/utils/buildManifest.js +40 -3
  238. package/src/utils/detailWidgetVisibility.js +145 -0
  239. package/src/utils/formatMetric.js +38 -2
  240. package/src/utils/gridStack.js +39 -0
  241. package/src/utils/humaniseKey.js +38 -0
  242. package/src/utils/notificationRuleLabel.js +5 -23
  243. package/src/utils/resolveManifestTokens.js +1 -0
  244. package/src/utils/sentinelTokens.js +9 -0
  245. package/src/utils/validateManifestV2.compiled.js +3 -3
  246. package/src/utils/widgetDispatch.js +8 -2
@@ -23,6 +23,15 @@ function resolveInitialQuickFilterIndex(quickFilters) {
23
23
  * @param {Array<object>} filterMaps Already route-resolved filter maps.
24
24
  * @return {object} The merged filter (scalar when a key has one value, array when many).
25
25
  */
26
+ /**
27
+ * Rows one board fetch asks for. A board shows every matching row at once,
28
+ * so it loads far more than a table page (OpenRegister serves up to 1000);
29
+ * past this the board offers "Load more".
30
+ *
31
+ * @type {number}
32
+ */
33
+ const BOARD_PAGE_SIZE = 500;
34
+
26
35
  /**
27
36
  * The sort a quick filter sets while it is active (`sort: { key, order }`, or a
28
37
  * list of those for a multi-column order), as sort keys, or null when it sets
@@ -233,7 +242,7 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
233
242
  defaultSortKeys: initialSortKeys,
234
243
  defaultActiveFilters: initialActiveFilters,
235
244
  defaultSearchTerm: initialSearchTerm,
236
- defaultPageSize: (props.pagination && props.pagination.limit) || undefined,
245
+ defaultPageSize: props.viewMode === 'board' ? BOARD_PAGE_SIZE : ((props.pagination && props.pagination.limit) || undefined),
237
246
  defaultVisibleColumns: configuredColumnKeys.length ? configuredColumnKeys : null,
238
247
  // `pages[].config.extend` → OpenRegister's repeated `_extend[]`. A
239
248
  // getter so a reactive change re-scopes the next fetch, matching how
@@ -337,5 +346,5 @@ function useSelfFetchList(props, instance, inject, extras = {}) {
337
346
  }
338
347
  }
339
348
 
340
- export { quickFilterSortKeys, useSelfFetchList };
349
+ export { BOARD_PAGE_SIZE, quickFilterSortKeys, useSelfFetchList };
341
350
  //# sourceMappingURL=useSelfFetchList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSelfFetchList.js","sources":["../../../../src/components/CnIndexPage/useSelfFetchList.js"],"sourcesContent":["import { computed, ref, watch } from 'vue'\nimport { useListView } from '../../composables/index.js'\nimport { useObjectSubscription } from '../../composables/useObjectSubscription.js'\nimport { useObjectStore } from '../../store/index.js'\n// Both filter resolvers live in `utils/routeFilters.js` so CnLogsPage applies\n// the same two grammars (route-param interpolation + `?key=value` deep links)\n// without pulling in this composable's index-only sidebar/subscription wiring.\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'\n\nfunction resolveInitialQuickFilterIndex(quickFilters) {\n\tconst tabs = Array.isArray(quickFilters) ? quickFilters : null\n\tif (!tabs || tabs.length === 0) {\n\t\treturn null\n\t}\n\tconst di = tabs.findIndex((t) => t && t.default === true)\n\treturn di >= 0 ? di : 0\n}\n\n/**\n * OR-merge several quick-filter maps into one fetch filter. Values for the\n * same key collapse to an array (deduped) when more than one tab contributes\n * it — `buildQueryString` serialises an array as `key[]=a&key[]=b`, which the\n * OpenRegister API interprets as an IN / OR match.\n *\n * @param {Array<object>} filterMaps Already route-resolved filter maps.\n * @return {object} The merged filter (scalar when a key has one value, array when many).\n */\n/**\n * The sort a quick filter sets while it is active (`sort: { key, order }`, or a\n * list of those for a multi-column order), as sort keys, or null when it sets\n * none.\n *\n * @param {object|undefined} tab A quick filter.\n * @return {Array<{key: string, order: string}>|null} The sort keys.\n * @spec openspec/changes/screens-quick-filter-sort/specs/index-page/spec.md\n */\nexport function quickFilterSortKeys(tab) {\n\tconst sort = tab && tab.sort\n\tif (!sort) {\n\t\treturn null\n\t}\n\tconst keys = (Array.isArray(sort) ? sort : [sort])\n\t\t.filter((entry) => entry && typeof entry.key === 'string' && entry.key !== '')\n\t\t.map((entry) => ({ key: entry.key, order: entry.order === 'desc' ? 'desc' : 'asc' }))\n\treturn keys.length > 0 ? keys : null\n}\n\nfunction unionFilterMaps(filterMaps) {\n\tconst grouped = {}\n\tfor (const map of filterMaps) {\n\t\tif (!map || typeof map !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tfor (const [k, v] of Object.entries(map)) {\n\t\t\tif (v === undefined || v === null || v === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!grouped[k]) {\n\t\t\t\tgrouped[k] = []\n\t\t\t}\n\t\t\tfor (const item of (Array.isArray(v) ? v : [v])) {\n\t\t\t\tif (!grouped[k].includes(item)) {\n\t\t\t\t\tgrouped[k].push(item)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconst out = {}\n\tfor (const [k, arr] of Object.entries(grouped)) {\n\t\tout[k] = arr.length === 1 ? arr[0] : arr\n\t}\n\treturn out\n}\n\n/**\n * Self-fetch mode for CnIndexPage: when register+schema are set but no\n * `objects` prop was passed (manifest-driven pages), drive the list ourselves.\n *\n * @param {object} props CnIndexPage props.\n * @param {import('vue').ComponentInternalInstance|null} instance Pass `getCurrentInstance()`.\n * @param {typeof import('vue').inject} inject Pass Vue's `inject`.\n * @param {{activeFolderSchema?: import('vue').Ref<?{schema: string, register?: string}>}} [extras] `activeFolderSchema`: a ref the page sets to the selected folder's `{ schema, register? }`, switching the loaded object type while it is non-null.\n * @return {object} { isSelfFetch, list, selfObjectStore, selfObjectType, activeQuickFilterIndex }\n */\nexport function useSelfFetchList(props, instance, inject, extras = {}) {\n\tconst objectsProvided = !!(\n\t\tinstance && instance.proxy && instance.proxy.$options && instance.proxy.$options.propsData\n\t\t&& Object.hasOwn(instance.proxy.$options.propsData, 'objects')\n\t)\n\t// A named source wins over register/schema. Without this the page would\n\t// fire an OpenRegister request whose rows it then discards in favour of the\n\t// source's — wasted, but worse than wasted if the manifest names both by\n\t// mistake, because the request succeeds and nothing says the two disagree.\n\tconst isSelfFetch = !!(props.register && props.schema) && !objectsProvided && !props.entitySource\n\n\t// \"Also search inside files\" switch, seeded from `?contentSearch=1` so a\n\t// shared link reproduces the list. Only meaningful when `searchInFiles` is on.\n\tconst routeAtSetup = instance && instance.proxy && instance.proxy.$route\n\tconst contentSearch = ref(props.searchInFiles === true && String(routeAtSetup?.query?.contentSearch) === '1')\n\n\tconst activeQuickFilterIndex = ref(resolveInitialQuickFilterIndex(withPersonalLenses(props.quickFilters, props.personalLenses)))\n\tconst selectedQuickFilterIndices = ref([])\n\tconst isMultiQuickFilter = props.quickFilterMultiple === true\n\n\tif (!isSelfFetch) {\n\t\treturn {\n\t\t\tisSelfFetch: false,\n\t\t\tlist: null,\n\t\t\tselfObjectStore: null,\n\t\t\tselfObjectType: '',\n\t\t\tactiveQuickFilterIndex,\n\t\t\tselectedQuickFilterIndices,\n\t\t\tcontentSearch,\n\t\t\tselfFetchTokenCtx: null,\n\t\t\tinitialQueryFilterKeys: [],\n\t\t}\n\t}\n\n\t// The object type follows the selected folder's own schema when it declares\n\t// one; otherwise the page's register and schema.\n\tconst activeFolderSchema = extras.activeFolderSchema || null\n\tconst resolvedTarget = computed(() => {\n\t\tconst folder = activeFolderSchema && activeFolderSchema.value\n\t\tif (folder && folder.schema) {\n\t\t\tconst register = folder.register || props.register\n\t\t\treturn { register, schema: folder.schema, type: `${register}-${folder.schema}` }\n\t\t}\n\t\t// A collection endpoint gets its own type key, so a plain registration of\n\t\t// the same pair elsewhere never resets it.\n\t\tconst collectionUrl = props.collectionUrl || ''\n\t\tconst type = `${props.register}-${props.schema}` + (collectionUrl ? `@${collectionUrl}` : '')\n\t\treturn { register: props.register, schema: props.schema, type, collectionUrl }\n\t})\n\tconst objectType = computed(() => resolvedTarget.value.type)\n\tconst sidebarState = inject('sidebarState', null) ?? inject('objectSidebarState', null)\n\tconst objectStore = useObjectStore()\n\n\t// Token-resolution context for `@workspace.<key>` / `@config.<key>` /\n\t// `@objectId` / `@object.<field>` inside `props.filter` / quick-filter\n\t// tab filters. Same injects + unwrap shape as CnObjectListWidget's\n\t// `objectCtx`/`workspaceCtx`/`tokenCtx` computeds (and CnDeltaWidget) —\n\t// `cnWorkspaceContext` is the reactive bag a dashboard/workspace-root\n\t// (e.g. hrmq's App.vue for multi-administratie) provides; `cnObjectContext`\n\t// is a detail-page's object context (rare on an index page, but harmless\n\t// to support); `cnAppConfig` is the page-level app config bag. All three\n\t// default to null/absent so an app that never provides them is unaffected.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the current token-resolution ctx `{ objectId?, object?, workspace, config }`\n\t * from the injected bags, unwrapping Vue refs. Called fresh on every fetch\n\t * so it always reflects the latest workspace/config state.\n\t *\n\t * @return {object} The token ctx.\n\t */\n\tfunction tokenCtx() {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t// Reactive signature of the workspace/config bags so a change (e.g. the\n\t// administration switcher writing `activeAdministrationId`) triggers a\n\t// re-fetch below — `fixedFilters` is a plain getter called at fetch time\n\t// (see useListView.resolveFixedFilters), it is NOT auto-tracked by Vue,\n\t// so without this the list would resolve the token once on mount and\n\t// never again. Stringified (like CnDeltaWidget's `sourceKey`) so the\n\t// watcher fires on real content changes only, not object identity.\n\tconst workspaceSignature = computed(() => JSON.stringify(unwrapCtx(workspaceCtxRaw) || {}))\n\tconst appConfigSignature = computed(() => JSON.stringify(unwrapCtx(appConfigRaw) || {}))\n\n\t// Pass register/schema in their positional id slots (not as a {register, schema} object as\n\t// second arg) — that previously made fetch URLs go to `/api/objects/undefined/[object Object]`.\n\t// Runs (sync, ahead of the list's own watcher) again whenever the type switches.\n\tfunction registerTarget() {\n\t\tconst { register, schema, type, collectionUrl } = resolvedTarget.value\n\t\tif (typeof objectStore.registerObjectType === 'function') {\n\t\t\tobjectStore.registerObjectType(type, schema, register, {\n\t\t\t\tregisterSlug: register,\n\t\t\t\tschemaSlug: schema,\n\t\t\t\t...(collectionUrl ? { collectionUrl } : {}),\n\t\t\t})\n\t\t}\n\t\t// A mixed list tells its own rows apart by `@self.register`, an id, so a slug register needs resolving.\n\t\tif (collectionUrl && !/^\\d+$/.test(String(register)) && typeof objectStore.fetchRegister === 'function') {\n\t\t\tobjectStore.fetchRegister(type)\n\t\t}\n\t}\n\tregisterTarget()\n\twatch(objectType, registerTarget, { flush: 'sync' })\n\n\t// Seed the visible-column set from the configured columns so the sidebar's\n\t// Columns tab reflects the curated default; null when none are configured\n\t// (schema-driven table — every column starts visible).\n\t// A column marked `hidden: true` is declared but starts outside the set,\n\t// so it is off until the person turns it on in the Columns tab.\n\tconst configuredColumnKeys = (props.columns || [])\n\t\t.filter((c) => !(c && typeof c === 'object' && c.hidden === true))\n\t\t.map((c) => (typeof c === 'string' ? c : c && c.key))\n\t\t.filter(Boolean)\n\n\t// Restore a persisted multi-column sort from the route query first (deep\n\t// link / reload); fall back to a host-passed `sortKeys` prop, then the\n\t// legacy single-key `sortKey`/`sortOrder` props.\n\tconst initialRoute = instance && instance.proxy && instance.proxy.$route\n\t// The page's own order, which a lens without a `sort` goes back to.\n\tconst pageSortKeys = (Array.isArray(props.sortKeys) && props.sortKeys.length > 0)\n\t\t? props.sortKeys\n\t\t: (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] : [])\n\t// The lens that is active on mount may set its own order.\n\tconst initialTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst initialTabSort = (!isMultiQuickFilter && Array.isArray(initialTabs))\n\t\t? quickFilterSortKeys(initialTabs[activeQuickFilterIndex.value])\n\t\t: null\n\tconst initialSortKeys = parseSortKeysFromQuery(initialRoute)\n\t\t|| initialTabSort\n\t\t|| (Array.isArray(props.sortKeys) && props.sortKeys.length > 0 ? props.sortKeys : undefined)\n\t// Same deep-link restore for facet filters and search, so opening a\n\t// shared/bookmarked URL reproduces the same view (CnIndexPage persists\n\t// both back to the route on change).\n\tconst initialActiveFilters = resolveQueryFilters(initialRoute && initialRoute.query, tokenCtx())\n\tconst initialSearchTerm = (initialRoute && typeof initialRoute.query?._search === 'string') ? initialRoute.query._search : ''\n\n\t// Set right after useListView returns; the fixed-filters getter reads it lazily.\n\tlet listHandle = null\n\tconst list = useListView(() => objectType.value, {\n\t\tobjectStore,\n\t\tsidebarState,\n\t\tdefaultSort: props.sortKey ? { key: props.sortKey, order: props.sortOrder || 'asc' } : undefined,\n\t\tdefaultSortKeys: initialSortKeys,\n\t\tdefaultActiveFilters: initialActiveFilters,\n\t\tdefaultSearchTerm: initialSearchTerm,\n\t\tdefaultPageSize: (props.pagination && props.pagination.limit) || undefined,\n\t\tdefaultVisibleColumns: configuredColumnKeys.length ? configuredColumnKeys : null,\n\t\t// `pages[].config.extend` → OpenRegister's repeated `_extend[]`. A\n\t\t// getter so a reactive change re-scopes the next fetch, matching how\n\t\t// `fixedFilters` is read.\n\t\textend: () => (Array.isArray(props.extend) ? props.extend : []),\n\t\tfixedFilters: () => {\n\t\t\tconst route = instance && instance.proxy && instance.proxy.$route\n\t\t\tconst params = (route && route.params) || {}\n\t\t\tconst ctx = tokenCtx()\n\t\t\tconst queryFilters = resolveQueryFilters(route && route.query, ctx)\n\t\t\tconst base = resolveFilterMap(props.filter, params, ctx)\n\t\t\t// A folder-sidebar scope declaring `searchFields` narrows what the\n\t\t\t// search box searches over while that scope is active. Read off the\n\t\t\t// instance, like `$route` above, because the active scope is the\n\t\t\t// component's own state rather than a prop — and read through the\n\t\t\t// one fixed-filter getter rather than a second search path.\n\t\t\tconst scopeSearchFields = (instance && instance.proxy && instance.proxy.activeScopeSearchFields) || []\n\t\t\tconst scopeBase = scopeSearchFields.length > 0 ? { _searchFields: scopeSearchFields } : {}\n\t\t\t// File-content search widens a text search, so it rides only with a term.\n\t\t\tconst widen = props.searchInFiles === true && contentSearch.value && !!(listHandle && listHandle.searchTerm.value)\n\t\t\t// The visible calendar month, only while the page is in calendar mode.\n\t\t\tconst month = (instance && instance.proxy && instance.proxy.calendarRangeFilter) || {}\n\t\t\tconst scope = { ...(widen ? { ...scopeBase, _content_search: 'true' } : scopeBase), ...month }\n\t\t\tconst lensed = withPersonalLenses(props.quickFilters, props.personalLenses)\n\t\t\tconst tabs = Array.isArray(lensed) ? lensed : null\n\t\t\tif (!tabs) {\n\t\t\t\treturn { ...queryFilters, ...scope, ...base }\n\t\t\t}\n\n\t\t\t// Multiple mode: OR the selected tabs' filters together (union).\n\t\t\tif (isMultiQuickFilter) {\n\t\t\t\tconst maps = selectedQuickFilterIndices.value\n\t\t\t\t\t.map((i) => resolveFilterMap(tabs[i]?.filter, params, ctx))\n\t\t\t\treturn { ...queryFilters, ...scope, ...base, ...unionFilterMaps(maps) }\n\t\t\t}\n\n\t\t\t// Single mode: the active tab's filter spread last so it wins\n\t\t\t// over a colliding props.filter entry.\n\t\t\tconst activeIdx = activeQuickFilterIndex.value\n\t\t\tconst tabFilter = (activeIdx !== null && activeIdx !== undefined) ? tabs[activeIdx]?.filter : null\n\t\t\treturn { ...queryFilters, ...scope, ...base, ...resolveFilterMap(tabFilter, params, ctx) }\n\t\t},\n\t})\n\n\tlistHandle = list\n\n\t// Re-fetch when the quick-filter selection changes (pre-existing), OR\n\t// when the workspace/app-config bag content changes (e.g. the\n\t// administration switcher writes a new `activeAdministrationId`) — a\n\t// `@workspace.<key>`/`@config.<key>` token in `props.filter` must re-scope\n\t// the list without a manual reload. A change to `props.filter` itself (a\n\t// host toggling a filter checkbox) re-fetches the same way.\n\tconst filterSignature = computed(() => JSON.stringify(props.filter ?? null))\n\twatch([activeQuickFilterIndex, selectedQuickFilterIndices, contentSearch, workspaceSignature, appConfigSignature, filterSignature], (now, before) => {\n\t\t// Switching lens: a lens with a `sort` sets that order; leaving one\n\t\t// for a lens without goes back to the page's own order. A column the\n\t\t// person sorts by afterwards still wins until the next switch.\n\t\tif (!isMultiQuickFilter && before && now[0] !== before[0]) {\n\t\t\tconst tabs = withPersonalLenses(props.quickFilters, props.personalLenses) || []\n\t\t\tconst keys = quickFilterSortKeys(tabs[now[0]]) || (quickFilterSortKeys(tabs[before[0]]) ? pageSortKeys : null)\n\t\t\tif (keys && list.sortKeys) {\n\t\t\t\tlist.sortKeys.value = keys\n\t\t\t\tlist.sortKey.value = keys[0]?.key ?? null\n\t\t\t\tlist.sortOrder.value = keys[0]?.order ?? 'asc'\n\t\t\t}\n\t\t}\n\t\tif (list && typeof list.refresh === 'function') {\n\t\t\tlist.refresh(1)\n\t\t}\n\t})\n\n\t// Live collection updates (manifest-live-updates): subscribe to the\n\t// page's `or-collection-{register}-{schema}` scope so another user's\n\t// create/update/delete refreshes this list without a manual reload.\n\t// The subscription lifecycle (mount/unmount, in-flight dedupe, epoch\n\t// guard against stale async resolution) lives in useObjectSubscription;\n\t// the refetch-on-event (with the last fetch params, burst-coalesced)\n\t// lives in liveUpdatesPlugin. The `subscribe` prop (default true; from\n\t// a manifest: `config.subscribe: false`) is the opt-out, read through\n\t// a reactive getter so a runtime flip attaches/detaches accordingly.\n\t// Stores without live-updates support (no `subscribe` action) are a\n\t// silent no-op inside the composable, keeping this fully inert.\n\tuseObjectSubscription(objectStore, () => objectType.value, null, {\n\t\tenabled: () => props.subscribe !== false,\n\t})\n\n\treturn {\n\t\tisSelfFetch: true,\n\t\tlist,\n\t\tselfObjectStore: objectStore,\n\t\tselfObjectType: objectType,\n\t\tactiveQuickFilterIndex,\n\t\tselectedQuickFilterIndices,\n\t\tcontentSearch,\n\t\t// The page persists the view state back into the query, and needs both\n\t\t// to do it without trampling the rest of it: the keys it adopted from\n\t\t// the query on load (the only non-`_` ones it may clear), and the ctx\n\t\t// those keys' `@`-tokens resolve against.\n\t\tselfFetchTokenCtx: tokenCtx,\n\t\tinitialQueryFilterKeys: Object.keys(initialActiveFilters),\n\t}\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSelfFetchList.js","sources":["../../../../src/components/CnIndexPage/useSelfFetchList.js"],"sourcesContent":["import { computed, ref, watch } from 'vue'\nimport { useListView } from '../../composables/index.js'\nimport { useObjectSubscription } from '../../composables/useObjectSubscription.js'\nimport { useObjectStore } from '../../store/index.js'\n// Both filter resolvers live in `utils/routeFilters.js` so CnLogsPage applies\n// the same two grammars (route-param interpolation + `?key=value` deep links)\n// without pulling in this composable's index-only sidebar/subscription wiring.\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'\n\nfunction resolveInitialQuickFilterIndex(quickFilters) {\n\tconst tabs = Array.isArray(quickFilters) ? quickFilters : null\n\tif (!tabs || tabs.length === 0) {\n\t\treturn null\n\t}\n\tconst di = tabs.findIndex((t) => t && t.default === true)\n\treturn di >= 0 ? di : 0\n}\n\n/**\n * OR-merge several quick-filter maps into one fetch filter. Values for the\n * same key collapse to an array (deduped) when more than one tab contributes\n * it — `buildQueryString` serialises an array as `key[]=a&key[]=b`, which the\n * OpenRegister API interprets as an IN / OR match.\n *\n * @param {Array<object>} filterMaps Already route-resolved filter maps.\n * @return {object} The merged filter (scalar when a key has one value, array when many).\n */\n/**\n * Rows one board fetch asks for. A board shows every matching row at once,\n * so it loads far more than a table page (OpenRegister serves up to 1000);\n * past this the board offers \"Load more\".\n *\n * @type {number}\n */\nexport const BOARD_PAGE_SIZE = 500\n\n/**\n * The sort a quick filter sets while it is active (`sort: { key, order }`, or a\n * list of those for a multi-column order), as sort keys, or null when it sets\n * none.\n *\n * @param {object|undefined} tab A quick filter.\n * @return {Array<{key: string, order: string}>|null} The sort keys.\n * @spec openspec/changes/screens-quick-filter-sort/specs/index-page/spec.md\n */\nexport function quickFilterSortKeys(tab) {\n\tconst sort = tab && tab.sort\n\tif (!sort) {\n\t\treturn null\n\t}\n\tconst keys = (Array.isArray(sort) ? sort : [sort])\n\t\t.filter((entry) => entry && typeof entry.key === 'string' && entry.key !== '')\n\t\t.map((entry) => ({ key: entry.key, order: entry.order === 'desc' ? 'desc' : 'asc' }))\n\treturn keys.length > 0 ? keys : null\n}\n\nfunction unionFilterMaps(filterMaps) {\n\tconst grouped = {}\n\tfor (const map of filterMaps) {\n\t\tif (!map || typeof map !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tfor (const [k, v] of Object.entries(map)) {\n\t\t\tif (v === undefined || v === null || v === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!grouped[k]) {\n\t\t\t\tgrouped[k] = []\n\t\t\t}\n\t\t\tfor (const item of (Array.isArray(v) ? v : [v])) {\n\t\t\t\tif (!grouped[k].includes(item)) {\n\t\t\t\t\tgrouped[k].push(item)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconst out = {}\n\tfor (const [k, arr] of Object.entries(grouped)) {\n\t\tout[k] = arr.length === 1 ? arr[0] : arr\n\t}\n\treturn out\n}\n\n/**\n * Self-fetch mode for CnIndexPage: when register+schema are set but no\n * `objects` prop was passed (manifest-driven pages), drive the list ourselves.\n *\n * @param {object} props CnIndexPage props.\n * @param {import('vue').ComponentInternalInstance|null} instance Pass `getCurrentInstance()`.\n * @param {typeof import('vue').inject} inject Pass Vue's `inject`.\n * @param {{activeFolderSchema?: import('vue').Ref<?{schema: string, register?: string}>}} [extras] `activeFolderSchema`: a ref the page sets to the selected folder's `{ schema, register? }`, switching the loaded object type while it is non-null.\n * @return {object} { isSelfFetch, list, selfObjectStore, selfObjectType, activeQuickFilterIndex }\n */\nexport function useSelfFetchList(props, instance, inject, extras = {}) {\n\tconst objectsProvided = !!(\n\t\tinstance && instance.proxy && instance.proxy.$options && instance.proxy.$options.propsData\n\t\t&& Object.hasOwn(instance.proxy.$options.propsData, 'objects')\n\t)\n\t// A named source wins over register/schema. Without this the page would\n\t// fire an OpenRegister request whose rows it then discards in favour of the\n\t// source's — wasted, but worse than wasted if the manifest names both by\n\t// mistake, because the request succeeds and nothing says the two disagree.\n\tconst isSelfFetch = !!(props.register && props.schema) && !objectsProvided && !props.entitySource\n\n\t// \"Also search inside files\" switch, seeded from `?contentSearch=1` so a\n\t// shared link reproduces the list. Only meaningful when `searchInFiles` is on.\n\tconst routeAtSetup = instance && instance.proxy && instance.proxy.$route\n\tconst contentSearch = ref(props.searchInFiles === true && String(routeAtSetup?.query?.contentSearch) === '1')\n\n\tconst activeQuickFilterIndex = ref(resolveInitialQuickFilterIndex(withPersonalLenses(props.quickFilters, props.personalLenses)))\n\tconst selectedQuickFilterIndices = ref([])\n\tconst isMultiQuickFilter = props.quickFilterMultiple === true\n\n\tif (!isSelfFetch) {\n\t\treturn {\n\t\t\tisSelfFetch: false,\n\t\t\tlist: null,\n\t\t\tselfObjectStore: null,\n\t\t\tselfObjectType: '',\n\t\t\tactiveQuickFilterIndex,\n\t\t\tselectedQuickFilterIndices,\n\t\t\tcontentSearch,\n\t\t\tselfFetchTokenCtx: null,\n\t\t\tinitialQueryFilterKeys: [],\n\t\t}\n\t}\n\n\t// The object type follows the selected folder's own schema when it declares\n\t// one; otherwise the page's register and schema.\n\tconst activeFolderSchema = extras.activeFolderSchema || null\n\tconst resolvedTarget = computed(() => {\n\t\tconst folder = activeFolderSchema && activeFolderSchema.value\n\t\tif (folder && folder.schema) {\n\t\t\tconst register = folder.register || props.register\n\t\t\treturn { register, schema: folder.schema, type: `${register}-${folder.schema}` }\n\t\t}\n\t\t// A collection endpoint gets its own type key, so a plain registration of\n\t\t// the same pair elsewhere never resets it.\n\t\tconst collectionUrl = props.collectionUrl || ''\n\t\tconst type = `${props.register}-${props.schema}` + (collectionUrl ? `@${collectionUrl}` : '')\n\t\treturn { register: props.register, schema: props.schema, type, collectionUrl }\n\t})\n\tconst objectType = computed(() => resolvedTarget.value.type)\n\tconst sidebarState = inject('sidebarState', null) ?? inject('objectSidebarState', null)\n\tconst objectStore = useObjectStore()\n\n\t// Token-resolution context for `@workspace.<key>` / `@config.<key>` /\n\t// `@objectId` / `@object.<field>` inside `props.filter` / quick-filter\n\t// tab filters. Same injects + unwrap shape as CnObjectListWidget's\n\t// `objectCtx`/`workspaceCtx`/`tokenCtx` computeds (and CnDeltaWidget) —\n\t// `cnWorkspaceContext` is the reactive bag a dashboard/workspace-root\n\t// (e.g. hrmq's App.vue for multi-administratie) provides; `cnObjectContext`\n\t// is a detail-page's object context (rare on an index page, but harmless\n\t// to support); `cnAppConfig` is the page-level app config bag. All three\n\t// default to null/absent so an app that never provides them is unaffected.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the current token-resolution ctx `{ objectId?, object?, workspace, config }`\n\t * from the injected bags, unwrapping Vue refs. Called fresh on every fetch\n\t * so it always reflects the latest workspace/config state.\n\t *\n\t * @return {object} The token ctx.\n\t */\n\tfunction tokenCtx() {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t// Reactive signature of the workspace/config bags so a change (e.g. the\n\t// administration switcher writing `activeAdministrationId`) triggers a\n\t// re-fetch below — `fixedFilters` is a plain getter called at fetch time\n\t// (see useListView.resolveFixedFilters), it is NOT auto-tracked by Vue,\n\t// so without this the list would resolve the token once on mount and\n\t// never again. Stringified (like CnDeltaWidget's `sourceKey`) so the\n\t// watcher fires on real content changes only, not object identity.\n\tconst workspaceSignature = computed(() => JSON.stringify(unwrapCtx(workspaceCtxRaw) || {}))\n\tconst appConfigSignature = computed(() => JSON.stringify(unwrapCtx(appConfigRaw) || {}))\n\n\t// Pass register/schema in their positional id slots (not as a {register, schema} object as\n\t// second arg) — that previously made fetch URLs go to `/api/objects/undefined/[object Object]`.\n\t// Runs (sync, ahead of the list's own watcher) again whenever the type switches.\n\tfunction registerTarget() {\n\t\tconst { register, schema, type, collectionUrl } = resolvedTarget.value\n\t\tif (typeof objectStore.registerObjectType === 'function') {\n\t\t\tobjectStore.registerObjectType(type, schema, register, {\n\t\t\t\tregisterSlug: register,\n\t\t\t\tschemaSlug: schema,\n\t\t\t\t...(collectionUrl ? { collectionUrl } : {}),\n\t\t\t})\n\t\t}\n\t\t// A mixed list tells its own rows apart by `@self.register`, an id, so a slug register needs resolving.\n\t\tif (collectionUrl && !/^\\d+$/.test(String(register)) && typeof objectStore.fetchRegister === 'function') {\n\t\t\tobjectStore.fetchRegister(type)\n\t\t}\n\t}\n\tregisterTarget()\n\twatch(objectType, registerTarget, { flush: 'sync' })\n\n\t// Seed the visible-column set from the configured columns so the sidebar's\n\t// Columns tab reflects the curated default; null when none are configured\n\t// (schema-driven table — every column starts visible).\n\t// A column marked `hidden: true` is declared but starts outside the set,\n\t// so it is off until the person turns it on in the Columns tab.\n\tconst configuredColumnKeys = (props.columns || [])\n\t\t.filter((c) => !(c && typeof c === 'object' && c.hidden === true))\n\t\t.map((c) => (typeof c === 'string' ? c : c && c.key))\n\t\t.filter(Boolean)\n\n\t// Restore a persisted multi-column sort from the route query first (deep\n\t// link / reload); fall back to a host-passed `sortKeys` prop, then the\n\t// legacy single-key `sortKey`/`sortOrder` props.\n\tconst initialRoute = instance && instance.proxy && instance.proxy.$route\n\t// The page's own order, which a lens without a `sort` goes back to.\n\tconst pageSortKeys = (Array.isArray(props.sortKeys) && props.sortKeys.length > 0)\n\t\t? props.sortKeys\n\t\t: (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] : [])\n\t// The lens that is active on mount may set its own order.\n\tconst initialTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst initialTabSort = (!isMultiQuickFilter && Array.isArray(initialTabs))\n\t\t? quickFilterSortKeys(initialTabs[activeQuickFilterIndex.value])\n\t\t: null\n\tconst initialSortKeys = parseSortKeysFromQuery(initialRoute)\n\t\t|| initialTabSort\n\t\t|| (Array.isArray(props.sortKeys) && props.sortKeys.length > 0 ? props.sortKeys : undefined)\n\t// Same deep-link restore for facet filters and search, so opening a\n\t// shared/bookmarked URL reproduces the same view (CnIndexPage persists\n\t// both back to the route on change).\n\tconst initialActiveFilters = resolveQueryFilters(initialRoute && initialRoute.query, tokenCtx())\n\tconst initialSearchTerm = (initialRoute && typeof initialRoute.query?._search === 'string') ? initialRoute.query._search : ''\n\n\t// Set right after useListView returns; the fixed-filters getter reads it lazily.\n\tlet listHandle = null\n\tconst list = useListView(() => objectType.value, {\n\t\tobjectStore,\n\t\tsidebarState,\n\t\tdefaultSort: props.sortKey ? { key: props.sortKey, order: props.sortOrder || 'asc' } : undefined,\n\t\tdefaultSortKeys: initialSortKeys,\n\t\tdefaultActiveFilters: initialActiveFilters,\n\t\tdefaultSearchTerm: initialSearchTerm,\n\t\tdefaultPageSize: props.viewMode === 'board' ? BOARD_PAGE_SIZE : ((props.pagination && props.pagination.limit) || undefined),\n\t\tdefaultVisibleColumns: configuredColumnKeys.length ? configuredColumnKeys : null,\n\t\t// `pages[].config.extend` → OpenRegister's repeated `_extend[]`. A\n\t\t// getter so a reactive change re-scopes the next fetch, matching how\n\t\t// `fixedFilters` is read.\n\t\textend: () => (Array.isArray(props.extend) ? props.extend : []),\n\t\tfixedFilters: () => {\n\t\t\tconst route = instance && instance.proxy && instance.proxy.$route\n\t\t\tconst params = (route && route.params) || {}\n\t\t\tconst ctx = tokenCtx()\n\t\t\tconst queryFilters = resolveQueryFilters(route && route.query, ctx)\n\t\t\tconst base = resolveFilterMap(props.filter, params, ctx)\n\t\t\t// A folder-sidebar scope declaring `searchFields` narrows what the\n\t\t\t// search box searches over while that scope is active. Read off the\n\t\t\t// instance, like `$route` above, because the active scope is the\n\t\t\t// component's own state rather than a prop — and read through the\n\t\t\t// one fixed-filter getter rather than a second search path.\n\t\t\tconst scopeSearchFields = (instance && instance.proxy && instance.proxy.activeScopeSearchFields) || []\n\t\t\tconst scopeBase = scopeSearchFields.length > 0 ? { _searchFields: scopeSearchFields } : {}\n\t\t\t// File-content search widens a text search, so it rides only with a term.\n\t\t\tconst widen = props.searchInFiles === true && contentSearch.value && !!(listHandle && listHandle.searchTerm.value)\n\t\t\t// The visible calendar month, only while the page is in calendar mode.\n\t\t\tconst month = (instance && instance.proxy && instance.proxy.calendarRangeFilter) || {}\n\t\t\tconst scope = { ...(widen ? { ...scopeBase, _content_search: 'true' } : scopeBase), ...month }\n\t\t\tconst lensed = withPersonalLenses(props.quickFilters, props.personalLenses)\n\t\t\tconst tabs = Array.isArray(lensed) ? lensed : null\n\t\t\tif (!tabs) {\n\t\t\t\treturn { ...queryFilters, ...scope, ...base }\n\t\t\t}\n\n\t\t\t// Multiple mode: OR the selected tabs' filters together (union).\n\t\t\tif (isMultiQuickFilter) {\n\t\t\t\tconst maps = selectedQuickFilterIndices.value\n\t\t\t\t\t.map((i) => resolveFilterMap(tabs[i]?.filter, params, ctx))\n\t\t\t\treturn { ...queryFilters, ...scope, ...base, ...unionFilterMaps(maps) }\n\t\t\t}\n\n\t\t\t// Single mode: the active tab's filter spread last so it wins\n\t\t\t// over a colliding props.filter entry.\n\t\t\tconst activeIdx = activeQuickFilterIndex.value\n\t\t\tconst tabFilter = (activeIdx !== null && activeIdx !== undefined) ? tabs[activeIdx]?.filter : null\n\t\t\treturn { ...queryFilters, ...scope, ...base, ...resolveFilterMap(tabFilter, params, ctx) }\n\t\t},\n\t})\n\n\tlistHandle = list\n\n\t// Re-fetch when the quick-filter selection changes (pre-existing), OR\n\t// when the workspace/app-config bag content changes (e.g. the\n\t// administration switcher writes a new `activeAdministrationId`) — a\n\t// `@workspace.<key>`/`@config.<key>` token in `props.filter` must re-scope\n\t// the list without a manual reload. A change to `props.filter` itself (a\n\t// host toggling a filter checkbox) re-fetches the same way.\n\tconst filterSignature = computed(() => JSON.stringify(props.filter ?? null))\n\twatch([activeQuickFilterIndex, selectedQuickFilterIndices, contentSearch, workspaceSignature, appConfigSignature, filterSignature], (now, before) => {\n\t\t// Switching lens: a lens with a `sort` sets that order; leaving one\n\t\t// for a lens without goes back to the page's own order. A column the\n\t\t// person sorts by afterwards still wins until the next switch.\n\t\tif (!isMultiQuickFilter && before && now[0] !== before[0]) {\n\t\t\tconst tabs = withPersonalLenses(props.quickFilters, props.personalLenses) || []\n\t\t\tconst keys = quickFilterSortKeys(tabs[now[0]]) || (quickFilterSortKeys(tabs[before[0]]) ? pageSortKeys : null)\n\t\t\tif (keys && list.sortKeys) {\n\t\t\t\tlist.sortKeys.value = keys\n\t\t\t\tlist.sortKey.value = keys[0]?.key ?? null\n\t\t\t\tlist.sortOrder.value = keys[0]?.order ?? 'asc'\n\t\t\t}\n\t\t}\n\t\tif (list && typeof list.refresh === 'function') {\n\t\t\tlist.refresh(1)\n\t\t}\n\t})\n\n\t// Live collection updates (manifest-live-updates): subscribe to the\n\t// page's `or-collection-{register}-{schema}` scope so another user's\n\t// create/update/delete refreshes this list without a manual reload.\n\t// The subscription lifecycle (mount/unmount, in-flight dedupe, epoch\n\t// guard against stale async resolution) lives in useObjectSubscription;\n\t// the refetch-on-event (with the last fetch params, burst-coalesced)\n\t// lives in liveUpdatesPlugin. The `subscribe` prop (default true; from\n\t// a manifest: `config.subscribe: false`) is the opt-out, read through\n\t// a reactive getter so a runtime flip attaches/detaches accordingly.\n\t// Stores without live-updates support (no `subscribe` action) are a\n\t// silent no-op inside the composable, keeping this fully inert.\n\tuseObjectSubscription(objectStore, () => objectType.value, null, {\n\t\tenabled: () => props.subscribe !== false,\n\t})\n\n\treturn {\n\t\tisSelfFetch: true,\n\t\tlist,\n\t\tselfObjectStore: objectStore,\n\t\tselfObjectType: objectType,\n\t\tactiveQuickFilterIndex,\n\t\tselectedQuickFilterIndices,\n\t\tcontentSearch,\n\t\t// The page persists the view state back into the query, and needs both\n\t\t// to do it without trampling the rest of it: the keys it adopted from\n\t\t// the query on load (the only non-`_` ones it may clear), and the ctx\n\t\t// those keys' `@`-tokens resolve against.\n\t\tselfFetchTokenCtx: tokenCtx,\n\t\tinitialQueryFilterKeys: Object.keys(initialActiveFilters),\n\t}\n}\n"],"names":[],"mappings":"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"}
@@ -132,7 +132,7 @@ var script = {
132
132
  /** Channel options `{ value, label }`. */
133
133
  channelOptions() {
134
134
  return Array.isArray(this.content.channels) && this.content.channels.length > 0
135
- ? this.content.channels
135
+ ? this.content.channels.map((channel) => ({ ...channel, label: this.copy(channel && channel.label) }))
136
136
  : [
137
137
  { value: 'telefoon', label: translate('nextcloud-vue', 'Phone') },
138
138
  { value: 'email', label: translate('nextcloud-vue', 'Email') },
@@ -153,10 +153,11 @@ var script = {
153
153
  const prop = properties[this.content.outcomeField || 'outcome'];
154
154
  if (prop && Array.isArray(prop.enum) && prop.enum.length > 0) {
155
155
  const labels = prop['x-enum-labels'] || prop.enumLabels || {};
156
- const tr = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k;
157
- return prop.enum.map((value) => ({ value, label: tr(labels[value] || String(value)) }))
156
+ return prop.enum.map((value) => ({ value, label: this.copy(labels[value] || String(value)) }))
158
157
  }
159
- return Array.isArray(this.content.outcomes) ? this.content.outcomes : []
158
+ return Array.isArray(this.content.outcomes)
159
+ ? this.content.outcomes.map((outcome) => ({ ...outcome, label: this.copy(outcome && outcome.label) }))
160
+ : []
160
161
  },
161
162
 
162
163
  /**
@@ -229,8 +230,7 @@ var script = {
229
230
  // `content.submitLabel` names what the form creates, e.g. "Save contact moment".
230
231
  registerLabel() {
231
232
  if (this.content.submitLabel) {
232
- const tr = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k;
233
- return tr(this.content.submitLabel)
233
+ return this.copy(this.content.submitLabel)
234
234
  }
235
235
  return translate('nextcloud-vue', 'Register')
236
236
  },
@@ -308,6 +308,29 @@ var script = {
308
308
  },
309
309
 
310
310
  /** The default channel value (first configured channel, else `telefoon`). */
311
+ /**
312
+ * A label the manifest supplies (a channel, an outcome, the submit
313
+ * label), translated: the app's own translation first, since the words
314
+ * are the app's, then the library's catalogue, then the text as
315
+ * written. Before, a channel label was shown as written and the submit
316
+ * label only reached the app's translate, so pipelinq showed "Phone"
317
+ * and "Save contact moment" in English to a Dutch reader.
318
+ *
319
+ * @spec openspec/changes/interaction-form-fits-and-reads-dutch/specs/interaction-form-widget/spec.md#requirement-the-interaction-form-fits-its-card-and-reads-in-the-readers-language
320
+ * @param {string} key The label as the manifest writes it.
321
+ * @return {string} The translated label.
322
+ */
323
+ copy(key) {
324
+ if (typeof key !== 'string' || key === '') {
325
+ return key
326
+ }
327
+ const host = typeof this.cnTranslate === 'function' ? this.cnTranslate(key) : key;
328
+ if (typeof host === 'string' && host !== '' && host !== key) {
329
+ return host
330
+ }
331
+ return translate('nextcloud-vue', key)
332
+ },
333
+
311
334
  firstChannel() {
312
335
  const ch = (this.content && this.content.channels) || [];
313
336
  return (Array.isArray(ch) && ch.length > 0 && ch[0].value) || 'telefoon'
@@ -1 +1 @@
1
- {"version":3,"file":"CnInteractionFormWidget.vue2.js","sources":["../../../../src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnFormWidgetBase\n\t\tblockClass=\"cn-interaction-form-widget\"\n\t\t:fields=\"formFields\"\n\t\t:model=\"form\"\n\t\t:errors=\"{ subject: subjectError }\"\n\t\t:canSubmit=\"canRegister\"\n\t\t:submitting=\"saving\"\n\t\t:submitLabel=\"registerLabel\"\n\t\t:submittingLabel=\"savingLabel\"\n\t\t:errorMessage=\"errorMessage\"\n\t\t@update:field=\"onFieldUpdate\"\n\t\t@submit=\"onRegister\">\n\t\t<!-- The client picker is the one control the base does not know about:\n\t\t a CnResourceSelect, so typing a name that does not exist yet offers\n\t\t \"Create '<name>'\" inline rather than a dead \"no results\" path. The\n\t\t base still supplies its field wrapper and spacing. -->\n\t\t<template #field-client>\n\t\t\t<CnResourceSelect\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"clientSchema\"\n\t\t\t\t:labelField=\"clientLabelField\"\n\t\t\t\t:modelValue=\"form.client\"\n\t\t\t\t:inputLabel=\"clientLabel\"\n\t\t\t\t:preload=\"true\"\n\t\t\t\t@update:modelValue=\"onClientChange\"\n\t\t\t\t@create=\"onClientCreated\" />\n\t\t</template>\n\n\t\t<!-- Opens what was just saved in a new tab, beside the submit button. -->\n\t\t<template #actions-start>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"savedHref\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:href=\"savedHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\tdata-testid=\"cn-interaction-form-open\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnFormWidgetBase>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport CnFormWidgetBase from '../CnFormWidgetBase/CnFormWidgetBase.vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport { useObjectStore } from '../../store/index.js'\n\n/**\n * CnInteractionFormWidget — the \"active interaction\" quick-log form of a\n * workspace page.\n *\n * Persists a contactmoment (channel / client / subject / summary / outcome) to\n * OpenRegister via `useObjectStore().saveObject`. It reads `selectedClient`\n * from the page-level workspace context (the page's client in focus, set by a\n * page-level picker) to pre-fill its Client field.\n *\n * By default it also writes the chosen client to `selectedClient` and the live\n * summary text to `activeSummary`, so sibling widgets react: a client list\n * filtered on `@workspace.selectedClient`, or a `CnKbSearchWidget` bound to\n * `activeSummary`. A page whose client in focus comes from its own picker sets\n * `writeWorkspace: false`, which keeps the form's values to the submission.\n *\n * The Client field belongs to this submission only. It is a\n * `CnResourceSelect`, so typing a name that doesn't exist yet offers\n * \"Create '<name>'\" inline — no dead \"no results\" path.\n *\n * Resolved by its registry type key `interaction-form`. All schema/field/enum\n * choices come from `content`, so the widget carries no app-specific vocabulary.\n *\n * Example content blob:\n * ```js\n * content: {\n * register: 'pipelinq',\n * schema: 'ticket',\n * defaults: { ticketType: 'contactmoment' }, // fixed fields stamped on every create\n * clientSchema: 'client',\n * clientField: 'client',\n * summaryField: 'description',\n * channels: [{ value: 'telefoon', label: 'Phone' }, …],\n * }\n * ```\n *\n * The outcome options are read from the target schema on load: the enum of\n * its `outcomeField` property, labelled by that property's `x-enum-labels`.\n * `outcomes: [{ value, label }]` is only a fallback for a schema that has no\n * enum there.\n *\n * With `detailRoute` set (a route name taking `:id`, like an object list's\n * `rowRoute`), a save puts an \"Open\" button left of the submit button that\n * opens the saved object in a new tab.\n *\n * `defaults` covers the case where the target schema requires a value the form\n * has no input for — typically a discriminator on a supertype schema. It is\n * merged before the mapped fields, so those always take precedence.\n */\nexport default {\n\tname: 'CnInteractionFormWidget',\n\n\tcomponents: { CnFormWidgetBase, CnResourceSelect, NcButton, OpenInNew },\n\n\tinject: {\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) from CnDashboardPage.\n\t\t * The widget reads `selectedClient` from it and, unless\n\t\t * `content.writeWorkspace` is false, writes `selectedClient` and\n\t\t * `activeSummary` back. Null on pages that don't provide one (the form\n\t\t * still saves).\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Host translate function from CnAppRoot, bound to the host app's id.\n\t\t * Translates the schema's outcome labels.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted configuration blob (see component description for the shape).\n\t\t *\n\t\t * @type {{register?: string, schema?: string, defaults?: object, clientSchema?: string, clientField?: string, clientLabelField?: string, subjectField?: string, summaryField?: string, channelField?: string, outcomeField?: string, channels?: Array, outcomes?: Array, submitLabel?: string, detailRoute?: string, writeWorkspace?: boolean}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\tform: {\n\t\t\t\tchannel: this.firstChannel(),\n\t\t\t\tclient: '',\n\t\t\t\tsubject: '',\n\t\t\t\tsummary: '',\n\t\t\t\toutcome: '',\n\t\t\t},\n\n\t\t\tsaving: false,\n\t\t\terrorMessage: '',\n\t\t\tsubjectError: '',\n\t\t\t// The target schema, fetched on load for its outcome enum.\n\t\t\tschemaDef: null,\n\t\t\t// The last saved object, `{ id, title }`, until the next save.\n\t\t\tsaved: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tregister() {\n\t\t\treturn this.content.register || 'pipelinq'\n\t\t},\n\n\t\tschema() {\n\t\t\treturn this.content.schema || 'contactmoment'\n\t\t},\n\n\t\tclientSchema() {\n\t\t\treturn this.content.clientSchema || 'client'\n\t\t},\n\n\t\tclientLabelField() {\n\t\t\treturn this.content.clientLabelField || 'name'\n\t\t},\n\n\t\ttypeSlug() {\n\t\t\treturn `${this.register}-${this.schema}`\n\t\t},\n\n\t\t/** Channel options `{ value, label }`. */\n\t\tchannelOptions() {\n\t\t\treturn Array.isArray(this.content.channels) && this.content.channels.length > 0\n\t\t\t\t? this.content.channels\n\t\t\t\t: [\n\t\t\t\t\t\t{ value: 'telefoon', label: t('nextcloud-vue', 'Phone') },\n\t\t\t\t\t\t{ value: 'email', label: t('nextcloud-vue', 'Email') },\n\t\t\t\t\t\t{ value: 'chat', label: t('nextcloud-vue', 'Chat') },\n\t\t\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * Outcome options `{ value, label }`, from the target schema's enum so\n\t\t * the form can only offer values the schema accepts. Labels come from\n\t\t * the property's `x-enum-labels`, translated. `content.outcomes` is the\n\t\t * fallback for a schema without an enum on that property.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\toutcomeOptions() {\n\t\t\tconst properties = (this.schemaDef && this.schemaDef.properties) || {}\n\t\t\tconst prop = properties[this.content.outcomeField || 'outcome']\n\t\t\tif (prop && Array.isArray(prop.enum) && prop.enum.length > 0) {\n\t\t\t\tconst labels = prop['x-enum-labels'] || prop.enumLabels || {}\n\t\t\t\tconst tr = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\t\treturn prop.enum.map((value) => ({ value, label: tr(labels[value] || String(value)) }))\n\t\t\t}\n\t\t\treturn Array.isArray(this.content.outcomes) ? this.content.outcomes : []\n\t\t},\n\n\t\t/**\n\t\t * The field descriptors handed to CnFormWidgetBase — the whole of this\n\t\t * widget's form, declared rather than drawn. Order matters: it is the\n\t\t * render order.\n\t\t *\n\t\t * `client` is declared here so it keeps its place in the stack and its\n\t\t * field wrapper, but its control comes from the `#field-client` slot\n\t\t * (a CnResourceSelect, which the base has no type for).\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tformFields() {\n\t\t\treturn [\n\t\t\t\t{ key: 'channel', type: 'select', label: this.channelLabel, options: this.channelOptions },\n\t\t\t\t{ key: 'client', type: 'custom', label: this.clientLabel },\n\t\t\t\t{ key: 'subject', type: 'text', label: this.subjectLabel },\n\t\t\t\t{ key: 'summary', type: 'textarea', label: this.summaryLabel, rows: 4 },\n\t\t\t\t{ key: 'outcome', type: 'select', label: this.outcomeLabel, options: this.outcomeOptions, clearable: true },\n\t\t\t]\n\t\t},\n\n\t\tobjectStore() {\n\t\t\ttry {\n\t\t\t\treturn useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/** The page's client in focus, or an empty string. */\n\t\tpageClient() {\n\t\t\tconst id = this.workspaceCtx && this.workspaceCtx.selectedClient\n\t\t\treturn id ? String(id) : ''\n\t\t},\n\n\t\tcanRegister() {\n\t\t\treturn Boolean(this.form.subject && this.form.subject.trim() && this.form.channel)\n\t\t},\n\n\t\tchannelLabel() {\n\t\t\treturn t('nextcloud-vue', 'Channel')\n\t\t},\n\n\t\tclientLabel() {\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tsubjectLabel() {\n\t\t\treturn t('nextcloud-vue', 'Subject')\n\t\t},\n\n\t\tsummaryLabel() {\n\t\t\treturn t('nextcloud-vue', 'Summary')\n\t\t},\n\n\t\toutcomeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Outcome')\n\t\t},\n\n\t\t// `content.submitLabel` names what the form creates, e.g. \"Save contact moment\".\n\t\tregisterLabel() {\n\t\t\tif (this.content.submitLabel) {\n\t\t\t\tconst tr = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\t\treturn tr(this.content.submitLabel)\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Register')\n\t\t},\n\n\t\tsavingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Saving…')\n\t\t},\n\n\t\t/**\n\t\t * URL of the saved object's page, from `content.detailRoute`. Empty\n\t\t * without a route or router, and then no Open button is shown.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tsavedHref() {\n\t\t\tconst route = this.content.detailRoute\n\t\t\tif (!this.saved || !this.saved.id || !route || !this.$router) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn this.$router.resolve({ name: route, params: { id: this.saved.id } }).href || ''\n\t\t\t} catch {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t},\n\n\t\t// `escape: false` because the label renders through `{{ }}`, which escapes.\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open \"{title}\"', { title: (this.saved && this.saved.title) || '' }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the form writes its client and summary to the workspace context. */\n\t\twritesWorkspace() {\n\t\t\treturn this.content.writeWorkspace !== false\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A new page client pre-fills the field; the field stays editable.\n\t\tpageClient: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.form.client = id\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.loadSchema()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Fetch the target schema, which the outcome options derive from. A\n\t\t * failure leaves the `content.outcomes` fallback in place.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadSchema() {\n\t\t\tconst store = this.objectStore\n\t\t\tif (!store || typeof store.fetchSchema !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Re-registering an existing type resets its cached objects and schema.\n\t\t\t\tif (typeof store.registerObjectType === 'function' && !store.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tstore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tthis.schemaDef = await store.fetchSchema(this.typeSlug)\n\t\t\t} catch {\n\t\t\t\tthis.schemaDef = null\n\t\t\t}\n\t\t},\n\n\t\t/** The default channel value (first configured channel, else `telefoon`). */\n\t\tfirstChannel() {\n\t\t\tconst ch = (this.content && this.content.channels) || []\n\t\t\treturn (Array.isArray(ch) && ch.length > 0 && ch[0].value) || 'telefoon'\n\t\t},\n\n\t\t/**\n\t\t * A field edit from CnFormWidgetBase. `summary` also streams into the\n\t\t * workspace context. `client` never arrives here: its control is the\n\t\t * `#field-client` slot, which calls onClientChange.\n\t\t *\n\t\t * @param {{key: string, value: unknown}} payload The changed field.\n\t\t * @return {void}\n\t\t */\n\t\tonFieldUpdate({ key, value }) {\n\t\t\tthis.form[key] = value\n\t\t\tif (key === 'summary') {\n\t\t\t\tthis.writeWorkspace('activeSummary', value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The submission's client, also written to the workspace context.\n\t\t *\n\t\t * @param {string} id The selected client id.\n\t\t */\n\t\tonClientChange(id) {\n\t\t\tthis.form.client = id || ''\n\t\t\tthis.writeWorkspace('selectedClient', this.form.client)\n\t\t},\n\n\t\t/**\n\t\t * A client created inline via \"Create '<name>'\" — select it.\n\t\t *\n\t\t * @param {object} client The created client object.\n\t\t */\n\t\tonClientCreated(client) {\n\t\t\tconst id = String((client && (client.id || (client['@self'] && client['@self'].id))) || '')\n\t\t\tif (id) {\n\t\t\t\tthis.form.client = id\n\t\t\t\tthis.writeWorkspace('selectedClient', id)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a key into the workspace context, unless `content.writeWorkspace`\n\t\t * is false.\n\t\t *\n\t\t * Vue 2.7's Options-API `inject` auto-unwraps the provided ref, so the\n\t\t * holder is usually the plain reactive object; the raw-ref shape\n\t\t * (`.value`) is supported for Composition-API consumers.\n\t\t *\n\t\t * @param {string} key The context key.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\twriteWorkspace(key, value) {\n\t\t\tconst holder = this.cnWorkspaceContext\n\t\t\tif (!this.writesWorkspace || !holder || typeof holder !== 'object') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ('value' in holder) {\n\t\t\t\tholder.value = { ...(holder.value || {}), [key]: value }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tholder[key] = value\n\t\t},\n\n\t\t/**\n\t\t * Persist the contactmoment. The agent identity is left to the server\n\t\t * (never sent from the client). Surfaces errors inline.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onRegister() {\n\t\t\tthis.subjectError = ''\n\t\t\tthis.errorMessage = ''\n\t\t\tif (!this.form.subject || !this.form.subject.trim()) {\n\t\t\t\tthis.subjectError = t('nextcloud-vue', 'Subject is required')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.objectStore) {\n\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Cannot save right now')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst c = this.content || {}\n\t\t\t// `defaults` is spread FIRST so the mapped fields below always win.\n\t\t\t// It exists for schemas that need a fixed value on every create the\n\t\t\t// form itself has no input for — e.g. a discriminator on a supertype\n\t\t\t// schema (`{ ticketType: 'contactmoment' }`), which would otherwise be\n\t\t\t// omitted and fail validation.\n\t\t\tconst payload = {\n\t\t\t\t...(c.defaults || {}),\n\t\t\t\t[c.subjectField || 'subject']: this.form.subject.trim(),\n\t\t\t\t[c.channelField || 'channel']: this.form.channel,\n\t\t\t\t[c.contactedAtField || 'contactedAt']: new Date().toISOString(),\n\t\t\t}\n\t\t\tif (this.form.client) {\n\t\t\t\tpayload[c.clientField || 'client'] = this.form.client\n\t\t\t}\n\t\t\tif (this.form.summary) {\n\t\t\t\tpayload[c.summaryField || 'summary'] = this.form.summary\n\t\t\t}\n\t\t\tif (this.form.outcome) {\n\t\t\t\tpayload[c.outcomeField || 'outcome'] = this.form.outcome\n\t\t\t}\n\n\t\t\tthis.saving = true\n\t\t\tthis.saved = null\n\t\t\ttry {\n\t\t\t\tif (typeof this.objectStore.registerObjectType === 'function' && !this.objectStore.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tthis.objectStore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tconst result = await this.objectStore.saveObject(this.typeSlug, payload)\n\t\t\t\tif (!result) {\n\t\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event saved A contactmoment was persisted.\n\t\t\t\t * @type {object} The saved object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', result)\n\t\t\t\tthis.saved = {\n\t\t\t\t\tid: String(result.id || (result['@self'] && result['@self'].id) || ''),\n\t\t\t\t\ttitle: payload[c.subjectField || 'subject'],\n\t\t\t\t}\n\t\t\t\t// Reset the per-interaction fields; keep the client for follow-ups.\n\t\t\t\tthis.form.subject = ''\n\t\t\t\tthis.form.summary = ''\n\t\t\t\tthis.form.outcome = ''\n\t\t\t\tthis.writeWorkspace('activeSummary', '')\n\t\t\t} catch (e) {\n\t\t\t\tthis.errorMessage = (e && e.message) || t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<!--\n The scoped style block is gone on purpose. The markup now lives in\n CnFormWidgetBase, so a scoped rule here would carry THIS component's\n data-v attribute and match none of it. The rules moved verbatim to\n src/css/form-widget.css, keyed on `cn-form-widget*`; the base also emits\n the `cn-interaction-form-widget*` names on the same elements, so app CSS\n targeting those keeps matching.\n-->\n"],"names":["CnFormWidgetBase","CnResourceSelect","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnInteractionFormWidget.vue2.js","sources":["../../../../src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnFormWidgetBase\n\t\tblockClass=\"cn-interaction-form-widget\"\n\t\t:fields=\"formFields\"\n\t\t:model=\"form\"\n\t\t:errors=\"{ subject: subjectError }\"\n\t\t:canSubmit=\"canRegister\"\n\t\t:submitting=\"saving\"\n\t\t:submitLabel=\"registerLabel\"\n\t\t:submittingLabel=\"savingLabel\"\n\t\t:errorMessage=\"errorMessage\"\n\t\t@update:field=\"onFieldUpdate\"\n\t\t@submit=\"onRegister\">\n\t\t<!-- The client picker is the one control the base does not know about:\n\t\t a CnResourceSelect, so typing a name that does not exist yet offers\n\t\t \"Create '<name>'\" inline rather than a dead \"no results\" path. The\n\t\t base still supplies its field wrapper and spacing. -->\n\t\t<template #field-client>\n\t\t\t<CnResourceSelect\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"clientSchema\"\n\t\t\t\t:labelField=\"clientLabelField\"\n\t\t\t\t:modelValue=\"form.client\"\n\t\t\t\t:inputLabel=\"clientLabel\"\n\t\t\t\t:preload=\"true\"\n\t\t\t\t@update:modelValue=\"onClientChange\"\n\t\t\t\t@create=\"onClientCreated\" />\n\t\t</template>\n\n\t\t<!-- Opens what was just saved in a new tab, beside the submit button. -->\n\t\t<template #actions-start>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"savedHref\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:href=\"savedHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\tdata-testid=\"cn-interaction-form-open\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnFormWidgetBase>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport CnFormWidgetBase from '../CnFormWidgetBase/CnFormWidgetBase.vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport { useObjectStore } from '../../store/index.js'\n\n/**\n * CnInteractionFormWidget — the \"active interaction\" quick-log form of a\n * workspace page.\n *\n * Persists a contactmoment (channel / client / subject / summary / outcome) to\n * OpenRegister via `useObjectStore().saveObject`. It reads `selectedClient`\n * from the page-level workspace context (the page's client in focus, set by a\n * page-level picker) to pre-fill its Client field.\n *\n * By default it also writes the chosen client to `selectedClient` and the live\n * summary text to `activeSummary`, so sibling widgets react: a client list\n * filtered on `@workspace.selectedClient`, or a `CnKbSearchWidget` bound to\n * `activeSummary`. A page whose client in focus comes from its own picker sets\n * `writeWorkspace: false`, which keeps the form's values to the submission.\n *\n * The Client field belongs to this submission only. It is a\n * `CnResourceSelect`, so typing a name that doesn't exist yet offers\n * \"Create '<name>'\" inline — no dead \"no results\" path.\n *\n * Resolved by its registry type key `interaction-form`. All schema/field/enum\n * choices come from `content`, so the widget carries no app-specific vocabulary.\n *\n * Example content blob:\n * ```js\n * content: {\n * register: 'pipelinq',\n * schema: 'ticket',\n * defaults: { ticketType: 'contactmoment' }, // fixed fields stamped on every create\n * clientSchema: 'client',\n * clientField: 'client',\n * summaryField: 'description',\n * channels: [{ value: 'telefoon', label: 'Phone' }, …],\n * }\n * ```\n *\n * The outcome options are read from the target schema on load: the enum of\n * its `outcomeField` property, labelled by that property's `x-enum-labels`.\n * `outcomes: [{ value, label }]` is only a fallback for a schema that has no\n * enum there.\n *\n * With `detailRoute` set (a route name taking `:id`, like an object list's\n * `rowRoute`), a save puts an \"Open\" button left of the submit button that\n * opens the saved object in a new tab.\n *\n * `defaults` covers the case where the target schema requires a value the form\n * has no input for — typically a discriminator on a supertype schema. It is\n * merged before the mapped fields, so those always take precedence.\n */\nexport default {\n\tname: 'CnInteractionFormWidget',\n\n\tcomponents: { CnFormWidgetBase, CnResourceSelect, NcButton, OpenInNew },\n\n\tinject: {\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) from CnDashboardPage.\n\t\t * The widget reads `selectedClient` from it and, unless\n\t\t * `content.writeWorkspace` is false, writes `selectedClient` and\n\t\t * `activeSummary` back. Null on pages that don't provide one (the form\n\t\t * still saves).\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Host translate function from CnAppRoot, bound to the host app's id.\n\t\t * Translates the schema's outcome labels.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted configuration blob (see component description for the shape).\n\t\t *\n\t\t * @type {{register?: string, schema?: string, defaults?: object, clientSchema?: string, clientField?: string, clientLabelField?: string, subjectField?: string, summaryField?: string, channelField?: string, outcomeField?: string, channels?: Array, outcomes?: Array, submitLabel?: string, detailRoute?: string, writeWorkspace?: boolean}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\tform: {\n\t\t\t\tchannel: this.firstChannel(),\n\t\t\t\tclient: '',\n\t\t\t\tsubject: '',\n\t\t\t\tsummary: '',\n\t\t\t\toutcome: '',\n\t\t\t},\n\n\t\t\tsaving: false,\n\t\t\terrorMessage: '',\n\t\t\tsubjectError: '',\n\t\t\t// The target schema, fetched on load for its outcome enum.\n\t\t\tschemaDef: null,\n\t\t\t// The last saved object, `{ id, title }`, until the next save.\n\t\t\tsaved: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tregister() {\n\t\t\treturn this.content.register || 'pipelinq'\n\t\t},\n\n\t\tschema() {\n\t\t\treturn this.content.schema || 'contactmoment'\n\t\t},\n\n\t\tclientSchema() {\n\t\t\treturn this.content.clientSchema || 'client'\n\t\t},\n\n\t\tclientLabelField() {\n\t\t\treturn this.content.clientLabelField || 'name'\n\t\t},\n\n\t\ttypeSlug() {\n\t\t\treturn `${this.register}-${this.schema}`\n\t\t},\n\n\t\t/** Channel options `{ value, label }`. */\n\t\tchannelOptions() {\n\t\t\treturn Array.isArray(this.content.channels) && this.content.channels.length > 0\n\t\t\t\t? this.content.channels.map((channel) => ({ ...channel, label: this.copy(channel && channel.label) }))\n\t\t\t\t: [\n\t\t\t\t\t\t{ value: 'telefoon', label: t('nextcloud-vue', 'Phone') },\n\t\t\t\t\t\t{ value: 'email', label: t('nextcloud-vue', 'Email') },\n\t\t\t\t\t\t{ value: 'chat', label: t('nextcloud-vue', 'Chat') },\n\t\t\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * Outcome options `{ value, label }`, from the target schema's enum so\n\t\t * the form can only offer values the schema accepts. Labels come from\n\t\t * the property's `x-enum-labels`, translated. `content.outcomes` is the\n\t\t * fallback for a schema without an enum on that property.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\toutcomeOptions() {\n\t\t\tconst properties = (this.schemaDef && this.schemaDef.properties) || {}\n\t\t\tconst prop = properties[this.content.outcomeField || 'outcome']\n\t\t\tif (prop && Array.isArray(prop.enum) && prop.enum.length > 0) {\n\t\t\t\tconst labels = prop['x-enum-labels'] || prop.enumLabels || {}\n\t\t\t\treturn prop.enum.map((value) => ({ value, label: this.copy(labels[value] || String(value)) }))\n\t\t\t}\n\t\t\treturn Array.isArray(this.content.outcomes)\n\t\t\t\t? this.content.outcomes.map((outcome) => ({ ...outcome, label: this.copy(outcome && outcome.label) }))\n\t\t\t\t: []\n\t\t},\n\n\t\t/**\n\t\t * The field descriptors handed to CnFormWidgetBase — the whole of this\n\t\t * widget's form, declared rather than drawn. Order matters: it is the\n\t\t * render order.\n\t\t *\n\t\t * `client` is declared here so it keeps its place in the stack and its\n\t\t * field wrapper, but its control comes from the `#field-client` slot\n\t\t * (a CnResourceSelect, which the base has no type for).\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tformFields() {\n\t\t\treturn [\n\t\t\t\t{ key: 'channel', type: 'select', label: this.channelLabel, options: this.channelOptions },\n\t\t\t\t{ key: 'client', type: 'custom', label: this.clientLabel },\n\t\t\t\t{ key: 'subject', type: 'text', label: this.subjectLabel },\n\t\t\t\t{ key: 'summary', type: 'textarea', label: this.summaryLabel, rows: 4 },\n\t\t\t\t{ key: 'outcome', type: 'select', label: this.outcomeLabel, options: this.outcomeOptions, clearable: true },\n\t\t\t]\n\t\t},\n\n\t\tobjectStore() {\n\t\t\ttry {\n\t\t\t\treturn useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/** The page's client in focus, or an empty string. */\n\t\tpageClient() {\n\t\t\tconst id = this.workspaceCtx && this.workspaceCtx.selectedClient\n\t\t\treturn id ? String(id) : ''\n\t\t},\n\n\t\tcanRegister() {\n\t\t\treturn Boolean(this.form.subject && this.form.subject.trim() && this.form.channel)\n\t\t},\n\n\t\tchannelLabel() {\n\t\t\treturn t('nextcloud-vue', 'Channel')\n\t\t},\n\n\t\tclientLabel() {\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tsubjectLabel() {\n\t\t\treturn t('nextcloud-vue', 'Subject')\n\t\t},\n\n\t\tsummaryLabel() {\n\t\t\treturn t('nextcloud-vue', 'Summary')\n\t\t},\n\n\t\toutcomeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Outcome')\n\t\t},\n\n\t\t// `content.submitLabel` names what the form creates, e.g. \"Save contact moment\".\n\t\tregisterLabel() {\n\t\t\tif (this.content.submitLabel) {\n\t\t\t\treturn this.copy(this.content.submitLabel)\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Register')\n\t\t},\n\n\t\tsavingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Saving…')\n\t\t},\n\n\t\t/**\n\t\t * URL of the saved object's page, from `content.detailRoute`. Empty\n\t\t * without a route or router, and then no Open button is shown.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tsavedHref() {\n\t\t\tconst route = this.content.detailRoute\n\t\t\tif (!this.saved || !this.saved.id || !route || !this.$router) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn this.$router.resolve({ name: route, params: { id: this.saved.id } }).href || ''\n\t\t\t} catch {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t},\n\n\t\t// `escape: false` because the label renders through `{{ }}`, which escapes.\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open \"{title}\"', { title: (this.saved && this.saved.title) || '' }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the form writes its client and summary to the workspace context. */\n\t\twritesWorkspace() {\n\t\t\treturn this.content.writeWorkspace !== false\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A new page client pre-fills the field; the field stays editable.\n\t\tpageClient: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.form.client = id\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.loadSchema()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Fetch the target schema, which the outcome options derive from. A\n\t\t * failure leaves the `content.outcomes` fallback in place.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadSchema() {\n\t\t\tconst store = this.objectStore\n\t\t\tif (!store || typeof store.fetchSchema !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Re-registering an existing type resets its cached objects and schema.\n\t\t\t\tif (typeof store.registerObjectType === 'function' && !store.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tstore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tthis.schemaDef = await store.fetchSchema(this.typeSlug)\n\t\t\t} catch {\n\t\t\t\tthis.schemaDef = null\n\t\t\t}\n\t\t},\n\n\t\t/** The default channel value (first configured channel, else `telefoon`). */\n\t\t/**\n\t\t * A label the manifest supplies (a channel, an outcome, the submit\n\t\t * label), translated: the app's own translation first, since the words\n\t\t * are the app's, then the library's catalogue, then the text as\n\t\t * written. Before, a channel label was shown as written and the submit\n\t\t * label only reached the app's translate, so pipelinq showed \"Phone\"\n\t\t * and \"Save contact moment\" in English to a Dutch reader.\n\t\t *\n\t\t * @spec openspec/changes/interaction-form-fits-and-reads-dutch/specs/interaction-form-widget/spec.md#requirement-the-interaction-form-fits-its-card-and-reads-in-the-readers-language\n\t\t * @param {string} key The label as the manifest writes it.\n\t\t * @return {string} The translated label.\n\t\t */\n\t\tcopy(key) {\n\t\t\tif (typeof key !== 'string' || key === '') {\n\t\t\t\treturn key\n\t\t\t}\n\t\t\tconst host = typeof this.cnTranslate === 'function' ? this.cnTranslate(key) : key\n\t\t\tif (typeof host === 'string' && host !== '' && host !== key) {\n\t\t\t\treturn host\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', key)\n\t\t},\n\n\t\tfirstChannel() {\n\t\t\tconst ch = (this.content && this.content.channels) || []\n\t\t\treturn (Array.isArray(ch) && ch.length > 0 && ch[0].value) || 'telefoon'\n\t\t},\n\n\t\t/**\n\t\t * A field edit from CnFormWidgetBase. `summary` also streams into the\n\t\t * workspace context. `client` never arrives here: its control is the\n\t\t * `#field-client` slot, which calls onClientChange.\n\t\t *\n\t\t * @param {{key: string, value: unknown}} payload The changed field.\n\t\t * @return {void}\n\t\t */\n\t\tonFieldUpdate({ key, value }) {\n\t\t\tthis.form[key] = value\n\t\t\tif (key === 'summary') {\n\t\t\t\tthis.writeWorkspace('activeSummary', value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The submission's client, also written to the workspace context.\n\t\t *\n\t\t * @param {string} id The selected client id.\n\t\t */\n\t\tonClientChange(id) {\n\t\t\tthis.form.client = id || ''\n\t\t\tthis.writeWorkspace('selectedClient', this.form.client)\n\t\t},\n\n\t\t/**\n\t\t * A client created inline via \"Create '<name>'\" — select it.\n\t\t *\n\t\t * @param {object} client The created client object.\n\t\t */\n\t\tonClientCreated(client) {\n\t\t\tconst id = String((client && (client.id || (client['@self'] && client['@self'].id))) || '')\n\t\t\tif (id) {\n\t\t\t\tthis.form.client = id\n\t\t\t\tthis.writeWorkspace('selectedClient', id)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a key into the workspace context, unless `content.writeWorkspace`\n\t\t * is false.\n\t\t *\n\t\t * Vue 2.7's Options-API `inject` auto-unwraps the provided ref, so the\n\t\t * holder is usually the plain reactive object; the raw-ref shape\n\t\t * (`.value`) is supported for Composition-API consumers.\n\t\t *\n\t\t * @param {string} key The context key.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\twriteWorkspace(key, value) {\n\t\t\tconst holder = this.cnWorkspaceContext\n\t\t\tif (!this.writesWorkspace || !holder || typeof holder !== 'object') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ('value' in holder) {\n\t\t\t\tholder.value = { ...(holder.value || {}), [key]: value }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tholder[key] = value\n\t\t},\n\n\t\t/**\n\t\t * Persist the contactmoment. The agent identity is left to the server\n\t\t * (never sent from the client). Surfaces errors inline.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onRegister() {\n\t\t\tthis.subjectError = ''\n\t\t\tthis.errorMessage = ''\n\t\t\tif (!this.form.subject || !this.form.subject.trim()) {\n\t\t\t\tthis.subjectError = t('nextcloud-vue', 'Subject is required')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.objectStore) {\n\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Cannot save right now')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst c = this.content || {}\n\t\t\t// `defaults` is spread FIRST so the mapped fields below always win.\n\t\t\t// It exists for schemas that need a fixed value on every create the\n\t\t\t// form itself has no input for — e.g. a discriminator on a supertype\n\t\t\t// schema (`{ ticketType: 'contactmoment' }`), which would otherwise be\n\t\t\t// omitted and fail validation.\n\t\t\tconst payload = {\n\t\t\t\t...(c.defaults || {}),\n\t\t\t\t[c.subjectField || 'subject']: this.form.subject.trim(),\n\t\t\t\t[c.channelField || 'channel']: this.form.channel,\n\t\t\t\t[c.contactedAtField || 'contactedAt']: new Date().toISOString(),\n\t\t\t}\n\t\t\tif (this.form.client) {\n\t\t\t\tpayload[c.clientField || 'client'] = this.form.client\n\t\t\t}\n\t\t\tif (this.form.summary) {\n\t\t\t\tpayload[c.summaryField || 'summary'] = this.form.summary\n\t\t\t}\n\t\t\tif (this.form.outcome) {\n\t\t\t\tpayload[c.outcomeField || 'outcome'] = this.form.outcome\n\t\t\t}\n\n\t\t\tthis.saving = true\n\t\t\tthis.saved = null\n\t\t\ttry {\n\t\t\t\tif (typeof this.objectStore.registerObjectType === 'function' && !this.objectStore.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tthis.objectStore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tconst result = await this.objectStore.saveObject(this.typeSlug, payload)\n\t\t\t\tif (!result) {\n\t\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event saved A contactmoment was persisted.\n\t\t\t\t * @type {object} The saved object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', result)\n\t\t\t\tthis.saved = {\n\t\t\t\t\tid: String(result.id || (result['@self'] && result['@self'].id) || ''),\n\t\t\t\t\ttitle: payload[c.subjectField || 'subject'],\n\t\t\t\t}\n\t\t\t\t// Reset the per-interaction fields; keep the client for follow-ups.\n\t\t\t\tthis.form.subject = ''\n\t\t\t\tthis.form.summary = ''\n\t\t\t\tthis.form.outcome = ''\n\t\t\t\tthis.writeWorkspace('activeSummary', '')\n\t\t\t} catch (e) {\n\t\t\t\tthis.errorMessage = (e && e.message) || t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<!--\n The scoped style block is gone on purpose. The markup now lives in\n CnFormWidgetBase, so a scoped rule here would carry THIS component's\n data-v attribute and match none of it. The rules moved verbatim to\n src/css/form-widget.css, keyed on `cn-form-widget*`; the base also emits\n the `cn-interaction-form-widget*` names on the same elements, so app CSS\n targeting those keeps matching.\n-->\n"],"names":["CnFormWidgetBase","CnResourceSelect","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnInteractionFormWidget.vue3.js","sources":["../../../../src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue?vue&type=template&id=693645c3&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnFormWidgetBase\n\t\tblockClass=\"cn-interaction-form-widget\"\n\t\t:fields=\"formFields\"\n\t\t:model=\"form\"\n\t\t:errors=\"{ subject: subjectError }\"\n\t\t:canSubmit=\"canRegister\"\n\t\t:submitting=\"saving\"\n\t\t:submitLabel=\"registerLabel\"\n\t\t:submittingLabel=\"savingLabel\"\n\t\t:errorMessage=\"errorMessage\"\n\t\t@update:field=\"onFieldUpdate\"\n\t\t@submit=\"onRegister\">\n\t\t<!-- The client picker is the one control the base does not know about:\n\t\t a CnResourceSelect, so typing a name that does not exist yet offers\n\t\t \"Create '<name>'\" inline rather than a dead \"no results\" path. The\n\t\t base still supplies its field wrapper and spacing. -->\n\t\t<template #field-client>\n\t\t\t<CnResourceSelect\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"clientSchema\"\n\t\t\t\t:labelField=\"clientLabelField\"\n\t\t\t\t:modelValue=\"form.client\"\n\t\t\t\t:inputLabel=\"clientLabel\"\n\t\t\t\t:preload=\"true\"\n\t\t\t\t@update:modelValue=\"onClientChange\"\n\t\t\t\t@create=\"onClientCreated\" />\n\t\t</template>\n\n\t\t<!-- Opens what was just saved in a new tab, beside the submit button. -->\n\t\t<template #actions-start>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"savedHref\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:href=\"savedHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\tdata-testid=\"cn-interaction-form-open\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnFormWidgetBase>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport CnFormWidgetBase from '../CnFormWidgetBase/CnFormWidgetBase.vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport { useObjectStore } from '../../store/index.js'\n\n/**\n * CnInteractionFormWidget — the \"active interaction\" quick-log form of a\n * workspace page.\n *\n * Persists a contactmoment (channel / client / subject / summary / outcome) to\n * OpenRegister via `useObjectStore().saveObject`. It reads `selectedClient`\n * from the page-level workspace context (the page's client in focus, set by a\n * page-level picker) to pre-fill its Client field.\n *\n * By default it also writes the chosen client to `selectedClient` and the live\n * summary text to `activeSummary`, so sibling widgets react: a client list\n * filtered on `@workspace.selectedClient`, or a `CnKbSearchWidget` bound to\n * `activeSummary`. A page whose client in focus comes from its own picker sets\n * `writeWorkspace: false`, which keeps the form's values to the submission.\n *\n * The Client field belongs to this submission only. It is a\n * `CnResourceSelect`, so typing a name that doesn't exist yet offers\n * \"Create '<name>'\" inline — no dead \"no results\" path.\n *\n * Resolved by its registry type key `interaction-form`. All schema/field/enum\n * choices come from `content`, so the widget carries no app-specific vocabulary.\n *\n * Example content blob:\n * ```js\n * content: {\n * register: 'pipelinq',\n * schema: 'ticket',\n * defaults: { ticketType: 'contactmoment' }, // fixed fields stamped on every create\n * clientSchema: 'client',\n * clientField: 'client',\n * summaryField: 'description',\n * channels: [{ value: 'telefoon', label: 'Phone' }, …],\n * }\n * ```\n *\n * The outcome options are read from the target schema on load: the enum of\n * its `outcomeField` property, labelled by that property's `x-enum-labels`.\n * `outcomes: [{ value, label }]` is only a fallback for a schema that has no\n * enum there.\n *\n * With `detailRoute` set (a route name taking `:id`, like an object list's\n * `rowRoute`), a save puts an \"Open\" button left of the submit button that\n * opens the saved object in a new tab.\n *\n * `defaults` covers the case where the target schema requires a value the form\n * has no input for — typically a discriminator on a supertype schema. It is\n * merged before the mapped fields, so those always take precedence.\n */\nexport default {\n\tname: 'CnInteractionFormWidget',\n\n\tcomponents: { CnFormWidgetBase, CnResourceSelect, NcButton, OpenInNew },\n\n\tinject: {\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) from CnDashboardPage.\n\t\t * The widget reads `selectedClient` from it and, unless\n\t\t * `content.writeWorkspace` is false, writes `selectedClient` and\n\t\t * `activeSummary` back. Null on pages that don't provide one (the form\n\t\t * still saves).\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Host translate function from CnAppRoot, bound to the host app's id.\n\t\t * Translates the schema's outcome labels.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted configuration blob (see component description for the shape).\n\t\t *\n\t\t * @type {{register?: string, schema?: string, defaults?: object, clientSchema?: string, clientField?: string, clientLabelField?: string, subjectField?: string, summaryField?: string, channelField?: string, outcomeField?: string, channels?: Array, outcomes?: Array, submitLabel?: string, detailRoute?: string, writeWorkspace?: boolean}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\tform: {\n\t\t\t\tchannel: this.firstChannel(),\n\t\t\t\tclient: '',\n\t\t\t\tsubject: '',\n\t\t\t\tsummary: '',\n\t\t\t\toutcome: '',\n\t\t\t},\n\n\t\t\tsaving: false,\n\t\t\terrorMessage: '',\n\t\t\tsubjectError: '',\n\t\t\t// The target schema, fetched on load for its outcome enum.\n\t\t\tschemaDef: null,\n\t\t\t// The last saved object, `{ id, title }`, until the next save.\n\t\t\tsaved: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tregister() {\n\t\t\treturn this.content.register || 'pipelinq'\n\t\t},\n\n\t\tschema() {\n\t\t\treturn this.content.schema || 'contactmoment'\n\t\t},\n\n\t\tclientSchema() {\n\t\t\treturn this.content.clientSchema || 'client'\n\t\t},\n\n\t\tclientLabelField() {\n\t\t\treturn this.content.clientLabelField || 'name'\n\t\t},\n\n\t\ttypeSlug() {\n\t\t\treturn `${this.register}-${this.schema}`\n\t\t},\n\n\t\t/** Channel options `{ value, label }`. */\n\t\tchannelOptions() {\n\t\t\treturn Array.isArray(this.content.channels) && this.content.channels.length > 0\n\t\t\t\t? this.content.channels\n\t\t\t\t: [\n\t\t\t\t\t\t{ value: 'telefoon', label: t('nextcloud-vue', 'Phone') },\n\t\t\t\t\t\t{ value: 'email', label: t('nextcloud-vue', 'Email') },\n\t\t\t\t\t\t{ value: 'chat', label: t('nextcloud-vue', 'Chat') },\n\t\t\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * Outcome options `{ value, label }`, from the target schema's enum so\n\t\t * the form can only offer values the schema accepts. Labels come from\n\t\t * the property's `x-enum-labels`, translated. `content.outcomes` is the\n\t\t * fallback for a schema without an enum on that property.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\toutcomeOptions() {\n\t\t\tconst properties = (this.schemaDef && this.schemaDef.properties) || {}\n\t\t\tconst prop = properties[this.content.outcomeField || 'outcome']\n\t\t\tif (prop && Array.isArray(prop.enum) && prop.enum.length > 0) {\n\t\t\t\tconst labels = prop['x-enum-labels'] || prop.enumLabels || {}\n\t\t\t\tconst tr = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\t\treturn prop.enum.map((value) => ({ value, label: tr(labels[value] || String(value)) }))\n\t\t\t}\n\t\t\treturn Array.isArray(this.content.outcomes) ? this.content.outcomes : []\n\t\t},\n\n\t\t/**\n\t\t * The field descriptors handed to CnFormWidgetBase — the whole of this\n\t\t * widget's form, declared rather than drawn. Order matters: it is the\n\t\t * render order.\n\t\t *\n\t\t * `client` is declared here so it keeps its place in the stack and its\n\t\t * field wrapper, but its control comes from the `#field-client` slot\n\t\t * (a CnResourceSelect, which the base has no type for).\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tformFields() {\n\t\t\treturn [\n\t\t\t\t{ key: 'channel', type: 'select', label: this.channelLabel, options: this.channelOptions },\n\t\t\t\t{ key: 'client', type: 'custom', label: this.clientLabel },\n\t\t\t\t{ key: 'subject', type: 'text', label: this.subjectLabel },\n\t\t\t\t{ key: 'summary', type: 'textarea', label: this.summaryLabel, rows: 4 },\n\t\t\t\t{ key: 'outcome', type: 'select', label: this.outcomeLabel, options: this.outcomeOptions, clearable: true },\n\t\t\t]\n\t\t},\n\n\t\tobjectStore() {\n\t\t\ttry {\n\t\t\t\treturn useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/** The page's client in focus, or an empty string. */\n\t\tpageClient() {\n\t\t\tconst id = this.workspaceCtx && this.workspaceCtx.selectedClient\n\t\t\treturn id ? String(id) : ''\n\t\t},\n\n\t\tcanRegister() {\n\t\t\treturn Boolean(this.form.subject && this.form.subject.trim() && this.form.channel)\n\t\t},\n\n\t\tchannelLabel() {\n\t\t\treturn t('nextcloud-vue', 'Channel')\n\t\t},\n\n\t\tclientLabel() {\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tsubjectLabel() {\n\t\t\treturn t('nextcloud-vue', 'Subject')\n\t\t},\n\n\t\tsummaryLabel() {\n\t\t\treturn t('nextcloud-vue', 'Summary')\n\t\t},\n\n\t\toutcomeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Outcome')\n\t\t},\n\n\t\t// `content.submitLabel` names what the form creates, e.g. \"Save contact moment\".\n\t\tregisterLabel() {\n\t\t\tif (this.content.submitLabel) {\n\t\t\t\tconst tr = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\t\treturn tr(this.content.submitLabel)\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Register')\n\t\t},\n\n\t\tsavingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Saving…')\n\t\t},\n\n\t\t/**\n\t\t * URL of the saved object's page, from `content.detailRoute`. Empty\n\t\t * without a route or router, and then no Open button is shown.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tsavedHref() {\n\t\t\tconst route = this.content.detailRoute\n\t\t\tif (!this.saved || !this.saved.id || !route || !this.$router) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn this.$router.resolve({ name: route, params: { id: this.saved.id } }).href || ''\n\t\t\t} catch {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t},\n\n\t\t// `escape: false` because the label renders through `{{ }}`, which escapes.\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open \"{title}\"', { title: (this.saved && this.saved.title) || '' }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the form writes its client and summary to the workspace context. */\n\t\twritesWorkspace() {\n\t\t\treturn this.content.writeWorkspace !== false\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A new page client pre-fills the field; the field stays editable.\n\t\tpageClient: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.form.client = id\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.loadSchema()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Fetch the target schema, which the outcome options derive from. A\n\t\t * failure leaves the `content.outcomes` fallback in place.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadSchema() {\n\t\t\tconst store = this.objectStore\n\t\t\tif (!store || typeof store.fetchSchema !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Re-registering an existing type resets its cached objects and schema.\n\t\t\t\tif (typeof store.registerObjectType === 'function' && !store.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tstore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tthis.schemaDef = await store.fetchSchema(this.typeSlug)\n\t\t\t} catch {\n\t\t\t\tthis.schemaDef = null\n\t\t\t}\n\t\t},\n\n\t\t/** The default channel value (first configured channel, else `telefoon`). */\n\t\tfirstChannel() {\n\t\t\tconst ch = (this.content && this.content.channels) || []\n\t\t\treturn (Array.isArray(ch) && ch.length > 0 && ch[0].value) || 'telefoon'\n\t\t},\n\n\t\t/**\n\t\t * A field edit from CnFormWidgetBase. `summary` also streams into the\n\t\t * workspace context. `client` never arrives here: its control is the\n\t\t * `#field-client` slot, which calls onClientChange.\n\t\t *\n\t\t * @param {{key: string, value: unknown}} payload The changed field.\n\t\t * @return {void}\n\t\t */\n\t\tonFieldUpdate({ key, value }) {\n\t\t\tthis.form[key] = value\n\t\t\tif (key === 'summary') {\n\t\t\t\tthis.writeWorkspace('activeSummary', value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The submission's client, also written to the workspace context.\n\t\t *\n\t\t * @param {string} id The selected client id.\n\t\t */\n\t\tonClientChange(id) {\n\t\t\tthis.form.client = id || ''\n\t\t\tthis.writeWorkspace('selectedClient', this.form.client)\n\t\t},\n\n\t\t/**\n\t\t * A client created inline via \"Create '<name>'\" — select it.\n\t\t *\n\t\t * @param {object} client The created client object.\n\t\t */\n\t\tonClientCreated(client) {\n\t\t\tconst id = String((client && (client.id || (client['@self'] && client['@self'].id))) || '')\n\t\t\tif (id) {\n\t\t\t\tthis.form.client = id\n\t\t\t\tthis.writeWorkspace('selectedClient', id)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a key into the workspace context, unless `content.writeWorkspace`\n\t\t * is false.\n\t\t *\n\t\t * Vue 2.7's Options-API `inject` auto-unwraps the provided ref, so the\n\t\t * holder is usually the plain reactive object; the raw-ref shape\n\t\t * (`.value`) is supported for Composition-API consumers.\n\t\t *\n\t\t * @param {string} key The context key.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\twriteWorkspace(key, value) {\n\t\t\tconst holder = this.cnWorkspaceContext\n\t\t\tif (!this.writesWorkspace || !holder || typeof holder !== 'object') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ('value' in holder) {\n\t\t\t\tholder.value = { ...(holder.value || {}), [key]: value }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tholder[key] = value\n\t\t},\n\n\t\t/**\n\t\t * Persist the contactmoment. The agent identity is left to the server\n\t\t * (never sent from the client). Surfaces errors inline.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onRegister() {\n\t\t\tthis.subjectError = ''\n\t\t\tthis.errorMessage = ''\n\t\t\tif (!this.form.subject || !this.form.subject.trim()) {\n\t\t\t\tthis.subjectError = t('nextcloud-vue', 'Subject is required')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.objectStore) {\n\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Cannot save right now')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst c = this.content || {}\n\t\t\t// `defaults` is spread FIRST so the mapped fields below always win.\n\t\t\t// It exists for schemas that need a fixed value on every create the\n\t\t\t// form itself has no input for — e.g. a discriminator on a supertype\n\t\t\t// schema (`{ ticketType: 'contactmoment' }`), which would otherwise be\n\t\t\t// omitted and fail validation.\n\t\t\tconst payload = {\n\t\t\t\t...(c.defaults || {}),\n\t\t\t\t[c.subjectField || 'subject']: this.form.subject.trim(),\n\t\t\t\t[c.channelField || 'channel']: this.form.channel,\n\t\t\t\t[c.contactedAtField || 'contactedAt']: new Date().toISOString(),\n\t\t\t}\n\t\t\tif (this.form.client) {\n\t\t\t\tpayload[c.clientField || 'client'] = this.form.client\n\t\t\t}\n\t\t\tif (this.form.summary) {\n\t\t\t\tpayload[c.summaryField || 'summary'] = this.form.summary\n\t\t\t}\n\t\t\tif (this.form.outcome) {\n\t\t\t\tpayload[c.outcomeField || 'outcome'] = this.form.outcome\n\t\t\t}\n\n\t\t\tthis.saving = true\n\t\t\tthis.saved = null\n\t\t\ttry {\n\t\t\t\tif (typeof this.objectStore.registerObjectType === 'function' && !this.objectStore.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tthis.objectStore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tconst result = await this.objectStore.saveObject(this.typeSlug, payload)\n\t\t\t\tif (!result) {\n\t\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event saved A contactmoment was persisted.\n\t\t\t\t * @type {object} The saved object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', result)\n\t\t\t\tthis.saved = {\n\t\t\t\t\tid: String(result.id || (result['@self'] && result['@self'].id) || ''),\n\t\t\t\t\ttitle: payload[c.subjectField || 'subject'],\n\t\t\t\t}\n\t\t\t\t// Reset the per-interaction fields; keep the client for follow-ups.\n\t\t\t\tthis.form.subject = ''\n\t\t\t\tthis.form.summary = ''\n\t\t\t\tthis.form.outcome = ''\n\t\t\t\tthis.writeWorkspace('activeSummary', '')\n\t\t\t} catch (e) {\n\t\t\t\tthis.errorMessage = (e && e.message) || t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<!--\n The scoped style block is gone on purpose. The markup now lives in\n CnFormWidgetBase, so a scoped rule here would carry THIS component's\n data-v attribute and match none of it. The rules moved verbatim to\n src/css/form-widget.css, keyed on `cn-form-widget*`; the base also emits\n the `cn-interaction-form-widget*` names on the same elements, so app CSS\n targeting those keeps matching.\n-->\n"],"names":["_createBlock","_createVNode","_createTextVNode"],"mappings":";;;;;;;;uBASEA;AACA,IAAA,UAAU,EAAC,4BAAY;AACtB,IAAA,MAAM,EAAE,QAAU,CAAA,UAAA;AAClB,IAAA,KAAK,EAAE,KAAI,CAAA,IAAA;AACX,IAAA,MAAM,aAAa,KAAS,CAAA,YAAA,EAAA;AAC5B,IAAA,SAAS,EAAE,QAAW,CAAA,WAAA;AACtB,IAAA,UAAU,EAAE,KAAM,CAAA,MAAA;AAClB,IAAA,WAAW,EAAE,QAAM,CAAA,aAAA;AACnB,IAAA,eAAe,EAAE,QAAU,CAAA,WAAA;gBACf,EAAE,KAAW,CAAA,YAAA;oBACb,EAAE,QAAa,CAAA,aAAA;AACvB,IAAA,QAAE,EAAC,QAAW,CAAA,UAAA;;AAKR,IAAA,cAAY,gBASC;iBAAA,CAAA,2BAAA,EAAA;AAPrB,QAAA,QAAQ,EAAE,QAAQ,CAAA,QAAA;AAClB,QAAA,MAAM,EAAE,QAAK,CAAA,YAAA;AACb,QAAA,UAAU,EAAE,QAAgB,CAAA,gBAAA;QAC5B,UAAU,EAAE,KAAI,CAAA,IAAA,CAAC,MAAM;AACvB,QAAA,UAAO,EAAA,QAAA,CAAA,WAAA;;AAEP,QAAA,qBAAiB,EAAE,QAAc,CAAA,cAAA;AACjC,QAAA,UAAQ,QAAa,CAAA,eAAA;;;AAIb,IAAA,eAAO,UAChB;OACM,QAAS,CAAA,SAAA;wBADfA;;AAEA,YAAA,OAAO,EAAC,UAAO;AACd,YAAA,IAAI,EAAE,QAAS,CAAA,SAAA;AAChB,YAAA,MAAM,EAAC,QAAG;AACV,YAAA,aAAW,EAAA,0BAAG;;AACL,YAAA,IAAA,UACR,MAAO;cAAPC,WAAO,CAAA,oBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;6BACG;AAAA,cAAAC,eAAA,CAAA;;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnInteractionFormWidget.vue3.js","sources":["../../../../src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue?vue&type=template&id=fc6b3252&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnFormWidgetBase\n\t\tblockClass=\"cn-interaction-form-widget\"\n\t\t:fields=\"formFields\"\n\t\t:model=\"form\"\n\t\t:errors=\"{ subject: subjectError }\"\n\t\t:canSubmit=\"canRegister\"\n\t\t:submitting=\"saving\"\n\t\t:submitLabel=\"registerLabel\"\n\t\t:submittingLabel=\"savingLabel\"\n\t\t:errorMessage=\"errorMessage\"\n\t\t@update:field=\"onFieldUpdate\"\n\t\t@submit=\"onRegister\">\n\t\t<!-- The client picker is the one control the base does not know about:\n\t\t a CnResourceSelect, so typing a name that does not exist yet offers\n\t\t \"Create '<name>'\" inline rather than a dead \"no results\" path. The\n\t\t base still supplies its field wrapper and spacing. -->\n\t\t<template #field-client>\n\t\t\t<CnResourceSelect\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"clientSchema\"\n\t\t\t\t:labelField=\"clientLabelField\"\n\t\t\t\t:modelValue=\"form.client\"\n\t\t\t\t:inputLabel=\"clientLabel\"\n\t\t\t\t:preload=\"true\"\n\t\t\t\t@update:modelValue=\"onClientChange\"\n\t\t\t\t@create=\"onClientCreated\" />\n\t\t</template>\n\n\t\t<!-- Opens what was just saved in a new tab, beside the submit button. -->\n\t\t<template #actions-start>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"savedHref\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:href=\"savedHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\tdata-testid=\"cn-interaction-form-open\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnFormWidgetBase>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport CnFormWidgetBase from '../CnFormWidgetBase/CnFormWidgetBase.vue'\nimport CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'\nimport { useObjectStore } from '../../store/index.js'\n\n/**\n * CnInteractionFormWidget — the \"active interaction\" quick-log form of a\n * workspace page.\n *\n * Persists a contactmoment (channel / client / subject / summary / outcome) to\n * OpenRegister via `useObjectStore().saveObject`. It reads `selectedClient`\n * from the page-level workspace context (the page's client in focus, set by a\n * page-level picker) to pre-fill its Client field.\n *\n * By default it also writes the chosen client to `selectedClient` and the live\n * summary text to `activeSummary`, so sibling widgets react: a client list\n * filtered on `@workspace.selectedClient`, or a `CnKbSearchWidget` bound to\n * `activeSummary`. A page whose client in focus comes from its own picker sets\n * `writeWorkspace: false`, which keeps the form's values to the submission.\n *\n * The Client field belongs to this submission only. It is a\n * `CnResourceSelect`, so typing a name that doesn't exist yet offers\n * \"Create '<name>'\" inline — no dead \"no results\" path.\n *\n * Resolved by its registry type key `interaction-form`. All schema/field/enum\n * choices come from `content`, so the widget carries no app-specific vocabulary.\n *\n * Example content blob:\n * ```js\n * content: {\n * register: 'pipelinq',\n * schema: 'ticket',\n * defaults: { ticketType: 'contactmoment' }, // fixed fields stamped on every create\n * clientSchema: 'client',\n * clientField: 'client',\n * summaryField: 'description',\n * channels: [{ value: 'telefoon', label: 'Phone' }, …],\n * }\n * ```\n *\n * The outcome options are read from the target schema on load: the enum of\n * its `outcomeField` property, labelled by that property's `x-enum-labels`.\n * `outcomes: [{ value, label }]` is only a fallback for a schema that has no\n * enum there.\n *\n * With `detailRoute` set (a route name taking `:id`, like an object list's\n * `rowRoute`), a save puts an \"Open\" button left of the submit button that\n * opens the saved object in a new tab.\n *\n * `defaults` covers the case where the target schema requires a value the form\n * has no input for — typically a discriminator on a supertype schema. It is\n * merged before the mapped fields, so those always take precedence.\n */\nexport default {\n\tname: 'CnInteractionFormWidget',\n\n\tcomponents: { CnFormWidgetBase, CnResourceSelect, NcButton, OpenInNew },\n\n\tinject: {\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) from CnDashboardPage.\n\t\t * The widget reads `selectedClient` from it and, unless\n\t\t * `content.writeWorkspace` is false, writes `selectedClient` and\n\t\t * `activeSummary` back. Null on pages that don't provide one (the form\n\t\t * still saves).\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Host translate function from CnAppRoot, bound to the host app's id.\n\t\t * Translates the schema's outcome labels.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted configuration blob (see component description for the shape).\n\t\t *\n\t\t * @type {{register?: string, schema?: string, defaults?: object, clientSchema?: string, clientField?: string, clientLabelField?: string, subjectField?: string, summaryField?: string, channelField?: string, outcomeField?: string, channels?: Array, outcomes?: Array, submitLabel?: string, detailRoute?: string, writeWorkspace?: boolean}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\tform: {\n\t\t\t\tchannel: this.firstChannel(),\n\t\t\t\tclient: '',\n\t\t\t\tsubject: '',\n\t\t\t\tsummary: '',\n\t\t\t\toutcome: '',\n\t\t\t},\n\n\t\t\tsaving: false,\n\t\t\terrorMessage: '',\n\t\t\tsubjectError: '',\n\t\t\t// The target schema, fetched on load for its outcome enum.\n\t\t\tschemaDef: null,\n\t\t\t// The last saved object, `{ id, title }`, until the next save.\n\t\t\tsaved: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tregister() {\n\t\t\treturn this.content.register || 'pipelinq'\n\t\t},\n\n\t\tschema() {\n\t\t\treturn this.content.schema || 'contactmoment'\n\t\t},\n\n\t\tclientSchema() {\n\t\t\treturn this.content.clientSchema || 'client'\n\t\t},\n\n\t\tclientLabelField() {\n\t\t\treturn this.content.clientLabelField || 'name'\n\t\t},\n\n\t\ttypeSlug() {\n\t\t\treturn `${this.register}-${this.schema}`\n\t\t},\n\n\t\t/** Channel options `{ value, label }`. */\n\t\tchannelOptions() {\n\t\t\treturn Array.isArray(this.content.channels) && this.content.channels.length > 0\n\t\t\t\t? this.content.channels.map((channel) => ({ ...channel, label: this.copy(channel && channel.label) }))\n\t\t\t\t: [\n\t\t\t\t\t\t{ value: 'telefoon', label: t('nextcloud-vue', 'Phone') },\n\t\t\t\t\t\t{ value: 'email', label: t('nextcloud-vue', 'Email') },\n\t\t\t\t\t\t{ value: 'chat', label: t('nextcloud-vue', 'Chat') },\n\t\t\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * Outcome options `{ value, label }`, from the target schema's enum so\n\t\t * the form can only offer values the schema accepts. Labels come from\n\t\t * the property's `x-enum-labels`, translated. `content.outcomes` is the\n\t\t * fallback for a schema without an enum on that property.\n\t\t *\n\t\t * @return {Array<{value: string, label: string}>}\n\t\t */\n\t\toutcomeOptions() {\n\t\t\tconst properties = (this.schemaDef && this.schemaDef.properties) || {}\n\t\t\tconst prop = properties[this.content.outcomeField || 'outcome']\n\t\t\tif (prop && Array.isArray(prop.enum) && prop.enum.length > 0) {\n\t\t\t\tconst labels = prop['x-enum-labels'] || prop.enumLabels || {}\n\t\t\t\treturn prop.enum.map((value) => ({ value, label: this.copy(labels[value] || String(value)) }))\n\t\t\t}\n\t\t\treturn Array.isArray(this.content.outcomes)\n\t\t\t\t? this.content.outcomes.map((outcome) => ({ ...outcome, label: this.copy(outcome && outcome.label) }))\n\t\t\t\t: []\n\t\t},\n\n\t\t/**\n\t\t * The field descriptors handed to CnFormWidgetBase — the whole of this\n\t\t * widget's form, declared rather than drawn. Order matters: it is the\n\t\t * render order.\n\t\t *\n\t\t * `client` is declared here so it keeps its place in the stack and its\n\t\t * field wrapper, but its control comes from the `#field-client` slot\n\t\t * (a CnResourceSelect, which the base has no type for).\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tformFields() {\n\t\t\treturn [\n\t\t\t\t{ key: 'channel', type: 'select', label: this.channelLabel, options: this.channelOptions },\n\t\t\t\t{ key: 'client', type: 'custom', label: this.clientLabel },\n\t\t\t\t{ key: 'subject', type: 'text', label: this.subjectLabel },\n\t\t\t\t{ key: 'summary', type: 'textarea', label: this.summaryLabel, rows: 4 },\n\t\t\t\t{ key: 'outcome', type: 'select', label: this.outcomeLabel, options: this.outcomeOptions, clearable: true },\n\t\t\t]\n\t\t},\n\n\t\tobjectStore() {\n\t\t\ttry {\n\t\t\t\treturn useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/** The page's client in focus, or an empty string. */\n\t\tpageClient() {\n\t\t\tconst id = this.workspaceCtx && this.workspaceCtx.selectedClient\n\t\t\treturn id ? String(id) : ''\n\t\t},\n\n\t\tcanRegister() {\n\t\t\treturn Boolean(this.form.subject && this.form.subject.trim() && this.form.channel)\n\t\t},\n\n\t\tchannelLabel() {\n\t\t\treturn t('nextcloud-vue', 'Channel')\n\t\t},\n\n\t\tclientLabel() {\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tsubjectLabel() {\n\t\t\treturn t('nextcloud-vue', 'Subject')\n\t\t},\n\n\t\tsummaryLabel() {\n\t\t\treturn t('nextcloud-vue', 'Summary')\n\t\t},\n\n\t\toutcomeLabel() {\n\t\t\treturn t('nextcloud-vue', 'Outcome')\n\t\t},\n\n\t\t// `content.submitLabel` names what the form creates, e.g. \"Save contact moment\".\n\t\tregisterLabel() {\n\t\t\tif (this.content.submitLabel) {\n\t\t\t\treturn this.copy(this.content.submitLabel)\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Register')\n\t\t},\n\n\t\tsavingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Saving…')\n\t\t},\n\n\t\t/**\n\t\t * URL of the saved object's page, from `content.detailRoute`. Empty\n\t\t * without a route or router, and then no Open button is shown.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tsavedHref() {\n\t\t\tconst route = this.content.detailRoute\n\t\t\tif (!this.saved || !this.saved.id || !route || !this.$router) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn this.$router.resolve({ name: route, params: { id: this.saved.id } }).href || ''\n\t\t\t} catch {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t},\n\n\t\t// `escape: false` because the label renders through `{{ }}`, which escapes.\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open \"{title}\"', { title: (this.saved && this.saved.title) || '' }, undefined, { escape: false })\n\t\t},\n\n\t\t/** Whether the form writes its client and summary to the workspace context. */\n\t\twritesWorkspace() {\n\t\t\treturn this.content.writeWorkspace !== false\n\t\t},\n\t},\n\n\twatch: {\n\t\t// A new page client pre-fills the field; the field stays editable.\n\t\tpageClient: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.form.client = id\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.loadSchema()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Fetch the target schema, which the outcome options derive from. A\n\t\t * failure leaves the `content.outcomes` fallback in place.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadSchema() {\n\t\t\tconst store = this.objectStore\n\t\t\tif (!store || typeof store.fetchSchema !== 'function') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// Re-registering an existing type resets its cached objects and schema.\n\t\t\t\tif (typeof store.registerObjectType === 'function' && !store.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tstore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tthis.schemaDef = await store.fetchSchema(this.typeSlug)\n\t\t\t} catch {\n\t\t\t\tthis.schemaDef = null\n\t\t\t}\n\t\t},\n\n\t\t/** The default channel value (first configured channel, else `telefoon`). */\n\t\t/**\n\t\t * A label the manifest supplies (a channel, an outcome, the submit\n\t\t * label), translated: the app's own translation first, since the words\n\t\t * are the app's, then the library's catalogue, then the text as\n\t\t * written. Before, a channel label was shown as written and the submit\n\t\t * label only reached the app's translate, so pipelinq showed \"Phone\"\n\t\t * and \"Save contact moment\" in English to a Dutch reader.\n\t\t *\n\t\t * @spec openspec/changes/interaction-form-fits-and-reads-dutch/specs/interaction-form-widget/spec.md#requirement-the-interaction-form-fits-its-card-and-reads-in-the-readers-language\n\t\t * @param {string} key The label as the manifest writes it.\n\t\t * @return {string} The translated label.\n\t\t */\n\t\tcopy(key) {\n\t\t\tif (typeof key !== 'string' || key === '') {\n\t\t\t\treturn key\n\t\t\t}\n\t\t\tconst host = typeof this.cnTranslate === 'function' ? this.cnTranslate(key) : key\n\t\t\tif (typeof host === 'string' && host !== '' && host !== key) {\n\t\t\t\treturn host\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', key)\n\t\t},\n\n\t\tfirstChannel() {\n\t\t\tconst ch = (this.content && this.content.channels) || []\n\t\t\treturn (Array.isArray(ch) && ch.length > 0 && ch[0].value) || 'telefoon'\n\t\t},\n\n\t\t/**\n\t\t * A field edit from CnFormWidgetBase. `summary` also streams into the\n\t\t * workspace context. `client` never arrives here: its control is the\n\t\t * `#field-client` slot, which calls onClientChange.\n\t\t *\n\t\t * @param {{key: string, value: unknown}} payload The changed field.\n\t\t * @return {void}\n\t\t */\n\t\tonFieldUpdate({ key, value }) {\n\t\t\tthis.form[key] = value\n\t\t\tif (key === 'summary') {\n\t\t\t\tthis.writeWorkspace('activeSummary', value)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The submission's client, also written to the workspace context.\n\t\t *\n\t\t * @param {string} id The selected client id.\n\t\t */\n\t\tonClientChange(id) {\n\t\t\tthis.form.client = id || ''\n\t\t\tthis.writeWorkspace('selectedClient', this.form.client)\n\t\t},\n\n\t\t/**\n\t\t * A client created inline via \"Create '<name>'\" — select it.\n\t\t *\n\t\t * @param {object} client The created client object.\n\t\t */\n\t\tonClientCreated(client) {\n\t\t\tconst id = String((client && (client.id || (client['@self'] && client['@self'].id))) || '')\n\t\t\tif (id) {\n\t\t\t\tthis.form.client = id\n\t\t\t\tthis.writeWorkspace('selectedClient', id)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write a key into the workspace context, unless `content.writeWorkspace`\n\t\t * is false.\n\t\t *\n\t\t * Vue 2.7's Options-API `inject` auto-unwraps the provided ref, so the\n\t\t * holder is usually the plain reactive object; the raw-ref shape\n\t\t * (`.value`) is supported for Composition-API consumers.\n\t\t *\n\t\t * @param {string} key The context key.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\twriteWorkspace(key, value) {\n\t\t\tconst holder = this.cnWorkspaceContext\n\t\t\tif (!this.writesWorkspace || !holder || typeof holder !== 'object') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif ('value' in holder) {\n\t\t\t\tholder.value = { ...(holder.value || {}), [key]: value }\n\t\t\t\treturn\n\t\t\t}\n\t\t\tholder[key] = value\n\t\t},\n\n\t\t/**\n\t\t * Persist the contactmoment. The agent identity is left to the server\n\t\t * (never sent from the client). Surfaces errors inline.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onRegister() {\n\t\t\tthis.subjectError = ''\n\t\t\tthis.errorMessage = ''\n\t\t\tif (!this.form.subject || !this.form.subject.trim()) {\n\t\t\t\tthis.subjectError = t('nextcloud-vue', 'Subject is required')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!this.objectStore) {\n\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Cannot save right now')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst c = this.content || {}\n\t\t\t// `defaults` is spread FIRST so the mapped fields below always win.\n\t\t\t// It exists for schemas that need a fixed value on every create the\n\t\t\t// form itself has no input for — e.g. a discriminator on a supertype\n\t\t\t// schema (`{ ticketType: 'contactmoment' }`), which would otherwise be\n\t\t\t// omitted and fail validation.\n\t\t\tconst payload = {\n\t\t\t\t...(c.defaults || {}),\n\t\t\t\t[c.subjectField || 'subject']: this.form.subject.trim(),\n\t\t\t\t[c.channelField || 'channel']: this.form.channel,\n\t\t\t\t[c.contactedAtField || 'contactedAt']: new Date().toISOString(),\n\t\t\t}\n\t\t\tif (this.form.client) {\n\t\t\t\tpayload[c.clientField || 'client'] = this.form.client\n\t\t\t}\n\t\t\tif (this.form.summary) {\n\t\t\t\tpayload[c.summaryField || 'summary'] = this.form.summary\n\t\t\t}\n\t\t\tif (this.form.outcome) {\n\t\t\t\tpayload[c.outcomeField || 'outcome'] = this.form.outcome\n\t\t\t}\n\n\t\t\tthis.saving = true\n\t\t\tthis.saved = null\n\t\t\ttry {\n\t\t\t\tif (typeof this.objectStore.registerObjectType === 'function' && !this.objectStore.objectTypeRegistry?.[this.typeSlug]) {\n\t\t\t\t\tthis.objectStore.registerObjectType(this.typeSlug, this.schema, this.register)\n\t\t\t\t}\n\t\t\t\tconst result = await this.objectStore.saveObject(this.typeSlug, payload)\n\t\t\t\tif (!result) {\n\t\t\t\t\tthis.errorMessage = t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event saved A contactmoment was persisted.\n\t\t\t\t * @type {object} The saved object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', result)\n\t\t\t\tthis.saved = {\n\t\t\t\t\tid: String(result.id || (result['@self'] && result['@self'].id) || ''),\n\t\t\t\t\ttitle: payload[c.subjectField || 'subject'],\n\t\t\t\t}\n\t\t\t\t// Reset the per-interaction fields; keep the client for follow-ups.\n\t\t\t\tthis.form.subject = ''\n\t\t\t\tthis.form.summary = ''\n\t\t\t\tthis.form.outcome = ''\n\t\t\t\tthis.writeWorkspace('activeSummary', '')\n\t\t\t} catch (e) {\n\t\t\t\tthis.errorMessage = (e && e.message) || t('nextcloud-vue', 'Failed to save interaction')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<!--\n The scoped style block is gone on purpose. The markup now lives in\n CnFormWidgetBase, so a scoped rule here would carry THIS component's\n data-v attribute and match none of it. The rules moved verbatim to\n src/css/form-widget.css, keyed on `cn-form-widget*`; the base also emits\n the `cn-interaction-form-widget*` names on the same elements, so app CSS\n targeting those keeps matching.\n-->\n"],"names":["_createBlock","_createVNode","_createTextVNode"],"mappings":";;;;;;;;uBASEA;AACA,IAAA,UAAU,EAAC,4BAAY;AACtB,IAAA,MAAM,EAAE,QAAU,CAAA,UAAA;AAClB,IAAA,KAAK,EAAE,KAAI,CAAA,IAAA;AACX,IAAA,MAAM,aAAa,KAAS,CAAA,YAAA,EAAA;AAC5B,IAAA,SAAS,EAAE,QAAW,CAAA,WAAA;AACtB,IAAA,UAAU,EAAE,KAAM,CAAA,MAAA;AAClB,IAAA,WAAW,EAAE,QAAM,CAAA,aAAA;AACnB,IAAA,eAAe,EAAE,QAAU,CAAA,WAAA;gBACf,EAAE,KAAW,CAAA,YAAA;oBACb,EAAE,QAAa,CAAA,aAAA;AACvB,IAAA,QAAE,EAAC,QAAW,CAAA,UAAA;;AAKR,IAAA,cAAY,gBASC;iBAAA,CAAA,2BAAA,EAAA;AAPrB,QAAA,QAAQ,EAAE,QAAQ,CAAA,QAAA;AAClB,QAAA,MAAM,EAAE,QAAK,CAAA,YAAA;AACb,QAAA,UAAU,EAAE,QAAgB,CAAA,gBAAA;QAC5B,UAAU,EAAE,KAAI,CAAA,IAAA,CAAC,MAAM;AACvB,QAAA,UAAO,EAAA,QAAA,CAAA,WAAA;;AAEP,QAAA,qBAAiB,EAAE,QAAc,CAAA,cAAA;AACjC,QAAA,UAAQ,QAAa,CAAA,eAAA;;;AAIb,IAAA,eAAO,UAChB;OACM,QAAS,CAAA,SAAA;wBADfA;;AAEA,YAAA,OAAO,EAAC,UAAO;AACd,YAAA,IAAI,EAAE,QAAS,CAAA,SAAA;AAChB,YAAA,MAAM,EAAC,QAAG;AACV,YAAA,aAAW,EAAA,0BAAG;;AACL,YAAA,IAAA,UACR,MAAO;cAAPC,WAAO,CAAA,oBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,CAAA;;6BACG;AAAA,cAAAC,eAAA,CAAA;;;;;;;;;;;;"}
@@ -2,7 +2,7 @@ import script from './CnObjectDataWidget.vue2.js';
2
2
  import { render } from './CnObjectDataWidget.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-59ab12e6";
5
+ script.__scopeId = "data-v-324e1a00";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };