From a48b315f165b20ede81b5802e1ab00d13f00451a Mon Sep 17 00:00:00 2001 From: summersamara Date: Thu, 4 Jan 2024 15:03:40 +0100 Subject: [PATCH] Implement graphql query for events calendar and agenda components - remove ICSCalendar and ICSAgenda components - fix highlight selected date https://git.potsda.mn/potsda.mn/mobilizon/issues/40 https://git.potsda.mn/potsda.mn/mobilizon/issues/41 --- .../{ICSAgenda.vue => EventsAgenda.vue} | 151 ++++++++++++------ .../FullCalendar/EventsCalendar.vue | 105 ++++++++++++ src/components/FullCalendar/ICSCalendar.vue | 56 ------- src/graphql/search.ts | 50 ++++++ src/views/Event/CalendarView.vue | 14 +- 5 files changed, 260 insertions(+), 116 deletions(-) rename src/components/FullCalendar/{ICSAgenda.vue => EventsAgenda.vue} (50%) create mode 100644 src/components/FullCalendar/EventsCalendar.vue delete mode 100644 src/components/FullCalendar/ICSCalendar.vue diff --git a/src/components/FullCalendar/ICSAgenda.vue b/src/components/FullCalendar/EventsAgenda.vue similarity index 50% rename from src/components/FullCalendar/ICSAgenda.vue rename to src/components/FullCalendar/EventsAgenda.vue index 9867e229e..bcbf806bd 100644 --- a/src/components/FullCalendar/ICSAgenda.vue +++ b/src/components/FullCalendar/EventsAgenda.vue @@ -8,37 +8,22 @@
-
- - @@ -46,7 +31,10 @@ import { useI18n } from "vue-i18n"; import { locale } from "@/utils/i18n"; import { computed, ref } from "vue"; -import Clock from "vue-material-design-icons/ClockTimeTenOutline.vue"; +import { useLazyQuery } from "@vue/apollo-composable"; +import { IEvent } from "@/types/event.model"; +import { Paginate } from "@/types/paginate"; +import { SEARCH_CALENDAR_EVENTS } from "@/graphql/search"; import FullCalendar from "@fullcalendar/vue3"; import { EventSegment } from "@fullcalendar/core"; import dayGridPlugin from "@fullcalendar/daygrid"; @@ -55,38 +43,94 @@ import interactionPlugin from "@fullcalendar/interaction"; import { formatDateISOStringWithoutTime, formatDateString, - formatTimeString, } from "@/filters/datetime"; - -const props = defineProps<{ - icsFeedUrl: string; -}>(); +import EventCard from "../Event/EventCard.vue"; +import EmptyContent from "../Utils/EmptyContent.vue"; const { t } = useI18n({ useScope: "global" }); const calendarRef = ref(); -const lastSelectedDate = ref(undefined); +const lastSelectedDate = ref(new Date().toISOString()); const listOfEventsByDate = ref<{ events: EventSegment[]; date?: string }>({ events: [], date: undefined, }); +const showEventsByDate = (dateStr: string) => { + dateStr = formatDateISOStringWithoutTime(dateStr); + const moreLinkElement = document.querySelectorAll( + `td[data-date='${dateStr}'] a.fc-more-link` + )[0] as undefined | HTMLElement; + + if (moreLinkElement) { + moreLinkElement.click(); + } else { + listOfEventsByDate.value = { + events: [], + date: dateStr, + }; + } + + calendarRef.value.getApi().select(dateStr); +}; + if (window.location.hash.length) { lastSelectedDate.value = formatDateISOStringWithoutTime( window.location.hash.replace("#_", "") ); +} else { + lastSelectedDate.value = formatDateISOStringWithoutTime( + new Date().toISOString() + ); } +const { load: searchEventsLoad, refetch: searchEventsRefetch } = useLazyQuery<{ + searchEvents: Paginate; +}>(SEARCH_CALENDAR_EVENTS); + const calendarOptions = computed((): object => { return { plugins: [dayGridPlugin, iCalendarPlugin, interactionPlugin], initialView: "dayGridMonth", initialDate: lastSelectedDate.value, - events: { - url: props.icsFeedUrl, - format: "ics", + events: async ( + info: { start: Date; end: Date; startStr: string; endStr: string }, + successCallback: (arg: object[]) => unknown, + failureCallback: (err: string) => unknown + ) => { + const queryVars = { + limit: 999, + beginsOn: info.start, + endsOn: info.end, + }; + + const result = + (await searchEventsLoad(undefined, queryVars)) || + (await searchEventsRefetch(queryVars))?.data; + + if (!result) { + failureCallback("failed to fetch calendar events"); + return; + } + + successCallback( + (result.searchEvents.elements ?? []).map((event: IEvent) => { + return { + id: event.id, + title: event.title, + start: event.beginsOn, + end: event.endsOn, + startStr: event.beginsOn, + endStr: event.endsOn, + url: event.url, + extendedProps: { + event: event, + }, + }; + }) + ); }, nextDayThreshold: "09:00:00", dayMaxEventRows: 0, @@ -97,7 +141,7 @@ const calendarOptions = computed((): object => { contentHeight: "auto", eventClassNames: "line-clamp-3 bg-mbz-yellow dark:bg-mbz-purple", headerToolbar: { - left: "prev,next,today", + left: "prev,next,customTodayButton", center: "", right: "title", }, @@ -110,16 +154,19 @@ const calendarOptions = computed((): object => { day: t("Day"), list: t("List"), }, - dateClick: (info: { dateStr: string }) => { - calendarRef.value.getApi().select(info.dateStr); + customButtons: { + customTodayButton: { + text: t("Today"), + click: () => { + calendarRef.value.getApi().today(); + lastSelectedDate.value = formatDateISOStringWithoutTime( + new Date().toISOString() + ); + }, + }, }, - select: (info: { startStr: string }) => { - const startDateStr = formatDateISOStringWithoutTime(info.startStr); - const moreLinkElement = document.querySelectorAll( - `td[data-date='${startDateStr}'] a.fc-more-link` - )[0] as undefined | HTMLElement; - - moreLinkElement?.click(); + dateClick: (info: { dateStr: string }) => { + showEventsByDate(info.dateStr); }, moreLinkClick: (info: { date: Date; @@ -144,7 +191,7 @@ const calendarOptions = computed((): object => { lastSelectedDate.value && arg.el.closest(`td[data-date='${lastSelectedDate.value}']`) ) { - calendarRef.value.getApi().select(lastSelectedDate.value); + showEventsByDate(lastSelectedDate.value); lastSelectedDate.value = undefined; } }, @@ -167,6 +214,10 @@ const calendarOptions = computed((): object => { margin-left: 0.1rem !important; } +.agenda-view .fc-more-link { + pointer-events: none !important; +} + .clock-icon { display: inline-block; vertical-align: middle; diff --git a/src/components/FullCalendar/EventsCalendar.vue b/src/components/FullCalendar/EventsCalendar.vue new file mode 100644 index 000000000..68ac3bfbb --- /dev/null +++ b/src/components/FullCalendar/EventsCalendar.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/src/components/FullCalendar/ICSCalendar.vue b/src/components/FullCalendar/ICSCalendar.vue deleted file mode 100644 index be1af1fdd..000000000 --- a/src/components/FullCalendar/ICSCalendar.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - diff --git a/src/graphql/search.ts b/src/graphql/search.ts index 1782d9c7a..e80ba04d0 100644 --- a/src/graphql/search.ts +++ b/src/graphql/search.ts @@ -201,6 +201,56 @@ export const SEARCH_EVENTS = gql` ${ACTOR_FRAGMENT} `; +export const SEARCH_CALENDAR_EVENTS = gql` + query SearchEvents( + $beginsOn: DateTime + $endsOn: DateTime + $eventPage: Int + $limit: Int + ) { + searchEvents( + beginsOn: $beginsOn + endsOn: $endsOn + page: $eventPage + limit: $limit + ) { + total + elements { + id + title + uuid + beginsOn + endsOn + picture { + id + url + } + status + tags { + ...TagFragment + } + physicalAddress { + ...AdressFragment + } + organizerActor { + ...ActorFragment + } + attributedTo { + ...ActorFragment + } + options { + ...EventOptions + } + __typename + } + } + } + ${EVENT_OPTIONS_FRAGMENT} + ${TAG_FRAGMENT} + ${ADDRESS_FRAGMENT} + ${ACTOR_FRAGMENT} +`; + export const SEARCH_GROUPS = gql` query SearchGroups( $location: String diff --git a/src/views/Event/CalendarView.vue b/src/views/Event/CalendarView.vue index ebb80f965..4b7a2cfdf 100644 --- a/src/views/Event/CalendarView.vue +++ b/src/views/Event/CalendarView.vue @@ -5,21 +5,15 @@
- - + +