@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":"CnMapsTab.vue2.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-maps-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing location') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"mapsAppUrl\"\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<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\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<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\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 location') }}\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<CnMapPoiPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnMapPoiPicker","CnMapPoiCreate","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnMapsTab.vue2.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-maps-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing location') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\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<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\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 location') }}\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<CnMapPoiPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnMapPoiPicker","CnMapPoiCreate","t"],"mappings":"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"}
@@ -109,8 +109,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
109
109
  createVNode(_component_NcButton, {
110
110
  variant: "primary",
111
111
  href: $props.mapsAppUrl,
112
- target: "_blank",
113
- rel: "noopener noreferrer"
112
+ target: "_blank"
114
113
  }, {
115
114
  icon: withCtx(() => [
116
115
  createVNode(_component_MapMarker, { size: 20 })
@@ -179,8 +178,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
179
178
  createVNode(_component_NcActionLink, {
180
179
  closeAfterClick: true,
181
180
  href: $options.pointUrl(point),
182
- target: "_blank",
183
- rel: "noopener noreferrer"
181
+ target: "_blank"
184
182
  }, {
185
183
  icon: withCtx(() => [
186
184
  createVNode(_component_OpenInNew, { size: 20 })
@@ -1 +1 @@
1
- {"version":3,"file":"CnMapsTab.vue3.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue?vue&type=template&id=00a3cb8e&lang.js"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-maps-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing location') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"mapsAppUrl\"\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<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\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<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\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 location') }}\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<CnMapPoiPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createTextVNode","_createVNode","_toDisplayString","_createBlock"],"mappings":"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"}
1
+ {"version":3,"file":"CnMapsTab.vue3.js","sources":["../../../../../src/integrations/builtin/maps/CnMapsTab.vue?vue&type=template&id=00a3cb8e&lang.js"],"sourcesContent":["<!--\n CnMapsTab — bespoke sidebar tab for the `maps` integration.\n\n Replaces the generic CnIntegrationTab for the `maps` leaf: renders\n linked POIs / favourites with name, category badge, comment snippet,\n and a nicely-formatted lat/lng coordinate pair. Each row deep-links\n into the NC Maps app — the provider already supplies a per-POI URL\n (`/index.php/apps/maps/#/m={id}`); the tab also exposes a separate\n \"Open on map\" link in the format requested by host bundles\n (`/index.php/apps/maps/?point={lat},{lng}` — see the task brief).\n\n Talks to the same OpenRegister pluggable-integration sub-resource\n `/api/objects/{register}/{schema}/{objectId}/integrations/maps`\n served by `OCA\\OpenRegister\\Service\\Integration\\Providers\\MapsProvider`\n (storage strategy `link-table` — marker `[or:{uuid}]` lives in the\n Maps `maps_favorites.name` column). Provider payload shape (verified\n against `MapsProvider::list`):\n { id, title, url, data: { id, name, lat, lng, category, comment, ... } }\n The richer \"spec\" shape (name, category, lat, lng, comment, url) is\n accepted too — the UI reads top-level and `data.*` fields defensively.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat title\n list which loses the category badge, the human-readable coordinate\n string and the comment snippet — three signals that make a POI\n legible at-a-glance. The bespoke tab surfaces them per row.\n\n Visual fidelity: each row mirrors a real NC Maps favourite — an\n offline-safe map-pin tile (no remote tiles / no external images),\n the bold location name, an address subline (falls back to the\n category when no street address is present), a monospace lat/long\n line, and a compact coordinate chip when the row carries coordinates.\n Rows are rendered as `NcListItem`s so the tab matches NC Maps' own\n favourites list look-and-feel. The pin tile is tinted with the leaf\n accent (`--cn-iw-accent`, set from the descriptor's `accentColor`)\n falling back to the theme primary.\n\n Mini-map preview: deliberately skipped — the only inline map\n component in this repo (`CnMapWidget`) is a heavy Leaflet wrapper\n intended for full-page rendering, not for a sidebar row, and would\n pull remote tiles. We surface a static pin tile + formatted\n coordinates + an \"Open on map\" link instead.\n\n Surface behaviour:\n - Empty state (\"No locations linked yet\") + \"Open Locations\" CTA\n when zero rows.\n - Loading state via NcLoadingIcon for the full-tab spin.\n - 503 unavailable banner (matches CnIntegrationTab's AD-23 graceful\n degradation), generic error path on non-OK / fetch throw.\n\n See `openregister/openspec/changes/integration-maps/` for the spec\n delta and ADR-019 (registry mechanism), ADR-022 (consumption).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-maps-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-maps-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-maps-tab__actions\">\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Link existing location') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"18\" />\n\t\t\t\t</template>\n\t\t\t\t{{ t('nextcloud-vue', 'Create new location') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<div v-else-if=\"error\" class=\"cn-maps-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<div\n\t\t\tv-else-if=\"points.length === 0 && !degraded\"\n\t\t\tclass=\"cn-sidebar-tab__empty cn-maps-tab__empty\">\n\t\t\t<MapMarker :size=\"32\" class=\"cn-maps-tab__empty-icon\" />\n\t\t\t<p>{{ emptyLabel }}</p>\n\t\t\t<NcButton\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:href=\"mapsAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<MapMarker :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openMapsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<ul v-else-if=\"points.length > 0\" class=\"cn-maps-tab__list\">\n\t\t\t<NcListItem\n\t\t\t\tv-for=\"point in points\"\n\t\t\t\t:key=\"pointKey(point)\"\n\t\t\t\tclass=\"cn-maps-tab__row\"\n\t\t\t\t:name=\"pointName(point)\"\n\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<span class=\"cn-maps-tab__pin\" aria-hidden=\"true\">\n\t\t\t\t\t\t<MapMarker :size=\"22\" class=\"cn-maps-tab__pin-icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #name>\n\t\t\t\t\t<span class=\"cn-maps-tab__title\">{{ pointName(point) }}</span>\n\t\t\t\t\t<span v-if=\"pointCategory(point)\" class=\"cn-maps-tab__category\">\n\t\t\t\t\t\t{{ pointCategory(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template #subname>\n\t\t\t\t\t<span class=\"cn-maps-tab__subname\">\n\t\t\t\t\t\t<span v-if=\"addressLabel(point)\" class=\"cn-maps-tab__address\">{{ addressLabel(point) }}</span>\n\t\t\t\t\t\t<span v-if=\"coordsLabel(point)\" class=\"cn-maps-tab__coords\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"13\" class=\"cn-maps-tab__coords-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-maps-tab__coords-text\">{{ coordsLabel(point) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"pointHasCoords(point)\"\n\t\t\t\t\t\t\t:href=\"openOnMapUrl(point)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-maps-tab__open-link\"\n\t\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pointComment(point)\" class=\"cn-maps-tab__comment\">\n\t\t\t\t\t\t{{ pointComment(point) }}\n\t\t\t\t\t</span>\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"pointHasCoords(point)\" #indicator>\n\t\t\t\t\t<span class=\"cn-maps-tab__coord-chip\" :title=\"coordsLabel(point)\">\n\t\t\t\t\t\t<MapMarker :size=\"11\" />\n\t\t\t\t\t\t{{ coordsLabel(point) }}\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<NcActionLink\n\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t:href=\"pointUrl(point)\"\n\t\t\t\t\t\ttarget=\"_blank\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<OpenInNew :size=\"20\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ openOnMapLabel }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton :closeAfterClick=\"true\" @click=\"unlinkPoint(point)\">\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 location') }}\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<CnMapPoiPicker\n\t\t\tv-if=\"pickerOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"pickerOpen = false\"\n\t\t\t@link=\"onLinkPick\" />\n\n\t\t<CnMapPoiCreate\n\t\t\tv-if=\"createOpen\"\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, NcActionLink, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport MapMarker from 'vue-material-design-icons/MapMarker.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport OpenInNew from 'vue-material-design-icons/OpenInNew.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'\nimport CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst COMMENT_MAX_CHARS = 160\n\n/**\n * CnMapsTab — bespoke sidebar list for the `maps` integration.\n *\n * Renders POIs / favourites pulled from the OR pluggable-integration\n * endpoint with name, category, comment snippet, formatted coordinates,\n * and an \"Open on map\" deep-link.\n */\nexport default {\n\tname: 'CnMapsTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tNcButton,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAlertCircleOutline,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tMapMarker,\n\t\tMapMarkerOutline,\n\t\tOpenInNew,\n\t\tPlus,\n\t\tCnMapPoiPicker,\n\t\tCnMapPoiCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (forwarded from the registry — always `'maps'`). */\n\t\tintegrationId: { type: String, default: 'maps' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No locations linked yet') },\n\t\t/** Pre-translated \"Open Locations\" CTA label. */\n\t\topenMapsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Locations') },\n\t\t/** Pre-translated \"Open on map\" inline link label. */\n\t\topenOnMapLabel: { type: String, default: () => t('nextcloud-vue', 'Open on map') },\n\t\t/** Pre-translated banner when Maps is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Location is currently unavailable.') },\n\t\t/** URL of the NC Maps app entry. */\n\t\tmapsAppUrl: { type: String, default: '/index.php/apps/maps' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpoints: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPoints()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPoints()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Base for the Tier-2 maps endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tmapsEndpoint() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/maps`\n\t\t},\n\n\t\tdataOf(point) {\n\t\t\treturn (point && typeof point.data === 'object' && point.data !== null) ? point.data : {}\n\t\t},\n\n\t\tpointKey(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? this.pointName(point)\n\t\t},\n\n\t\t/**\n\t\t * The NC Maps favorite id used for the Tier-2 unlink DELETE.\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {(number|string)} The favorite id.\n\t\t */\n\t\tfavoriteIdOf(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\treturn point.favoriteId ?? point.id ?? d.favoriteId ?? d.id ?? ''\n\t\t},\n\n\t\tpointName(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst candidate = point.name ?? point.title ?? d.name ?? d.title ?? ''\n\t\t\t// Provider returns the name (which carries the `[or:{uuid}]`\n\t\t\t// marker) as title fallback. Strip the marker for display.\n\t\t\treturn stripMarker(candidate) || String(this.pointKey(point))\n\t\t},\n\n\t\tpointCategory(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst c = point.category ?? d.category ?? ''\n\t\t\t// Maps fixtures sometimes land a bare `or:{uuid}` marker in\n\t\t\t// the category column when no real category exists. Strip\n\t\t\t// the marker and suppress the chip when nothing meaningful\n\t\t\t// remains — surfacing the marker as a chip artefact (see\n\t\t\t// phase-d1 maps-tab.png) is the bug we're fixing.\n\t\t\treturn stripMarker(c)\n\t\t},\n\n\t\tpointComment(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.comment ?? d.comment ?? ''\n\t\t\tconst text = String(raw).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (text.length > COMMENT_MAX_CHARS) {\n\t\t\t\treturn text.slice(0, COMMENT_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Human-readable address subline for the row.\n\t\t *\n\t\t * NC Maps favourites don't always carry a structured address, so\n\t\t * read a few common fields defensively (top-level + `data.*`) and\n\t\t * fall back to the category when no street address exists — that\n\t\t * keeps the subline informative without surfacing the bare coords\n\t\t * twice (coords have their own line).\n\t\t *\n\t\t * @param {object} point Provider row.\n\t\t *\n\t\t * @return {string} The address subline (may be empty).\n\t\t */\n\t\taddressLabel(point) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst raw = point.address ?? point.formattedAddress ?? d.address ?? d.formattedAddress ?? d.street ?? ''\n\t\t\tconst text = stripMarker(String(raw)).replace(/\\s+/g, ' ').trim()\n\t\t\tif (text !== '') {\n\t\t\t\treturn text\n\t\t\t}\n\t\t\treturn this.pointCategory(point)\n\t\t},\n\n\t\tcoordOf(point, key) {\n\t\t\tconst d = this.dataOf(point)\n\t\t\tconst v = point[key] ?? d[key] ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = typeof v === 'number' ? v : Number(v)\n\t\t\treturn Number.isFinite(num) === true ? num : null\n\t\t},\n\n\t\tpointLat(point) {\n\t\t\treturn this.coordOf(point, 'lat')\n\t\t},\n\n\t\tpointLng(point) {\n\t\t\treturn this.coordOf(point, 'lng')\n\t\t},\n\n\t\tpointHasCoords(point) {\n\t\t\treturn this.pointLat(point) !== null && this.pointLng(point) !== null\n\t\t},\n\n\t\tcoordsLabel(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${lat.toFixed(5)}, ${lng.toFixed(5)}`\n\t\t},\n\n\t\t// A provider URL is used only when it is a safe scheme; else the app link.\n\t\tpointUrl(point) {\n\t\t\tif (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {\n\t\t\t\treturn point.url\n\t\t\t}\n\t\t\tif (this.pointHasCoords(point)) {\n\t\t\t\treturn this.openOnMapUrl(point)\n\t\t\t}\n\t\t\treturn this.mapsAppUrl\n\t\t},\n\n\t\topenOnMapUrl(point) {\n\t\t\tconst lat = this.pointLat(point)\n\t\t\tconst lng = this.pointLng(point)\n\t\t\tif (lat === null || lng === null) {\n\t\t\t\treturn this.mapsAppUrl\n\t\t\t}\n\t\t\treturn `${this.mapsAppUrl}/?point=${lat},${lng}`\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), {\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.fetchPoints()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This location is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\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('[CnMapsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link location.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\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('[CnMapsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create location.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoint(point) {\n\t\t\tconst favoriteId = this.favoriteIdOf(point)\n\t\t\tif (!favoriteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.mapsEndpoint()}/${favoriteId}`), {\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.fetchPoints()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\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('[CnMapsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink location.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchPoints() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.mapsEndpoint()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.points = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.points = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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('[CnMapsTab] failed to fetch locations', err)\n\t\t\t\tthis.points = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load locations.')\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-maps-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-maps-tab__banner {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 10px;\n\tmargin-bottom: 10px;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n\tfont-size: 0.9em;\n}\n\n.cn-maps-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-maps-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-maps-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Offline-safe static pin tile — a tinted rounded square with a marker\n glyph, mirroring a Maps favourite thumbnail without loading any tiles. */\n.cn-maps-tab__pin {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground: var(--cn-iw-accent, var(--color-primary-element, #0082c9));\n\tcolor: var(--color-primary-element-text, #fff);\n\tflex-shrink: 0;\n}\n\n.cn-maps-tab__pin-icon {\n\tcolor: var(--color-primary-element-text, #fff);\n}\n\n.cn-maps-tab__title {\n\tfont-weight: 500;\n\tcolor: var(--color-main-text);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__category {\n\tflex-shrink: 0;\n\tmargin-inline-start: 6px;\n\tfont-size: 0.7em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.03em;\n\tvertical-align: middle;\n}\n\n.cn-maps-tab__subname {\n\tdisplay: flex;\n\talign-items: center;\n\tflex-wrap: wrap;\n\tgap: 4px 10px;\n}\n\n.cn-maps-tab__address {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-maps-tab__coords {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-maps-tab__coords-icon {\n\tcolor: var(--cn-iw-accent, var(--color-primary-element));\n}\n\n.cn-maps-tab__coords-text {\n\tfont-family: var(--font-face-monospace, monospace);\n}\n\n.cn-maps-tab__open-link {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: none;\n}\n\n.cn-maps-tab__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-maps-tab__comment {\n\tdisplay: -webkit-box;\n\tmargin-top: 2px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\t-webkit-line-clamp: 2;\n\t-webkit-box-orient: vertical;\n}\n\n/* Compact coordinate chip in the row indicator slot. */\n.cn-maps-tab__coord-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 10px;\n\tfont-size: 0.7em;\n\tfont-family: var(--font-face-monospace, monospace);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_createBlock","_toDisplayString"],"mappings":"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"}
@@ -6,6 +6,7 @@ import Briefcase from 'vue-material-design-icons/Briefcase.vue';
6
6
  import CheckCircleOutline from 'vue-material-design-icons/CheckCircleOutline.vue';
7
7
  import LockOutline from 'vue-material-design-icons/LockOutline.vue';
8
8
  import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
9
+ import { safeHref } from '../../../utils/safeHref.js';
9
10
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
10
11
 
11
12
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
@@ -188,7 +189,8 @@ var script = {
188
189
  },
189
190
 
190
191
  wpUrl(wp) {
191
- return wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl
192
+ const url = wp.url ?? wp._links?.self?.href;
193
+ return url && safeHref(url) !== '#' ? url : this.openprojectAppUrl
192
194
  },
193
195
 
194
196
  wpStatus(wp) {
@@ -1 +1 @@
1
- {"version":3,"file":"CnOpenprojectCard.vue2.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectCard.vue"],"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":["CnDetailCard","t","n"],"mappings":"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"}
1
+ {"version":3,"file":"CnOpenprojectCard.vue2.js","sources":["../../../../../src/integrations/builtin/openproject/CnOpenprojectCard.vue"],"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":["CnDetailCard","t","n"],"mappings":"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"}