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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
  7. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
  10. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +21 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
  14. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  15. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  21. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  22. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  23. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  24. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  30. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  32. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  34. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  35. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  36. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  38. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  42. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  44. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  47. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  49. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  52. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  58. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  59. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  60. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  61. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  62. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  64. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  66. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  67. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  68. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  69. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  70. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  71. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  73. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  75. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  77. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  80. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  82. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  84. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  85. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  86. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  87. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  88. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  89. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  90. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  91. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  92. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  93. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  94. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  95. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  96. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
  97. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  98. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
  99. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  100. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  101. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  102. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  103. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  104. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  105. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  106. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  108. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  109. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  110. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  111. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  113. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  115. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  117. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  119. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  120. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  121. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  122. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  123. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  124. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  125. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  126. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  127. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  128. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  129. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  130. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  131. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  132. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  133. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  134. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  135. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  136. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  137. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  138. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  139. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  141. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  142. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  143. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  144. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  146. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  147. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  148. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  149. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  150. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  151. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  152. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  153. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  154. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  155. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  156. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  157. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  158. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  159. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  160. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  161. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  162. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  163. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  164. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  165. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  166. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  167. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  168. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  169. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  170. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  171. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  172. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  173. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  174. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  175. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  176. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  177. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  178. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  179. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  180. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  181. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  182. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  183. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  184. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  185. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  186. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  187. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  188. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  189. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  190. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  191. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  192. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  193. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  194. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  195. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  196. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  197. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  198. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  199. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  200. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  201. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  202. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  203. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  204. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  205. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  206. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  207. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  208. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  209. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  210. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  211. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  212. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  213. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  214. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  215. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  216. package/dist/esm/l10n/en.json.js +2 -0
  217. package/dist/esm/l10n/en.json.js.map +1 -1
  218. package/dist/esm/l10n/nl.json.js +2 -0
  219. package/dist/esm/l10n/nl.json.js.map +1 -1
  220. package/dist/esm/nextcloud-vue.css +78 -7
  221. package/dist/esm/utils/actionLink.js +5 -0
  222. package/dist/esm/utils/actionLink.js.map +1 -1
  223. package/dist/esm/utils/actionsDispatcher.js +16 -1
  224. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  225. package/dist/esm/utils/linkNavigation.js +6 -3
  226. package/dist/esm/utils/linkNavigation.js.map +1 -1
  227. package/dist/esm/utils/rowAuxClick.js +30 -5
  228. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  229. package/dist/esm/utils/safeHref.js +18 -3
  230. package/dist/esm/utils/safeHref.js.map +1 -1
  231. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  232. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  233. package/dist/nextcloud-vue.cjs.js +885 -393
  234. package/dist/nextcloud-vue.cjs.js.map +2 -2
  235. package/dist/nextcloud-vue.css +78 -7
  236. package/l10n/en.json +2 -0
  237. package/l10n/nl.json +2 -0
  238. package/package.json +1 -1
  239. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  240. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  241. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  242. package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
  243. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  244. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  245. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  246. package/src/components/CnDataTable/CnDataTable.md +1 -1
  247. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  248. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  249. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  250. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  251. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  252. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  253. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  254. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  255. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  256. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  257. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  258. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  259. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  260. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  261. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  262. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  263. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  264. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  265. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  266. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  267. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  268. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  269. package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
  270. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  271. package/src/components/CnTabs/CnTabs.vue +7 -3
  272. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  273. package/src/css/table.css +26 -0
  274. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  275. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  276. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  277. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  278. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  279. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  280. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  281. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  282. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  283. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  284. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  285. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  286. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  287. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  288. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  289. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  290. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  291. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  292. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  293. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  294. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  295. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  296. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  297. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  298. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  299. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  300. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  301. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  302. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  303. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  304. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  305. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  306. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  307. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  308. package/src/schemas/app-manifest-v2.schema.json +2 -1
  309. package/src/utils/actionLink.js +5 -0
  310. package/src/utils/actionsDispatcher.js +15 -0
  311. package/src/utils/linkNavigation.js +6 -3
  312. package/src/utils/rowAuxClick.js +29 -4
  313. package/src/utils/safeHref.js +18 -3
  314. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CnPollsCard.vue2.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsCard.vue"],"sourcesContent":["<!--\n CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n\n Replaces the generic CnIntegrationCard for the `polls` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N polls\" + \"M open\"\n secondary line names the most-recent poll with its leading\n option (per integration-polls spec).\n - detail-page : compact list of linked polls\n with deadline countdown and a mini per-option bar set per row.\n - single-entity : chip with poll title + leading\n option indicator (referenceType: 'polls').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnPollsTab; for `single-entity` the optional `value` prop addresses a\n single poll by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\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 v-if=\"entity\" class=\"cn-polls-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pollUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pollTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"leadingOption(entity)\" class=\"cn-polls-card__chip-leader\">\n\t\t\t\t\t· {{ leadingOption(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-polls-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-polls-card__headline\">\n\t\t\t\t<div class=\"cn-polls-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-polls-card__headline-recent\">\n\t\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pollTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"leadingOption(mostRecent)\" class=\"cn-polls-card__headline-leader\">\n\t\t\t\t\t\t· {{ leadingOption(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-polls-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"poll in displayedPolls\"\n\t\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\t\tclass=\"cn-polls-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-polls-card__row--closed': isClosed(poll) }\">\n\t\t\t\t\t<div class=\"cn-polls-card__row-header\">\n\t\t\t\t\t\t<span class=\"cn-polls-card__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"16\" />\n\t\t\t\t\t\t\t<FormatListChecks v-else :size=\"16\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__status\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(poll)\" class=\"cn-polls-card__subtitle\">{{ rowMeta(poll) }}</span>\n\t\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-card__options\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__option\">\n\t\t\t\t\t\t\t<div class=\"cn-polls-card__option-label\">\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-count\">{{ optionPercent(poll, opt) }}%</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar\"\n\t\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar-fill\"\n\t\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.vue'\nimport Poll from 'vue-material-design-icons/Poll.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/**\n * CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n *\n * Renders Polls-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnPollsCard',\n\n\tcomponents: { CnDetailCard, CnStatusBadge, NcLoadingIcon, CalendarRange, FormatListChecks, Poll },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 (poll id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Polls') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Poll },\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 polls linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\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\tdisplayedPolls() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.polls.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.polls\n\t\t},\n\n\t\topenCount() {\n\t\t\treturn this.polls.reduce((sum, p) => sum + (this.isClosed(p) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.polls.length\n\t\t\tconst open = this.openCount\n\t\t\tif (open === 0) {\n\t\t\t\treturn n('nextcloud-vue', '{count} poll (all closed)', '{count} polls (all closed)', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} poll', '{count} polls', total, { count: total })\n\t\t\tconst openFragment = n('nextcloud-vue', '{count} open', '{count} open', open, { count: open })\n\t\t\treturn `${totalFragment} · ${openFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.polls.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by deadline / created desc.\n\t\t\tconst sorted = [...this.polls].sort((a, b) => {\n\t\t\t\tconst ta = this.sortTimestamp(a)\n\t\t\t\tconst tb = this.sortTimestamp(b)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.name ?? this.pollKey(poll)\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url) {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = poll.id ?? ''\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst c = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(c) ? c : 0\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\tleadingOption(poll) {\n\t\t\tconst opts = this.pollOptions(poll)\n\t\t\tif (opts.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet best = opts[0]\n\t\t\tfor (const o of opts) {\n\t\t\t\tif (this.optionCount(o) > this.optionCount(best)) {\n\t\t\t\t\tbest = o\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.optionCount(best) === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.optionLabel(best)} (${this.optionPercent(poll, best)}%)`\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\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 ? null : parsed\n\t\t},\n\n\t\tsortTimestamp(poll) {\n\t\t\tconst d = this.deadlineMs(poll)\n\t\t\tif (d !== null) {\n\t\t\t\treturn d\n\t\t\t}\n\t\t\tconst c = Number(poll.created ?? 0)\n\t\t\treturn Number.isFinite(c) ? (c < 1e12 ? c * 1000 : c) : 0\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\trowMeta(poll) {\n\t\t\tif (this.isClosed(poll)) {\n\t\t\t\treturn t('nextcloud-vue', 'Closed')\n\t\t\t}\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst diff = ms - Date.now()\n\t\t\tconst days = Math.ceil(diff / MS_PER_DAY)\n\t\t\tif (days <= 1) {\n\t\t\t\tconst hours = Math.max(1, Math.ceil(diff / (60 * 60 * 1000)))\n\t\t\t\treturn t('nextcloud-vue', 'Closes in {n} hours', { n: hours })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Closes in {n} days', { n: days })\n\t\t},\n\n\t\tchipSubtitle(poll) {\n\t\t\treturn this.leadingOption(poll) || this.pollTitle(poll)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.polls = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\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('[CnPollsCard] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnPollsCard] failed to fetch single poll', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-polls-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-polls-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__headline-leader {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-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-polls-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__chip-leader {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-polls-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-polls-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-polls-card__row--closed {\n\topacity: 0.85;\n}\n\n.cn-polls-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-card__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-card__row--closed .cn-polls-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-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-polls-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n\n.cn-polls-card__options {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0 0 0 24px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-card__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1px;\n}\n\n.cn-polls-card__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 6px;\n\tfont-size: 0.8em;\n}\n\n.cn-polls-card__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__option-bar {\n\theight: 4px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-card__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n</style>\n"],"names":["CnDetailCard","CnStatusBadge","t","n"],"mappings":"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"}
1
+ {"version":3,"file":"CnPollsCard.vue2.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsCard.vue"],"sourcesContent":["<!--\n CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n\n Replaces the generic CnIntegrationCard for the `polls` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N polls\" + \"M open\"\n secondary line names the most-recent poll with its leading\n option (per integration-polls spec).\n - detail-page : compact list of linked polls\n with deadline countdown and a mini per-option bar set per row.\n - single-entity : chip with poll title + leading\n option indicator (referenceType: 'polls').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnPollsTab; for `single-entity` the optional `value` prop addresses a\n single poll by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\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 v-if=\"entity\" class=\"cn-polls-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pollUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pollTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"leadingOption(entity)\" class=\"cn-polls-card__chip-leader\">\n\t\t\t\t\t· {{ leadingOption(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-polls-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-polls-card__headline\">\n\t\t\t\t<div class=\"cn-polls-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-polls-card__headline-recent\">\n\t\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pollTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"leadingOption(mostRecent)\" class=\"cn-polls-card__headline-leader\">\n\t\t\t\t\t\t· {{ leadingOption(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-polls-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"poll in displayedPolls\"\n\t\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\t\tclass=\"cn-polls-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-polls-card__row--closed': isClosed(poll) }\">\n\t\t\t\t\t<div class=\"cn-polls-card__row-header\">\n\t\t\t\t\t\t<span class=\"cn-polls-card__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"16\" />\n\t\t\t\t\t\t\t<FormatListChecks v-else :size=\"16\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__status\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(poll)\" class=\"cn-polls-card__subtitle\">{{ rowMeta(poll) }}</span>\n\t\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-card__options\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__option\">\n\t\t\t\t\t\t\t<div class=\"cn-polls-card__option-label\">\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-count\">{{ optionPercent(poll, opt) }}%</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar\"\n\t\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar-fill\"\n\t\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.vue'\nimport Poll from 'vue-material-design-icons/Poll.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/**\n * CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n *\n * Renders Polls-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnPollsCard',\n\n\tcomponents: { CnDetailCard, CnStatusBadge, NcLoadingIcon, CalendarRange, FormatListChecks, Poll },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 (poll id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Polls') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Poll },\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 polls linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\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\tdisplayedPolls() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.polls.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.polls\n\t\t},\n\n\t\topenCount() {\n\t\t\treturn this.polls.reduce((sum, p) => sum + (this.isClosed(p) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.polls.length\n\t\t\tconst open = this.openCount\n\t\t\tif (open === 0) {\n\t\t\t\treturn n('nextcloud-vue', '{count} poll (all closed)', '{count} polls (all closed)', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} poll', '{count} polls', total, { count: total })\n\t\t\tconst openFragment = n('nextcloud-vue', '{count} open', '{count} open', open, { count: open })\n\t\t\treturn `${totalFragment} · ${openFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.polls.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by deadline / created desc.\n\t\t\tconst sorted = [...this.polls].sort((a, b) => {\n\t\t\t\tconst ta = this.sortTimestamp(a)\n\t\t\t\tconst tb = this.sortTimestamp(b)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.name ?? this.pollKey(poll)\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url && safeHref(poll.url) !== '#') {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = poll.id ?? ''\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst c = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(c) ? c : 0\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\tleadingOption(poll) {\n\t\t\tconst opts = this.pollOptions(poll)\n\t\t\tif (opts.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet best = opts[0]\n\t\t\tfor (const o of opts) {\n\t\t\t\tif (this.optionCount(o) > this.optionCount(best)) {\n\t\t\t\t\tbest = o\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.optionCount(best) === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.optionLabel(best)} (${this.optionPercent(poll, best)}%)`\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\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 ? null : parsed\n\t\t},\n\n\t\tsortTimestamp(poll) {\n\t\t\tconst d = this.deadlineMs(poll)\n\t\t\tif (d !== null) {\n\t\t\t\treturn d\n\t\t\t}\n\t\t\tconst c = Number(poll.created ?? 0)\n\t\t\treturn Number.isFinite(c) ? (c < 1e12 ? c * 1000 : c) : 0\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\trowMeta(poll) {\n\t\t\tif (this.isClosed(poll)) {\n\t\t\t\treturn t('nextcloud-vue', 'Closed')\n\t\t\t}\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst diff = ms - Date.now()\n\t\t\tconst days = Math.ceil(diff / MS_PER_DAY)\n\t\t\tif (days <= 1) {\n\t\t\t\tconst hours = Math.max(1, Math.ceil(diff / (60 * 60 * 1000)))\n\t\t\t\treturn t('nextcloud-vue', 'Closes in {n} hours', { n: hours })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Closes in {n} days', { n: days })\n\t\t},\n\n\t\tchipSubtitle(poll) {\n\t\t\treturn this.leadingOption(poll) || this.pollTitle(poll)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.polls = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\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('[CnPollsCard] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnPollsCard] failed to fetch single poll', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-polls-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-polls-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__headline-leader {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-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-polls-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__chip-leader {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-polls-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-polls-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-polls-card__row--closed {\n\topacity: 0.85;\n}\n\n.cn-polls-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-card__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-card__row--closed .cn-polls-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-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-polls-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n\n.cn-polls-card__options {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0 0 0 24px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-card__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1px;\n}\n\n.cn-polls-card__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 6px;\n\tfont-size: 0.8em;\n}\n\n.cn-polls-card__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__option-bar {\n\theight: 4px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-card__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n</style>\n"],"names":["CnDetailCard","CnStatusBadge","t","n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnPollsCard.vue3.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsCard.vue?vue&type=template&id=5977968a&lang.js"],"sourcesContent":["<!--\n CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n\n Replaces the generic CnIntegrationCard for the `polls` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N polls\" + \"M open\"\n secondary line names the most-recent poll with its leading\n option (per integration-polls spec).\n - detail-page : compact list of linked polls\n with deadline countdown and a mini per-option bar set per row.\n - single-entity : chip with poll title + leading\n option indicator (referenceType: 'polls').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnPollsTab; for `single-entity` the optional `value` prop addresses a\n single poll by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\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 v-if=\"entity\" class=\"cn-polls-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pollUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pollTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"leadingOption(entity)\" class=\"cn-polls-card__chip-leader\">\n\t\t\t\t\t· {{ leadingOption(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-polls-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-polls-card__headline\">\n\t\t\t\t<div class=\"cn-polls-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-polls-card__headline-recent\">\n\t\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pollTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"leadingOption(mostRecent)\" class=\"cn-polls-card__headline-leader\">\n\t\t\t\t\t\t· {{ leadingOption(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-polls-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"poll in displayedPolls\"\n\t\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\t\tclass=\"cn-polls-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-polls-card__row--closed': isClosed(poll) }\">\n\t\t\t\t\t<div class=\"cn-polls-card__row-header\">\n\t\t\t\t\t\t<span class=\"cn-polls-card__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"16\" />\n\t\t\t\t\t\t\t<FormatListChecks v-else :size=\"16\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__status\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(poll)\" class=\"cn-polls-card__subtitle\">{{ rowMeta(poll) }}</span>\n\t\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-card__options\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__option\">\n\t\t\t\t\t\t\t<div class=\"cn-polls-card__option-label\">\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-count\">{{ optionPercent(poll, opt) }}%</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar\"\n\t\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar-fill\"\n\t\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.vue'\nimport Poll from 'vue-material-design-icons/Poll.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/**\n * CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n *\n * Renders Polls-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnPollsCard',\n\n\tcomponents: { CnDetailCard, CnStatusBadge, NcLoadingIcon, CalendarRange, FormatListChecks, Poll },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 (poll id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Polls') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Poll },\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 polls linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\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\tdisplayedPolls() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.polls.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.polls\n\t\t},\n\n\t\topenCount() {\n\t\t\treturn this.polls.reduce((sum, p) => sum + (this.isClosed(p) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.polls.length\n\t\t\tconst open = this.openCount\n\t\t\tif (open === 0) {\n\t\t\t\treturn n('nextcloud-vue', '{count} poll (all closed)', '{count} polls (all closed)', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} poll', '{count} polls', total, { count: total })\n\t\t\tconst openFragment = n('nextcloud-vue', '{count} open', '{count} open', open, { count: open })\n\t\t\treturn `${totalFragment} · ${openFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.polls.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by deadline / created desc.\n\t\t\tconst sorted = [...this.polls].sort((a, b) => {\n\t\t\t\tconst ta = this.sortTimestamp(a)\n\t\t\t\tconst tb = this.sortTimestamp(b)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.name ?? this.pollKey(poll)\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url) {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = poll.id ?? ''\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst c = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(c) ? c : 0\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\tleadingOption(poll) {\n\t\t\tconst opts = this.pollOptions(poll)\n\t\t\tif (opts.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet best = opts[0]\n\t\t\tfor (const o of opts) {\n\t\t\t\tif (this.optionCount(o) > this.optionCount(best)) {\n\t\t\t\t\tbest = o\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.optionCount(best) === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.optionLabel(best)} (${this.optionPercent(poll, best)}%)`\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\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 ? null : parsed\n\t\t},\n\n\t\tsortTimestamp(poll) {\n\t\t\tconst d = this.deadlineMs(poll)\n\t\t\tif (d !== null) {\n\t\t\t\treturn d\n\t\t\t}\n\t\t\tconst c = Number(poll.created ?? 0)\n\t\t\treturn Number.isFinite(c) ? (c < 1e12 ? c * 1000 : c) : 0\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\trowMeta(poll) {\n\t\t\tif (this.isClosed(poll)) {\n\t\t\t\treturn t('nextcloud-vue', 'Closed')\n\t\t\t}\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst diff = ms - Date.now()\n\t\t\tconst days = Math.ceil(diff / MS_PER_DAY)\n\t\t\tif (days <= 1) {\n\t\t\t\tconst hours = Math.max(1, Math.ceil(diff / (60 * 60 * 1000)))\n\t\t\t\treturn t('nextcloud-vue', 'Closes in {n} hours', { n: hours })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Closes in {n} days', { n: days })\n\t\t},\n\n\t\tchipSubtitle(poll) {\n\t\t\treturn this.leadingOption(poll) || this.pollTitle(poll)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.polls = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\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('[CnPollsCard] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnPollsCard] failed to fetch single poll', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-polls-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-polls-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__headline-leader {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-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-polls-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__chip-leader {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-polls-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-polls-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-polls-card__row--closed {\n\topacity: 0.85;\n}\n\n.cn-polls-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-card__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-card__row--closed .cn-polls-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-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-polls-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n\n.cn-polls-card__options {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0 0 0 24px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-card__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1px;\n}\n\n.cn-polls-card__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 6px;\n\tfont-size: 0.8em;\n}\n\n.cn-polls-card__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__option-bar {\n\theight: 4px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-card__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n</style>\n"],"names":["_Fragment","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_openBlock","_normalizeClass","_createBlock","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnPollsCard.vue3.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsCard.vue?vue&type=template&id=5977968a&lang.js"],"sourcesContent":["<!--\n CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n\n Replaces the generic CnIntegrationCard for the `polls` leaf. Branches\n on `surface` per AD-19:\n - user-dashboard / app-dashboard : headline \"N polls\" + \"M open\"\n secondary line names the most-recent poll with its leading\n option (per integration-polls spec).\n - detail-page : compact list of linked polls\n with deadline countdown and a mini per-option bar set per row.\n - single-entity : chip with poll title + leading\n option indicator (referenceType: 'polls').\n\n Pulls rows from the same OR pluggable-integration sub-resource as\n CnPollsTab; for `single-entity` the optional `value` prop addresses a\n single poll by id (matching CnIntegrationCard's fetchSingle\n contract).\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism), AD-19 (surface fallback).\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 v-if=\"entity\" class=\"cn-polls-card__chip\" :title=\"chipSubtitle(entity)\">\n\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t<a\n\t\t\t\t\t:href=\"pollUrl(entity)\"\n\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\trel=\"noopener\">{{ pollTitle(entity) }}</a>\n\t\t\t\t<span v-if=\"leadingOption(entity)\" class=\"cn-polls-card__chip-leader\">\n\t\t\t\t\t· {{ leadingOption(entity) }}\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-polls-card__empty\">{{ emptyLabel }}</span>\n\t\t</template>\n\n\t\t<!-- dashboard surfaces: headline + most-recent -->\n\t\t<template v-else-if=\"surface === 'user-dashboard' || surface === 'app-dashboard'\">\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<div v-else class=\"cn-polls-card__headline\">\n\t\t\t\t<div class=\"cn-polls-card__headline-line\">\n\t\t\t\t\t<strong>{{ countHeadline }}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div v-if=\"mostRecent\" class=\"cn-polls-card__headline-recent\">\n\t\t\t\t\t<Poll :size=\"14\" />\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(mostRecent)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\">{{ pollTitle(mostRecent) }}</a>\n\t\t\t\t\t<span v-if=\"leadingOption(mostRecent)\" class=\"cn-polls-card__headline-leader\">\n\t\t\t\t\t\t· {{ leadingOption(mostRecent) }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</template>\n\n\t\t<!-- detail-page surface: compact list -->\n\t\t<template v-else>\n\t\t\t<div v-if=\"degraded\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ degraded }}\n\t\t\t</div>\n\t\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-polls-card__empty\">\n\t\t\t\t{{ emptyLabel }}\n\t\t\t</div>\n\t\t\t<ul v-else class=\"cn-polls-card__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"poll in displayedPolls\"\n\t\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\t\tclass=\"cn-polls-card__row\"\n\t\t\t\t\t:class=\"{ 'cn-polls-card__row--closed': isClosed(poll) }\">\n\t\t\t\t\t<div class=\"cn-polls-card__row-header\">\n\t\t\t\t\t\t<span class=\"cn-polls-card__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"16\" />\n\t\t\t\t\t\t\t<FormatListChecks v-else :size=\"16\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__status\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<span v-if=\"rowMeta(poll)\" class=\"cn-polls-card__subtitle\">{{ rowMeta(poll) }}</span>\n\t\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-card__options\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\t\tclass=\"cn-polls-card__option\">\n\t\t\t\t\t\t\t<div class=\"cn-polls-card__option-label\">\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-polls-card__option-count\">{{ optionPercent(poll, opt) }}%</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar\"\n\t\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"cn-polls-card__option-bar-fill\"\n\t\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</template>\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translatePlural as n, translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.vue'\nimport Poll from 'vue-material-design-icons/Poll.vue'\nimport CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\nconst VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']\nconst COMPACT_LIMIT = 5\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\n/**\n * CnPollsCard — bespoke surface-aware widget for the `polls` integration.\n *\n * Renders Polls-aware metadata across all four surfaces. See the\n * file-level docblock for surface-by-surface behaviour.\n */\nexport default {\n\tname: 'CnPollsCard',\n\n\tcomponents: { CnDetailCard, CnStatusBadge, NcLoadingIcon, CalendarRange, FormatListChecks, Poll },\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 (poll id). */\n\t\tvalue: { type: String, default: '' },\n\t\t/** Pre-translated card title. */\n\t\ttitle: { type: String, default: () => t('nextcloud-vue', 'Polls') },\n\t\t/** Optional Material Design Icon component. */\n\t\ticon: { type: Object, default: () => Poll },\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 polls linked yet') },\n\t\t/** Pre-translated unavailable label. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tentity: null,\n\t\t\tloading: false,\n\t\t\tdegraded: '',\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\tdisplayedPolls() {\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\treturn this.polls.slice(0, COMPACT_LIMIT)\n\t\t\t}\n\t\t\treturn this.polls\n\t\t},\n\n\t\topenCount() {\n\t\t\treturn this.polls.reduce((sum, p) => sum + (this.isClosed(p) ? 0 : 1), 0)\n\t\t},\n\n\t\tcountHeadline() {\n\t\t\tconst total = this.polls.length\n\t\t\tconst open = this.openCount\n\t\t\tif (open === 0) {\n\t\t\t\treturn n('nextcloud-vue', '{count} poll (all closed)', '{count} polls (all closed)', total, { count: total })\n\t\t\t}\n\t\t\tconst totalFragment = n('nextcloud-vue', '{count} poll', '{count} polls', total, { count: total })\n\t\t\tconst openFragment = n('nextcloud-vue', '{count} open', '{count} open', open, { count: open })\n\t\t\treturn `${totalFragment} · ${openFragment}`\n\t\t},\n\n\t\tmostRecent() {\n\t\t\tif (this.polls.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Sort defensive copy by deadline / created desc.\n\t\t\tconst sorted = [...this.polls].sort((a, b) => {\n\t\t\t\tconst ta = this.sortTimestamp(a)\n\t\t\t\tconst tb = this.sortTimestamp(b)\n\t\t\t\treturn tb - ta\n\t\t\t})\n\t\t\treturn sorted[0]\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.fetch()\n\t\t} },\n\n\t\tsurface() {\n\t\t\tthis.fetch()\n\t\t},\n\n\t\tvalue() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.name ?? this.pollKey(poll)\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url && safeHref(poll.url) !== '#') {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = poll.id ?? ''\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst c = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(c) ? c : 0\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\tleadingOption(poll) {\n\t\t\tconst opts = this.pollOptions(poll)\n\t\t\tif (opts.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet best = opts[0]\n\t\t\tfor (const o of opts) {\n\t\t\t\tif (this.optionCount(o) > this.optionCount(best)) {\n\t\t\t\t\tbest = o\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.optionCount(best) === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `${this.optionLabel(best)} (${this.optionPercent(poll, best)}%)`\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\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 ? null : parsed\n\t\t},\n\n\t\tsortTimestamp(poll) {\n\t\t\tconst d = this.deadlineMs(poll)\n\t\t\tif (d !== null) {\n\t\t\t\treturn d\n\t\t\t}\n\t\t\tconst c = Number(poll.created ?? 0)\n\t\t\treturn Number.isFinite(c) ? (c < 1e12 ? c * 1000 : c) : 0\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\trowMeta(poll) {\n\t\t\tif (this.isClosed(poll)) {\n\t\t\t\treturn t('nextcloud-vue', 'Closed')\n\t\t\t}\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst diff = ms - Date.now()\n\t\t\tconst days = Math.ceil(diff / MS_PER_DAY)\n\t\t\tif (days <= 1) {\n\t\t\t\tconst hours = Math.max(1, Math.ceil(diff / (60 * 60 * 1000)))\n\t\t\t\treturn t('nextcloud-vue', 'Closes in {n} hours', { n: hours })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Closes in {n} days', { n: days })\n\t\t},\n\n\t\tchipSubtitle(poll) {\n\t\t\treturn this.leadingOption(poll) || this.pollTitle(poll)\n\t\t},\n\n\t\tfetch() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingle()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetchList()\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.polls = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\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('[CnPollsCard] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tasync fetchSingle() {\n\t\t\tif (!this.value || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.entity = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(`${this.baseUrl()}/${encodeURIComponent(this.value)}`), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tthis.entity = await response.json()\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.entity = null\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnPollsCard] failed to fetch single poll', err)\n\t\t\t\tthis.entity = null\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-polls-card__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tpadding: 8px 0;\n}\n\n.cn-polls-card__headline {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-card__headline-line {\n\tfont-size: 1.1em;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__headline-recent {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tfont-size: 0.9em;\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-card__headline-recent a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__headline-recent a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__headline-leader {\n\tcolor: var(--color-text-maxcontrast);\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-polls-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-polls-card__chip a {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n.cn-polls-card__chip a:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__chip-leader {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-polls-card__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-polls-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-polls-card__row--closed {\n\topacity: 0.85;\n}\n\n.cn-polls-card__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-card__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-card__row--closed .cn-polls-card__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-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-polls-card__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-card__subtitle {\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 24px;\n}\n\n.cn-polls-card__options {\n\tlist-style: none;\n\tmargin: 2px 0 0;\n\tpadding: 0 0 0 24px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-card__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 1px;\n}\n\n.cn-polls-card__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 6px;\n\tfont-size: 0.8em;\n}\n\n.cn-polls-card__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-card__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-card__option-bar {\n\theight: 4px;\n\tborder-radius: 2px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-card__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n</style>\n"],"names":["_Fragment","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_openBlock","_normalizeClass","_createBlock","_renderList"],"mappings":"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"}
@@ -15,6 +15,7 @@ import __sfc_main$1 from '../../../components/CnPollPicker/CnPollPicker.vue.js';
15
15
  import __sfc_main from '../../../components/CnStatusBadge/CnStatusBadge.vue.js';
16
16
  import { stripMarker } from '../../utils/marker.js';
17
17
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
18
+ import { safeHref } from '../../../utils/safeHref.js';
18
19
 
19
20
  /**
20
21
  * CnPollsTab — bespoke live-tally list for the `polls` integration.
@@ -199,7 +200,7 @@ var script = {
199
200
  },
200
201
 
201
202
  pollUrl(poll) {
202
- if (poll.url) {
203
+ if (poll.url && safeHref(poll.url) !== '#') {
203
204
  return poll.url
204
205
  }
205
206
  const id = this.pollKey(poll);
@@ -1 +1 @@
1
- {"version":3,"file":"CnPollsTab.vue2.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsTab.vue"],"sourcesContent":["<!--\n CnPollsTab — bespoke sidebar tab for the `polls` integration.\n\n Replaces the generic CnIntegrationTab for the `polls` leaf: renders a\n live vote tally for each linked NC Polls poll, mirroring the real NC\n Polls list look. Each row foregrounds a poll-type icon (text vs date\n poll), the bold poll question/title, an open / closed status chip\n (CnStatusBadge), a meta line with participant + option counts, a\n relative expiry (NcDateTime) when a deadline is present, and a\n per-option vote-tally progress bar. A trailing \"Open in Polls\" action\n per row deep-links to `/index.php/apps/polls/vote/{id}`.\n\n Tier-2 surface (this commit):\n - \"Link existing poll\" → opens CnPollPicker (modal)\n - \"Create new poll\" → opens CnPollCreate (modal)\n - Per-row unlink → DELETE …/polls/{pollId}\n\n Talks to the OpenRegister Tier-2 poll-link endpoints\n GET /api/objects/{r}/{s}/{id}/polls — list\n POST /api/objects/{r}/{s}/{id}/polls — link existing\n POST /api/objects/{r}/{s}/{id}/polls/new — create + link\n DELETE /api/objects/{r}/{s}/{id}/polls/{pollId} — unlink\n GET /api/integrations/polls/available — picker source\n served by `OCA\\OpenRegister\\Controller\\PollLinksController`.\n\n Surface behaviour:\n - Empty state with \"Open Polls\" CTA when no linked polls.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-polls-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-polls-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-polls-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 poll') }}\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 poll') }}\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-polls-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-sidebar-tab__empty cn-polls-tab__empty\">\n\t\t\t<Poll :size=\"32\" class=\"cn-polls-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=\"pollsAppUrl\"\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<Poll :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openPollsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<ul v-else class=\"cn-polls-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"poll in polls\"\n\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\tclass=\"cn-polls-tab__row\"\n\t\t\t\t:class=\"{ 'cn-polls-tab__row--closed': isClosed(poll) }\">\n\t\t\t\t<div class=\"cn-polls-tab__row-header\">\n\t\t\t\t\t<span class=\"cn-polls-tab__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"20\" />\n\t\t\t\t\t\t<FormatListChecks v-else :size=\"20\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__status\" />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Unlink poll')\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__unlink\"\n\t\t\t\t\t\t@click=\"unlinkPoll(poll)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-polls-tab__meta\">\n\t\t\t\t\t<span class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<AccountMultiple :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ voterMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<FormatListBulleted :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ optionMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"deadlineMs(poll) !== null\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__time\"\n\t\t\t\t\t\t\t:timestamp=\"deadlineMs(poll)\"\n\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__options\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__option\">\n\t\t\t\t\t\t<div class=\"cn-polls-tab__option-label\">\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-count\">\n\t\t\t\t\t\t\t\t{{ optionCount(opt) }} ({{ optionPercent(poll, opt) }}%)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar\"\n\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar-fill\"\n\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\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=\"pollDescription(poll)\" class=\"cn-polls-tab__description\">\n\t\t\t\t\t{{ pollDescription(poll) }}\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnPollPicker\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<CnPollCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.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 Poll from 'vue-material-design-icons/Poll.vue'\nimport CnPollCreate from '../../../components/CnPollCreate/CnPollCreate.vue'\nimport CnPollPicker from '../../../components/CnPollPicker/CnPollPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\n/**\n * CnPollsTab — bespoke live-tally list for the `polls` integration.\n *\n * Renders rows pulled from the OR pluggable-integration endpoint that\n * mirror the real NC Polls list: type icon, bold question, open/closed\n * status chip, participant + option counts, a relative expiry, and\n * per-option vote-tally progress bars. Tier-2: includes link/create\n * modals and per-row unlink.\n */\nexport default {\n\tname: 'CnPollsTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tAccountMultiple,\n\t\tAlertCircleOutline,\n\t\tCalendarRange,\n\t\tClockOutline,\n\t\tFormatListBulleted,\n\t\tFormatListChecks,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tPoll,\n\t\tCnPollPicker,\n\t\tCnPollCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 polls linked yet') },\n\t\t/** Pre-translated label for the \"Open in Polls\" CTA. */\n\t\topenPollsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Polls') },\n\t\t/** Pre-translated banner when Polls is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPolls()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 polls endpoints (link/new/destroy).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tpollsEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/polls`)\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.pollsEndpoint(), {\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.fetchPolls()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This poll is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\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('[CnPollsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\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('[CnPollsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoll(poll) {\n\t\t\tconst pollId = this.pollKey(poll)\n\t\t\tif (!pollId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/${pollId}`, {\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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\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('[CnPollsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\n\t\t\t}\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.pollId ?? poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.pollTitle ?? poll.name ?? this.pollKey(poll)\n\t\t\t// Strip any legacy `[or:{uuid}]` marker from displayed title\n\t\t\t// via the shared helper (ADR-019).\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollDescription(poll) {\n\t\t\treturn stripMarker(poll.description)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url) {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = this.pollKey(poll)\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst n = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(n) ? n : 0\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\tvoterCount(poll) {\n\t\t\tconst n = Number(poll.voterCount ?? poll.voters ?? poll.participants ?? 0)\n\t\t\treturn Number.isFinite(n) && n > 0 ? n : this.totalVotes(poll)\n\t\t},\n\n\t\tvoterMetaLabel(poll) {\n\t\t\tconst voters = this.voterCount(poll)\n\t\t\treturn t('nextcloud-vue', '{n} participants', { n: voters })\n\t\t},\n\n\t\toptionMetaLabel(poll) {\n\t\t\tconst count = this.pollOptions(poll).length\n\t\t\treturn t('nextcloud-vue', '{n} options', { n: count })\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Treat as unix seconds when it's small enough to be a 10-digit epoch.\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 ? null : parsed\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\t// Without a deadline, fall back to a `closed` flag if present.\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tasync fetchPolls() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.polls = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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('[CnPollsTab] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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-polls-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-polls-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-polls-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-polls-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-polls-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 10px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tborder-left: 3px solid var(--color-primary-element);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-polls-tab__row--closed {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.78;\n}\n\n.cn-polls-tab__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-tab__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__row--closed .cn-polls-tab__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__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\tfont-weight: 600;\n}\n\na.cn-polls-tab__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-tab__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 12px;\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n\n.cn-polls-tab__meta-item {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-polls-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__time {\n\tfont-size: 1em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__options {\n\tlist-style: none;\n\tmargin: 4px 0 0;\n\tpadding: 0 0 0 28px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-tab__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-polls-tab__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-tab__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__option-bar {\n\theight: 6px;\n\tborder-radius: 3px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-tab__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n\n.cn-polls-tab__description {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n</style>\n"],"names":["CnStatusBadge","CnPollPicker","CnPollCreate","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnPollsTab.vue2.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsTab.vue"],"sourcesContent":["<!--\n CnPollsTab — bespoke sidebar tab for the `polls` integration.\n\n Replaces the generic CnIntegrationTab for the `polls` leaf: renders a\n live vote tally for each linked NC Polls poll, mirroring the real NC\n Polls list look. Each row foregrounds a poll-type icon (text vs date\n poll), the bold poll question/title, an open / closed status chip\n (CnStatusBadge), a meta line with participant + option counts, a\n relative expiry (NcDateTime) when a deadline is present, and a\n per-option vote-tally progress bar. A trailing \"Open in Polls\" action\n per row deep-links to `/index.php/apps/polls/vote/{id}`.\n\n Tier-2 surface (this commit):\n - \"Link existing poll\" → opens CnPollPicker (modal)\n - \"Create new poll\" → opens CnPollCreate (modal)\n - Per-row unlink → DELETE …/polls/{pollId}\n\n Talks to the OpenRegister Tier-2 poll-link endpoints\n GET /api/objects/{r}/{s}/{id}/polls — list\n POST /api/objects/{r}/{s}/{id}/polls — link existing\n POST /api/objects/{r}/{s}/{id}/polls/new — create + link\n DELETE /api/objects/{r}/{s}/{id}/polls/{pollId} — unlink\n GET /api/integrations/polls/available — picker source\n served by `OCA\\OpenRegister\\Controller\\PollLinksController`.\n\n Surface behaviour:\n - Empty state with \"Open Polls\" CTA when no linked polls.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-polls-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-polls-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-polls-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 poll') }}\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 poll') }}\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-polls-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-sidebar-tab__empty cn-polls-tab__empty\">\n\t\t\t<Poll :size=\"32\" class=\"cn-polls-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=\"pollsAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Poll :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openPollsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<ul v-else class=\"cn-polls-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"poll in polls\"\n\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\tclass=\"cn-polls-tab__row\"\n\t\t\t\t:class=\"{ 'cn-polls-tab__row--closed': isClosed(poll) }\">\n\t\t\t\t<div class=\"cn-polls-tab__row-header\">\n\t\t\t\t\t<span class=\"cn-polls-tab__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"20\" />\n\t\t\t\t\t\t<FormatListChecks v-else :size=\"20\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__status\" />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Unlink poll')\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__unlink\"\n\t\t\t\t\t\t@click=\"unlinkPoll(poll)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-polls-tab__meta\">\n\t\t\t\t\t<span class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<AccountMultiple :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ voterMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<FormatListBulleted :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ optionMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"deadlineMs(poll) !== null\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__time\"\n\t\t\t\t\t\t\t:timestamp=\"deadlineMs(poll)\"\n\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__options\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__option\">\n\t\t\t\t\t\t<div class=\"cn-polls-tab__option-label\">\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-count\">\n\t\t\t\t\t\t\t\t{{ optionCount(opt) }} ({{ optionPercent(poll, opt) }}%)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar\"\n\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar-fill\"\n\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\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=\"pollDescription(poll)\" class=\"cn-polls-tab__description\">\n\t\t\t\t\t{{ pollDescription(poll) }}\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnPollPicker\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<CnPollCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.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 Poll from 'vue-material-design-icons/Poll.vue'\nimport CnPollCreate from '../../../components/CnPollCreate/CnPollCreate.vue'\nimport CnPollPicker from '../../../components/CnPollPicker/CnPollPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\n/**\n * CnPollsTab — bespoke live-tally list for the `polls` integration.\n *\n * Renders rows pulled from the OR pluggable-integration endpoint that\n * mirror the real NC Polls list: type icon, bold question, open/closed\n * status chip, participant + option counts, a relative expiry, and\n * per-option vote-tally progress bars. Tier-2: includes link/create\n * modals and per-row unlink.\n */\nexport default {\n\tname: 'CnPollsTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tAccountMultiple,\n\t\tAlertCircleOutline,\n\t\tCalendarRange,\n\t\tClockOutline,\n\t\tFormatListBulleted,\n\t\tFormatListChecks,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tPoll,\n\t\tCnPollPicker,\n\t\tCnPollCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 polls linked yet') },\n\t\t/** Pre-translated label for the \"Open in Polls\" CTA. */\n\t\topenPollsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Polls') },\n\t\t/** Pre-translated banner when Polls is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPolls()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 polls endpoints (link/new/destroy).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tpollsEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/polls`)\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.pollsEndpoint(), {\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.fetchPolls()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This poll is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\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('[CnPollsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\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('[CnPollsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoll(poll) {\n\t\t\tconst pollId = this.pollKey(poll)\n\t\t\tif (!pollId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/${pollId}`, {\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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\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('[CnPollsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\n\t\t\t}\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.pollId ?? poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.pollTitle ?? poll.name ?? this.pollKey(poll)\n\t\t\t// Strip any legacy `[or:{uuid}]` marker from displayed title\n\t\t\t// via the shared helper (ADR-019).\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollDescription(poll) {\n\t\t\treturn stripMarker(poll.description)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url && safeHref(poll.url) !== '#') {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = this.pollKey(poll)\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst n = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(n) ? n : 0\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\tvoterCount(poll) {\n\t\t\tconst n = Number(poll.voterCount ?? poll.voters ?? poll.participants ?? 0)\n\t\t\treturn Number.isFinite(n) && n > 0 ? n : this.totalVotes(poll)\n\t\t},\n\n\t\tvoterMetaLabel(poll) {\n\t\t\tconst voters = this.voterCount(poll)\n\t\t\treturn t('nextcloud-vue', '{n} participants', { n: voters })\n\t\t},\n\n\t\toptionMetaLabel(poll) {\n\t\t\tconst count = this.pollOptions(poll).length\n\t\t\treturn t('nextcloud-vue', '{n} options', { n: count })\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Treat as unix seconds when it's small enough to be a 10-digit epoch.\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 ? null : parsed\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\t// Without a deadline, fall back to a `closed` flag if present.\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tasync fetchPolls() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.polls = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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('[CnPollsTab] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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-polls-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-polls-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-polls-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-polls-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-polls-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 10px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tborder-left: 3px solid var(--color-primary-element);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-polls-tab__row--closed {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.78;\n}\n\n.cn-polls-tab__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-tab__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__row--closed .cn-polls-tab__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__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\tfont-weight: 600;\n}\n\na.cn-polls-tab__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-tab__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 12px;\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n\n.cn-polls-tab__meta-item {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-polls-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__time {\n\tfont-size: 1em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__options {\n\tlist-style: none;\n\tmargin: 4px 0 0;\n\tpadding: 0 0 0 28px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-tab__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-polls-tab__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-tab__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__option-bar {\n\theight: 6px;\n\tborder-radius: 3px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-tab__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n\n.cn-polls-tab__description {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n</style>\n"],"names":["CnStatusBadge","CnPollPicker","CnPollCreate","t"],"mappings":"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"}
@@ -111,8 +111,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
111
111
  createVNode(_component_NcButton, {
112
112
  variant: "primary",
113
113
  href: $props.pollsAppUrl,
114
- target: "_blank",
115
- rel: "noopener noreferrer"
114
+ target: "_blank"
116
115
  }, {
117
116
  icon: withCtx(() => [
118
117
  createVNode(_component_Poll, { size: 20 })
@@ -1 +1 @@
1
- {"version":3,"file":"CnPollsTab.vue3.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsTab.vue?vue&type=template&id=abac3b8c&lang.js"],"sourcesContent":["<!--\n CnPollsTab — bespoke sidebar tab for the `polls` integration.\n\n Replaces the generic CnIntegrationTab for the `polls` leaf: renders a\n live vote tally for each linked NC Polls poll, mirroring the real NC\n Polls list look. Each row foregrounds a poll-type icon (text vs date\n poll), the bold poll question/title, an open / closed status chip\n (CnStatusBadge), a meta line with participant + option counts, a\n relative expiry (NcDateTime) when a deadline is present, and a\n per-option vote-tally progress bar. A trailing \"Open in Polls\" action\n per row deep-links to `/index.php/apps/polls/vote/{id}`.\n\n Tier-2 surface (this commit):\n - \"Link existing poll\" → opens CnPollPicker (modal)\n - \"Create new poll\" → opens CnPollCreate (modal)\n - Per-row unlink → DELETE …/polls/{pollId}\n\n Talks to the OpenRegister Tier-2 poll-link endpoints\n GET /api/objects/{r}/{s}/{id}/polls — list\n POST /api/objects/{r}/{s}/{id}/polls — link existing\n POST /api/objects/{r}/{s}/{id}/polls/new — create + link\n DELETE /api/objects/{r}/{s}/{id}/polls/{pollId} — unlink\n GET /api/integrations/polls/available — picker source\n served by `OCA\\OpenRegister\\Controller\\PollLinksController`.\n\n Surface behaviour:\n - Empty state with \"Open Polls\" CTA when no linked polls.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-polls-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-polls-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-polls-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 poll') }}\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 poll') }}\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-polls-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-sidebar-tab__empty cn-polls-tab__empty\">\n\t\t\t<Poll :size=\"32\" class=\"cn-polls-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=\"pollsAppUrl\"\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<Poll :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openPollsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<ul v-else class=\"cn-polls-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"poll in polls\"\n\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\tclass=\"cn-polls-tab__row\"\n\t\t\t\t:class=\"{ 'cn-polls-tab__row--closed': isClosed(poll) }\">\n\t\t\t\t<div class=\"cn-polls-tab__row-header\">\n\t\t\t\t\t<span class=\"cn-polls-tab__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"20\" />\n\t\t\t\t\t\t<FormatListChecks v-else :size=\"20\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__status\" />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Unlink poll')\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__unlink\"\n\t\t\t\t\t\t@click=\"unlinkPoll(poll)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-polls-tab__meta\">\n\t\t\t\t\t<span class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<AccountMultiple :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ voterMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<FormatListBulleted :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ optionMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"deadlineMs(poll) !== null\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__time\"\n\t\t\t\t\t\t\t:timestamp=\"deadlineMs(poll)\"\n\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__options\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__option\">\n\t\t\t\t\t\t<div class=\"cn-polls-tab__option-label\">\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-count\">\n\t\t\t\t\t\t\t\t{{ optionCount(opt) }} ({{ optionPercent(poll, opt) }}%)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar\"\n\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar-fill\"\n\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\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=\"pollDescription(poll)\" class=\"cn-polls-tab__description\">\n\t\t\t\t\t{{ pollDescription(poll) }}\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnPollPicker\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<CnPollCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.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 Poll from 'vue-material-design-icons/Poll.vue'\nimport CnPollCreate from '../../../components/CnPollCreate/CnPollCreate.vue'\nimport CnPollPicker from '../../../components/CnPollPicker/CnPollPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\n/**\n * CnPollsTab — bespoke live-tally list for the `polls` integration.\n *\n * Renders rows pulled from the OR pluggable-integration endpoint that\n * mirror the real NC Polls list: type icon, bold question, open/closed\n * status chip, participant + option counts, a relative expiry, and\n * per-option vote-tally progress bars. Tier-2: includes link/create\n * modals and per-row unlink.\n */\nexport default {\n\tname: 'CnPollsTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tAccountMultiple,\n\t\tAlertCircleOutline,\n\t\tCalendarRange,\n\t\tClockOutline,\n\t\tFormatListBulleted,\n\t\tFormatListChecks,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tPoll,\n\t\tCnPollPicker,\n\t\tCnPollCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 polls linked yet') },\n\t\t/** Pre-translated label for the \"Open in Polls\" CTA. */\n\t\topenPollsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Polls') },\n\t\t/** Pre-translated banner when Polls is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPolls()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 polls endpoints (link/new/destroy).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tpollsEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/polls`)\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.pollsEndpoint(), {\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.fetchPolls()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This poll is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\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('[CnPollsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\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('[CnPollsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoll(poll) {\n\t\t\tconst pollId = this.pollKey(poll)\n\t\t\tif (!pollId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/${pollId}`, {\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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\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('[CnPollsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\n\t\t\t}\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.pollId ?? poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.pollTitle ?? poll.name ?? this.pollKey(poll)\n\t\t\t// Strip any legacy `[or:{uuid}]` marker from displayed title\n\t\t\t// via the shared helper (ADR-019).\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollDescription(poll) {\n\t\t\treturn stripMarker(poll.description)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url) {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = this.pollKey(poll)\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst n = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(n) ? n : 0\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\tvoterCount(poll) {\n\t\t\tconst n = Number(poll.voterCount ?? poll.voters ?? poll.participants ?? 0)\n\t\t\treturn Number.isFinite(n) && n > 0 ? n : this.totalVotes(poll)\n\t\t},\n\n\t\tvoterMetaLabel(poll) {\n\t\t\tconst voters = this.voterCount(poll)\n\t\t\treturn t('nextcloud-vue', '{n} participants', { n: voters })\n\t\t},\n\n\t\toptionMetaLabel(poll) {\n\t\t\tconst count = this.pollOptions(poll).length\n\t\t\treturn t('nextcloud-vue', '{n} options', { n: count })\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Treat as unix seconds when it's small enough to be a 10-digit epoch.\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 ? null : parsed\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\t// Without a deadline, fall back to a `closed` flag if present.\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tasync fetchPolls() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.polls = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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('[CnPollsTab] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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-polls-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-polls-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-polls-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-polls-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-polls-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 10px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tborder-left: 3px solid var(--color-primary-element);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-polls-tab__row--closed {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.78;\n}\n\n.cn-polls-tab__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-tab__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__row--closed .cn-polls-tab__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__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\tfont-weight: 600;\n}\n\na.cn-polls-tab__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-tab__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 12px;\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n\n.cn-polls-tab__meta-item {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-polls-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__time {\n\tfont-size: 1em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__options {\n\tlist-style: none;\n\tmargin: 4px 0 0;\n\tpadding: 0 0 0 28px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-tab__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-polls-tab__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-tab__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__option-bar {\n\theight: 6px;\n\tborder-radius: 3px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-tab__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n\n.cn-polls-tab__description {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","_createTextVNode","_toDisplayString","_normalizeClass","_createBlock","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnPollsTab.vue3.js","sources":["../../../../../src/integrations/builtin/polls/CnPollsTab.vue?vue&type=template&id=abac3b8c&lang.js"],"sourcesContent":["<!--\n CnPollsTab — bespoke sidebar tab for the `polls` integration.\n\n Replaces the generic CnIntegrationTab for the `polls` leaf: renders a\n live vote tally for each linked NC Polls poll, mirroring the real NC\n Polls list look. Each row foregrounds a poll-type icon (text vs date\n poll), the bold poll question/title, an open / closed status chip\n (CnStatusBadge), a meta line with participant + option counts, a\n relative expiry (NcDateTime) when a deadline is present, and a\n per-option vote-tally progress bar. A trailing \"Open in Polls\" action\n per row deep-links to `/index.php/apps/polls/vote/{id}`.\n\n Tier-2 surface (this commit):\n - \"Link existing poll\" → opens CnPollPicker (modal)\n - \"Create new poll\" → opens CnPollCreate (modal)\n - Per-row unlink → DELETE …/polls/{pollId}\n\n Talks to the OpenRegister Tier-2 poll-link endpoints\n GET /api/objects/{r}/{s}/{id}/polls — list\n POST /api/objects/{r}/{s}/{id}/polls — link existing\n POST /api/objects/{r}/{s}/{id}/polls/new — create + link\n DELETE /api/objects/{r}/{s}/{id}/polls/{pollId} — unlink\n GET /api/integrations/polls/available — picker source\n served by `OCA\\OpenRegister\\Controller\\PollLinksController`.\n\n Surface behaviour:\n - Empty state with \"Open Polls\" CTA when no linked polls.\n - Loading + 503 \"currently unavailable\" + generic error states match\n CnIntegrationTab's behaviour for AD-23 graceful degradation.\n\n See `openregister/openspec/changes/integration-polls/` for the spec\n delta and ADR-019 (registry mechanism).\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-polls-tab\">\n\t\t<div v-if=\"degraded\" class=\"cn-polls-tab__banner\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ degraded }}</span>\n\t\t</div>\n\n\t\t<div class=\"cn-polls-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 poll') }}\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 poll') }}\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-polls-tab__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\t\t<div v-else-if=\"polls.length === 0\" class=\"cn-sidebar-tab__empty cn-polls-tab__empty\">\n\t\t\t<Poll :size=\"32\" class=\"cn-polls-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=\"pollsAppUrl\"\n\t\t\t\ttarget=\"_blank\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Poll :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ openPollsLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<ul v-else class=\"cn-polls-tab__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"poll in polls\"\n\t\t\t\t:key=\"pollKey(poll)\"\n\t\t\t\tclass=\"cn-polls-tab__row\"\n\t\t\t\t:class=\"{ 'cn-polls-tab__row--closed': isClosed(poll) }\">\n\t\t\t\t<div class=\"cn-polls-tab__row-header\">\n\t\t\t\t\t<span class=\"cn-polls-tab__row-icon\" :title=\"typeLabel(poll)\" :aria-label=\"typeLabel(poll)\">\n\t\t\t\t\t\t<CalendarRange v-if=\"isDatePoll(poll)\" :size=\"20\" />\n\t\t\t\t\t\t<FormatListChecks v-else :size=\"20\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<a\n\t\t\t\t\t\t:href=\"pollUrl(poll)\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__title\">{{ pollTitle(poll) }}</a>\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t:label=\"statusLabel(poll)\"\n\t\t\t\t\t\t:variant=\"statusVariant(poll)\"\n\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__status\" />\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"tertiary-no-background\"\n\t\t\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Unlink poll')\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__unlink\"\n\t\t\t\t\t\t@click=\"unlinkPoll(poll)\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<LinkOff :size=\"18\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\n\t\t\t\t<div class=\"cn-polls-tab__meta\">\n\t\t\t\t\t<span class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<AccountMultiple :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ voterMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<FormatListBulleted :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<span>{{ optionMetaLabel(poll) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"deadlineMs(poll) !== null\" class=\"cn-polls-tab__meta-item\">\n\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-polls-tab__meta-icon\" />\n\t\t\t\t\t\t<NcDateTime\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__time\"\n\t\t\t\t\t\t\t:timestamp=\"deadlineMs(poll)\"\n\t\t\t\t\t\t\trelativeTime=\"short\" />\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t<ul v-if=\"pollOptions(poll).length > 0\" class=\"cn-polls-tab__options\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"opt in pollOptions(poll)\"\n\t\t\t\t\t\t:key=\"optionKey(opt)\"\n\t\t\t\t\t\tclass=\"cn-polls-tab__option\">\n\t\t\t\t\t\t<div class=\"cn-polls-tab__option-label\">\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-text\">{{ optionLabel(opt) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-polls-tab__option-count\">\n\t\t\t\t\t\t\t\t{{ optionCount(opt) }} ({{ optionPercent(poll, opt) }}%)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar\"\n\t\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\t\t:aria-valuenow=\"optionPercent(poll, opt)\"\n\t\t\t\t\t\t\taria-valuemin=\"0\"\n\t\t\t\t\t\t\taria-valuemax=\"100\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"cn-polls-tab__option-bar-fill\"\n\t\t\t\t\t\t\t\t:style=\"{ width: optionPercent(poll, opt) + '%' }\" />\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=\"pollDescription(poll)\" class=\"cn-polls-tab__description\">\n\t\t\t\t\t{{ pollDescription(poll) }}\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnPollPicker\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<CnPollCreate\n\t\t\tv-if=\"createOpen\"\n\t\t\t@close=\"createOpen = false\"\n\t\t\t@create=\"onCreatePick\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcDateTime, NcLoadingIcon } from '@nextcloud/vue'\nimport AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport CalendarRange from 'vue-material-design-icons/CalendarRange.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'\nimport FormatListChecks from 'vue-material-design-icons/FormatListChecks.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 Poll from 'vue-material-design-icons/Poll.vue'\nimport CnPollCreate from '../../../components/CnPollCreate/CnPollCreate.vue'\nimport CnPollPicker from '../../../components/CnPollPicker/CnPollPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'\nimport { stripMarker } from '../../utils/marker.js'\n\n/**\n * CnPollsTab — bespoke live-tally list for the `polls` integration.\n *\n * Renders rows pulled from the OR pluggable-integration endpoint that\n * mirror the real NC Polls list: type icon, bold question, open/closed\n * status chip, participant + option counts, a relative expiry, and\n * per-option vote-tally progress bars. Tier-2: includes link/create\n * modals and per-row unlink.\n */\nexport default {\n\tname: 'CnPollsTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcDateTime,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tAccountMultiple,\n\t\tAlertCircleOutline,\n\t\tCalendarRange,\n\t\tClockOutline,\n\t\tFormatListBulleted,\n\t\tFormatListChecks,\n\t\tLinkOff,\n\t\tLinkVariant,\n\t\tPlus,\n\t\tPoll,\n\t\tCnPollPicker,\n\t\tCnPollCreate,\n\t},\n\n\tprops: {\n\t\t/** Stable integration id (forwarded from the registry — always `'polls'`). */\n\t\tintegrationId: { type: String, default: 'polls' },\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 polls linked yet') },\n\t\t/** Pre-translated label for the \"Open in Polls\" CTA. */\n\t\topenPollsLabel: { type: String, default: () => t('nextcloud-vue', 'Open Polls') },\n\t\t/** Pre-translated banner when Polls is unavailable. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'NC Polls is currently unavailable.') },\n\t\t/** URL of the NC Polls app entry. */\n\t\tpollsAppUrl: { type: String, default: '/index.php/apps/polls' },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpolls: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: '',\n\t\t\tpickerOpen: false,\n\t\t\tcreateOpen: false,\n\t\t}\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler(id) {\n\t\t\tif (id) {\n\t\t\t\tthis.fetchPolls()\n\t\t\t}\n\t\t} },\n\n\t\tregister() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.fetchPolls()\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tbaseUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`)\n\t\t},\n\n\t\t/**\n\t\t * Base for the Tier-2 polls endpoints (link/new/destroy).\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tpollsEndpoint() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/polls`)\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.pickerOpen = true\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.createOpen = true\n\t\t},\n\n\t\tasync onLinkPick(payload) {\n\t\t\tthis.pickerOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.pollsEndpoint(), {\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.fetchPolls()\n\t\t\t\t} else if (response.status === 409) {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'This poll is already linked.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\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('[CnPollsTab] link failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not link poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync onCreatePick(payload) {\n\t\t\tthis.createOpen = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\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('[CnPollsTab] create failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not create poll.')\n\t\t\t}\n\t\t},\n\n\t\tasync unlinkPoll(poll) {\n\t\t\tconst pollId = this.pollKey(poll)\n\t\t\tif (!pollId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.pollsEndpoint()}/${pollId}`, {\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.fetchPolls()\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\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('[CnPollsTab] unlink failed', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink poll.')\n\t\t\t}\n\t\t},\n\n\t\tpollKey(poll) {\n\t\t\treturn poll.pollId ?? poll.id ?? poll.reference ?? ''\n\t\t},\n\n\t\tpollTitle(poll) {\n\t\t\tconst raw = poll.title ?? poll.pollTitle ?? poll.name ?? this.pollKey(poll)\n\t\t\t// Strip any legacy `[or:{uuid}]` marker from displayed title\n\t\t\t// via the shared helper (ADR-019).\n\t\t\treturn stripMarker(raw) || String(raw)\n\t\t},\n\n\t\tpollDescription(poll) {\n\t\t\treturn stripMarker(poll.description)\n\t\t},\n\n\t\tpollUrl(poll) {\n\t\t\tif (poll.url && safeHref(poll.url) !== '#') {\n\t\t\t\treturn poll.url\n\t\t\t}\n\t\t\tconst id = this.pollKey(poll)\n\t\t\treturn id ? `/index.php/apps/polls/vote/${id}` : this.pollsAppUrl\n\t\t},\n\n\t\tpollType(poll) {\n\t\t\treturn poll.type ?? poll.pollType ?? ''\n\t\t},\n\n\t\tisDatePoll(poll) {\n\t\t\tconst type = String(this.pollType(poll)).toLowerCase()\n\t\t\treturn type.indexOf('date') !== -1\n\t\t},\n\n\t\ttypeLabel(poll) {\n\t\t\treturn this.isDatePoll(poll)\n\t\t\t\t? t('nextcloud-vue', 'Date poll')\n\t\t\t\t: t('nextcloud-vue', 'Text poll')\n\t\t},\n\n\t\tpollOptions(poll) {\n\t\t\tconst opts = poll.options ?? poll.results ?? poll.optionResults ?? []\n\t\t\treturn Array.isArray(opts) ? opts : []\n\t\t},\n\n\t\toptionKey(opt) {\n\t\t\treturn opt.id ?? opt.key ?? opt.text ?? ''\n\t\t},\n\n\t\toptionLabel(opt) {\n\t\t\treturn opt.text ?? opt.label ?? opt.name ?? String(this.optionKey(opt))\n\t\t},\n\n\t\toptionCount(opt) {\n\t\t\tconst n = Number(opt.votes ?? opt.count ?? opt.voteCount ?? 0)\n\t\t\treturn Number.isFinite(n) ? n : 0\n\t\t},\n\n\t\toptionPercent(poll, opt) {\n\t\t\tconst total = this.totalVotes(poll)\n\t\t\tif (total <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.round((this.optionCount(opt) / total) * 100)\n\t\t},\n\n\t\ttotalVotes(poll) {\n\t\t\treturn this.pollOptions(poll).reduce((sum, o) => sum + this.optionCount(o), 0)\n\t\t},\n\n\t\tvoterCount(poll) {\n\t\t\tconst n = Number(poll.voterCount ?? poll.voters ?? poll.participants ?? 0)\n\t\t\treturn Number.isFinite(n) && n > 0 ? n : this.totalVotes(poll)\n\t\t},\n\n\t\tvoterMetaLabel(poll) {\n\t\t\tconst voters = this.voterCount(poll)\n\t\t\treturn t('nextcloud-vue', '{n} participants', { n: voters })\n\t\t},\n\n\t\toptionMetaLabel(poll) {\n\t\t\tconst count = this.pollOptions(poll).length\n\t\t\treturn t('nextcloud-vue', '{n} options', { n: count })\n\t\t},\n\n\t\tdeadline(poll) {\n\t\t\treturn poll.deadline ?? poll.expire ?? poll.expiresAt ?? null\n\t\t},\n\n\t\tdeadlineMs(poll) {\n\t\t\tconst v = this.deadline(poll)\n\t\t\tif (v === null || v === undefined || v === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Treat as unix seconds when it's small enough to be a 10-digit epoch.\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 ? null : parsed\n\t\t},\n\n\t\tisClosed(poll) {\n\t\t\tconst ms = this.deadlineMs(poll)\n\t\t\tif (ms === null) {\n\t\t\t\t// Without a deadline, fall back to a `closed` flag if present.\n\t\t\t\treturn poll.closed === true || poll.status === 'closed'\n\t\t\t}\n\t\t\treturn ms <= Date.now()\n\t\t},\n\n\t\tstatusLabel(poll) {\n\t\t\treturn this.isClosed(poll)\n\t\t\t\t? t('nextcloud-vue', 'Closed')\n\t\t\t\t: t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\tstatusVariant(poll) {\n\t\t\treturn this.isClosed(poll) ? 'default' : 'success'\n\t\t},\n\n\t\tasync fetchPolls() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.degraded = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(this.baseUrl(), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tconst rows = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t\tthis.polls = rows\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.degraded = this.unavailableLabel\n\t\t\t\t} else {\n\t\t\t\t\tthis.polls = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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('[CnPollsTab] failed to fetch polls', err)\n\t\t\t\tthis.polls = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load polls.')\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-polls-tab__actions {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n\tflex-wrap: wrap;\n}\n\n.cn-polls-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-polls-tab__error {\n\tcolor: var(--color-error);\n\tfont-size: 0.9em;\n\tmargin: 4px 0 8px;\n}\n\n.cn-polls-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-polls-tab__empty-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding: 10px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tborder-left: 3px solid var(--color-primary-element);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-polls-tab__row--closed {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.78;\n}\n\n.cn-polls-tab__row-header {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-polls-tab__row-icon {\n\tdisplay: inline-flex;\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__row--closed .cn-polls-tab__row-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__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\tfont-weight: 600;\n}\n\na.cn-polls-tab__title:hover {\n\ttext-decoration: underline;\n}\n\n.cn-polls-tab__status {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__unlink {\n\tflex-shrink: 0;\n}\n\n.cn-polls-tab__meta {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 12px;\n\tfont-size: 0.8em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n\n.cn-polls-tab__meta-item {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.cn-polls-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__time {\n\tfont-size: 1em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__options {\n\tlist-style: none;\n\tmargin: 4px 0 0;\n\tpadding: 0 0 0 28px;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-polls-tab__option {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-polls-tab__option-label {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 0.85em;\n}\n\n.cn-polls-tab__option-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--color-main-text);\n}\n\n.cn-polls-tab__option-count {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-polls-tab__option-bar {\n\theight: 6px;\n\tborder-radius: 3px;\n\tbackground: var(--color-background-dark);\n\toverflow: hidden;\n}\n\n.cn-polls-tab__option-bar-fill {\n\theight: 100%;\n\tbackground: var(--color-primary-element, #21468B);\n\ttransition: width 0.2s ease;\n}\n\n.cn-polls-tab__description {\n\tfont-size: 0.85em;\n\tcolor: var(--color-text-maxcontrast);\n\tpadding-left: 28px;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createElementVNode","_createTextVNode","_toDisplayString","_normalizeClass","_createBlock","_renderList"],"mappings":"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"}