@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
@@ -12,6 +12,7 @@ import Plus from 'vue-material-design-icons/Plus.vue';
12
12
  import Timer from 'vue-material-design-icons/Timer.vue';
13
13
  import __sfc_main$1 from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue.js';
14
14
  import __sfc_main from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue.js';
15
+ import { safeHref } from '../../../utils/safeHref.js';
15
16
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
16
17
 
17
18
  /**
@@ -188,7 +189,7 @@ var script = {
188
189
  },
189
190
 
190
191
  rowUrl(row) {
191
- if (row.url) {
192
+ if (row.url && safeHref(row.url) !== '#') {
192
193
  return row.url
193
194
  }
194
195
  const kind = this.rowKind(row);
@@ -1 +1 @@
1
- {"version":3,"file":"CnTimeTrackerTab.vue2.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue"],"sourcesContent":["<!--\n CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationTab for the `time-tracker` leaf:\n renders the NC TimeManager rows linked to the parent OR object as a\n flat list. TimeManager exposes THREE kinds of rows in the same\n payload (per wave-2.4 design — see `TimeProvider.php`); each row\n carries a `kind` discriminator (`'client'` | `'task'` | `'time'`)\n which this tab shows as a coloured chip alongside the title.\n\n Rows show:\n - client: name + linked-tasks count\n - task: name + duration + billable indicator\n - time: start time + duration + billable indicator\n\n Clicking a row deep-links to the TimeManager UI — own `url` field\n wins; otherwise the tab synthesises a sensible permalink per kind.\n\n Talks to the OpenRegister Tier-2 time-tracker link API\n GET /api/objects/{register}/{schema}/{id}/time-tracker\n POST /api/objects/{register}/{schema}/{id}/time-tracker (link existing)\n POST /api/objects/{register}/{schema}/{id}/time-tracker/new (create + link client)\n DELETE /api/objects/{register}/{schema}/{id}/time-tracker/{entryId} (unlink)\n served by `OCA\\OpenRegister\\Controller\\TimeTrackerLinksController`\n (link-table strategy — rows in `openregister_timetracker_links`; the\n `TimeProvider` still falls back to the legacy `[or:{uuid}]` note marker).\n\n Tier-2 surface: a Link/Create actions bar drives the\n CnTimeTrackerPicker (pick an existing entry) + CnTimeTrackerCreate\n (create a client) dialogs; each row has a per-row unlink button.\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open TimeManager\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 / 501 \"currently unavailable\" banner when TimeManager is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses TimeManager's primary signals — *what kind of entity\n the row is*, its duration, and whether it's billable. Surfacing them\n inline lets case handlers triage tracked work at a glance without\n switching to TimeManager itself.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1322` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-time-tracker-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-time-tracker-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 entry') }}\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 client') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-time-tracker-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-time-tracker-tab__empty\">\n\t\t\t<Clock :size=\"32\" class=\"cn-time-tracker-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=\"timeTrackerAppUrl\"\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<Clock :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openTimeTrackerLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<div v-if=\"totalDurationLabel\" class=\"cn-time-tracker-tab__summary\">\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-label\">\n\t\t\t\t\t<Timer :size=\"16\" />\n\t\t\t\t\t{{ t('nextcloud-vue', 'Total tracked') }}\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-value\">{{ totalDurationLabel }}</span>\n\t\t\t</div>\n\t\t\t<ul class=\"cn-time-tracker-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:name=\"rowTitle(row)\"\n\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__row-icon\" :class=\"rowClass(row)\">\n\t\t\t\t\t\t\t<ClipboardTextOutline v-if=\"rowKind(row) === 'task'\" :size=\"22\" />\n\t\t\t\t\t\t\t<AccountOutline v-else-if=\"rowKind(row) === 'client'\" :size=\"22\" />\n\t\t\t\t\t\t\t<ClockOutline v-else :size=\"22\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__subline\">\n\t\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__kind-chip\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\tv-if=\"startedAtMs(row) !== null\"\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__date\"\n\t\t\t\t\t\t\t\t:timestamp=\"startedAtMs(row)\"\n\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t<span v-else-if=\"taskCountLabel(row)\" class=\"cn-time-tracker-tab__task-count\">\n\t\t\t\t\t\t\t\t{{ taskCountLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #details>\n\t\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-tab__duration\">\n\t\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"isBillable(row)\" #indicator>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__billable\" :title=\"billableTitle\">\n\t\t\t\t\t\t\t<CurrencyEur :size=\"14\" />\n\t\t\t\t\t\t\t{{ billableLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__unlink\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkRow(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\t\t</template>\n\n\t\t<CnTimeTrackerPicker\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<CnTimeTrackerCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClipboardTextOutline from 'vue-material-design-icons/ClipboardTextOutline.vue'\nimport Clock from 'vue-material-design-icons/Clock.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Timer from 'vue-material-design-icons/Timer.vue'\nimport CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'\nimport CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n * integration.\n *\n * Renders linked TimeManager clients / tasks / time entries with a\n * kind chip, duration, billable indicator. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAccountOutline,\n\t\tAlertCircleOutline,\n\t\tClipboardTextOutline,\n\t\tClock,\n\t\tClockOutline,\n\t\tCurrencyEur,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tTimer,\n\t\tCnTimeTrackerPicker,\n\t\tCnTimeTrackerCreate,\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 `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 tracked time linked yet') },\n\t\t/** Pre-translated label for the \"Open TimeManager\" CTA. */\n\t\topenTimeTrackerLabel: { type: String, default: () => t('nextcloud-vue', 'Open TimeManager') },\n\t\t/** Pre-translated banner when TimeManager is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t\t/** Pre-translated \"billable\" label. */\n\t\tbillableLabel: { type: String, default: () => t('nextcloud-vue', 'Billable') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t\tbillableTitle: t('nextcloud-vue', 'This entry is marked as billable.'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Total tracked seconds across all linked rows that carry a\n\t\t * `duration` (clients are parent containers and usually omit it).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Human-readable total duration shown in the summary header, or\n\t\t * empty when nothing tracked.\n\t\t *\n\t\t * @return {string} Formatted \"Xh Ym\" / \"Ym\" total.\n\t\t */\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(this.totalSeconds)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Base for the Tier-2 time-tracker endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/time-tracker`\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'client' when the provider doesn't\n\t\t * surface a `kind` field (the current shipping TimeProvider\n\t\t * only returns projects-as-clients without an explicit kind).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'client'|'task'|'time'} Row kind.\n\t\t */\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-tab__kind-chip--${this.rowKind(row)}`\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn `cn-time-tracker-tab__row--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url) {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Total seconds carried on the row, normalised across the kind\n\t\t * variants. Tasks/time entries usually carry `duration` as\n\t\t * seconds (or minutes when explicitly suffixed).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Seconds total or null if absent / unparseable.\n\t\t */\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\t/**\n\t\t * Format a seconds total as a compact \"Xh Ym\" / \"Ym\" duration.\n\t\t *\n\t\t * @param {number} seconds Total seconds.\n\t\t * @return {string} Formatted duration.\n\t\t */\n\t\tformatDuration(seconds) {\n\t\t\tconst totalMinutes = Math.round(seconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(total)\n\t\t},\n\n\t\ttaskCountLabel(row) {\n\t\t\tif (this.rowKind(row) !== 'client') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = row.tasks ?? row.taskCount ?? row.data?.task_count ?? null\n\t\t\tif (raw === null || raw === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} tasks', { count: num })\n\t\t},\n\n\t\t/**\n\t\t * Start timestamp in milliseconds for the row, suitable for\n\t\t * `NcDateTime`. Only time entries carry a start; returns null when\n\t\t * absent or unparseable.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Epoch milliseconds or null.\n\t\t */\n\t\tstartedAtMs(row) {\n\t\t\tif (this.rowKind(row) !== 'time') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = row.startedAt ?? row.started_at ?? row.start ?? row.data?.start ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst ms = typeof raw === 'number' ? raw * 1000 : Date.parse(raw)\n\t\t\tif (Number.isNaN(ms)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ms\n\t\t},\n\n\t\tisBillable(row) {\n\t\t\treturn row.billable === true || row.billable === 1 || row.billable === '1'\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.baseUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\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('[CnTimeTrackerTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkRow(row) {\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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('[CnTimeTrackerTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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-time-tracker-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-time-tracker-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-time-tracker-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-time-tracker-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-time-tracker-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tmargin-bottom: 8px;\n\tborder-radius: var(--border-radius-large, var(--border-radius));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-time-tracker-tab__summary-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__summary-value {\n\tfont-weight: 700;\n\tfont-size: 1.15em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-time-tracker-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--client {\n\tbackground: var(--color-primary-element-light, var(--color-background-dark));\n\tcolor: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__date,\n.cn-time-tracker-tab__task-count {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__kind-chip {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__kind-chip--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__duration {\n\tfont-weight: 700;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__billable {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tfont-size: 0.72em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-background);\n\tbackground: var(--color-success, #46ba61);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["ClipboardTextOutline","CnTimeTrackerPicker","CnTimeTrackerCreate","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnTimeTrackerTab.vue2.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue"],"sourcesContent":["<!--\n CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationTab for the `time-tracker` leaf:\n renders the NC TimeManager rows linked to the parent OR object as a\n flat list. TimeManager exposes THREE kinds of rows in the same\n payload (per wave-2.4 design — see `TimeProvider.php`); each row\n carries a `kind` discriminator (`'client'` | `'task'` | `'time'`)\n which this tab shows as a coloured chip alongside the title.\n\n Rows show:\n - client: name + linked-tasks count\n - task: name + duration + billable indicator\n - time: start time + duration + billable indicator\n\n Clicking a row deep-links to the TimeManager UI — own `url` field\n wins; otherwise the tab synthesises a sensible permalink per kind.\n\n Talks to the OpenRegister Tier-2 time-tracker link API\n GET /api/objects/{register}/{schema}/{id}/time-tracker\n POST /api/objects/{register}/{schema}/{id}/time-tracker (link existing)\n POST /api/objects/{register}/{schema}/{id}/time-tracker/new (create + link client)\n DELETE /api/objects/{register}/{schema}/{id}/time-tracker/{entryId} (unlink)\n served by `OCA\\OpenRegister\\Controller\\TimeTrackerLinksController`\n (link-table strategy — rows in `openregister_timetracker_links`; the\n `TimeProvider` still falls back to the legacy `[or:{uuid}]` note marker).\n\n Tier-2 surface: a Link/Create actions bar drives the\n CnTimeTrackerPicker (pick an existing entry) + CnTimeTrackerCreate\n (create a client) dialogs; each row has a per-row unlink button.\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open TimeManager\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 / 501 \"currently unavailable\" banner when TimeManager is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses TimeManager's primary signals — *what kind of entity\n the row is*, its duration, and whether it's billable. Surfacing them\n inline lets case handlers triage tracked work at a glance without\n switching to TimeManager itself.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1322` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-time-tracker-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-time-tracker-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 entry') }}\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 client') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-time-tracker-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-time-tracker-tab__empty\">\n\t\t\t<Clock :size=\"32\" class=\"cn-time-tracker-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=\"timeTrackerAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Clock :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openTimeTrackerLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<div v-if=\"totalDurationLabel\" class=\"cn-time-tracker-tab__summary\">\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-label\">\n\t\t\t\t\t<Timer :size=\"16\" />\n\t\t\t\t\t{{ t('nextcloud-vue', 'Total tracked') }}\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-value\">{{ totalDurationLabel }}</span>\n\t\t\t</div>\n\t\t\t<ul class=\"cn-time-tracker-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:name=\"rowTitle(row)\"\n\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__row-icon\" :class=\"rowClass(row)\">\n\t\t\t\t\t\t\t<ClipboardTextOutline v-if=\"rowKind(row) === 'task'\" :size=\"22\" />\n\t\t\t\t\t\t\t<AccountOutline v-else-if=\"rowKind(row) === 'client'\" :size=\"22\" />\n\t\t\t\t\t\t\t<ClockOutline v-else :size=\"22\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__subline\">\n\t\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__kind-chip\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\tv-if=\"startedAtMs(row) !== null\"\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__date\"\n\t\t\t\t\t\t\t\t:timestamp=\"startedAtMs(row)\"\n\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t<span v-else-if=\"taskCountLabel(row)\" class=\"cn-time-tracker-tab__task-count\">\n\t\t\t\t\t\t\t\t{{ taskCountLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #details>\n\t\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-tab__duration\">\n\t\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"isBillable(row)\" #indicator>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__billable\" :title=\"billableTitle\">\n\t\t\t\t\t\t\t<CurrencyEur :size=\"14\" />\n\t\t\t\t\t\t\t{{ billableLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__unlink\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkRow(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\t\t</template>\n\n\t\t<CnTimeTrackerPicker\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<CnTimeTrackerCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClipboardTextOutline from 'vue-material-design-icons/ClipboardTextOutline.vue'\nimport Clock from 'vue-material-design-icons/Clock.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Timer from 'vue-material-design-icons/Timer.vue'\nimport CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'\nimport CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n * integration.\n *\n * Renders linked TimeManager clients / tasks / time entries with a\n * kind chip, duration, billable indicator. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAccountOutline,\n\t\tAlertCircleOutline,\n\t\tClipboardTextOutline,\n\t\tClock,\n\t\tClockOutline,\n\t\tCurrencyEur,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tTimer,\n\t\tCnTimeTrackerPicker,\n\t\tCnTimeTrackerCreate,\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 `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 tracked time linked yet') },\n\t\t/** Pre-translated label for the \"Open TimeManager\" CTA. */\n\t\topenTimeTrackerLabel: { type: String, default: () => t('nextcloud-vue', 'Open TimeManager') },\n\t\t/** Pre-translated banner when TimeManager is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t\t/** Pre-translated \"billable\" label. */\n\t\tbillableLabel: { type: String, default: () => t('nextcloud-vue', 'Billable') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t\tbillableTitle: t('nextcloud-vue', 'This entry is marked as billable.'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Total tracked seconds across all linked rows that carry a\n\t\t * `duration` (clients are parent containers and usually omit it).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Human-readable total duration shown in the summary header, or\n\t\t * empty when nothing tracked.\n\t\t *\n\t\t * @return {string} Formatted \"Xh Ym\" / \"Ym\" total.\n\t\t */\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(this.totalSeconds)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Base for the Tier-2 time-tracker endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/time-tracker`\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'client' when the provider doesn't\n\t\t * surface a `kind` field (the current shipping TimeProvider\n\t\t * only returns projects-as-clients without an explicit kind).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'client'|'task'|'time'} Row kind.\n\t\t */\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-tab__kind-chip--${this.rowKind(row)}`\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn `cn-time-tracker-tab__row--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Total seconds carried on the row, normalised across the kind\n\t\t * variants. Tasks/time entries usually carry `duration` as\n\t\t * seconds (or minutes when explicitly suffixed).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Seconds total or null if absent / unparseable.\n\t\t */\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\t/**\n\t\t * Format a seconds total as a compact \"Xh Ym\" / \"Ym\" duration.\n\t\t *\n\t\t * @param {number} seconds Total seconds.\n\t\t * @return {string} Formatted duration.\n\t\t */\n\t\tformatDuration(seconds) {\n\t\t\tconst totalMinutes = Math.round(seconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(total)\n\t\t},\n\n\t\ttaskCountLabel(row) {\n\t\t\tif (this.rowKind(row) !== 'client') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = row.tasks ?? row.taskCount ?? row.data?.task_count ?? null\n\t\t\tif (raw === null || raw === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} tasks', { count: num })\n\t\t},\n\n\t\t/**\n\t\t * Start timestamp in milliseconds for the row, suitable for\n\t\t * `NcDateTime`. Only time entries carry a start; returns null when\n\t\t * absent or unparseable.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Epoch milliseconds or null.\n\t\t */\n\t\tstartedAtMs(row) {\n\t\t\tif (this.rowKind(row) !== 'time') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = row.startedAt ?? row.started_at ?? row.start ?? row.data?.start ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst ms = typeof raw === 'number' ? raw * 1000 : Date.parse(raw)\n\t\t\tif (Number.isNaN(ms)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ms\n\t\t},\n\n\t\tisBillable(row) {\n\t\t\treturn row.billable === true || row.billable === 1 || row.billable === '1'\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.baseUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\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('[CnTimeTrackerTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkRow(row) {\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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('[CnTimeTrackerTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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-time-tracker-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-time-tracker-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-time-tracker-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-time-tracker-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-time-tracker-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tmargin-bottom: 8px;\n\tborder-radius: var(--border-radius-large, var(--border-radius));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-time-tracker-tab__summary-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__summary-value {\n\tfont-weight: 700;\n\tfont-size: 1.15em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-time-tracker-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--client {\n\tbackground: var(--color-primary-element-light, var(--color-background-dark));\n\tcolor: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__date,\n.cn-time-tracker-tab__task-count {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__kind-chip {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__kind-chip--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__duration {\n\tfont-weight: 700;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__billable {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tfont-size: 0.72em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-background);\n\tbackground: var(--color-success, #46ba61);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["ClipboardTextOutline","CnTimeTrackerPicker","CnTimeTrackerCreate","t"],"mappings":";;;;;;;;;;;;;;;;;AA4LA;;;AAGC;;;;AAID,aAAe;CACd,IAAI,EAAE,kBAAkB;;CAExB,UAAU,EAAE;AACX,EAAA,cAAc;AACd,EAAA,QAAQ;EACR,UAAU;EACV,UAAU;EACV,aAAa;AACb,EAAA,cAAc;AACd,EAAA,kBAAkB;AAClB,wBAAAA,wBAAoB;EACpB,KAAK;EACL,YAAY;EACZ,WAAW;AACX,EAAA,OAAO;EACP,WAAW;EACX,IAAI;EACJ,KAAK;AACL,uBAAAC,UAAmB;AACnB,uBAAAC,YAAmB;EACnB;;AAED,CAAA,KAAK,EAAE;AACN;AACA;EACA,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAa,EAAG;AACxD;AACA;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AAC1C;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACvC;AACA,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AACrC;EACA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAuB,EAAG;AAC5D;AACA,EAAA,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,4BAA4B,GAAG;;AAE7F,EAAA,oBAAoB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,kBAAkB,GAAG;;EAE7F,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,0CAA0C,CAAA,EAAG;AACjH;AACA,EAAA,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,+BAA+B;AAC3E;AACA,EAAA,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,UAAU,GAAG;EAC9E;;AAED,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;AACN,GAAA,IAAI,EAAE,EAAE;GACR,OAAO,EAAE,KAAK;AACd,GAAA,KAAK,EAAE,EAAE;GACT,QAAQ,EAAE,EAAE;AACZ,GAAA,UAAU,EAAE,KAAK;AACjB,GAAA,UAAU,EAAE,KAAK;AACjB,GAAA,aAAa,EAAEA,SAAC,CAAC,eAAe,EAAE,mCAAmC,CAAC;AACvE,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;AACT;;;AAGC;;;AAGD,EAAA,YAAY,GAAG;AACd,GAAA,IAAI,QAAQ,EAAA;GACZ,KAAK,MAAM,GAAE,IAAK,IAAI,CAAC,IAAI,EAAE;AAC5B,IAAA,MAAM,CAAE,GAAE,IAAI,CAAC,eAAe,CAAC,GAAG,EAAA;IAClC,IAAI,CAAA,KAAM,IAAI,EAAE;AACf,KAAA,KAAI,IAAK,EAAA;AACV,KAAA;AACD,IAAA;GACA,OAAO,KAAI;GACX;;AAED;;;AAGC;AACA;;AAED,EAAA,kBAAkB,GAAG;GACpB,IAAI,IAAI,CAAC,iBAAiB,CAAC,EAAE;AAC5B,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,CAAA;GAC5C;EACD;;AAED,CAAA,KAAK,EAAE;AACN,EAAA,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE;AACxC,GAAA,IAAI,EAAE,EAAE;AACP,IAAA,IAAI,CAAC,SAAS,GAAC;AAChB,IAAA;AACD,GAAA,EAAG;;EAEH,QAAQ,GAAG;AACV,GAAA,IAAI,CAAC,SAAS,GAAC;GACf;;AAED,EAAA,MAAM,GAAG;AACR,GAAA,IAAI,CAAC,SAAS,GAAC;GACf;EACD;;AAED,CAAA,OAAO,EAAE;KACRA,SAAC;;AAED;;AAEC;AACA;;AAED,EAAA,OAAO,GAAG;AACT,GAAA,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAA;GAC7F;;EAED,MAAM,CAAC,GAAG,EAAE;AACX,GAAA,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,IAAG,IAAK,GAAG,CAAC,IAAI,EAAE,IAAK,IAAG,EAAC;GAChD;;EAED,QAAQ,CAAC,GAAG,EAAE;AACb,GAAA,OAAO,GAAG,CAAC,KAAI,IAAK,GAAG,CAAC,IAAK,IAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAA;GAC/C;;AAED;;;;AAIC;;AAEA;;EAED,OAAO,CAAC,GAAG,EAAE;GACZ,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,IAAK,IAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,WAAW,GAAC;AACzD,GAAA,IAAI,CAAE,KAAI,MAAM,EAAE;IACjB,OAAO,MAAK;AACb,IAAA;AACA,GAAA,IAAI,CAAA,KAAM,MAAK,IAAK,CAAA,KAAM,WAAW,MAAM,YAAY,EAAE;IACxD,OAAO,MAAK;AACb,IAAA;GACA,OAAO,QAAO;GACd;;AAED,EAAA,SAAS,CAAC,GAAG,EAAE;GACd,MAAM,IAAG,GAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAA;AAC7B,GAAA,IAAI,IAAK,KAAI,MAAM,EAAE;AACpB,IAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,MAAM,CAAA;AACjC,IAAA;AACA,GAAA,IAAI,IAAK,KAAI,MAAM,EAAE;AACpB,IAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,OAAO,CAAA;AAClC,IAAA;AACA,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,QAAQ,CAAA;GAClC;;AAED,EAAA,aAAa,CAAC,GAAG,EAAE;GAClB,OAAO,CAAC,gCAAgC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;GAC5D;;EAED,QAAQ,CAAC,GAAG,EAAE;GACb,OAAO,CAAC,0BAA0B,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;GACtD;;EAED,MAAM,CAAC,GAAG,EAAE;AACX,GAAA,IAAI,GAAG,CAAC,GAAE,IAAK,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAA,KAAM,GAAG,EAAE;IACzC,OAAO,GAAG,CAAC,GAAE;AACd,IAAA;GACA,MAAM,IAAG,GAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAA;GAC7B,MAAM,EAAG,GAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAA;AAC1B,GAAA,IAAI,SAAS,QAAQ,EAAE;IACtB,MAAM,QAAS,GAAE,GAAG,CAAC,QAAO,IAAK,GAAC;IAClC,IAAI,QAAQ,EAAE;KACb,OAAO,CAAC,oCAAoC,EAAE,QAAQ,CAAC,CAAA;AACxD,KAAA;AACD,IAAA;AACA,GAAA,IAAI,IAAK,KAAI,MAAM,EAAE;IACpB,MAAM,MAAO,GAAE,GAAG,CAAC,MAAK,IAAK,GAAC;IAC9B,IAAI,MAAM,EAAE;KACX,OAAO,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAA;AACpD,KAAA;AACD,IAAA;AACA,GAAA,IAAI,IAAG,KAAM,MAAK,IAAK,EAAE,EAAE;IAC1B,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAA;AAChD,IAAA;AACA,GAAA,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,qCAAqC,EAAE,EAAE,CAAC,CAAA;AACnD,IAAA;GACA,OAAO,IAAI,CAAC,iBAAgB;GAC5B;;AAED;;;AAGC;AACA;;;;AAID,EAAA,eAAe,CAAC,GAAG,EAAE;GACpB,MAAM,GAAE,GAAI,GAAG,CAAC,QAAO,IAAK,GAAG,CAAC,OAAM,IAAK,GAAG,CAAC,IAAI,EAAE,QAAO,IAAK,KAAG;AACpE,GAAA,IAAI,GAAE,KAAM,IAAK,IAAG,QAAQ,SAAU,IAAG,GAAI,KAAI,EAAE,EAAE;AACpD,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,GAAI,GAAE,MAAM,CAAC,GAAG,EAAA;AACtB,GAAA,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAA,GAAI,IAAK,GAAE,GAAE;GACpC;;AAED;;AAEC;AACA;AACA;;EAED,cAAc,CAAC,OAAO,EAAE;AACvB,GAAA,MAAM,eAAe,IAAI,CAAC,KAAK,CAAC,OAAQ,GAAE,EAAE,EAAA;AAC5C,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,KAAK,CAAC,YAAa,GAAE,EAAE,EAAA;GAC1C,MAAM,OAAQ,GAAE,YAAa,GAAE,GAAC;GAChC,IAAI,KAAI,GAAI,CAAC,EAAE;IACd,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAA;AAC9B,IAAA;AACA,GAAA,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;GACnB;;AAED,EAAA,aAAa,CAAC,GAAG,EAAE;AAClB,GAAA,MAAM,KAAI,GAAI,IAAI,CAAC,eAAe,CAAC,GAAG,EAAA;AACtC,GAAA,IAAI,KAAI,KAAM,IAAI,EAAE;AACnB,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAA;GAChC;;AAED,EAAA,cAAc,CAAC,GAAG,EAAE;AACnB,GAAA,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAA,KAAM,QAAQ,EAAE;AACnC,IAAA,OAAO,EAAC;AACT,IAAA;GACA,MAAM,GAAE,GAAI,GAAG,CAAC,KAAI,IAAK,GAAG,CAAC,SAAQ,IAAK,GAAG,CAAC,IAAI,EAAE,UAAS,IAAK,KAAG;AACrE,GAAA,IAAI,GAAI,KAAI,IAAG,IAAK,GAAI,KAAI,SAAS,EAAE;AACtC,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,GAAI,GAAE,MAAM,CAAC,GAAG,EAAA;AACtB,GAAA,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;AACtB,IAAA,OAAO,EAAC;AACT,IAAA;GACA,OAAOA,SAAC,CAAC,eAAe,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,GAAE,EAAG,CAAA;GACzD;;AAED;;;AAGC;AACA;;AAEA;;AAED,EAAA,WAAW,CAAC,GAAG,EAAE;AAChB,GAAA,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAE,KAAI,MAAM,EAAE;AACjC,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,GAAE,GAAI,GAAG,CAAC,SAAU,IAAG,GAAG,CAAC,UAAW,IAAG,GAAG,CAAC,KAAM,IAAG,GAAG,CAAC,IAAI,EAAE,KAAM,IAAG,KAAG;AAClF,GAAA,IAAI,GAAE,KAAM,IAAK,IAAG,QAAQ,SAAU,IAAG,GAAI,KAAI,EAAE,EAAE;AACpD,IAAA,OAAO,IAAG;AACX,IAAA;GACA,MAAM,EAAG,GAAE,OAAO,GAAE,KAAM,QAAS,GAAE,GAAE,GAAI,IAAG,GAAI,IAAI,CAAC,KAAK,CAAC,GAAG,EAAA;AAChE,GAAA,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;AACrB,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,OAAO,EAAC;GACR;;AAED,EAAA,UAAU,CAAC,GAAG,EAAE;AACf,GAAA,OAAO,GAAG,CAAC,QAAS,KAAI,IAAG,IAAK,GAAG,CAAC,QAAO,KAAM,CAAA,IAAK,GAAG,CAAC,QAAO,KAAM,GAAE;GACzE;;EAED,UAAU,GAAG;AACZ,GAAA,IAAI,CAAC,aAAa,KAAG;GACrB;;EAED,UAAU,GAAG;AACZ,GAAA,IAAI,CAAC,aAAa,KAAG;GACrB;;EAED,MAAM,UAAU,CAAC,OAAO,EAAE;AACzB,GAAA,IAAI,CAAC,UAAS,GAAI,MAAI;AACtB,GAAA,IAAI;IACH,MAAM,WAAW,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE;KACvD,MAAM,EAAE,MAAM;KACd,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,cAAc,EAAE,kBAAiB,EAAG;KAClE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC7B,EAAA;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;AAChB,KAAA,MAAM,IAAI,CAAC,SAAS,GAAC;AACtB,KAAA,MAAO,IAAI,QAAQ,CAAC,MAAK,KAAM,GAAG,EAAE;KACnC,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,+BAA+B,EAAA;KAC9D,MAAK;KACN,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,uBAAuB,EAAA;AACxD,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,EAAA;IACnD,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,uBAAuB,EAAA;AACxD,IAAA;GACA;;EAED,MAAM,YAAY,CAAC,OAAO,EAAE;AAC3B,GAAA,IAAI,CAAC,UAAS,GAAI,MAAI;AACtB,GAAA,IAAI;IACH,MAAM,QAAS,GAAE,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE;KAChE,MAAM,EAAE,MAAM;KACd,OAAO,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,cAAc,EAAE,kBAAiB,EAAG;KAClE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC7B,EAAA;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;AAChB,KAAA,MAAM,IAAI,CAAC,SAAS,GAAC;KACpB,MAAK;KACN,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,0BAA0B,EAAA;AAC3D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,EAAA;IACrD,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,0BAA0B,EAAA;AAC3D,IAAA;GACA;;AAED,EAAA,MAAM,SAAS,CAAC,GAAG,EAAE;GACpB,MAAM,EAAG,GAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAA;AAC1B,GAAA,IAAI,CAAC,EAAE,EAAE;IACR,MAAK;AACN,IAAA;AACA,GAAA,IAAI;AACH,IAAA,MAAM,QAAS,GAAE,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;AACtF,KAAA,MAAM,EAAE,QAAQ;KAChB,OAAO,EAAE,YAAY,EAAE;KACvB,EAAA;AACD,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;AAChB,KAAA,MAAM,IAAI,CAAC,SAAS,GAAC;KACpB,MAAK;KACN,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,yBAAyB,EAAA;AAC1D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,EAAA;IACrD,IAAI,CAAC,KAAI,GAAIA,SAAC,CAAC,eAAe,EAAE,yBAAyB,EAAA;AAC1D,IAAA;GACA;;AAED,EAAA,MAAM,SAAS,GAAG;GACjB,IAAI,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE;IACrD,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;GAClB,IAAI,CAAC,KAAM,GAAE,GAAC;AACd,GAAA,IAAI,CAAC,QAAO,GAAI,GAAC;AACjB,GAAA,IAAI;AACH,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,IAAI,EAAA;AACnF,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,KAAA,MAAM,IAAG,GAAI,IAAI,CAAC,OAAM,IAAK,IAAI,CAAC,KAAI,KAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAE,GAAE,IAAK,GAAE,EAAE,CAAE,IAAG,GAAC;KACjF,IAAI,CAAC,IAAK,GAAE,KAAG;KACd,MAAK,IAAI,QAAQ,CAAC,MAAO,KAAI,GAAE,IAAK,QAAQ,CAAC,MAAO,KAAI,GAAG,EAAE;KAC9D,IAAI,CAAC,IAAK,GAAE,GAAC;AACb,KAAA,IAAI,CAAC,QAAS,GAAE,IAAI,CAAC,iBAAe;KACnC,MAAK;KACN,IAAI,CAAC,IAAK,GAAE,GAAC;KACb,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,8BAA8B,EAAA;AAC/D,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,EAAA;IAC5D,IAAI,CAAC,IAAK,GAAE,GAAC;IACb,IAAI,CAAC,KAAM,GAAEA,SAAC,CAAC,eAAe,EAAE,8BAA8B,EAAA;AAC/D,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;EACD;AACF;;;;"}
@@ -105,8 +105,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
105
105
  createVNode(_component_NcButton, {
106
106
  variant: "primary",
107
107
  href: $props.timeTrackerAppUrl,
108
- target: "_blank",
109
- rel: "noopener noreferrer"
108
+ target: "_blank"
110
109
  }, {
111
110
  icon: withCtx(() => [
112
111
  createVNode(_component_Clock, { size: 20 })
@@ -1 +1 @@
1
- {"version":3,"file":"CnTimeTrackerTab.vue3.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue?vue&type=template&id=53500c09&lang.js"],"sourcesContent":["<!--\n CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationTab for the `time-tracker` leaf:\n renders the NC TimeManager rows linked to the parent OR object as a\n flat list. TimeManager exposes THREE kinds of rows in the same\n payload (per wave-2.4 design — see `TimeProvider.php`); each row\n carries a `kind` discriminator (`'client'` | `'task'` | `'time'`)\n which this tab shows as a coloured chip alongside the title.\n\n Rows show:\n - client: name + linked-tasks count\n - task: name + duration + billable indicator\n - time: start time + duration + billable indicator\n\n Clicking a row deep-links to the TimeManager UI — own `url` field\n wins; otherwise the tab synthesises a sensible permalink per kind.\n\n Talks to the OpenRegister Tier-2 time-tracker link API\n GET /api/objects/{register}/{schema}/{id}/time-tracker\n POST /api/objects/{register}/{schema}/{id}/time-tracker (link existing)\n POST /api/objects/{register}/{schema}/{id}/time-tracker/new (create + link client)\n DELETE /api/objects/{register}/{schema}/{id}/time-tracker/{entryId} (unlink)\n served by `OCA\\OpenRegister\\Controller\\TimeTrackerLinksController`\n (link-table strategy — rows in `openregister_timetracker_links`; the\n `TimeProvider` still falls back to the legacy `[or:{uuid}]` note marker).\n\n Tier-2 surface: a Link/Create actions bar drives the\n CnTimeTrackerPicker (pick an existing entry) + CnTimeTrackerCreate\n (create a client) dialogs; each row has a per-row unlink button.\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open TimeManager\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 / 501 \"currently unavailable\" banner when TimeManager is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses TimeManager's primary signals — *what kind of entity\n the row is*, its duration, and whether it's billable. Surfacing them\n inline lets case handlers triage tracked work at a glance without\n switching to TimeManager itself.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1322` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-time-tracker-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-time-tracker-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 entry') }}\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 client') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-time-tracker-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-time-tracker-tab__empty\">\n\t\t\t<Clock :size=\"32\" class=\"cn-time-tracker-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=\"timeTrackerAppUrl\"\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<Clock :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openTimeTrackerLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<div v-if=\"totalDurationLabel\" class=\"cn-time-tracker-tab__summary\">\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-label\">\n\t\t\t\t\t<Timer :size=\"16\" />\n\t\t\t\t\t{{ t('nextcloud-vue', 'Total tracked') }}\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-value\">{{ totalDurationLabel }}</span>\n\t\t\t</div>\n\t\t\t<ul class=\"cn-time-tracker-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:name=\"rowTitle(row)\"\n\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__row-icon\" :class=\"rowClass(row)\">\n\t\t\t\t\t\t\t<ClipboardTextOutline v-if=\"rowKind(row) === 'task'\" :size=\"22\" />\n\t\t\t\t\t\t\t<AccountOutline v-else-if=\"rowKind(row) === 'client'\" :size=\"22\" />\n\t\t\t\t\t\t\t<ClockOutline v-else :size=\"22\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__subline\">\n\t\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__kind-chip\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\tv-if=\"startedAtMs(row) !== null\"\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__date\"\n\t\t\t\t\t\t\t\t:timestamp=\"startedAtMs(row)\"\n\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t<span v-else-if=\"taskCountLabel(row)\" class=\"cn-time-tracker-tab__task-count\">\n\t\t\t\t\t\t\t\t{{ taskCountLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #details>\n\t\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-tab__duration\">\n\t\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"isBillable(row)\" #indicator>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__billable\" :title=\"billableTitle\">\n\t\t\t\t\t\t\t<CurrencyEur :size=\"14\" />\n\t\t\t\t\t\t\t{{ billableLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__unlink\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkRow(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\t\t</template>\n\n\t\t<CnTimeTrackerPicker\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<CnTimeTrackerCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClipboardTextOutline from 'vue-material-design-icons/ClipboardTextOutline.vue'\nimport Clock from 'vue-material-design-icons/Clock.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Timer from 'vue-material-design-icons/Timer.vue'\nimport CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'\nimport CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\n\n/**\n * CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n * integration.\n *\n * Renders linked TimeManager clients / tasks / time entries with a\n * kind chip, duration, billable indicator. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAccountOutline,\n\t\tAlertCircleOutline,\n\t\tClipboardTextOutline,\n\t\tClock,\n\t\tClockOutline,\n\t\tCurrencyEur,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tTimer,\n\t\tCnTimeTrackerPicker,\n\t\tCnTimeTrackerCreate,\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 `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 tracked time linked yet') },\n\t\t/** Pre-translated label for the \"Open TimeManager\" CTA. */\n\t\topenTimeTrackerLabel: { type: String, default: () => t('nextcloud-vue', 'Open TimeManager') },\n\t\t/** Pre-translated banner when TimeManager is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t\t/** Pre-translated \"billable\" label. */\n\t\tbillableLabel: { type: String, default: () => t('nextcloud-vue', 'Billable') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t\tbillableTitle: t('nextcloud-vue', 'This entry is marked as billable.'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Total tracked seconds across all linked rows that carry a\n\t\t * `duration` (clients are parent containers and usually omit it).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Human-readable total duration shown in the summary header, or\n\t\t * empty when nothing tracked.\n\t\t *\n\t\t * @return {string} Formatted \"Xh Ym\" / \"Ym\" total.\n\t\t */\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(this.totalSeconds)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Base for the Tier-2 time-tracker endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/time-tracker`\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'client' when the provider doesn't\n\t\t * surface a `kind` field (the current shipping TimeProvider\n\t\t * only returns projects-as-clients without an explicit kind).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'client'|'task'|'time'} Row kind.\n\t\t */\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-tab__kind-chip--${this.rowKind(row)}`\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn `cn-time-tracker-tab__row--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url) {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Total seconds carried on the row, normalised across the kind\n\t\t * variants. Tasks/time entries usually carry `duration` as\n\t\t * seconds (or minutes when explicitly suffixed).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Seconds total or null if absent / unparseable.\n\t\t */\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\t/**\n\t\t * Format a seconds total as a compact \"Xh Ym\" / \"Ym\" duration.\n\t\t *\n\t\t * @param {number} seconds Total seconds.\n\t\t * @return {string} Formatted duration.\n\t\t */\n\t\tformatDuration(seconds) {\n\t\t\tconst totalMinutes = Math.round(seconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(total)\n\t\t},\n\n\t\ttaskCountLabel(row) {\n\t\t\tif (this.rowKind(row) !== 'client') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = row.tasks ?? row.taskCount ?? row.data?.task_count ?? null\n\t\t\tif (raw === null || raw === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} tasks', { count: num })\n\t\t},\n\n\t\t/**\n\t\t * Start timestamp in milliseconds for the row, suitable for\n\t\t * `NcDateTime`. Only time entries carry a start; returns null when\n\t\t * absent or unparseable.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Epoch milliseconds or null.\n\t\t */\n\t\tstartedAtMs(row) {\n\t\t\tif (this.rowKind(row) !== 'time') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = row.startedAt ?? row.started_at ?? row.start ?? row.data?.start ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst ms = typeof raw === 'number' ? raw * 1000 : Date.parse(raw)\n\t\t\tif (Number.isNaN(ms)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ms\n\t\t},\n\n\t\tisBillable(row) {\n\t\t\treturn row.billable === true || row.billable === 1 || row.billable === '1'\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.baseUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\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('[CnTimeTrackerTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkRow(row) {\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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('[CnTimeTrackerTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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-time-tracker-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-time-tracker-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-time-tracker-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-time-tracker-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-time-tracker-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tmargin-bottom: 8px;\n\tborder-radius: var(--border-radius-large, var(--border-radius));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-time-tracker-tab__summary-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__summary-value {\n\tfont-weight: 700;\n\tfont-size: 1.15em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-time-tracker-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--client {\n\tbackground: var(--color-primary-element-light, var(--color-background-dark));\n\tcolor: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__date,\n.cn-time-tracker-tab__task-count {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__kind-chip {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__kind-chip--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__duration {\n\tfont-weight: 700;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__billable {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tfont-size: 0.72em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-background);\n\tbackground: var(--color-success, #46ba61);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createTextVNode","_createBlock","_toDisplayString","_createElementVNode","_normalizeClass"],"mappings":"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"}
1
+ {"version":3,"file":"CnTimeTrackerTab.vue3.js","sources":["../../../../../src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue?vue&type=template&id=53500c09&lang.js"],"sourcesContent":["<!--\n CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n integration.\n\n Replaces the generic CnIntegrationTab for the `time-tracker` leaf:\n renders the NC TimeManager rows linked to the parent OR object as a\n flat list. TimeManager exposes THREE kinds of rows in the same\n payload (per wave-2.4 design — see `TimeProvider.php`); each row\n carries a `kind` discriminator (`'client'` | `'task'` | `'time'`)\n which this tab shows as a coloured chip alongside the title.\n\n Rows show:\n - client: name + linked-tasks count\n - task: name + duration + billable indicator\n - time: start time + duration + billable indicator\n\n Clicking a row deep-links to the TimeManager UI — own `url` field\n wins; otherwise the tab synthesises a sensible permalink per kind.\n\n Talks to the OpenRegister Tier-2 time-tracker link API\n GET /api/objects/{register}/{schema}/{id}/time-tracker\n POST /api/objects/{register}/{schema}/{id}/time-tracker (link existing)\n POST /api/objects/{register}/{schema}/{id}/time-tracker/new (create + link client)\n DELETE /api/objects/{register}/{schema}/{id}/time-tracker/{entryId} (unlink)\n served by `OCA\\OpenRegister\\Controller\\TimeTrackerLinksController`\n (link-table strategy — rows in `openregister_timetracker_links`; the\n `TimeProvider` still falls back to the legacy `[or:{uuid}]` note marker).\n\n Tier-2 surface: a Link/Create actions bar drives the\n CnTimeTrackerPicker (pick an existing entry) + CnTimeTrackerCreate\n (create a client) dialogs; each row has a per-row unlink button.\n\n Surface behaviour (per ADR-017 graceful degradation):\n - Empty state with \"Open TimeManager\" CTA when no linked rows.\n - Loading spinner during fetch.\n - 503 / 501 \"currently unavailable\" banner when TimeManager is down.\n - Generic error label when fetch throws.\n\n Bespoke-vs-generic rationale: the generic tab renders a flat link\n list which loses TimeManager's primary signals — *what kind of entity\n the row is*, its duration, and whether it's billable. Surfacing them\n inline lets case handlers triage tracked work at a glance without\n switching to TimeManager itself.\n\n Note: the leaf slug is `time-tracker` (with a hyphen); the underlying\n NC app id is `timemanager` (no hyphen).\n\n See `openregister/openspec/changes/integration-time-tracker/` for the\n spec delta, ADR-019 (registry mechanism), ADR-022 (sidebar tab\n contract), `openregister#1322` for provider follow-ups.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-time-tracker-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-time-tracker-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div v-if=\"!degraded\" class=\"cn-time-tracker-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 entry') }}\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 client') }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"error\" class=\"cn-time-tracker-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"rows.length === 0\" class=\"cn-sidebar-tab__empty cn-time-tracker-tab__empty\">\n\t\t\t<Clock :size=\"32\" class=\"cn-time-tracker-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=\"timeTrackerAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Clock :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openTimeTrackerLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<template v-else>\n\t\t\t<div v-if=\"totalDurationLabel\" class=\"cn-time-tracker-tab__summary\">\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-label\">\n\t\t\t\t\t<Timer :size=\"16\" />\n\t\t\t\t\t{{ t('nextcloud-vue', 'Total tracked') }}\n\t\t\t\t</span>\n\t\t\t\t<span class=\"cn-time-tracker-tab__summary-value\">{{ totalDurationLabel }}</span>\n\t\t\t</div>\n\t\t\t<ul class=\"cn-time-tracker-tab__list\">\n\t\t\t\t<NcListItem\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"rowKey(row)\"\n\t\t\t\t\tclass=\"cn-time-tracker-tab__row\"\n\t\t\t\t\t:class=\"rowClass(row)\"\n\t\t\t\t\t:name=\"rowTitle(row)\"\n\t\t\t\t\t:bold=\"true\"\n\t\t\t\t\t:href=\"rowUrl(row)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__row-icon\" :class=\"rowClass(row)\">\n\t\t\t\t\t\t\t<ClipboardTextOutline v-if=\"rowKind(row) === 'task'\" :size=\"22\" />\n\t\t\t\t\t\t\t<AccountOutline v-else-if=\"rowKind(row) === 'client'\" :size=\"22\" />\n\t\t\t\t\t\t\t<ClockOutline v-else :size=\"22\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__subline\">\n\t\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__kind-chip\" :class=\"kindChipClass(row)\">\n\t\t\t\t\t\t\t\t{{ kindLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\t\tv-if=\"startedAtMs(row) !== null\"\n\t\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__date\"\n\t\t\t\t\t\t\t\t:timestamp=\"startedAtMs(row)\"\n\t\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t\t\t<span v-else-if=\"taskCountLabel(row)\" class=\"cn-time-tracker-tab__task-count\">\n\t\t\t\t\t\t\t\t{{ taskCountLabel(row) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #details>\n\t\t\t\t\t\t<span v-if=\"durationLabel(row)\" class=\"cn-time-tracker-tab__duration\">\n\t\t\t\t\t\t\t{{ durationLabel(row) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"isBillable(row)\" #indicator>\n\t\t\t\t\t\t<span class=\"cn-time-tracker-tab__billable\" :title=\"billableTitle\">\n\t\t\t\t\t\t\t<CurrencyEur :size=\"14\" />\n\t\t\t\t\t\t\t{{ billableLabel }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #actions>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tclass=\"cn-time-tracker-tab__unlink\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"unlinkRow(row)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Unlink entry') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</template>\n\t\t\t\t</NcListItem>\n\t\t\t</ul>\n\t\t</template>\n\n\t\t<CnTimeTrackerPicker\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<CnTimeTrackerCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcDateTime, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountOutline from 'vue-material-design-icons/AccountOutline.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClipboardTextOutline from 'vue-material-design-icons/ClipboardTextOutline.vue'\nimport Clock from 'vue-material-design-icons/Clock.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'\nimport LinkOff from 'vue-material-design-icons/LinkOff.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport Timer from 'vue-material-design-icons/Timer.vue'\nimport CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'\nimport CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\n\n/**\n * CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`\n * integration.\n *\n * Renders linked TimeManager clients / tasks / time entries with a\n * kind chip, duration, billable indicator. See file-level docblock.\n */\nexport default {\n\tname: 'CnTimeTrackerTab',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcListItem,\n\t\tNcLoadingIcon,\n\t\tAccountOutline,\n\t\tAlertCircleOutline,\n\t\tClipboardTextOutline,\n\t\tClock,\n\t\tClockOutline,\n\t\tCurrencyEur,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tTimer,\n\t\tCnTimeTrackerPicker,\n\t\tCnTimeTrackerCreate,\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 `'time-tracker'`). */\n\t\tintegrationId: { type: String, default: 'time-tracker' },\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 tracked time linked yet') },\n\t\t/** Pre-translated label for the \"Open TimeManager\" CTA. */\n\t\topenTimeTrackerLabel: { type: String, default: () => t('nextcloud-vue', 'Open TimeManager') },\n\t\t/** Pre-translated banner when TimeManager is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC TimeManager is currently unavailable.') },\n\t\t/** URL of the NC TimeManager app entry. */\n\t\ttimeTrackerAppUrl: { type: String, default: '/index.php/apps/timemanager' },\n\t\t/** Pre-translated \"billable\" label. */\n\t\tbillableLabel: { type: String, default: () => t('nextcloud-vue', 'Billable') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t\tbillableTitle: t('nextcloud-vue', 'This entry is marked as billable.'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Total tracked seconds across all linked rows that carry a\n\t\t * `duration` (clients are parent containers and usually omit it).\n\t\t *\n\t\t * @return {number} Seconds total.\n\t\t */\n\t\ttotalSeconds() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tconst d = this.durationSeconds(row)\n\t\t\t\tif (d !== null) {\n\t\t\t\t\ttotal += d\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Human-readable total duration shown in the summary header, or\n\t\t * empty when nothing tracked.\n\t\t *\n\t\t * @return {string} Formatted \"Xh Ym\" / \"Ym\" total.\n\t\t */\n\t\ttotalDurationLabel() {\n\t\t\tif (this.totalSeconds === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(this.totalSeconds)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchRows()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchRows()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Base for the Tier-2 time-tracker endpoints (list/link/new/destroy).\n\t\t *\n\t\t * @return {string} The endpoint URL.\n\t\t */\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/time-tracker`\n\t\t},\n\n\t\trowKey(row) {\n\t\t\treturn row.id ?? row.uuid ?? row.data?.uuid ?? ''\n\t\t},\n\n\t\trowTitle(row) {\n\t\t\treturn row.title ?? row.name ?? this.rowKey(row)\n\t\t},\n\n\t\t/**\n\t\t * Discriminator. Defaults to 'client' when the provider doesn't\n\t\t * surface a `kind` field (the current shipping TimeProvider\n\t\t * only returns projects-as-clients without an explicit kind).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {'client'|'task'|'time'} Row kind.\n\t\t */\n\t\trowKind(row) {\n\t\t\tconst k = String(row.kind ?? row.type ?? '').toLowerCase()\n\t\t\tif (k === 'task') {\n\t\t\t\treturn 'task'\n\t\t\t}\n\t\t\tif (k === 'time' || k === 'entry' || k === 'time-entry') {\n\t\t\t\treturn 'time'\n\t\t\t}\n\t\t\treturn 'client'\n\t\t},\n\n\t\tkindLabel(row) {\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tif (kind === 'task') {\n\t\t\t\treturn t('nextcloud-vue', 'Task')\n\t\t\t}\n\t\t\tif (kind === 'time') {\n\t\t\t\treturn t('nextcloud-vue', 'Entry')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Client')\n\t\t},\n\n\t\tkindChipClass(row) {\n\t\t\treturn `cn-time-tracker-tab__kind-chip--${this.rowKind(row)}`\n\t\t},\n\n\t\trowClass(row) {\n\t\t\treturn `cn-time-tracker-tab__row--${this.rowKind(row)}`\n\t\t},\n\n\t\trowUrl(row) {\n\t\t\tif (row.url && safeHref(row.url) !== '#') {\n\t\t\t\treturn row.url\n\t\t\t}\n\t\t\tconst kind = this.rowKind(row)\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (kind === 'client') {\n\t\t\t\tconst clientId = row.clientId ?? id\n\t\t\t\tif (clientId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/clients/${clientId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'task') {\n\t\t\t\tconst taskId = row.taskId ?? id\n\t\t\t\tif (taskId) {\n\t\t\t\t\treturn `/index.php/apps/timemanager/tasks/${taskId}`\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (kind === 'time' && id) {\n\t\t\t\treturn `/index.php/apps/timemanager/times/${id}`\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\treturn `/index.php/apps/timemanager/projects/${id}`\n\t\t\t}\n\t\t\treturn this.timeTrackerAppUrl\n\t\t},\n\n\t\t/**\n\t\t * Total seconds carried on the row, normalised across the kind\n\t\t * variants. Tasks/time entries usually carry `duration` as\n\t\t * seconds (or minutes when explicitly suffixed).\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Seconds total or null if absent / unparseable.\n\t\t */\n\t\tdurationSeconds(row) {\n\t\t\tconst raw = row.duration ?? row.seconds ?? row.data?.duration ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\treturn Number.isNaN(num) ? null : num\n\t\t},\n\n\t\t/**\n\t\t * Format a seconds total as a compact \"Xh Ym\" / \"Ym\" duration.\n\t\t *\n\t\t * @param {number} seconds Total seconds.\n\t\t * @return {string} Formatted duration.\n\t\t */\n\t\tformatDuration(seconds) {\n\t\t\tconst totalMinutes = Math.round(seconds / 60)\n\t\t\tconst hours = Math.floor(totalMinutes / 60)\n\t\t\tconst minutes = totalMinutes % 60\n\t\t\tif (hours > 0) {\n\t\t\t\treturn `${hours}h ${minutes}m`\n\t\t\t}\n\t\t\treturn `${minutes}m`\n\t\t},\n\n\t\tdurationLabel(row) {\n\t\t\tconst total = this.durationSeconds(row)\n\t\t\tif (total === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.formatDuration(total)\n\t\t},\n\n\t\ttaskCountLabel(row) {\n\t\t\tif (this.rowKind(row) !== 'client') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = row.tasks ?? row.taskCount ?? row.data?.task_count ?? null\n\t\t\tif (raw === null || raw === undefined) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst num = Number(raw)\n\t\t\tif (Number.isNaN(num)) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} tasks', { count: num })\n\t\t},\n\n\t\t/**\n\t\t * Start timestamp in milliseconds for the row, suitable for\n\t\t * `NcDateTime`. Only time entries carry a start; returns null when\n\t\t * absent or unparseable.\n\t\t *\n\t\t * @param {object} row Provider row.\n\t\t * @return {number|null} Epoch milliseconds or null.\n\t\t */\n\t\tstartedAtMs(row) {\n\t\t\tif (this.rowKind(row) !== 'time') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = row.startedAt ?? row.started_at ?? row.start ?? row.data?.start ?? null\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst ms = typeof raw === 'number' ? raw * 1000 : Date.parse(raw)\n\t\t\tif (Number.isNaN(ms)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ms\n\t\t},\n\n\t\tisBillable(row) {\n\t\t\treturn row.billable === true || row.billable === 1 || row.billable === '1'\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.baseUrl()), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This entry is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/new`), {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { ...buildHeaders(), 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\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('[CnTimeTrackerTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create client.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkRow(row) {\n\t\t\tconst id = this.rowKey(row)\n\t\t\tif (!id) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(id)}`), {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t})\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tawait this.fetchRows()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnTimeTrackerTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink entry.')\n\t\t\t}\n\t\t},\n\n\t\tasync fetchRows() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.rows = rows\n\t\t\t\t} else if (response.status === 503 || response.status === 501) {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.rows = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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('[CnTimeTrackerTab] failed to fetch rows', err)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load tracked time.')\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-time-tracker-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 10px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-time-tracker-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-time-tracker-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-time-tracker-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-time-tracker-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tmargin-bottom: 8px;\n\tborder-radius: var(--border-radius-large, var(--border-radius));\n\tbackground: var(--color-background-hover);\n}\n\n.cn-time-tracker-tab__summary-label {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__summary-value {\n\tfont-weight: 700;\n\tfont-size: 1.15em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-time-tracker-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-time-tracker-tab__row-icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 40px;\n\theight: 40px;\n\tborder-radius: 50%;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--client {\n\tbackground: var(--color-primary-element-light, var(--color-background-dark));\n\tcolor: var(--color-primary-element, #21468B);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__row-icon.cn-time-tracker-tab__row--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__subline {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tflex-wrap: wrap;\n}\n\n.cn-time-tracker-tab__date,\n.cn-time-tracker-tab__task-count {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-time-tracker-tab__kind-chip {\n\tdisplay: inline-block;\n\tpadding: 1px 6px;\n\tfont-size: 0.7em;\n\tfont-weight: 600;\n\tborder-radius: 8px;\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-main-text);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__kind-chip--client {\n\tbackground: var(--color-primary-element, #21468B);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--task {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__kind-chip--time {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-time-tracker-tab__duration {\n\tfont-weight: 700;\n\tfont-size: 1.05em;\n\tcolor: var(--color-main-text);\n\tfont-variant-numeric: tabular-nums;\n\twhite-space: nowrap;\n}\n\n.cn-time-tracker-tab__billable {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 1px 6px;\n\tborder-radius: 8px;\n\tfont-size: 0.72em;\n\tfont-weight: 600;\n\tcolor: var(--color-main-background);\n\tbackground: var(--color-success, #46ba61);\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_toDisplayString","_createBlock","_normalizeClass"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { translate, translatePlural } from '@nextcloud/l10n';
2
2
  import { NcLoadingIcon } from '@nextcloud/vue';
3
3
  import FileDocumentMultiple from 'vue-material-design-icons/FileDocumentMultiple.vue';
4
4
  import __sfc_main from '../../../components/CnDetailCard/CnDetailCard.vue.js';
5
+ import { safeHref } from '../../../utils/safeHref.js';
5
6
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
6
7
 
7
8
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
@@ -167,8 +168,10 @@ var script = {
167
168
  return String(page.title ?? page.page ?? page.reference ?? this.pageKey(page))
168
169
  },
169
170
 
171
+ // An unsafe scheme is treated like a missing URL.
170
172
  pageUrl(page) {
171
- return page.url ?? ''
173
+ const url = page.url ?? '';
174
+ return url && safeHref(url) !== '#' ? url : ''
172
175
  },
173
176
 
174
177
  breadcrumbLabel(page) {
@@ -1 +1 @@
1
- {"version":3,"file":"CnXwikiCard.vue2.js","sources":["../../../../../src/integrations/builtin/xwiki/CnXwikiCard.vue"],"sourcesContent":["<!--\n CnXwikiCard — bespoke surface-aware widget for the `xwiki` integration.\n\n Replaces the generic CnIntegrationCard for the `xwiki` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard: headline count of linked XWiki\n pages + most-recent linked page title + breadcrumb + an auth-\n status badge (configured / not configured / unhealthy).\n - detail-page: linked-pages list with macro-inert text preview of\n the first page (HTML stripped to plain text, `<script>` body\n removed, ~500-char truncation — AD-1) + \"Open in XWiki\" link.\n - single-entity: title + breadcrumb chip resolved from the `value`\n prop (for `referenceType: 'xwiki'` properties); falls back to a\n minimal chip with just the `value` when the lookup fails.\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnXwikiTab; for `single-entity` the optional `value` prop addresses a\n single page by canonical reference (matching the underlying provider\n `get()` contract).\n\n External-fetch latency is normal for XWiki because the OR provider\n hops through Integriq → external HTTP. The card shows a loading\n spinner for the whole round-trip; failure modes (503 +\n `details.cause` per AD-23) collapse to a compact `authStatus` badge\n on the dashboard surfaces instead of a fat banner.\n\n Security note: the text preview NEVER injects HTML into the DOM. The\n `<script>`/`<style>` bodies are stripped first, then all tags are\n removed, and finally the result is bound through `{{ }}` (text\n interpolation) — macros remain inert text.\n\n See `openregister/openspec/changes/integration-xwiki/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback),\n AD-1 (text-only preview).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :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\n\t\t\t\tv-if=\"entity\"\n\t\t\t\tclass=\"cn-xwiki-card__chip\"\n\t\t\t\t:title=\"chipSubtitle(entity)\">\n\t\t\t\t<FileDocumentMultiple :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pageUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pageTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"breadcrumbLabel(entity)\" class=\"cn-xwiki-card__chip-breadcrumb\">\n\t\t\t\t\t· {{ breadcrumbLabel(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else-if=\"value\" class=\"cn-xwiki-card__chip cn-xwiki-card__chip--fallback\">\n\t\t\t\t<FileDocumentMultiple :size=\"14\" />\n\t\t\t\t<span>{{ value }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-xwiki-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: count + most-recent + auth status badge -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div class=\"cn-xwiki-card__dashboard\">\n\t\t\t\t<div class=\"cn-xwiki-card__headline-row\">\n\t\t\t\t\t<strong v-if=\"!degraded\" class=\"cn-xwiki-card__count\">{{ countHeadline }}</strong>\n\t\t\t\t\t<span v-else class=\"cn-xwiki-card__count cn-xwiki-card__count--muted\">\n\t\t\t\t\t\t{{ degradedHeadline }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-xwiki-card__auth-badge\"\n\t\t\t\t\t\t:class=\"`cn-xwiki-card__auth-badge--${authBadge.kind}`\"\n\t\t\t\t\t\t:title=\"authBadge.title\">\n\t\t\t\t\t\t{{ authBadge.label }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"!degraded && mostRecent\" class=\"cn-xwiki-card__recent\">\n\t\t\t\t\t<FileDocumentMultiple :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pageUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pageTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"breadcrumbLabel(mostRecent)\" class=\"cn-xwiki-card__recent-breadcrumb\">\n\t\t\t\t\t\t· {{ breadcrumbLabel(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-else-if=\"!degraded && pages.length === 0\" class=\"cn-xwiki-card__empty\">\n\t\t\t\t\t{{ emptyLabel }}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: list + text preview of first page -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-xwiki-card__banner\" :class=\"`cn-xwiki-card__banner--${bannerKind}`\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"pages.length === 0\" class=\"cn-xwiki-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-xwiki-card__detail\">\n\t\t\t\t<ul class=\"cn-xwiki-card__list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"page in pages\"\n\t\t\t\t\t\t:key=\"pageKey(page)\"\n\t\t\t\t\t\tclass=\"cn-xwiki-card__row\">\n\t\t\t\t\t\t<div class=\"cn-xwiki-card__row-header\">\n\t\t\t\t\t\t\t<FileDocumentMultiple :size=\"16\" class=\"cn-xwiki-card__row-icon\" />\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"pageUrl(page)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-xwiki-card__title\">{{ pageTitle(page) }}</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"breadcrumbLabel(page)\" class=\"cn-xwiki-card__breadcrumb\">\n\t\t\t\t\t\t\t{{ breadcrumbLabel(page) }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"previewText\" class=\"cn-xwiki-card__preview\">\n\t\t\t\t\t<p>{{ previewText }}</p>\n\t\t\t\t\t<a\n\t\t\t\t\t\tv-if=\"firstPageUrl\"\n\t\t\t\t\t\t:href=\"firstPageUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-xwiki-card__open-link\">\n\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport FileDocumentMultiple from 'vue-material-design-icons/FileDocumentMultiple.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']\nconst PREVIEW_MAX_CHARS = 500\n\n/**\n * CnXwikiCard — bespoke surface-aware widget for the `xwiki` integration.\n *\n * Renders XWiki-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnXwikiCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, FileDocumentMultiple },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'xwiki'`). */\n\t\tintegrationId: { type: String, default: 'xwiki' },\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 (page canonical reference). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Articles') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => FileDocumentMultiple },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No XWiki pages linked yet') },\n\t\t/** Pre-translated \"Open in XWiki\" label. */\n\t\topenLabel: { type: String, default: () => t('nextcloud-vue', 'Open in XWiki') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpages: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t\t/** One of: 'none' | 'unconfigured' | 'auth' | 'upstream' | 'error'. */\n\t\t\tbannerKind: 'none',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\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\tmostRecent() {\n\t\t\tif (this.pages.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.pages].sort((a, b) => this.modifiedMs(b) - this.modifiedMs(a))\n\t\t\treturn sorted[0]\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.pages.length\n\t\t\treturn n('nextcloud-vue', '{count} linked page', '{count} linked pages', total, { count: total })\n\t\t},\n\n\t\tdegradedHeadline() {\n\t\t\tif (this.bannerKind === 'unconfigured') {\n\t\t\t\treturn t('nextcloud-vue', 'Not configured')\n\t\t\t}\n\t\t\tif (this.bannerKind === 'auth') {\n\t\t\t\treturn t('nextcloud-vue', 'Auth failed')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Unavailable')\n\t\t},\n\n\t\tauthBadge() {\n\t\t\tif (this.bannerKind === 'unconfigured') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: 'missing',\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Not configured'),\n\t\t\t\t\ttitle: t('nextcloud-vue', 'Add an XWiki source in Integriq to enable this integration.'),\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.bannerKind === 'auth') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: 'unhealthy',\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Auth failed'),\n\t\t\t\t\ttitle: t('nextcloud-vue', 'XWiki returned 401 — check the Integriq source credentials.'),\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.bannerKind === 'upstream' || this.bannerKind === 'error') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: 'unhealthy',\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Unavailable'),\n\t\t\t\t\ttitle: t('nextcloud-vue', 'The upstream XWiki host did not respond.'),\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: 'configured',\n\t\t\t\tlabel: t('nextcloud-vue', 'Configured'),\n\t\t\t\ttitle: t('nextcloud-vue', 'Integriq source is configured.'),\n\t\t\t}\n\t\t},\n\n\t\tfirstPageUrl() {\n\t\t\tif (this.pages.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.pageUrl(this.pages[0])\n\t\t},\n\n\t\tpreviewText() {\n\t\t\tif (this.surface !== 'detail-page' || this.pages.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = this.pages[0].content ?? this.pages[0].renderedContent ?? ''\n\t\t\treturn this.toSafePreviewText(String(raw))\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\tpageKey(page) {\n\t\t\treturn page.id ?? page.reference ?? ''\n\t\t},\n\n\t\tpageTitle(page) {\n\t\t\treturn String(page.title ?? page.page ?? page.reference ?? this.pageKey(page))\n\t\t},\n\n\t\tpageUrl(page) {\n\t\t\treturn page.url ?? ''\n\t\t},\n\n\t\tbreadcrumbLabel(page) {\n\t\t\tconst crumb = page.breadcrumb\n\t\t\tif (Array.isArray(crumb) === false || crumb.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst ancestors = crumb.slice(0, -1)\n\t\t\tif (ancestors.length === 0) {\n\t\t\t\treturn String(page.space ?? '')\n\t\t\t}\n\t\t\t// XWiki renders space paths with a \"›\" chevron separator.\n\t\t\treturn ancestors.map((c) => String(c)).filter((s) => s !== '').join(' › ')\n\t\t},\n\n\t\tchipSubtitle(page) {\n\t\t\treturn this.breadcrumbLabel(page) || this.pageTitle(page)\n\t\t},\n\n\t\tmodifiedMs(page) {\n\t\t\tconst v = page.modified ?? page.lastModified ?? page.updated ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\treturn v < 1e12 ? v * 1000 : v\n\t\t\t}\n\t\t\tconst parsed = Date.parse(String(v))\n\t\t\treturn Number.isNaN(parsed) === true ? 0 : parsed\n\t\t},\n\n\t\t/**\n\t\t * Reduce a (possibly rendered-HTML) string to safe inert text:\n\t\t * 1. Strip `<script>` and `<style>` blocks entirely (incl. body).\n\t\t * 2. Strip all remaining HTML tags.\n\t\t * 3. Collapse whitespace.\n\t\t * 4. Truncate to PREVIEW_MAX_CHARS.\n\t\t *\n\t\t * Macro markup (e.g. `{{velocity}}…{{/velocity}}`) is preserved as\n\t\t * inert text — it is never executed because the result is bound\n\t\t * via `{{ }}` text interpolation in the template, not v-html.\n\t\t *\n\t\t * @param {string} raw Raw rendered-HTML body or plain text.\n\t\t *\n\t\t * @return {string} Safe preview text (≤ PREVIEW_MAX_CHARS).\n\t\t */\n\t\ttoSafePreviewText(raw) {\n\t\t\tif (raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet text = raw\n\t\t\t\t.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n\t\t\t\t.replace(/<style\\b[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n\t\t\t\t.replace(/<[^>]+>/g, ' ')\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t.trim()\n\t\t\tif (text.length > PREVIEW_MAX_CHARS) {\n\t\t\t\ttext = text.slice(0, PREVIEW_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Map a 503 cause (`details.cause` per AD-23) to a banner kind.\n\t\t *\n\t\t * @param {string} cause Cause string from the controller envelope.\n\t\t *\n\t\t * @return {string} Banner kind (`unconfigured` / `auth` / `upstream`).\n\t\t */\n\t\tbannerFromCause(cause) {\n\t\t\tif (cause === 'openconnector-down' || cause === 'openconnector-source-missing') {\n\t\t\t\treturn 'unconfigured'\n\t\t\t}\n\t\t\tif (cause === 'provider-auth') {\n\t\t\t\treturn 'auth'\n\t\t\t}\n\t\t\treturn 'upstream'\n\t\t},\n\n\t\tapplyDegradedFromCause(cause) {\n\t\t\tthis.bannerKind = this.bannerFromCause(cause)\n\t\t\tif (this.bannerKind === 'unconfigured') {\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'XWiki connection not configured — add a source in Integriq.')\n\t\t\t} else if (this.bannerKind === 'auth') {\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'XWiki returned 401 — check the Integriq source credentials.')\n\t\t\t} else {\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'XWiki is currently unavailable.')\n\t\t\t}\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.bannerKind = 'none'\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.pages = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.pages = []\n\t\t\t\t\tlet cause = ''\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst body = await response.json()\n\t\t\t\t\t\tcause = String(body?.details?.cause ?? '')\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tcause = ''\n\t\t\t\t\t}\n\t\t\t\t\tthis.applyDegradedFromCause(cause)\n\t\t\t\t} else {\n\t\t\t\t\tthis.pages = []\n\t\t\t\t\tthis.bannerKind = 'error'\n\t\t\t\t\tthis.degraded = t('nextcloud-vue', 'Could not load XWiki pages.')\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('[CnXwikiCard] failed to fetch XWiki pages', err)\n\t\t\t\tthis.pages = []\n\t\t\t\tthis.bannerKind = 'error'\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'Could not load XWiki pages.')\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.bannerKind = 'none'\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 {\n\t\t\t\t\t// On any non-OK (incl. 503) we keep the chip-fallback path\n\t\t\t\t\t// (renders the raw `value` as a minimal chip). The dashboard\n\t\t\t\t\t// surfaces show the badge; single-entity is intentionally\n\t\t\t\t\t// quieter — it's an inline chip in a form/table.\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tif (response.status === 503) {\n\t\t\t\t\t\tlet cause = ''\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst body = await response.json()\n\t\t\t\t\t\t\tcause = String(body?.details?.cause ?? '')\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tcause = ''\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.bannerKind = this.bannerFromCause(cause)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.bannerKind = 'error'\n\t\t\t\t\t}\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('[CnXwikiCard] failed to fetch single XWiki page', err)\n\t\t\t\tthis.entity = null\n\t\t\t\tthis.bannerKind = 'error'\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-xwiki-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-xwiki-card__dashboard {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-xwiki-card__headline-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-xwiki-card__count {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-xwiki-card__count--muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-weight: normal;\n}\n\n.cn-xwiki-card__auth-badge {\n\tflex-shrink: 0;\n\tfont-size: 0.75em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\twhite-space: nowrap;\n}\n\n.cn-xwiki-card__auth-badge--configured {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__auth-badge--missing {\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tborder: 1px solid var(--color-border);\n}\n\n.cn-xwiki-card__auth-badge--unhealthy {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__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-xwiki-card__recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-xwiki-card__recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-card__recent-breadcrumb {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-xwiki-card__banner {\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tfont-size: 0.9em;\n\tmargin-bottom: 8px;\n}\n\n.cn-xwiki-card__banner--unconfigured {\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tborder-left: 3px solid var(--color-primary-element, #21468B);\n}\n\n.cn-xwiki-card__banner--auth,\n.cn-xwiki-card__banner--error {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__banner--upstream {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__list {\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-xwiki-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-xwiki-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-xwiki-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-xwiki-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n}\n\n.cn-xwiki-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-xwiki-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-card__breadcrumb {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n\n.cn-xwiki-card__preview {\n\tpadding-top: 8px;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 0.9em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-xwiki-card__preview p {\n\tmargin: 0 0 6px;\n}\n\n.cn-xwiki-card__open-link {\n\tcolor: var(--color-primary-element, #21468B);\n\ttext-decoration: none;\n\tfont-weight: 500;\n}\n\n.cn-xwiki-card__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-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-xwiki-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-xwiki-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-card__chip-breadcrumb {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-xwiki-card__chip--fallback {\n\topacity: 0.85;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":";;;;;;AA8IA,MAAM,cAAe,GAAE,CAAC,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAA;AACzF,MAAM,iBAAgB,GAAI,IAAE;;AAE5B;AACC;AACA;;;;AAID,aAAe;AACd,CAAA,IAAI,EAAE,aAAa;;CAEnB,UAAU,EAAE,gBAAEA,UAAY,EAAE,aAAa,EAAE,oBAAmB,EAAG;;AAEjE,CAAA,KAAK,EAAE;AACN;AACA,EAAA,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS;AACjD;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AAC1C;AACA,EAAA,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;AACxC;AACA,EAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;AAE1C,EAAA,OAAO,EAAE;GACR,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,aAAa;AACtB,GAAA,SAAS,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;GAC5C;;;AAGD,EAAA,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAC,EAAG;;EAEpC,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,UAAU,CAAA,EAAG;AACtE;EACA,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,oBAAmB,EAAG;AAC3D;EACA,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAuB,EAAG;AAC5D;AACA,EAAA,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;AAC7C;AACA,EAAA,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,2BAA2B,GAAG;AAC5F;AACA,EAAA,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAMA,SAAC,CAAC,eAAe,EAAE,eAAe,GAAG;EAC/E;;AAED,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;AACN,GAAA,KAAK,EAAE,EAAE;GACT,MAAM,EAAE,IAAI;GACZ,OAAO,EAAE,KAAK;GACd,QAAQ,EAAE,EAAE;;AAEZ,GAAA,UAAU,EAAE,MAAM;AACnB,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;EACT,SAAS,GAAG;AACX,GAAA,OAAO,IAAI,CAAC,KAAM,IAAG,IAAI,CAAC,aAAY;GACtC;;EAED,QAAQ,GAAG;AACV,GAAA,OAAO,IAAI,CAAC,IAAG;GACf;;EAED,UAAU,GAAG;GACZ,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE;AAC5B,IAAA,OAAO,IAAG;AACX,IAAA;AACA,GAAA,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC,CAAA,GAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAA;AACrF,GAAA,OAAO,MAAM,CAAC,CAAC,CAAA;GACf;;AAED,EAAA,aAAa,GAAG;GACf,MAAM,KAAM,GAAE,IAAI,CAAC,KAAK,CAAC,OAAK;AAC9B,GAAA,OAAOC,eAAC,CAAC,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,CAAA;GAChG;;AAED,EAAA,gBAAgB,GAAG;AAClB,GAAA,IAAI,IAAI,CAAC,UAAS,KAAM,cAAc,EAAE;AACvC,IAAA,OAAOD,SAAC,CAAC,eAAe,EAAE,gBAAgB,CAAA;AAC3C,IAAA;GACA,IAAI,IAAI,CAAC,UAAW,KAAI,MAAM,EAAE;AAC/B,IAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,aAAa,CAAA;AACxC,IAAA;AACA,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,aAAa,CAAA;GACvC;;EAED,SAAS,GAAG;AACX,GAAA,IAAI,IAAI,CAAC,UAAS,KAAM,cAAc,EAAE;AACvC,IAAA,OAAO;AACN,KAAA,IAAI,EAAE,SAAS;AACf,KAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,gBAAgB,CAAC;AAC3C,KAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,6DAA6D,CAAC;AACzF,KAAA;AACD,IAAA;GACA,IAAI,IAAI,CAAC,UAAW,KAAI,MAAM,EAAE;AAC/B,IAAA,OAAO;AACN,KAAA,IAAI,EAAE,WAAW;AACjB,KAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,aAAa,CAAC;AACxC,KAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,6DAA6D,CAAC;AACzF,KAAA;AACD,IAAA;GACA,IAAI,IAAI,CAAC,UAAW,KAAI,UAAW,IAAG,IAAI,CAAC,UAAS,KAAM,OAAO,EAAE;AAClE,IAAA,OAAO;AACN,KAAA,IAAI,EAAE,WAAW;AACjB,KAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,aAAa,CAAC;KACxC,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,0CAA0C,CAAC;AACtE,KAAA;AACD,IAAA;AACA,GAAA,OAAO;AACN,IAAA,IAAI,EAAE,YAAY;AAClB,IAAA,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,YAAY,CAAC;IACvC,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,gCAAgC,CAAC;AAC5D,IAAA;GACA;;AAED,EAAA,YAAY,GAAG;GACd,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE;AAC5B,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;GACjC;;EAED,WAAW,GAAG;GACb,IAAI,IAAI,CAAC,OAAQ,KAAI,aAAc,IAAG,IAAI,CAAC,KAAK,CAAC,MAAK,KAAM,CAAC,EAAE;AAC9D,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,GAAI,GAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,eAAgB,IAAG,GAAC;AACvE,GAAA,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;GACzC;EACD;;AAED,CAAA,KAAK,EAAE;AACN,EAAA,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG;GACtC,IAAI,CAAC,KAAK,GAAC;AACZ,GAAA,EAAG;;AAEH,EAAA,OAAO,GAAG;GACT,IAAI,CAAC,KAAK,GAAC;GACX;;AAED,EAAA,KAAK,GAAG;AACP,GAAA,IAAI,IAAI,CAAC,YAAY,eAAe,EAAE;AACrC,IAAA,IAAI,CAAC,WAAW,GAAC;AAClB,IAAA;GACA;EACD;;AAED,CAAA,OAAO,EAAE;AACR,EAAA,OAAO,GAAG;GACT,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;GACnH;;EAED,OAAO,CAAC,IAAI,EAAE;AACb,GAAA,OAAO,IAAI,CAAC,EAAC,IAAK,IAAI,CAAC,SAAU,IAAG,EAAC;GACrC;;AAED,EAAA,SAAS,CAAC,IAAI,EAAE;AACf,GAAA,OAAO,MAAM,CAAC,IAAI,CAAC,KAAI,IAAK,IAAI,CAAC,IAAK,IAAG,IAAI,CAAC,SAAQ,IAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;GAC7E;;EAED,OAAO,CAAC,IAAI,EAAE;AACb,GAAA,OAAO,IAAI,CAAC,GAAI,IAAG,EAAC;GACpB;;AAED,EAAA,eAAe,CAAC,IAAI,EAAE;GACrB,MAAM,KAAI,GAAI,IAAI,CAAC,WAAS;GAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAA,KAAM,KAAI,IAAK,KAAK,CAAC,WAAW,CAAC,EAAE;AACzD,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,MAAM,SAAU,GAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAA;GACnC,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC,KAAM,IAAG,EAAE,CAAA;AAC/B,IAAA;;AAEA,GAAA,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAE,KAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAA;GACzE;;AAED,EAAA,YAAY,CAAC,IAAI,EAAE;GAClB,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAE,IAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAA;GACxD;;AAED,EAAA,UAAU,CAAC,IAAI,EAAE;GAChB,MAAM,CAAA,GAAI,IAAI,CAAC,QAAS,IAAG,IAAI,CAAC,YAAa,IAAG,IAAI,CAAC,OAAM,IAAK,KAAG;AACnE,GAAA,IAAI,CAAE,KAAI,IAAK,IAAG,CAAA,KAAM,SAAQ,IAAK,MAAM,EAAE,EAAE;AAC9C,IAAA,OAAO,CAAA;AACR,IAAA;GACA,IAAI,OAAO,CAAE,KAAI,QAAQ,EAAE;AAC1B,IAAA,OAAO,CAAA,GAAI,IAAG,GAAI,CAAE,GAAE,OAAO,CAAA;AAC9B,IAAA;AACA,GAAA,MAAM,MAAK,GAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAA;AACnC,GAAA,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,MAAM,IAAG,GAAI,CAAE,GAAE,MAAK;GAChD;;AAED;;;;AAIC;AACA;AACA;;;;AAIA;;AAEA;;;EAGD,iBAAiB,CAAC,GAAG,EAAE;GACtB,IAAI,GAAE,KAAM,EAAE,EAAE;AACf,IAAA,OAAO,EAAC;AACT,IAAA;GACA,IAAI,IAAK,GAAE,GAAE;KACX,OAAO,CAAC,qCAAqC,EAAE,EAAE,CAAA;KACjD,OAAO,CAAC,mCAAmC,EAAE,EAAE,CAAA;KAC/C,OAAO,CAAC,UAAU,EAAE,GAAG,CAAA;AACxB,KAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAA;AACpB,KAAC,IAAI,GAAC;AACP,GAAA,IAAI,IAAI,CAAC,SAAS,iBAAiB,EAAE;IACpC,IAAK,GAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,OAAO,EAAC,GAAI,IAAE;AACvD,IAAA;AACA,GAAA,OAAO,IAAG;GACV;;AAED;;AAEC;;AAEA;;;EAGD,eAAe,CAAC,KAAK,EAAE;AACtB,GAAA,IAAI,KAAI,KAAM,oBAAmB,IAAK,KAAI,KAAM,8BAA8B,EAAE;AAC/E,IAAA,OAAO,cAAa;AACrB,IAAA;GACA,IAAI,KAAM,KAAI,eAAe,EAAE;IAC9B,OAAO,MAAK;AACb,IAAA;AACA,GAAA,OAAO,UAAS;GAChB;;EAED,sBAAsB,CAAC,KAAK,EAAE;AAC7B,GAAA,IAAI,CAAC,UAAS,GAAI,IAAI,CAAC,eAAe,CAAC,KAAK,EAAA;AAC5C,GAAA,IAAI,IAAI,CAAC,UAAS,KAAM,cAAc,EAAE;AACvC,IAAA,IAAI,CAAC,QAAS,GAAEA,SAAC,CAAC,eAAe,EAAE,6DAA6D,EAAA;AACjG,IAAA,MAAO,IAAI,IAAI,CAAC,eAAe,MAAM,EAAE;AACtC,IAAA,IAAI,CAAC,QAAS,GAAEA,SAAC,CAAC,eAAe,EAAE,6DAA6D,EAAA;IAC/F,MAAK;AACN,IAAA,IAAI,CAAC,QAAO,GAAIA,SAAC,CAAC,eAAe,EAAE,iCAAiC,EAAA;AACrE,IAAA;GACA;;AAED,EAAA,KAAK,GAAG;AACP,GAAA,IAAI,IAAI,CAAC,YAAY,eAAe,EAAE;AACrC,IAAA,IAAI,CAAC,WAAW,GAAC;IACjB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,SAAS,GAAC;GACf;;AAED,EAAA,MAAM,SAAS,GAAG;GACjB,IAAI,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE;IACrD,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;AAClB,GAAA,IAAI,CAAC,QAAO,GAAI,GAAC;AACjB,GAAA,IAAI,CAAC,UAAW,GAAE,OAAK;AACvB,GAAA,IAAI;AACH,IAAA,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,IAAI,EAAA;AACnF,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;KAChB,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,KAAA,IAAI,CAAC,KAAM,GAAE,IAAI,CAAC,WAAW,IAAI,CAAC,KAAM,KAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAE,GAAE,IAAK,GAAE,EAAE,CAAE,IAAG,GAAC;AAClF,KAAA,MAAO,IAAI,QAAQ,CAAC,MAAK,KAAM,GAAG,EAAE;KACnC,IAAI,CAAC,KAAM,GAAE,GAAC;KACd,IAAI,KAAI,GAAI,GAAC;AACb,KAAA,IAAI;MACH,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,MAAA,KAAI,GAAI,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAI,IAAK,EAAE,EAAA;AAC1C,MAAA,CAAE,MAAM;AACP,MAAA,KAAI,GAAI,GAAC;AACV,MAAA;AACA,KAAA,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAA;KAChC,MAAK;KACN,IAAI,CAAC,KAAM,GAAE,GAAC;KACd,IAAI,CAAC,aAAa,QAAM;KACxB,IAAI,CAAC,QAAO,GAAIA,SAAC,CAAC,eAAe,EAAE,6BAA6B,EAAA;AACjE,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,GAAG,EAAA;IAC9D,IAAI,CAAC,KAAM,GAAE,GAAC;IACd,IAAI,CAAC,aAAa,QAAM;IACxB,IAAI,CAAC,QAAO,GAAIA,SAAC,CAAC,eAAe,EAAE,6BAA6B,EAAA;AACjE,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;;AAED,EAAA,MAAM,WAAW,GAAG;GACnB,IAAI,CAAC,IAAI,CAAC,KAAI,IAAK,CAAC,IAAI,CAAC,QAAO,IAAK,CAAC,IAAI,CAAC,MAAK,IAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;AACpE,IAAA,IAAI,CAAC,MAAK,GAAI,KAAG;IACjB,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,OAAM,GAAI,KAAG;AAClB,GAAA,IAAI,CAAC,QAAO,GAAI,GAAC;AACjB,GAAA,IAAI,CAAC,UAAW,GAAE,OAAK;AACvB,GAAA,IAAI;IACH,MAAM,QAAO,GAAI,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,EAAC,EAAG,EAAA;AAC1H,IAAA,IAAI,QAAQ,CAAC,EAAE,EAAE;AAChB,KAAA,IAAI,CAAC,MAAK,GAAI,MAAM,QAAQ,CAAC,IAAI,GAAC;KACjC,MAAK;;;;AAIN;AACA,KAAA,IAAI,CAAC,MAAK,GAAI,KAAG;KACjB,IAAI,QAAQ,CAAC,MAAO,KAAI,GAAG,EAAE;MAC5B,IAAI,KAAI,GAAI,GAAC;AACb,MAAA,IAAI;OACH,MAAM,IAAK,GAAE,MAAM,QAAQ,CAAC,IAAI,GAAC;AACjC,OAAA,KAAI,GAAI,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAI,IAAK,EAAE,EAAA;AAC1C,OAAA,CAAE,MAAM;AACP,OAAA,KAAI,GAAI,GAAC;AACV,OAAA;AACA,MAAA,IAAI,CAAC,UAAS,GAAI,IAAI,CAAC,eAAe,CAAC,KAAK,EAAA;MAC3C,MAAK;MACN,IAAI,CAAC,aAAa,QAAM;AACzB,MAAA;AACD,KAAA;IACC,CAAA,OAAO,GAAG,EAAE;AACb;IACA,OAAO,CAAC,KAAK,CAAC,iDAAiD,EAAE,GAAG,EAAA;AACpE,IAAA,IAAI,CAAC,MAAK,GAAI,KAAG;IACjB,IAAI,CAAC,aAAa,QAAM;AACzB,IAAA,SAAU;AACT,IAAA,IAAI,CAAC,OAAQ,GAAE,MAAI;AACpB,IAAA;GACA;EACD;AACF;;;;"}
1
+ {"version":3,"file":"CnXwikiCard.vue2.js","sources":["../../../../../src/integrations/builtin/xwiki/CnXwikiCard.vue"],"sourcesContent":["<!--\n CnXwikiCard — bespoke surface-aware widget for the `xwiki` integration.\n\n Replaces the generic CnIntegrationCard for the `xwiki` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard: headline count of linked XWiki\n pages + most-recent linked page title + breadcrumb + an auth-\n status badge (configured / not configured / unhealthy).\n - detail-page: linked-pages list with macro-inert text preview of\n the first page (HTML stripped to plain text, `<script>` body\n removed, ~500-char truncation — AD-1) + \"Open in XWiki\" link.\n - single-entity: title + breadcrumb chip resolved from the `value`\n prop (for `referenceType: 'xwiki'` properties); falls back to a\n minimal chip with just the `value` when the lookup fails.\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnXwikiTab; for `single-entity` the optional `value` prop addresses a\n single page by canonical reference (matching the underlying provider\n `get()` contract).\n\n External-fetch latency is normal for XWiki because the OR provider\n hops through Integriq → external HTTP. The card shows a loading\n spinner for the whole round-trip; failure modes (503 +\n `details.cause` per AD-23) collapse to a compact `authStatus` badge\n on the dashboard surfaces instead of a fat banner.\n\n Security note: the text preview NEVER injects HTML into the DOM. The\n `<script>`/`<style>` bodies are stripped first, then all tags are\n removed, and finally the result is bound through `{{ }}` (text\n interpolation) — macros remain inert text.\n\n See `openregister/openspec/changes/integration-xwiki/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback),\n AD-1 (text-only preview).\n-->\n<template>\n\t<CnDetailCard :title=\"cardTitle\" :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\n\t\t\t\tv-if=\"entity\"\n\t\t\t\tclass=\"cn-xwiki-card__chip\"\n\t\t\t\t:title=\"chipSubtitle(entity)\">\n\t\t\t\t<FileDocumentMultiple :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pageUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pageTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"breadcrumbLabel(entity)\" class=\"cn-xwiki-card__chip-breadcrumb\">\n\t\t\t\t\t· {{ breadcrumbLabel(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else-if=\"value\" class=\"cn-xwiki-card__chip cn-xwiki-card__chip--fallback\">\n\t\t\t\t<FileDocumentMultiple :size=\"14\" />\n\t\t\t\t<span>{{ value }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-xwiki-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: count + most-recent + auth status badge -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div class=\"cn-xwiki-card__dashboard\">\n\t\t\t\t<div class=\"cn-xwiki-card__headline-row\">\n\t\t\t\t\t<strong v-if=\"!degraded\" class=\"cn-xwiki-card__count\">{{ countHeadline }}</strong>\n\t\t\t\t\t<span v-else class=\"cn-xwiki-card__count cn-xwiki-card__count--muted\">\n\t\t\t\t\t\t{{ degradedHeadline }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-xwiki-card__auth-badge\"\n\t\t\t\t\t\t:class=\"`cn-xwiki-card__auth-badge--${authBadge.kind}`\"\n\t\t\t\t\t\t:title=\"authBadge.title\">\n\t\t\t\t\t\t{{ authBadge.label }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"!degraded && mostRecent\" class=\"cn-xwiki-card__recent\">\n\t\t\t\t\t<FileDocumentMultiple :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pageUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pageTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"breadcrumbLabel(mostRecent)\" class=\"cn-xwiki-card__recent-breadcrumb\">\n\t\t\t\t\t\t· {{ breadcrumbLabel(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div v-else-if=\"!degraded && pages.length === 0\" class=\"cn-xwiki-card__empty\">\n\t\t\t\t\t{{ emptyLabel }}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: list + text preview of first page -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-xwiki-card__banner\" :class=\"`cn-xwiki-card__banner--${bannerKind}`\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"pages.length === 0\" class=\"cn-xwiki-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-xwiki-card__detail\">\n\t\t\t\t<ul class=\"cn-xwiki-card__list\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"page in pages\"\n\t\t\t\t\t\t:key=\"pageKey(page)\"\n\t\t\t\t\t\tclass=\"cn-xwiki-card__row\">\n\t\t\t\t\t\t<div class=\"cn-xwiki-card__row-header\">\n\t\t\t\t\t\t\t<FileDocumentMultiple :size=\"16\" class=\"cn-xwiki-card__row-icon\" />\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t:href=\"pageUrl(page)\"\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\t\tclass=\"cn-xwiki-card__title\">{{ pageTitle(page) }}</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div v-if=\"breadcrumbLabel(page)\" class=\"cn-xwiki-card__breadcrumb\">\n\t\t\t\t\t\t\t{{ breadcrumbLabel(page) }}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"previewText\" class=\"cn-xwiki-card__preview\">\n\t\t\t\t\t<p>{{ previewText }}</p>\n\t\t\t\t\t<a\n\t\t\t\t\t\tv-if=\"firstPageUrl\"\n\t\t\t\t\t\t:href=\"firstPageUrl\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-xwiki-card__open-link\">\n\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport FileDocumentMultiple from 'vue-material-design-icons/FileDocumentMultiple.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']\nconst PREVIEW_MAX_CHARS = 500\n\n/**\n * CnXwikiCard — bespoke surface-aware widget for the `xwiki` integration.\n *\n * Renders XWiki-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnXwikiCard',\n\n\tcomponents: { CnDetailCard, NcLoadingIcon, FileDocumentMultiple },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'xwiki'`). */\n\t\tintegrationId: { type: String, default: 'xwiki' },\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 (page canonical reference). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Articles') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => FileDocumentMultiple },\n\t\t/** Base API URL. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Whether the card body is collapsible. */\n\t\tcollapsible: { type: Boolean, default: true },\n\t\t/** Pre-translated empty-state label. */\n\t\temptyLabel: { type: String, default: () => t('nextcloud-vue', 'No XWiki pages linked yet') },\n\t\t/** Pre-translated \"Open in XWiki\" label. */\n\t\topenLabel: { type: String, default: () => t('nextcloud-vue', 'Open in XWiki') },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpages: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\n\t\t\t/** One of: 'none' | 'unconfigured' | 'auth' | 'upstream' | 'error'. */\n\t\t\tbannerKind: 'none',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcardTitle() {\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\tmostRecent() {\n\t\t\tif (this.pages.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst sorted = [...this.pages].sort((a, b) => this.modifiedMs(b) - this.modifiedMs(a))\n\t\t\treturn sorted[0]\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.pages.length\n\t\t\treturn n('nextcloud-vue', '{count} linked page', '{count} linked pages', total, { count: total })\n\t\t},\n\n\t\tdegradedHeadline() {\n\t\t\tif (this.bannerKind === 'unconfigured') {\n\t\t\t\treturn t('nextcloud-vue', 'Not configured')\n\t\t\t}\n\t\t\tif (this.bannerKind === 'auth') {\n\t\t\t\treturn t('nextcloud-vue', 'Auth failed')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Unavailable')\n\t\t},\n\n\t\tauthBadge() {\n\t\t\tif (this.bannerKind === 'unconfigured') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: 'missing',\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Not configured'),\n\t\t\t\t\ttitle: t('nextcloud-vue', 'Add an XWiki source in Integriq to enable this integration.'),\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.bannerKind === 'auth') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: 'unhealthy',\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Auth failed'),\n\t\t\t\t\ttitle: t('nextcloud-vue', 'XWiki returned 401 — check the Integriq source credentials.'),\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.bannerKind === 'upstream' || this.bannerKind === 'error') {\n\t\t\t\treturn {\n\t\t\t\t\tkind: 'unhealthy',\n\t\t\t\t\tlabel: t('nextcloud-vue', 'Unavailable'),\n\t\t\t\t\ttitle: t('nextcloud-vue', 'The upstream XWiki host did not respond.'),\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: 'configured',\n\t\t\t\tlabel: t('nextcloud-vue', 'Configured'),\n\t\t\t\ttitle: t('nextcloud-vue', 'Integriq source is configured.'),\n\t\t\t}\n\t\t},\n\n\t\tfirstPageUrl() {\n\t\t\tif (this.pages.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn this.pageUrl(this.pages[0])\n\t\t},\n\n\t\tpreviewText() {\n\t\t\tif (this.surface !== 'detail-page' || this.pages.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst raw = this.pages[0].content ?? this.pages[0].renderedContent ?? ''\n\t\t\treturn this.toSafePreviewText(String(raw))\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\tpageKey(page) {\n\t\t\treturn page.id ?? page.reference ?? ''\n\t\t},\n\n\t\tpageTitle(page) {\n\t\t\treturn String(page.title ?? page.page ?? page.reference ?? this.pageKey(page))\n\t\t},\n\n\t\t// An unsafe scheme is treated like a missing URL.\n\t\tpageUrl(page) {\n\t\t\tconst url = page.url ?? ''\n\t\t\treturn url && safeHref(url) !== '#' ? url : ''\n\t\t},\n\n\t\tbreadcrumbLabel(page) {\n\t\t\tconst crumb = page.breadcrumb\n\t\t\tif (Array.isArray(crumb) === false || crumb.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst ancestors = crumb.slice(0, -1)\n\t\t\tif (ancestors.length === 0) {\n\t\t\t\treturn String(page.space ?? '')\n\t\t\t}\n\t\t\t// XWiki renders space paths with a \"›\" chevron separator.\n\t\t\treturn ancestors.map((c) => String(c)).filter((s) => s !== '').join(' › ')\n\t\t},\n\n\t\tchipSubtitle(page) {\n\t\t\treturn this.breadcrumbLabel(page) || this.pageTitle(page)\n\t\t},\n\n\t\tmodifiedMs(page) {\n\t\t\tconst v = page.modified ?? page.lastModified ?? page.updated ?? null\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\treturn v < 1e12 ? v * 1000 : v\n\t\t\t}\n\t\t\tconst parsed = Date.parse(String(v))\n\t\t\treturn Number.isNaN(parsed) === true ? 0 : parsed\n\t\t},\n\n\t\t/**\n\t\t * Reduce a (possibly rendered-HTML) string to safe inert text:\n\t\t * 1. Strip `<script>` and `<style>` blocks entirely (incl. body).\n\t\t * 2. Strip all remaining HTML tags.\n\t\t * 3. Collapse whitespace.\n\t\t * 4. Truncate to PREVIEW_MAX_CHARS.\n\t\t *\n\t\t * Macro markup (e.g. `{{velocity}}…{{/velocity}}`) is preserved as\n\t\t * inert text — it is never executed because the result is bound\n\t\t * via `{{ }}` text interpolation in the template, not v-html.\n\t\t *\n\t\t * @param {string} raw Raw rendered-HTML body or plain text.\n\t\t *\n\t\t * @return {string} Safe preview text (≤ PREVIEW_MAX_CHARS).\n\t\t */\n\t\ttoSafePreviewText(raw) {\n\t\t\tif (raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet text = raw\n\t\t\t\t.replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n\t\t\t\t.replace(/<style\\b[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n\t\t\t\t.replace(/<[^>]+>/g, ' ')\n\t\t\t\t.replace(/\\s+/g, ' ')\n\t\t\t\t.trim()\n\t\t\tif (text.length > PREVIEW_MAX_CHARS) {\n\t\t\t\ttext = text.slice(0, PREVIEW_MAX_CHARS).trimEnd() + '…'\n\t\t\t}\n\t\t\treturn text\n\t\t},\n\n\t\t/**\n\t\t * Map a 503 cause (`details.cause` per AD-23) to a banner kind.\n\t\t *\n\t\t * @param {string} cause Cause string from the controller envelope.\n\t\t *\n\t\t * @return {string} Banner kind (`unconfigured` / `auth` / `upstream`).\n\t\t */\n\t\tbannerFromCause(cause) {\n\t\t\tif (cause === 'openconnector-down' || cause === 'openconnector-source-missing') {\n\t\t\t\treturn 'unconfigured'\n\t\t\t}\n\t\t\tif (cause === 'provider-auth') {\n\t\t\t\treturn 'auth'\n\t\t\t}\n\t\t\treturn 'upstream'\n\t\t},\n\n\t\tapplyDegradedFromCause(cause) {\n\t\t\tthis.bannerKind = this.bannerFromCause(cause)\n\t\t\tif (this.bannerKind === 'unconfigured') {\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'XWiki connection not configured — add a source in Integriq.')\n\t\t\t} else if (this.bannerKind === 'auth') {\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'XWiki returned 401 — check the Integriq source credentials.')\n\t\t\t} else {\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'XWiki is currently unavailable.')\n\t\t\t}\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.bannerKind = 'none'\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.pages = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.pages = []\n\t\t\t\t\tlet cause = ''\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst body = await response.json()\n\t\t\t\t\t\tcause = String(body?.details?.cause ?? '')\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tcause = ''\n\t\t\t\t\t}\n\t\t\t\t\tthis.applyDegradedFromCause(cause)\n\t\t\t\t} else {\n\t\t\t\t\tthis.pages = []\n\t\t\t\t\tthis.bannerKind = 'error'\n\t\t\t\t\tthis.degraded = t('nextcloud-vue', 'Could not load XWiki pages.')\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('[CnXwikiCard] failed to fetch XWiki pages', err)\n\t\t\t\tthis.pages = []\n\t\t\t\tthis.bannerKind = 'error'\n\t\t\t\tthis.degraded = t('nextcloud-vue', 'Could not load XWiki pages.')\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.bannerKind = 'none'\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 {\n\t\t\t\t\t// On any non-OK (incl. 503) we keep the chip-fallback path\n\t\t\t\t\t// (renders the raw `value` as a minimal chip). The dashboard\n\t\t\t\t\t// surfaces show the badge; single-entity is intentionally\n\t\t\t\t\t// quieter — it's an inline chip in a form/table.\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tif (response.status === 503) {\n\t\t\t\t\t\tlet cause = ''\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst body = await response.json()\n\t\t\t\t\t\t\tcause = String(body?.details?.cause ?? '')\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tcause = ''\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.bannerKind = this.bannerFromCause(cause)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.bannerKind = 'error'\n\t\t\t\t\t}\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('[CnXwikiCard] failed to fetch single XWiki page', err)\n\t\t\t\tthis.entity = null\n\t\t\t\tthis.bannerKind = 'error'\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-xwiki-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-xwiki-card__dashboard {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-xwiki-card__headline-row {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px;\n}\n\n.cn-xwiki-card__count {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-xwiki-card__count--muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-weight: normal;\n}\n\n.cn-xwiki-card__auth-badge {\n\tflex-shrink: 0;\n\tfont-size: 0.75em;\n\tpadding: 2px 8px;\n\tborder-radius: 10px;\n\twhite-space: nowrap;\n}\n\n.cn-xwiki-card__auth-badge--configured {\n\tbackground: var(--color-success, #46ba61);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__auth-badge--missing {\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tborder: 1px solid var(--color-border);\n}\n\n.cn-xwiki-card__auth-badge--unhealthy {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__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-xwiki-card__recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-xwiki-card__recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-card__recent-breadcrumb {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-xwiki-card__banner {\n\tpadding: 8px 10px;\n\tborder-radius: var(--border-radius);\n\tfont-size: 0.9em;\n\tmargin-bottom: 8px;\n}\n\n.cn-xwiki-card__banner--unconfigured {\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tborder-left: 3px solid var(--color-primary-element, #21468B);\n}\n\n.cn-xwiki-card__banner--auth,\n.cn-xwiki-card__banner--error {\n\tbackground: var(--color-error, #e9322d);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__banner--upstream {\n\tbackground: var(--color-warning, #e9a40f);\n\tcolor: var(--color-main-background);\n}\n\n.cn-xwiki-card__list {\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-xwiki-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-xwiki-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-xwiki-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-xwiki-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n\tflex-shrink: 0;\n}\n\n.cn-xwiki-card__title {\n\tflex: 1;\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\na.cn-xwiki-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-card__breadcrumb {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n\n.cn-xwiki-card__preview {\n\tpadding-top: 8px;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 0.9em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-xwiki-card__preview p {\n\tmargin: 0 0 6px;\n}\n\n.cn-xwiki-card__open-link {\n\tcolor: var(--color-primary-element, #21468B);\n\ttext-decoration: none;\n\tfont-weight: 500;\n}\n\n.cn-xwiki-card__open-link:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-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-xwiki-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-xwiki-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-xwiki-card__chip-breadcrumb {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-xwiki-card__chip--fallback {\n\topacity: 0.85;\n}\n</style>\n"],"names":["CnDetailCard","t","n"],"mappings":"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"}