@conduction/nextcloud-vue 2.85.0 → 2.87.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 (301) hide show
  1. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue.js +1 -1
  2. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue2.js +3 -2
  3. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue2.js.map +1 -1
  4. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue.js +1 -1
  6. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js +5 -4
  7. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js.map +1 -1
  8. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue.js +1 -1
  10. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue2.js +2 -1
  11. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue2.js.map +1 -1
  12. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue3.js.map +1 -1
  13. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  14. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +2 -1
  15. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  16. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +156 -14
  19. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  20. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +105 -74
  21. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  22. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue.js +1 -1
  23. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue2.js +2 -1
  24. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue2.js.map +1 -1
  25. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue3.js.map +1 -1
  26. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue.js +1 -1
  27. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js +2 -1
  28. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js.map +1 -1
  29. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js.map +1 -1
  30. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  31. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +6 -5
  32. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  33. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  34. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue.js +1 -1
  35. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue2.js +2 -1
  36. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue2.js.map +1 -1
  37. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue3.js.map +1 -1
  38. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  39. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +2 -1
  40. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  41. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  42. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
  43. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +2 -1
  44. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
  45. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  47. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +2 -1
  48. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  49. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  50. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue.js +1 -1
  51. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue2.js +3 -1
  52. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue2.js.map +1 -1
  53. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue3.js.map +1 -1
  54. package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue2.js +2 -1
  55. package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue2.js.map +1 -1
  56. package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue3.js.map +1 -1
  57. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue.js +1 -1
  58. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue2.js +2 -1
  59. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue2.js.map +1 -1
  60. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue3.js.map +1 -1
  61. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue.js +1 -1
  62. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue2.js +3 -1
  63. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue2.js.map +1 -1
  64. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue3.js.map +1 -1
  65. package/dist/esm/components/CnFileManager/CnFileManager.vue.js +1 -1
  66. package/dist/esm/components/CnFileManager/CnFileManager.vue2.js +3 -1
  67. package/dist/esm/components/CnFileManager/CnFileManager.vue2.js.map +1 -1
  68. package/dist/esm/components/CnFileManager/CnFileManager.vue3.js.map +1 -1
  69. package/dist/esm/components/CnFilePreview/CnFilePreview.vue.js +1 -1
  70. package/dist/esm/components/CnFilePreview/CnFilePreview.vue2.js +2 -1
  71. package/dist/esm/components/CnFilePreview/CnFilePreview.vue2.js.map +1 -1
  72. package/dist/esm/components/CnFilePreview/CnFilePreview.vue3.js.map +1 -1
  73. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  74. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +49 -1
  75. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  76. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +3 -3
  77. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  78. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue.js +1 -1
  79. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +2 -1
  80. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
  81. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
  82. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -5
  83. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  84. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  85. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  86. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +2 -1
  87. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  88. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  89. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue.js +1 -1
  90. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue2.js +3 -2
  91. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue2.js.map +1 -1
  92. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue3.js.map +1 -1
  93. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
  94. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +28 -0
  95. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  96. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +82 -75
  97. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  98. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue.js +1 -1
  99. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue2.js +5 -4
  100. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue2.js.map +1 -1
  101. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue3.js.map +1 -1
  102. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue.js +1 -1
  103. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue2.js +2 -1
  104. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue2.js.map +1 -1
  105. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue3.js.map +1 -1
  106. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue.js +1 -1
  107. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue2.js +2 -1
  108. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue2.js.map +1 -1
  109. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue3.js.map +1 -1
  110. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue.js +1 -1
  111. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +2 -1
  112. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  113. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  114. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue.js +1 -1
  115. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue2.js +2 -1
  116. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue2.js.map +1 -1
  117. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue3.js.map +1 -1
  118. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue.js +1 -1
  119. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue2.js +2 -1
  120. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue2.js.map +1 -1
  121. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue3.js.map +1 -1
  122. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue.js +1 -1
  123. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue2.js +2 -1
  124. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue2.js.map +1 -1
  125. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue3.js.map +1 -1
  126. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue2.js +2 -0
  127. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue2.js.map +1 -1
  128. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue3.js +2 -2
  129. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue3.js.map +1 -1
  130. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue.js +1 -1
  131. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +2 -1
  132. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
  133. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
  134. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue.js +1 -1
  135. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue2.js +2 -1
  136. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue2.js.map +1 -1
  137. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue3.js.map +1 -1
  138. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  139. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +36 -0
  140. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  141. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +55 -40
  142. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  143. package/dist/esm/components/CnTasksCard/CnTasksCard.vue.js +1 -1
  144. package/dist/esm/components/CnTasksCard/CnTasksCard.vue2.js +2 -1
  145. package/dist/esm/components/CnTasksCard/CnTasksCard.vue2.js.map +1 -1
  146. package/dist/esm/components/CnTasksCard/CnTasksCard.vue3.js.map +1 -1
  147. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
  148. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +2 -1
  149. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
  150. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
  151. package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue.js +1 -1
  152. package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue2.js +2 -1
  153. package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue2.js.map +1 -1
  154. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue.js +1 -1
  155. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue2.js +2 -1
  156. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue2.js.map +1 -1
  157. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue3.js.map +1 -1
  158. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
  159. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +3 -26
  160. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  161. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  162. package/dist/esm/dialogs/CnNoteHistoryDialog.vue.js +1 -1
  163. package/dist/esm/dialogs/CnNoteHistoryDialog.vue2.js +2 -1
  164. package/dist/esm/dialogs/CnNoteHistoryDialog.vue2.js.map +1 -1
  165. package/dist/esm/dialogs/CnNoteHistoryDialog.vue3.js.map +1 -1
  166. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue.js +1 -1
  167. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue2.js +3 -2
  168. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue2.js.map +1 -1
  169. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue3.js.map +1 -1
  170. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue.js +1 -1
  171. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue2.js +4 -3
  172. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue2.js.map +1 -1
  173. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue3.js.map +1 -1
  174. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue.js +1 -1
  175. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -2
  176. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  177. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  178. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
  179. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +2 -1
  180. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
  181. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
  182. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
  183. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +6 -5
  184. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
  185. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
  186. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue.js +1 -1
  187. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue2.js +2 -1
  188. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue2.js.map +1 -1
  189. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue3.js.map +1 -1
  190. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue.js +1 -1
  191. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  192. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  193. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  194. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
  195. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  196. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  197. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  198. package/dist/esm/integrations/builtin/email/CnEmailCard.vue.js +1 -1
  199. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js +2 -1
  200. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  201. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  202. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue.js +1 -1
  203. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +2 -1
  204. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  205. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  206. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue.js +1 -1
  207. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue2.js +2 -1
  208. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue2.js.map +1 -1
  209. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue3.js.map +1 -1
  210. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue.js +1 -1
  211. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  212. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  213. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  214. package/dist/esm/l10n/en.json.js +35 -2
  215. package/dist/esm/l10n/en.json.js.map +1 -1
  216. package/dist/esm/l10n/nl.json.js +38 -2
  217. package/dist/esm/l10n/nl.json.js.map +1 -1
  218. package/dist/esm/library-style.js +1 -1
  219. package/dist/esm/nextcloud-vue.css +1068 -962
  220. package/dist/esm/store/useObjectStore.js +7 -2
  221. package/dist/esm/store/useObjectStore.js.map +1 -1
  222. package/dist/esm/utils/boardColumns.js +50 -1
  223. package/dist/esm/utils/boardColumns.js.map +1 -1
  224. package/dist/esm/utils/boardReferenceColumns.js +115 -4
  225. package/dist/esm/utils/boardReferenceColumns.js.map +1 -1
  226. package/dist/esm/utils/contentHeaderLink.js +48 -1
  227. package/dist/esm/utils/contentHeaderLink.js.map +1 -1
  228. package/dist/esm/utils/schema.js +5 -4
  229. package/dist/esm/utils/schema.js.map +1 -1
  230. package/dist/esm/utils/userLocale.js +30 -0
  231. package/dist/esm/utils/userLocale.js.map +1 -0
  232. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  233. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  234. package/dist/nextcloud-vue.cjs.js +1088 -476
  235. package/dist/nextcloud-vue.cjs.js.map +2 -2
  236. package/dist/nextcloud-vue.css +1068 -962
  237. package/l10n/en.json +35 -2
  238. package/l10n/nl.json +38 -2
  239. package/package.json +1 -1
  240. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +3 -2
  241. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +5 -4
  242. package/src/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue +2 -1
  243. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +2 -1
  244. package/src/components/CnBoardView/CnBoardView.vue +189 -24
  245. package/src/components/CnCalendarEventPicker/CnCalendarEventPicker.vue +2 -1
  246. package/src/components/CnCalendarWidget/CnCalendarWidget.vue +2 -1
  247. package/src/components/CnChartWidget/CnChartWidget.vue +6 -5
  248. package/src/components/CnCollectivePagePicker/CnCollectivePagePicker.vue +2 -1
  249. package/src/components/CnConversationThread/CnConversationThread.vue +2 -1
  250. package/src/components/CnCountdownWidget/CnCountdownWidget.vue +2 -1
  251. package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -1
  252. package/src/components/CnDataMatrix/CnDataMatrix.vue +2 -1
  253. package/src/components/CnDetailPage/CnDetailHeaderChips.vue +2 -1
  254. package/src/components/CnEmailPicker/CnEmailPicker.vue +2 -1
  255. package/src/components/CnFederationStatus/CnFederationStatus.vue +3 -1
  256. package/src/components/CnFileManager/CnFileManager.vue +2 -1
  257. package/src/components/CnFilePreview/CnFilePreview.vue +2 -1
  258. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +54 -6
  259. package/src/components/CnFollowToggle/CnFollowToggle.vue +2 -1
  260. package/src/components/CnIndexPage/CnIndexPage.vue +30 -4
  261. package/src/components/CnNotesCard/CnNotesCard.vue +2 -1
  262. package/src/components/CnObjectCalendar/CnObjectCalendar.vue +3 -2
  263. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +36 -0
  264. package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +5 -4
  265. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +2 -1
  266. package/src/components/CnObjectSidebar/CnFlowTasksPanel.vue +2 -1
  267. package/src/components/CnObjectSidebar/CnNotesTab.vue +2 -1
  268. package/src/components/CnObjectSidebar/CnTasksTab.vue +2 -1
  269. package/src/components/CnPropertySourceField/CnPropertySourceField.vue +2 -1
  270. package/src/components/CnRoadmapItem/CnRoadmapItem.vue +2 -1
  271. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +4 -2
  272. package/src/components/CnStatsBlock/CnStatsBlock.vue +2 -1
  273. package/src/components/CnSummaryAggregates/CnSummaryAggregates.vue +2 -1
  274. package/src/components/CnTabsWidget/CnTabsWidget.vue +71 -0
  275. package/src/components/CnTasksCard/CnTasksCard.vue +2 -1
  276. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +2 -1
  277. package/src/components/CnTranslatedBadge/CnTranslatedBadge.vue +2 -1
  278. package/src/components/CnVersionHistory/CnVersionHistory.vue +2 -1
  279. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +3 -26
  280. package/src/css/board.css +40 -0
  281. package/src/dialogs/CnNoteHistoryDialog.vue +2 -1
  282. package/src/integrations/builtin/activity/CnActivityCard.vue +3 -2
  283. package/src/integrations/builtin/activity/CnActivityTab.vue +4 -3
  284. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +3 -2
  285. package/src/integrations/builtin/calendar/CnCalendarCard.vue +2 -1
  286. package/src/integrations/builtin/calendar/CnCalendarTab.vue +6 -5
  287. package/src/integrations/builtin/contactmoment/CnContactmomentCard.vue +2 -1
  288. package/src/integrations/builtin/cospend/CnCospendTab.vue +2 -1
  289. package/src/integrations/builtin/deck/CnDeckTab.vue +2 -1
  290. package/src/integrations/builtin/email/CnEmailCard.vue +2 -1
  291. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +2 -1
  292. package/src/integrations/builtin/field-inspection/CnFieldInspectionTab.vue +2 -1
  293. package/src/integrations/builtin/photos/CnPhotosTab.vue +2 -1
  294. package/src/schemas/app-manifest-v2.schema.json +4 -1
  295. package/src/store/useObjectStore.js +7 -2
  296. package/src/utils/boardColumns.js +49 -0
  297. package/src/utils/boardReferenceColumns.js +114 -3
  298. package/src/utils/contentHeaderLink.js +47 -0
  299. package/src/utils/schema.js +5 -4
  300. package/src/utils/userLocale.js +26 -0
  301. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CnCospendTab.vue2.js","sources":["../../../../../src/integrations/builtin/cospend/CnCospendTab.vue"],"sourcesContent":["<!--\n CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n\n Replaces the generic CnIntegrationTab for the `cospend` leaf: renders\n the NC Cospend rows linked to the parent OR object as a Cospend-style\n list of projects and bills.\n Cospend exposes BOTH project rows AND bill rows in the same payload\n (per wave 2.3 design — see `CospendProvider.php`); each row carries\n a `type` discriminator (`'project'` | `'bill'`) which this tab shows\n as a `[Project]` / `[Bill]` status badge alongside the title.\n\n Rows render via NcListItem to mirror real Cospend:\n - project: name + [Project] badge + currency, payer (when present)\n - bill: title + [Bill] badge + payer · date subline, prominent\n right-aligned amount with currency\n A per-currency total footer summarises the linked spend, matching\n Cospend's project balance row.\n\n Clicking a project row deep-links to\n /index.php/apps/cospend/p/{id}\n Bills inherit the same URL (Cospend doesn't have a stable per-bill\n permalink — the project page lists its bills); a row's own `url`\n field wins if the provider already constructed one.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/cospend`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\CospendProvider`\n (link-table strategy — marker `[or:{uuid}]` in `cospend_projects.name`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Costs\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Cospend is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses Cospend's primary signals — *whether the row is a\n project or a bill*, the amount, and the currency. Surfacing them\n inline lets case handlers triage spending at a glance without\n context-switching into Cospend.\n\n See `openregister/openspec/changes/integration-cospend/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1324` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-cospend-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-cospend-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-cospend-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing project') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new project') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-cospend-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-cospend-tab__empty\">\n\t\t\t<CurrencyEur :size=\"32\" class=\"cn-cospend-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"cospendAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CurrencyEur :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openCospendLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<ul class=\"cn-cospend-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-cospend-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__row-icon\" :class=\"iconClass(row)\">\n\t\t\t\t\t\t\t<CashMultiple v-if=\"rowType(row) === 'bill'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FolderOutline v-else :size=\"20\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #name>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__title\">{{ rowTitle(row) }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__subline\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tclass=\"cn-cospend-tab__type-chip\"\n\t\t\t\t\t\t\t\t:class=\"typeChipClass(row)\"\n\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t:variant=\"typeVariant(row)\"\n\t\t\t\t\t\t\t\t:label=\"typeLabel(row)\" />\n\t\t\t\t\t\t\t<span v-if=\"sublineText(row)\" class=\"cn-cospend-tab__subline-text\">\n\t\t\t\t\t\t\t\t{{ sublineText(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #indicator>\n\t\t\t\t\t\t<span v-if=\"amountLabel(row)\" class=\"cn-cospend-tab__amount\" :class=\"amountClass(row)\">\n\t\t\t\t\t\t\t{{ amountLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tv-if=\"entryIdOf(row)\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkEntry(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\n\t\t\t<div v-if=\"totals.length > 0\" class=\"cn-cospend-tab__totals\">\n\t\t\t\t<span class=\"cn-cospend-tab__totals-label\">{{ t('nextcloud-vue', 'Total') }}</span>\n\t\t\t\t<ul class=\"cn-cospend-tab__totals-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in totals\"\n\t\t\t\t\t\t:key=\"bucket.currency\"\n\t\t\t\t\t\tclass=\"cn-cospend-tab__totals-row\">\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-currency\">{{ bucket.currency }}</span>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-amount\">{{ bucket.amountLabel }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<CnCospendPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnCospendCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CashMultiple from 'vue-material-design-icons/CashMultiple.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCospendCreate from '../../../components/CnCospendCreate/CnCospendCreate.vue'\nimport CnCospendPicker from '../../../components/CnCospendPicker/CnCospendPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n *\n * Renders linked Cospend projects + bills with a type badge, amount,\n * currency, payer, date and a per-currency total footer. See the\n * file-level docblock for surface behaviour.\n */\nexport default {\n\tname: 'CnCospendTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tCashMultiple,\n\t\tCurrencyEur,\n\t\tFolderOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnCospendPicker,\n\t\tCnCospendCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'cospend'`). */\n\t\tintegrationId: { type: String, default: 'cospend' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No costs linked yet') },\n\t\t/** Pre-translated label for the \"Open Costs\" CTA. */\n\t\topenCospendLabel: { type: String, default: () => t('nextcloud-vue', 'Open Costs') },\n\t\t/** Pre-translated banner when Cospend is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Costs is currently unavailable.') },\n\t\t/** URL of the NC Cospend app entry. */\n\t\tcospendAppUrl: { type: String, default: '/index.php/apps/cospend' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Per-currency totals of every row carrying an amount, mirroring\n\t\t * the balance row Cospend renders under a project.\n\t\t *\n\t\t * @return {Array<{currency: string, amountLabel: string}>}\n\t\t */\n\t\ttotals() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst amount = this.amountRaw(row)\n\t\t\t\tif (amount === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst currency = this.currencyOf(row) || ''\n\t\t\t\tconst existing = groups.get(currency) || { currency, total: 0 }\n\t\t\t\texisting.total += amount\n\t\t\t\tgroups.set(currency, existing)\n\t\t\t}\n\t\t\treturn Array.from(groups.values()).map((b) => {\n\t\t\t\tconst code = b.currency || t('nextcloud-vue', 'Unspecified')\n\t\t\t\treturn {\n\t\t\t\t\tcurrency: code,\n\t\t\t\t\tamountLabel: b.currency ? `${b.total.toFixed(2)} ${b.currency}` : b.total.toFixed(2),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 cospend endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tcospendEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/cospend`)\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? ''\n\t\t},\n\n\t\t/**\n\t\t * Resolve the link-row id for a Tier-2 row (used for unlink).\n\t\t * Legacy marker rows have no `entryId` so the unlink button hides.\n\t\t *\n\t\t * @param {object} row Provider/link row.\n\t\t *\n\t\t * @return {(number|string)} The link row id, or '' when absent.\n\t\t */\n\t\tentryIdOf(row) {\n\t\t\tconst d = (row && typeof row.data === 'object' && row.data !== null) ? row.data : {}\n\t\t\treturn row.entryId ?? d.id ?? ''\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.cospendEndpoint(), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/new`, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkEntry(row) {\n\t\t\tconst id = this.entryIdOf(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/${id}`, {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null\n\t\t\tif (projectId !== null && projectId !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${projectId}`\n\t\t\t}\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (id !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${id}`\n\t\t\t}\n\t\t\treturn this.cospendAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'project' when the provider doesn't\n\t\t * surface a `type` field (the current shipping CospendProvider\n\t\t * only returns projects); when wave-2.3 ships bills the chip\n\t\t * label flips accordingly.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'project'|'bill'} Row type.\n\t\t */\n\t\trowType(row) {\n\t\t\tconst t = String(row.type ?? row.kind ?? '').toLowerCase()\n\t\t\tif (t === 'bill' || t === 'expense') {\n\t\t\t\treturn 'bill'\n\t\t\t}\n\t\t\treturn 'project'\n\t\t},\n\n\t\ttypeLabel(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? t('nextcloud-vue', 'Bill')\n\t\t\t\t: t('nextcloud-vue', 'Project')\n\t\t},\n\n\t\ttypeChipClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__type-chip--bill'\n\t\t\t\t: 'cn-cospend-tab__type-chip--project'\n\t\t},\n\n\t\ttypeVariant(row) {\n\t\t\treturn this.rowType(row) === 'bill' ? 'warning' : 'primary'\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row--bill'\n\t\t\t\t: 'cn-cospend-tab__row--project'\n\t\t},\n\n\t\ticonClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row-icon--bill'\n\t\t\t\t: 'cn-cospend-tab__row-icon--project'\n\t\t},\n\n\t\tamountClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__amount--bill'\n\t\t\t\t: 'cn-cospend-tab__amount--project'\n\t\t},\n\n\t\tamountRaw(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tcurrencyOf(row) {\n\t\t\treturn row.currency ?? row.currency_name ?? row.data?.currency_name ?? row.data?.currency ?? ''\n\t\t},\n\n\t\tamountLabel(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst currency = this.currencyOf(row)\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn currency ? `${raw} ${currency}` : String(raw)\n\t\t\t}\n\t\t\tconst fixed = num.toFixed(2)\n\t\t\treturn currency ? `${fixed} ${currency}` : fixed\n\t\t},\n\n\t\tpayerLabel(row) {\n\t\t\treturn row.payer ?? row.user_id ?? row.data?.user_id ?? row.data?.payer ?? ''\n\t\t},\n\n\t\tdateLabel(row) {\n\t\t\tconst raw = row.date ?? row.created_at ?? row.data?.date ?? row.data?.timestamp ?? null\n\t\t\tif (!raw) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = typeof raw === 'number'\n\t\t\t\t? new Date(raw * 1000)\n\t\t\t\t: new Date(raw)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(undefined, { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Subline shown under the title. For bills this is the payer and\n\t\t * date joined with a middot (\"alice · 20 May 2026\"); for projects\n\t\t * it is the currency (or payer) when present.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {string} Subline text, or '' when nothing to show.\n\t\t */\n\t\tsublineText(row) {\n\t\t\tconst parts = []\n\t\t\tif (this.rowType(row) === 'bill') {\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t\tconst date = this.dateLabel(row)\n\t\t\t\tif (date) {\n\t\t\t\t\tparts.push(date)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst currency = this.currencyOf(row)\n\t\t\t\tif (currency) {\n\t\t\t\t\tparts.push(currency)\n\t\t\t\t}\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cospend-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-cospend-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-cospend-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-cospend-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-cospend-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n/* Coloured left rail per row type, echoing Cospend's project/bill split. */\n.cn-cospend-tab__row {\n\tborder-radius: var(--border-radius);\n}\n\n.cn-cospend-tab__row--bill {\n\tborder-left: 3px solid var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row--project {\n\tborder-left: 3px solid var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 32px;\n\theight: 32px;\n\tborder-radius: 50%;\n\tcolor: var(--color-main-background);\n}\n\n.cn-cospend-tab__row-icon--bill {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row-icon--project {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__subline-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-cospend-tab__type-chip {\n\tflex-shrink: 0;\n}\n\n.cn-cospend-tab__amount {\n\tfont-weight: 700;\n\tfont-size: 0.95em;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__amount--bill {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n}\n\n.cn-cospend-tab__totals {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tmargin-top: 8px;\n\tpadding: 8px 10px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-cospend-tab__totals-label {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tfont-size: 0.78em;\n\tletter-spacing: 0.04em;\n}\n\n.cn-cospend-tab__totals-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\ttext-align: right;\n}\n\n.cn-cospend-tab__totals-row {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-cospend-tab__totals-currency {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__totals-amount {\n\tfont-weight: 700;\n\tcolor: var(--color-main-text);\n}\n</style>\n"],"names":["CashMultiple","CnStatusBadge","CnCospendPicker","CnCospendCreate","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnCospendTab.vue2.js","sources":["../../../../../src/integrations/builtin/cospend/CnCospendTab.vue"],"sourcesContent":["<!--\n CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n\n Replaces the generic CnIntegrationTab for the `cospend` leaf: renders\n the NC Cospend rows linked to the parent OR object as a Cospend-style\n list of projects and bills.\n Cospend exposes BOTH project rows AND bill rows in the same payload\n (per wave 2.3 design — see `CospendProvider.php`); each row carries\n a `type` discriminator (`'project'` | `'bill'`) which this tab shows\n as a `[Project]` / `[Bill]` status badge alongside the title.\n\n Rows render via NcListItem to mirror real Cospend:\n - project: name + [Project] badge + currency, payer (when present)\n - bill: title + [Bill] badge + payer · date subline, prominent\n right-aligned amount with currency\n A per-currency total footer summarises the linked spend, matching\n Cospend's project balance row.\n\n Clicking a project row deep-links to\n /index.php/apps/cospend/p/{id}\n Bills inherit the same URL (Cospend doesn't have a stable per-bill\n permalink — the project page lists its bills); a row's own `url`\n field wins if the provider already constructed one.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/cospend`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\CospendProvider`\n (link-table strategy — marker `[or:{uuid}]` in `cospend_projects.name`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Costs\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Cospend is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses Cospend's primary signals — *whether the row is a\n project or a bill*, the amount, and the currency. Surfacing them\n inline lets case handlers triage spending at a glance without\n context-switching into Cospend.\n\n See `openregister/openspec/changes/integration-cospend/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1324` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-cospend-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-cospend-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-cospend-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing project') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new project') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-cospend-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-cospend-tab__empty\">\n\t\t\t<CurrencyEur :size=\"32\" class=\"cn-cospend-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"cospendAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CurrencyEur :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openCospendLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<ul class=\"cn-cospend-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-cospend-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__row-icon\" :class=\"iconClass(row)\">\n\t\t\t\t\t\t\t<CashMultiple v-if=\"rowType(row) === 'bill'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FolderOutline v-else :size=\"20\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #name>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__title\">{{ rowTitle(row) }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__subline\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tclass=\"cn-cospend-tab__type-chip\"\n\t\t\t\t\t\t\t\t:class=\"typeChipClass(row)\"\n\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t:variant=\"typeVariant(row)\"\n\t\t\t\t\t\t\t\t:label=\"typeLabel(row)\" />\n\t\t\t\t\t\t\t<span v-if=\"sublineText(row)\" class=\"cn-cospend-tab__subline-text\">\n\t\t\t\t\t\t\t\t{{ sublineText(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #indicator>\n\t\t\t\t\t\t<span v-if=\"amountLabel(row)\" class=\"cn-cospend-tab__amount\" :class=\"amountClass(row)\">\n\t\t\t\t\t\t\t{{ amountLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tv-if=\"entryIdOf(row)\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkEntry(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\n\t\t\t<div v-if=\"totals.length > 0\" class=\"cn-cospend-tab__totals\">\n\t\t\t\t<span class=\"cn-cospend-tab__totals-label\">{{ t('nextcloud-vue', 'Total') }}</span>\n\t\t\t\t<ul class=\"cn-cospend-tab__totals-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in totals\"\n\t\t\t\t\t\t:key=\"bucket.currency\"\n\t\t\t\t\t\tclass=\"cn-cospend-tab__totals-row\">\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-currency\">{{ bucket.currency }}</span>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-amount\">{{ bucket.amountLabel }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<CnCospendPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnCospendCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CashMultiple from 'vue-material-design-icons/CashMultiple.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCospendCreate from '../../../components/CnCospendCreate/CnCospendCreate.vue'\nimport CnCospendPicker from '../../../components/CnCospendPicker/CnCospendPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\n\n/**\n * CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n *\n * Renders linked Cospend projects + bills with a type badge, amount,\n * currency, payer, date and a per-currency total footer. See the\n * file-level docblock for surface behaviour.\n */\nexport default {\n\tname: 'CnCospendTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tCashMultiple,\n\t\tCurrencyEur,\n\t\tFolderOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnCospendPicker,\n\t\tCnCospendCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'cospend'`). */\n\t\tintegrationId: { type: String, default: 'cospend' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No costs linked yet') },\n\t\t/** Pre-translated label for the \"Open Costs\" CTA. */\n\t\topenCospendLabel: { type: String, default: () => t('nextcloud-vue', 'Open Costs') },\n\t\t/** Pre-translated banner when Cospend is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Costs is currently unavailable.') },\n\t\t/** URL of the NC Cospend app entry. */\n\t\tcospendAppUrl: { type: String, default: '/index.php/apps/cospend' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Per-currency totals of every row carrying an amount, mirroring\n\t\t * the balance row Cospend renders under a project.\n\t\t *\n\t\t * @return {Array<{currency: string, amountLabel: string}>}\n\t\t */\n\t\ttotals() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst amount = this.amountRaw(row)\n\t\t\t\tif (amount === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst currency = this.currencyOf(row) || ''\n\t\t\t\tconst existing = groups.get(currency) || { currency, total: 0 }\n\t\t\t\texisting.total += amount\n\t\t\t\tgroups.set(currency, existing)\n\t\t\t}\n\t\t\treturn Array.from(groups.values()).map((b) => {\n\t\t\t\tconst code = b.currency || t('nextcloud-vue', 'Unspecified')\n\t\t\t\treturn {\n\t\t\t\t\tcurrency: code,\n\t\t\t\t\tamountLabel: b.currency ? `${b.total.toFixed(2)} ${b.currency}` : b.total.toFixed(2),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 cospend endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tcospendEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/cospend`)\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? ''\n\t\t},\n\n\t\t/**\n\t\t * Resolve the link-row id for a Tier-2 row (used for unlink).\n\t\t * Legacy marker rows have no `entryId` so the unlink button hides.\n\t\t *\n\t\t * @param {object} row Provider/link row.\n\t\t *\n\t\t * @return {(number|string)} The link row id, or '' when absent.\n\t\t */\n\t\tentryIdOf(row) {\n\t\t\tconst d = (row && typeof row.data === 'object' && row.data !== null) ? row.data : {}\n\t\t\treturn row.entryId ?? d.id ?? ''\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.cospendEndpoint(), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/new`, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkEntry(row) {\n\t\t\tconst id = this.entryIdOf(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/${id}`, {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null\n\t\t\tif (projectId !== null && projectId !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${projectId}`\n\t\t\t}\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (id !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${id}`\n\t\t\t}\n\t\t\treturn this.cospendAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'project' when the provider doesn't\n\t\t * surface a `type` field (the current shipping CospendProvider\n\t\t * only returns projects); when wave-2.3 ships bills the chip\n\t\t * label flips accordingly.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'project'|'bill'} Row type.\n\t\t */\n\t\trowType(row) {\n\t\t\tconst t = String(row.type ?? row.kind ?? '').toLowerCase()\n\t\t\tif (t === 'bill' || t === 'expense') {\n\t\t\t\treturn 'bill'\n\t\t\t}\n\t\t\treturn 'project'\n\t\t},\n\n\t\ttypeLabel(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? t('nextcloud-vue', 'Bill')\n\t\t\t\t: t('nextcloud-vue', 'Project')\n\t\t},\n\n\t\ttypeChipClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__type-chip--bill'\n\t\t\t\t: 'cn-cospend-tab__type-chip--project'\n\t\t},\n\n\t\ttypeVariant(row) {\n\t\t\treturn this.rowType(row) === 'bill' ? 'warning' : 'primary'\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row--bill'\n\t\t\t\t: 'cn-cospend-tab__row--project'\n\t\t},\n\n\t\ticonClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row-icon--bill'\n\t\t\t\t: 'cn-cospend-tab__row-icon--project'\n\t\t},\n\n\t\tamountClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__amount--bill'\n\t\t\t\t: 'cn-cospend-tab__amount--project'\n\t\t},\n\n\t\tamountRaw(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tcurrencyOf(row) {\n\t\t\treturn row.currency ?? row.currency_name ?? row.data?.currency_name ?? row.data?.currency ?? ''\n\t\t},\n\n\t\tamountLabel(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst currency = this.currencyOf(row)\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn currency ? `${raw} ${currency}` : String(raw)\n\t\t\t}\n\t\t\tconst fixed = num.toFixed(2)\n\t\t\treturn currency ? `${fixed} ${currency}` : fixed\n\t\t},\n\n\t\tpayerLabel(row) {\n\t\t\treturn row.payer ?? row.user_id ?? row.data?.user_id ?? row.data?.payer ?? ''\n\t\t},\n\n\t\tdateLabel(row) {\n\t\t\tconst raw = row.date ?? row.created_at ?? row.data?.date ?? row.data?.timestamp ?? null\n\t\t\tif (!raw) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = typeof raw === 'number'\n\t\t\t\t? new Date(raw * 1000)\n\t\t\t\t: new Date(raw)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Subline shown under the title. For bills this is the payer and\n\t\t * date joined with a middot (\"alice · 20 May 2026\"); for projects\n\t\t * it is the currency (or payer) when present.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {string} Subline text, or '' when nothing to show.\n\t\t */\n\t\tsublineText(row) {\n\t\t\tconst parts = []\n\t\t\tif (this.rowType(row) === 'bill') {\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t\tconst date = this.dateLabel(row)\n\t\t\t\tif (date) {\n\t\t\t\t\tparts.push(date)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst currency = this.currencyOf(row)\n\t\t\t\tif (currency) {\n\t\t\t\t\tparts.push(currency)\n\t\t\t\t}\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cospend-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-cospend-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-cospend-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-cospend-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-cospend-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n/* Coloured left rail per row type, echoing Cospend's project/bill split. */\n.cn-cospend-tab__row {\n\tborder-radius: var(--border-radius);\n}\n\n.cn-cospend-tab__row--bill {\n\tborder-left: 3px solid var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row--project {\n\tborder-left: 3px solid var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 32px;\n\theight: 32px;\n\tborder-radius: 50%;\n\tcolor: var(--color-main-background);\n}\n\n.cn-cospend-tab__row-icon--bill {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row-icon--project {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__subline-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-cospend-tab__type-chip {\n\tflex-shrink: 0;\n}\n\n.cn-cospend-tab__amount {\n\tfont-weight: 700;\n\tfont-size: 0.95em;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__amount--bill {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n}\n\n.cn-cospend-tab__totals {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tmargin-top: 8px;\n\tpadding: 8px 10px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-cospend-tab__totals-label {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tfont-size: 0.78em;\n\tletter-spacing: 0.04em;\n}\n\n.cn-cospend-tab__totals-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\ttext-align: right;\n}\n\n.cn-cospend-tab__totals-row {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-cospend-tab__totals-currency {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__totals-amount {\n\tfont-weight: 700;\n\tcolor: var(--color-main-text);\n}\n</style>\n"],"names":["CashMultiple","CnStatusBadge","CnCospendPicker","CnCospendCreate","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnCospendTab.vue3.js","sources":["../../../../../src/integrations/builtin/cospend/CnCospendTab.vue?vue&type=template&id=06d4adfe&lang.js"],"sourcesContent":["<!--\n CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n\n Replaces the generic CnIntegrationTab for the `cospend` leaf: renders\n the NC Cospend rows linked to the parent OR object as a Cospend-style\n list of projects and bills.\n Cospend exposes BOTH project rows AND bill rows in the same payload\n (per wave 2.3 design — see `CospendProvider.php`); each row carries\n a `type` discriminator (`'project'` | `'bill'`) which this tab shows\n as a `[Project]` / `[Bill]` status badge alongside the title.\n\n Rows render via NcListItem to mirror real Cospend:\n - project: name + [Project] badge + currency, payer (when present)\n - bill: title + [Bill] badge + payer · date subline, prominent\n right-aligned amount with currency\n A per-currency total footer summarises the linked spend, matching\n Cospend's project balance row.\n\n Clicking a project row deep-links to\n /index.php/apps/cospend/p/{id}\n Bills inherit the same URL (Cospend doesn't have a stable per-bill\n permalink — the project page lists its bills); a row's own `url`\n field wins if the provider already constructed one.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/cospend`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\CospendProvider`\n (link-table strategy — marker `[or:{uuid}]` in `cospend_projects.name`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Costs\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Cospend is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses Cospend's primary signals — *whether the row is a\n project or a bill*, the amount, and the currency. Surfacing them\n inline lets case handlers triage spending at a glance without\n context-switching into Cospend.\n\n See `openregister/openspec/changes/integration-cospend/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1324` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-cospend-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-cospend-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-cospend-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing project') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new project') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-cospend-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-cospend-tab__empty\">\n\t\t\t<CurrencyEur :size=\"32\" class=\"cn-cospend-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"cospendAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CurrencyEur :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openCospendLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<ul class=\"cn-cospend-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-cospend-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__row-icon\" :class=\"iconClass(row)\">\n\t\t\t\t\t\t\t<CashMultiple v-if=\"rowType(row) === 'bill'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FolderOutline v-else :size=\"20\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #name>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__title\">{{ rowTitle(row) }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__subline\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tclass=\"cn-cospend-tab__type-chip\"\n\t\t\t\t\t\t\t\t:class=\"typeChipClass(row)\"\n\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t:variant=\"typeVariant(row)\"\n\t\t\t\t\t\t\t\t:label=\"typeLabel(row)\" />\n\t\t\t\t\t\t\t<span v-if=\"sublineText(row)\" class=\"cn-cospend-tab__subline-text\">\n\t\t\t\t\t\t\t\t{{ sublineText(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #indicator>\n\t\t\t\t\t\t<span v-if=\"amountLabel(row)\" class=\"cn-cospend-tab__amount\" :class=\"amountClass(row)\">\n\t\t\t\t\t\t\t{{ amountLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tv-if=\"entryIdOf(row)\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkEntry(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\n\t\t\t<div v-if=\"totals.length > 0\" class=\"cn-cospend-tab__totals\">\n\t\t\t\t<span class=\"cn-cospend-tab__totals-label\">{{ t('nextcloud-vue', 'Total') }}</span>\n\t\t\t\t<ul class=\"cn-cospend-tab__totals-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in totals\"\n\t\t\t\t\t\t:key=\"bucket.currency\"\n\t\t\t\t\t\tclass=\"cn-cospend-tab__totals-row\">\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-currency\">{{ bucket.currency }}</span>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-amount\">{{ bucket.amountLabel }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<CnCospendPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnCospendCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CashMultiple from 'vue-material-design-icons/CashMultiple.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCospendCreate from '../../../components/CnCospendCreate/CnCospendCreate.vue'\nimport CnCospendPicker from '../../../components/CnCospendPicker/CnCospendPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n *\n * Renders linked Cospend projects + bills with a type badge, amount,\n * currency, payer, date and a per-currency total footer. See the\n * file-level docblock for surface behaviour.\n */\nexport default {\n\tname: 'CnCospendTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tCashMultiple,\n\t\tCurrencyEur,\n\t\tFolderOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnCospendPicker,\n\t\tCnCospendCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'cospend'`). */\n\t\tintegrationId: { type: String, default: 'cospend' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No costs linked yet') },\n\t\t/** Pre-translated label for the \"Open Costs\" CTA. */\n\t\topenCospendLabel: { type: String, default: () => t('nextcloud-vue', 'Open Costs') },\n\t\t/** Pre-translated banner when Cospend is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Costs is currently unavailable.') },\n\t\t/** URL of the NC Cospend app entry. */\n\t\tcospendAppUrl: { type: String, default: '/index.php/apps/cospend' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Per-currency totals of every row carrying an amount, mirroring\n\t\t * the balance row Cospend renders under a project.\n\t\t *\n\t\t * @return {Array<{currency: string, amountLabel: string}>}\n\t\t */\n\t\ttotals() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst amount = this.amountRaw(row)\n\t\t\t\tif (amount === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst currency = this.currencyOf(row) || ''\n\t\t\t\tconst existing = groups.get(currency) || { currency, total: 0 }\n\t\t\t\texisting.total += amount\n\t\t\t\tgroups.set(currency, existing)\n\t\t\t}\n\t\t\treturn Array.from(groups.values()).map((b) => {\n\t\t\t\tconst code = b.currency || t('nextcloud-vue', 'Unspecified')\n\t\t\t\treturn {\n\t\t\t\t\tcurrency: code,\n\t\t\t\t\tamountLabel: b.currency ? `${b.total.toFixed(2)} ${b.currency}` : b.total.toFixed(2),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 cospend endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tcospendEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/cospend`)\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? ''\n\t\t},\n\n\t\t/**\n\t\t * Resolve the link-row id for a Tier-2 row (used for unlink).\n\t\t * Legacy marker rows have no `entryId` so the unlink button hides.\n\t\t *\n\t\t * @param {object} row Provider/link row.\n\t\t *\n\t\t * @return {(number|string)} The link row id, or '' when absent.\n\t\t */\n\t\tentryIdOf(row) {\n\t\t\tconst d = (row && typeof row.data === 'object' && row.data !== null) ? row.data : {}\n\t\t\treturn row.entryId ?? d.id ?? ''\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.cospendEndpoint(), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/new`, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkEntry(row) {\n\t\t\tconst id = this.entryIdOf(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/${id}`, {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null\n\t\t\tif (projectId !== null && projectId !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${projectId}`\n\t\t\t}\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (id !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${id}`\n\t\t\t}\n\t\t\treturn this.cospendAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'project' when the provider doesn't\n\t\t * surface a `type` field (the current shipping CospendProvider\n\t\t * only returns projects); when wave-2.3 ships bills the chip\n\t\t * label flips accordingly.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'project'|'bill'} Row type.\n\t\t */\n\t\trowType(row) {\n\t\t\tconst t = String(row.type ?? row.kind ?? '').toLowerCase()\n\t\t\tif (t === 'bill' || t === 'expense') {\n\t\t\t\treturn 'bill'\n\t\t\t}\n\t\t\treturn 'project'\n\t\t},\n\n\t\ttypeLabel(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? t('nextcloud-vue', 'Bill')\n\t\t\t\t: t('nextcloud-vue', 'Project')\n\t\t},\n\n\t\ttypeChipClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__type-chip--bill'\n\t\t\t\t: 'cn-cospend-tab__type-chip--project'\n\t\t},\n\n\t\ttypeVariant(row) {\n\t\t\treturn this.rowType(row) === 'bill' ? 'warning' : 'primary'\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row--bill'\n\t\t\t\t: 'cn-cospend-tab__row--project'\n\t\t},\n\n\t\ticonClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row-icon--bill'\n\t\t\t\t: 'cn-cospend-tab__row-icon--project'\n\t\t},\n\n\t\tamountClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__amount--bill'\n\t\t\t\t: 'cn-cospend-tab__amount--project'\n\t\t},\n\n\t\tamountRaw(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tcurrencyOf(row) {\n\t\t\treturn row.currency ?? row.currency_name ?? row.data?.currency_name ?? row.data?.currency ?? ''\n\t\t},\n\n\t\tamountLabel(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst currency = this.currencyOf(row)\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn currency ? `${raw} ${currency}` : String(raw)\n\t\t\t}\n\t\t\tconst fixed = num.toFixed(2)\n\t\t\treturn currency ? `${fixed} ${currency}` : fixed\n\t\t},\n\n\t\tpayerLabel(row) {\n\t\t\treturn row.payer ?? row.user_id ?? row.data?.user_id ?? row.data?.payer ?? ''\n\t\t},\n\n\t\tdateLabel(row) {\n\t\t\tconst raw = row.date ?? row.created_at ?? row.data?.date ?? row.data?.timestamp ?? null\n\t\t\tif (!raw) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = typeof raw === 'number'\n\t\t\t\t? new Date(raw * 1000)\n\t\t\t\t: new Date(raw)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(undefined, { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Subline shown under the title. For bills this is the payer and\n\t\t * date joined with a middot (\"alice · 20 May 2026\"); for projects\n\t\t * it is the currency (or payer) when present.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {string} Subline text, or '' when nothing to show.\n\t\t */\n\t\tsublineText(row) {\n\t\t\tconst parts = []\n\t\t\tif (this.rowType(row) === 'bill') {\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t\tconst date = this.dateLabel(row)\n\t\t\t\tif (date) {\n\t\t\t\t\tparts.push(date)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst currency = this.currencyOf(row)\n\t\t\t\tif (currency) {\n\t\t\t\t\tparts.push(currency)\n\t\t\t\t}\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cospend-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-cospend-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-cospend-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-cospend-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-cospend-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n/* Coloured left rail per row type, echoing Cospend's project/bill split. */\n.cn-cospend-tab__row {\n\tborder-radius: var(--border-radius);\n}\n\n.cn-cospend-tab__row--bill {\n\tborder-left: 3px solid var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row--project {\n\tborder-left: 3px solid var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 32px;\n\theight: 32px;\n\tborder-radius: 50%;\n\tcolor: var(--color-main-background);\n}\n\n.cn-cospend-tab__row-icon--bill {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row-icon--project {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__subline-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-cospend-tab__type-chip {\n\tflex-shrink: 0;\n}\n\n.cn-cospend-tab__amount {\n\tfont-weight: 700;\n\tfont-size: 0.95em;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__amount--bill {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n}\n\n.cn-cospend-tab__totals {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tmargin-top: 8px;\n\tpadding: 8px 10px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-cospend-tab__totals-label {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tfont-size: 0.78em;\n\tletter-spacing: 0.04em;\n}\n\n.cn-cospend-tab__totals-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\ttext-align: right;\n}\n\n.cn-cospend-tab__totals-row {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-cospend-tab__totals-currency {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__totals-amount {\n\tfont-weight: 700;\n\tcolor: var(--color-main-text);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_createBlock","_toDisplayString","_normalizeClass"],"mappings":"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"}
1
+ {"version":3,"file":"CnCospendTab.vue3.js","sources":["../../../../../src/integrations/builtin/cospend/CnCospendTab.vue?vue&type=template&id=1d48ce21&lang.js"],"sourcesContent":["<!--\n CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n\n Replaces the generic CnIntegrationTab for the `cospend` leaf: renders\n the NC Cospend rows linked to the parent OR object as a Cospend-style\n list of projects and bills.\n Cospend exposes BOTH project rows AND bill rows in the same payload\n (per wave 2.3 design — see `CospendProvider.php`); each row carries\n a `type` discriminator (`'project'` | `'bill'`) which this tab shows\n as a `[Project]` / `[Bill]` status badge alongside the title.\n\n Rows render via NcListItem to mirror real Cospend:\n - project: name + [Project] badge + currency, payer (when present)\n - bill: title + [Bill] badge + payer · date subline, prominent\n right-aligned amount with currency\n A per-currency total footer summarises the linked spend, matching\n Cospend's project balance row.\n\n Clicking a project row deep-links to\n /index.php/apps/cospend/p/{id}\n Bills inherit the same URL (Cospend doesn't have a stable per-bill\n permalink — the project page lists its bills); a row's own `url`\n field wins if the provider already constructed one.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/cospend`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\CospendProvider`\n (link-table strategy — marker `[or:{uuid}]` in `cospend_projects.name`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Costs\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Cospend is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses Cospend's primary signals — *whether the row is a\n project or a bill*, the amount, and the currency. Surfacing them\n inline lets case handlers triage spending at a glance without\n context-switching into Cospend.\n\n See `openregister/openspec/changes/integration-cospend/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1324` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-cospend-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-cospend-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-cospend-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing project') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new project') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-cospend-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-cospend-tab__empty\">\n\t\t\t<CurrencyEur :size=\"32\" class=\"cn-cospend-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"cospendAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CurrencyEur :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openCospendLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<ul class=\"cn-cospend-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-cospend-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__row-icon\" :class=\"iconClass(row)\">\n\t\t\t\t\t\t\t<CashMultiple v-if=\"rowType(row) === 'bill'\" :size=\"20\" />\n\t\t\t\t\t\t\t<FolderOutline v-else :size=\"20\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #name>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__title\">{{ rowTitle(row) }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__subline\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tclass=\"cn-cospend-tab__type-chip\"\n\t\t\t\t\t\t\t\t:class=\"typeChipClass(row)\"\n\t\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\t\t:variant=\"typeVariant(row)\"\n\t\t\t\t\t\t\t\t:label=\"typeLabel(row)\" />\n\t\t\t\t\t\t\t<span v-if=\"sublineText(row)\" class=\"cn-cospend-tab__subline-text\">\n\t\t\t\t\t\t\t\t{{ sublineText(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #indicator>\n\t\t\t\t\t\t<span v-if=\"amountLabel(row)\" class=\"cn-cospend-tab__amount\" :class=\"amountClass(row)\">\n\t\t\t\t\t\t\t{{ amountLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tv-if=\"entryIdOf(row)\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkEntry(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\n\t\t\t<div v-if=\"totals.length > 0\" class=\"cn-cospend-tab__totals\">\n\t\t\t\t<span class=\"cn-cospend-tab__totals-label\">{{ t('nextcloud-vue', 'Total') }}</span>\n\t\t\t\t<ul class=\"cn-cospend-tab__totals-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in totals\"\n\t\t\t\t\t\t:key=\"bucket.currency\"\n\t\t\t\t\t\tclass=\"cn-cospend-tab__totals-row\">\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-currency\">{{ bucket.currency }}</span>\n\t\t\t\t\t\t<span class=\"cn-cospend-tab__totals-amount\">{{ bucket.amountLabel }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<CnCospendPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnCospendCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CashMultiple from 'vue-material-design-icons/CashMultiple.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport FolderOutline from 'vue-material-design-icons/FolderOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCospendCreate from '../../../components/CnCospendCreate/CnCospendCreate.vue'\nimport CnCospendPicker from '../../../components/CnCospendPicker/CnCospendPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\n\n/**\n * CnCospendTab — bespoke sidebar tab for the `cospend` integration.\n *\n * Renders linked Cospend projects + bills with a type badge, amount,\n * currency, payer, date and a per-currency total footer. See the\n * file-level docblock for surface behaviour.\n */\nexport default {\n\tname: 'CnCospendTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tCashMultiple,\n\t\tCurrencyEur,\n\t\tFolderOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tCnStatusBadge,\n\t\tCnCospendPicker,\n\t\tCnCospendCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'cospend'`). */\n\t\tintegrationId: { type: String, default: 'cospend' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No costs linked yet') },\n\t\t/** Pre-translated label for the \"Open Costs\" CTA. */\n\t\topenCospendLabel: { type: String, default: () => t('nextcloud-vue', 'Open Costs') },\n\t\t/** Pre-translated banner when Cospend is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Costs is currently unavailable.') },\n\t\t/** URL of the NC Cospend app entry. */\n\t\tcospendAppUrl: { type: String, default: '/index.php/apps/cospend' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Per-currency totals of every row carrying an amount, mirroring\n\t\t * the balance row Cospend renders under a project.\n\t\t *\n\t\t * @return {Array<{currency: string, amountLabel: string}>}\n\t\t */\n\t\ttotals() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst amount = this.amountRaw(row)\n\t\t\t\tif (amount === null) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst currency = this.currencyOf(row) || ''\n\t\t\t\tconst existing = groups.get(currency) || { currency, total: 0 }\n\t\t\t\texisting.total += amount\n\t\t\t\tgroups.set(currency, existing)\n\t\t\t}\n\t\t\treturn Array.from(groups.values()).map((b) => {\n\t\t\t\tconst code = b.currency || t('nextcloud-vue', 'Unspecified')\n\t\t\t\treturn {\n\t\t\t\t\tcurrency: code,\n\t\t\t\t\tamountLabel: b.currency ? `${b.total.toFixed(2)} ${b.currency}` : b.total.toFixed(2),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 cospend endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tcospendEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/cospend`)\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? ''\n\t\t},\n\n\t\t/**\n\t\t * Resolve the link-row id for a Tier-2 row (used for unlink).\n\t\t * Legacy marker rows have no `entryId` so the unlink button hides.\n\t\t *\n\t\t * @param {object} row Provider/link row.\n\t\t *\n\t\t * @return {(number|string)} The link row id, or '' when absent.\n\t\t */\n\t\tentryIdOf(row) {\n\t\t\tconst d = (row && typeof row.data === 'object' && row.data !== null) ? row.data : {}\n\t\t\treturn row.entryId ?? d.id ?? ''\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.cospendEndpoint(), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/new`, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create project.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkEntry(row) {\n\t\t\tconst id = this.entryIdOf(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.cospendEndpoint()}/${id}`, {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null\n\t\t\tif (projectId !== null && projectId !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${projectId}`\n\t\t\t}\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (id !== '') {\n\t\t\t\treturn `/index.php/apps/cospend/p/${id}`\n\t\t\t}\n\t\t\treturn this.cospendAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'project' when the provider doesn't\n\t\t * surface a `type` field (the current shipping CospendProvider\n\t\t * only returns projects); when wave-2.3 ships bills the chip\n\t\t * label flips accordingly.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'project'|'bill'} Row type.\n\t\t */\n\t\trowType(row) {\n\t\t\tconst t = String(row.type ?? row.kind ?? '').toLowerCase()\n\t\t\tif (t === 'bill' || t === 'expense') {\n\t\t\t\treturn 'bill'\n\t\t\t}\n\t\t\treturn 'project'\n\t\t},\n\n\t\ttypeLabel(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? t('nextcloud-vue', 'Bill')\n\t\t\t\t: t('nextcloud-vue', 'Project')\n\t\t},\n\n\t\ttypeChipClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__type-chip--bill'\n\t\t\t\t: 'cn-cospend-tab__type-chip--project'\n\t\t},\n\n\t\ttypeVariant(row) {\n\t\t\treturn this.rowType(row) === 'bill' ? 'warning' : 'primary'\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row--bill'\n\t\t\t\t: 'cn-cospend-tab__row--project'\n\t\t},\n\n\t\ticonClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__row-icon--bill'\n\t\t\t\t: 'cn-cospend-tab__row-icon--project'\n\t\t},\n\n\t\tamountClass(row) {\n\t\t\treturn this.rowType(row) === 'bill'\n\t\t\t\t? 'cn-cospend-tab__amount--bill'\n\t\t\t\t: 'cn-cospend-tab__amount--project'\n\t\t},\n\n\t\tamountRaw(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\tcurrencyOf(row) {\n\t\t\treturn row.currency ?? row.currency_name ?? row.data?.currency_name ?? row.data?.currency ?? ''\n\t\t},\n\n\t\tamountLabel(row) {\n\t\t\tconst raw = row.amount ?? row.data?.amount ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst currency = this.currencyOf(row)\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn currency ? `${raw} ${currency}` : String(raw)\n\t\t\t}\n\t\t\tconst fixed = num.toFixed(2)\n\t\t\treturn currency ? `${fixed} ${currency}` : fixed\n\t\t},\n\n\t\tpayerLabel(row) {\n\t\t\treturn row.payer ?? row.user_id ?? row.data?.user_id ?? row.data?.payer ?? ''\n\t\t},\n\n\t\tdateLabel(row) {\n\t\t\tconst raw = row.date ?? row.created_at ?? row.data?.date ?? row.data?.timestamp ?? null\n\t\t\tif (!raw) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = typeof raw === 'number'\n\t\t\t\t? new Date(raw * 1000)\n\t\t\t\t: new Date(raw)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Subline shown under the title. For bills this is the payer and\n\t\t * date joined with a middot (\"alice · 20 May 2026\"); for projects\n\t\t * it is the currency (or payer) when present.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {string} Subline text, or '' when nothing to show.\n\t\t */\n\t\tsublineText(row) {\n\t\t\tconst parts = []\n\t\t\tif (this.rowType(row) === 'bill') {\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t\tconst date = this.dateLabel(row)\n\t\t\t\tif (date) {\n\t\t\t\t\tparts.push(date)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst currency = this.currencyOf(row)\n\t\t\t\tif (currency) {\n\t\t\t\t\tparts.push(currency)\n\t\t\t\t}\n\t\t\t\tconst payer = this.payerLabel(row)\n\t\t\t\tif (payer) {\n\t\t\t\t\tparts.push(payer)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCospendTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load costs.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cospend-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-cospend-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-cospend-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-cospend-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-cospend-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n/* Coloured left rail per row type, echoing Cospend's project/bill split. */\n.cn-cospend-tab__row {\n\tborder-radius: var(--border-radius);\n}\n\n.cn-cospend-tab__row--bill {\n\tborder-left: 3px solid var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row--project {\n\tborder-left: 3px solid var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 32px;\n\theight: 32px;\n\tborder-radius: 50%;\n\tcolor: var(--color-main-background);\n}\n\n.cn-cospend-tab__row-icon--bill {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-cospend-tab__row-icon--project {\n\tbackground: var(--color-primary-element, #21468B);\n}\n\n.cn-cospend-tab__title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__subline-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-cospend-tab__type-chip {\n\tflex-shrink: 0;\n}\n\n.cn-cospend-tab__amount {\n\tfont-weight: 700;\n\tfont-size: 0.95em;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-cospend-tab__amount--bill {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n}\n\n.cn-cospend-tab__totals {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tmargin-top: 8px;\n\tpadding: 8px 10px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-cospend-tab__totals-label {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tfont-size: 0.78em;\n\tletter-spacing: 0.04em;\n}\n\n.cn-cospend-tab__totals-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\ttext-align: right;\n}\n\n.cn-cospend-tab__totals-row {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: flex-end;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-cospend-tab__totals-currency {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cospend-tab__totals-amount {\n\tfont-weight: 700;\n\tcolor: var(--color-main-text);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_createBlock","_toDisplayString","_normalizeClass"],"mappings":"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"}
@@ -2,7 +2,7 @@ import script from './CnDeckTab.vue2.js';
2
2
  import { render } from './CnDeckTab.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-8813a904";
5
+ script.__scopeId = "data-v-d4b5abb2";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -8,6 +8,7 @@ import ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue';
8
8
  import __sfc_main$1 from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue.js';
9
9
  import __sfc_main from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue.js';
10
10
  import __sfc_main$2 from '../../../components/CnStatusBadge/CnStatusBadge.vue.js';
11
+ import { userLocale } from '../../../utils/userLocale.js';
11
12
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
12
13
  import { safeHref } from '../../../utils/safeHref.js';
13
14
 
@@ -269,7 +270,7 @@ var script = {
269
270
  if (Number.isNaN(date.getTime())) {
270
271
  return ''
271
272
  }
272
- return date.toLocaleDateString(undefined, { dateStyle: 'medium' })
273
+ return date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })
273
274
  },
274
275
 
275
276
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"CnDeckTab.vue2.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckTab.vue"],"sourcesContent":["<!--\n CnDeckTab — bespoke sidebar tab for the `deck` integration.\n\n Replaces the generic CnIntegrationTab for the `deck` leaf: renders a\n kanban-mini view of the cards linked to the parent OR object, grouped\n by stack (\"To Do\" / \"Doing\" / \"Done\" or whatever stacks the cards live\n in). Each card row shows title, optional due date, and optional label\n chips. Clicking a row deep-links to the card in NC Deck.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/deck`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\DeckProvider`\n (which delegates to `OCA\\OpenRegister\\Service\\DeckCardService`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Deck\" CTA when no linked cards.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Deck is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Deck's primary signal — *where the card sits in the\n workflow* (stack name). Grouping by stack reproduces the kanban affordance\n inline so case handlers can see \"card X is in Doing\" at a glance.\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta, ADR-019 (registry mechanism) and ADR-022 (sidebar tab contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-deck-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-deck-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-deck-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing card') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new card') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-deck-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-sidebar-tab__empty cn-deck-tab__empty\">\n\t\t\t<ViewColumnOutline :size=\"32\" class=\"cn-deck-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"deckAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ViewColumnOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openDeckLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-deck-tab__kanban\">\n\t\t\t<section\n\t\t\t\tv-for=\"stack in stackColumns\"\n\t\t\t\t:key=\"stack.key\"\n\t\t\t\tclass=\"cn-deck-tab__column\">\n\t\t\t\t<header class=\"cn-deck-tab__column-header\">\n\t\t\t\t\t<span class=\"cn-deck-tab__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t<span class=\"cn-deck-tab__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-deck-tab__column-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\tclass=\"cn-deck-tab__card\"\n\t\t\t\t\t\t:class=\"{ 'cn-deck-tab__card--overdue': isOverdue(card) }\">\n\t\t\t\t\t\t<!-- Colored label strip along the top edge, like a real Deck card. -->\n\t\t\t\t\t\t<div v-if=\"cardLabels(card).length > 0\" class=\"cn-deck-tab__card-labels\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"label in cardLabels(card)\"\n\t\t\t\t\t\t\t\t:key=\"label.id || label.title\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__chip\"\n\t\t\t\t\t\t\t\t:style=\"chipStyle(label)\"\n\t\t\t\t\t\t\t\t:title=\"chipText(label)\">\n\t\t\t\t\t\t\t\t{{ chipText(label) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Board / stack context badge above the title. -->\n\t\t\t\t\t\t<span v-if=\"boardBadge(card)\" class=\"cn-deck-tab__card-board\">\n\t\t\t\t\t\t\t<ViewColumnOutline :size=\"12\" />\n\t\t\t\t\t\t\t{{ boardBadge(card) }}\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-title\">{{ cardTitle(card) }}</a>\n\n\t\t\t\t\t\t<!-- Footer: due-date chip + assignee avatars, like Deck's card footer. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"dueLabel(card) || cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-footer\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:label=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:variant=\"dueVariant(card)\"\n\t\t\t\t\t\t\t\tsize=\"small\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<ClockOutline :size=\"12\" class=\"cn-deck-tab__due-icon\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</CnStatusBadge>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatars\">\n\t\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t\tv-for=\"assignee in visibleAssignees(card)\"\n\t\t\t\t\t\t\t\t\t:key=\"assigneeKey(assignee)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar\"\n\t\t\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t\t\t:displayName=\"assigneeName(assignee)\"\n\t\t\t\t\t\t\t\t\t:user=\"assigneeSeed(assignee)\"\n\t\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"assigneeOverflow(card) > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar-overflow\"\n\t\t\t\t\t\t\t\t\t:title=\"overflowTitle(card)\">+{{ assigneeOverflow(card) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</div>\n\n\t\t<CnDeckCardPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnDeckCardCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'\nimport CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */\nconst MAX_VISIBLE_ASSIGNEES = 3\n\n/**\n * CnDeckTab — kanban-mini sidebar tab for the `deck` integration.\n *\n * Groups linked cards by stack so case handlers see workflow position\n * inline; deep-links to NC Deck for editing. See file-level docblock\n * for surface behaviour.\n */\nexport default {\n\tname: 'CnDeckTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tClockOutline,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tViewColumnOutline,\n\t\tCnDeckCardPicker,\n\t\tCnDeckCardCreate,\n\t\tCnStatusBadge,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'deck'`). */\n\t\tintegrationId: { type: String, default: 'deck' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No cards linked yet') },\n\t\t/** Pre-translated label for the \"Open Deck\" CTA. */\n\t\topenDeckLabel: { type: String, default: () => t('nextcloud-vue', 'Open Deck') },\n\t\t/** Pre-translated banner when Deck is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Deck is currently unavailable.') },\n\t\t/** URL of the NC Deck app entry. */\n\t\tdeckAppUrl: { type: String, default: '/index.php/apps/deck' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tcards: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Cards grouped by stack — preserves stack order via first-seen ordering.\n\t\t *\n\t\t * Each column is `{ key, label, cards: [] }`. The `key` is the\n\t\t * numeric stack id (or 'unstacked' for cards without one); the\n\t\t * `label` is the stack title when the provider returned one,\n\t\t * otherwise a placeholder.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tstackColumns() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const card of this.cards) {\n\t\t\t\tconst stackId = card.stackId ?? card.stack?.id ?? 'unstacked'\n\t\t\t\tconst stackLabel = card.stackTitle\n\t\t\t\t\t?? card.stack?.title\n\t\t\t\t\t?? (stackId === 'unstacked'\n\t\t\t\t\t\t? t('nextcloud-vue', 'No stack')\n\t\t\t\t\t\t: t('nextcloud-vue', 'Stack {n}', { n: stackId }))\n\t\t\t\tconst key = String(stackId)\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label: stackLabel, cards: [] })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).cards.push(card)\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchCards()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchCards()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchCards()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 deck endpoints (link/new). The provider's\n\t\t * generic `/integrations/deck` surface accepts the same POST\n\t\t * shape, but the bespoke Tier-2 routes are nicer for the\n\t\t * picker's `/deck/new` create-flow.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tdeckEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.deckEndpoint()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchCards()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This card is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.deckEndpoint()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchCards()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t}\n\t\t},\n\n\t\tcardKey(card) {\n\t\t\treturn card.cardId ?? card.id ?? ''\n\t\t},\n\n\t\tcardTitle(card) {\n\t\t\treturn card.cardTitle ?? card.title ?? this.cardKey(card)\n\t\t},\n\n\t\tcardUrl(card) {\n\t\t\tif (card.url && safeHref(card.url) !== '#') {\n\t\t\t\treturn card.url\n\t\t\t}\n\t\t\tconst boardId = card.boardId ?? card.board?.id ?? ''\n\t\t\tconst cardId = card.cardId ?? card.id ?? ''\n\t\t\tif (boardId && cardId) {\n\t\t\t\treturn `/index.php/apps/deck/board/${boardId}/card/${cardId}`\n\t\t\t}\n\t\t\treturn this.deckAppUrl\n\t\t},\n\n\t\tcardLabels(card) {\n\t\t\tconst labels = card.labels ?? card.tags ?? []\n\t\t\treturn Array.isArray(labels) ? labels : []\n\t\t},\n\n\t\tchipText(label) {\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label\n\t\t\t}\n\t\t\treturn label.title ?? label.name ?? ''\n\t\t},\n\n\t\tchipStyle(label) {\n\t\t\tif (typeof label === 'object' && label.color) {\n\t\t\t\tconst colour = String(label.color)\n\t\t\t\tconst fill = colour.startsWith('#') ? colour : `#${colour}`\n\t\t\t\treturn { background: fill, color: this.readableTextColour(fill) }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\n\t\t/**\n\t\t * Pick black or white text for a hex background so Deck's coloured\n\t\t * label chips stay legible (per-channel luminance, ITU-R BT.601).\n\t\t *\n\t\t * @param {string} hex background colour, e.g. `#31cc7c`\n\t\t * @return {string} `#000000` or `#ffffff`\n\t\t */\n\t\treadableTextColour(hex) {\n\t\t\tconst clean = hex.replace('#', '')\n\t\t\tconst full = clean.length === 3\n\t\t\t\t? clean.split('').map((c) => c + c).join('')\n\t\t\t\t: clean\n\t\t\tif (full.length < 6) {\n\t\t\t\treturn '#ffffff'\n\t\t\t}\n\t\t\tconst r = parseInt(full.slice(0, 2), 16)\n\t\t\tconst g = parseInt(full.slice(2, 4), 16)\n\t\t\tconst b = parseInt(full.slice(4, 6), 16)\n\t\t\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255\n\t\t\treturn luminance > 0.6 ? '#000000' : '#ffffff'\n\t\t},\n\n\t\tisOverdue(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn date.getTime() < Date.now()\n\t\t},\n\n\t\tdueLabel(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(undefined, { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Status-badge variant for the due-date chip: red when overdue,\n\t\t * neutral otherwise — mirrors Deck's own overdue-date highlight.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} CnStatusBadge variant\n\t\t */\n\t\tdueVariant(card) {\n\t\t\treturn this.isOverdue(card) ? 'error' : 'default'\n\t\t},\n\n\t\t/**\n\t\t * Short board/stack context label rendered as a badge above the\n\t\t * title — the \"where does this card live\" signal a real Deck card\n\t\t * carries via its board colour bar + stack column.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} board (or board · stack) label, or '' when unknown\n\t\t */\n\t\tboardBadge(card) {\n\t\t\tconst board = card.boardTitle ?? card.board?.title ?? ''\n\t\t\tconst stack = card.stackTitle ?? card.stack?.title ?? ''\n\t\t\tif (board && stack) {\n\t\t\t\treturn `${board} · ${stack}`\n\t\t\t}\n\t\t\treturn board || stack || ''\n\t\t},\n\n\t\t/**\n\t\t * Normalised assignee list for a card. Accepts Deck's\n\t\t * `assignedUsers` (objects with a nested `participant`) as well as\n\t\t * a plain `assignees` array of strings/objects.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} assignee entries\n\t\t */\n\t\tcardAssignees(card) {\n\t\t\tconst raw = card.assignedUsers ?? card.assignees ?? card.users ?? []\n\t\t\treturn Array.isArray(raw) ? raw : []\n\t\t},\n\n\t\t/**\n\t\t * The first {@link MAX_VISIBLE_ASSIGNEES} assignees to render as\n\t\t * avatars; the remainder collapse into a +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} visible assignee entries\n\t\t */\n\t\tvisibleAssignees(card) {\n\t\t\treturn this.cardAssignees(card).slice(0, MAX_VISIBLE_ASSIGNEES)\n\t\t},\n\n\t\t/**\n\t\t * Count of assignees hidden behind the +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {number} overflow count (0 when all fit)\n\t\t */\n\t\tassigneeOverflow(card) {\n\t\t\tconst extra = this.cardAssignees(card).length - MAX_VISIBLE_ASSIGNEES\n\t\t\treturn extra > 0 ? extra : 0\n\t\t},\n\n\t\t/**\n\t\t * Tooltip listing the assignees collapsed into the overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} comma-separated overflow assignee names\n\t\t */\n\t\toverflowTitle(card) {\n\t\t\treturn this.cardAssignees(card)\n\t\t\t\t.slice(MAX_VISIBLE_ASSIGNEES)\n\t\t\t\t.map((a) => this.assigneeName(a))\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Unwrap an assignee entry to its participant payload — Deck nests\n\t\t * the user under `participant`; flat shapes pass through.\n\t\t *\n\t\t * @param {object|string} assignee raw assignee entry\n\t\t * @return {object} normalised participant object\n\t\t */\n\t\tassigneeParticipant(assignee) {\n\t\t\tif (typeof assignee === 'string') {\n\t\t\t\treturn { uid: assignee, displayname: assignee }\n\t\t\t}\n\t\t\treturn assignee.participant ?? assignee\n\t\t},\n\n\t\tassigneeName(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? t('nextcloud-vue', 'Unknown')\n\t\t},\n\n\t\tassigneeSeed(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? '')\n\t\t},\n\n\t\tassigneeKey(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? Math.random())\n\t\t},\n\n\t\tasync fetchCards() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.cards = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckTab] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-deck-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-deck-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-deck-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-deck-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-deck-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-tab__kanban {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-deck-tab__column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-tab__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 0 2px;\n}\n\n.cn-deck-tab__column-title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-tab__column-count {\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-dark);\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n}\n\n.cn-deck-tab__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-deck-tab__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n\ttransition: box-shadow 0.1s ease-in-out, border-color 0.1s ease-in-out;\n}\n\n.cn-deck-tab__card:hover {\n\tbox-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n\tborder-color: var(--color-border-dark);\n}\n\n.cn-deck-tab__card--overdue {\n\tborder-color: var(--color-error);\n}\n\n.cn-deck-tab__card-board {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\talign-self: flex-start;\n\tmax-width: 100%;\n\tpadding: 1px 7px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\tfont-weight: 600;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-deck-tab__card-title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-tab__card-labels {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n/* Deck label chips: solid card-label colour with a readable text shade. */\n.cn-deck-tab__chip {\n\tdisplay: inline-block;\n\tmax-width: 100%;\n\tpadding: 1px 8px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tline-height: 1.5;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-footer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-top: 2px;\n}\n\n.cn-deck-tab__due-icon {\n\tdisplay: inline-flex;\n\tmargin-right: 2px;\n\tvertical-align: text-bottom;\n}\n\n.cn-deck-tab__avatars {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-left: auto;\n}\n\n.cn-deck-tab__avatar {\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-deck-tab__avatar:not(:first-child) {\n\tmargin-left: -8px;\n}\n\n.cn-deck-tab__avatar-overflow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tmargin-left: -8px;\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.65em;\n\tfont-weight: 600;\n}\n</style>\n"],"names":["CnDeckCardPicker","CnDeckCardCreate","CnStatusBadge","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnDeckTab.vue2.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckTab.vue"],"sourcesContent":["<!--\n CnDeckTab — bespoke sidebar tab for the `deck` integration.\n\n Replaces the generic CnIntegrationTab for the `deck` leaf: renders a\n kanban-mini view of the cards linked to the parent OR object, grouped\n by stack (\"To Do\" / \"Doing\" / \"Done\" or whatever stacks the cards live\n in). Each card row shows title, optional due date, and optional label\n chips. Clicking a row deep-links to the card in NC Deck.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/deck`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\DeckProvider`\n (which delegates to `OCA\\OpenRegister\\Service\\DeckCardService`).\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open Deck\" CTA when no linked cards.\n - Loading spinner during fetch.\n - 503 \"currently unavailable\" banner when Deck is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link list\n which loses Deck's primary signal — *where the card sits in the\n workflow* (stack name). Grouping by stack reproduces the kanban affordance\n inline so case handlers can see \"card X is in Doing\" at a glance.\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta, ADR-019 (registry mechanism) and ADR-022 (sidebar tab contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-deck-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-deck-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-deck-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing card') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new card') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-deck-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-sidebar-tab__empty cn-deck-tab__empty\">\n\t\t\t<ViewColumnOutline :size=\"32\" class=\"cn-deck-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"deckAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ViewColumnOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openDeckLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<div v-else class=\"cn-deck-tab__kanban\">\n\t\t\t<section\n\t\t\t\tv-for=\"stack in stackColumns\"\n\t\t\t\t:key=\"stack.key\"\n\t\t\t\tclass=\"cn-deck-tab__column\">\n\t\t\t\t<header class=\"cn-deck-tab__column-header\">\n\t\t\t\t\t<span class=\"cn-deck-tab__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t<span class=\"cn-deck-tab__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t</header>\n\t\t\t\t<ul class=\"cn-deck-tab__column-list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\tclass=\"cn-deck-tab__card\"\n\t\t\t\t\t\t:class=\"{ 'cn-deck-tab__card--overdue': isOverdue(card) }\">\n\t\t\t\t\t\t<!-- Colored label strip along the top edge, like a real Deck card. -->\n\t\t\t\t\t\t<div v-if=\"cardLabels(card).length > 0\" class=\"cn-deck-tab__card-labels\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"label in cardLabels(card)\"\n\t\t\t\t\t\t\t\t:key=\"label.id || label.title\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__chip\"\n\t\t\t\t\t\t\t\t:style=\"chipStyle(label)\"\n\t\t\t\t\t\t\t\t:title=\"chipText(label)\">\n\t\t\t\t\t\t\t\t{{ chipText(label) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<!-- Board / stack context badge above the title. -->\n\t\t\t\t\t\t<span v-if=\"boardBadge(card)\" class=\"cn-deck-tab__card-board\">\n\t\t\t\t\t\t\t<ViewColumnOutline :size=\"12\" />\n\t\t\t\t\t\t\t{{ boardBadge(card) }}\n\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-title\">{{ cardTitle(card) }}</a>\n\n\t\t\t\t\t\t<!-- Footer: due-date chip + assignee avatars, like Deck's card footer. -->\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-if=\"dueLabel(card) || cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\tclass=\"cn-deck-tab__card-footer\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:label=\"dueLabel(card)\"\n\t\t\t\t\t\t\t\t:variant=\"dueVariant(card)\"\n\t\t\t\t\t\t\t\tsize=\"small\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<ClockOutline :size=\"12\" class=\"cn-deck-tab__due-icon\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t</CnStatusBadge>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tv-if=\"cardAssignees(card).length > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatars\">\n\t\t\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\t\t\tv-for=\"assignee in visibleAssignees(card)\"\n\t\t\t\t\t\t\t\t\t:key=\"assigneeKey(assignee)\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar\"\n\t\t\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t\t\t:displayName=\"assigneeName(assignee)\"\n\t\t\t\t\t\t\t\t\t:user=\"assigneeSeed(assignee)\"\n\t\t\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\t\t\thideStatus />\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tv-if=\"assigneeOverflow(card) > 0\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-deck-tab__avatar-overflow\"\n\t\t\t\t\t\t\t\t\t:title=\"overflowTitle(card)\">+{{ assigneeOverflow(card) }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</div>\n\n\t\t<CnDeckCardPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnDeckCardCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcAvatar, NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'\nimport CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\n\n/** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */\nconst MAX_VISIBLE_ASSIGNEES = 3\n\n/**\n * CnDeckTab — kanban-mini sidebar tab for the `deck` integration.\n *\n * Groups linked cards by stack so case handlers see workflow position\n * inline; deep-links to NC Deck for editing. See file-level docblock\n * for surface behaviour.\n */\nexport default {\n\tname: 'CnDeckTab',\n\n\tcomponents: {\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tClockOutline,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tViewColumnOutline,\n\t\tCnDeckCardPicker,\n\t\tCnDeckCardCreate,\n\t\tCnStatusBadge,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'deck'`). */\n\t\tintegrationId: { type: String, default: 'deck' },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No cards linked yet') },\n\t\t/** Pre-translated label for the \"Open Deck\" CTA. */\n\t\topenDeckLabel: { type: String, default: () => t('nextcloud-vue', 'Open Deck') },\n\t\t/** Pre-translated banner when Deck is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Deck is currently unavailable.') },\n\t\t/** URL of the NC Deck app entry. */\n\t\tdeckAppUrl: { type: String, default: '/index.php/apps/deck' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tcards: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Cards grouped by stack — preserves stack order via first-seen ordering.\n\t\t *\n\t\t * Each column is `{ key, label, cards: [] }`. The `key` is the\n\t\t * numeric stack id (or 'unstacked' for cards without one); the\n\t\t * `label` is the stack title when the provider returned one,\n\t\t * otherwise a placeholder.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tstackColumns() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const card of this.cards) {\n\t\t\t\tconst stackId = card.stackId ?? card.stack?.id ?? 'unstacked'\n\t\t\t\tconst stackLabel = card.stackTitle\n\t\t\t\t\t?? card.stack?.title\n\t\t\t\t\t?? (stackId === 'unstacked'\n\t\t\t\t\t\t? t('nextcloud-vue', 'No stack')\n\t\t\t\t\t\t: t('nextcloud-vue', 'Stack {n}', { n: stackId }))\n\t\t\t\tconst key = String(stackId)\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label: stackLabel, cards: [] })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).cards.push(card)\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchCards()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchCards()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchCards()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 deck endpoints (link/new). The provider's\n\t\t * generic `/integrations/deck` surface accepts the same POST\n\t\t * shape, but the bespoke Tier-2 routes are nicer for the\n\t\t * picker's `/deck/new` create-flow.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tdeckEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.deckEndpoint()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchCards()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This card is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link card.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.deckEndpoint()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchCards()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create card.')\n\t\t\t}\n\t\t},\n\n\t\tcardKey(card) {\n\t\t\treturn card.cardId ?? card.id ?? ''\n\t\t},\n\n\t\tcardTitle(card) {\n\t\t\treturn card.cardTitle ?? card.title ?? this.cardKey(card)\n\t\t},\n\n\t\tcardUrl(card) {\n\t\t\tif (card.url && safeHref(card.url) !== '#') {\n\t\t\t\treturn card.url\n\t\t\t}\n\t\t\tconst boardId = card.boardId ?? card.board?.id ?? ''\n\t\t\tconst cardId = card.cardId ?? card.id ?? ''\n\t\t\tif (boardId && cardId) {\n\t\t\t\treturn `/index.php/apps/deck/board/${boardId}/card/${cardId}`\n\t\t\t}\n\t\t\treturn this.deckAppUrl\n\t\t},\n\n\t\tcardLabels(card) {\n\t\t\tconst labels = card.labels ?? card.tags ?? []\n\t\t\treturn Array.isArray(labels) ? labels : []\n\t\t},\n\n\t\tchipText(label) {\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label\n\t\t\t}\n\t\t\treturn label.title ?? label.name ?? ''\n\t\t},\n\n\t\tchipStyle(label) {\n\t\t\tif (typeof label === 'object' && label.color) {\n\t\t\t\tconst colour = String(label.color)\n\t\t\t\tconst fill = colour.startsWith('#') ? colour : `#${colour}`\n\t\t\t\treturn { background: fill, color: this.readableTextColour(fill) }\n\t\t\t}\n\t\t\treturn {}\n\t\t},\n\n\t\t/**\n\t\t * Pick black or white text for a hex background so Deck's coloured\n\t\t * label chips stay legible (per-channel luminance, ITU-R BT.601).\n\t\t *\n\t\t * @param {string} hex background colour, e.g. `#31cc7c`\n\t\t * @return {string} `#000000` or `#ffffff`\n\t\t */\n\t\treadableTextColour(hex) {\n\t\t\tconst clean = hex.replace('#', '')\n\t\t\tconst full = clean.length === 3\n\t\t\t\t? clean.split('').map((c) => c + c).join('')\n\t\t\t\t: clean\n\t\t\tif (full.length < 6) {\n\t\t\t\treturn '#ffffff'\n\t\t\t}\n\t\t\tconst r = parseInt(full.slice(0, 2), 16)\n\t\t\tconst g = parseInt(full.slice(2, 4), 16)\n\t\t\tconst b = parseInt(full.slice(4, 6), 16)\n\t\t\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255\n\t\t\treturn luminance > 0.6 ? '#000000' : '#ffffff'\n\t\t},\n\n\t\tisOverdue(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn date.getTime() < Date.now()\n\t\t},\n\n\t\tdueLabel(card) {\n\t\t\tconst due = card.duedate ?? card.dueDate ?? null\n\t\t\tif (!due) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst date = new Date(due)\n\t\t\tif (Number.isNaN(date.getTime())) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn date.toLocaleDateString(userLocale(), { dateStyle: 'medium' })\n\t\t},\n\n\t\t/**\n\t\t * Status-badge variant for the due-date chip: red when overdue,\n\t\t * neutral otherwise — mirrors Deck's own overdue-date highlight.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} CnStatusBadge variant\n\t\t */\n\t\tdueVariant(card) {\n\t\t\treturn this.isOverdue(card) ? 'error' : 'default'\n\t\t},\n\n\t\t/**\n\t\t * Short board/stack context label rendered as a badge above the\n\t\t * title — the \"where does this card live\" signal a real Deck card\n\t\t * carries via its board colour bar + stack column.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} board (or board · stack) label, or '' when unknown\n\t\t */\n\t\tboardBadge(card) {\n\t\t\tconst board = card.boardTitle ?? card.board?.title ?? ''\n\t\t\tconst stack = card.stackTitle ?? card.stack?.title ?? ''\n\t\t\tif (board && stack) {\n\t\t\t\treturn `${board} · ${stack}`\n\t\t\t}\n\t\t\treturn board || stack || ''\n\t\t},\n\n\t\t/**\n\t\t * Normalised assignee list for a card. Accepts Deck's\n\t\t * `assignedUsers` (objects with a nested `participant`) as well as\n\t\t * a plain `assignees` array of strings/objects.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} assignee entries\n\t\t */\n\t\tcardAssignees(card) {\n\t\t\tconst raw = card.assignedUsers ?? card.assignees ?? card.users ?? []\n\t\t\treturn Array.isArray(raw) ? raw : []\n\t\t},\n\n\t\t/**\n\t\t * The first {@link MAX_VISIBLE_ASSIGNEES} assignees to render as\n\t\t * avatars; the remainder collapse into a +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {Array} visible assignee entries\n\t\t */\n\t\tvisibleAssignees(card) {\n\t\t\treturn this.cardAssignees(card).slice(0, MAX_VISIBLE_ASSIGNEES)\n\t\t},\n\n\t\t/**\n\t\t * Count of assignees hidden behind the +N overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {number} overflow count (0 when all fit)\n\t\t */\n\t\tassigneeOverflow(card) {\n\t\t\tconst extra = this.cardAssignees(card).length - MAX_VISIBLE_ASSIGNEES\n\t\t\treturn extra > 0 ? extra : 0\n\t\t},\n\n\t\t/**\n\t\t * Tooltip listing the assignees collapsed into the overflow badge.\n\t\t *\n\t\t * @param {object} card the card row\n\t\t * @return {string} comma-separated overflow assignee names\n\t\t */\n\t\toverflowTitle(card) {\n\t\t\treturn this.cardAssignees(card)\n\t\t\t\t.slice(MAX_VISIBLE_ASSIGNEES)\n\t\t\t\t.map((a) => this.assigneeName(a))\n\t\t\t\t.join(', ')\n\t\t},\n\n\t\t/**\n\t\t * Unwrap an assignee entry to its participant payload — Deck nests\n\t\t * the user under `participant`; flat shapes pass through.\n\t\t *\n\t\t * @param {object|string} assignee raw assignee entry\n\t\t * @return {object} normalised participant object\n\t\t */\n\t\tassigneeParticipant(assignee) {\n\t\t\tif (typeof assignee === 'string') {\n\t\t\t\treturn { uid: assignee, displayname: assignee }\n\t\t\t}\n\t\t\treturn assignee.participant ?? assignee\n\t\t},\n\n\t\tassigneeName(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn p.displayname ?? p.displayName ?? p.uid ?? p.id ?? t('nextcloud-vue', 'Unknown')\n\t\t},\n\n\t\tassigneeSeed(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? '')\n\t\t},\n\n\t\tassigneeKey(assignee) {\n\t\t\tconst p = this.assigneeParticipant(assignee)\n\t\t\treturn String(p.uid ?? p.id ?? p.displayname ?? p.displayName ?? Math.random())\n\t\t},\n\n\t\tasync fetchCards() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.cards = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.cards = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckTab] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load cards.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-deck-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-deck-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-deck-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-deck-tab__empty {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px 8px;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-align: center;\n}\n\n.cn-deck-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-tab__kanban {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-deck-tab__column {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 8px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-tab__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 0 2px;\n}\n\n.cn-deck-tab__column-title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-tab__column-count {\n\tfont-size: 0.75em;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-dark);\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n}\n\n.cn-deck-tab__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-deck-tab__card {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n\ttransition: box-shadow 0.1s ease-in-out, border-color 0.1s ease-in-out;\n}\n\n.cn-deck-tab__card:hover {\n\tbox-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);\n\tborder-color: var(--color-border-dark);\n}\n\n.cn-deck-tab__card--overdue {\n\tborder-color: var(--color-error);\n}\n\n.cn-deck-tab__card-board {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\talign-self: flex-start;\n\tmax-width: 100%;\n\tpadding: 1px 7px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\tfont-weight: 600;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-deck-tab__card-title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-tab__card-labels {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n}\n\n/* Deck label chips: solid card-label colour with a readable text shade. */\n.cn-deck-tab__chip {\n\tdisplay: inline-block;\n\tmax-width: 100%;\n\tpadding: 1px 8px;\n\tfont-size: 0.7em;\n\tfont-weight: 500;\n\tline-height: 1.5;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-tab__card-footer {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tflex-wrap: wrap;\n\tmargin-top: 2px;\n}\n\n.cn-deck-tab__due-icon {\n\tdisplay: inline-flex;\n\tmargin-right: 2px;\n\tvertical-align: text-bottom;\n}\n\n.cn-deck-tab__avatars {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-left: auto;\n}\n\n.cn-deck-tab__avatar {\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n}\n\n.cn-deck-tab__avatar:not(:first-child) {\n\tmargin-left: -8px;\n}\n\n.cn-deck-tab__avatar-overflow {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tmargin-left: -8px;\n\tborder: 2px solid var(--color-main-background);\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.65em;\n\tfont-weight: 600;\n}\n</style>\n"],"names":["CnDeckCardPicker","CnDeckCardCreate","CnStatusBadge","t"],"mappings":"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"}