@conduction/nextcloud-vue 2.142.0 → 2.144.0

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 (167) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +7 -2
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  6. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +27 -0
  7. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue3.js +2 -1
  12. package/dist/esm/components/CnAiCompanion/CnAiChatPanel.vue3.js.map +1 -1
  13. package/dist/esm/components/CnAiCompanion/CnAiInput.vue2.js +9 -0
  14. package/dist/esm/components/CnAiCompanion/CnAiInput.vue2.js.map +1 -1
  15. package/dist/esm/components/CnAiCompanion/CnAiInput.vue3.js +21 -22
  16. package/dist/esm/components/CnAiCompanion/CnAiInput.vue3.js.map +1 -1
  17. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  18. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +29 -1
  19. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  20. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js +28 -13
  21. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  22. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue.js +1 -1
  23. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
  24. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +2 -1
  25. package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
  26. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +1 -1
  27. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -1
  28. package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -1
  29. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  30. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  31. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  32. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  33. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  34. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +1 -1
  35. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  36. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  37. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  38. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
  39. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  40. package/dist/esm/components/CnEventsCard/CnEventsCard.vue.js +1 -1
  41. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js +9 -0
  42. package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js.map +1 -1
  43. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js +24 -13
  44. package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js.map +1 -1
  45. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  46. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +29 -3
  47. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +4 -2
  49. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +17 -4
  51. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  52. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +21 -1
  53. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +18 -2
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  57. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  58. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +3 -0
  59. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  60. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js +19 -14
  61. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  62. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
  63. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +30 -0
  64. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
  65. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js +158 -102
  66. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
  67. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue2.js +6 -1
  68. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue2.js.map +1 -1
  69. package/dist/esm/components/CnPageHeader/CnHeaderTrail.vue3.js.map +1 -1
  70. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  71. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +12 -1
  72. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  73. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +2 -2
  74. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  75. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
  76. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +12 -0
  78. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
  79. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +1 -1
  80. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -1
  82. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue.js +1 -1
  83. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +110 -2
  84. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  85. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +146 -80
  86. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  87. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +1 -1
  88. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +4 -0
  89. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -1
  90. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +18 -8
  91. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -1
  92. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
  93. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +45 -0
  94. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
  95. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js +67 -34
  96. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
  97. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
  98. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +3 -22
  99. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
  100. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
  101. package/dist/esm/integrations/builtin/calendar/eventStatus.js +67 -0
  102. package/dist/esm/integrations/builtin/calendar/eventStatus.js.map +1 -0
  103. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue.js +1 -1
  104. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +39 -0
  105. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  106. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js +81 -32
  107. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  108. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
  109. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +5 -6
  110. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  111. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/deck/deckCardParts.js +59 -0
  113. package/dist/esm/integrations/builtin/deck/deckCardParts.js.map +1 -0
  114. package/dist/esm/l10n/en.json.js +6 -0
  115. package/dist/esm/l10n/en.json.js.map +1 -1
  116. package/dist/esm/l10n/nl.json.js +7 -1
  117. package/dist/esm/l10n/nl.json.js.map +1 -1
  118. package/dist/esm/library-style.js +1 -1
  119. package/dist/esm/nextcloud-vue.css +1264 -597
  120. package/dist/esm/utils/fetchFilterCounts.js +5 -2
  121. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  122. package/dist/esm/utils/nextcloudDirectory.js +19 -6
  123. package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
  124. package/dist/nextcloud-vue.cjs.js +1619 -849
  125. package/dist/nextcloud-vue.cjs.js.map +2 -2
  126. package/dist/nextcloud-vue.css +1264 -597
  127. package/l10n/en.json +6 -0
  128. package/l10n/nl.json +7 -1
  129. package/package.json +1 -1
  130. package/src/components/CnActionDialog/CnActionDialog.vue +28 -3
  131. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +28 -1
  132. package/src/components/CnAiCompanion/CnAiChatPanel.vue +12 -0
  133. package/src/components/CnAiCompanion/CnAiInput.vue +32 -1
  134. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +34 -4
  135. package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +1 -0
  136. package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +8 -0
  137. package/src/components/CnChoiceCards/CnChoiceCards.vue +1 -0
  138. package/src/components/CnConversationThread/CnConversationThread.vue +5 -2
  139. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +1 -0
  140. package/src/components/CnEventsCard/CnEventsCard.vue +27 -5
  141. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +43 -4
  142. package/src/components/CnFilesBrowser/filesBrowser.js +17 -4
  143. package/src/components/CnFilesBrowser/filesBrowserColumns.js +20 -1
  144. package/src/components/CnIndexPage/CnIndexPage.vue +18 -2
  145. package/src/components/CnNotesCard/CnNotesCard.vue +8 -0
  146. package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +114 -46
  147. package/src/components/CnPageHeader/CnHeaderTrail.vue +6 -1
  148. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +21 -1
  149. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +26 -1
  150. package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +6 -0
  151. package/src/components/CnTasksWidget/CnTasksWidget.vue +170 -13
  152. package/src/components/CnTimelineWidget/CnTimelineManage.vue +10 -1
  153. package/src/css/dialog.css +25 -1
  154. package/src/css/kpi-card.css +3 -0
  155. package/src/css/look-board-detail.css +348 -0
  156. package/src/css/look-board-index.css +56 -1
  157. package/src/css/look-board.css +98 -1
  158. package/src/integrations/builtin/calendar/CnCalendarCard.vue +76 -11
  159. package/src/integrations/builtin/calendar/CnCalendarTab.vue +3 -22
  160. package/src/integrations/builtin/calendar/__tests__/CnCalendarCardBoardRows.spec.js +52 -0
  161. package/src/integrations/builtin/calendar/eventStatus.js +63 -0
  162. package/src/integrations/builtin/deck/CnDeckCard.vue +66 -0
  163. package/src/integrations/builtin/deck/CnDeckTab.vue +5 -6
  164. package/src/integrations/builtin/deck/__tests__/CnDeckCardBoardRows.spec.js +49 -0
  165. package/src/integrations/builtin/deck/deckCardParts.js +56 -0
  166. package/src/utils/fetchFilterCounts.js +5 -2
  167. package/src/utils/nextcloudDirectory.js +19 -6
@@ -1 +1 @@
1
- {"version":3,"file":"CnCalendarTab.vue2.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarTab.vue"],"sourcesContent":["<!--\n CnCalendarTab — bespoke sidebar tab for the `calendar` integration.\n\n Tier-2 rewire (additive link-table strategy):\n - \"Add meeting\" button opens CnCalendarEventCreate modal that POSTs\n to /api/objects/{r}/{s}/{id}/events (writes X-OR-* on the VEVENT\n AND a link-table row).\n - \"Link existing meeting\" button opens CnCalendarEventPicker modal\n that POSTs to /api/objects/{r}/{s}/{id}/events/link (writes ONLY\n a link-table row — we may not own the VEVENT).\n - Per-row UNLINK action: DELETE /events/{eventUid}/link — removes\n the link only, the VEVENT survives on the user's calendar.\n - Per-row DELETE action: DELETE /events/{eventId} — destroys the\n VEVENT (legacy semantics — also cleans up the link-table row).\n\n Read path: GET /api/objects/{r}/{s}/{id}/events returns the UNION of\n link-table rows and the legacy X-OR-* CalDAV scan, deduped by\n (calendarUri, eventUid) and annotated with a `source` field\n (link-table / xor-only / both).\n\n Visual fidelity (NC Calendar): each row foregrounds a date block (month\n abbreviation + day-of-month, accented like the Calendar app's agenda),\n a start–end time range, a bold event title, the location with a\n map-marker icon, an attendee avatar stack with an overflow count, and a\n CnStatusBadge for the VEVENT status (confirmed / tentative / cancelled).\n\n ADR-004 modal isolation: every modal lives in its own .vue file under\n `src/components/CnCalendar*/` and is imported here as a child component.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-calendar-tab\">\n\t\t<!-- Action bar (replaces the inline create form) -->\n\t\t<div class=\"cn-calendar-tab__action-row\">\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ addEventLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Error banner -->\n\t\t<div v-if=\"error\" class=\"cn-calendar-tab__banner cn-calendar-tab__banner--error\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ error }}</span>\n\t\t</div>\n\n\t\t<!-- Unavailable banner (503) -->\n\t\t<div v-if=\"degraded\" class=\"cn-calendar-tab__banner\" role=\"status\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ unavailableLabel }}</span>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"sortedEvents.length === 0\" class=\"cn-sidebar-tab__empty cn-calendar-tab__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- Agenda -->\n\t\t<div v-else class=\"cn-calendar-tab__agenda\">\n\t\t\t<div v-if=\"upcomingEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title\">\n\t\t\t\t\t{{ upcomingLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in upcomingEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event\">\n\t\t\t\t\t<!-- Date block (month + day) -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__date\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Main column: title, time range, location, attendees -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Row actions -->\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"pastEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title cn-calendar-tab__group-title--past\">\n\t\t\t\t\t{{ pastLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in pastEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event cn-calendar-tab__event--past\">\n\t\t\t\t\t<div class=\"cn-calendar-tab__date cn-calendar-tab__date--past\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Picker modal (Tier-2 link existing flow) -->\n\t\t<CnCalendarEventPicker\n\t\t\tv-if=\"showPicker\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@link=\"onPickerLink\"\n\t\t\t@close=\"closePicker\" />\n\n\t\t<!-- Create modal (Tier-2 create-and-link flow) -->\n\t\t<CnCalendarEventCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@created=\"onEventCreated\"\n\t\t\t@close=\"closeCreate\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport {\n\tNcActionButton,\n\tNcActions,\n\tNcButton,\n\tNcLoadingIcon,\n} from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LinkVariantOff from 'vue-material-design-icons/LinkVariantOff.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCalendarEventCreate from '../../../components/CnCalendarEventCreate/CnCalendarEventCreate.vue'\nimport CnCalendarEventPicker from '../../../components/CnCalendarEventPicker/CnCalendarEventPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\n\nconst MAX_AVATARS = 3\n\n/**\n * CnCalendarTab — bespoke sidebar tab for the calendar integration.\n *\n * Tier-2 wiring: link/create flows are modal-driven (ADR-004 isolation),\n * and per-row actions distinguish UNLINK (preserves the VEVENT) from\n * DELETE (destroys the VEVENT). Rows mirror NC Calendar's agenda look:\n * date block, time range, title, location, attendee avatars + status.\n */\nexport default {\n\tname: 'CnCalendarTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tPlus,\n\t\tAlertCircleOutline,\n\t\tLinkVariant,\n\t\tLinkVariantOff,\n\t\tClockOutline,\n\t\tMapMarkerOutline,\n\t\tDelete,\n\t\tCnCalendarEventPicker,\n\t\tCnCalendarEventCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (matches PHP-side provider id, always `calendar`). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Label for the button that creates a meeting. */\n\t\taddEventLabel: { type: String, default: () => t('nextcloud-vue', 'Add meeting') },\n\t\t/** Label for the button that links an existing meeting. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing') },\n\t\t/** Label for the action that unlinks a meeting from the object. */\n\t\tunlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Unlink') },\n\t\t/** Label for the action that deletes a meeting. */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete meeting') },\n\t\t/** Empty state shown when no meeting is linked yet. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings linked yet') },\n\t\t/** Stand-in title for a meeting that has none. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** Heading above the meetings that are still to come. */\n\t\tupcomingLabel: { type: String, default: () => t('nextcloud-vue', 'Upcoming') },\n\t\t/** Heading above the meetings that already happened. */\n\t\tpastLabel: { type: String, default: () => t('nextcloud-vue', 'Past') },\n\t\t/** Message shown when the Calendar app cannot be reached. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'Nextcloud Calendar is currently unavailable.') },\n\t},\n\n\temits: ['linked', 'unlinked', 'deleted'],\n\n\tdata() {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: false,\n\t\t\trowBusyKey: null,\n\t\t\tshowPicker: false,\n\t\t\tshowCreate: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tsortedEvents() {\n\t\t\tconst copy = Array.isArray(this.events) ? [...this.events] : []\n\t\t\tcopy.sort((a, b) => {\n\t\t\t\tconst ta = a.dtstart ? new Date(a.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\tconst tb = b.dtstart ? new Date(b.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\treturn ta - tb\n\t\t\t})\n\t\t\treturn copy\n\t\t},\n\n\t\tupcomingEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t})\n\t\t},\n\n\t\tpastEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() < now\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchEvents()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseObjectUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}`)\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\t// Prefer eventUid (Tier-2 stable identifier across calendars).\n\t\t\treturn ev.uid || ev.id || ''\n\t\t},\n\n\t\tasync fetchEvents() {\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 = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.baseObjectUrl()}/events`, { 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.events = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\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('[CnCalendarTab] failed to fetch events', err)\n\t\t\t\tthis.events = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.showCreate = true\n\t\t},\n\n\t\tcloseCreate() {\n\t\t\tthis.showCreate = false\n\t\t},\n\n\t\tasync onEventCreated() {\n\t\t\tthis.closeCreate()\n\t\t\tawait this.fetchEvents()\n\t\t\tthis.$emit('linked')\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.showPicker = true\n\t\t},\n\n\t\tclosePicker() {\n\t\t\tthis.showPicker = false\n\t\t},\n\n\t\tasync onPickerLink(payload) {\n\t\t\t// payload: { calendarUri, eventUid }\n\t\t\tthis.closePicker()\n\t\t\tthis.error = ''\n\t\t\tconst result = await linkCalendarEvent(`${this.baseObjectUrl()}/events`, payload)\n\t\t\tif (result.ok) {\n\t\t\t\tawait this.fetchEvents()\n\t\t\t\tthis.$emit('linked', payload)\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tasync unlink(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tif (this.rowBusyKey || !key) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Tier-2 unlink-only endpoint: DELETE /events/{eventUid}/link\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(key)}/link`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('unlinked', key)\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\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('[CnCalendarTab] failed to unlink event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\tasync deleteEvent(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tconst eventUri = ev.id\n\t\t\tif (this.rowBusyKey || !eventUri) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Legacy destroy endpoint — destroys the VEVENT and cleans the link row.\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(eventUri)}`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('deleted', key)\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\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('[CnCalendarTab] failed to delete event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\t// --- Presentation helpers (NC Calendar agenda look) ---\n\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? null : d\n\t\t},\n\n\t\tmonthOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\treturn d.toLocaleDateString(userLocale(), { month: 'short' })\n\t\t},\n\n\t\tdayOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\treturn String(d.getDate())\n\t\t},\n\n\t\tformatTimeRange(ev) {\n\t\t\tconst start = this.parseDate(ev.dtstart)\n\t\t\tif (!start) {\n\t\t\t\treturn ev.dtstart ? String(ev.dtstart) : t('nextcloud-vue', 'Time not set')\n\t\t\t}\n\t\t\tconst end = this.parseDate(ev.dtend)\n\t\t\tconst dateLabel = start.toLocaleDateString(userLocale(), { weekday: 'short', day: 'numeric', month: 'short' })\n\t\t\tconst timeOpts = { hour: '2-digit', minute: '2-digit' }\n\t\t\tconst startTime = start.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\tif (end) {\n\t\t\t\tconst sameDay = start.toDateString() === end.toDateString()\n\t\t\t\tconst endTime = end.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\t\tif (sameDay) {\n\t\t\t\t\treturn `${dateLabel} · ${startTime} – ${endTime}`\n\t\t\t\t}\n\t\t\t\tconst endDateLabel = end.toLocaleDateString(userLocale(), { day: 'numeric', month: 'short' })\n\t\t\t\treturn `${dateLabel} ${startTime} – ${endDateLabel} ${endTime}`\n\t\t\t}\n\t\t\treturn `${dateLabel} · ${startTime}`\n\t\t},\n\n\t\tstatusLabel(ev) {\n\t\t\tconst status = (ev.status || '').toString().toLowerCase()\n\t\t\tif (status === 'confirmed') {\n\t\t\t\treturn t('nextcloud-vue', 'Confirmed')\n\t\t\t}\n\t\t\tif (status === 'tentative') {\n\t\t\t\treturn t('nextcloud-vue', 'Tentative')\n\t\t\t}\n\t\t\tif (status === 'cancelled') {\n\t\t\t\treturn t('nextcloud-vue', 'Cancelled')\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\tstatusVariant(ev) {\n\t\t\tconst status = (ev.status || '').toString().toLowerCase()\n\t\t\tif (status === 'confirmed') {\n\t\t\t\treturn 'success'\n\t\t\t}\n\t\t\tif (status === 'tentative') {\n\t\t\t\treturn 'warning'\n\t\t\t}\n\t\t\tif (status === 'cancelled') {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn 'default'\n\t\t},\n\n\t\tattendeeList(ev) {\n\t\t\tconst raw = ev.attendees || ev.participants || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn raw.map((att) => {\n\t\t\t\tif (typeof att === 'string') {\n\t\t\t\t\treturn { name: att, initials: this.initialsFor(att) }\n\t\t\t\t}\n\t\t\t\tconst name = att.name || att.displayName || att.cn || att.email || ''\n\t\t\t\treturn { name, initials: this.initialsFor(name) }\n\t\t\t}).filter((a) => a.name)\n\t\t},\n\n\t\tvisibleAttendees(ev) {\n\t\t\treturn this.attendeeList(ev).slice(0, MAX_AVATARS)\n\t\t},\n\n\t\textraAttendees(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn total > MAX_AVATARS ? total - MAX_AVATARS : 0\n\t\t},\n\n\t\tattendeeCountLabel(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn t('nextcloud-vue', '{count} attendees', { count: total })\n\t\t},\n\n\t\tinitialsFor(name) {\n\t\t\tconst clean = (name || '').trim()\n\t\t\tif (!clean) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\tconst local = clean.includes('@') ? clean.split('@')[0] : clean\n\t\t\tconst parts = local.split(/[\\s._-]+/).filter(Boolean)\n\t\t\tif (parts.length === 0) {\n\t\t\t\treturn clean.charAt(0).toUpperCase()\n\t\t\t}\n\t\t\tif (parts.length === 1) {\n\t\t\t\treturn parts[0].charAt(0).toUpperCase()\n\t\t\t}\n\t\t\treturn (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-calendar-tab__action-row {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 12px;\n}\n\n.cn-calendar-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-calendar-tab__banner--error {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-calendar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-tab__agenda {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n}\n\n.cn-calendar-tab__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-calendar-tab__group-title {\n\tmargin: 0 0 4px 0;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__group-title--past {\n\topacity: 0.7;\n}\n\n/* Event row */\n.cn-calendar-tab__event {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 8px;\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-calendar-tab__event + .cn-calendar-tab__event {\n\tmargin-top: 4px;\n}\n\n.cn-calendar-tab__event--past {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.72;\n}\n\n/* Date block */\n.cn-calendar-tab__date {\n\tflex-shrink: 0;\n\twidth: 44px;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 4px 0;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text, #fff);\n\tline-height: 1.1;\n}\n\n.cn-calendar-tab__date--past {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__date-month {\n\tfont-size: 10px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.05em;\n}\n\n.cn-calendar-tab__date-day {\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n/* Main column */\n.cn-calendar-tab__main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-calendar-tab__title-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__title {\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-tab__when,\n.cn-calendar-tab__location {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__location-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Attendees */\n.cn-calendar-tab__attendees {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-top: 2px;\n}\n\n.cn-calendar-tab__avatar {\n\twidth: 24px;\n\theight: 24px;\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-size: 10px;\n\tfont-weight: 600;\n\tbackground: var(--color-primary-element-light, var(--color-primary-light));\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tborder: 2px solid var(--color-main-background);\n\tmargin-left: -6px;\n\tflex-shrink: 0;\n}\n\n.cn-calendar-tab__avatar:first-child {\n\tmargin-left: 0;\n}\n\n.cn-calendar-tab__avatar--more {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Actions */\n.cn-calendar-tab__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["CnStatusBadge","CnCalendarEventPicker","CnCalendarEventCreate","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnCalendarTab.vue2.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarTab.vue"],"sourcesContent":["<!--\n CnCalendarTab — bespoke sidebar tab for the `calendar` integration.\n\n Tier-2 rewire (additive link-table strategy):\n - \"Add meeting\" button opens CnCalendarEventCreate modal that POSTs\n to /api/objects/{r}/{s}/{id}/events (writes X-OR-* on the VEVENT\n AND a link-table row).\n - \"Link existing meeting\" button opens CnCalendarEventPicker modal\n that POSTs to /api/objects/{r}/{s}/{id}/events/link (writes ONLY\n a link-table row — we may not own the VEVENT).\n - Per-row UNLINK action: DELETE /events/{eventUid}/link — removes\n the link only, the VEVENT survives on the user's calendar.\n - Per-row DELETE action: DELETE /events/{eventId} — destroys the\n VEVENT (legacy semantics — also cleans up the link-table row).\n\n Read path: GET /api/objects/{r}/{s}/{id}/events returns the UNION of\n link-table rows and the legacy X-OR-* CalDAV scan, deduped by\n (calendarUri, eventUid) and annotated with a `source` field\n (link-table / xor-only / both).\n\n Visual fidelity (NC Calendar): each row foregrounds a date block (month\n abbreviation + day-of-month, accented like the Calendar app's agenda),\n a start–end time range, a bold event title, the location with a\n map-marker icon, an attendee avatar stack with an overflow count, and a\n CnStatusBadge for the VEVENT status (confirmed / tentative / cancelled).\n\n ADR-004 modal isolation: every modal lives in its own .vue file under\n `src/components/CnCalendar*/` and is imported here as a child component.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-calendar-tab\">\n\t\t<!-- Action bar (replaces the inline create form) -->\n\t\t<div class=\"cn-calendar-tab__action-row\">\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ addEventLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Error banner -->\n\t\t<div v-if=\"error\" class=\"cn-calendar-tab__banner cn-calendar-tab__banner--error\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ error }}</span>\n\t\t</div>\n\n\t\t<!-- Unavailable banner (503) -->\n\t\t<div v-if=\"degraded\" class=\"cn-calendar-tab__banner\" role=\"status\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ unavailableLabel }}</span>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"sortedEvents.length === 0\" class=\"cn-sidebar-tab__empty cn-calendar-tab__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- Agenda -->\n\t\t<div v-else class=\"cn-calendar-tab__agenda\">\n\t\t\t<div v-if=\"upcomingEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title\">\n\t\t\t\t\t{{ upcomingLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in upcomingEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event\">\n\t\t\t\t\t<!-- Date block (month + day) -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__date\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Main column: title, time range, location, attendees -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Row actions -->\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"pastEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title cn-calendar-tab__group-title--past\">\n\t\t\t\t\t{{ pastLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in pastEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event cn-calendar-tab__event--past\">\n\t\t\t\t\t<div class=\"cn-calendar-tab__date cn-calendar-tab__date--past\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Picker modal (Tier-2 link existing flow) -->\n\t\t<CnCalendarEventPicker\n\t\t\tv-if=\"showPicker\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@link=\"onPickerLink\"\n\t\t\t@close=\"closePicker\" />\n\n\t\t<!-- Create modal (Tier-2 create-and-link flow) -->\n\t\t<CnCalendarEventCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@created=\"onEventCreated\"\n\t\t\t@close=\"closeCreate\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport {\n\tNcActionButton,\n\tNcActions,\n\tNcButton,\n\tNcLoadingIcon,\n} from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LinkVariantOff from 'vue-material-design-icons/LinkVariantOff.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCalendarEventCreate from '../../../components/CnCalendarEventCreate/CnCalendarEventCreate.vue'\nimport CnCalendarEventPicker from '../../../components/CnCalendarEventPicker/CnCalendarEventPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\nimport { eventStatusLabel, eventStatusVariant } from './eventStatus.js'\n\nconst MAX_AVATARS = 3\n\n/**\n * CnCalendarTab — bespoke sidebar tab for the calendar integration.\n *\n * Tier-2 wiring: link/create flows are modal-driven (ADR-004 isolation),\n * and per-row actions distinguish UNLINK (preserves the VEVENT) from\n * DELETE (destroys the VEVENT). Rows mirror NC Calendar's agenda look:\n * date block, time range, title, location, attendee avatars + status.\n */\nexport default {\n\tname: 'CnCalendarTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tPlus,\n\t\tAlertCircleOutline,\n\t\tLinkVariant,\n\t\tLinkVariantOff,\n\t\tClockOutline,\n\t\tMapMarkerOutline,\n\t\tDelete,\n\t\tCnCalendarEventPicker,\n\t\tCnCalendarEventCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (matches PHP-side provider id, always `calendar`). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Label for the button that creates a meeting. */\n\t\taddEventLabel: { type: String, default: () => t('nextcloud-vue', 'Add meeting') },\n\t\t/** Label for the button that links an existing meeting. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing') },\n\t\t/** Label for the action that unlinks a meeting from the object. */\n\t\tunlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Unlink') },\n\t\t/** Label for the action that deletes a meeting. */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete meeting') },\n\t\t/** Empty state shown when no meeting is linked yet. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings linked yet') },\n\t\t/** Stand-in title for a meeting that has none. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** Heading above the meetings that are still to come. */\n\t\tupcomingLabel: { type: String, default: () => t('nextcloud-vue', 'Upcoming') },\n\t\t/** Heading above the meetings that already happened. */\n\t\tpastLabel: { type: String, default: () => t('nextcloud-vue', 'Past') },\n\t\t/** Message shown when the Calendar app cannot be reached. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'Nextcloud Calendar is currently unavailable.') },\n\t},\n\n\temits: ['linked', 'unlinked', 'deleted'],\n\n\tdata() {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: false,\n\t\t\trowBusyKey: null,\n\t\t\tshowPicker: false,\n\t\t\tshowCreate: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tsortedEvents() {\n\t\t\tconst copy = Array.isArray(this.events) ? [...this.events] : []\n\t\t\tcopy.sort((a, b) => {\n\t\t\t\tconst ta = a.dtstart ? new Date(a.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\tconst tb = b.dtstart ? new Date(b.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\treturn ta - tb\n\t\t\t})\n\t\t\treturn copy\n\t\t},\n\n\t\tupcomingEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t})\n\t\t},\n\n\t\tpastEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() < now\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchEvents()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseObjectUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}`)\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\t// Prefer eventUid (Tier-2 stable identifier across calendars).\n\t\t\treturn ev.uid || ev.id || ''\n\t\t},\n\n\t\tasync fetchEvents() {\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 = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.baseObjectUrl()}/events`, { 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.events = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\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('[CnCalendarTab] failed to fetch events', err)\n\t\t\t\tthis.events = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.showCreate = true\n\t\t},\n\n\t\tcloseCreate() {\n\t\t\tthis.showCreate = false\n\t\t},\n\n\t\tasync onEventCreated() {\n\t\t\tthis.closeCreate()\n\t\t\tawait this.fetchEvents()\n\t\t\tthis.$emit('linked')\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.showPicker = true\n\t\t},\n\n\t\tclosePicker() {\n\t\t\tthis.showPicker = false\n\t\t},\n\n\t\tasync onPickerLink(payload) {\n\t\t\t// payload: { calendarUri, eventUid }\n\t\t\tthis.closePicker()\n\t\t\tthis.error = ''\n\t\t\tconst result = await linkCalendarEvent(`${this.baseObjectUrl()}/events`, payload)\n\t\t\tif (result.ok) {\n\t\t\t\tawait this.fetchEvents()\n\t\t\t\tthis.$emit('linked', payload)\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tasync unlink(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tif (this.rowBusyKey || !key) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Tier-2 unlink-only endpoint: DELETE /events/{eventUid}/link\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(key)}/link`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('unlinked', key)\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\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('[CnCalendarTab] failed to unlink event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\tasync deleteEvent(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tconst eventUri = ev.id\n\t\t\tif (this.rowBusyKey || !eventUri) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Legacy destroy endpoint — destroys the VEVENT and cleans the link row.\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(eventUri)}`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('deleted', key)\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\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('[CnCalendarTab] failed to delete event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\t// --- Presentation helpers (NC Calendar agenda look) ---\n\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? null : d\n\t\t},\n\n\t\tmonthOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\treturn d.toLocaleDateString(userLocale(), { month: 'short' })\n\t\t},\n\n\t\tdayOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\treturn String(d.getDate())\n\t\t},\n\n\t\tformatTimeRange(ev) {\n\t\t\tconst start = this.parseDate(ev.dtstart)\n\t\t\tif (!start) {\n\t\t\t\treturn ev.dtstart ? String(ev.dtstart) : t('nextcloud-vue', 'Time not set')\n\t\t\t}\n\t\t\tconst end = this.parseDate(ev.dtend)\n\t\t\tconst dateLabel = start.toLocaleDateString(userLocale(), { weekday: 'short', day: 'numeric', month: 'short' })\n\t\t\tconst timeOpts = { hour: '2-digit', minute: '2-digit' }\n\t\t\tconst startTime = start.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\tif (end) {\n\t\t\t\tconst sameDay = start.toDateString() === end.toDateString()\n\t\t\t\tconst endTime = end.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\t\tif (sameDay) {\n\t\t\t\t\treturn `${dateLabel} · ${startTime} – ${endTime}`\n\t\t\t\t}\n\t\t\t\tconst endDateLabel = end.toLocaleDateString(userLocale(), { day: 'numeric', month: 'short' })\n\t\t\t\treturn `${dateLabel} ${startTime} – ${endDateLabel} ${endTime}`\n\t\t\t}\n\t\t\treturn `${dateLabel} · ${startTime}`\n\t\t},\n\n\t\tstatusLabel(ev) {\n\t\t\treturn eventStatusLabel(ev)\n\t\t},\n\n\t\tstatusVariant(ev) {\n\t\t\treturn eventStatusVariant(ev)\n\t\t},\n\n\t\tattendeeList(ev) {\n\t\t\tconst raw = ev.attendees || ev.participants || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn raw.map((att) => {\n\t\t\t\tif (typeof att === 'string') {\n\t\t\t\t\treturn { name: att, initials: this.initialsFor(att) }\n\t\t\t\t}\n\t\t\t\tconst name = att.name || att.displayName || att.cn || att.email || ''\n\t\t\t\treturn { name, initials: this.initialsFor(name) }\n\t\t\t}).filter((a) => a.name)\n\t\t},\n\n\t\tvisibleAttendees(ev) {\n\t\t\treturn this.attendeeList(ev).slice(0, MAX_AVATARS)\n\t\t},\n\n\t\textraAttendees(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn total > MAX_AVATARS ? total - MAX_AVATARS : 0\n\t\t},\n\n\t\tattendeeCountLabel(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn t('nextcloud-vue', '{count} attendees', { count: total })\n\t\t},\n\n\t\tinitialsFor(name) {\n\t\t\tconst clean = (name || '').trim()\n\t\t\tif (!clean) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\tconst local = clean.includes('@') ? clean.split('@')[0] : clean\n\t\t\tconst parts = local.split(/[\\s._-]+/).filter(Boolean)\n\t\t\tif (parts.length === 0) {\n\t\t\t\treturn clean.charAt(0).toUpperCase()\n\t\t\t}\n\t\t\tif (parts.length === 1) {\n\t\t\t\treturn parts[0].charAt(0).toUpperCase()\n\t\t\t}\n\t\t\treturn (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-calendar-tab__action-row {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 12px;\n}\n\n.cn-calendar-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-calendar-tab__banner--error {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-calendar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-tab__agenda {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n}\n\n.cn-calendar-tab__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-calendar-tab__group-title {\n\tmargin: 0 0 4px 0;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__group-title--past {\n\topacity: 0.7;\n}\n\n/* Event row */\n.cn-calendar-tab__event {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 8px;\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-calendar-tab__event + .cn-calendar-tab__event {\n\tmargin-top: 4px;\n}\n\n.cn-calendar-tab__event--past {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.72;\n}\n\n/* Date block */\n.cn-calendar-tab__date {\n\tflex-shrink: 0;\n\twidth: 44px;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 4px 0;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text, #fff);\n\tline-height: 1.1;\n}\n\n.cn-calendar-tab__date--past {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__date-month {\n\tfont-size: 10px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.05em;\n}\n\n.cn-calendar-tab__date-day {\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n/* Main column */\n.cn-calendar-tab__main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-calendar-tab__title-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__title {\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-tab__when,\n.cn-calendar-tab__location {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__location-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Attendees */\n.cn-calendar-tab__attendees {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-top: 2px;\n}\n\n.cn-calendar-tab__avatar {\n\twidth: 24px;\n\theight: 24px;\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-size: 10px;\n\tfont-weight: 600;\n\tbackground: var(--color-primary-element-light, var(--color-primary-light));\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tborder: 2px solid var(--color-main-background);\n\tmargin-left: -6px;\n\tflex-shrink: 0;\n}\n\n.cn-calendar-tab__avatar:first-child {\n\tmargin-left: 0;\n}\n\n.cn-calendar-tab__avatar--more {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Actions */\n.cn-calendar-tab__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["CnStatusBadge","CnCalendarEventPicker","CnCalendarEventCreate","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnCalendarTab.vue3.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarTab.vue?vue&type=template&id=d6947736&lang.js"],"sourcesContent":["<!--\n CnCalendarTab — bespoke sidebar tab for the `calendar` integration.\n\n Tier-2 rewire (additive link-table strategy):\n - \"Add meeting\" button opens CnCalendarEventCreate modal that POSTs\n to /api/objects/{r}/{s}/{id}/events (writes X-OR-* on the VEVENT\n AND a link-table row).\n - \"Link existing meeting\" button opens CnCalendarEventPicker modal\n that POSTs to /api/objects/{r}/{s}/{id}/events/link (writes ONLY\n a link-table row — we may not own the VEVENT).\n - Per-row UNLINK action: DELETE /events/{eventUid}/link — removes\n the link only, the VEVENT survives on the user's calendar.\n - Per-row DELETE action: DELETE /events/{eventId} — destroys the\n VEVENT (legacy semantics — also cleans up the link-table row).\n\n Read path: GET /api/objects/{r}/{s}/{id}/events returns the UNION of\n link-table rows and the legacy X-OR-* CalDAV scan, deduped by\n (calendarUri, eventUid) and annotated with a `source` field\n (link-table / xor-only / both).\n\n Visual fidelity (NC Calendar): each row foregrounds a date block (month\n abbreviation + day-of-month, accented like the Calendar app's agenda),\n a start–end time range, a bold event title, the location with a\n map-marker icon, an attendee avatar stack with an overflow count, and a\n CnStatusBadge for the VEVENT status (confirmed / tentative / cancelled).\n\n ADR-004 modal isolation: every modal lives in its own .vue file under\n `src/components/CnCalendar*/` and is imported here as a child component.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-calendar-tab\">\n\t\t<!-- Action bar (replaces the inline create form) -->\n\t\t<div class=\"cn-calendar-tab__action-row\">\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ addEventLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Error banner -->\n\t\t<div v-if=\"error\" class=\"cn-calendar-tab__banner cn-calendar-tab__banner--error\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ error }}</span>\n\t\t</div>\n\n\t\t<!-- Unavailable banner (503) -->\n\t\t<div v-if=\"degraded\" class=\"cn-calendar-tab__banner\" role=\"status\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ unavailableLabel }}</span>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"sortedEvents.length === 0\" class=\"cn-sidebar-tab__empty cn-calendar-tab__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- Agenda -->\n\t\t<div v-else class=\"cn-calendar-tab__agenda\">\n\t\t\t<div v-if=\"upcomingEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title\">\n\t\t\t\t\t{{ upcomingLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in upcomingEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event\">\n\t\t\t\t\t<!-- Date block (month + day) -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__date\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Main column: title, time range, location, attendees -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Row actions -->\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"pastEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title cn-calendar-tab__group-title--past\">\n\t\t\t\t\t{{ pastLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in pastEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event cn-calendar-tab__event--past\">\n\t\t\t\t\t<div class=\"cn-calendar-tab__date cn-calendar-tab__date--past\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Picker modal (Tier-2 link existing flow) -->\n\t\t<CnCalendarEventPicker\n\t\t\tv-if=\"showPicker\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@link=\"onPickerLink\"\n\t\t\t@close=\"closePicker\" />\n\n\t\t<!-- Create modal (Tier-2 create-and-link flow) -->\n\t\t<CnCalendarEventCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@created=\"onEventCreated\"\n\t\t\t@close=\"closeCreate\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport {\n\tNcActionButton,\n\tNcActions,\n\tNcButton,\n\tNcLoadingIcon,\n} from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LinkVariantOff from 'vue-material-design-icons/LinkVariantOff.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCalendarEventCreate from '../../../components/CnCalendarEventCreate/CnCalendarEventCreate.vue'\nimport CnCalendarEventPicker from '../../../components/CnCalendarEventPicker/CnCalendarEventPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\n\nconst MAX_AVATARS = 3\n\n/**\n * CnCalendarTab — bespoke sidebar tab for the calendar integration.\n *\n * Tier-2 wiring: link/create flows are modal-driven (ADR-004 isolation),\n * and per-row actions distinguish UNLINK (preserves the VEVENT) from\n * DELETE (destroys the VEVENT). Rows mirror NC Calendar's agenda look:\n * date block, time range, title, location, attendee avatars + status.\n */\nexport default {\n\tname: 'CnCalendarTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tPlus,\n\t\tAlertCircleOutline,\n\t\tLinkVariant,\n\t\tLinkVariantOff,\n\t\tClockOutline,\n\t\tMapMarkerOutline,\n\t\tDelete,\n\t\tCnCalendarEventPicker,\n\t\tCnCalendarEventCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (matches PHP-side provider id, always `calendar`). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Label for the button that creates a meeting. */\n\t\taddEventLabel: { type: String, default: () => t('nextcloud-vue', 'Add meeting') },\n\t\t/** Label for the button that links an existing meeting. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing') },\n\t\t/** Label for the action that unlinks a meeting from the object. */\n\t\tunlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Unlink') },\n\t\t/** Label for the action that deletes a meeting. */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete meeting') },\n\t\t/** Empty state shown when no meeting is linked yet. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings linked yet') },\n\t\t/** Stand-in title for a meeting that has none. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** Heading above the meetings that are still to come. */\n\t\tupcomingLabel: { type: String, default: () => t('nextcloud-vue', 'Upcoming') },\n\t\t/** Heading above the meetings that already happened. */\n\t\tpastLabel: { type: String, default: () => t('nextcloud-vue', 'Past') },\n\t\t/** Message shown when the Calendar app cannot be reached. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'Nextcloud Calendar is currently unavailable.') },\n\t},\n\n\temits: ['linked', 'unlinked', 'deleted'],\n\n\tdata() {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: false,\n\t\t\trowBusyKey: null,\n\t\t\tshowPicker: false,\n\t\t\tshowCreate: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tsortedEvents() {\n\t\t\tconst copy = Array.isArray(this.events) ? [...this.events] : []\n\t\t\tcopy.sort((a, b) => {\n\t\t\t\tconst ta = a.dtstart ? new Date(a.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\tconst tb = b.dtstart ? new Date(b.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\treturn ta - tb\n\t\t\t})\n\t\t\treturn copy\n\t\t},\n\n\t\tupcomingEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t})\n\t\t},\n\n\t\tpastEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() < now\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchEvents()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseObjectUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}`)\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\t// Prefer eventUid (Tier-2 stable identifier across calendars).\n\t\t\treturn ev.uid || ev.id || ''\n\t\t},\n\n\t\tasync fetchEvents() {\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 = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.baseObjectUrl()}/events`, { 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.events = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\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('[CnCalendarTab] failed to fetch events', err)\n\t\t\t\tthis.events = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.showCreate = true\n\t\t},\n\n\t\tcloseCreate() {\n\t\t\tthis.showCreate = false\n\t\t},\n\n\t\tasync onEventCreated() {\n\t\t\tthis.closeCreate()\n\t\t\tawait this.fetchEvents()\n\t\t\tthis.$emit('linked')\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.showPicker = true\n\t\t},\n\n\t\tclosePicker() {\n\t\t\tthis.showPicker = false\n\t\t},\n\n\t\tasync onPickerLink(payload) {\n\t\t\t// payload: { calendarUri, eventUid }\n\t\t\tthis.closePicker()\n\t\t\tthis.error = ''\n\t\t\tconst result = await linkCalendarEvent(`${this.baseObjectUrl()}/events`, payload)\n\t\t\tif (result.ok) {\n\t\t\t\tawait this.fetchEvents()\n\t\t\t\tthis.$emit('linked', payload)\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tasync unlink(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tif (this.rowBusyKey || !key) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Tier-2 unlink-only endpoint: DELETE /events/{eventUid}/link\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(key)}/link`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('unlinked', key)\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\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('[CnCalendarTab] failed to unlink event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\tasync deleteEvent(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tconst eventUri = ev.id\n\t\t\tif (this.rowBusyKey || !eventUri) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Legacy destroy endpoint — destroys the VEVENT and cleans the link row.\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(eventUri)}`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('deleted', key)\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\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('[CnCalendarTab] failed to delete event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\t// --- Presentation helpers (NC Calendar agenda look) ---\n\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? null : d\n\t\t},\n\n\t\tmonthOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\treturn d.toLocaleDateString(userLocale(), { month: 'short' })\n\t\t},\n\n\t\tdayOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\treturn String(d.getDate())\n\t\t},\n\n\t\tformatTimeRange(ev) {\n\t\t\tconst start = this.parseDate(ev.dtstart)\n\t\t\tif (!start) {\n\t\t\t\treturn ev.dtstart ? String(ev.dtstart) : t('nextcloud-vue', 'Time not set')\n\t\t\t}\n\t\t\tconst end = this.parseDate(ev.dtend)\n\t\t\tconst dateLabel = start.toLocaleDateString(userLocale(), { weekday: 'short', day: 'numeric', month: 'short' })\n\t\t\tconst timeOpts = { hour: '2-digit', minute: '2-digit' }\n\t\t\tconst startTime = start.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\tif (end) {\n\t\t\t\tconst sameDay = start.toDateString() === end.toDateString()\n\t\t\t\tconst endTime = end.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\t\tif (sameDay) {\n\t\t\t\t\treturn `${dateLabel} · ${startTime} – ${endTime}`\n\t\t\t\t}\n\t\t\t\tconst endDateLabel = end.toLocaleDateString(userLocale(), { day: 'numeric', month: 'short' })\n\t\t\t\treturn `${dateLabel} ${startTime} – ${endDateLabel} ${endTime}`\n\t\t\t}\n\t\t\treturn `${dateLabel} · ${startTime}`\n\t\t},\n\n\t\tstatusLabel(ev) {\n\t\t\tconst status = (ev.status || '').toString().toLowerCase()\n\t\t\tif (status === 'confirmed') {\n\t\t\t\treturn t('nextcloud-vue', 'Confirmed')\n\t\t\t}\n\t\t\tif (status === 'tentative') {\n\t\t\t\treturn t('nextcloud-vue', 'Tentative')\n\t\t\t}\n\t\t\tif (status === 'cancelled') {\n\t\t\t\treturn t('nextcloud-vue', 'Cancelled')\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\tstatusVariant(ev) {\n\t\t\tconst status = (ev.status || '').toString().toLowerCase()\n\t\t\tif (status === 'confirmed') {\n\t\t\t\treturn 'success'\n\t\t\t}\n\t\t\tif (status === 'tentative') {\n\t\t\t\treturn 'warning'\n\t\t\t}\n\t\t\tif (status === 'cancelled') {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn 'default'\n\t\t},\n\n\t\tattendeeList(ev) {\n\t\t\tconst raw = ev.attendees || ev.participants || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn raw.map((att) => {\n\t\t\t\tif (typeof att === 'string') {\n\t\t\t\t\treturn { name: att, initials: this.initialsFor(att) }\n\t\t\t\t}\n\t\t\t\tconst name = att.name || att.displayName || att.cn || att.email || ''\n\t\t\t\treturn { name, initials: this.initialsFor(name) }\n\t\t\t}).filter((a) => a.name)\n\t\t},\n\n\t\tvisibleAttendees(ev) {\n\t\t\treturn this.attendeeList(ev).slice(0, MAX_AVATARS)\n\t\t},\n\n\t\textraAttendees(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn total > MAX_AVATARS ? total - MAX_AVATARS : 0\n\t\t},\n\n\t\tattendeeCountLabel(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn t('nextcloud-vue', '{count} attendees', { count: total })\n\t\t},\n\n\t\tinitialsFor(name) {\n\t\t\tconst clean = (name || '').trim()\n\t\t\tif (!clean) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\tconst local = clean.includes('@') ? clean.split('@')[0] : clean\n\t\t\tconst parts = local.split(/[\\s._-]+/).filter(Boolean)\n\t\t\tif (parts.length === 0) {\n\t\t\t\treturn clean.charAt(0).toUpperCase()\n\t\t\t}\n\t\t\tif (parts.length === 1) {\n\t\t\t\treturn parts[0].charAt(0).toUpperCase()\n\t\t\t}\n\t\t\treturn (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-calendar-tab__action-row {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 12px;\n}\n\n.cn-calendar-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-calendar-tab__banner--error {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-calendar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-tab__agenda {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n}\n\n.cn-calendar-tab__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-calendar-tab__group-title {\n\tmargin: 0 0 4px 0;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__group-title--past {\n\topacity: 0.7;\n}\n\n/* Event row */\n.cn-calendar-tab__event {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 8px;\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-calendar-tab__event + .cn-calendar-tab__event {\n\tmargin-top: 4px;\n}\n\n.cn-calendar-tab__event--past {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.72;\n}\n\n/* Date block */\n.cn-calendar-tab__date {\n\tflex-shrink: 0;\n\twidth: 44px;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 4px 0;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text, #fff);\n\tline-height: 1.1;\n}\n\n.cn-calendar-tab__date--past {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__date-month {\n\tfont-size: 10px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.05em;\n}\n\n.cn-calendar-tab__date-day {\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n/* Main column */\n.cn-calendar-tab__main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-calendar-tab__title-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__title {\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-tab__when,\n.cn-calendar-tab__location {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__location-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Attendees */\n.cn-calendar-tab__attendees {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-top: 2px;\n}\n\n.cn-calendar-tab__avatar {\n\twidth: 24px;\n\theight: 24px;\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-size: 10px;\n\tfont-weight: 600;\n\tbackground: var(--color-primary-element-light, var(--color-primary-light));\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tborder: 2px solid var(--color-main-background);\n\tmargin-left: -6px;\n\tflex-shrink: 0;\n}\n\n.cn-calendar-tab__avatar:first-child {\n\tmargin-left: 0;\n}\n\n.cn-calendar-tab__avatar--more {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Actions */\n.cn-calendar-tab__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["_createElementVNode","_createVNode","_createTextVNode","_openBlock","_createElementBlock","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnCalendarTab.vue3.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarTab.vue?vue&type=template&id=44e2340e&lang.js"],"sourcesContent":["<!--\n CnCalendarTab — bespoke sidebar tab for the `calendar` integration.\n\n Tier-2 rewire (additive link-table strategy):\n - \"Add meeting\" button opens CnCalendarEventCreate modal that POSTs\n to /api/objects/{r}/{s}/{id}/events (writes X-OR-* on the VEVENT\n AND a link-table row).\n - \"Link existing meeting\" button opens CnCalendarEventPicker modal\n that POSTs to /api/objects/{r}/{s}/{id}/events/link (writes ONLY\n a link-table row — we may not own the VEVENT).\n - Per-row UNLINK action: DELETE /events/{eventUid}/link — removes\n the link only, the VEVENT survives on the user's calendar.\n - Per-row DELETE action: DELETE /events/{eventId} — destroys the\n VEVENT (legacy semantics — also cleans up the link-table row).\n\n Read path: GET /api/objects/{r}/{s}/{id}/events returns the UNION of\n link-table rows and the legacy X-OR-* CalDAV scan, deduped by\n (calendarUri, eventUid) and annotated with a `source` field\n (link-table / xor-only / both).\n\n Visual fidelity (NC Calendar): each row foregrounds a date block (month\n abbreviation + day-of-month, accented like the Calendar app's agenda),\n a start–end time range, a bold event title, the location with a\n map-marker icon, an attendee avatar stack with an overflow count, and a\n CnStatusBadge for the VEVENT status (confirmed / tentative / cancelled).\n\n ADR-004 modal isolation: every modal lives in its own .vue file under\n `src/components/CnCalendar*/` and is imported here as a child component.\n-->\n<template>\n\t<div class=\"cn-sidebar-tab cn-calendar-tab\">\n\t\t<!-- Action bar (replaces the inline create form) -->\n\t\t<div class=\"cn-calendar-tab__action-row\">\n\t\t\t<NcButton variant=\"primary\" @click=\"openCreate\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ addEventLabel }}\n\t\t\t</NcButton>\n\t\t\t<NcButton variant=\"secondary\" @click=\"openPicker\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<LinkVariant :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ linkExistingLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Error banner -->\n\t\t<div v-if=\"error\" class=\"cn-calendar-tab__banner cn-calendar-tab__banner--error\" role=\"alert\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ error }}</span>\n\t\t</div>\n\n\t\t<!-- Unavailable banner (503) -->\n\t\t<div v-if=\"degraded\" class=\"cn-calendar-tab__banner\" role=\"status\">\n\t\t\t<AlertCircleOutline :size=\"18\" />\n\t\t\t<span>{{ unavailableLabel }}</span>\n\t\t</div>\n\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\t\t<div v-else-if=\"sortedEvents.length === 0\" class=\"cn-sidebar-tab__empty cn-calendar-tab__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- Agenda -->\n\t\t<div v-else class=\"cn-calendar-tab__agenda\">\n\t\t\t<div v-if=\"upcomingEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title\">\n\t\t\t\t\t{{ upcomingLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in upcomingEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event\">\n\t\t\t\t\t<!-- Date block (month + day) -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__date\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Main column: title, time range, location, attendees -->\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<!-- Row actions -->\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"pastEvents.length > 0\" class=\"cn-calendar-tab__group\">\n\t\t\t\t<h4 class=\"cn-calendar-tab__group-title cn-calendar-tab__group-title--past\">\n\t\t\t\t\t{{ pastLabel }}\n\t\t\t\t</h4>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"ev in pastEvents\"\n\t\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\t\tclass=\"cn-calendar-tab__event cn-calendar-tab__event--past\">\n\t\t\t\t\t<div class=\"cn-calendar-tab__date cn-calendar-tab__date--past\" aria-hidden=\"true\">\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-month\">{{ monthOf(ev) }}</span>\n\t\t\t\t\t\t<span class=\"cn-calendar-tab__date-day\">{{ dayOf(ev) }}</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"cn-calendar-tab__main\">\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__title-row\">\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:label=\"statusLabel(ev)\"\n\t\t\t\t\t\t\t\t:variant=\"statusVariant(ev)\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div class=\"cn-calendar-tab__when\">\n\t\t\t\t\t\t\t<ClockOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span>{{ formatTimeRange(ev) }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"ev.location\" class=\"cn-calendar-tab__location\">\n\t\t\t\t\t\t\t<MapMarkerOutline :size=\"14\" class=\"cn-calendar-tab__meta-icon\" />\n\t\t\t\t\t\t\t<span class=\"cn-calendar-tab__location-text\">{{ ev.location }}</span>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div v-if=\"attendeeList(ev).length > 0\" class=\"cn-calendar-tab__attendees\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"(att, idx) in visibleAttendees(ev)\"\n\t\t\t\t\t\t\t\t:key=\"idx\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar\"\n\t\t\t\t\t\t\t\t:title=\"att.name\">\n\t\t\t\t\t\t\t\t{{ att.initials }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"extraAttendees(ev) > 0\"\n\t\t\t\t\t\t\t\tclass=\"cn-calendar-tab__avatar cn-calendar-tab__avatar--more\"\n\t\t\t\t\t\t\t\t:title=\"attendeeCountLabel(ev)\">\n\t\t\t\t\t\t\t\t+{{ extraAttendees(ev) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<NcActions :forceMenu=\"true\" class=\"cn-calendar-tab__actions\">\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"unlink(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<LinkVariantOff :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ unlinkLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<NcActionButton :disabled=\"rowBusyKey === rowKey(ev)\" @click=\"deleteEvent(ev)\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Picker modal (Tier-2 link existing flow) -->\n\t\t<CnCalendarEventPicker\n\t\t\tv-if=\"showPicker\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@link=\"onPickerLink\"\n\t\t\t@close=\"closePicker\" />\n\n\t\t<!-- Create modal (Tier-2 create-and-link flow) -->\n\t\t<CnCalendarEventCreate\n\t\t\tv-if=\"showCreate\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@created=\"onEventCreated\"\n\t\t\t@close=\"closeCreate\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport {\n\tNcActionButton,\n\tNcActions,\n\tNcButton,\n\tNcLoadingIcon,\n} from '@nextcloud/vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ClockOutline from 'vue-material-design-icons/ClockOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.vue'\nimport LinkVariantOff from 'vue-material-design-icons/LinkVariantOff.vue'\nimport MapMarkerOutline from 'vue-material-design-icons/MapMarkerOutline.vue'\nimport Plus from 'vue-material-design-icons/Plus.vue'\nimport CnCalendarEventCreate from '../../../components/CnCalendarEventCreate/CnCalendarEventCreate.vue'\nimport CnCalendarEventPicker from '../../../components/CnCalendarEventPicker/CnCalendarEventPicker.vue'\nimport CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\nimport { eventStatusLabel, eventStatusVariant } from './eventStatus.js'\n\nconst MAX_AVATARS = 3\n\n/**\n * CnCalendarTab — bespoke sidebar tab for the calendar integration.\n *\n * Tier-2 wiring: link/create flows are modal-driven (ADR-004 isolation),\n * and per-row actions distinguish UNLINK (preserves the VEVENT) from\n * DELETE (destroys the VEVENT). Rows mirror NC Calendar's agenda look:\n * date block, time range, title, location, attendee avatars + status.\n */\nexport default {\n\tname: 'CnCalendarTab',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcLoadingIcon,\n\t\tCnStatusBadge,\n\t\tPlus,\n\t\tAlertCircleOutline,\n\t\tLinkVariant,\n\t\tLinkVariantOff,\n\t\tClockOutline,\n\t\tMapMarkerOutline,\n\t\tDelete,\n\t\tCnCalendarEventPicker,\n\t\tCnCalendarEventCreate,\n\t},\n\n\tprops: {\n\t\t/* eslint-disable vue/no-unused-properties -- the integration dispatch binds integrationId on every integration component (see CnIntegrationWidgetGrid), so declaring it keeps it out of $attrs */\n\t\t/** Stable integration id (matches PHP-side provider id, always `calendar`). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/* eslint-enable vue/no-unused-properties */\n\t\t/** Parent object id. */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register id (slug or uuid). */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Label for the button that creates a meeting. */\n\t\taddEventLabel: { type: String, default: () => t('nextcloud-vue', 'Add meeting') },\n\t\t/** Label for the button that links an existing meeting. */\n\t\tlinkExistingLabel: { type: String, default: () => t('nextcloud-vue', 'Link existing') },\n\t\t/** Label for the action that unlinks a meeting from the object. */\n\t\tunlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Unlink') },\n\t\t/** Label for the action that deletes a meeting. */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete meeting') },\n\t\t/** Empty state shown when no meeting is linked yet. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings linked yet') },\n\t\t/** Stand-in title for a meeting that has none. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** Heading above the meetings that are still to come. */\n\t\tupcomingLabel: { type: String, default: () => t('nextcloud-vue', 'Upcoming') },\n\t\t/** Heading above the meetings that already happened. */\n\t\tpastLabel: { type: String, default: () => t('nextcloud-vue', 'Past') },\n\t\t/** Message shown when the Calendar app cannot be reached. */\n\t\tunavailableLabel: { type: String, default: () => t('nextcloud-vue', 'Nextcloud Calendar is currently unavailable.') },\n\t},\n\n\temits: ['linked', 'unlinked', 'deleted'],\n\n\tdata() {\n\t\treturn {\n\t\t\tevents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tdegraded: false,\n\t\t\trowBusyKey: null,\n\t\t\tshowPicker: false,\n\t\t\tshowCreate: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tsortedEvents() {\n\t\t\tconst copy = Array.isArray(this.events) ? [...this.events] : []\n\t\t\tcopy.sort((a, b) => {\n\t\t\t\tconst ta = a.dtstart ? new Date(a.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\tconst tb = b.dtstart ? new Date(b.dtstart).getTime() : Number.POSITIVE_INFINITY\n\t\t\t\treturn ta - tb\n\t\t\t})\n\t\t\treturn copy\n\t\t},\n\n\t\tupcomingEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t})\n\t\t},\n\n\t\tpastEvents() {\n\t\t\tconst now = Date.now()\n\t\t\treturn this.sortedEvents.filter((ev) => {\n\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn new Date(ev.dtstart).getTime() < now\n\t\t\t})\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchEvents()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tbaseObjectUrl() {\n\t\t\treturn prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}`)\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\t// Prefer eventUid (Tier-2 stable identifier across calendars).\n\t\t\treturn ev.uid || ev.id || ''\n\t\t},\n\n\t\tasync fetchEvents() {\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 = false\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`${this.baseObjectUrl()}/events`, { 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.events = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.events = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\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('[CnCalendarTab] failed to fetch events', err)\n\t\t\t\tthis.events = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meetings.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\topenCreate() {\n\t\t\tthis.showCreate = true\n\t\t},\n\n\t\tcloseCreate() {\n\t\t\tthis.showCreate = false\n\t\t},\n\n\t\tasync onEventCreated() {\n\t\t\tthis.closeCreate()\n\t\t\tawait this.fetchEvents()\n\t\t\tthis.$emit('linked')\n\t\t},\n\n\t\topenPicker() {\n\t\t\tthis.showPicker = true\n\t\t},\n\n\t\tclosePicker() {\n\t\t\tthis.showPicker = false\n\t\t},\n\n\t\tasync onPickerLink(payload) {\n\t\t\t// payload: { calendarUri, eventUid }\n\t\t\tthis.closePicker()\n\t\t\tthis.error = ''\n\t\t\tconst result = await linkCalendarEvent(`${this.baseObjectUrl()}/events`, payload)\n\t\t\tif (result.ok) {\n\t\t\t\tawait this.fetchEvents()\n\t\t\t\tthis.$emit('linked', payload)\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tasync unlink(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tif (this.rowBusyKey || !key) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Tier-2 unlink-only endpoint: DELETE /events/{eventUid}/link\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(key)}/link`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('unlinked', key)\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.degraded = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\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('[CnCalendarTab] failed to unlink event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not unlink the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\tasync deleteEvent(ev) {\n\t\t\tconst key = this.rowKey(ev)\n\t\t\tconst eventUri = ev.id\n\t\t\tif (this.rowBusyKey || !eventUri) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.rowBusyKey = key\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\t// Legacy destroy endpoint — destroys the VEVENT and cleans the link row.\n\t\t\t\tconst url = `${this.baseObjectUrl()}/events/${encodeURIComponent(eventUri)}`\n\t\t\t\tconst response = await fetch(url, { method: 'DELETE', headers: buildHeaders() })\n\t\t\t\tif (response.ok || response.status === 204) {\n\t\t\t\t\tthis.events = this.events.filter((row) => this.rowKey(row) !== key)\n\t\t\t\t\tthis.$emit('deleted', key)\n\t\t\t\t} else {\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\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('[CnCalendarTab] failed to delete event', err)\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not delete the meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.rowBusyKey = null\n\t\t\t}\n\t\t},\n\n\t\t// --- Presentation helpers (NC Calendar agenda look) ---\n\n\t\tparseDate(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst d = new Date(value)\n\t\t\treturn Number.isNaN(d.getTime()) ? null : d\n\t\t},\n\n\t\tmonthOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\treturn d.toLocaleDateString(userLocale(), { month: 'short' })\n\t\t},\n\n\t\tdayOf(ev) {\n\t\t\tconst d = this.parseDate(ev.dtstart)\n\t\t\tif (!d) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\treturn String(d.getDate())\n\t\t},\n\n\t\tformatTimeRange(ev) {\n\t\t\tconst start = this.parseDate(ev.dtstart)\n\t\t\tif (!start) {\n\t\t\t\treturn ev.dtstart ? String(ev.dtstart) : t('nextcloud-vue', 'Time not set')\n\t\t\t}\n\t\t\tconst end = this.parseDate(ev.dtend)\n\t\t\tconst dateLabel = start.toLocaleDateString(userLocale(), { weekday: 'short', day: 'numeric', month: 'short' })\n\t\t\tconst timeOpts = { hour: '2-digit', minute: '2-digit' }\n\t\t\tconst startTime = start.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\tif (end) {\n\t\t\t\tconst sameDay = start.toDateString() === end.toDateString()\n\t\t\t\tconst endTime = end.toLocaleTimeString(userLocale(), timeOpts)\n\t\t\t\tif (sameDay) {\n\t\t\t\t\treturn `${dateLabel} · ${startTime} – ${endTime}`\n\t\t\t\t}\n\t\t\t\tconst endDateLabel = end.toLocaleDateString(userLocale(), { day: 'numeric', month: 'short' })\n\t\t\t\treturn `${dateLabel} ${startTime} – ${endDateLabel} ${endTime}`\n\t\t\t}\n\t\t\treturn `${dateLabel} · ${startTime}`\n\t\t},\n\n\t\tstatusLabel(ev) {\n\t\t\treturn eventStatusLabel(ev)\n\t\t},\n\n\t\tstatusVariant(ev) {\n\t\t\treturn eventStatusVariant(ev)\n\t\t},\n\n\t\tattendeeList(ev) {\n\t\t\tconst raw = ev.attendees || ev.participants || []\n\t\t\tif (!Array.isArray(raw)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn raw.map((att) => {\n\t\t\t\tif (typeof att === 'string') {\n\t\t\t\t\treturn { name: att, initials: this.initialsFor(att) }\n\t\t\t\t}\n\t\t\t\tconst name = att.name || att.displayName || att.cn || att.email || ''\n\t\t\t\treturn { name, initials: this.initialsFor(name) }\n\t\t\t}).filter((a) => a.name)\n\t\t},\n\n\t\tvisibleAttendees(ev) {\n\t\t\treturn this.attendeeList(ev).slice(0, MAX_AVATARS)\n\t\t},\n\n\t\textraAttendees(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn total > MAX_AVATARS ? total - MAX_AVATARS : 0\n\t\t},\n\n\t\tattendeeCountLabel(ev) {\n\t\t\tconst total = this.attendeeList(ev).length\n\t\t\treturn t('nextcloud-vue', '{count} attendees', { count: total })\n\t\t},\n\n\t\tinitialsFor(name) {\n\t\t\tconst clean = (name || '').trim()\n\t\t\tif (!clean) {\n\t\t\t\treturn '?'\n\t\t\t}\n\t\t\tconst local = clean.includes('@') ? clean.split('@')[0] : clean\n\t\t\tconst parts = local.split(/[\\s._-]+/).filter(Boolean)\n\t\t\tif (parts.length === 0) {\n\t\t\t\treturn clean.charAt(0).toUpperCase()\n\t\t\t}\n\t\t\tif (parts.length === 1) {\n\t\t\t\treturn parts[0].charAt(0).toUpperCase()\n\t\t\t}\n\t\t\treturn (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-tab {\n\tpadding: 12px;\n\toverflow-x: hidden;\n}\n\n.cn-calendar-tab__action-row {\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 12px;\n}\n\n.cn-calendar-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-calendar-tab__banner--error {\n\tbackground: var(--color-error, #e9322d);\n}\n\n.cn-calendar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-tab__agenda {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 14px;\n}\n\n.cn-calendar-tab__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-calendar-tab__group-title {\n\tmargin: 0 0 4px 0;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__group-title--past {\n\topacity: 0.7;\n}\n\n/* Event row */\n.cn-calendar-tab__event {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 8px;\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-calendar-tab__event + .cn-calendar-tab__event {\n\tmargin-top: 4px;\n}\n\n.cn-calendar-tab__event--past {\n\tborder-left-color: var(--color-border-dark, var(--color-border));\n\tbackground: transparent;\n\topacity: 0.72;\n}\n\n/* Date block */\n.cn-calendar-tab__date {\n\tflex-shrink: 0;\n\twidth: 44px;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 4px 0;\n\tborder-radius: var(--border-radius);\n\tbackground: var(--color-primary-element);\n\tcolor: var(--color-primary-element-text, #fff);\n\tline-height: 1.1;\n}\n\n.cn-calendar-tab__date--past {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__date-month {\n\tfont-size: 10px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.05em;\n}\n\n.cn-calendar-tab__date-day {\n\tfont-size: 18px;\n\tfont-weight: 700;\n}\n\n/* Main column */\n.cn-calendar-tab__main {\n\tflex: 1;\n\tmin-width: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n}\n\n.cn-calendar-tab__title-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__title {\n\tfont-size: 14px;\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-tab__when,\n.cn-calendar-tab__location {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 5px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmin-width: 0;\n}\n\n.cn-calendar-tab__meta-icon {\n\tflex-shrink: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-tab__location-text {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* Attendees */\n.cn-calendar-tab__attendees {\n\tdisplay: flex;\n\talign-items: center;\n\tmargin-top: 2px;\n}\n\n.cn-calendar-tab__avatar {\n\twidth: 24px;\n\theight: 24px;\n\tborder-radius: 50%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-size: 10px;\n\tfont-weight: 600;\n\tbackground: var(--color-primary-element-light, var(--color-primary-light));\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tborder: 2px solid var(--color-main-background);\n\tmargin-left: -6px;\n\tflex-shrink: 0;\n}\n\n.cn-calendar-tab__avatar:first-child {\n\tmargin-left: 0;\n}\n\n.cn-calendar-tab__avatar--more {\n\tbackground: var(--color-background-dark);\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* Actions */\n.cn-calendar-tab__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["_createElementVNode","_createVNode","_createTextVNode","_openBlock","_createElementBlock","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { translate } from '@nextcloud/l10n';
2
+
3
+ /**
4
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
5
+ * SPDX-License-Identifier: EUPL-1.2
6
+ *
7
+ * A calendar event's status as a label and a badge tone, shared by the
8
+ * calendar tab and the calendar card.
9
+ */
10
+
11
+
12
+ /**
13
+ * The event's status in words, or '' when it has none the reader needs.
14
+ *
15
+ * @param {object} ev The event.
16
+ * @return {string}
17
+ */
18
+ function eventStatusLabel(ev) {
19
+ const status = String(ev?.status || '').toLowerCase();
20
+ if (status === 'confirmed') {
21
+ return translate('nextcloud-vue', 'Confirmed')
22
+ }
23
+ if (status === 'tentative') {
24
+ return translate('nextcloud-vue', 'Tentative')
25
+ }
26
+ if (status === 'cancelled') {
27
+ return translate('nextcloud-vue', 'Cancelled')
28
+ }
29
+ return ''
30
+ }
31
+
32
+ /**
33
+ * The badge tone of the event's status.
34
+ *
35
+ * @param {object} ev The event.
36
+ * @return {string} `success`, `warning`, `error` or `default`.
37
+ */
38
+ function eventStatusVariant(ev) {
39
+ const status = String(ev?.status || '').toLowerCase();
40
+ if (status === 'confirmed') {
41
+ return 'success'
42
+ }
43
+ if (status === 'tentative') {
44
+ return 'warning'
45
+ }
46
+ if (status === 'cancelled') {
47
+ return 'error'
48
+ }
49
+ return 'default'
50
+ }
51
+
52
+ /**
53
+ * The names of the event's attendees.
54
+ *
55
+ * @param {object} ev The event.
56
+ * @return {Array<string>}
57
+ */
58
+ function eventAttendeeNames(ev) {
59
+ const raw = ev?.attendees || ev?.participants || [];
60
+ if (!Array.isArray(raw)) {
61
+ return []
62
+ }
63
+ return raw.map((att) => (typeof att === 'string' ? att : (att.name || att.displayName || att.cn || att.email || ''))).filter(Boolean)
64
+ }
65
+
66
+ export { eventAttendeeNames, eventStatusLabel, eventStatusVariant };
67
+ //# sourceMappingURL=eventStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventStatus.js","sources":["../../../../../src/integrations/builtin/calendar/eventStatus.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * A calendar event's status as a label and a badge tone, shared by the\n * calendar tab and the calendar card.\n */\n\nimport { translate as t } from '@nextcloud/l10n'\n\n/**\n * The event's status in words, or '' when it has none the reader needs.\n *\n * @param {object} ev The event.\n * @return {string}\n */\nexport function eventStatusLabel(ev) {\n\tconst status = String(ev?.status || '').toLowerCase()\n\tif (status === 'confirmed') {\n\t\treturn t('nextcloud-vue', 'Confirmed')\n\t}\n\tif (status === 'tentative') {\n\t\treturn t('nextcloud-vue', 'Tentative')\n\t}\n\tif (status === 'cancelled') {\n\t\treturn t('nextcloud-vue', 'Cancelled')\n\t}\n\treturn ''\n}\n\n/**\n * The badge tone of the event's status.\n *\n * @param {object} ev The event.\n * @return {string} `success`, `warning`, `error` or `default`.\n */\nexport function eventStatusVariant(ev) {\n\tconst status = String(ev?.status || '').toLowerCase()\n\tif (status === 'confirmed') {\n\t\treturn 'success'\n\t}\n\tif (status === 'tentative') {\n\t\treturn 'warning'\n\t}\n\tif (status === 'cancelled') {\n\t\treturn 'error'\n\t}\n\treturn 'default'\n}\n\n/**\n * The names of the event's attendees.\n *\n * @param {object} ev The event.\n * @return {Array<string>}\n */\nexport function eventAttendeeNames(ev) {\n\tconst raw = ev?.attendees || ev?.participants || []\n\tif (!Array.isArray(raw)) {\n\t\treturn []\n\t}\n\treturn raw.map((att) => (typeof att === 'string' ? att : (att.name || att.displayName || att.cn || att.email || ''))).filter(Boolean)\n}\n"],"names":["t"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,EAAE,EAAE;AACrC,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,GAAE;AACtD,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE;AAC7B,EAAE,OAAOA,SAAC,CAAC,eAAe,EAAE,WAAW,CAAC;AACxC,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE;AAC7B,EAAE,OAAOA,SAAC,CAAC,eAAe,EAAE,WAAW,CAAC;AACxC,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE;AAC7B,EAAE,OAAOA,SAAC,CAAC,eAAe,EAAE,WAAW,CAAC;AACxC,EAAE;AACF,CAAC,OAAO,EAAE;AACV,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,EAAE,EAAE;AACvC,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,GAAE;AACtD,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE;AAC7B,EAAE,OAAO,SAAS;AAClB,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE;AAC7B,EAAE,OAAO,SAAS;AAClB,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE;AAC7B,EAAE,OAAO,OAAO;AAChB,EAAE;AACF,CAAC,OAAO,SAAS;AACjB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,EAAE,EAAE;AACvC,CAAC,MAAM,GAAG,GAAG,EAAE,EAAE,SAAS,IAAI,EAAE,EAAE,YAAY,IAAI,GAAE;AACpD,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;AAC1B,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,OAAO,GAAG,KAAK,QAAQ,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;AACtI;;;;"}
@@ -2,7 +2,7 @@ import script from './CnDeckCard.vue2.js';
2
2
  import { render } from './CnDeckCard.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-d8cff1c2";
5
+ script.__scopeId = "data-v-68f05db0";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -6,6 +6,8 @@ import ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue';
6
6
  import __sfc_main from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue.js';
7
7
  import __sfc_main$1 from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue.js';
8
8
  import __sfc_main$2 from '../../../components/CnDetailCard/CnDetailCard.vue.js';
9
+ import { normalizeLook } from '../../../composables/useLook.js';
10
+ import { cardBoardTitle, cardStackTitle, cardAssignees, assigneeDisplayName, cardLabels } from './deckCardParts.js';
9
11
  import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
10
12
  import { safeHref } from '../../../utils/safeHref.js';
11
13
 
@@ -22,6 +24,10 @@ var script = {
22
24
 
23
25
  components: { CnDeckCardCreate: __sfc_main, CnDeckCardPicker: __sfc_main$1, CnDetailCard: __sfc_main$2, LinkVariant, NcButton, NcLoadingIcon, Plus, ViewColumnOutline },
24
26
 
27
+ inject: {
28
+ cnLook: { default: 'nextcloud' },
29
+ },
30
+
25
31
  props: {
26
32
  /** Stable integration id (forwarded from the registry — always `'deck'`). */
27
33
  integrationId: { type: String, default: 'deck' },
@@ -64,6 +70,8 @@ var script = {
64
70
  linkLabel: { type: String, default: () => translate('nextcloud-vue', 'Link an existing card') },
65
71
  /** Label of the `create` button. */
66
72
  createLabel: { type: String, default: () => translate('nextcloud-vue', 'New card') },
73
+ /** Label of the link to Deck under the rows (board look). */
74
+ openDeckLabel: { type: String, default: () => translate('nextcloud-vue', 'Open Deck') },
67
75
  },
68
76
 
69
77
  data() {
@@ -92,6 +100,11 @@ var script = {
92
100
  return this.headerActions.filter((action, index, all) => ['create', 'link'].includes(action) && all.indexOf(action) === index)
93
101
  },
94
102
 
103
+ /** @return {boolean} The board look draws ruled rows instead of the mini-kanban. */
104
+ isBoard() {
105
+ return normalizeLook(this.cnLook) === 'board'
106
+ },
107
+
95
108
  cardHeaderTitle() {
96
109
  return this.title || this.integrationId
97
110
  },
@@ -186,6 +199,32 @@ var script = {
186
199
  },
187
200
 
188
201
  methods: {
202
+ /**
203
+ * A card's meta line in the board look: its board, its list and its
204
+ * assignees, separated by middots.
205
+ *
206
+ * @param {object} card The card row.
207
+ * @return {string}
208
+ */
209
+ boardMetaOf(card) {
210
+ const board = cardBoardTitle(card);
211
+ const stack = cardStackTitle(card);
212
+ return [
213
+ board ? translate('nextcloud-vue', 'Board {name}', { name: board }) : '',
214
+ stack ? translate('nextcloud-vue', 'list {name}', { name: stack }) : '',
215
+ ...cardAssignees(card).map(assigneeDisplayName),
216
+ ].filter(Boolean).join(' · ')
217
+ },
218
+
219
+ /**
220
+ * @param {object} card The card row.
221
+ * @return {string} Its first label's title, or ''.
222
+ */
223
+ firstLabel(card) {
224
+ const label = cardLabels(card)[0];
225
+ return label ? String(label.title ?? label.name ?? '') : ''
226
+ },
227
+
189
228
  /** @return {string} The object's Deck endpoint the link and create flows post to. */
190
229
  deckEndpoint() {
191
230
  return `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/deck`