@conduction/nextcloud-vue 2.78.0 → 2.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/dist/bundled-packages.json +0 -2
  2. package/dist/esm/bundled-packages.json +0 -2
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
  5. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  6. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
  7. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  8. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +115 -104
  9. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  13. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +39 -3
  14. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  15. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +158 -0
  17. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  18. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +157 -96
  19. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  20. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +139 -1
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +77 -0
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +5 -2
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +62 -4
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +85 -1
  32. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  33. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
  34. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  35. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
  36. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
  37. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +736 -0
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
  40. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
  41. package/dist/esm/components/CnFactsWidget/index.js +30 -0
  42. package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
  43. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
  44. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
  45. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +551 -0
  46. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
  47. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +183 -0
  48. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
  49. package/dist/esm/components/CnFieldListWidget/index.js +29 -0
  50. package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
  51. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
  52. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
  54. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  55. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
  56. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  57. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
  58. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
  59. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +66 -18
  60. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  61. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
  62. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  63. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +18 -3
  64. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  65. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  66. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +330 -74
  67. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  68. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +34 -15
  69. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  70. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
  71. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
  72. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
  73. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
  74. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
  75. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
  76. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
  77. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
  79. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  80. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
  81. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  82. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  83. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
  84. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  85. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  86. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
  87. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
  88. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +387 -0
  89. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
  90. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +169 -0
  91. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
  92. package/dist/esm/components/CnRecordListWidget/index.js +31 -0
  93. package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
  94. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  95. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
  96. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  97. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
  98. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
  99. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
  100. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
  101. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
  102. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
  103. package/dist/esm/components/CnSectionsWidget/index.js +35 -0
  104. package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
  105. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
  106. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  108. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  109. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
  110. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  111. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +152 -8
  112. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  113. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +84 -23
  114. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  115. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
  116. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  117. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
  118. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  119. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  120. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +106 -2
  121. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  122. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +105 -53
  123. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  124. package/dist/esm/composables/useAppManifest.js +8 -3
  125. package/dist/esm/composables/useAppManifest.js.map +1 -1
  126. package/dist/esm/composables/useFormDraft.js +140 -9
  127. package/dist/esm/composables/useFormDraft.js.map +1 -1
  128. package/dist/esm/index.js +5 -0
  129. package/dist/esm/index.js.map +1 -1
  130. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
  131. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  132. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
  133. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  134. package/dist/esm/integrations/builtin/field-inspection.js +21 -0
  135. package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
  136. package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
  137. package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
  138. package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
  139. package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
  140. package/dist/esm/integrations/offline/index.js +2 -1
  141. package/dist/esm/integrations/offline/index.js.map +1 -1
  142. package/dist/esm/integrations/offline/offlineDb.js +6 -1
  143. package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
  144. package/dist/esm/integrations/offline/syncReplayService.js +8 -2
  145. package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
  146. package/dist/esm/l10n/en.json.js +72 -1
  147. package/dist/esm/l10n/en.json.js.map +1 -1
  148. package/dist/esm/l10n/nl.json.js +120 -2
  149. package/dist/esm/l10n/nl.json.js.map +1 -1
  150. package/dist/esm/nextcloud-vue.css +1314 -74
  151. package/dist/esm/utils/boardDate.js +193 -0
  152. package/dist/esm/utils/boardDate.js.map +1 -0
  153. package/dist/esm/utils/detailActionModel.js +60 -3
  154. package/dist/esm/utils/detailActionModel.js.map +1 -1
  155. package/dist/esm/utils/dynamicProperties.js +5 -1
  156. package/dist/esm/utils/dynamicProperties.js.map +1 -1
  157. package/dist/esm/utils/fillTemplate.js +114 -0
  158. package/dist/esm/utils/fillTemplate.js.map +1 -0
  159. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  160. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  161. package/dist/esm/utils/queryPresets.js +96 -0
  162. package/dist/esm/utils/queryPresets.js.map +1 -0
  163. package/dist/esm/utils/resolveFilterTokens.js +11 -0
  164. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  165. package/dist/esm/utils/schema.js +21 -0
  166. package/dist/esm/utils/schema.js.map +1 -1
  167. package/dist/esm/utils/sentinelTokens.js +3 -3
  168. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  169. package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
  170. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  171. package/dist/esm/utils/widgetDispatch.js +7 -1
  172. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  173. package/dist/nextcloud-vue.cjs.js +14278 -8169
  174. package/dist/nextcloud-vue.cjs.js.map +2 -2
  175. package/dist/nextcloud-vue.css +1314 -74
  176. package/l10n/en.json +72 -1
  177. package/l10n/nl.json +120 -2
  178. package/package.json +1 -1
  179. package/scripts/build-validators.js +26 -0
  180. package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
  181. package/src/components/CnActionsBar/CnActionsBar.md +4 -0
  182. package/src/components/CnActionsBar/CnActionsBar.vue +83 -42
  183. package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
  184. package/src/components/CnBannerWidget/CnBannerWidget.vue +39 -3
  185. package/src/components/CnCellRenderer/CnCellRenderer.vue +306 -2
  186. package/src/components/CnDashboardPage/CnDashboardPage.vue +219 -4
  187. package/src/components/CnDataTable/CnDataTable.md +2 -0
  188. package/src/components/CnDataTable/CnDataTable.vue +82 -2
  189. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  190. package/src/components/CnDetailPage/CnDetailPage.vue +63 -3
  191. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +127 -1
  192. package/src/components/CnFactsWidget/CnFactsWidget.vue +1050 -0
  193. package/src/components/CnFactsWidget/index.js +29 -0
  194. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +746 -0
  195. package/src/components/CnFieldListWidget/index.js +28 -0
  196. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
  197. package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
  198. package/src/components/CnFilesBrowser/index.js +1 -0
  199. package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
  200. package/src/components/CnFormDialog/CnFormDialog.md +9 -3
  201. package/src/components/CnFormDialog/CnFormDialog.vue +70 -22
  202. package/src/components/CnFormPage/CnFormPage.vue +18 -3
  203. package/src/components/CnIndexPage/CnIndexPage.vue +281 -9
  204. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
  205. package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
  206. package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
  207. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
  208. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +603 -0
  209. package/src/components/CnRecordListWidget/index.js +30 -0
  210. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
  211. package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
  212. package/src/components/CnSectionsWidget/index.js +34 -0
  213. package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
  214. package/src/components/CnStatWidget/CnStatWidget.vue +1 -1
  215. package/src/components/CnTabsWidget/CnTabsWidget.vue +214 -9
  216. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +4 -0
  217. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
  218. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +188 -4
  219. package/src/components/index.js +5 -1
  220. package/src/composables/__tests__/useFormDraft.spec.js +69 -0
  221. package/src/composables/useAppManifest.js +8 -3
  222. package/src/composables/useFormDraft.js +139 -8
  223. package/src/css/form-field.css +14 -0
  224. package/src/css/index-page.css +71 -0
  225. package/src/css/kpi-card.css +32 -0
  226. package/src/css/look-board-detail.css +87 -0
  227. package/src/css/look-board-index.css +143 -14
  228. package/src/css/look-board.css +114 -2
  229. package/src/css/table.css +56 -0
  230. package/src/index.js +8 -0
  231. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
  232. package/src/integrations/builtin/field-inspection.js +21 -0
  233. package/src/integrations/offline/checklistSubmission.js +137 -0
  234. package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
  235. package/src/integrations/offline/index.js +3 -0
  236. package/src/integrations/offline/offlineDb.js +6 -1
  237. package/src/integrations/offline/syncReplayService.js +8 -2
  238. package/src/schemas/app-manifest-v2.schema.json +149 -5
  239. package/src/utils/boardDate.js +189 -0
  240. package/src/utils/detailActionModel.js +59 -2
  241. package/src/utils/dynamicProperties.js +5 -1
  242. package/src/utils/fillTemplate.js +110 -0
  243. package/src/utils/libraryWidgetKeys.js +4 -0
  244. package/src/utils/queryPresets.js +93 -0
  245. package/src/utils/resolveFilterTokens.js +11 -0
  246. package/src/utils/schema.js +21 -0
  247. package/src/utils/sentinelTokens.js +3 -3
  248. package/src/utils/validateManifestV2.compiled.js +57 -2
  249. package/src/utils/widgetDispatch.js +7 -1
  250. package/dist/esm/_virtual/equal.js +0 -4
  251. package/dist/esm/_virtual/equal.js.map +0 -1
  252. package/dist/esm/_virtual/ucs2length.js +0 -4
  253. package/dist/esm/_virtual/ucs2length.js.map +0 -1
  254. package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
  255. package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
  256. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
  257. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
  258. package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
  259. package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
@@ -107,16 +107,27 @@
107
107
  margin-inline-start: 0;
108
108
  }
109
109
 
110
- /* The first of Save view, Filter and the view switch takes the free space, so
111
- the three sit at the end of row 1. */
112
- .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--views > :is(.cn-saved-views__save, .cn-actions-bar__filter-button, .cn-actions-bar__view-toggle) {
110
+ /* The first of Save view and the Filter + view switch unit takes the free
111
+ space (the board's `flex: 1` spacer), so they sit at the end of row 1.
112
+ When the line is full the unit wraps as one to the START of the next line
113
+ while Save view stays at the end of row 1 (DqZaken); without Save view the
114
+ unit sits at the end of row 1 (PqTickets, PqLeads, PtPortals). */
115
+ .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--views > :is(.cn-saved-views__save, .cn-actions-bar__view-controls) {
113
116
  margin-inline-start: auto;
114
117
  }
115
118
 
116
- .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--views > :is(.cn-saved-views__save, .cn-actions-bar__filter-button, .cn-actions-bar__view-toggle) ~ :is(.cn-saved-views__save, .cn-actions-bar__filter-button, .cn-actions-bar__view-toggle) {
119
+ .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--views > .cn-saved-views__save ~ .cn-actions-bar__view-controls {
117
120
  margin-inline-start: 0;
118
121
  }
119
122
 
123
+ .cn-look-board .cn-actions-bar--board .cn-actions-bar__view-controls {
124
+ display: flex;
125
+ flex: 0 0 auto;
126
+ flex-wrap: nowrap;
127
+ align-items: center;
128
+ gap: 8px;
129
+ }
130
+
120
131
  /* Row 2: the search field and the active filters on a line of their own. */
121
132
  .cn-look-board .cn-actions-bar--board > .cn-actions-bar__row--search {
122
133
  display: flex;
@@ -202,16 +213,39 @@
202
213
  color: var(--color-primary-element-text);
203
214
  }
204
215
 
205
- /* Save view: 34px, radius 17, on the light primary tint. */
216
+ /* Save view: a ghost button, 34px, radius 17, no border, on the light primary
217
+ tint, a 14px icon 6px before the label, on one line. */
206
218
  .cn-look-board .cn-saved-views__save .button-vue {
207
219
  min-height: 34px;
208
220
  height: 34px;
209
- padding: 0 14px;
221
+ padding: 0 12px;
222
+ border: 0;
210
223
  border-radius: 17px;
211
224
  background: var(--color-primary-element-light);
212
225
  color: var(--color-primary-element-light-text);
213
226
  font-size: 13px;
214
227
  font-weight: 600;
228
+ white-space: nowrap;
229
+ }
230
+
231
+ .cn-look-board .cn-saved-views__save .button-vue:hover {
232
+ background: var(--color-primary-element-light-hover);
233
+ }
234
+
235
+ .cn-look-board .cn-saved-views__save .button-vue .button-vue__wrapper {
236
+ gap: 6px;
237
+ }
238
+
239
+ .cn-look-board .cn-saved-views__save .button-vue .button-vue__icon {
240
+ width: auto;
241
+ min-width: 0;
242
+ height: auto;
243
+ min-height: 0;
244
+ }
245
+
246
+ .cn-look-board .cn-saved-views__save .button-vue .button-vue__text {
247
+ margin: 0;
248
+ padding: 0;
215
249
  }
216
250
 
217
251
  /* Filter: a labelled secondary button, 36px, radius 8, with the active count. */
@@ -267,6 +301,15 @@
267
301
  color: var(--color-main-text);
268
302
  }
269
303
 
304
+ /* A segment the page cannot open: drawn, as the boards do, but faded and inert. */
305
+ .cn-look-board .cn-actions-bar--board .cn-actions-bar__view-toggle .cn-actions-bar__view-toggle-btn--disabled,
306
+ .cn-look-board .cn-actions-bar--board .cn-actions-bar__view-toggle .cn-actions-bar__view-toggle-btn--disabled:hover {
307
+ background: transparent !important;
308
+ color: var(--color-text-maxcontrast);
309
+ opacity: 0.5;
310
+ cursor: not-allowed;
311
+ }
312
+
270
313
  /* The search field: 40px, radius 8, not a pill. */
271
314
  .cn-look-board .cn-actions-bar--board .cn-actions-bar__search {
272
315
  flex: 1 1 260px;
@@ -414,6 +457,48 @@
414
457
  border-bottom: 0;
415
458
  }
416
459
 
460
+ /* The table fits its card (screens-index-table-fit-parity). Under the auto
461
+ layout every one-line cell asked for its whole text up to the 22rem cap, so
462
+ seven text columns made a 1500px table in a 1118px card at a 1440px
463
+ viewport: the last columns and the row menus sat past the card's edge. The
464
+ fixed layout shares the card's width: the selection and menu columns keep
465
+ their own width, the first data column takes 18%, the others split the
466
+ rest, and a value that does not fit is cut with an ellipsis (its full text
467
+ stays in the cell's tooltip). A column's own `width` still wins. */
468
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table {
469
+ table-layout: fixed;
470
+ width: 100%;
471
+ }
472
+
473
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table th.cn-table-head--title {
474
+ width: 18%;
475
+ }
476
+
477
+ /* The board's data cells sit 12px apart (PqTickets), the first one 18px from
478
+ the selection column; the selection and menu columns keep 16px. */
479
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table th:not(.cn-table-col--checkbox):not(.cn-table-col--actions):not(.cn-table-col--icon),
480
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table td:not(.cn-table-col--checkbox):not(.cn-table-col--actions):not(.cn-table-col--icon) {
481
+ padding-inline: 12px;
482
+ }
483
+
484
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table th.cn-table-head--title,
485
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table td.cn-table-col--title {
486
+ padding-inline-start: 18px;
487
+ }
488
+
489
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table td > .cn-cell-renderer {
490
+ display: block;
491
+ min-width: 0;
492
+ overflow: hidden;
493
+ text-overflow: ellipsis;
494
+ white-space: nowrap;
495
+ }
496
+
497
+ /* A narrow header cuts its label, not its filter funnel. */
498
+ .cn-look-board .cn-index-page__main--table .cn-table-container--board .cn-data-table th .cn-table-header__sort:has(~ .cn-table-header__filter) {
499
+ max-width: calc(100% - 26px);
500
+ }
501
+
417
502
  /* The selection column: 28px, 18px checkboxes. */
418
503
  .cn-look-board .cn-table-container--board .cn-data-table .cn-table-col--checkbox {
419
504
  box-sizing: content-box;
@@ -443,6 +528,29 @@
443
528
  text-decoration: none;
444
529
  }
445
530
 
531
+ /* Only the title is underlined (DqZaken). An underline set on the cell
532
+ reaches every in-flow child whatever its own text-decoration, so the
533
+ secondary lines become inline blocks on a line of their own: an atomic
534
+ inline box is the one thing the underline does not reach. */
535
+ .cn-look-board .cn-table-container--board .cn-data-table td.cn-table-col--title .cn-table-cell__secondary {
536
+ display: inline-block;
537
+ width: 100%;
538
+ vertical-align: top;
539
+ }
540
+
541
+ /* `rowTitle: "plain"` (PqTickets, OcPublicaties): the title is bold text in
542
+ the main text colour without an underline, the secondary line 13px muted,
543
+ 2px under it. The row stays clickable through its stretched row link. */
544
+ .cn-look-board .cn-table-container--board.cn-table-container--title-plain .cn-data-table td.cn-table-col--title {
545
+ font-weight: 700;
546
+ text-decoration: none;
547
+ }
548
+
549
+ .cn-look-board .cn-table-container--board.cn-table-container--title-plain .cn-data-table td.cn-table-col--title .cn-table-cell__secondary {
550
+ margin-top: 2px;
551
+ font-size: 13px;
552
+ }
553
+
446
554
  /* A status pill: 3px 10px, radius 12, 13px weight 600. The colours stay the
447
555
  status colour map's. */
448
556
  .cn-look-board .cn-table-container--board .cn-data-table .cn-status-badge {
@@ -465,15 +573,36 @@
465
573
  --default-clickable-area: 34px;
466
574
  }
467
575
 
576
+ /* The row menu: a 34px square, three dots, a grey border on the main
577
+ background (DqZaken, PqTickets). A theme that styles every secondary button
578
+ with `!important` (the nldesign theme: a primary border, 8px 16px padding,
579
+ `min-width: max-content`) would otherwise turn it into a 64px wide button
580
+ with a blue border, so the board values are `!important` too. */
581
+ .cn-look-board .cn-row-actions--board .button-vue,
582
+ .cn-look-board .cn-row-actions--board .button-vue:hover,
583
+ .cn-look-board .cn-row-actions--board .button-vue:focus {
584
+ box-sizing: border-box !important;
585
+ width: 34px !important;
586
+ min-width: 34px !important;
587
+ height: 34px !important;
588
+ min-height: 34px !important;
589
+ padding: 0 !important;
590
+ border: 1px solid var(--color-border-dark) !important;
591
+ border-radius: 8px !important;
592
+ color: var(--cn-board-text-soft) !important;
593
+ }
594
+
468
595
  .cn-look-board .cn-row-actions--board .button-vue {
469
- width: 34px;
470
- min-width: 34px;
471
- height: 34px;
472
- min-height: 34px;
473
- padding: 0;
474
- border: 1px solid var(--color-border-dark);
475
- border-radius: 8px;
476
- background: var(--color-main-background);
596
+ background: var(--color-main-background) !important;
597
+ }
598
+
599
+ .cn-look-board .cn-row-actions--board .button-vue:hover {
600
+ background: var(--color-background-hover) !important;
601
+ }
602
+
603
+ .cn-look-board .cn-row-actions--board .button-vue:focus-visible {
604
+ outline: 2px solid var(--color-main-text);
605
+ outline-offset: 2px;
477
606
  }
478
607
 
479
608
  /* ------------------------------------------------------------------ footer */
@@ -12,9 +12,14 @@
12
12
  * counterpart default to one.
13
13
  */
14
14
  .cn-look-board {
15
- /* Page content area: padding, max width, gap between blocks. */
15
+ /* Page content area: padding, max width, gap between blocks. The content
16
+ spans the whole content area (screens-one-scroller-parity): grids grow
17
+ with the window and shrink only when the sidebar takes its share. A
18
+ 1240px cap left dead space right of the content, and on a dashboard,
19
+ which scrolled itself, put the scrollbar beside the content instead of
20
+ at the window edge. */
16
21
  --cn-board-content-padding: 24px 28px;
17
- --cn-board-content-max-width: 1240px;
22
+ --cn-board-content-max-width: none;
18
23
  --cn-board-section-gap: 20px;
19
24
  /* Buttons and inputs. */
20
25
  --cn-board-control-height: 40px;
@@ -54,6 +59,15 @@
54
59
  gap: var(--cn-board-section-gap);
55
60
  }
56
61
 
62
+ /* One scroller: the content area. CnDashboardPage scrolls itself
63
+ (`height: 100%; overflow-y: auto`), which nested a second scrollbar inside
64
+ NcAppContent's; under the board look the page grows with its widgets and
65
+ NcAppContent, which already spans to the window edge, scrolls it. */
66
+ .cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) {
67
+ height: auto;
68
+ overflow-y: visible;
69
+ }
70
+
57
71
  .cn-look-board .cn-index-page:not(.cn-look-nextcloud *) .cn-page-header,
58
72
  .cn-look-board .cn-settings-page:not(.cn-look-nextcloud *) .cn-page-header,
59
73
  .cn-look-board .cn-index-page:not(.cn-look-nextcloud *) .cn-index-page__below-header,
@@ -146,6 +160,14 @@
146
160
  margin-inline-start: -264px;
147
161
  }
148
162
 
163
+ /* The navigation draws the line between it and the content. NcContent draws a
164
+ second one on the content while the navigation is open, which doubled the
165
+ line and put the page 1px further from the navigation than the board's 28px.
166
+ Outranks NcContent's rule (six classes) with seven. */
167
+ .cn-look-board .app-navigation[data-testid="cn-nav"]:not(.app-navigation--closed):not(.app-navigation--close) ~ .app-content.app-content {
168
+ border-inline-start: none;
169
+ }
170
+
149
171
  .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__content {
150
172
  box-sizing: border-box;
151
173
  gap: 20px;
@@ -155,6 +177,52 @@
155
177
  .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__list {
156
178
  height: auto;
157
179
  flex: 1 1 auto;
180
+ margin: 0;
181
+ gap: 2px;
182
+ }
183
+
184
+ /* No scrollbars inside the navigation (screens-one-scroller-parity).
185
+ NcAppNavigation gives the region above the list `overflow-y: scroll`, an
186
+ always-on track (with arrow buttons on classic scrollbars) beside the
187
+ primary action that has nothing to scroll. It takes its content's height
188
+ instead. A menu taller than the window still scrolls (wheel, touch, focus),
189
+ without drawing a bar of its own; the one visible scrollbar is the page's. */
190
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__body:not(.app-navigation__body--no-list) {
191
+ flex-shrink: 0;
192
+ overflow: visible;
193
+ }
194
+
195
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__list,
196
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__body--no-list {
197
+ scrollbar-width: none;
198
+ }
199
+
200
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__list::-webkit-scrollbar,
201
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__body--no-list::-webkit-scrollbar {
202
+ display: none;
203
+ }
204
+
205
+ /* The board's vertical rhythm (AppZijbalk): the primary action 20px from the
206
+ top, the list 20px under it, entries 2px apart. An empty search slot is a
207
+ flex child too and pushed everything 20px down, so it goes. */
208
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__search:empty {
209
+ display: none;
210
+ }
211
+
212
+ /* A caption is a 12px label with 6px under it, 20px after the group above
213
+ and 2px before its first entry. NcAppNavigationCaption gives its name a
214
+ 15px font on a 34px line, which made it 44px high. */
215
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation__list > .app-navigation-caption:not(:first-child) {
216
+ margin-top: 18px;
217
+ }
218
+
219
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation-caption .app-navigation-caption__name {
220
+ margin: 0;
221
+ padding: 0;
222
+ font-size: inherit;
223
+ font-weight: inherit;
224
+ line-height: normal;
225
+ color: inherit;
158
226
  }
159
227
 
160
228
  .cn-look-board .app-navigation[data-testid="cn-nav"] .app-navigation-entry,
@@ -239,6 +307,43 @@
239
307
  font-size: 15px;
240
308
  }
241
309
 
310
+ /* The footer of the board: Help and Advanced as two 40px rows at weight 400,
311
+ 2px apart. The Help row kept the 42px of a list entry, and the Advanced
312
+ foldout drew a 34px button inside 4px of padding, 20px under Help. */
313
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .cn-app-nav__footer-list {
314
+ gap: 2px;
315
+ }
316
+
317
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .cn-app-nav__footer-list .app-navigation-entry,
318
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .cn-app-nav__footer-list .app-navigation-entry-link {
319
+ min-height: 40px;
320
+ font-weight: 400;
321
+ }
322
+
323
+ .cn-look-board .app-navigation[data-testid="cn-nav"] [data-testid="cn-nav-settings"] {
324
+ padding: 0;
325
+ }
326
+
327
+ .cn-look-board .app-navigation[data-testid="cn-nav"] .cn-app-nav__footer-list + [data-testid="cn-nav-settings"] {
328
+ margin-top: -18px;
329
+ }
330
+
331
+ .cn-look-board .app-navigation[data-testid="cn-nav"] [data-testid="cn-nav-settings"] > :first-child {
332
+ margin: 0;
333
+ }
334
+
335
+ .cn-look-board .app-navigation[data-testid="cn-nav"] [data-testid="cn-nav-settings"] > :first-child .button-vue {
336
+ box-sizing: border-box;
337
+ width: 100%;
338
+ height: 40px;
339
+ min-height: 40px;
340
+ padding: 0 10px;
341
+ border-radius: 8px;
342
+ font-size: 15px;
343
+ font-weight: 400;
344
+ justify-content: flex-start;
345
+ }
346
+
242
347
  /* The card above the footer, pushed down. */
243
348
  .cn-look-board .cn-app-nav__card {
244
349
  order: 0;
@@ -443,3 +548,10 @@
443
548
  .cn-look-board .cn-status-badge--primary:not(.cn-status-badge--solid):not(.cn-look-nextcloud *) {
444
549
  color: var(--color-primary-element-light-text);
445
550
  }
551
+
552
+ /* ---- Date and age cells (screens-cell-date-parity) ---------------------- */
553
+ /* The board sets every waiting time ("3 dagen", "4 uur") in weight 600; a
554
+ late one is red through its variant class. */
555
+ .cn-look-board .cn-cell-renderer__age:not(.cn-look-nextcloud *) {
556
+ font-weight: 600;
557
+ }
package/src/css/table.css CHANGED
@@ -230,6 +230,53 @@
230
230
  min-width: 0;
231
231
  }
232
232
 
233
+ /* Fit to the container (CnDataTable's `fitWidth`, on in CnWidgetObjectTable).
234
+
235
+ In a narrow tile the auto layout made the table as wide as its one-line
236
+ cells, so it ran past the tile and clipped the trailing column (a date).
237
+ Here every column but one takes the width of its content (`width: 1%` asks
238
+ for as little as the content allows), and the grow column takes what is
239
+ left. `max-width: 0` is what lets that column go below its content's width:
240
+ the table no longer counts its text, so the text is cut with an ellipsis
241
+ instead of widening the table. The full value stays in the cell's tooltip. */
242
+ .cn-data-table--fit {
243
+ width: 100%;
244
+ }
245
+
246
+ .cn-data-table--fit th,
247
+ .cn-data-table--fit td {
248
+ width: 1%;
249
+ }
250
+
251
+ .cn-data-table--fit th.cn-table-col--grow,
252
+ .cn-data-table--fit td.cn-table-col--grow {
253
+ width: auto;
254
+ min-width: 0;
255
+ max-width: 0;
256
+ overflow: hidden;
257
+ text-overflow: ellipsis;
258
+ white-space: nowrap;
259
+ }
260
+
261
+ /* The secondary line of the growing column ("2026-0004" under a case title)
262
+ is a line of its own with its own ellipsis. The cell does not wrap, so in
263
+ the board look, where the secondary line is an inline block (the title
264
+ underline cannot reach an atomic inline box), it stayed on the title's line
265
+ and ran out of the tile. A full-width float is a block (computed `display:
266
+ block`) that always starts under the line before it, and, being out of the
267
+ flow, the cell's underline does not reach it either. The cell is a table
268
+ cell, which contains its floats, so the row grows to hold the line.
269
+ @spec openspec/changes/dashboard-tile-text-fits/specs/dashboard-tile-text-fits/spec.md#requirement-the-secondary-line-of-a-narrow-tables-growing-column-sits-under-the-value */
270
+ .cn-data-table.cn-data-table--fit td.cn-table-col--grow .cn-table-cell__secondary {
271
+ float: inline-start;
272
+ clear: both;
273
+ box-sizing: border-box;
274
+ width: 100%;
275
+ overflow: hidden;
276
+ text-overflow: ellipsis;
277
+ white-space: nowrap;
278
+ }
279
+
233
280
  .cn-data-table th {
234
281
  background: var(--color-background-dark);
235
282
  font-weight: 500;
@@ -578,6 +625,15 @@
578
625
  color: var(--color-text-maxcontrast);
579
626
  }
580
627
 
628
+ /* The empty row holds a sentence (the empty text, or why a lens cannot
629
+ answer), not a cell value: it wraps instead of being cut to one line. The
630
+ selector carries the table class so it outranks the one-line cell rule. */
631
+ .cn-data-table .cn-table-empty td {
632
+ max-width: none;
633
+ white-space: normal;
634
+ overflow-wrap: anywhere;
635
+ }
636
+
581
637
  /* ========================================
582
638
  Backwards compatibility aliases
583
639
  (legacy OpenRegister CSS class names)
package/src/index.js CHANGED
@@ -112,6 +112,7 @@ export {
112
112
  CnEnvironmentBanner,
113
113
  CnExportWizard,
114
114
  CnFacetSidebar,
115
+ CnFactsWidget,
115
116
  CnFavouriteToggle,
116
117
  CnFeaturesAndRoadmapLink,
117
118
  CnFeaturesAndRoadmapPage,
@@ -120,6 +121,7 @@ export {
120
121
  CnFeaturesTab,
121
122
  CnFederationStatus,
122
123
  CnFieldHelper,
124
+ CnFieldListWidget,
123
125
  CnFileField,
124
126
  CnFileManager,
125
127
  CnFilePreview,
@@ -236,6 +238,7 @@ export {
236
238
  CnQuickFilterBar,
237
239
  CnQuicklinksWidget,
238
240
  CnQuicklinksWidgetForm,
241
+ CnRecordListWidget,
239
242
  CnReferencePreview,
240
243
  CnRegisterMapping,
241
244
  CnRegisterSchemaSelect,
@@ -258,6 +261,7 @@ export {
258
261
  CnSaveViewDialog,
259
262
  CnSchemaFormDialog,
260
263
  CnSearchPage,
264
+ CnSectionsWidget,
261
265
  CnSettingsCard,
262
266
  CnSettingsPage,
263
267
  CnSettingsSection,
@@ -338,6 +342,10 @@ export {
338
342
  // @deprecated Use `CnBuildiqEditButton`.
339
343
  export { CnOpenBuildEditButton } from './components/index.js'
340
344
 
345
+ // A CnFilesBrowser data source that reads an OpenRegister object's folder
346
+ // through OpenRegister's files API (openspec: files-browser-openregister-source).
347
+ export { createOpenRegisterSource } from './components/index.js'
348
+
341
349
  // Workplace dashboard primitives (openspec: workplace-dashboard-primitives):
342
350
  // the week strip and stacked bar widgets with their forms, the segmented
343
351
  // control, and the brand stripe.
@@ -134,6 +134,13 @@
134
134
  (an invalid `ButtonType`, so the visual primary style never applied
135
135
  either). See CnFormPage.vue's inline comment for the submit-button
136
136
  half of this bug in detail. -->
137
+ <p v-if="saveError"
138
+ class="cn-field-inspection__error"
139
+ role="alert"
140
+ data-testid="cn-fi-save-error">
141
+ {{ saveError }}
142
+ </p>
143
+
137
144
  <NcButton type="submit"
138
145
  variant="primary"
139
146
  data-testid="cn-fi-save"
@@ -157,7 +164,8 @@ import { NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
157
164
  import ClipboardCheckOutline from 'vue-material-design-icons/ClipboardCheckOutline.vue'
158
165
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
159
166
  import CnOfflineQueue from '../../../components/CnOfflineQueue/index.js'
160
- import { checklistProgress, classifyGps, syncIndicator, validateChecklistAnswers } from '../../offline/fieldCollectionHelpers.js'
167
+ import { buildChecklistSubmission } from '../../offline/checklistSubmission.js'
168
+ import { checklistProgress, classifyGps, normaliseChecklistTemplate, syncIndicator, validateChecklistAnswers } from '../../offline/fieldCollectionHelpers.js'
161
169
  import {
162
170
  countPending,
163
171
  countStuck,
@@ -236,6 +244,8 @@ export default {
236
244
  activeTemplate: null,
237
245
  answers: {},
238
246
  errors: [],
247
+ /** Why the last save could not be queued, in words. */
248
+ saveError: '',
239
249
  }
240
250
  },
241
251
 
@@ -449,8 +459,13 @@ export default {
449
459
  this.errors = []
450
460
  const cfg = this.config
451
461
  const templateRef = item?.[cfg.templateRefField]
462
+ // The app's template shape (sections, its own item keys and type
463
+ // words) is read into the leaf's flat item list here, once.
452
464
  this.activeTemplate = templateRef
453
- ? await getCachedObject(this.effectiveRegister, cfg.referenceSchema, 'references', String(templateRef))
465
+ ? normaliseChecklistTemplate(
466
+ await getCachedObject(this.effectiveRegister, cfg.referenceSchema, 'references', String(templateRef)),
467
+ cfg,
468
+ )
454
469
  : null
455
470
  const initial = {}
456
471
  for (const q of (this.activeTemplate?.items || [])) {
@@ -464,6 +479,7 @@ export default {
464
479
  this.activeTemplate = null
465
480
  this.answers = {}
466
481
  this.errors = []
482
+ this.saveError = ''
467
483
  },
468
484
 
469
485
  /**
@@ -485,7 +501,9 @@ export default {
485
501
  },
486
502
 
487
503
  /**
488
- * Validate answers, then queue a `create` mutation for the result.
504
+ * Validate answers, then queue the run: an object create on
505
+ * `resultSchema`, or, when the config names a `resultEndpoint`, one
506
+ * `submit` to that endpoint.
489
507
  *
490
508
  * @return {Promise<void>}
491
509
  */
@@ -499,26 +517,22 @@ export default {
499
517
  try {
500
518
  const cfg = this.config
501
519
  const gps = await this.captureGps()
502
- const gpsClass = classifyGps(gps)
503
- const now = new Date().toISOString()
504
- const items = this.activeTemplate.items.map((q) => ({
505
- questionId: q.questionId,
506
- answer: this.answers[q.questionId].answer,
507
- evidenceRefs: this.answers[q.questionId].evidenceRefs,
508
- answeredAt: now,
509
- gpsAtAnswer: gps ? { ...gps, source: gpsClass.source } : { source: 'sensorless' },
510
- }))
511
- const payload = {
512
- inspectionRef: this.itemId(this.activeItem),
513
- checklistTemplateRef: this.activeTemplate.id ?? this.activeTemplate['@self']?.id,
514
- items,
515
- }
520
+ const submission = buildChecklistSubmission(cfg, {
521
+ plannedItem: this.activeItem,
522
+ template: this.activeTemplate,
523
+ answers: this.answers,
524
+ gps,
525
+ gpsSource: classifyGps(gps).source,
526
+ capturedAt: new Date().toISOString(),
527
+ capturedOffline: this.offline,
528
+ })
516
529
  await enqueueMutation({
517
530
  deviceId: this.deviceId,
518
- operationType: 'create',
531
+ operationType: submission.operationType,
519
532
  register: this.effectiveRegister,
520
- schema: cfg.resultSchema,
521
- payload,
533
+ schema: submission.schema,
534
+ endpoint: submission.endpoint,
535
+ payload: submission.payload,
522
536
  })
523
537
  this.pendingCount = await countPending(this.deviceId)
524
538
  this.stuckCount = await countStuck(this.deviceId)
@@ -527,6 +541,7 @@ export default {
527
541
  await this.drain()
528
542
  }
529
543
  } catch (e) {
544
+ this.saveError = e?.message || t('nextcloud-vue', 'The answers could not be saved on this device.')
530
545
  // eslint-disable-next-line no-console
531
546
  console.error('[CnFieldInspectionCard] saveChecklist failed', e)
532
547
  } finally {
@@ -56,6 +56,27 @@ export const DEFAULT_FIELD_INSPECTION_CONFIG = {
56
56
  // Schema holding conflict records. Empty keeps a conflict on the device,
57
57
  // and the queue row says so rather than pretending it was filed.
58
58
  conflictSchema: '',
59
+ // How a template's items are read (normaliseChecklistTemplate). Empty
60
+ // `sectionsField` reads a flat `items[]`; set it when the items sit inside
61
+ // ordered sections, each with its own `items[]`.
62
+ sectionsField: '',
63
+ itemKeyField: 'questionId',
64
+ itemTextField: 'text',
65
+ itemTypeField: 'type',
66
+ // The app's own item type words onto the leaf's (`yes_no`,
67
+ // `photo_required`; anything else renders as free text).
68
+ itemTypeMap: {},
69
+ // An item whose `photoRequiredField` equals `photoRequiredValue` needs a
70
+ // photo as well as an answer. Empty switches the gate off.
71
+ photoRequiredField: '',
72
+ photoRequiredValue: '',
73
+ // Where a finished run goes (checklistSubmission). Empty queues an object
74
+ // create on `resultSchema`. Set, it is a path under the Nextcloud root with
75
+ // `{field}` placeholders from the planned item, and the run replays as one
76
+ // POST there, naming the template and the planned item under these keys.
77
+ resultEndpoint: '',
78
+ resultTemplateParam: 'templateId',
79
+ resultPlannedItemParam: 'plannedItem',
59
80
  }
60
81
 
61
82
  /**