@conduction/nextcloud-vue 2.129.0 → 2.130.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.
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +23 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +87 -4
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +29 -3
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +68 -14
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnEventsCard/CnEventsCard.vue.js +1 -1
- package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js +16 -5
- package/dist/esm/components/CnEventsCard/CnEventsCard.vue2.js.map +1 -1
- package/dist/esm/components/CnEventsCard/CnEventsCard.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesCard/CnFilesCard.vue.js +1 -1
- package/dist/esm/components/CnFilesCard/CnFilesCard.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesCard/CnFilesCard.vue3.js +11 -2
- package/dist/esm/components/CnFilesCard/CnFilesCard.vue3.js.map +1 -1
- package/dist/esm/components/CnIcon/semanticIcons.js +2 -0
- package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +32 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +3 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +62 -2
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +515 -356
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +6 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js +17 -9
- package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +323 -165
- package/dist/esm/utils/boardReferenceColumns.js +38 -1
- package/dist/esm/utils/boardReferenceColumns.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +989 -479
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +323 -165
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +67 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +59 -8
- package/src/components/CnEventsCard/CnEventsCard.vue +16 -5
- package/src/components/CnFilesCard/CnFilesCard.vue +7 -0
- package/src/components/CnIcon/semanticIcons.js +2 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +34 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +186 -8
- package/src/css/look-board-detail.css +27 -2
- package/src/css/look-board-index.css +50 -2
- package/src/css/look-board.css +52 -2
- package/src/integrations/builtin/calendar/CnCalendarCard.vue +12 -3
- package/src/integrations/builtin/calendar/__tests__/CnCalendarCardHeaderActions.spec.js +12 -1
- package/src/schemas/app-manifest-v2.schema.json +27 -3
- package/src/utils/boardReferenceColumns.js +37 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -2,7 +2,7 @@ import script from './CnCalendarCard.vue2.js';
|
|
|
2
2
|
import { render } from './CnCalendarCard.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-25d68a70";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -10,6 +10,7 @@ import Plus from 'vue-material-design-icons/Plus.vue';
|
|
|
10
10
|
import __sfc_main from '../../../components/CnCalendarEventCreate/CnCalendarEventCreate.vue.js';
|
|
11
11
|
import __sfc_main$1 from '../../../components/CnCalendarEventPicker/CnCalendarEventPicker.vue.js';
|
|
12
12
|
import __sfc_main$2 from '../../../components/CnDetailCard/CnDetailCard.vue.js';
|
|
13
|
+
import __sfc_main$3 from '../../../components/CnIcon/CnIcon.vue.js';
|
|
13
14
|
import { userLocale } from '../../../utils/userLocale.js';
|
|
14
15
|
import { linkCalendarEvent } from './calendarEventLink.js';
|
|
15
16
|
import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
@@ -22,7 +23,7 @@ import { prefixUrl, buildHeaders } from '../../../utils/headers.js';
|
|
|
22
23
|
var script = {
|
|
23
24
|
name: 'CnCalendarCard',
|
|
24
25
|
|
|
25
|
-
components: { CnCalendarEventCreate: __sfc_main, CnCalendarEventPicker: __sfc_main$1, CnDetailCard: __sfc_main$2, LinkVariant, NcButton, NcLoadingIcon, Plus, CalendarClock, CalendarCheck },
|
|
26
|
+
components: { CnCalendarEventCreate: __sfc_main, CnCalendarEventPicker: __sfc_main$1, CnDetailCard: __sfc_main$2, CnIcon: __sfc_main$3, LinkVariant, NcButton, NcLoadingIcon, Plus, CalendarClock, CalendarCheck },
|
|
26
27
|
|
|
27
28
|
props: {
|
|
28
29
|
/** OpenRegister register id (slug or uuid). Optional for user-dashboard. */
|
|
@@ -71,6 +72,10 @@ var script = {
|
|
|
71
72
|
headerActions: { type: Array, default: () => [] },
|
|
72
73
|
/** Label of the `create` button. */
|
|
73
74
|
createLabel: { type: String, default: () => translate('nextcloud-vue', 'Make an appointment') },
|
|
75
|
+
/** `secondary` draws the `create` button outlined instead of primary. */
|
|
76
|
+
createVariant: { type: String, default: 'primary' },
|
|
77
|
+
/** Icon name of the `create` button; a plus without it. */
|
|
78
|
+
createIcon: { type: String, default: '' },
|
|
74
79
|
/** Label of the `link` button. */
|
|
75
80
|
linkLabel: { type: String, default: () => translate('nextcloud-vue', 'Link an existing one') },
|
|
76
81
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCalendarCard.vue2.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarCard.vue"],"sourcesContent":["<!--\n CnCalendarCard — bespoke widget for the `calendar` integration.\n\n Surface-aware (AD-19) widget that renders linked VEVENTs across the\n four pluggable-integration-registry surfaces with one component:\n\n - `user-dashboard` : compact list, \"next 5 upcoming meetings\" across\n the user's objects (one row per VEVENT). Falls\n back to a list scoped by the optional\n register/schema/objectId when given (registry\n dashboards pass the full triple) so it still\n works without a global \"across all my objects\"\n query — see AD-22.\n - `app-dashboard` : same compact list scoped to the current app's\n register/schema/objectId, when given.\n - `detail-page` : full per-object meeting list with a quick\n \"Open in Calendar\" CTA — the inline create\n form lives in CnCalendarTab, not here, to keep\n the detail card compact (design.md AD-3).\n - `single-entity` : a single VEVENT rendered as a chip with date +\n summary + status icon. Accepts `entityId`\n (composite \"calendarId/eventUri\") for the\n referenceType auto-rendering path.\n\n Loading / empty / error states follow ADR-017 component composition.\n All strings pass through t('nextcloud-vue', ...) for nl + en\n translation (ADR-007). Theming uses Nextcloud CSS variables only so\n the nldesign app's overrides apply transparently (ADR-010).\n-->\n<template>\n\t<CnDetailCard :title=\"resolvedTitle\" :icon=\"Calendar\" :collapsible=\"collapsible\">\n\t\t<!-- `headerActions`: the calendar tab's create and link-existing flows in the title row (detail page). -->\n\t\t<template v-if=\"shownHeaderActions.length > 0\" #actions>\n\t\t\t<NcButton\n\t\t\t\tv-for=\"action in shownHeaderActions\"\n\t\t\t\t:key=\"action\"\n\t\t\t\t:variant=\"action === 'create' ? 'primary' : 'secondary'\"\n\t\t\t\t:data-testid=\"`cn-calendar-card-${action}`\"\n\t\t\t\t@click=\"action === 'create' ? (showCreate = true) : (showPicker = true)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus v-if=\"action === 'create'\" :size=\"20\" />\n\t\t\t\t\t<LinkVariant v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action === 'create' ? createLabel : linkLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\n\t\t<!-- Loading -->\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Error -->\n\t\t<div v-else-if=\"error\" class=\"cn-calendar-card__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<!-- Empty -->\n\t\t<div v-else-if=\"displayedEvents.length === 0\" class=\"cn-calendar-card__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- single-entity surface: one compact chip -->\n\t\t<div v-else-if=\"surface === 'single-entity'\" class=\"cn-calendar-card__chip\">\n\t\t\t<CalendarClock :size=\"18\" class=\"cn-calendar-card__chip-icon\" />\n\t\t\t<span class=\"cn-calendar-card__chip-title\">{{ displayedEvents[0].summary || untitledLabel }}</span>\n\t\t\t<span v-if=\"displayedEvents[0].dtstart\" class=\"cn-calendar-card__chip-when\">\n\t\t\t\t{{ formatShort(displayedEvents[0].dtstart) }}\n\t\t\t</span>\n\t\t\t<span\n\t\t\t\tv-if=\"displayedEvents[0].status\"\n\t\t\t\tclass=\"cn-calendar-card__chip-status\"\n\t\t\t\t:class=\"`cn-calendar-card__chip-status--${displayedEvents[0].status}`\">\n\t\t\t\t{{ displayedEvents[0].status }}\n\t\t\t</span>\n\t\t</div>\n\n\t\t<!-- list surfaces: user/app/detail share the row template -->\n\t\t<div v-else class=\"cn-calendar-card__list\">\n\t\t\t<div\n\t\t\t\tv-for=\"ev in displayedEvents\"\n\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\tclass=\"cn-calendar-card__row\">\n\t\t\t\t<div class=\"cn-calendar-card__row-icon\">\n\t\t\t\t\t<CalendarClock v-if=\"isUpcoming(ev)\" :size=\"20\" class=\"cn-calendar-card__icon--upcoming\" />\n\t\t\t\t\t<CalendarCheck v-else :size=\"20\" class=\"cn-calendar-card__icon--past\" />\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-calendar-card__row-main\">\n\t\t\t\t\t<span class=\"cn-calendar-card__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t<div class=\"cn-calendar-card__meta\">\n\t\t\t\t\t\t<span v-if=\"ev.dtstart\" class=\"cn-calendar-card__when\">{{ formatShort(ev.dtstart) }}</span>\n\t\t\t\t\t\t<span v-if=\"ev.location\" class=\"cn-calendar-card__location\">· {{ ev.location }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer: detail surface gets \"Open in NC Calendar\" CTA -->\n\t\t<template v-if=\"surface === 'detail-page'\" #footer>\n\t\t\t<a\n\t\t\t\tclass=\"cn-calendar-card__cta\"\n\t\t\t\t:href=\"calendarAppUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">\n\t\t\t\t{{ openInCalendarLabel }}\n\t\t\t</a>\n\t\t</template>\n\n\t\t<!-- Footer: dashboard surfaces get \"Show all\" when truncated -->\n\t\t<template v-else-if=\"allEvents.length > maxDisplay && surface !== 'single-entity'\" #footer>\n\t\t\t<button class=\"cn-calendar-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ allEvents.length }})\n\t\t\t</button>\n\t\t</template>\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=\"showPicker = false\" />\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=\"showCreate = false\" />\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport Calendar from 'vue-material-design-icons/Calendar.vue'\nimport CalendarCheck from 'vue-material-design-icons/CalendarCheck.vue'\nimport CalendarClock from 'vue-material-design-icons/CalendarClock.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.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 CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\n\n/**\n * CnCalendarCard — surface-aware compact widget for the calendar\n * integration. Renders linked VEVENTs across all four widget surfaces\n * (AD-19) with the same component.\n */\nexport default {\n\tname: 'CnCalendarCard',\n\n\tcomponents: { CnCalendarEventCreate, CnCalendarEventPicker, CnDetailCard, LinkVariant, NcButton, NcLoadingIcon, Plus, CalendarClock, CalendarCheck },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for user-dashboard. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for user-dashboard. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Optional for user-dashboard. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Stable integration id (matches PHP-side provider id). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/**\n\t\t * Composite entity id \"calendarId/eventUri\" for the\n\t\t * single-entity surface. Ignored on other surfaces.\n\t\t */\n\t\tentityId: { type: String, default: '' },\n\t\t/** Rendering surface per ADR-019 AD-19. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (v) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(v),\n\t\t},\n\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Maximum rows on list surfaces (ignored on single-entity). */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/** Whether the CnDetailCard collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Empty-state label. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings') },\n\t\t/** Fallback when a VEVENT has no summary. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** detail-page footer CTA label. */\n\t\topenInCalendarLabel: { type: String, default: () => t('nextcloud-vue', 'Open in Calendar') },\n\t\t/** dashboard \"show all\" footer label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/**\n\t\t * Buttons in the card's title row on a detail page: `create` makes a\n\t\t * meeting linked to the object, `link` links an existing one from the\n\t\t * user's calendar. In the order given; none by default.\n\t\t */\n\t\theaderActions: { type: Array, default: () => [] },\n\t\t/** Label of the `create` button. */\n\t\tcreateLabel: { type: String, default: () => t('nextcloud-vue', 'Make an appointment') },\n\t\t/** Label of the `link` button. */\n\t\tlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Link an existing one') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tCalendar: markRaw(Calendar),\n\t\t\tallEvents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tshowCreate: false,\n\t\t\tshowPicker: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The title-row buttons to draw: only on a detail page with an\n\t\t * object, only the known ones, in the order given.\n\t\t *\n\t\t * @return {Array<string>}\n\t\t */\n\t\tshownHeaderActions() {\n\t\t\tif (this.surface !== 'detail-page' || !this.objectId || !Array.isArray(this.headerActions)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.headerActions.filter((action, index, all) => ['create', 'link'].includes(action) && all.indexOf(action) === index)\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Meetings')\n\t\t},\n\n\t\tcalendarAppUrl() {\n\t\t\ttry {\n\t\t\t\treturn generateUrl('/apps/calendar')\n\t\t\t} catch {\n\t\t\t\treturn '/apps/calendar'\n\t\t\t}\n\t\t},\n\n\t\tdisplayedEvents() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\treturn this.allEvents.slice(0, 1)\n\t\t\t}\n\t\t\t// list surfaces sort by dtstart ascending, then cap at maxDisplay.\n\t\t\tconst sorted = [...this.allEvents].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\t// On dashboard surfaces, surface upcoming meetings first.\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tconst upcoming = sorted.filter((ev) => {\n\t\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t\t})\n\t\t\t\treturn upcoming.slice(0, this.maxDisplay)\n\t\t\t}\n\t\t\treturn sorted.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.refresh()\n\t\t} },\n\n\t\tentityId() {\n\t\t\tthis.refresh()\n\t\t},\n\n\t\tsurface() {\n\t\t\tthis.refresh()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A meeting was made on this object: close the form, read the list again.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEventCreated() {\n\t\t\tthis.showCreate = false\n\t\t\tthis.refresh()\n\t\t},\n\n\t\t/**\n\t\t * Link the picked meeting, then read the list again, or say why not.\n\t\t *\n\t\t * @param {{calendarUri: string, eventUid: string}} payload The picked event.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPickerLink(payload) {\n\t\t\tthis.showPicker = false\n\t\t\tconst result = await linkCalendarEvent(prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/events`), payload)\n\t\t\tif (result.ok) {\n\t\t\t\tthis.error = ''\n\t\t\t\tthis.refresh()\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\tif (ev.calendarId && ev.id) {\n\t\t\t\treturn `${ev.calendarId}/${ev.id}`\n\t\t\t}\n\t\t\treturn ev.id || ev.uid || ''\n\t\t},\n\n\t\tisUpcoming(ev) {\n\t\t\tif (!ev.dtstart) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= Date.now()\n\t\t\t} catch {\n\t\t\t\treturn true\n\t\t\t}\n\t\t},\n\n\t\trefresh() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingleEntity()\n\t\t\t} else {\n\t\t\t\tthis.fetchList()\n\t\t\t}\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.allEvents = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Calendar is currently unavailable.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\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('[CnCalendarCard] failed to fetch events', err)\n\t\t\t\tthis.allEvents = []\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\tasync fetchSingleEntity() {\n\t\t\tif (!this.entityId || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst url = `${this.baseUrl()}/${encodeURI(this.entityId)}`\n\t\t\t\tconst response = await fetch(prefixUrl(url), { 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.allEvents = data && (data.id || data.uid) ? [data] : []\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tif (response.status !== 404) {\n\t\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCalendarCard] failed to fetch single entity', err)\n\t\t\t\tthis.allEvents = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatShort(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst d = new Date(value)\n\t\t\t\tif (Number.isNaN(d.getTime())) {\n\t\t\t\t\treturn String(value)\n\t\t\t\t}\n\t\t\t\tconst opts = this.surface === 'single-entity'\n\t\t\t\t\t? { dateStyle: 'medium' }\n\t\t\t\t\t: { dateStyle: 'medium', timeStyle: 'short' }\n\t\t\t\treturn d.toLocaleString(userLocale(), opts)\n\t\t\t} catch {\n\t\t\t\treturn String(value)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-card__empty {\n\ttext-align: center;\n\tpadding: 16px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__error {\n\tcolor: var(--color-error);\n\tpadding: 8px 0;\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__list {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-calendar-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-calendar-card__row-icon {\n\tflex-shrink: 0;\n\tpadding-top: 1px;\n}\n\n.cn-calendar-card__icon--upcoming { color: var(--color-primary-element); }\n\n.cn-calendar-card__icon--past { color: var(--color-text-maxcontrast); }\n\n.cn-calendar-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-calendar-card__title {\n\tdisplay: block;\n\tfont-size: 13px;\n\tfont-weight: 500;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-card__meta {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmargin-top: 2px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-card__when {\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__location {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 10px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-hover);\n\tfont-size: 13px;\n\tmax-width: 100%;\n}\n\n.cn-calendar-card__chip-icon {\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-calendar-card__chip-title {\n\tfont-weight: 500;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-when {\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-status {\n\tfont-size: 11px;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tpadding: 1px 6px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-darker);\n}\n\n.cn-calendar-card__chip-status--confirmed { background: var(--color-success); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--tentative { background: var(--color-warning, #e9a40f); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--cancelled { background: var(--color-error); color: var(--color-main-background); }\n\n.cn-calendar-card__cta,\n.cn-calendar-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\tpadding: 0;\n\twidth: 100%;\n\ttext-align: center;\n\ttext-decoration: none;\n\tdisplay: inline-block;\n}\n\n.cn-calendar-card__cta:hover,\n.cn-calendar-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnCalendarEventCreate","CnCalendarEventPicker","CnDetailCard","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCalendarCard.vue2.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarCard.vue"],"sourcesContent":["<!--\n CnCalendarCard — bespoke widget for the `calendar` integration.\n\n Surface-aware (AD-19) widget that renders linked VEVENTs across the\n four pluggable-integration-registry surfaces with one component:\n\n - `user-dashboard` : compact list, \"next 5 upcoming meetings\" across\n the user's objects (one row per VEVENT). Falls\n back to a list scoped by the optional\n register/schema/objectId when given (registry\n dashboards pass the full triple) so it still\n works without a global \"across all my objects\"\n query — see AD-22.\n - `app-dashboard` : same compact list scoped to the current app's\n register/schema/objectId, when given.\n - `detail-page` : full per-object meeting list with a quick\n \"Open in Calendar\" CTA — the inline create\n form lives in CnCalendarTab, not here, to keep\n the detail card compact (design.md AD-3).\n - `single-entity` : a single VEVENT rendered as a chip with date +\n summary + status icon. Accepts `entityId`\n (composite \"calendarId/eventUri\") for the\n referenceType auto-rendering path.\n\n Loading / empty / error states follow ADR-017 component composition.\n All strings pass through t('nextcloud-vue', ...) for nl + en\n translation (ADR-007). Theming uses Nextcloud CSS variables only so\n the nldesign app's overrides apply transparently (ADR-010).\n-->\n<template>\n\t<CnDetailCard :title=\"resolvedTitle\" :icon=\"Calendar\" :collapsible=\"collapsible\">\n\t\t<!-- `headerActions`: the calendar tab's create and link-existing flows in the title row (detail page). -->\n\t\t<template v-if=\"shownHeaderActions.length > 0\" #actions>\n\t\t\t<NcButton\n\t\t\t\tv-for=\"action in shownHeaderActions\"\n\t\t\t\t:key=\"action\"\n\t\t\t\t:variant=\"action === 'create' && createVariant !== 'secondary' ? 'primary' : 'secondary'\"\n\t\t\t\t:data-testid=\"`cn-calendar-card-${action}`\"\n\t\t\t\t@click=\"action === 'create' ? (showCreate = true) : (showPicker = true)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CnIcon v-if=\"action === 'create' && createIcon\"\n\t\t\t\t\t\t:name=\"createIcon\"\n\t\t\t\t\t\t:size=\"20\"\n\t\t\t\t\t\tdata-testid=\"cn-calendar-card-create-icon\" />\n\t\t\t\t\t<Plus v-else-if=\"action === 'create'\" :size=\"20\" />\n\t\t\t\t\t<LinkVariant v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action === 'create' ? createLabel : linkLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\n\t\t<!-- Loading -->\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Error -->\n\t\t<div v-else-if=\"error\" class=\"cn-calendar-card__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<!-- Empty -->\n\t\t<div v-else-if=\"displayedEvents.length === 0\" class=\"cn-calendar-card__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- single-entity surface: one compact chip -->\n\t\t<div v-else-if=\"surface === 'single-entity'\" class=\"cn-calendar-card__chip\">\n\t\t\t<CalendarClock :size=\"18\" class=\"cn-calendar-card__chip-icon\" />\n\t\t\t<span class=\"cn-calendar-card__chip-title\">{{ displayedEvents[0].summary || untitledLabel }}</span>\n\t\t\t<span v-if=\"displayedEvents[0].dtstart\" class=\"cn-calendar-card__chip-when\">\n\t\t\t\t{{ formatShort(displayedEvents[0].dtstart) }}\n\t\t\t</span>\n\t\t\t<span\n\t\t\t\tv-if=\"displayedEvents[0].status\"\n\t\t\t\tclass=\"cn-calendar-card__chip-status\"\n\t\t\t\t:class=\"`cn-calendar-card__chip-status--${displayedEvents[0].status}`\">\n\t\t\t\t{{ displayedEvents[0].status }}\n\t\t\t</span>\n\t\t</div>\n\n\t\t<!-- list surfaces: user/app/detail share the row template -->\n\t\t<div v-else class=\"cn-calendar-card__list\">\n\t\t\t<div\n\t\t\t\tv-for=\"ev in displayedEvents\"\n\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\tclass=\"cn-calendar-card__row\">\n\t\t\t\t<div class=\"cn-calendar-card__row-icon\">\n\t\t\t\t\t<CalendarClock v-if=\"isUpcoming(ev)\" :size=\"20\" class=\"cn-calendar-card__icon--upcoming\" />\n\t\t\t\t\t<CalendarCheck v-else :size=\"20\" class=\"cn-calendar-card__icon--past\" />\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-calendar-card__row-main\">\n\t\t\t\t\t<span class=\"cn-calendar-card__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t<div class=\"cn-calendar-card__meta\">\n\t\t\t\t\t\t<span v-if=\"ev.dtstart\" class=\"cn-calendar-card__when\">{{ formatShort(ev.dtstart) }}</span>\n\t\t\t\t\t\t<span v-if=\"ev.location\" class=\"cn-calendar-card__location\">· {{ ev.location }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer: detail surface gets \"Open in NC Calendar\" CTA -->\n\t\t<template v-if=\"surface === 'detail-page'\" #footer>\n\t\t\t<a\n\t\t\t\tclass=\"cn-calendar-card__cta\"\n\t\t\t\t:href=\"calendarAppUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">\n\t\t\t\t{{ openInCalendarLabel }}\n\t\t\t</a>\n\t\t</template>\n\n\t\t<!-- Footer: dashboard surfaces get \"Show all\" when truncated -->\n\t\t<template v-else-if=\"allEvents.length > maxDisplay && surface !== 'single-entity'\" #footer>\n\t\t\t<button class=\"cn-calendar-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ allEvents.length }})\n\t\t\t</button>\n\t\t</template>\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=\"showPicker = false\" />\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=\"showCreate = false\" />\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport Calendar from 'vue-material-design-icons/Calendar.vue'\nimport CalendarCheck from 'vue-material-design-icons/CalendarCheck.vue'\nimport CalendarClock from 'vue-material-design-icons/CalendarClock.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.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 CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport CnIcon from '../../../components/CnIcon/CnIcon.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\n\n/**\n * CnCalendarCard — surface-aware compact widget for the calendar\n * integration. Renders linked VEVENTs across all four widget surfaces\n * (AD-19) with the same component.\n */\nexport default {\n\tname: 'CnCalendarCard',\n\n\tcomponents: { CnCalendarEventCreate, CnCalendarEventPicker, CnDetailCard, CnIcon, LinkVariant, NcButton, NcLoadingIcon, Plus, CalendarClock, CalendarCheck },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for user-dashboard. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for user-dashboard. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Optional for user-dashboard. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Stable integration id (matches PHP-side provider id). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/**\n\t\t * Composite entity id \"calendarId/eventUri\" for the\n\t\t * single-entity surface. Ignored on other surfaces.\n\t\t */\n\t\tentityId: { type: String, default: '' },\n\t\t/** Rendering surface per ADR-019 AD-19. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (v) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(v),\n\t\t},\n\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Maximum rows on list surfaces (ignored on single-entity). */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/** Whether the CnDetailCard collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Empty-state label. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings') },\n\t\t/** Fallback when a VEVENT has no summary. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** detail-page footer CTA label. */\n\t\topenInCalendarLabel: { type: String, default: () => t('nextcloud-vue', 'Open in Calendar') },\n\t\t/** dashboard \"show all\" footer label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/**\n\t\t * Buttons in the card's title row on a detail page: `create` makes a\n\t\t * meeting linked to the object, `link` links an existing one from the\n\t\t * user's calendar. In the order given; none by default.\n\t\t */\n\t\theaderActions: { type: Array, default: () => [] },\n\t\t/** Label of the `create` button. */\n\t\tcreateLabel: { type: String, default: () => t('nextcloud-vue', 'Make an appointment') },\n\t\t/** `secondary` draws the `create` button outlined instead of primary. */\n\t\tcreateVariant: { type: String, default: 'primary' },\n\t\t/** Icon name of the `create` button; a plus without it. */\n\t\tcreateIcon: { type: String, default: '' },\n\t\t/** Label of the `link` button. */\n\t\tlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Link an existing one') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tCalendar: markRaw(Calendar),\n\t\t\tallEvents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tshowCreate: false,\n\t\t\tshowPicker: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The title-row buttons to draw: only on a detail page with an\n\t\t * object, only the known ones, in the order given.\n\t\t *\n\t\t * @return {Array<string>}\n\t\t */\n\t\tshownHeaderActions() {\n\t\t\tif (this.surface !== 'detail-page' || !this.objectId || !Array.isArray(this.headerActions)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.headerActions.filter((action, index, all) => ['create', 'link'].includes(action) && all.indexOf(action) === index)\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Meetings')\n\t\t},\n\n\t\tcalendarAppUrl() {\n\t\t\ttry {\n\t\t\t\treturn generateUrl('/apps/calendar')\n\t\t\t} catch {\n\t\t\t\treturn '/apps/calendar'\n\t\t\t}\n\t\t},\n\n\t\tdisplayedEvents() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\treturn this.allEvents.slice(0, 1)\n\t\t\t}\n\t\t\t// list surfaces sort by dtstart ascending, then cap at maxDisplay.\n\t\t\tconst sorted = [...this.allEvents].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\t// On dashboard surfaces, surface upcoming meetings first.\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tconst upcoming = sorted.filter((ev) => {\n\t\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t\t})\n\t\t\t\treturn upcoming.slice(0, this.maxDisplay)\n\t\t\t}\n\t\t\treturn sorted.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.refresh()\n\t\t} },\n\n\t\tentityId() {\n\t\t\tthis.refresh()\n\t\t},\n\n\t\tsurface() {\n\t\t\tthis.refresh()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A meeting was made on this object: close the form, read the list again.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEventCreated() {\n\t\t\tthis.showCreate = false\n\t\t\tthis.refresh()\n\t\t},\n\n\t\t/**\n\t\t * Link the picked meeting, then read the list again, or say why not.\n\t\t *\n\t\t * @param {{calendarUri: string, eventUid: string}} payload The picked event.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPickerLink(payload) {\n\t\t\tthis.showPicker = false\n\t\t\tconst result = await linkCalendarEvent(prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/events`), payload)\n\t\t\tif (result.ok) {\n\t\t\t\tthis.error = ''\n\t\t\t\tthis.refresh()\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\tif (ev.calendarId && ev.id) {\n\t\t\t\treturn `${ev.calendarId}/${ev.id}`\n\t\t\t}\n\t\t\treturn ev.id || ev.uid || ''\n\t\t},\n\n\t\tisUpcoming(ev) {\n\t\t\tif (!ev.dtstart) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= Date.now()\n\t\t\t} catch {\n\t\t\t\treturn true\n\t\t\t}\n\t\t},\n\n\t\trefresh() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingleEntity()\n\t\t\t} else {\n\t\t\t\tthis.fetchList()\n\t\t\t}\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.allEvents = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Calendar is currently unavailable.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\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('[CnCalendarCard] failed to fetch events', err)\n\t\t\t\tthis.allEvents = []\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\tasync fetchSingleEntity() {\n\t\t\tif (!this.entityId || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst url = `${this.baseUrl()}/${encodeURI(this.entityId)}`\n\t\t\t\tconst response = await fetch(prefixUrl(url), { 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.allEvents = data && (data.id || data.uid) ? [data] : []\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tif (response.status !== 404) {\n\t\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCalendarCard] failed to fetch single entity', err)\n\t\t\t\tthis.allEvents = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatShort(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst d = new Date(value)\n\t\t\t\tif (Number.isNaN(d.getTime())) {\n\t\t\t\t\treturn String(value)\n\t\t\t\t}\n\t\t\t\tconst opts = this.surface === 'single-entity'\n\t\t\t\t\t? { dateStyle: 'medium' }\n\t\t\t\t\t: { dateStyle: 'medium', timeStyle: 'short' }\n\t\t\t\treturn d.toLocaleString(userLocale(), opts)\n\t\t\t} catch {\n\t\t\t\treturn String(value)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-card__empty {\n\ttext-align: center;\n\tpadding: 16px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__error {\n\tcolor: var(--color-error);\n\tpadding: 8px 0;\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__list {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-calendar-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-calendar-card__row-icon {\n\tflex-shrink: 0;\n\tpadding-top: 1px;\n}\n\n.cn-calendar-card__icon--upcoming { color: var(--color-primary-element); }\n\n.cn-calendar-card__icon--past { color: var(--color-text-maxcontrast); }\n\n.cn-calendar-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-calendar-card__title {\n\tdisplay: block;\n\tfont-size: 13px;\n\tfont-weight: 500;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-card__meta {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmargin-top: 2px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-card__when {\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__location {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 10px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-hover);\n\tfont-size: 13px;\n\tmax-width: 100%;\n}\n\n.cn-calendar-card__chip-icon {\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-calendar-card__chip-title {\n\tfont-weight: 500;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-when {\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-status {\n\tfont-size: 11px;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tpadding: 1px 6px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-darker);\n}\n\n.cn-calendar-card__chip-status--confirmed { background: var(--color-success); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--tentative { background: var(--color-warning, #e9a40f); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--cancelled { background: var(--color-error); color: var(--color-main-background); }\n\n.cn-calendar-card__cta,\n.cn-calendar-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\tpadding: 0;\n\twidth: 100%;\n\ttext-align: center;\n\ttext-decoration: none;\n\tdisplay: inline-block;\n}\n\n.cn-calendar-card__cta:hover,\n.cn-calendar-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["CnCalendarEventCreate","CnCalendarEventPicker","CnDetailCard","CnIcon","t"],"mappings":"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"}
|
|
@@ -37,6 +37,7 @@ const _hoisted_12 = {
|
|
|
37
37
|
const _hoisted_13 = ["href"];
|
|
38
38
|
|
|
39
39
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
40
|
+
const _component_CnIcon = resolveComponent("CnIcon");
|
|
40
41
|
const _component_Plus = resolveComponent("Plus");
|
|
41
42
|
const _component_LinkVariant = resolveComponent("LinkVariant");
|
|
42
43
|
const _component_NcButton = resolveComponent("NcButton");
|
|
@@ -138,20 +139,27 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
138
139
|
(openBlock(true), createElementBlock(Fragment, null, renderList($options.shownHeaderActions, (action) => {
|
|
139
140
|
return (openBlock(), createBlock(_component_NcButton, {
|
|
140
141
|
key: action,
|
|
141
|
-
variant: action === 'create' ? 'primary' : 'secondary',
|
|
142
|
+
variant: action === 'create' && $props.createVariant !== 'secondary' ? 'primary' : 'secondary',
|
|
142
143
|
"data-testid": `cn-calendar-card-${action}`,
|
|
143
144
|
onClick: $event => (action === 'create' ? ($data.showCreate = true) : ($data.showPicker = true))
|
|
144
145
|
}, {
|
|
145
146
|
icon: withCtx(() => [
|
|
146
|
-
(action === 'create')
|
|
147
|
-
? (openBlock(), createBlock(
|
|
147
|
+
(action === 'create' && $props.createIcon)
|
|
148
|
+
? (openBlock(), createBlock(_component_CnIcon, {
|
|
148
149
|
key: 0,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
name: $props.createIcon,
|
|
151
|
+
size: 20,
|
|
152
|
+
"data-testid": "cn-calendar-card-create-icon"
|
|
153
|
+
}, null, 8 /* PROPS */, ["name"]))
|
|
154
|
+
: (action === 'create')
|
|
155
|
+
? (openBlock(), createBlock(_component_Plus, {
|
|
156
|
+
key: 1,
|
|
157
|
+
size: 20
|
|
158
|
+
}))
|
|
159
|
+
: (openBlock(), createBlock(_component_LinkVariant, {
|
|
160
|
+
key: 2,
|
|
161
|
+
size: 20
|
|
162
|
+
}))
|
|
155
163
|
]),
|
|
156
164
|
default: withCtx(() => [
|
|
157
165
|
createTextVNode(" " + toDisplayString(action === 'create' ? $props.createLabel : $props.linkLabel), 1 /* TEXT */)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCalendarCard.vue3.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarCard.vue?vue&type=template&id=7e949157&lang.js"],"sourcesContent":["<!--\n CnCalendarCard — bespoke widget for the `calendar` integration.\n\n Surface-aware (AD-19) widget that renders linked VEVENTs across the\n four pluggable-integration-registry surfaces with one component:\n\n - `user-dashboard` : compact list, \"next 5 upcoming meetings\" across\n the user's objects (one row per VEVENT). Falls\n back to a list scoped by the optional\n register/schema/objectId when given (registry\n dashboards pass the full triple) so it still\n works without a global \"across all my objects\"\n query — see AD-22.\n - `app-dashboard` : same compact list scoped to the current app's\n register/schema/objectId, when given.\n - `detail-page` : full per-object meeting list with a quick\n \"Open in Calendar\" CTA — the inline create\n form lives in CnCalendarTab, not here, to keep\n the detail card compact (design.md AD-3).\n - `single-entity` : a single VEVENT rendered as a chip with date +\n summary + status icon. Accepts `entityId`\n (composite \"calendarId/eventUri\") for the\n referenceType auto-rendering path.\n\n Loading / empty / error states follow ADR-017 component composition.\n All strings pass through t('nextcloud-vue', ...) for nl + en\n translation (ADR-007). Theming uses Nextcloud CSS variables only so\n the nldesign app's overrides apply transparently (ADR-010).\n-->\n<template>\n\t<CnDetailCard :title=\"resolvedTitle\" :icon=\"Calendar\" :collapsible=\"collapsible\">\n\t\t<!-- `headerActions`: the calendar tab's create and link-existing flows in the title row (detail page). -->\n\t\t<template v-if=\"shownHeaderActions.length > 0\" #actions>\n\t\t\t<NcButton\n\t\t\t\tv-for=\"action in shownHeaderActions\"\n\t\t\t\t:key=\"action\"\n\t\t\t\t:variant=\"action === 'create' ? 'primary' : 'secondary'\"\n\t\t\t\t:data-testid=\"`cn-calendar-card-${action}`\"\n\t\t\t\t@click=\"action === 'create' ? (showCreate = true) : (showPicker = true)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<Plus v-if=\"action === 'create'\" :size=\"20\" />\n\t\t\t\t\t<LinkVariant v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action === 'create' ? createLabel : linkLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\n\t\t<!-- Loading -->\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Error -->\n\t\t<div v-else-if=\"error\" class=\"cn-calendar-card__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<!-- Empty -->\n\t\t<div v-else-if=\"displayedEvents.length === 0\" class=\"cn-calendar-card__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- single-entity surface: one compact chip -->\n\t\t<div v-else-if=\"surface === 'single-entity'\" class=\"cn-calendar-card__chip\">\n\t\t\t<CalendarClock :size=\"18\" class=\"cn-calendar-card__chip-icon\" />\n\t\t\t<span class=\"cn-calendar-card__chip-title\">{{ displayedEvents[0].summary || untitledLabel }}</span>\n\t\t\t<span v-if=\"displayedEvents[0].dtstart\" class=\"cn-calendar-card__chip-when\">\n\t\t\t\t{{ formatShort(displayedEvents[0].dtstart) }}\n\t\t\t</span>\n\t\t\t<span\n\t\t\t\tv-if=\"displayedEvents[0].status\"\n\t\t\t\tclass=\"cn-calendar-card__chip-status\"\n\t\t\t\t:class=\"`cn-calendar-card__chip-status--${displayedEvents[0].status}`\">\n\t\t\t\t{{ displayedEvents[0].status }}\n\t\t\t</span>\n\t\t</div>\n\n\t\t<!-- list surfaces: user/app/detail share the row template -->\n\t\t<div v-else class=\"cn-calendar-card__list\">\n\t\t\t<div\n\t\t\t\tv-for=\"ev in displayedEvents\"\n\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\tclass=\"cn-calendar-card__row\">\n\t\t\t\t<div class=\"cn-calendar-card__row-icon\">\n\t\t\t\t\t<CalendarClock v-if=\"isUpcoming(ev)\" :size=\"20\" class=\"cn-calendar-card__icon--upcoming\" />\n\t\t\t\t\t<CalendarCheck v-else :size=\"20\" class=\"cn-calendar-card__icon--past\" />\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-calendar-card__row-main\">\n\t\t\t\t\t<span class=\"cn-calendar-card__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t<div class=\"cn-calendar-card__meta\">\n\t\t\t\t\t\t<span v-if=\"ev.dtstart\" class=\"cn-calendar-card__when\">{{ formatShort(ev.dtstart) }}</span>\n\t\t\t\t\t\t<span v-if=\"ev.location\" class=\"cn-calendar-card__location\">· {{ ev.location }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer: detail surface gets \"Open in NC Calendar\" CTA -->\n\t\t<template v-if=\"surface === 'detail-page'\" #footer>\n\t\t\t<a\n\t\t\t\tclass=\"cn-calendar-card__cta\"\n\t\t\t\t:href=\"calendarAppUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">\n\t\t\t\t{{ openInCalendarLabel }}\n\t\t\t</a>\n\t\t</template>\n\n\t\t<!-- Footer: dashboard surfaces get \"Show all\" when truncated -->\n\t\t<template v-else-if=\"allEvents.length > maxDisplay && surface !== 'single-entity'\" #footer>\n\t\t\t<button class=\"cn-calendar-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ allEvents.length }})\n\t\t\t</button>\n\t\t</template>\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=\"showPicker = false\" />\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=\"showCreate = false\" />\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport Calendar from 'vue-material-design-icons/Calendar.vue'\nimport CalendarCheck from 'vue-material-design-icons/CalendarCheck.vue'\nimport CalendarClock from 'vue-material-design-icons/CalendarClock.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.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 CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\n\n/**\n * CnCalendarCard — surface-aware compact widget for the calendar\n * integration. Renders linked VEVENTs across all four widget surfaces\n * (AD-19) with the same component.\n */\nexport default {\n\tname: 'CnCalendarCard',\n\n\tcomponents: { CnCalendarEventCreate, CnCalendarEventPicker, CnDetailCard, LinkVariant, NcButton, NcLoadingIcon, Plus, CalendarClock, CalendarCheck },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for user-dashboard. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for user-dashboard. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Optional for user-dashboard. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Stable integration id (matches PHP-side provider id). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/**\n\t\t * Composite entity id \"calendarId/eventUri\" for the\n\t\t * single-entity surface. Ignored on other surfaces.\n\t\t */\n\t\tentityId: { type: String, default: '' },\n\t\t/** Rendering surface per ADR-019 AD-19. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (v) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(v),\n\t\t},\n\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Maximum rows on list surfaces (ignored on single-entity). */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/** Whether the CnDetailCard collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Empty-state label. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings') },\n\t\t/** Fallback when a VEVENT has no summary. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** detail-page footer CTA label. */\n\t\topenInCalendarLabel: { type: String, default: () => t('nextcloud-vue', 'Open in Calendar') },\n\t\t/** dashboard \"show all\" footer label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/**\n\t\t * Buttons in the card's title row on a detail page: `create` makes a\n\t\t * meeting linked to the object, `link` links an existing one from the\n\t\t * user's calendar. In the order given; none by default.\n\t\t */\n\t\theaderActions: { type: Array, default: () => [] },\n\t\t/** Label of the `create` button. */\n\t\tcreateLabel: { type: String, default: () => t('nextcloud-vue', 'Make an appointment') },\n\t\t/** Label of the `link` button. */\n\t\tlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Link an existing one') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tCalendar: markRaw(Calendar),\n\t\t\tallEvents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tshowCreate: false,\n\t\t\tshowPicker: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The title-row buttons to draw: only on a detail page with an\n\t\t * object, only the known ones, in the order given.\n\t\t *\n\t\t * @return {Array<string>}\n\t\t */\n\t\tshownHeaderActions() {\n\t\t\tif (this.surface !== 'detail-page' || !this.objectId || !Array.isArray(this.headerActions)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.headerActions.filter((action, index, all) => ['create', 'link'].includes(action) && all.indexOf(action) === index)\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Meetings')\n\t\t},\n\n\t\tcalendarAppUrl() {\n\t\t\ttry {\n\t\t\t\treturn generateUrl('/apps/calendar')\n\t\t\t} catch {\n\t\t\t\treturn '/apps/calendar'\n\t\t\t}\n\t\t},\n\n\t\tdisplayedEvents() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\treturn this.allEvents.slice(0, 1)\n\t\t\t}\n\t\t\t// list surfaces sort by dtstart ascending, then cap at maxDisplay.\n\t\t\tconst sorted = [...this.allEvents].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\t// On dashboard surfaces, surface upcoming meetings first.\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tconst upcoming = sorted.filter((ev) => {\n\t\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t\t})\n\t\t\t\treturn upcoming.slice(0, this.maxDisplay)\n\t\t\t}\n\t\t\treturn sorted.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.refresh()\n\t\t} },\n\n\t\tentityId() {\n\t\t\tthis.refresh()\n\t\t},\n\n\t\tsurface() {\n\t\t\tthis.refresh()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A meeting was made on this object: close the form, read the list again.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEventCreated() {\n\t\t\tthis.showCreate = false\n\t\t\tthis.refresh()\n\t\t},\n\n\t\t/**\n\t\t * Link the picked meeting, then read the list again, or say why not.\n\t\t *\n\t\t * @param {{calendarUri: string, eventUid: string}} payload The picked event.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPickerLink(payload) {\n\t\t\tthis.showPicker = false\n\t\t\tconst result = await linkCalendarEvent(prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/events`), payload)\n\t\t\tif (result.ok) {\n\t\t\t\tthis.error = ''\n\t\t\t\tthis.refresh()\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\tif (ev.calendarId && ev.id) {\n\t\t\t\treturn `${ev.calendarId}/${ev.id}`\n\t\t\t}\n\t\t\treturn ev.id || ev.uid || ''\n\t\t},\n\n\t\tisUpcoming(ev) {\n\t\t\tif (!ev.dtstart) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= Date.now()\n\t\t\t} catch {\n\t\t\t\treturn true\n\t\t\t}\n\t\t},\n\n\t\trefresh() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingleEntity()\n\t\t\t} else {\n\t\t\t\tthis.fetchList()\n\t\t\t}\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.allEvents = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Calendar is currently unavailable.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\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('[CnCalendarCard] failed to fetch events', err)\n\t\t\t\tthis.allEvents = []\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\tasync fetchSingleEntity() {\n\t\t\tif (!this.entityId || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst url = `${this.baseUrl()}/${encodeURI(this.entityId)}`\n\t\t\t\tconst response = await fetch(prefixUrl(url), { 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.allEvents = data && (data.id || data.uid) ? [data] : []\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tif (response.status !== 404) {\n\t\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCalendarCard] failed to fetch single entity', err)\n\t\t\t\tthis.allEvents = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatShort(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst d = new Date(value)\n\t\t\t\tif (Number.isNaN(d.getTime())) {\n\t\t\t\t\treturn String(value)\n\t\t\t\t}\n\t\t\t\tconst opts = this.surface === 'single-entity'\n\t\t\t\t\t? { dateStyle: 'medium' }\n\t\t\t\t\t: { dateStyle: 'medium', timeStyle: 'short' }\n\t\t\t\treturn d.toLocaleString(userLocale(), opts)\n\t\t\t} catch {\n\t\t\t\treturn String(value)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-card__empty {\n\ttext-align: center;\n\tpadding: 16px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__error {\n\tcolor: var(--color-error);\n\tpadding: 8px 0;\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__list {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-calendar-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-calendar-card__row-icon {\n\tflex-shrink: 0;\n\tpadding-top: 1px;\n}\n\n.cn-calendar-card__icon--upcoming { color: var(--color-primary-element); }\n\n.cn-calendar-card__icon--past { color: var(--color-text-maxcontrast); }\n\n.cn-calendar-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-calendar-card__title {\n\tdisplay: block;\n\tfont-size: 13px;\n\tfont-weight: 500;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-card__meta {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmargin-top: 2px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-card__when {\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__location {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 10px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-hover);\n\tfont-size: 13px;\n\tmax-width: 100%;\n}\n\n.cn-calendar-card__chip-icon {\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-calendar-card__chip-title {\n\tfont-weight: 500;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-when {\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-status {\n\tfont-size: 11px;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tpadding: 1px 6px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-darker);\n}\n\n.cn-calendar-card__chip-status--confirmed { background: var(--color-success); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--tentative { background: var(--color-warning, #e9a40f); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--cancelled { background: var(--color-error); color: var(--color-main-background); }\n\n.cn-calendar-card__cta,\n.cn-calendar-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\tpadding: 0;\n\twidth: 100%;\n\ttext-align: center;\n\ttext-decoration: none;\n\tdisplay: inline-block;\n}\n\n.cn-calendar-card__cta:hover,\n.cn-calendar-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_toDisplayString","_createElementBlock","_createElementVNode","_openBlock","_normalizeClass","_Fragment","_renderList","_createTextVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCalendarCard.vue3.js","sources":["../../../../../src/integrations/builtin/calendar/CnCalendarCard.vue?vue&type=template&id=25d68a70&lang.js"],"sourcesContent":["<!--\n CnCalendarCard — bespoke widget for the `calendar` integration.\n\n Surface-aware (AD-19) widget that renders linked VEVENTs across the\n four pluggable-integration-registry surfaces with one component:\n\n - `user-dashboard` : compact list, \"next 5 upcoming meetings\" across\n the user's objects (one row per VEVENT). Falls\n back to a list scoped by the optional\n register/schema/objectId when given (registry\n dashboards pass the full triple) so it still\n works without a global \"across all my objects\"\n query — see AD-22.\n - `app-dashboard` : same compact list scoped to the current app's\n register/schema/objectId, when given.\n - `detail-page` : full per-object meeting list with a quick\n \"Open in Calendar\" CTA — the inline create\n form lives in CnCalendarTab, not here, to keep\n the detail card compact (design.md AD-3).\n - `single-entity` : a single VEVENT rendered as a chip with date +\n summary + status icon. Accepts `entityId`\n (composite \"calendarId/eventUri\") for the\n referenceType auto-rendering path.\n\n Loading / empty / error states follow ADR-017 component composition.\n All strings pass through t('nextcloud-vue', ...) for nl + en\n translation (ADR-007). Theming uses Nextcloud CSS variables only so\n the nldesign app's overrides apply transparently (ADR-010).\n-->\n<template>\n\t<CnDetailCard :title=\"resolvedTitle\" :icon=\"Calendar\" :collapsible=\"collapsible\">\n\t\t<!-- `headerActions`: the calendar tab's create and link-existing flows in the title row (detail page). -->\n\t\t<template v-if=\"shownHeaderActions.length > 0\" #actions>\n\t\t\t<NcButton\n\t\t\t\tv-for=\"action in shownHeaderActions\"\n\t\t\t\t:key=\"action\"\n\t\t\t\t:variant=\"action === 'create' && createVariant !== 'secondary' ? 'primary' : 'secondary'\"\n\t\t\t\t:data-testid=\"`cn-calendar-card-${action}`\"\n\t\t\t\t@click=\"action === 'create' ? (showCreate = true) : (showPicker = true)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CnIcon v-if=\"action === 'create' && createIcon\"\n\t\t\t\t\t\t:name=\"createIcon\"\n\t\t\t\t\t\t:size=\"20\"\n\t\t\t\t\t\tdata-testid=\"cn-calendar-card-create-icon\" />\n\t\t\t\t\t<Plus v-else-if=\"action === 'create'\" :size=\"20\" />\n\t\t\t\t\t<LinkVariant v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action === 'create' ? createLabel : linkLabel }}\n\t\t\t</NcButton>\n\t\t</template>\n\n\t\t<!-- Loading -->\n\t\t<NcLoadingIcon v-if=\"loading\" />\n\n\t\t<!-- Error -->\n\t\t<div v-else-if=\"error\" class=\"cn-calendar-card__error\" role=\"alert\">\n\t\t\t{{ error }}\n\t\t</div>\n\n\t\t<!-- Empty -->\n\t\t<div v-else-if=\"displayedEvents.length === 0\" class=\"cn-calendar-card__empty\">\n\t\t\t{{ noEventsLabel }}\n\t\t</div>\n\n\t\t<!-- single-entity surface: one compact chip -->\n\t\t<div v-else-if=\"surface === 'single-entity'\" class=\"cn-calendar-card__chip\">\n\t\t\t<CalendarClock :size=\"18\" class=\"cn-calendar-card__chip-icon\" />\n\t\t\t<span class=\"cn-calendar-card__chip-title\">{{ displayedEvents[0].summary || untitledLabel }}</span>\n\t\t\t<span v-if=\"displayedEvents[0].dtstart\" class=\"cn-calendar-card__chip-when\">\n\t\t\t\t{{ formatShort(displayedEvents[0].dtstart) }}\n\t\t\t</span>\n\t\t\t<span\n\t\t\t\tv-if=\"displayedEvents[0].status\"\n\t\t\t\tclass=\"cn-calendar-card__chip-status\"\n\t\t\t\t:class=\"`cn-calendar-card__chip-status--${displayedEvents[0].status}`\">\n\t\t\t\t{{ displayedEvents[0].status }}\n\t\t\t</span>\n\t\t</div>\n\n\t\t<!-- list surfaces: user/app/detail share the row template -->\n\t\t<div v-else class=\"cn-calendar-card__list\">\n\t\t\t<div\n\t\t\t\tv-for=\"ev in displayedEvents\"\n\t\t\t\t:key=\"rowKey(ev)\"\n\t\t\t\tclass=\"cn-calendar-card__row\">\n\t\t\t\t<div class=\"cn-calendar-card__row-icon\">\n\t\t\t\t\t<CalendarClock v-if=\"isUpcoming(ev)\" :size=\"20\" class=\"cn-calendar-card__icon--upcoming\" />\n\t\t\t\t\t<CalendarCheck v-else :size=\"20\" class=\"cn-calendar-card__icon--past\" />\n\t\t\t\t</div>\n\t\t\t\t<div class=\"cn-calendar-card__row-main\">\n\t\t\t\t\t<span class=\"cn-calendar-card__title\">{{ ev.summary || untitledLabel }}</span>\n\t\t\t\t\t<div class=\"cn-calendar-card__meta\">\n\t\t\t\t\t\t<span v-if=\"ev.dtstart\" class=\"cn-calendar-card__when\">{{ formatShort(ev.dtstart) }}</span>\n\t\t\t\t\t\t<span v-if=\"ev.location\" class=\"cn-calendar-card__location\">· {{ ev.location }}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Footer: detail surface gets \"Open in NC Calendar\" CTA -->\n\t\t<template v-if=\"surface === 'detail-page'\" #footer>\n\t\t\t<a\n\t\t\t\tclass=\"cn-calendar-card__cta\"\n\t\t\t\t:href=\"calendarAppUrl\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\">\n\t\t\t\t{{ openInCalendarLabel }}\n\t\t\t</a>\n\t\t</template>\n\n\t\t<!-- Footer: dashboard surfaces get \"Show all\" when truncated -->\n\t\t<template v-else-if=\"allEvents.length > maxDisplay && surface !== 'single-entity'\" #footer>\n\t\t\t<button class=\"cn-calendar-card__show-all\" @click=\"$emit('show-all')\">\n\t\t\t\t{{ showAllLabel }} ({{ allEvents.length }})\n\t\t\t</button>\n\t\t</template>\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=\"showPicker = false\" />\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=\"showCreate = false\" />\n\t</CnDetailCard>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport { markRaw } from 'vue'\nimport Calendar from 'vue-material-design-icons/Calendar.vue'\nimport CalendarCheck from 'vue-material-design-icons/CalendarCheck.vue'\nimport CalendarClock from 'vue-material-design-icons/CalendarClock.vue'\nimport LinkVariant from 'vue-material-design-icons/LinkVariant.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 CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'\nimport CnIcon from '../../../components/CnIcon/CnIcon.vue'\nimport { buildHeaders, prefixUrl } from '../../../utils/index.js'\nimport { userLocale } from '../../../utils/userLocale.js'\nimport { linkCalendarEvent } from './calendarEventLink.js'\n\n/**\n * CnCalendarCard — surface-aware compact widget for the calendar\n * integration. Renders linked VEVENTs across all four widget surfaces\n * (AD-19) with the same component.\n */\nexport default {\n\tname: 'CnCalendarCard',\n\n\tcomponents: { CnCalendarEventCreate, CnCalendarEventPicker, CnDetailCard, CnIcon, LinkVariant, NcButton, NcLoadingIcon, Plus, CalendarClock, CalendarCheck },\n\n\tprops: {\n\t\t/** OpenRegister register id (slug or uuid). Optional for user-dashboard. */\n\t\tregister: { type: String, default: '' },\n\t\t/** OpenRegister schema id (slug or uuid). Optional for user-dashboard. */\n\t\tschema: { type: String, default: '' },\n\t\t/** Parent object id. Optional for user-dashboard. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Stable integration id (matches PHP-side provider id). */\n\t\tintegrationId: { type: String, default: 'calendar' },\n\t\t/**\n\t\t * Composite entity id \"calendarId/eventUri\" for the\n\t\t * single-entity surface. Ignored on other surfaces.\n\t\t */\n\t\tentityId: { type: String, default: '' },\n\t\t/** Rendering surface per ADR-019 AD-19. */\n\t\tsurface: {\n\t\t\ttype: String,\n\t\t\tdefault: 'detail-page',\n\t\t\tvalidator: (v) => ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'].includes(v),\n\t\t},\n\n\t\t/** Base API URL for OpenRegister. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Maximum rows on list surfaces (ignored on single-entity). */\n\t\tmaxDisplay: { type: Number, default: 5 },\n\t\t/** Whether the CnDetailCard collapses. */\n\t\tcollapsible: { type: Boolean, default: false },\n\t\t/** Override the card title (defaults to the translated label). */\n\t\ttitle: { type: String, default: '' },\n\n\t\t// --- Pre-translated labels (ADR-007) ---\n\t\t/** Empty-state label. */\n\t\tnoEventsLabel: { type: String, default: () => t('nextcloud-vue', 'No meetings') },\n\t\t/** Fallback when a VEVENT has no summary. */\n\t\tuntitledLabel: { type: String, default: () => t('nextcloud-vue', '(no title)') },\n\t\t/** detail-page footer CTA label. */\n\t\topenInCalendarLabel: { type: String, default: () => t('nextcloud-vue', 'Open in Calendar') },\n\t\t/** dashboard \"show all\" footer label. */\n\t\tshowAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },\n\t\t/**\n\t\t * Buttons in the card's title row on a detail page: `create` makes a\n\t\t * meeting linked to the object, `link` links an existing one from the\n\t\t * user's calendar. In the order given; none by default.\n\t\t */\n\t\theaderActions: { type: Array, default: () => [] },\n\t\t/** Label of the `create` button. */\n\t\tcreateLabel: { type: String, default: () => t('nextcloud-vue', 'Make an appointment') },\n\t\t/** `secondary` draws the `create` button outlined instead of primary. */\n\t\tcreateVariant: { type: String, default: 'primary' },\n\t\t/** Icon name of the `create` button; a plus without it. */\n\t\tcreateIcon: { type: String, default: '' },\n\t\t/** Label of the `link` button. */\n\t\tlinkLabel: { type: String, default: () => t('nextcloud-vue', 'Link an existing one') },\n\t},\n\n\temits: ['show-all'],\n\n\tdata() {\n\t\treturn {\n\t\t\tCalendar: markRaw(Calendar),\n\t\t\tallEvents: [],\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tshowCreate: false,\n\t\t\tshowPicker: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The title-row buttons to draw: only on a detail page with an\n\t\t * object, only the known ones, in the order given.\n\t\t *\n\t\t * @return {Array<string>}\n\t\t */\n\t\tshownHeaderActions() {\n\t\t\tif (this.surface !== 'detail-page' || !this.objectId || !Array.isArray(this.headerActions)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.headerActions.filter((action, index, all) => ['create', 'link'].includes(action) && all.indexOf(action) === index)\n\t\t},\n\n\t\tresolvedTitle() {\n\t\t\treturn this.title || t('nextcloud-vue', 'Meetings')\n\t\t},\n\n\t\tcalendarAppUrl() {\n\t\t\ttry {\n\t\t\t\treturn generateUrl('/apps/calendar')\n\t\t\t} catch {\n\t\t\t\treturn '/apps/calendar'\n\t\t\t}\n\t\t},\n\n\t\tdisplayedEvents() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\treturn this.allEvents.slice(0, 1)\n\t\t\t}\n\t\t\t// list surfaces sort by dtstart ascending, then cap at maxDisplay.\n\t\t\tconst sorted = [...this.allEvents].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\t// On dashboard surfaces, surface upcoming meetings first.\n\t\t\tif (this.surface === 'user-dashboard' || this.surface === 'app-dashboard') {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tconst upcoming = sorted.filter((ev) => {\n\t\t\t\t\tif (!ev.dtstart) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t\treturn new Date(ev.dtstart).getTime() >= now\n\t\t\t\t})\n\t\t\t\treturn upcoming.slice(0, this.maxDisplay)\n\t\t\t}\n\t\t\treturn sorted.slice(0, this.maxDisplay)\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: { immediate: true, handler() {\n\t\t\tthis.refresh()\n\t\t} },\n\n\t\tentityId() {\n\t\t\tthis.refresh()\n\t\t},\n\n\t\tsurface() {\n\t\t\tthis.refresh()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A meeting was made on this object: close the form, read the list again.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEventCreated() {\n\t\t\tthis.showCreate = false\n\t\t\tthis.refresh()\n\t\t},\n\n\t\t/**\n\t\t * Link the picked meeting, then read the list again, or say why not.\n\t\t *\n\t\t * @param {{calendarUri: string, eventUid: string}} payload The picked event.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPickerLink(payload) {\n\t\t\tthis.showPicker = false\n\t\t\tconst result = await linkCalendarEvent(prefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/events`), payload)\n\t\t\tif (result.ok) {\n\t\t\t\tthis.error = ''\n\t\t\t\tthis.refresh()\n\t\t\t} else {\n\t\t\t\tthis.error = result.error\n\t\t\t}\n\t\t},\n\n\t\tbaseUrl() {\n\t\t\treturn `${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/integrations/${this.integrationId}`\n\t\t},\n\n\t\trowKey(ev) {\n\t\t\tif (ev.calendarId && ev.id) {\n\t\t\t\treturn `${ev.calendarId}/${ev.id}`\n\t\t\t}\n\t\t\treturn ev.id || ev.uid || ''\n\t\t},\n\n\t\tisUpcoming(ev) {\n\t\t\tif (!ev.dtstart) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(ev.dtstart).getTime() >= Date.now()\n\t\t\t} catch {\n\t\t\t\treturn true\n\t\t\t}\n\t\t},\n\n\t\trefresh() {\n\t\t\tif (this.surface === 'single-entity') {\n\t\t\t\tthis.fetchSingleEntity()\n\t\t\t} else {\n\t\t\t\tthis.fetchList()\n\t\t\t}\n\t\t},\n\n\t\tasync fetchList() {\n\t\t\tif (!this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(prefixUrl(this.baseUrl()), { headers: buildHeaders() })\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.allEvents = data.results || data.items || (Array.isArray(data) ? data : []) || []\n\t\t\t\t} else if (response.status === 503) {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tthis.error = t('nextcloud-vue', 'Calendar is currently unavailable.')\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\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('[CnCalendarCard] failed to fetch events', err)\n\t\t\t\tthis.allEvents = []\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\tasync fetchSingleEntity() {\n\t\t\tif (!this.entityId || !this.register || !this.schema || !this.objectId) {\n\t\t\t\tthis.allEvents = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst url = `${this.baseUrl()}/${encodeURI(this.entityId)}`\n\t\t\t\tconst response = await fetch(prefixUrl(url), { 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.allEvents = data && (data.id || data.uid) ? [data] : []\n\t\t\t\t} else {\n\t\t\t\t\tthis.allEvents = []\n\t\t\t\t\tif (response.status !== 404) {\n\t\t\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('[CnCalendarCard] failed to fetch single entity', err)\n\t\t\t\tthis.allEvents = []\n\t\t\t\tthis.error = t('nextcloud-vue', 'Could not load meeting.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\tformatShort(value) {\n\t\t\tif (!value) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst d = new Date(value)\n\t\t\t\tif (Number.isNaN(d.getTime())) {\n\t\t\t\t\treturn String(value)\n\t\t\t\t}\n\t\t\t\tconst opts = this.surface === 'single-entity'\n\t\t\t\t\t? { dateStyle: 'medium' }\n\t\t\t\t\t: { dateStyle: 'medium', timeStyle: 'short' }\n\t\t\t\treturn d.toLocaleString(userLocale(), opts)\n\t\t\t} catch {\n\t\t\t\treturn String(value)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-calendar-card__empty {\n\ttext-align: center;\n\tpadding: 16px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__error {\n\tcolor: var(--color-error);\n\tpadding: 8px 0;\n\tfont-size: 13px;\n}\n\n.cn-calendar-card__list {\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-calendar-card__row {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: 10px;\n\tpadding: 8px 0;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-calendar-card__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-calendar-card__row-icon {\n\tflex-shrink: 0;\n\tpadding-top: 1px;\n}\n\n.cn-calendar-card__icon--upcoming { color: var(--color-primary-element); }\n\n.cn-calendar-card__icon--past { color: var(--color-text-maxcontrast); }\n\n.cn-calendar-card__row-main {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-calendar-card__title {\n\tdisplay: block;\n\tfont-size: 13px;\n\tfont-weight: 500;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-calendar-card__meta {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 6px;\n\tmargin-top: 2px;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-calendar-card__when {\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__location {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 10px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-hover);\n\tfont-size: 13px;\n\tmax-width: 100%;\n}\n\n.cn-calendar-card__chip-icon {\n\tcolor: var(--color-primary-element);\n\tflex-shrink: 0;\n}\n\n.cn-calendar-card__chip-title {\n\tfont-weight: 500;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-when {\n\tcolor: var(--color-text-maxcontrast);\n\twhite-space: nowrap;\n}\n\n.cn-calendar-card__chip-status {\n\tfont-size: 11px;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n\tpadding: 1px 6px;\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: var(--color-background-darker);\n}\n\n.cn-calendar-card__chip-status--confirmed { background: var(--color-success); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--tentative { background: var(--color-warning, #e9a40f); color: var(--color-main-background); }\n\n.cn-calendar-card__chip-status--cancelled { background: var(--color-error); color: var(--color-main-background); }\n\n.cn-calendar-card__cta,\n.cn-calendar-card__show-all {\n\tbackground: none;\n\tborder: none;\n\tcolor: var(--color-primary-element);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\tpadding: 0;\n\twidth: 100%;\n\ttext-align: center;\n\ttext-decoration: none;\n\tdisplay: inline-block;\n}\n\n.cn-calendar-card__cta:hover,\n.cn-calendar-card__show-all:hover {\n\ttext-decoration: underline;\n}\n</style>\n"],"names":["_createBlock","_toDisplayString","_createElementBlock","_createElementVNode","_openBlock","_normalizeClass","_Fragment","_renderList","_createTextVNode"],"mappings":"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"}
|