@conduction/nextcloud-vue 2.56.0-beta.4 → 2.56.0-beta.5

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 (300) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
  7. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
  10. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  11. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  15. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  16. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  17. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  18. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  19. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  20. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  26. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  28. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  32. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  33. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  34. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  35. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  36. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  38. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  40. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  42. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  43. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  45. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  48. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  49. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  54. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  55. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  56. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  57. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  58. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  60. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  62. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  64. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  65. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  66. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  67. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  68. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  69. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  70. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  71. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  73. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  75. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  76. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  78. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  80. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  82. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  84. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  85. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  86. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  87. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  88. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  89. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  90. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  91. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  92. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  93. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  94. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  95. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  96. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  97. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  98. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  99. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  100. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  101. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  102. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  103. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  104. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  105. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  106. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  107. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  108. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  109. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  110. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  111. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  113. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  115. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  117. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  119. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  120. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  121. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  122. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  123. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  124. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  125. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  126. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  127. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  128. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  129. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  130. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  131. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  132. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  133. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  134. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  135. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  136. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  137. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  138. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  139. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  141. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  142. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  143. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  144. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  146. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  147. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  148. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  149. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  150. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  151. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  152. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  153. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  154. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  155. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  156. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  157. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  158. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  159. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  160. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  161. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  162. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  163. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  164. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  165. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  166. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  167. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  168. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  169. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  170. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  171. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  172. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  173. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  174. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  175. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  176. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  177. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  178. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  179. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  180. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  181. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  182. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  183. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  184. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  185. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  186. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  187. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  188. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  189. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  190. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  191. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  192. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  193. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  194. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  195. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  196. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  197. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  198. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  199. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  200. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  201. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  202. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  203. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  204. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  205. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  206. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  207. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  208. package/dist/esm/l10n/en.json.js +1 -0
  209. package/dist/esm/l10n/en.json.js.map +1 -1
  210. package/dist/esm/l10n/nl.json.js +1 -0
  211. package/dist/esm/l10n/nl.json.js.map +1 -1
  212. package/dist/esm/nextcloud-vue.css +71 -7
  213. package/dist/esm/utils/actionLink.js +5 -0
  214. package/dist/esm/utils/actionLink.js.map +1 -1
  215. package/dist/esm/utils/actionsDispatcher.js +16 -1
  216. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  217. package/dist/esm/utils/linkNavigation.js +6 -3
  218. package/dist/esm/utils/linkNavigation.js.map +1 -1
  219. package/dist/esm/utils/rowAuxClick.js +30 -5
  220. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  221. package/dist/esm/utils/safeHref.js +18 -3
  222. package/dist/esm/utils/safeHref.js.map +1 -1
  223. package/dist/nextcloud-vue.cjs.js +809 -383
  224. package/dist/nextcloud-vue.cjs.js.map +2 -2
  225. package/dist/nextcloud-vue.css +71 -7
  226. package/l10n/en.json +1 -0
  227. package/l10n/nl.json +1 -0
  228. package/package.json +1 -1
  229. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  230. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  231. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  232. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  233. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  234. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  235. package/src/components/CnDataTable/CnDataTable.md +1 -1
  236. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  237. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  238. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  239. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  240. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  241. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  242. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  243. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  244. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  245. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  246. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  247. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  248. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  249. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  250. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  251. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  252. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  253. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  254. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  255. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  256. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  257. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  258. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  259. package/src/components/CnTabs/CnTabs.vue +7 -3
  260. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  261. package/src/css/table.css +26 -0
  262. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  263. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  264. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  265. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  266. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  267. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  268. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  269. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  270. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  271. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  272. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  273. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  274. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  275. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  276. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  277. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  278. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  279. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  280. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  281. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  282. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  283. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  284. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  285. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  286. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  287. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  288. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  289. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  290. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  291. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  292. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  293. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  294. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  295. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  296. package/src/utils/actionLink.js +5 -0
  297. package/src/utils/actionsDispatcher.js +15 -0
  298. package/src/utils/linkNavigation.js +6 -3
  299. package/src/utils/rowAuxClick.js +29 -4
  300. package/src/utils/safeHref.js +18 -3
@@ -1 +1 @@
1
- {"version":3,"file":"CnCospendTab.vue3.js","sources":["../../../../../src/integrations/builtin/cospend/CnCospendTab.vue?vue&type=template&id=037c757e&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\trel=\"noopener noreferrer\">\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 } 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) {\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","_createVNode","_createTextVNode","_createBlock","_toDisplayString","_createElementVNode","_normalizeClass"],"mappings":"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"}
1
+ {"version":3,"file":"CnCospendTab.vue3.js","sources":["../../../../../src/integrations/builtin/cospend/CnCospendTab.vue?vue&type=template&id=037c757e&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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"}
@@ -2,6 +2,7 @@ import { translate, translatePlural } from '@nextcloud/l10n';
2
2
  import { NcLoadingIcon } from '@nextcloud/vue';
3
3
  import ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue';
4
4
  import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
5
+ import { safeHref } from '../../../utils/safeHref.js';
5
6
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
6
7
 
7
8
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
@@ -168,7 +169,7 @@ var script = {
168
169
  },
169
170
 
170
171
  cardUrl(card) {
171
- if (card.url) {
172
+ if (card.url && safeHref(card.url) !== '#') {
172
173
  return card.url
173
174
  }
174
175
  const boardId = card.boardId ?? card.board?.id ?? '';
@@ -1 +1 @@
1
- {"version":3,"file":"CnDeckCard.vue2.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckCard.vue"],"sourcesContent":["<!--\n CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n\n Replaces the generic CnIntegrationCard for the `deck` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N cards across M stacks\";\n secondary line names the most recent card.\n - detail-page : mini three-column kanban view of\n the linked card's board with the linked card highlighted in its\n current stack (per design.md AD-2).\n - single-entity : chip with card title + stack name\n (referenceType: 'deck').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnDeckTab; for `single-entity` the optional `value` prop addresses a\n single card by id (matching CnIntegrationCard's fetchSingle contract).\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback),\n AD-2 (mini-kanban on detail-page).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-deck-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"cardUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ cardTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"stackTitle(entity)\" class=\"cn-deck-card__chip-stack\">\n\t\t\t\t\t· {{ stackTitle(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-deck-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__headline\">\n\t\t\t\t<div class=\"cn-deck-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"stackDistribution.length > 0\" class=\"cn-deck-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in stackDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-deck-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-label\">{{ bucket.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-deck-card__headline-recent\">\n\t\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"cardUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ cardTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: mini-kanban -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__kanban\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"stack in miniColumns\"\n\t\t\t\t\t:key=\"stack.key\"\n\t\t\t\t\tclass=\"cn-deck-card__column\">\n\t\t\t\t\t<header class=\"cn-deck-card__column-header\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul class=\"cn-deck-card__column-list\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\t\tclass=\"cn-deck-card__row\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-deck-card__row--highlight': isLinked(card) }\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-card__title\">{{ cardTitle(card) }}</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\n\n/**\n * CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n *\n * Renders Deck-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnDeckCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, ViewColumnOutline },\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/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (card id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Cards') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ViewColumnOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\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 unavailable label. */\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\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\t/**\n\t\t * Cards grouped by stack for both the mini-kanban (detail-page)\n\t\t * and the stack-distribution list (dashboard surfaces).\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tcolumns() {\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\n\t\t/**\n\t\t * Up to three columns for the mini-kanban view on `detail-page`.\n\t\t *\n\t\t * Per AD-2 (design.md): when the linked card has its own stack\n\t\t * we always include that stack; otherwise the first three stacks\n\t\t * by first-seen order are surfaced.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tminiColumns() {\n\t\t\treturn this.columns.slice(0, 3)\n\t\t},\n\n\t\tstackDistribution() {\n\t\t\treturn this.columns.map((col) => ({\n\t\t\t\tkey: col.key,\n\t\t\t\tlabel: col.label,\n\t\t\t\tcount: col.cards.length,\n\t\t\t}))\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.cards.length\n\t\t\tconst stackCount = this.columns.length\n\t\t\tif (stackCount <= 1) {\n\t\t\t\treturn n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\t}\n\t\t\tconst cardFragment = n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\tconst stacksFragment = n('nextcloud-vue', 'across {count} stack', 'across {count} stacks', stackCount, { count: stackCount })\n\t\t\treturn `${cardFragment} ${stacksFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.cards.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.cards].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.linkedAt ?? a.lastActivity ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.linkedAt ?? b.lastActivity ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\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\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) {\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\tstackTitle(card) {\n\t\t\treturn card.stackTitle ?? card.stack?.title ?? ''\n\t\t},\n\n\t\tisLinked(card) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.cardKey(card)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(card) {\n\t\t\treturn this.stackTitle(card) || this.cardTitle(card)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\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.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\tthis.cards = data.results || data.items || (Array.isArray(data) ? data : []) || []\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}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckCard] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\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('[CnDeckCard] failed to fetch single card', err)\n\t\t\t\tthis.entity = null\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-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-deck-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-deck-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-deck-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-deck-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip-stack {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n\n.cn-deck-card__kanban {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: flex-start;\n}\n\n.cn-deck-card__column {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 6px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-card__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 4px;\n}\n\n.cn-deck-card__column-title {\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__column-count {\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-deck-card__row {\n\tpadding: 4px 6px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.8em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-deck-card__title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-deck-card__title:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}
1
+ {"version":3,"file":"CnDeckCard.vue2.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckCard.vue"],"sourcesContent":["<!--\n CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n\n Replaces the generic CnIntegrationCard for the `deck` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N cards across M stacks\";\n secondary line names the most recent card.\n - detail-page : mini three-column kanban view of\n the linked card's board with the linked card highlighted in its\n current stack (per design.md AD-2).\n - single-entity : chip with card title + stack name\n (referenceType: 'deck').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnDeckTab; for `single-entity` the optional `value` prop addresses a\n single card by id (matching CnIntegrationCard's fetchSingle contract).\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback),\n AD-2 (mini-kanban on detail-page).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-deck-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"cardUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ cardTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"stackTitle(entity)\" class=\"cn-deck-card__chip-stack\">\n\t\t\t\t\t· {{ stackTitle(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-deck-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__headline\">\n\t\t\t\t<div class=\"cn-deck-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"stackDistribution.length > 0\" class=\"cn-deck-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in stackDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-deck-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-label\">{{ bucket.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-deck-card__headline-recent\">\n\t\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"cardUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ cardTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: mini-kanban -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__kanban\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"stack in miniColumns\"\n\t\t\t\t\t:key=\"stack.key\"\n\t\t\t\t\tclass=\"cn-deck-card__column\">\n\t\t\t\t\t<header class=\"cn-deck-card__column-header\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul class=\"cn-deck-card__column-list\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\t\tclass=\"cn-deck-card__row\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-deck-card__row--highlight': isLinked(card) }\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-card__title\">{{ cardTitle(card) }}</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\n\n/**\n * CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n *\n * Renders Deck-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnDeckCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, ViewColumnOutline },\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/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (card id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Cards') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ViewColumnOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\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 unavailable label. */\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\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\t/**\n\t\t * Cards grouped by stack for both the mini-kanban (detail-page)\n\t\t * and the stack-distribution list (dashboard surfaces).\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tcolumns() {\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\n\t\t/**\n\t\t * Up to three columns for the mini-kanban view on `detail-page`.\n\t\t *\n\t\t * Per AD-2 (design.md): when the linked card has its own stack\n\t\t * we always include that stack; otherwise the first three stacks\n\t\t * by first-seen order are surfaced.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tminiColumns() {\n\t\t\treturn this.columns.slice(0, 3)\n\t\t},\n\n\t\tstackDistribution() {\n\t\t\treturn this.columns.map((col) => ({\n\t\t\t\tkey: col.key,\n\t\t\t\tlabel: col.label,\n\t\t\t\tcount: col.cards.length,\n\t\t\t}))\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.cards.length\n\t\t\tconst stackCount = this.columns.length\n\t\t\tif (stackCount <= 1) {\n\t\t\t\treturn n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\t}\n\t\t\tconst cardFragment = n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\tconst stacksFragment = n('nextcloud-vue', 'across {count} stack', 'across {count} stacks', stackCount, { count: stackCount })\n\t\t\treturn `${cardFragment} ${stacksFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.cards.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.cards].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.linkedAt ?? a.lastActivity ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.linkedAt ?? b.lastActivity ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\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\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\tstackTitle(card) {\n\t\t\treturn card.stackTitle ?? card.stack?.title ?? ''\n\t\t},\n\n\t\tisLinked(card) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.cardKey(card)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(card) {\n\t\t\treturn this.stackTitle(card) || this.cardTitle(card)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\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.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\tthis.cards = data.results || data.items || (Array.isArray(data) ? data : []) || []\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}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckCard] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\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('[CnDeckCard] failed to fetch single card', err)\n\t\t\t\tthis.entity = null\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-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-deck-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-deck-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-deck-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-deck-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip-stack {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n\n.cn-deck-card__kanban {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: flex-start;\n}\n\n.cn-deck-card__column {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 6px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-card__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 4px;\n}\n\n.cn-deck-card__column-title {\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__column-count {\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-deck-card__row {\n\tpadding: 4px 6px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.8em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-deck-card__title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-deck-card__title:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":";;;;;;;AAkHA,MAAM,cAAe,GAAE,CAAC,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAA;;AAEzF;;AAEC;;;;AAID,aAAe;AACd,CAAA,IAAI,EAAE,YAAY;;CAElB,UAAU,EAAE,gBAAEA,UAAY,EAAE,aAAa,EAAE,iBAAgB,EAAG;;AAE9D,CAAA,KAAK,EAAE;AACN;AACA,EAAA,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ;AAChD;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AAC1C;AACA,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AACxC;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;AAE1C,EAAA,OAAO,EAAE;GACR,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,aAAa;AACtB,GAAA,SAAS,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;GAC5C;;AAED;AACA,EAAA,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI;;EAE9C,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,OAAO,CAAA,EAAG;AACnE;EACA,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAAgB,EAAG;AACxD;EACA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAuB,EAAG;AAC5D;AACA,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;AAC7C;AACA,EAAA,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,qBAAqB,GAAG;AACtF;AACA,EAAA,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,mCAAmC,GAAG;;EAE1G,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAqB,EAAG;EAC7D;;AAED,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;AACN,GAAA,KAAK,EAAE,EAAE;GACT,MAAM,EAAE,IAAI;GACZ,OAAO,EAAE,KAAK;GACd,QAAQ,EAAE,EAAE;AACb,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;AACT,EAAA,eAAe,GAAG;AACjB,GAAA,OAAO,IAAI,CAAC,KAAM,IAAG,IAAI,CAAC,aAAY;GACtC;;EAED,QAAQ,GAAG;AACV,GAAA,OAAO,IAAI,CAAC,IAAG;GACf;;AAED;;;AAGC;;;AAGD,EAAA,OAAO,GAAG;AACT,GAAA,MAAM,MAAK,GAAI,IAAI,GAAG,GAAC;GACvB,KAAK,MAAM,IAAG,IAAK,IAAI,CAAC,KAAK,EAAE;IAC9B,MAAM,OAAM,GAAI,IAAI,CAAC,OAAQ,IAAG,IAAI,CAAC,KAAK,EAAE,MAAM,YAAU;AAC5D,IAAA,MAAM,UAAS,GAAI,IAAI,CAAC,UAAS;AAChC,QAAG,IAAI,CAAC,KAAK,EAAE,KAAI;SACf,OAAQ,KAAI,WAAU;QACvBA,SAAC,CAAC,eAAe,EAAE,UAAU,CAAA;AAC/B,QAAEA,SAAC,CAAC,eAAe,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,OAAM,EAAG,CAAC,EAAA;IACnD,MAAM,GAAE,GAAI,MAAM,CAAC,OAAO,EAAA;AAC1B,IAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;KACrB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAA;AACtD,KAAA;AACA,IAAA,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAA;AAChC,IAAA;AACA,GAAA,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA;GACjC;;AAED;;AAEC;;;;AAIA;;;EAGD,WAAW,GAAG;GACb,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAA;GAC9B;;AAED,EAAA,iBAAiB,GAAG;AACnB,GAAA,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM;IACjC,GAAG,EAAE,GAAG,CAAC,GAAG;AACZ,IAAA,KAAK,EAAE,GAAG,CAAC,KAAK;IAChB,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM;AACxB,IAAC,CAAC,CAAA;GACF;;AAED,EAAA,aAAa,GAAG;GACf,MAAM,KAAM,GAAE,IAAI,CAAC,KAAK,CAAC,OAAK;AAC9B,GAAA,MAAM,aAAa,IAAI,CAAC,OAAO,CAAC,OAAK;AACrC,GAAA,IAAI,UAAS,IAAK,CAAC,EAAE;AACpB,IAAA,OAAOC,eAAC,CAAC,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,CAAA;AACnF,IAAA;AACA,GAAA,MAAM,YAAW,GAAIA,eAAC,CAAC,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAA;GAChG,MAAM,cAAe,GAAEA,eAAC,CAAC,eAAe,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,UAAS,EAAG,EAAA;AAC5H,GAAA,OAAO,CAAC,EAAE,YAAY,EAAE,EAAE,cAAc,CAAC,CAAA;GACzC;;EAED,UAAU,GAAG;GACZ,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE;AAC5B,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,MAAK,GAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK;IAC7C,MAAM,EAAG,GAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAO,IAAK,CAAC,CAAC,YAAa,IAAG,EAAE,CAAE,IAAG,EAAA;IAC7D,MAAM,EAAG,GAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAO,IAAK,CAAC,CAAC,YAAa,IAAG,EAAE,CAAE,IAAG,EAAA;IAC7D,OAAO,EAAC,GAAI,EAAC;IACb,EAAA;AACD,GAAA,OAAO,MAAM,CAAC,CAAC,CAAA;GACf;EACD;;AAED,CAAA,KAAK,EAAE;AACN,EAAA,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG;GACtC,IAAI,CAAC,KAAK,GAAC;AACZ,GAAA,EAAG;;AAEH,EAAA,OAAO,GAAG;GACT,IAAI,CAAC,KAAK,GAAC;GACX;;AAED,EAAA,KAAK,GAAG;AACP,GAAA,IAAI,IAAI,CAAC,YAAY,eAAe,EAAE;AACrC,IAAA,IAAI,CAAC,WAAW,GAAC;AAClB,IAAA;GACA;EACD;;AAED,CAAA,OAAO,EAAE;AACR,EAAA,OAAO,GAAG;GACT,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;GACnH;;EAED,OAAO,CAAC,IAAI,EAAE;GACb,OAAO,IAAI,CAAC,MAAO,IAAG,IAAI,CAAC,EAAC,IAAK,EAAC;GAClC;;AAED,EAAA,SAAS,CAAC,IAAI,EAAE;GACf,OAAO,IAAI,CAAC,SAAU,IAAG,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;GACxD;;EAED,OAAO,CAAC,IAAI,EAAE;AACb,GAAA,IAAI,IAAI,CAAC,GAAE,IAAK,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAA,KAAM,GAAG,EAAE;IAC3C,OAAO,IAAI,CAAC,GAAE;AACf,IAAA;AACA,GAAA,MAAM,OAAQ,GAAE,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,EAAE,EAAG,IAAG,GAAC;AACnD,GAAA,MAAM,MAAO,GAAE,IAAI,CAAC,MAAK,IAAK,IAAI,CAAC,EAAG,IAAG,GAAC;AAC1C,GAAA,IAAI,OAAQ,IAAG,MAAM,EAAE;IACtB,OAAO,CAAC,2BAA2B,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAC7D,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,UAAS;GACrB;;AAED,EAAA,UAAU,CAAC,IAAI,EAAE;AAChB,GAAA,OAAO,IAAI,CAAC,UAAS,IAAK,IAAI,CAAC,KAAK,EAAE,KAAM,IAAG,EAAC;GAChD;;AAED,EAAA,QAAQ,CAAC,IAAI,EAAE;AACd,GAAA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;IAChB,OAAO,KAAI;AACZ,IAAA;GACA,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA,KAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAA;GACvD;;AAED,EAAA,YAAY,CAAC,IAAI,EAAE;GAClB,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAA,IAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAA;GACnD;;AAED,EAAA,KAAK,GAAG;AACP,GAAA,IAAI,IAAI,CAAC,YAAY,eAAe,EAAE;AACrC,IAAA,IAAI,CAAC,WAAW,GAAC;IACjB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,SAAS,GAAC;GACf;;AAED,EAAA,MAAM,SAAS,GAAG;GACjB,IAAI,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE;IACrD,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;AAClB,GAAA,IAAI,CAAC,QAAO,GAAI,GAAC;AACjB,GAAA,IAAI;AACH,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,IAAI,EAAA;AACnF,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,KAAA,IAAI,CAAC,KAAM,GAAE,IAAI,CAAC,WAAW,IAAI,CAAC,KAAM,KAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAE,GAAE,IAAK,GAAE,EAAE,CAAE,IAAG,GAAC;AAClF,KAAA,MAAO,IAAI,QAAQ,CAAC,MAAK,KAAM,GAAG,EAAE;KACnC,IAAI,CAAC,KAAM,GAAE,GAAC;AACd,KAAA,IAAI,CAAC,QAAS,GAAE,IAAI,CAAC,iBAAe;KACnC,MAAK;KACN,IAAI,CAAC,KAAM,GAAE,GAAC;AACf,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,GAAG,EAAA;IACvD,IAAI,CAAC,KAAM,GAAE,GAAC;AACf,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;;AAED,EAAA,MAAM,WAAW,GAAG;GACnB,IAAI,CAAC,IAAI,CAAC,KAAI,IAAK,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,MAAK,IAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;AACpE,IAAA,IAAI,CAAC,MAAK,GAAI,KAAG;IACjB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;AAClB,GAAA,IAAI,CAAC,QAAO,GAAI,GAAC;AACjB,GAAA,IAAI;IACH,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,EAAC,EAAG,EAAA;AAC1H,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;AAChB,KAAA,IAAI,CAAC,MAAK,GAAI,MAAM,QAAQ,CAAC,IAAI,GAAC;AACnC,KAAA,MAAO,IAAI,QAAQ,CAAC,MAAK,KAAM,GAAG,EAAE;AACnC,KAAA,IAAI,CAAC,MAAK,GAAI,KAAG;AACjB,KAAA,IAAI,CAAC,QAAS,GAAE,IAAI,CAAC,iBAAe;KACnC,MAAK;AACN,KAAA,IAAI,CAAC,MAAK,GAAI,KAAG;AAClB,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,GAAG,EAAA;AAC7D,IAAA,IAAI,CAAC,MAAK,GAAI,KAAG;AAClB,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;EACD;AACF;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnDeckCard.vue3.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckCard.vue?vue&type=template&id=02981da7&lang.js"],"sourcesContent":["<!--\n CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n\n Replaces the generic CnIntegrationCard for the `deck` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N cards across M stacks\";\n secondary line names the most recent card.\n - detail-page : mini three-column kanban view of\n the linked card's board with the linked card highlighted in its\n current stack (per design.md AD-2).\n - single-entity : chip with card title + stack name\n (referenceType: 'deck').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnDeckTab; for `single-entity` the optional `value` prop addresses a\n single card by id (matching CnIntegrationCard's fetchSingle contract).\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback),\n AD-2 (mini-kanban on detail-page).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-deck-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"cardUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ cardTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"stackTitle(entity)\" class=\"cn-deck-card__chip-stack\">\n\t\t\t\t\t· {{ stackTitle(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-deck-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__headline\">\n\t\t\t\t<div class=\"cn-deck-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"stackDistribution.length > 0\" class=\"cn-deck-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in stackDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-deck-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-label\">{{ bucket.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-deck-card__headline-recent\">\n\t\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"cardUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ cardTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: mini-kanban -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__kanban\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"stack in miniColumns\"\n\t\t\t\t\t:key=\"stack.key\"\n\t\t\t\t\tclass=\"cn-deck-card__column\">\n\t\t\t\t\t<header class=\"cn-deck-card__column-header\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul class=\"cn-deck-card__column-list\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\t\tclass=\"cn-deck-card__row\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-deck-card__row--highlight': isLinked(card) }\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-card__title\">{{ cardTitle(card) }}</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\n\n/**\n * CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n *\n * Renders Deck-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnDeckCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, ViewColumnOutline },\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/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (card id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Cards') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ViewColumnOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\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 unavailable label. */\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\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\t/**\n\t\t * Cards grouped by stack for both the mini-kanban (detail-page)\n\t\t * and the stack-distribution list (dashboard surfaces).\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tcolumns() {\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\n\t\t/**\n\t\t * Up to three columns for the mini-kanban view on `detail-page`.\n\t\t *\n\t\t * Per AD-2 (design.md): when the linked card has its own stack\n\t\t * we always include that stack; otherwise the first three stacks\n\t\t * by first-seen order are surfaced.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tminiColumns() {\n\t\t\treturn this.columns.slice(0, 3)\n\t\t},\n\n\t\tstackDistribution() {\n\t\t\treturn this.columns.map((col) => ({\n\t\t\t\tkey: col.key,\n\t\t\t\tlabel: col.label,\n\t\t\t\tcount: col.cards.length,\n\t\t\t}))\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.cards.length\n\t\t\tconst stackCount = this.columns.length\n\t\t\tif (stackCount <= 1) {\n\t\t\t\treturn n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\t}\n\t\t\tconst cardFragment = n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\tconst stacksFragment = n('nextcloud-vue', 'across {count} stack', 'across {count} stacks', stackCount, { count: stackCount })\n\t\t\treturn `${cardFragment} ${stacksFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.cards.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.cards].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.linkedAt ?? a.lastActivity ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.linkedAt ?? b.lastActivity ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\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\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) {\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\tstackTitle(card) {\n\t\t\treturn card.stackTitle ?? card.stack?.title ?? ''\n\t\t},\n\n\t\tisLinked(card) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.cardKey(card)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(card) {\n\t\t\treturn this.stackTitle(card) || this.cardTitle(card)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\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.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\tthis.cards = data.results || data.items || (Array.isArray(data) ? data : []) || []\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}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckCard] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\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('[CnDeckCard] failed to fetch single card', err)\n\t\t\t\tthis.entity = null\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-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-deck-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-deck-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-deck-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-deck-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip-stack {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n\n.cn-deck-card__kanban {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: flex-start;\n}\n\n.cn-deck-card__column {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 6px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-card__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 4px;\n}\n\n.cn-deck-card__column-title {\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__column-count {\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-deck-card__row {\n\tpadding: 4px 6px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.8em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-deck-card__title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-deck-card__title:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_Fragment","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_openBlock","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnDeckCard.vue3.js","sources":["../../../../../src/integrations/builtin/deck/CnDeckCard.vue?vue&type=template&id=02981da7&lang.js"],"sourcesContent":["<!--\n CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n\n Replaces the generic CnIntegrationCard for the `deck` leaf. Branches on\n `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N cards across M stacks\";\n secondary line names the most recent card.\n - detail-page : mini three-column kanban view of\n the linked card's board with the linked card highlighted in its\n current stack (per design.md AD-2).\n - single-entity : chip with card title + stack name\n (referenceType: 'deck').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnDeckTab; for `single-entity` the optional `value` prop addresses a\n single card by id (matching CnIntegrationCard's fetchSingle contract).\n\n See `openregister/openspec/changes/integration-deck/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback),\n AD-2 (mini-kanban on detail-page).\n-->\n<template>\n\t<CnDetailCard :title=\"cardHeaderTitle\" :icon=\"cardIcon\" :collapsible=\"collapsible\">\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- single-entity surface: chip -->\n\t\t<template v-else-if=\"surface === 'single-entity'\">\n\t\t\t<span v-if=\"entity\" class=\"cn-deck-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"cardUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ cardTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"stackTitle(entity)\" class=\"cn-deck-card__chip-stack\">\n\t\t\t\t\t· {{ stackTitle(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-deck-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__headline\">\n\t\t\t\t<div class=\"cn-deck-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"stackDistribution.length > 0\" class=\"cn-deck-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in stackDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-deck-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-label\">{{ bucket.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__distribution-count\">{{ bucket.count }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-deck-card__headline-recent\">\n\t\t\t\t\t<ViewColumnOutline :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"cardUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ cardTitle(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: mini-kanban -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"cards.length === 0\" class=\"cn-deck-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-deck-card__kanban\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"stack in miniColumns\"\n\t\t\t\t\t:key=\"stack.key\"\n\t\t\t\t\tclass=\"cn-deck-card__column\">\n\t\t\t\t\t<header class=\"cn-deck-card__column-header\">\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-title\">{{ stack.label }}</span>\n\t\t\t\t\t\t<span class=\"cn-deck-card__column-count\">{{ stack.cards.length }}</span>\n\t\t\t\t\t</header>\n\t\t\t\t\t<ul class=\"cn-deck-card__column-list\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"card in stack.cards\"\n\t\t\t\t\t\t\t:key=\"cardKey(card)\"\n\t\t\t\t\t\t\tclass=\"cn-deck-card__row\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-deck-card__row--highlight': isLinked(card) }\">\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"cardUrl(card)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-deck-card__title\">{{ cardTitle(card) }}</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\n\n/**\n * CnDeckCard — bespoke surface-aware widget for the `deck` integration.\n *\n * Renders Deck-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnDeckCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, ViewColumnOutline },\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/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, required: true },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, required: true },\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** Rendering surface (AD-19). */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (s) => VALID_SURFACES.includes(s),\n\t\t},\n\n\t\t/** Optional single-entity reference (card id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Cards') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => ViewColumnOutline },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\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 unavailable label. */\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\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardHeaderTitle() {\n\t\t\treturn this.title || this.integrationId\n\t\t},\n\n\t\tcardIcon() {\n\t\t\treturn this.icon\n\t\t},\n\n\t\t/**\n\t\t * Cards grouped by stack for both the mini-kanban (detail-page)\n\t\t * and the stack-distribution list (dashboard surfaces).\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tcolumns() {\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\n\t\t/**\n\t\t * Up to three columns for the mini-kanban view on `detail-page`.\n\t\t *\n\t\t * Per AD-2 (design.md): when the linked card has its own stack\n\t\t * we always include that stack; otherwise the first three stacks\n\t\t * by first-seen order are surfaced.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, cards: Array}>}\n\t\t */\n\t\tminiColumns() {\n\t\t\treturn this.columns.slice(0, 3)\n\t\t},\n\n\t\tstackDistribution() {\n\t\t\treturn this.columns.map((col) => ({\n\t\t\t\tkey: col.key,\n\t\t\t\tlabel: col.label,\n\t\t\t\tcount: col.cards.length,\n\t\t\t}))\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.cards.length\n\t\t\tconst stackCount = this.columns.length\n\t\t\tif (stackCount <= 1) {\n\t\t\t\treturn n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\t}\n\t\t\tconst cardFragment = n('nextcloud-vue', '{count} card', '{count} cards', total, { count: total })\n\t\t\tconst stacksFragment = n('nextcloud-vue', 'across {count} stack', 'across {count} stacks', stackCount, { count: stackCount })\n\t\t\treturn `${cardFragment} ${stacksFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.cards.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.cards].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.linkedAt ?? a.lastActivity ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.linkedAt ?? b.lastActivity ?? '') || 0\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\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\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\tstackTitle(card) {\n\t\t\treturn card.stackTitle ?? card.stack?.title ?? ''\n\t\t},\n\n\t\tisLinked(card) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.cardKey(card)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(card) {\n\t\t\treturn this.stackTitle(card) || this.cardTitle(card)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\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.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\tthis.cards = data.results || data.items || (Array.isArray(data) ? data : []) || []\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}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnDeckCard] failed to fetch cards', err)\n\t\t\t\tthis.cards = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\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('[CnDeckCard] failed to fetch single card', err)\n\t\t\t\tthis.entity = null\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-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-deck-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-deck-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__distribution {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-deck-card__distribution-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-deck-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-hover);\n\tfont-size: 0.9em;\n\tmax-width: 100%;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n/* stylelint-disable-next-line no-descending-specificity */\n.cn-deck-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-deck-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-deck-card__chip-stack {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n\n.cn-deck-card__kanban {\n\tdisplay: flex;\n\tgap: 8px;\n\talign-items: flex-start;\n}\n\n.cn-deck-card__column {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 6px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-deck-card__column-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 4px;\n}\n\n.cn-deck-card__column-title {\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__column-count {\n\tfont-size: 0.7em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-deck-card__column-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-deck-card__row {\n\tpadding: 4px 6px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.8em;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-deck-card__row--highlight {\n\tborder-color: var(--color-primary-element, #21468B);\n\tbackground: var(--color-primary-element-light, var(--color-background-darker));\n\tfont-weight: 600;\n}\n\n.cn-deck-card__title {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-deck-card__title:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_Fragment","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_openBlock","_renderList"],"mappings":"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"}
@@ -9,6 +9,7 @@ import __sfc_main$1 from '../../../components/CnDeckCardCreate/CnDeckCardCreate.
9
9
  import __sfc_main from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue.js';
10
10
  import __sfc_main$2 from '../../../components/CnStatusBadge/CnStatusBadge.vue.js';
11
11
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
12
+ import { safeHref } from '../../../utils/safeHref.js';
12
13
 
13
14
  /** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */
14
15
  const MAX_VISIBLE_ASSIGNEES = 3;
@@ -193,7 +194,7 @@ var script = {
193
194
  },
194
195
 
195
196
  cardUrl(card) {
196
- if (card.url) {
197
+ if (card.url && safeHref(card.url) !== '#') {
197
198
  return card.url
198
199
  }
199
200
  const boardId = card.boardId ?? card.board?.id ?? '';
@@ -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\trel=\"noopener noreferrer\">\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 } 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) {\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'\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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"}
@@ -104,8 +104,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
104
104
  createVNode(_component_NcButton, {
105
105
  variant: "primary",
106
106
  href: $props.deckAppUrl,
107
- target: "_blank",
108
- rel: "noopener noreferrer"
107
+ target: "_blank"
109
108
  }, {
110
109
  icon: withCtx(() => [
111
110
  createVNode(_component_ViewColumnOutline, { size: 20 })