@conduction/nextcloud-vue 2.56.0-beta.3 → 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 (314) 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/CnAppRoot/CnAppRoot.vue2.js +21 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
  14. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  15. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  21. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  22. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  23. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  24. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  30. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  32. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  34. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  35. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  36. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  38. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  42. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  44. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  47. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  49. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  52. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  58. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  59. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  60. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  61. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  62. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  64. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  66. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  67. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  68. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  69. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  70. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  71. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  73. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  75. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  77. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  80. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  82. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  84. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  85. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  86. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  87. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  88. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  89. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  90. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  91. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  92. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  93. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  94. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  95. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  96. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
  97. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  98. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
  99. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  100. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  101. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  102. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  103. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  104. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  105. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  106. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  108. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  109. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  110. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  111. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  113. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  115. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  117. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  119. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  120. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  121. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  122. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  123. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  124. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  125. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  126. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  127. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  128. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  129. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  130. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  131. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  132. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  133. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  134. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  135. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  136. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  137. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  138. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  139. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  141. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  142. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  143. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  144. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  146. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  147. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  148. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  149. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  150. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  151. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  152. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  153. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  154. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  155. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  156. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  157. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  158. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  159. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  160. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  161. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  162. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  163. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  164. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  165. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  166. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  167. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  168. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  169. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  170. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  171. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  172. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  173. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  174. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  175. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  176. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  177. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  178. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  179. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  180. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  181. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  182. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  183. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  184. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  185. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  186. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  187. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  188. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  189. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  190. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  191. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  192. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  193. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  194. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  195. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  196. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  197. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  198. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  199. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  200. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  201. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  202. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  203. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  204. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  205. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  206. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  207. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  208. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  209. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  210. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  211. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  212. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  213. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  214. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  215. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  216. package/dist/esm/l10n/en.json.js +2 -0
  217. package/dist/esm/l10n/en.json.js.map +1 -1
  218. package/dist/esm/l10n/nl.json.js +2 -0
  219. package/dist/esm/l10n/nl.json.js.map +1 -1
  220. package/dist/esm/nextcloud-vue.css +78 -7
  221. package/dist/esm/utils/actionLink.js +5 -0
  222. package/dist/esm/utils/actionLink.js.map +1 -1
  223. package/dist/esm/utils/actionsDispatcher.js +16 -1
  224. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  225. package/dist/esm/utils/linkNavigation.js +6 -3
  226. package/dist/esm/utils/linkNavigation.js.map +1 -1
  227. package/dist/esm/utils/rowAuxClick.js +30 -5
  228. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  229. package/dist/esm/utils/safeHref.js +18 -3
  230. package/dist/esm/utils/safeHref.js.map +1 -1
  231. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  232. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  233. package/dist/nextcloud-vue.cjs.js +885 -393
  234. package/dist/nextcloud-vue.cjs.js.map +2 -2
  235. package/dist/nextcloud-vue.css +78 -7
  236. package/l10n/en.json +2 -0
  237. package/l10n/nl.json +2 -0
  238. package/package.json +1 -1
  239. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  240. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  241. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  242. package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
  243. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  244. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  245. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  246. package/src/components/CnDataTable/CnDataTable.md +1 -1
  247. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  248. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  249. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  250. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  251. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  252. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  253. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  254. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  255. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  256. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  257. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  258. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  259. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  260. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  261. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  262. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  263. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  264. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  265. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  266. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  267. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  268. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  269. package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
  270. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  271. package/src/components/CnTabs/CnTabs.vue +7 -3
  272. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  273. package/src/css/table.css +26 -0
  274. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  275. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  276. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  277. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  278. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  279. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  280. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  281. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  282. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  283. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  284. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  285. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  286. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  287. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  288. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  289. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  290. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  291. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  292. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  293. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  294. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  295. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  296. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  297. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  298. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  299. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  300. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  301. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  302. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  303. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  304. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  305. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  306. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  307. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  308. package/src/schemas/app-manifest-v2.schema.json +2 -1
  309. package/src/utils/actionLink.js +5 -0
  310. package/src/utils/actionsDispatcher.js +15 -0
  311. package/src/utils/linkNavigation.js +6 -3
  312. package/src/utils/rowAuxClick.js +29 -4
  313. package/src/utils/safeHref.js +18 -3
  314. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CnOpenprojectCard.vue3.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectCard.vue?vue&type=template&id=70e3c271&lang.js"],"sourcesContent":["<!--\n CnOpenprojectCard — bespoke surface-aware widget for the\n `openproject` integration.\n\n Replaces the generic CnIntegrationCard for the `openproject` leaf.\n Branches on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N open work packages\",\n status distribution (New / In progress / Closed / …), the most\n recent linked WP, plus the auth-status badge.\n - detail-page : full row list with status pill,\n type badge and assignee — same shape as the tab but read-only\n and bounded.\n - single-entity : chip with subject + status pill\n (referenceType: 'openproject').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnOpenprojectTab; for `single-entity` the optional `value` prop\n addresses a single work package by id (matching CnIntegrationCard's\n fetchSingle contract).\n\n As an external integration the widget also surfaces the\n Integriq auth status alongside the data (configured / missing /\n expired); ADR-019 mandates the auth state never silently degrades to\n an empty list.\n\n See `openregister/openspec/changes/integration-openproject/` for the\n spec delta, ADR-019 (registry mechanism), AD-19 (surface fallback).\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-openproject-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Briefcase :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"wpUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ wpSubject(entity) }}</a>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"wpStatus(entity)\"\n\t\t\t\t\tclass=\"cn-openproject-card__chip-pill\"\n\t\t\t\t\t:class=\"statusPillClass(entity)\">\n\t\t\t\t\t{{ wpStatus(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-openproject-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + status distribution + auth badge -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div\n\t\t\t\tv-if=\"authBanner\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--error\">\n\t\t\t\t<LockOutline :size=\"14\" />\n\t\t\t\t<span>{{ authBanner }}</span>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tv-else-if=\"unconfigured\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--warn\">\n\t\t\t\t<AlertCircleOutline :size=\"14\" />\n\t\t\t\t<span>{{ unconfiguredLabel }}</span>\n\t\t\t</div>\n\t\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-openproject-card__headline\">\n\t\t\t\t<div class=\"cn-openproject-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t\t<span class=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--ok\">\n\t\t\t\t\t\t<CheckCircleOutline :size=\"13\" />\n\t\t\t\t\t\t{{ authConfiguredLabel }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"statusDistribution.length > 0\" class=\"cn-openproject-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in statusDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-openproject-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-openproject-card__distribution-label\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"cn-openproject-card__distribution-dot\"\n\t\t\t\t\t\t\t\t:class=\"bucket.pillClass\" />\n\t\t\t\t\t\t\t{{ bucket.label }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-openproject-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-openproject-card__headline-recent\">\n\t\t\t\t\t<Briefcase :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"wpUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ wpSubject(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: bounded row list -->\n\t\t<template v-else>\n\t\t\t<div\n\t\t\t\tv-if=\"authBanner\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--error\">\n\t\t\t\t<LockOutline :size=\"14\" />\n\t\t\t\t<span>{{ authBanner }}</span>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tv-else-if=\"unconfigured\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--warn\">\n\t\t\t\t<AlertCircleOutline :size=\"14\" />\n\t\t\t\t<span>{{ unconfiguredLabel }}</span>\n\t\t\t</div>\n\t\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-openproject-card__rows\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"wp in workPackages\"\n\t\t\t\t\t:key=\"wpKey(wp)\"\n\t\t\t\t\tclass=\"cn-openproject-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-openproject-card__row--highlight': isLinked(wp) }\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"wpUrl(wp)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-openproject-card__title\">{{ wpSubject(wp) }}</a>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"wpStatus(wp)\"\n\t\t\t\t\t\tclass=\"cn-openproject-card__chip-pill\"\n\t\t\t\t\t\t:class=\"statusPillClass(wp)\">\n\t\t\t\t\t\t{{ wpStatus(wp) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"wpAssignee(wp)\" class=\"cn-openproject-card__assignee\">\n\t\t\t\t\t\t<AccountCircleOutline :size=\"12\" />\n\t\t\t\t\t\t{{ wpAssignee(wp) }}\n\t\t\t\t\t</span>\n\t\t\t\t</li>\n\t\t\t</ul>\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 AccountCircleOutline from 'vue-material-design-icons/AccountCircleOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport Briefcase from 'vue-material-design-icons/Briefcase.vue'\nimport CheckCircleOutline from 'vue-material-design-icons/CheckCircleOutline.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.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 * CnOpenprojectCard — bespoke surface-aware widget for the\n * `openproject` integration.\n *\n * Renders OpenProject-aware metadata across all four surfaces, with\n * special handling for the Integriq auth-status states (ADR-019\n * external storage). See the file-level docblock for surface-by-surface\n * behaviour.\n */\nexport default {\n\tname: 'CnOpenprojectCard',\n\n\tcomponents: {\n\t\tCnDetailCard,\n\t\tNcLoadingIcon,\n\t\tAccountCircleOutline,\n\t\tAlertCircleOutline,\n\t\tBriefcase,\n\t\tCheckCircleOutline,\n\t\tLockOutline,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'openproject'`). */\n\t\tintegrationId: { type: String, default: 'openproject' },\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 (work-package id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Projects') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Briefcase },\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: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No work packages linked yet'),\n\t\t},\n\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is currently unavailable.'),\n\t\t},\n\n\t\t/** Pre-translated auth-expired label. */\n\t\tauthExpiredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Authorisation for OpenProject expired.'),\n\t\t},\n\n\t\t/** Pre-translated unconfigured label. */\n\t\tunconfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject not configured in Integriq.'),\n\t\t},\n\n\t\t/** Pre-translated \"configured\" badge label. */\n\t\tauthConfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Connected'),\n\t\t},\n\n\t\t/** Fallback URL of the OpenProject app entry. */\n\t\topenprojectAppUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: 'https://www.openproject.org',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tworkPackages: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t\tauthBanner: '',\n\t\t\tunconfigured: false,\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 * Work packages bucketed by canonical status pill class so the\n\t\t * dashboard surface's distribution list and the count headline\n\t\t * stay in sync.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, pillClass: string, count: number}>}\n\t\t */\n\t\tstatusBuckets() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const wp of this.workPackages) {\n\t\t\t\tconst label = this.wpStatus(wp) || t('nextcloud-vue', 'Unknown')\n\t\t\t\tconst pillClass = this.statusPillClass(wp)\n\t\t\t\tconst key = `${pillClass}:${label}`\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label, pillClass, count: 0 })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).count += 1\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\n\t\tstatusDistribution() {\n\t\t\treturn this.statusBuckets\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.workPackages.length\n\t\t\treturn n('nextcloud-vue', '{count} work package', '{count} work packages', total, { count: total })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.workPackages.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.workPackages].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.linkedAt ?? a.updatedAt ?? a.updated_at ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.linkedAt ?? b.updatedAt ?? b.updated_at ?? '') || 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\twpKey(wp) {\n\t\t\treturn wp.id ?? wp.reference ?? ''\n\t\t},\n\n\t\twpSubject(wp) {\n\t\t\treturn wp.title ?? wp.subject ?? wp.name ?? this.wpKey(wp)\n\t\t},\n\n\t\twpUrl(wp) {\n\t\t\treturn wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl\n\t\t},\n\n\t\twpStatus(wp) {\n\t\t\treturn wp.status ?? wp._links?.status?.title ?? ''\n\t\t},\n\n\t\twpAssignee(wp) {\n\t\t\treturn wp.assignee\n\t\t\t\t?? wp._links?.assignee?.title\n\t\t\t\t?? wp._embedded?.assignee?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\tstatusPillClass(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'cn-openproject-card__chip-pill--done'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'cn-openproject-card__chip-pill--progress'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'cn-openproject-card__chip-pill--blocked'\n\t\t\t}\n\t\t\treturn 'cn-openproject-card__chip-pill--new'\n\t\t},\n\n\t\tisLinked(wp) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.wpKey(wp)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(wp) {\n\t\t\treturn this.wpStatus(wp) || this.wpSubject(wp)\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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.workPackages = data.results\n\t\t\t\t\t\t|| data.items\n\t\t\t\t\t\t|| data._embedded?.elements\n\t\t\t\t\t\t|| (Array.isArray(data) ? data : [])\n\t\t\t\t\t\t|| []\n\t\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\n\t\t\t\t} else if (response.status === 412 || response.status === 404) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.workPackages = []\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('[CnOpenprojectCard] failed to fetch work packages', err)\n\t\t\t\tthis.workPackages = []\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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 === 401 || response.status === 403) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\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('[CnOpenprojectCard] failed to fetch single work package', 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-openproject-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-openproject-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-openproject-card__headline-line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-openproject-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-openproject-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-openproject-card__distribution-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-openproject-card__distribution-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--new {\n\tbackground: var(--color-background-darker, var(--color-background-dark));\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--progress {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--done {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--blocked {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-openproject-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-openproject-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-openproject-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-openproject-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-openproject-card__auth-badge {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tfont-size: 0.75em;\n\tfont-weight: 500;\n}\n\n.cn-openproject-card__auth-badge--ok {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__auth-badge--warn {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__auth-badge--error {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-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-openproject-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-openproject-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-openproject-card__chip-pill {\n\tdisplay: inline-block;\n\tpadding: 1px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\tfont-size: 0.78em;\n\tfont-weight: 500;\n}\n\n.cn-openproject-card__chip-pill--new {\n\tbackground: var(--color-background-darker, var(--color-background-dark));\n}\n\n.cn-openproject-card__chip-pill--progress {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__chip-pill--done {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__chip-pill--blocked {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-openproject-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.85em;\n}\n\n.cn-openproject-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-openproject-card__title {\n\tflex: 1;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-openproject-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-openproject-card__assignee {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_normalizeClass","_openBlock","_createTextVNode","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnOpenprojectCard.vue3.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectCard.vue?vue&type=template&id=70e3c271&lang.js"],"sourcesContent":["<!--\n CnOpenprojectCard — bespoke surface-aware widget for the\n `openproject` integration.\n\n Replaces the generic CnIntegrationCard for the `openproject` leaf.\n Branches on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N open work packages\",\n status distribution (New / In progress / Closed / …), the most\n recent linked WP, plus the auth-status badge.\n - detail-page : full row list with status pill,\n type badge and assignee — same shape as the tab but read-only\n and bounded.\n - single-entity : chip with subject + status pill\n (referenceType: 'openproject').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnOpenprojectTab; for `single-entity` the optional `value` prop\n addresses a single work package by id (matching CnIntegrationCard's\n fetchSingle contract).\n\n As an external integration the widget also surfaces the\n Integriq auth status alongside the data (configured / missing /\n expired); ADR-019 mandates the auth state never silently degrades to\n an empty list.\n\n See `openregister/openspec/changes/integration-openproject/` for the\n spec delta, ADR-019 (registry mechanism), AD-19 (surface fallback).\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-openproject-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Briefcase :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"wpUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ wpSubject(entity) }}</a>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"wpStatus(entity)\"\n\t\t\t\t\tclass=\"cn-openproject-card__chip-pill\"\n\t\t\t\t\t:class=\"statusPillClass(entity)\">\n\t\t\t\t\t{{ wpStatus(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-openproject-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + status distribution + auth badge -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div\n\t\t\t\tv-if=\"authBanner\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--error\">\n\t\t\t\t<LockOutline :size=\"14\" />\n\t\t\t\t<span>{{ authBanner }}</span>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tv-else-if=\"unconfigured\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--warn\">\n\t\t\t\t<AlertCircleOutline :size=\"14\" />\n\t\t\t\t<span>{{ unconfiguredLabel }}</span>\n\t\t\t</div>\n\t\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-openproject-card__headline\">\n\t\t\t\t<div class=\"cn-openproject-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t\t<span class=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--ok\">\n\t\t\t\t\t\t<CheckCircleOutline :size=\"13\" />\n\t\t\t\t\t\t{{ authConfiguredLabel }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<ul v-if=\"statusDistribution.length > 0\" class=\"cn-openproject-card__distribution\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"bucket in statusDistribution\"\n\t\t\t\t\t\t:key=\"bucket.key\"\n\t\t\t\t\t\tclass=\"cn-openproject-card__distribution-row\">\n\t\t\t\t\t\t<span class=\"cn-openproject-card__distribution-label\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"cn-openproject-card__distribution-dot\"\n\t\t\t\t\t\t\t\t:class=\"bucket.pillClass\" />\n\t\t\t\t\t\t\t{{ bucket.label }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-openproject-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-openproject-card__headline-recent\">\n\t\t\t\t\t<Briefcase :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"wpUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ wpSubject(mostRecent) }}</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: bounded row list -->\n\t\t<template v-else>\n\t\t\t<div\n\t\t\t\tv-if=\"authBanner\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--error\">\n\t\t\t\t<LockOutline :size=\"14\" />\n\t\t\t\t<span>{{ authBanner }}</span>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tv-else-if=\"unconfigured\"\n\t\t\t\tclass=\"cn-openproject-card__auth-badge cn-openproject-card__auth-badge--warn\">\n\t\t\t\t<AlertCircleOutline :size=\"14\" />\n\t\t\t\t<span>{{ unconfiguredLabel }}</span>\n\t\t\t</div>\n\t\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-openproject-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-openproject-card__rows\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"wp in workPackages\"\n\t\t\t\t\t:key=\"wpKey(wp)\"\n\t\t\t\t\tclass=\"cn-openproject-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-openproject-card__row--highlight': isLinked(wp) }\">\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"wpUrl(wp)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-openproject-card__title\">{{ wpSubject(wp) }}</a>\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-if=\"wpStatus(wp)\"\n\t\t\t\t\t\tclass=\"cn-openproject-card__chip-pill\"\n\t\t\t\t\t\t:class=\"statusPillClass(wp)\">\n\t\t\t\t\t\t{{ wpStatus(wp) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"wpAssignee(wp)\" class=\"cn-openproject-card__assignee\">\n\t\t\t\t\t\t<AccountCircleOutline :size=\"12\" />\n\t\t\t\t\t\t{{ wpAssignee(wp) }}\n\t\t\t\t\t</span>\n\t\t\t\t</li>\n\t\t\t</ul>\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 AccountCircleOutline from 'vue-material-design-icons/AccountCircleOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport Briefcase from 'vue-material-design-icons/Briefcase.vue'\nimport CheckCircleOutline from 'vue-material-design-icons/CheckCircleOutline.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.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 * CnOpenprojectCard — bespoke surface-aware widget for the\n * `openproject` integration.\n *\n * Renders OpenProject-aware metadata across all four surfaces, with\n * special handling for the Integriq auth-status states (ADR-019\n * external storage). See the file-level docblock for surface-by-surface\n * behaviour.\n */\nexport default {\n\tname: 'CnOpenprojectCard',\n\n\tcomponents: {\n\t\tCnDetailCard,\n\t\tNcLoadingIcon,\n\t\tAccountCircleOutline,\n\t\tAlertCircleOutline,\n\t\tBriefcase,\n\t\tCheckCircleOutline,\n\t\tLockOutline,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'openproject'`). */\n\t\tintegrationId: { type: String, default: 'openproject' },\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 (work-package id). */\n\t\tvalue: { type: [String, Number], default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Projects') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Briefcase },\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: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No work packages linked yet'),\n\t\t},\n\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is currently unavailable.'),\n\t\t},\n\n\t\t/** Pre-translated auth-expired label. */\n\t\tauthExpiredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Authorisation for OpenProject expired.'),\n\t\t},\n\n\t\t/** Pre-translated unconfigured label. */\n\t\tunconfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject not configured in Integriq.'),\n\t\t},\n\n\t\t/** Pre-translated \"configured\" badge label. */\n\t\tauthConfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Connected'),\n\t\t},\n\n\t\t/** Fallback URL of the OpenProject app entry. */\n\t\topenprojectAppUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: 'https://www.openproject.org',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tworkPackages: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t\tauthBanner: '',\n\t\t\tunconfigured: false,\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 * Work packages bucketed by canonical status pill class so the\n\t\t * dashboard surface's distribution list and the count headline\n\t\t * stay in sync.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, pillClass: string, count: number}>}\n\t\t */\n\t\tstatusBuckets() {\n\t\t\tconst groups = new Map()\n\t\t\tfor (const wp of this.workPackages) {\n\t\t\t\tconst label = this.wpStatus(wp) || t('nextcloud-vue', 'Unknown')\n\t\t\t\tconst pillClass = this.statusPillClass(wp)\n\t\t\t\tconst key = `${pillClass}:${label}`\n\t\t\t\tif (!groups.has(key)) {\n\t\t\t\t\tgroups.set(key, { key, label, pillClass, count: 0 })\n\t\t\t\t}\n\t\t\t\tgroups.get(key).count += 1\n\t\t\t}\n\t\t\treturn Array.from(groups.values())\n\t\t},\n\n\t\tstatusDistribution() {\n\t\t\treturn this.statusBuckets\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.workPackages.length\n\t\t\treturn n('nextcloud-vue', '{count} work package', '{count} work packages', total, { count: total })\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.workPackages.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.workPackages].sort((a, b) => {\n\t\t\t\tconst ta = Date.parse(a.linkedAt ?? a.updatedAt ?? a.updated_at ?? '') || 0\n\t\t\t\tconst tb = Date.parse(b.linkedAt ?? b.updatedAt ?? b.updated_at ?? '') || 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\twpKey(wp) {\n\t\t\treturn wp.id ?? wp.reference ?? ''\n\t\t},\n\n\t\twpSubject(wp) {\n\t\t\treturn wp.title ?? wp.subject ?? wp.name ?? this.wpKey(wp)\n\t\t},\n\n\t\twpUrl(wp) {\n\t\t\tconst url = wp.url ?? wp._links?.self?.href\n\t\t\treturn url && safeHref(url) !== '#' ? url : this.openprojectAppUrl\n\t\t},\n\n\t\twpStatus(wp) {\n\t\t\treturn wp.status ?? wp._links?.status?.title ?? ''\n\t\t},\n\n\t\twpAssignee(wp) {\n\t\t\treturn wp.assignee\n\t\t\t\t?? wp._links?.assignee?.title\n\t\t\t\t?? wp._embedded?.assignee?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\tstatusPillClass(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'cn-openproject-card__chip-pill--done'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'cn-openproject-card__chip-pill--progress'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'cn-openproject-card__chip-pill--blocked'\n\t\t\t}\n\t\t\treturn 'cn-openproject-card__chip-pill--new'\n\t\t},\n\n\t\tisLinked(wp) {\n\t\t\tif (!this.value) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn String(this.wpKey(wp)) === String(this.value)\n\t\t},\n\n\t\tchipSubtitle(wp) {\n\t\t\treturn this.wpStatus(wp) || this.wpSubject(wp)\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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.workPackages = data.results\n\t\t\t\t\t\t|| data.items\n\t\t\t\t\t\t|| data._embedded?.elements\n\t\t\t\t\t\t|| (Array.isArray(data) ? data : [])\n\t\t\t\t\t\t|| []\n\t\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\n\t\t\t\t} else if (response.status === 412 || response.status === 404) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.workPackages = []\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('[CnOpenprojectCard] failed to fetch work packages', err)\n\t\t\t\tthis.workPackages = []\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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 === 401 || response.status === 403) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\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('[CnOpenprojectCard] failed to fetch single work package', 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-openproject-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-openproject-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-openproject-card__headline-line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-openproject-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-openproject-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-openproject-card__distribution-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-openproject-card__distribution-dot {\n\twidth: 8px;\n\theight: 8px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--new {\n\tbackground: var(--color-background-darker, var(--color-background-dark));\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--progress {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--done {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-openproject-card__distribution-dot.cn-openproject-card__chip-pill--blocked {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-openproject-card__distribution-count {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-openproject-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-openproject-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-openproject-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-openproject-card__auth-badge {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tfont-size: 0.75em;\n\tfont-weight: 500;\n}\n\n.cn-openproject-card__auth-badge--ok {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__auth-badge--warn {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__auth-badge--error {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-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-openproject-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-openproject-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-openproject-card__chip-pill {\n\tdisplay: inline-block;\n\tpadding: 1px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\tfont-size: 0.78em;\n\tfont-weight: 500;\n}\n\n.cn-openproject-card__chip-pill--new {\n\tbackground: var(--color-background-darker, var(--color-background-dark));\n}\n\n.cn-openproject-card__chip-pill--progress {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__chip-pill--done {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__chip-pill--blocked {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-openproject-card__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-openproject-card__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tfont-size: 0.85em;\n}\n\n.cn-openproject-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-openproject-card__title {\n\tflex: 1;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\na.cn-openproject-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-openproject-card__assignee {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n</style>\n"],"names":["_createBlock","_createElementBlock","_Fragment","_createVNode","_createElementVNode","_toDisplayString","_normalizeClass","_openBlock","_createTextVNode","_renderList"],"mappings":"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"}
@@ -18,6 +18,7 @@ import __sfc_main$2 from '../../../components/CnOpenProjectCreate/CnOpenProjectC
18
18
  import __sfc_main$1 from '../../../components/CnOpenProjectPicker/CnOpenProjectPicker.vue.js';
19
19
  import __sfc_main from '../../../components/CnStatusBadge/CnStatusBadge.vue.js';
20
20
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
21
+ import { safeHref } from '../../../utils/safeHref.js';
21
22
 
22
23
  /**
23
24
  * CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.
@@ -333,7 +334,8 @@ var script = {
333
334
  },
334
335
 
335
336
  wpUrl(wp) {
336
- return wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl
337
+ const url = wp.url ?? wp._links?.self?.href;
338
+ return url && safeHref(url) !== '#' ? url : this.openprojectAppUrl
337
339
  },
338
340
 
339
341
  wpStatus(wp) {
@@ -1 +1 @@
1
- {"version":3,"file":"CnOpenprojectTab.vue2.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectTab.vue"],"sourcesContent":["<!--\n CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n\n Replaces the generic CnIntegrationTab for the `openproject` leaf:\n renders the work packages linked to the parent OR object as a flat\n list, each row showing subject, type badge (Task / Bug / Feature /\n …), status pill (New / In progress / Closed / …), priority indicator,\n assignee avatar, and project name. Clicking a row deep-links to the\n work package in OpenProject.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/openproject`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\OpenProjectProvider`\n (which delegates through `ExternalIntegrationRouter` to the\n Integriq source named `openproject`).\n\n Surface behaviour (per ADR-017 graceful degradation + ADR-019 external\n storage policy):\n - Unconfigured: 412 / `health=unavailable` from the provider →\n \"Configure OpenProject connection\" CTA pointing at Integriq.\n - Authorisation expired: 401 with `authStatus: 'expired'` →\n reconnect-in-Integriq banner.\n - Other 4xx/5xx: surface the upstream message (not a silent empty).\n - Loading: spinner.\n - Empty: \"No work packages linked yet\" + \"Open OpenProject\" CTA.\n - Degraded (503): \"OpenProject is currently unavailable\" banner.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses OpenProject's primary signals — *type of work*,\n *current status* and *who's responsible*. Surfacing them inline\n reproduces enough of the OpenProject row affordance for case handlers\n to triage at a glance without context-switching to OpenProject itself.\n\n As the first external-storage integration this tab also pioneers the\n auth-status surfacing the umbrella's `IntegrationHealth` contract\n exposes — silently failing on 401 is a debugging black hole.\n\n See `openregister/openspec/changes/integration-openproject/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-openproject-tab\">\n\t\t<div v-if=\"authBanner\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--auth\" role=\"alert\">\n\t\t\t<LockOutline :size=\"18\" />\n\t\t\t<span>{{ authBanner }}</span>\n\t\t\t<a\n\t\t\t\tv-if=\"openconnectorUrl\"\n\t\t\t\t:href=\"openconnectorUrl\"\n\t\t\t\tclass=\"cn-openproject-tab__banner-cta\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">{{ openconnectorLabel }}</a>\n\t\t</div>\n\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--warn\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<!-- Link / Create actions — hidden while unconfigured (the empty\n\t\t state below carries the Configure CTA instead) and while an\n\t\t auth banner is showing. -->\n\t\t<div v-if=\"!unconfigured && !authBanner\" class=\"cn-openproject-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 work package') }}\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 work package') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Unconfigured: provider reports the Integriq `openproject` source is absent. -->\n\t\t<div\n\t\t\tv-else-if=\"unconfigured\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ unconfiguredLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"openconnectorUrl\"\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<CogOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ configureLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-else-if=\"error\" class=\"cn-openproject-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:href=\"openprojectAppUrl\"\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<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openOpenprojectLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else class=\"cn-openproject-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"wp in workPackages\"\n\t\t\t\t:key=\"wpKey(wp)\"\n\t\t\t\tclass=\"cn-openproject-tab__row\"\n\t\t\t\t:class=\"rowClass(wp)\"\n\t\t\t\t:name=\"wpSubject(wp)\"\n\t\t\t\t:bold=\"true\"\n\t\t\t\t:href=\"wpUrl(wp)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<!-- Type indicator: a coloured square chip echoing OpenProject's\n\t\t\t\t type colour-coding (Task / Bug / Feature / Milestone). -->\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-openproject-tab__type-icon\"\n\t\t\t\t\t\t:class=\"typeBadgeClass(wp)\"\n\t\t\t\t\t\t:title=\"typeTitle(wp)\">\n\t\t\t\t\t\t<RhombusOutline v-if=\"isMilestoneType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<Bug v-else-if=\"isBugType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<StarFourPointsOutline v-else-if=\"isFeatureType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<CheckboxMarkedOutline v-else :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Sub-line: #id reference + status chip, the OpenProject row meta. -->\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-openproject-tab__subline\">\n\t\t\t\t\t\t<span v-if=\"wpReference(wp)\" class=\"cn-openproject-tab__ref\">{{ wpReference(wp) }}</span>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"wpStatus(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__status-pill\"\n\t\t\t\t\t\t\t:class=\"statusPillClass(wp)\"\n\t\t\t\t\t\t\t:label=\"wpStatus(wp)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(wp)\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"wpProject(wp)\" class=\"cn-openproject-tab__project\">\n\t\t\t\t\t\t\t<Briefcase :size=\"12\" />\n\t\t\t\t\t\t\t{{ wpProject(wp) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Trailing column: type badge label so the type stays legible. -->\n\t\t\t\t<template v-if=\"wpType(wp)\" #details>\n\t\t\t\t\t<span class=\"cn-openproject-tab__type-label\">{{ wpType(wp) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Right-edge indicators: priority marker + assignee avatar. -->\n\t\t\t\t<template #indicator>\n\t\t\t\t\t<span class=\"cn-openproject-tab__indicators\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"wpPriority(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__priority\"\n\t\t\t\t\t\t\t:class=\"priorityClass(wp)\"\n\t\t\t\t\t\t\t:title=\"priorityTitle(wp)\">\n\t\t\t\t\t\t\t<AlertCircleOutline v-if=\"isHighPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronDoubleUp v-else-if=\"isMediumPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronUp v-else :size=\"14\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\tv-if=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__assignee\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t:displayName=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\t:user=\"assigneeSeed(wp)\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\thideStatus\n\t\t\t\t\t\t\t:title=\"assigneeTitle(wp)\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tclass=\"cn-openproject-tab__unlink\"\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t@click=\"unlinkWorkPackage(wp)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink work package') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnOpenProjectPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnOpenProjectCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\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, NcAvatar, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport Briefcase from 'vue-material-design-icons/Briefcase.vue'\nimport Bug from 'vue-material-design-icons/Bug.vue'\nimport CheckboxMarkedOutline from 'vue-material-design-icons/CheckboxMarkedOutline.vue'\nimport ChevronDoubleUp from 'vue-material-design-icons/ChevronDoubleUp.vue'\nimport ChevronUp from 'vue-material-design-icons/ChevronUp.vue'\nimport CogOutline from 'vue-material-design-icons/CogOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RhombusOutline from 'vue-material-design-icons/RhombusOutline.vue'\nimport StarFourPointsOutline from 'vue-material-design-icons/StarFourPointsOutline.vue'\nimport CnOpenProjectCreate from '../../../components/CnOpenProjectCreate/CnOpenProjectCreate.vue'\nimport CnOpenProjectPicker from '../../../components/CnOpenProjectPicker/CnOpenProjectPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n *\n * Renders linked work packages with type / status / priority /\n * assignee, plus auth-status surfacing for Integriq configured /\n * missing / expired states. See the file-level docblock for the full\n * surface contract.\n */\nexport default {\n\tname: 'CnOpenprojectTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tBriefcase,\n\t\tBug,\n\t\tCheckboxMarkedOutline,\n\t\tChevronDoubleUp,\n\t\tChevronUp,\n\t\tCogOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tLockOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tRhombusOutline,\n\t\tStarFourPointsOutline,\n\t\tCnStatusBadge,\n\t\tCnOpenProjectPicker,\n\t\tCnOpenProjectCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'openproject'`). */\n\t\tintegrationId: { type: String, default: 'openproject' },\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: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No work packages linked yet'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Open OpenProject\" CTA. */\n\t\topenOpenprojectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open OpenProject'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Configure OpenProject connection\" CTA. */\n\t\tconfigureLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Configure OpenProject connection'),\n\t\t},\n\n\t\t/** Pre-translated label shown when the integration is unconfigured. */\n\t\tunconfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is not configured yet. Add an `openproject` source in Integriq to start linking work packages.'),\n\t\t},\n\n\t\t/** Pre-translated banner when OpenProject is unavailable. */\n\t\tunavailableLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is currently unavailable.'),\n\t\t},\n\n\t\t/** Pre-translated banner when the OAuth/API token has expired. */\n\t\tauthExpiredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Authorisation for OpenProject expired. Reconnect the source in Integriq to restore access.'),\n\t\t},\n\n\t\t/** Pre-translated link label to the Integriq admin. */\n\t\topenconnectorLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open Integriq'),\n\t\t},\n\n\t\t/** URL of the Integriq source admin page (for the `openproject` source). */\n\t\topenconnectorUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '/index.php/apps/openconnector/sources/openproject',\n\t\t},\n\n\t\t/** Fallback URL of the OpenProject app entry. */\n\t\topenprojectAppUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: 'https://www.openproject.org',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tworkPackages: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tauthBanner: '',\n\t\t\tunconfigured: false,\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\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.fetchWorkPackages()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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 openproject endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\topenProjectEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/openproject`)\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.openProjectEndpoint(), {\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.fetchWorkPackages()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This work package is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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('[CnOpenprojectTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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.openProjectEndpoint()}/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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\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('[CnOpenprojectTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkWorkPackage(wp) {\n\t\t\tconst id = this.wpKey(wp)\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.openProjectEndpoint()}/${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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\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('[CnOpenprojectTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\n\t\t\t}\n\t\t},\n\n\t\twpKey(wp) {\n\t\t\treturn wp.id ?? wp.reference ?? ''\n\t\t},\n\n\t\t/**\n\t\t * The OpenProject `#id` reference shown on the row sub-line.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A `#123` reference, or '' when no id is known.\n\t\t */\n\t\twpReference(wp) {\n\t\t\tconst id = wp.id ?? wp.reference ?? ''\n\t\t\tif (id === '' || id === null || id === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst str = String(id)\n\t\t\treturn str.charAt(0) === '#' ? str : '#' + str\n\t\t},\n\n\t\t/**\n\t\t * Stable seed for NcAvatar's deterministic colour + initials.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A non-empty seed.\n\t\t */\n\t\tassigneeSeed(wp) {\n\t\t\treturn String(this.wpAssignee(wp) || this.wpKey(wp) || 'op')\n\t\t},\n\n\t\t/**\n\t\t * Row modifier classes (high-priority emphasis on the left edge).\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\trowClass(wp) {\n\t\t\treturn { 'cn-openproject-tab__row--high-priority': this.isHighPriority(wp) }\n\t\t},\n\n\t\ttypeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Type: {type}', { type: this.wpType(wp) || t('nextcloud-vue', 'Task') })\n\t\t},\n\n\t\tisBugType(wp) {\n\t\t\treturn String(this.wpType(wp)).toLowerCase().includes('bug')\n\t\t},\n\n\t\tisMilestoneType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('milestone') || type.includes('phase')\n\t\t},\n\n\t\tisFeatureType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('feature') || type.includes('user story') || type.includes('epic')\n\t\t},\n\n\t\t/**\n\t\t * CnStatusBadge variant matching the OpenProject status family.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A CnStatusBadge variant.\n\t\t */\n\t\tstatusVariant(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'success'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'warning'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn 'info'\n\t\t},\n\n\t\t/**\n\t\t * Priority-marker emphasis class.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\tpriorityClass(wp) {\n\t\t\treturn {\n\t\t\t\t'cn-openproject-tab__priority--high': this.isHighPriority(wp),\n\t\t\t\t'cn-openproject-tab__priority--medium': this.isMediumPriority(wp),\n\t\t\t}\n\t\t},\n\n\t\twpSubject(wp) {\n\t\t\treturn wp.title ?? wp.subject ?? wp.name ?? this.wpKey(wp)\n\t\t},\n\n\t\twpUrl(wp) {\n\t\t\treturn wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl\n\t\t},\n\n\t\twpStatus(wp) {\n\t\t\treturn wp.status ?? wp._links?.status?.title ?? ''\n\t\t},\n\n\t\twpType(wp) {\n\t\t\treturn wp.type ?? wp._links?.type?.title ?? wp._embedded?.type?.name ?? ''\n\t\t},\n\n\t\twpPriority(wp) {\n\t\t\treturn wp.priority ?? wp._links?.priority?.title ?? wp._embedded?.priority?.name ?? ''\n\t\t},\n\n\t\twpAssignee(wp) {\n\t\t\treturn wp.assignee\n\t\t\t\t?? wp._links?.assignee?.title\n\t\t\t\t?? wp._embedded?.assignee?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\twpProject(wp) {\n\t\t\treturn wp.project\n\t\t\t\t?? wp._links?.project?.title\n\t\t\t\t?? wp._embedded?.project?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\tassigneeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Assigned to {name}', { name: this.wpAssignee(wp) })\n\t\t},\n\n\t\tpriorityTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Priority: {priority}', { priority: this.wpPriority(wp) })\n\t\t},\n\n\t\tisHighPriority(wp) {\n\t\t\tconst priority = String(this.wpPriority(wp)).toLowerCase()\n\t\t\treturn priority === 'high' || priority === 'immediate' || priority === 'urgent'\n\t\t},\n\n\t\tisMediumPriority(wp) {\n\t\t\treturn String(this.wpPriority(wp)).toLowerCase() === 'normal'\n\t\t},\n\n\t\tstatusPillClass(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--done'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--progress'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--blocked'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__status-pill--new'\n\t\t},\n\n\t\ttypeBadgeClass(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\tif (type.includes('bug')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--bug'\n\t\t\t}\n\t\t\tif (type.includes('feature') || type.includes('user story') || type.includes('epic')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--feature'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__type-badge--task'\n\t\t},\n\n\t\tasync fetchWorkPackages() {\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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\n\t\t\t\t\t\t|| data.items\n\t\t\t\t\t\t|| data._embedded?.elements\n\t\t\t\t\t\t|| (Array.isArray(data) ? data : [])\n\t\t\t\t\t\t|| []\n\t\t\t\t\tthis.workPackages = rows\n\t\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\n\t\t\t\t} else if (response.status === 412) {\n\t\t\t\t\t// Provider returned \"preconditions failed\" — convention for \"unconfigured\".\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 404) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.error = await this.extractError(response)\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('[CnOpenprojectTab] failed to fetch work packages', err)\n\t\t\t\tthis.workPackages = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load work packages.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync extractError(response) {\n\t\t\ttry {\n\t\t\t\tconst body = await response.json()\n\t\t\t\tif (body && typeof body.message === 'string' && body.message !== '') {\n\t\t\t\t\treturn body.message\n\t\t\t\t}\n\t\t\t\tif (body && typeof body.error === 'string' && body.error !== '') {\n\t\t\t\t\treturn body.error\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through.\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Could not load work packages.')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-openproject-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n}\n\n.cn-openproject-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-openproject-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\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-openproject-tab__banner--warn {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__banner--auth {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-openproject-tab__banner-cta {\n\tmargin-left: auto;\n\tcolor: var(--color-main-background);\n\ttext-decoration: underline;\n}\n\n.cn-openproject-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-openproject-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-openproject-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* High-priority work packages get a coloured left rail so case handlers\n spot them while scanning the list. */\n.cn-openproject-tab__row--high-priority {\n\tborder-left: 3px solid var(--color-error);\n}\n\n/* Type indicator: a rounded square chip echoing OpenProject's\n colour-coded work-package types. */\n.cn-openproject-tab__type-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 30px;\n\theight: 30px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element, #1A67A3);\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--bug {\n\tbackground: var(--color-error);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--feature {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--task {\n\tbackground: #1A67A3;\n}\n\n.cn-openproject-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* OpenProject-style `#id` reference, monospaced for a ticket feel. */\n.cn-openproject-tab__ref {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__project {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n}\n\n.cn-openproject-tab__type-label {\n\tfont-size: 0.78em;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-openproject-tab__indicators {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-openproject-tab__priority {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__priority--high {\n\tcolor: var(--color-error);\n}\n\n.cn-openproject-tab__priority--medium {\n\tcolor: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__assignee {\n\tflex-shrink: 0;\n}\n\n/* CnStatusBadge keeps its own colours; the legacy status-pill modifier\n hooks are retained so existing tests and consumers keep matching. */\n.cn-openproject-tab__status-pill {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["CnStatusBadge","CnOpenProjectPicker","CnOpenProjectCreate","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnOpenprojectTab.vue2.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectTab.vue"],"sourcesContent":["<!--\n CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n\n Replaces the generic CnIntegrationTab for the `openproject` leaf:\n renders the work packages linked to the parent OR object as a flat\n list, each row showing subject, type badge (Task / Bug / Feature /\n …), status pill (New / In progress / Closed / …), priority indicator,\n assignee avatar, and project name. Clicking a row deep-links to the\n work package in OpenProject.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/openproject`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\OpenProjectProvider`\n (which delegates through `ExternalIntegrationRouter` to the\n Integriq source named `openproject`).\n\n Surface behaviour (per ADR-017 graceful degradation + ADR-019 external\n storage policy):\n - Unconfigured: 412 / `health=unavailable` from the provider →\n \"Configure OpenProject connection\" CTA pointing at Integriq.\n - Authorisation expired: 401 with `authStatus: 'expired'` →\n reconnect-in-Integriq banner.\n - Other 4xx/5xx: surface the upstream message (not a silent empty).\n - Loading: spinner.\n - Empty: \"No work packages linked yet\" + \"Open OpenProject\" CTA.\n - Degraded (503): \"OpenProject is currently unavailable\" banner.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses OpenProject's primary signals — *type of work*,\n *current status* and *who's responsible*. Surfacing them inline\n reproduces enough of the OpenProject row affordance for case handlers\n to triage at a glance without context-switching to OpenProject itself.\n\n As the first external-storage integration this tab also pioneers the\n auth-status surfacing the umbrella's `IntegrationHealth` contract\n exposes — silently failing on 401 is a debugging black hole.\n\n See `openregister/openspec/changes/integration-openproject/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-openproject-tab\">\n\t\t<div v-if=\"authBanner\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--auth\" role=\"alert\">\n\t\t\t<LockOutline :size=\"18\" />\n\t\t\t<span>{{ authBanner }}</span>\n\t\t\t<a\n\t\t\t\tv-if=\"openconnectorUrl\"\n\t\t\t\t:href=\"openconnectorUrl\"\n\t\t\t\tclass=\"cn-openproject-tab__banner-cta\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">{{ openconnectorLabel }}</a>\n\t\t</div>\n\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--warn\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<!-- Link / Create actions — hidden while unconfigured (the empty\n\t\t state below carries the Configure CTA instead) and while an\n\t\t auth banner is showing. -->\n\t\t<div v-if=\"!unconfigured && !authBanner\" class=\"cn-openproject-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 work package') }}\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 work package') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Unconfigured: provider reports the Integriq `openproject` source is absent. -->\n\t\t<div\n\t\t\tv-else-if=\"unconfigured\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ unconfiguredLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"openconnectorUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CogOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ configureLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-else-if=\"error\" class=\"cn-openproject-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:href=\"openprojectAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openOpenprojectLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else class=\"cn-openproject-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"wp in workPackages\"\n\t\t\t\t:key=\"wpKey(wp)\"\n\t\t\t\tclass=\"cn-openproject-tab__row\"\n\t\t\t\t:class=\"rowClass(wp)\"\n\t\t\t\t:name=\"wpSubject(wp)\"\n\t\t\t\t:bold=\"true\"\n\t\t\t\t:href=\"wpUrl(wp)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<!-- Type indicator: a coloured square chip echoing OpenProject's\n\t\t\t\t type colour-coding (Task / Bug / Feature / Milestone). -->\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-openproject-tab__type-icon\"\n\t\t\t\t\t\t:class=\"typeBadgeClass(wp)\"\n\t\t\t\t\t\t:title=\"typeTitle(wp)\">\n\t\t\t\t\t\t<RhombusOutline v-if=\"isMilestoneType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<Bug v-else-if=\"isBugType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<StarFourPointsOutline v-else-if=\"isFeatureType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<CheckboxMarkedOutline v-else :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Sub-line: #id reference + status chip, the OpenProject row meta. -->\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-openproject-tab__subline\">\n\t\t\t\t\t\t<span v-if=\"wpReference(wp)\" class=\"cn-openproject-tab__ref\">{{ wpReference(wp) }}</span>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"wpStatus(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__status-pill\"\n\t\t\t\t\t\t\t:class=\"statusPillClass(wp)\"\n\t\t\t\t\t\t\t:label=\"wpStatus(wp)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(wp)\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"wpProject(wp)\" class=\"cn-openproject-tab__project\">\n\t\t\t\t\t\t\t<Briefcase :size=\"12\" />\n\t\t\t\t\t\t\t{{ wpProject(wp) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Trailing column: type badge label so the type stays legible. -->\n\t\t\t\t<template v-if=\"wpType(wp)\" #details>\n\t\t\t\t\t<span class=\"cn-openproject-tab__type-label\">{{ wpType(wp) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Right-edge indicators: priority marker + assignee avatar. -->\n\t\t\t\t<template #indicator>\n\t\t\t\t\t<span class=\"cn-openproject-tab__indicators\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"wpPriority(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__priority\"\n\t\t\t\t\t\t\t:class=\"priorityClass(wp)\"\n\t\t\t\t\t\t\t:title=\"priorityTitle(wp)\">\n\t\t\t\t\t\t\t<AlertCircleOutline v-if=\"isHighPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronDoubleUp v-else-if=\"isMediumPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronUp v-else :size=\"14\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\tv-if=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__assignee\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t:displayName=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\t:user=\"assigneeSeed(wp)\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\thideStatus\n\t\t\t\t\t\t\t:title=\"assigneeTitle(wp)\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tclass=\"cn-openproject-tab__unlink\"\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t@click=\"unlinkWorkPackage(wp)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink work package') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnOpenProjectPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnOpenProjectCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\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, NcAvatar, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport Briefcase from 'vue-material-design-icons/Briefcase.vue'\nimport Bug from 'vue-material-design-icons/Bug.vue'\nimport CheckboxMarkedOutline from 'vue-material-design-icons/CheckboxMarkedOutline.vue'\nimport ChevronDoubleUp from 'vue-material-design-icons/ChevronDoubleUp.vue'\nimport ChevronUp from 'vue-material-design-icons/ChevronUp.vue'\nimport CogOutline from 'vue-material-design-icons/CogOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RhombusOutline from 'vue-material-design-icons/RhombusOutline.vue'\nimport StarFourPointsOutline from 'vue-material-design-icons/StarFourPointsOutline.vue'\nimport CnOpenProjectCreate from '../../../components/CnOpenProjectCreate/CnOpenProjectCreate.vue'\nimport CnOpenProjectPicker from '../../../components/CnOpenProjectPicker/CnOpenProjectPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n *\n * Renders linked work packages with type / status / priority /\n * assignee, plus auth-status surfacing for Integriq configured /\n * missing / expired states. See the file-level docblock for the full\n * surface contract.\n */\nexport default {\n\tname: 'CnOpenprojectTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tBriefcase,\n\t\tBug,\n\t\tCheckboxMarkedOutline,\n\t\tChevronDoubleUp,\n\t\tChevronUp,\n\t\tCogOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tLockOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tRhombusOutline,\n\t\tStarFourPointsOutline,\n\t\tCnStatusBadge,\n\t\tCnOpenProjectPicker,\n\t\tCnOpenProjectCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'openproject'`). */\n\t\tintegrationId: { type: String, default: 'openproject' },\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: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No work packages linked yet'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Open OpenProject\" CTA. */\n\t\topenOpenprojectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open OpenProject'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Configure OpenProject connection\" CTA. */\n\t\tconfigureLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Configure OpenProject connection'),\n\t\t},\n\n\t\t/** Pre-translated label shown when the integration is unconfigured. */\n\t\tunconfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is not configured yet. Add an `openproject` source in Integriq to start linking work packages.'),\n\t\t},\n\n\t\t/** Pre-translated banner when OpenProject is unavailable. */\n\t\tunavailableLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is currently unavailable.'),\n\t\t},\n\n\t\t/** Pre-translated banner when the OAuth/API token has expired. */\n\t\tauthExpiredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Authorisation for OpenProject expired. Reconnect the source in Integriq to restore access.'),\n\t\t},\n\n\t\t/** Pre-translated link label to the Integriq admin. */\n\t\topenconnectorLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open Integriq'),\n\t\t},\n\n\t\t/** URL of the Integriq source admin page (for the `openproject` source). */\n\t\topenconnectorUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '/index.php/apps/openconnector/sources/openproject',\n\t\t},\n\n\t\t/** Fallback URL of the OpenProject app entry. */\n\t\topenprojectAppUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: 'https://www.openproject.org',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tworkPackages: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tauthBanner: '',\n\t\t\tunconfigured: false,\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\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.fetchWorkPackages()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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 openproject endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\topenProjectEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/openproject`)\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.openProjectEndpoint(), {\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.fetchWorkPackages()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This work package is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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('[CnOpenprojectTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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.openProjectEndpoint()}/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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\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('[CnOpenprojectTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkWorkPackage(wp) {\n\t\t\tconst id = this.wpKey(wp)\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.openProjectEndpoint()}/${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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\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('[CnOpenprojectTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\n\t\t\t}\n\t\t},\n\n\t\twpKey(wp) {\n\t\t\treturn wp.id ?? wp.reference ?? ''\n\t\t},\n\n\t\t/**\n\t\t * The OpenProject `#id` reference shown on the row sub-line.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A `#123` reference, or '' when no id is known.\n\t\t */\n\t\twpReference(wp) {\n\t\t\tconst id = wp.id ?? wp.reference ?? ''\n\t\t\tif (id === '' || id === null || id === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst str = String(id)\n\t\t\treturn str.charAt(0) === '#' ? str : '#' + str\n\t\t},\n\n\t\t/**\n\t\t * Stable seed for NcAvatar's deterministic colour + initials.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A non-empty seed.\n\t\t */\n\t\tassigneeSeed(wp) {\n\t\t\treturn String(this.wpAssignee(wp) || this.wpKey(wp) || 'op')\n\t\t},\n\n\t\t/**\n\t\t * Row modifier classes (high-priority emphasis on the left edge).\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\trowClass(wp) {\n\t\t\treturn { 'cn-openproject-tab__row--high-priority': this.isHighPriority(wp) }\n\t\t},\n\n\t\ttypeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Type: {type}', { type: this.wpType(wp) || t('nextcloud-vue', 'Task') })\n\t\t},\n\n\t\tisBugType(wp) {\n\t\t\treturn String(this.wpType(wp)).toLowerCase().includes('bug')\n\t\t},\n\n\t\tisMilestoneType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('milestone') || type.includes('phase')\n\t\t},\n\n\t\tisFeatureType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('feature') || type.includes('user story') || type.includes('epic')\n\t\t},\n\n\t\t/**\n\t\t * CnStatusBadge variant matching the OpenProject status family.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A CnStatusBadge variant.\n\t\t */\n\t\tstatusVariant(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'success'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'warning'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn 'info'\n\t\t},\n\n\t\t/**\n\t\t * Priority-marker emphasis class.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\tpriorityClass(wp) {\n\t\t\treturn {\n\t\t\t\t'cn-openproject-tab__priority--high': this.isHighPriority(wp),\n\t\t\t\t'cn-openproject-tab__priority--medium': this.isMediumPriority(wp),\n\t\t\t}\n\t\t},\n\n\t\twpSubject(wp) {\n\t\t\treturn wp.title ?? wp.subject ?? wp.name ?? this.wpKey(wp)\n\t\t},\n\n\t\twpUrl(wp) {\n\t\t\tconst url = wp.url ?? wp._links?.self?.href\n\t\t\treturn url && safeHref(url) !== '#' ? url : this.openprojectAppUrl\n\t\t},\n\n\t\twpStatus(wp) {\n\t\t\treturn wp.status ?? wp._links?.status?.title ?? ''\n\t\t},\n\n\t\twpType(wp) {\n\t\t\treturn wp.type ?? wp._links?.type?.title ?? wp._embedded?.type?.name ?? ''\n\t\t},\n\n\t\twpPriority(wp) {\n\t\t\treturn wp.priority ?? wp._links?.priority?.title ?? wp._embedded?.priority?.name ?? ''\n\t\t},\n\n\t\twpAssignee(wp) {\n\t\t\treturn wp.assignee\n\t\t\t\t?? wp._links?.assignee?.title\n\t\t\t\t?? wp._embedded?.assignee?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\twpProject(wp) {\n\t\t\treturn wp.project\n\t\t\t\t?? wp._links?.project?.title\n\t\t\t\t?? wp._embedded?.project?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\tassigneeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Assigned to {name}', { name: this.wpAssignee(wp) })\n\t\t},\n\n\t\tpriorityTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Priority: {priority}', { priority: this.wpPriority(wp) })\n\t\t},\n\n\t\tisHighPriority(wp) {\n\t\t\tconst priority = String(this.wpPriority(wp)).toLowerCase()\n\t\t\treturn priority === 'high' || priority === 'immediate' || priority === 'urgent'\n\t\t},\n\n\t\tisMediumPriority(wp) {\n\t\t\treturn String(this.wpPriority(wp)).toLowerCase() === 'normal'\n\t\t},\n\n\t\tstatusPillClass(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--done'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--progress'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--blocked'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__status-pill--new'\n\t\t},\n\n\t\ttypeBadgeClass(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\tif (type.includes('bug')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--bug'\n\t\t\t}\n\t\t\tif (type.includes('feature') || type.includes('user story') || type.includes('epic')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--feature'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__type-badge--task'\n\t\t},\n\n\t\tasync fetchWorkPackages() {\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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\n\t\t\t\t\t\t|| data.items\n\t\t\t\t\t\t|| data._embedded?.elements\n\t\t\t\t\t\t|| (Array.isArray(data) ? data : [])\n\t\t\t\t\t\t|| []\n\t\t\t\t\tthis.workPackages = rows\n\t\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\n\t\t\t\t} else if (response.status === 412) {\n\t\t\t\t\t// Provider returned \"preconditions failed\" — convention for \"unconfigured\".\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 404) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.error = await this.extractError(response)\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('[CnOpenprojectTab] failed to fetch work packages', err)\n\t\t\t\tthis.workPackages = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load work packages.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync extractError(response) {\n\t\t\ttry {\n\t\t\t\tconst body = await response.json()\n\t\t\t\tif (body && typeof body.message === 'string' && body.message !== '') {\n\t\t\t\t\treturn body.message\n\t\t\t\t}\n\t\t\t\tif (body && typeof body.error === 'string' && body.error !== '') {\n\t\t\t\t\treturn body.error\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through.\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Could not load work packages.')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-openproject-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n}\n\n.cn-openproject-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-openproject-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\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-openproject-tab__banner--warn {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__banner--auth {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-openproject-tab__banner-cta {\n\tmargin-left: auto;\n\tcolor: var(--color-main-background);\n\ttext-decoration: underline;\n}\n\n.cn-openproject-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-openproject-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-openproject-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* High-priority work packages get a coloured left rail so case handlers\n spot them while scanning the list. */\n.cn-openproject-tab__row--high-priority {\n\tborder-left: 3px solid var(--color-error);\n}\n\n/* Type indicator: a rounded square chip echoing OpenProject's\n colour-coded work-package types. */\n.cn-openproject-tab__type-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 30px;\n\theight: 30px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element, #1A67A3);\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--bug {\n\tbackground: var(--color-error);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--feature {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--task {\n\tbackground: #1A67A3;\n}\n\n.cn-openproject-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* OpenProject-style `#id` reference, monospaced for a ticket feel. */\n.cn-openproject-tab__ref {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__project {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n}\n\n.cn-openproject-tab__type-label {\n\tfont-size: 0.78em;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-openproject-tab__indicators {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-openproject-tab__priority {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__priority--high {\n\tcolor: var(--color-error);\n}\n\n.cn-openproject-tab__priority--medium {\n\tcolor: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__assignee {\n\tflex-shrink: 0;\n}\n\n/* CnStatusBadge keeps its own colours; the legacy status-pill modifier\n hooks are retained so existing tests and consumers keep matching. */\n.cn-openproject-tab__status-pill {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["CnStatusBadge","CnOpenProjectPicker","CnOpenProjectCreate","t"],"mappings":"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"}
@@ -132,8 +132,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
132
132
  createVNode(_component_NcButton, {
133
133
  variant: "primary",
134
134
  href: $props.openconnectorUrl,
135
- target: "_blank",
136
- rel: "noopener noreferrer"
135
+ target: "_blank"
137
136
  }, {
138
137
  icon: withCtx(() => [
139
138
  createVNode(_component_CogOutline, { size: 20 })
@@ -156,8 +155,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
156
155
  createVNode(_component_NcButton, {
157
156
  variant: "secondary",
158
157
  href: $props.openprojectAppUrl,
159
- target: "_blank",
160
- rel: "noopener noreferrer"
158
+ target: "_blank"
161
159
  }, {
162
160
  icon: withCtx(() => [
163
161
  createVNode(_component_OpenInNew, { size: 20 })
@@ -1 +1 @@
1
- {"version":3,"file":"CnOpenprojectTab.vue3.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectTab.vue?vue&type=template&id=4c26c5c4&lang.js"],"sourcesContent":["<!--\n CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n\n Replaces the generic CnIntegrationTab for the `openproject` leaf:\n renders the work packages linked to the parent OR object as a flat\n list, each row showing subject, type badge (Task / Bug / Feature /\n …), status pill (New / In progress / Closed / …), priority indicator,\n assignee avatar, and project name. Clicking a row deep-links to the\n work package in OpenProject.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/openproject`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\OpenProjectProvider`\n (which delegates through `ExternalIntegrationRouter` to the\n Integriq source named `openproject`).\n\n Surface behaviour (per ADR-017 graceful degradation + ADR-019 external\n storage policy):\n - Unconfigured: 412 / `health=unavailable` from the provider →\n \"Configure OpenProject connection\" CTA pointing at Integriq.\n - Authorisation expired: 401 with `authStatus: 'expired'` →\n reconnect-in-Integriq banner.\n - Other 4xx/5xx: surface the upstream message (not a silent empty).\n - Loading: spinner.\n - Empty: \"No work packages linked yet\" + \"Open OpenProject\" CTA.\n - Degraded (503): \"OpenProject is currently unavailable\" banner.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses OpenProject's primary signals — *type of work*,\n *current status* and *who's responsible*. Surfacing them inline\n reproduces enough of the OpenProject row affordance for case handlers\n to triage at a glance without context-switching to OpenProject itself.\n\n As the first external-storage integration this tab also pioneers the\n auth-status surfacing the umbrella's `IntegrationHealth` contract\n exposes — silently failing on 401 is a debugging black hole.\n\n See `openregister/openspec/changes/integration-openproject/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-openproject-tab\">\n\t\t<div v-if=\"authBanner\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--auth\" role=\"alert\">\n\t\t\t<LockOutline :size=\"18\" />\n\t\t\t<span>{{ authBanner }}</span>\n\t\t\t<a\n\t\t\t\tv-if=\"openconnectorUrl\"\n\t\t\t\t:href=\"openconnectorUrl\"\n\t\t\t\tclass=\"cn-openproject-tab__banner-cta\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">{{ openconnectorLabel }}</a>\n\t\t</div>\n\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--warn\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<!-- Link / Create actions — hidden while unconfigured (the empty\n\t\t state below carries the Configure CTA instead) and while an\n\t\t auth banner is showing. -->\n\t\t<div v-if=\"!unconfigured && !authBanner\" class=\"cn-openproject-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 work package') }}\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 work package') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Unconfigured: provider reports the Integriq `openproject` source is absent. -->\n\t\t<div\n\t\t\tv-else-if=\"unconfigured\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ unconfiguredLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"openconnectorUrl\"\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<CogOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ configureLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-else-if=\"error\" class=\"cn-openproject-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:href=\"openprojectAppUrl\"\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<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openOpenprojectLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else class=\"cn-openproject-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"wp in workPackages\"\n\t\t\t\t:key=\"wpKey(wp)\"\n\t\t\t\tclass=\"cn-openproject-tab__row\"\n\t\t\t\t:class=\"rowClass(wp)\"\n\t\t\t\t:name=\"wpSubject(wp)\"\n\t\t\t\t:bold=\"true\"\n\t\t\t\t:href=\"wpUrl(wp)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<!-- Type indicator: a coloured square chip echoing OpenProject's\n\t\t\t\t type colour-coding (Task / Bug / Feature / Milestone). -->\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-openproject-tab__type-icon\"\n\t\t\t\t\t\t:class=\"typeBadgeClass(wp)\"\n\t\t\t\t\t\t:title=\"typeTitle(wp)\">\n\t\t\t\t\t\t<RhombusOutline v-if=\"isMilestoneType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<Bug v-else-if=\"isBugType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<StarFourPointsOutline v-else-if=\"isFeatureType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<CheckboxMarkedOutline v-else :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Sub-line: #id reference + status chip, the OpenProject row meta. -->\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-openproject-tab__subline\">\n\t\t\t\t\t\t<span v-if=\"wpReference(wp)\" class=\"cn-openproject-tab__ref\">{{ wpReference(wp) }}</span>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"wpStatus(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__status-pill\"\n\t\t\t\t\t\t\t:class=\"statusPillClass(wp)\"\n\t\t\t\t\t\t\t:label=\"wpStatus(wp)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(wp)\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"wpProject(wp)\" class=\"cn-openproject-tab__project\">\n\t\t\t\t\t\t\t<Briefcase :size=\"12\" />\n\t\t\t\t\t\t\t{{ wpProject(wp) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Trailing column: type badge label so the type stays legible. -->\n\t\t\t\t<template v-if=\"wpType(wp)\" #details>\n\t\t\t\t\t<span class=\"cn-openproject-tab__type-label\">{{ wpType(wp) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Right-edge indicators: priority marker + assignee avatar. -->\n\t\t\t\t<template #indicator>\n\t\t\t\t\t<span class=\"cn-openproject-tab__indicators\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"wpPriority(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__priority\"\n\t\t\t\t\t\t\t:class=\"priorityClass(wp)\"\n\t\t\t\t\t\t\t:title=\"priorityTitle(wp)\">\n\t\t\t\t\t\t\t<AlertCircleOutline v-if=\"isHighPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronDoubleUp v-else-if=\"isMediumPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronUp v-else :size=\"14\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\tv-if=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__assignee\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t:displayName=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\t:user=\"assigneeSeed(wp)\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\thideStatus\n\t\t\t\t\t\t\t:title=\"assigneeTitle(wp)\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tclass=\"cn-openproject-tab__unlink\"\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t@click=\"unlinkWorkPackage(wp)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink work package') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnOpenProjectPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnOpenProjectCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\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, NcAvatar, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport Briefcase from 'vue-material-design-icons/Briefcase.vue'\nimport Bug from 'vue-material-design-icons/Bug.vue'\nimport CheckboxMarkedOutline from 'vue-material-design-icons/CheckboxMarkedOutline.vue'\nimport ChevronDoubleUp from 'vue-material-design-icons/ChevronDoubleUp.vue'\nimport ChevronUp from 'vue-material-design-icons/ChevronUp.vue'\nimport CogOutline from 'vue-material-design-icons/CogOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RhombusOutline from 'vue-material-design-icons/RhombusOutline.vue'\nimport StarFourPointsOutline from 'vue-material-design-icons/StarFourPointsOutline.vue'\nimport CnOpenProjectCreate from '../../../components/CnOpenProjectCreate/CnOpenProjectCreate.vue'\nimport CnOpenProjectPicker from '../../../components/CnOpenProjectPicker/CnOpenProjectPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n *\n * Renders linked work packages with type / status / priority /\n * assignee, plus auth-status surfacing for Integriq configured /\n * missing / expired states. See the file-level docblock for the full\n * surface contract.\n */\nexport default {\n\tname: 'CnOpenprojectTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tBriefcase,\n\t\tBug,\n\t\tCheckboxMarkedOutline,\n\t\tChevronDoubleUp,\n\t\tChevronUp,\n\t\tCogOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tLockOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tRhombusOutline,\n\t\tStarFourPointsOutline,\n\t\tCnStatusBadge,\n\t\tCnOpenProjectPicker,\n\t\tCnOpenProjectCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'openproject'`). */\n\t\tintegrationId: { type: String, default: 'openproject' },\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: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No work packages linked yet'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Open OpenProject\" CTA. */\n\t\topenOpenprojectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open OpenProject'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Configure OpenProject connection\" CTA. */\n\t\tconfigureLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Configure OpenProject connection'),\n\t\t},\n\n\t\t/** Pre-translated label shown when the integration is unconfigured. */\n\t\tunconfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is not configured yet. Add an `openproject` source in Integriq to start linking work packages.'),\n\t\t},\n\n\t\t/** Pre-translated banner when OpenProject is unavailable. */\n\t\tunavailableLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is currently unavailable.'),\n\t\t},\n\n\t\t/** Pre-translated banner when the OAuth/API token has expired. */\n\t\tauthExpiredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Authorisation for OpenProject expired. Reconnect the source in Integriq to restore access.'),\n\t\t},\n\n\t\t/** Pre-translated link label to the Integriq admin. */\n\t\topenconnectorLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open Integriq'),\n\t\t},\n\n\t\t/** URL of the Integriq source admin page (for the `openproject` source). */\n\t\topenconnectorUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '/index.php/apps/openconnector/sources/openproject',\n\t\t},\n\n\t\t/** Fallback URL of the OpenProject app entry. */\n\t\topenprojectAppUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: 'https://www.openproject.org',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tworkPackages: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tauthBanner: '',\n\t\t\tunconfigured: false,\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\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.fetchWorkPackages()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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 openproject endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\topenProjectEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/openproject`)\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.openProjectEndpoint(), {\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.fetchWorkPackages()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This work package is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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('[CnOpenprojectTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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.openProjectEndpoint()}/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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\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('[CnOpenprojectTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkWorkPackage(wp) {\n\t\t\tconst id = this.wpKey(wp)\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.openProjectEndpoint()}/${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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\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('[CnOpenprojectTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\n\t\t\t}\n\t\t},\n\n\t\twpKey(wp) {\n\t\t\treturn wp.id ?? wp.reference ?? ''\n\t\t},\n\n\t\t/**\n\t\t * The OpenProject `#id` reference shown on the row sub-line.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A `#123` reference, or '' when no id is known.\n\t\t */\n\t\twpReference(wp) {\n\t\t\tconst id = wp.id ?? wp.reference ?? ''\n\t\t\tif (id === '' || id === null || id === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst str = String(id)\n\t\t\treturn str.charAt(0) === '#' ? str : '#' + str\n\t\t},\n\n\t\t/**\n\t\t * Stable seed for NcAvatar's deterministic colour + initials.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A non-empty seed.\n\t\t */\n\t\tassigneeSeed(wp) {\n\t\t\treturn String(this.wpAssignee(wp) || this.wpKey(wp) || 'op')\n\t\t},\n\n\t\t/**\n\t\t * Row modifier classes (high-priority emphasis on the left edge).\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\trowClass(wp) {\n\t\t\treturn { 'cn-openproject-tab__row--high-priority': this.isHighPriority(wp) }\n\t\t},\n\n\t\ttypeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Type: {type}', { type: this.wpType(wp) || t('nextcloud-vue', 'Task') })\n\t\t},\n\n\t\tisBugType(wp) {\n\t\t\treturn String(this.wpType(wp)).toLowerCase().includes('bug')\n\t\t},\n\n\t\tisMilestoneType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('milestone') || type.includes('phase')\n\t\t},\n\n\t\tisFeatureType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('feature') || type.includes('user story') || type.includes('epic')\n\t\t},\n\n\t\t/**\n\t\t * CnStatusBadge variant matching the OpenProject status family.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A CnStatusBadge variant.\n\t\t */\n\t\tstatusVariant(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'success'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'warning'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn 'info'\n\t\t},\n\n\t\t/**\n\t\t * Priority-marker emphasis class.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\tpriorityClass(wp) {\n\t\t\treturn {\n\t\t\t\t'cn-openproject-tab__priority--high': this.isHighPriority(wp),\n\t\t\t\t'cn-openproject-tab__priority--medium': this.isMediumPriority(wp),\n\t\t\t}\n\t\t},\n\n\t\twpSubject(wp) {\n\t\t\treturn wp.title ?? wp.subject ?? wp.name ?? this.wpKey(wp)\n\t\t},\n\n\t\twpUrl(wp) {\n\t\t\treturn wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl\n\t\t},\n\n\t\twpStatus(wp) {\n\t\t\treturn wp.status ?? wp._links?.status?.title ?? ''\n\t\t},\n\n\t\twpType(wp) {\n\t\t\treturn wp.type ?? wp._links?.type?.title ?? wp._embedded?.type?.name ?? ''\n\t\t},\n\n\t\twpPriority(wp) {\n\t\t\treturn wp.priority ?? wp._links?.priority?.title ?? wp._embedded?.priority?.name ?? ''\n\t\t},\n\n\t\twpAssignee(wp) {\n\t\t\treturn wp.assignee\n\t\t\t\t?? wp._links?.assignee?.title\n\t\t\t\t?? wp._embedded?.assignee?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\twpProject(wp) {\n\t\t\treturn wp.project\n\t\t\t\t?? wp._links?.project?.title\n\t\t\t\t?? wp._embedded?.project?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\tassigneeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Assigned to {name}', { name: this.wpAssignee(wp) })\n\t\t},\n\n\t\tpriorityTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Priority: {priority}', { priority: this.wpPriority(wp) })\n\t\t},\n\n\t\tisHighPriority(wp) {\n\t\t\tconst priority = String(this.wpPriority(wp)).toLowerCase()\n\t\t\treturn priority === 'high' || priority === 'immediate' || priority === 'urgent'\n\t\t},\n\n\t\tisMediumPriority(wp) {\n\t\t\treturn String(this.wpPriority(wp)).toLowerCase() === 'normal'\n\t\t},\n\n\t\tstatusPillClass(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--done'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--progress'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--blocked'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__status-pill--new'\n\t\t},\n\n\t\ttypeBadgeClass(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\tif (type.includes('bug')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--bug'\n\t\t\t}\n\t\t\tif (type.includes('feature') || type.includes('user story') || type.includes('epic')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--feature'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__type-badge--task'\n\t\t},\n\n\t\tasync fetchWorkPackages() {\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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\n\t\t\t\t\t\t|| data.items\n\t\t\t\t\t\t|| data._embedded?.elements\n\t\t\t\t\t\t|| (Array.isArray(data) ? data : [])\n\t\t\t\t\t\t|| []\n\t\t\t\t\tthis.workPackages = rows\n\t\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\n\t\t\t\t} else if (response.status === 412) {\n\t\t\t\t\t// Provider returned \"preconditions failed\" — convention for \"unconfigured\".\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 404) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.error = await this.extractError(response)\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('[CnOpenprojectTab] failed to fetch work packages', err)\n\t\t\t\tthis.workPackages = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load work packages.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync extractError(response) {\n\t\t\ttry {\n\t\t\t\tconst body = await response.json()\n\t\t\t\tif (body && typeof body.message === 'string' && body.message !== '') {\n\t\t\t\t\treturn body.message\n\t\t\t\t}\n\t\t\t\tif (body && typeof body.error === 'string' && body.error !== '') {\n\t\t\t\t\treturn body.error\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through.\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Could not load work packages.')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-openproject-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n}\n\n.cn-openproject-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-openproject-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\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-openproject-tab__banner--warn {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__banner--auth {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-openproject-tab__banner-cta {\n\tmargin-left: auto;\n\tcolor: var(--color-main-background);\n\ttext-decoration: underline;\n}\n\n.cn-openproject-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-openproject-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-openproject-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* High-priority work packages get a coloured left rail so case handlers\n spot them while scanning the list. */\n.cn-openproject-tab__row--high-priority {\n\tborder-left: 3px solid var(--color-error);\n}\n\n/* Type indicator: a rounded square chip echoing OpenProject's\n colour-coded work-package types. */\n.cn-openproject-tab__type-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 30px;\n\theight: 30px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element, #1A67A3);\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--bug {\n\tbackground: var(--color-error);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--feature {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--task {\n\tbackground: #1A67A3;\n}\n\n.cn-openproject-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* OpenProject-style `#id` reference, monospaced for a ticket feel. */\n.cn-openproject-tab__ref {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__project {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n}\n\n.cn-openproject-tab__type-label {\n\tfont-size: 0.78em;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-openproject-tab__indicators {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-openproject-tab__priority {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__priority--high {\n\tcolor: var(--color-error);\n}\n\n.cn-openproject-tab__priority--medium {\n\tcolor: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__assignee {\n\tflex-shrink: 0;\n}\n\n/* CnStatusBadge keeps its own colours; the legacy status-pill modifier\n hooks are retained so existing tests and consumers keep matching. */\n.cn-openproject-tab__status-pill {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createElementVNode","_createTextVNode","_toDisplayString","_createBlock","_normalizeClass"],"mappings":"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"}
1
+ {"version":3,"file":"CnOpenprojectTab.vue3.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectTab.vue?vue&type=template&id=4c26c5c4&lang.js"],"sourcesContent":["<!--\n CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n\n Replaces the generic CnIntegrationTab for the `openproject` leaf:\n renders the work packages linked to the parent OR object as a flat\n list, each row showing subject, type badge (Task / Bug / Feature /\n …), status pill (New / In progress / Closed / …), priority indicator,\n assignee avatar, and project name. Clicking a row deep-links to the\n work package in OpenProject.\n\n Talks to the OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/openproject`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\OpenProjectProvider`\n (which delegates through `ExternalIntegrationRouter` to the\n Integriq source named `openproject`).\n\n Surface behaviour (per ADR-017 graceful degradation + ADR-019 external\n storage policy):\n - Unconfigured: 412 / `health=unavailable` from the provider →\n \"Configure OpenProject connection\" CTA pointing at Integriq.\n - Authorisation expired: 401 with `authStatus: 'expired'` →\n reconnect-in-Integriq banner.\n - Other 4xx/5xx: surface the upstream message (not a silent empty).\n - Loading: spinner.\n - Empty: \"No work packages linked yet\" + \"Open OpenProject\" CTA.\n - Degraded (503): \"OpenProject is currently unavailable\" banner.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses OpenProject's primary signals — *type of work*,\n *current status* and *who's responsible*. Surfacing them inline\n reproduces enough of the OpenProject row affordance for case handlers\n to triage at a glance without context-switching to OpenProject itself.\n\n As the first external-storage integration this tab also pioneers the\n auth-status surfacing the umbrella's `IntegrationHealth` contract\n exposes — silently failing on 401 is a debugging black hole.\n\n See `openregister/openspec/changes/integration-openproject/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-openproject-tab\">\n\t\t<div v-if=\"authBanner\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--auth\" role=\"alert\">\n\t\t\t<LockOutline :size=\"18\" />\n\t\t\t<span>{{ authBanner }}</span>\n\t\t\t<a\n\t\t\t\tv-if=\"openconnectorUrl\"\n\t\t\t\t:href=\"openconnectorUrl\"\n\t\t\t\tclass=\"cn-openproject-tab__banner-cta\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">{{ openconnectorLabel }}</a>\n\t\t</div>\n\t\t<div v-else-if=\"degraded\" class=\"cn-openproject-tab__banner cn-openproject-tab__banner--warn\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<!-- Link / Create actions — hidden while unconfigured (the empty\n\t\t state below carries the Configure CTA instead) and while an\n\t\t auth banner is showing. -->\n\t\t<div v-if=\"!unconfigured && !authBanner\" class=\"cn-openproject-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 work package') }}\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 work package') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Unconfigured: provider reports the Integriq `openproject` source is absent. -->\n\t\t<div\n\t\t\tv-else-if=\"unconfigured\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ unconfiguredLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"openconnectorUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CogOutline :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ configureLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<div v-else-if=\"error\" class=\"cn-openproject-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div v-else-if=\"workPackages.length === 0\" class=\"cn-sidebar-tab__empty cn-openproject-tab__empty\">\n\t\t\t<Briefcase :size=\"32\" class=\"cn-openproject-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:href=\"openprojectAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openOpenprojectLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else class=\"cn-openproject-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"wp in workPackages\"\n\t\t\t\t:key=\"wpKey(wp)\"\n\t\t\t\tclass=\"cn-openproject-tab__row\"\n\t\t\t\t:class=\"rowClass(wp)\"\n\t\t\t\t:name=\"wpSubject(wp)\"\n\t\t\t\t:bold=\"true\"\n\t\t\t\t:href=\"wpUrl(wp)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<!-- Type indicator: a coloured square chip echoing OpenProject's\n\t\t\t\t type colour-coding (Task / Bug / Feature / Milestone). -->\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-openproject-tab__type-icon\"\n\t\t\t\t\t\t:class=\"typeBadgeClass(wp)\"\n\t\t\t\t\t\t:title=\"typeTitle(wp)\">\n\t\t\t\t\t\t<RhombusOutline v-if=\"isMilestoneType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<Bug v-else-if=\"isBugType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<StarFourPointsOutline v-else-if=\"isFeatureType(wp)\" :size=\"18\" />\n\t\t\t\t\t\t<CheckboxMarkedOutline v-else :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Sub-line: #id reference + status chip, the OpenProject row meta. -->\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-openproject-tab__subline\">\n\t\t\t\t\t\t<span v-if=\"wpReference(wp)\" class=\"cn-openproject-tab__ref\">{{ wpReference(wp) }}</span>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"wpStatus(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__status-pill\"\n\t\t\t\t\t\t\t:class=\"statusPillClass(wp)\"\n\t\t\t\t\t\t\t:label=\"wpStatus(wp)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(wp)\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"wpProject(wp)\" class=\"cn-openproject-tab__project\">\n\t\t\t\t\t\t\t<Briefcase :size=\"12\" />\n\t\t\t\t\t\t\t{{ wpProject(wp) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Trailing column: type badge label so the type stays legible. -->\n\t\t\t\t<template v-if=\"wpType(wp)\" #details>\n\t\t\t\t\t<span class=\"cn-openproject-tab__type-label\">{{ wpType(wp) }}</span>\n\t\t\t\t</template>\n\t\t\t\t<!-- Right-edge indicators: priority marker + assignee avatar. -->\n\t\t\t\t<template #indicator>\n\t\t\t\t\t<span class=\"cn-openproject-tab__indicators\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"wpPriority(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__priority\"\n\t\t\t\t\t\t\t:class=\"priorityClass(wp)\"\n\t\t\t\t\t\t\t:title=\"priorityTitle(wp)\">\n\t\t\t\t\t\t\t<AlertCircleOutline v-if=\"isHighPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronDoubleUp v-else-if=\"isMediumPriority(wp)\" :size=\"14\" />\n\t\t\t\t\t\t\t<ChevronUp v-else :size=\"14\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t\tv-if=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\tclass=\"cn-openproject-tab__assignee\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\t:displayName=\"wpAssignee(wp)\"\n\t\t\t\t\t\t\t:user=\"assigneeSeed(wp)\"\n\t\t\t\t\t\t\t:isNoUser=\"true\"\n\t\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t\t:disableTooltip=\"false\"\n\t\t\t\t\t\t\thideStatus\n\t\t\t\t\t\t\t:title=\"assigneeTitle(wp)\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #actions>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tclass=\"cn-openproject-tab__unlink\"\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t@click=\"unlinkWorkPackage(wp)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink work package') }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcListItem>\n\t\t</ul>\n\n\t\t<CnOpenProjectPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnOpenProjectCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t:openconnectorUrl=\"openconnectorUrl\"\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, NcAvatar, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport Briefcase from 'vue-material-design-icons/Briefcase.vue'\nimport Bug from 'vue-material-design-icons/Bug.vue'\nimport CheckboxMarkedOutline from 'vue-material-design-icons/CheckboxMarkedOutline.vue'\nimport ChevronDoubleUp from 'vue-material-design-icons/ChevronDoubleUp.vue'\nimport ChevronUp from 'vue-material-design-icons/ChevronUp.vue'\nimport CogOutline from 'vue-material-design-icons/CogOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LockOutline from 'vue-material-design-icons/LockOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport RhombusOutline from 'vue-material-design-icons/RhombusOutline.vue'\nimport StarFourPointsOutline from 'vue-material-design-icons/StarFourPointsOutline.vue'\nimport CnOpenProjectCreate from '../../../components/CnOpenProjectCreate/CnOpenProjectCreate.vue'\nimport CnOpenProjectPicker from '../../../components/CnOpenProjectPicker/CnOpenProjectPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.\n *\n * Renders linked work packages with type / status / priority /\n * assignee, plus auth-status surfacing for Integriq configured /\n * missing / expired states. See the file-level docblock for the full\n * surface contract.\n */\nexport default {\n\tname: 'CnOpenprojectTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcAvatar,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tBriefcase,\n\t\tBug,\n\t\tCheckboxMarkedOutline,\n\t\tChevronDoubleUp,\n\t\tChevronUp,\n\t\tCogOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tLockOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tRhombusOutline,\n\t\tStarFourPointsOutline,\n\t\tCnStatusBadge,\n\t\tCnOpenProjectPicker,\n\t\tCnOpenProjectCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'openproject'`). */\n\t\tintegrationId: { type: String, default: 'openproject' },\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: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No work packages linked yet'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Open OpenProject\" CTA. */\n\t\topenOpenprojectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open OpenProject'),\n\t\t},\n\n\t\t/** Pre-translated label for the \"Configure OpenProject connection\" CTA. */\n\t\tconfigureLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Configure OpenProject connection'),\n\t\t},\n\n\t\t/** Pre-translated label shown when the integration is unconfigured. */\n\t\tunconfiguredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is not configured yet. Add an `openproject` source in Integriq to start linking work packages.'),\n\t\t},\n\n\t\t/** Pre-translated banner when OpenProject is unavailable. */\n\t\tunavailableLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'OpenProject is currently unavailable.'),\n\t\t},\n\n\t\t/** Pre-translated banner when the OAuth/API token has expired. */\n\t\tauthExpiredLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Authorisation for OpenProject expired. Reconnect the source in Integriq to restore access.'),\n\t\t},\n\n\t\t/** Pre-translated link label to the Integriq admin. */\n\t\topenconnectorLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Open Integriq'),\n\t\t},\n\n\t\t/** URL of the Integriq source admin page (for the `openproject` source). */\n\t\topenconnectorUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '/index.php/apps/openconnector/sources/openproject',\n\t\t},\n\n\t\t/** Fallback URL of the OpenProject app entry. */\n\t\topenprojectAppUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: 'https://www.openproject.org',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tworkPackages: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tauthBanner: '',\n\t\t\tunconfigured: false,\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\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.fetchWorkPackages()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchWorkPackages()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\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 openproject endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\topenProjectEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/openproject`)\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.openProjectEndpoint(), {\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.fetchWorkPackages()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This work package is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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('[CnOpenprojectTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link work package.')\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.openProjectEndpoint()}/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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\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('[CnOpenprojectTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create work package.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkWorkPackage(wp) {\n\t\t\tconst id = this.wpKey(wp)\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.openProjectEndpoint()}/${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.fetchWorkPackages()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\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('[CnOpenprojectTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink work package.')\n\t\t\t}\n\t\t},\n\n\t\twpKey(wp) {\n\t\t\treturn wp.id ?? wp.reference ?? ''\n\t\t},\n\n\t\t/**\n\t\t * The OpenProject `#id` reference shown on the row sub-line.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A `#123` reference, or '' when no id is known.\n\t\t */\n\t\twpReference(wp) {\n\t\t\tconst id = wp.id ?? wp.reference ?? ''\n\t\t\tif (id === '' || id === null || id === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst str = String(id)\n\t\t\treturn str.charAt(0) === '#' ? str : '#' + str\n\t\t},\n\n\t\t/**\n\t\t * Stable seed for NcAvatar's deterministic colour + initials.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A non-empty seed.\n\t\t */\n\t\tassigneeSeed(wp) {\n\t\t\treturn String(this.wpAssignee(wp) || this.wpKey(wp) || 'op')\n\t\t},\n\n\t\t/**\n\t\t * Row modifier classes (high-priority emphasis on the left edge).\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\trowClass(wp) {\n\t\t\treturn { 'cn-openproject-tab__row--high-priority': this.isHighPriority(wp) }\n\t\t},\n\n\t\ttypeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Type: {type}', { type: this.wpType(wp) || t('nextcloud-vue', 'Task') })\n\t\t},\n\n\t\tisBugType(wp) {\n\t\t\treturn String(this.wpType(wp)).toLowerCase().includes('bug')\n\t\t},\n\n\t\tisMilestoneType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('milestone') || type.includes('phase')\n\t\t},\n\n\t\tisFeatureType(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\treturn type.includes('feature') || type.includes('user story') || type.includes('epic')\n\t\t},\n\n\t\t/**\n\t\t * CnStatusBadge variant matching the OpenProject status family.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {string} A CnStatusBadge variant.\n\t\t */\n\t\tstatusVariant(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'success'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'warning'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn 'info'\n\t\t},\n\n\t\t/**\n\t\t * Priority-marker emphasis class.\n\t\t *\n\t\t * @param {object} wp - The work-package row.\n\t\t * @return {object} The class map.\n\t\t */\n\t\tpriorityClass(wp) {\n\t\t\treturn {\n\t\t\t\t'cn-openproject-tab__priority--high': this.isHighPriority(wp),\n\t\t\t\t'cn-openproject-tab__priority--medium': this.isMediumPriority(wp),\n\t\t\t}\n\t\t},\n\n\t\twpSubject(wp) {\n\t\t\treturn wp.title ?? wp.subject ?? wp.name ?? this.wpKey(wp)\n\t\t},\n\n\t\twpUrl(wp) {\n\t\t\tconst url = wp.url ?? wp._links?.self?.href\n\t\t\treturn url && safeHref(url) !== '#' ? url : this.openprojectAppUrl\n\t\t},\n\n\t\twpStatus(wp) {\n\t\t\treturn wp.status ?? wp._links?.status?.title ?? ''\n\t\t},\n\n\t\twpType(wp) {\n\t\t\treturn wp.type ?? wp._links?.type?.title ?? wp._embedded?.type?.name ?? ''\n\t\t},\n\n\t\twpPriority(wp) {\n\t\t\treturn wp.priority ?? wp._links?.priority?.title ?? wp._embedded?.priority?.name ?? ''\n\t\t},\n\n\t\twpAssignee(wp) {\n\t\t\treturn wp.assignee\n\t\t\t\t?? wp._links?.assignee?.title\n\t\t\t\t?? wp._embedded?.assignee?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\twpProject(wp) {\n\t\t\treturn wp.project\n\t\t\t\t?? wp._links?.project?.title\n\t\t\t\t?? wp._embedded?.project?.name\n\t\t\t\t?? ''\n\t\t},\n\n\t\tassigneeTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Assigned to {name}', { name: this.wpAssignee(wp) })\n\t\t},\n\n\t\tpriorityTitle(wp) {\n\t\t\treturn t('nextcloud-vue', 'Priority: {priority}', { priority: this.wpPriority(wp) })\n\t\t},\n\n\t\tisHighPriority(wp) {\n\t\t\tconst priority = String(this.wpPriority(wp)).toLowerCase()\n\t\t\treturn priority === 'high' || priority === 'immediate' || priority === 'urgent'\n\t\t},\n\n\t\tisMediumPriority(wp) {\n\t\t\treturn String(this.wpPriority(wp)).toLowerCase() === 'normal'\n\t\t},\n\n\t\tstatusPillClass(wp) {\n\t\t\tconst status = String(this.wpStatus(wp)).toLowerCase()\n\t\t\tif (status.includes('closed') || status.includes('done') || status.includes('resolved')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--done'\n\t\t\t}\n\t\t\tif (status.includes('progress') || status.includes('developed') || status.includes('review')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--progress'\n\t\t\t}\n\t\t\tif (status.includes('reject') || status.includes('block')) {\n\t\t\t\treturn 'cn-openproject-tab__status-pill--blocked'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__status-pill--new'\n\t\t},\n\n\t\ttypeBadgeClass(wp) {\n\t\t\tconst type = String(this.wpType(wp)).toLowerCase()\n\t\t\tif (type.includes('bug')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--bug'\n\t\t\t}\n\t\t\tif (type.includes('feature') || type.includes('user story') || type.includes('epic')) {\n\t\t\t\treturn 'cn-openproject-tab__type-badge--feature'\n\t\t\t}\n\t\t\treturn 'cn-openproject-tab__type-badge--task'\n\t\t},\n\n\t\tasync fetchWorkPackages() {\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\tthis.authBanner = ''\n\t\t\tthis.unconfigured = false\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\n\t\t\t\t\t\t|| data.items\n\t\t\t\t\t\t|| data._embedded?.elements\n\t\t\t\t\t\t|| (Array.isArray(data) ? data : [])\n\t\t\t\t\t\t|| []\n\t\t\t\t\tthis.workPackages = rows\n\t\t\t\t} else if (response.status === 401 || response.status === 403) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.authBanner = this.authExpiredLabel\n\t\t\t\t} else if (response.status === 412) {\n\t\t\t\t\t// Provider returned \"preconditions failed\" — convention for \"unconfigured\".\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else if (response.status === 404) {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.unconfigured = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.workPackages = []\n\t\t\t\t\tthis.error = await this.extractError(response)\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('[CnOpenprojectTab] failed to fetch work packages', err)\n\t\t\t\tthis.workPackages = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load work packages.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync extractError(response) {\n\t\t\ttry {\n\t\t\t\tconst body = await response.json()\n\t\t\t\tif (body && typeof body.message === 'string' && body.message !== '') {\n\t\t\t\t\treturn body.message\n\t\t\t\t}\n\t\t\t\tif (body && typeof body.error === 'string' && body.error !== '') {\n\t\t\t\t\treturn body.error\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through.\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Could not load work packages.')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-openproject-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n}\n\n.cn-openproject-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-openproject-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\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-openproject-tab__banner--warn {\n\tbackground: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__banner--auth {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-openproject-tab__banner-cta {\n\tmargin-left: auto;\n\tcolor: var(--color-main-background);\n\ttext-decoration: underline;\n}\n\n.cn-openproject-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-openproject-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-openproject-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* High-priority work packages get a coloured left rail so case handlers\n spot them while scanning the list. */\n.cn-openproject-tab__row--high-priority {\n\tborder-left: 3px solid var(--color-error);\n}\n\n/* Type indicator: a rounded square chip echoing OpenProject's\n colour-coded work-package types. */\n.cn-openproject-tab__type-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 30px;\n\theight: 30px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element, #1A67A3);\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--bug {\n\tbackground: var(--color-error);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--feature {\n\tbackground: var(--color-success, #46ba61);\n}\n\n.cn-openproject-tab__type-icon.cn-openproject-tab__type-badge--task {\n\tbackground: #1A67A3;\n}\n\n.cn-openproject-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* OpenProject-style `#id` reference, monospaced for a ticket feel. */\n.cn-openproject-tab__ref {\n\tfont-family: var(--font-face-monospace, monospace);\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__project {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n}\n\n.cn-openproject-tab__type-label {\n\tfont-size: 0.78em;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-openproject-tab__indicators {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n}\n\n.cn-openproject-tab__priority {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-openproject-tab__priority--high {\n\tcolor: var(--color-error);\n}\n\n.cn-openproject-tab__priority--medium {\n\tcolor: var(--color-warning, #e9a40f);\n}\n\n.cn-openproject-tab__assignee {\n\tflex-shrink: 0;\n}\n\n/* CnStatusBadge keeps its own colours; the legacy status-pill modifier\n hooks are retained so existing tests and consumers keep matching. */\n.cn-openproject-tab__status-pill {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createElementVNode","_createTextVNode","_createBlock","_toDisplayString","_normalizeClass"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { NcLoadingIcon } from '@nextcloud/vue';
3
3
  import ImageIcon from 'vue-material-design-icons/Image.vue';
4
4
  import ImageMultiple from 'vue-material-design-icons/ImageMultiple.vue';
5
5
  import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
6
+ import { safeHref } from '../../../utils/safeHref.js';
6
7
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
7
8
 
8
9
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
@@ -140,7 +141,7 @@ var script = {
140
141
  },
141
142
 
142
143
  albumUrl(album) {
143
- if (album && album.url) {
144
+ if (album && album.url && safeHref(album.url) !== '#') {
144
145
  return album.url
145
146
  }
146
147
  const name = this.albumName(album);