import { compareAsc, getYear, getMonth, getWeek, getDate, endOfWeek, startOfWeek, eachDayOfInterval, addWeeks, } from "date-fns"; import { toLocalTime, formatDate } from "./date"; import { graphql } from "@/gql"; import { EventFragment, EventOccurrence } from "@/gql/graphql"; export type { EventFragment } from "@/gql/graphql"; export type SingularEvent = EventFragment & { occurrence: EventOccurrence; }; const EventFragmentDefinition = graphql(` fragment Event on EventPage { __typename id slug title body { id blockType field ... on RichTextBlock { rawValue value } } featuredImage { url width height } facebookUrl ticketUrl priceRegular priceMember priceStudent occurrences { ... on EventOccurrence { __typename id start end venue { __typename id slug title } } } } `); export const allEventsQuery = graphql(` query allEvents { events: pages(contentType: "events.EventPage") { ... on EventPage { ...Event } } } `); export function getSingularEvents(events: EventFragment[]) { return events .map((event) => { return event.occurrences ?.filter((x): x is EventOccurrence => isDefined(x)) .map((occurrence) => { const eventOccurrence = structuredClone(event); eventOccurrence.occurrence = occurrence; return eventOccurrence; }); }) .flat() .filter((x): x is SingularEvent => isDefined(x)); } function isDefined(val: T | undefined | null): val is T { return val !== undefined && val !== null; } interface EventsByDate { [yearMonth: string]: { [week: number]: { [day: number]: SingularEvent[]; }; }; } export function organizeEventsByDate(events: SingularEvent[]): EventsByDate { const sortedEvents = events.sort((a, b) => compareAsc(new Date(a.occurrence.start), new Date(b.occurrence.start)) ); const eventsByDate: EventsByDate = {}; const minDate = new Date(sortedEvents[0]?.occurrence.start); const maxDate = new Date( sortedEvents[sortedEvents.length - 1]?.occurrence.start ); let currentDate = startOfWeek(minDate, { weekStartsOn: 1 }); while (currentDate <= maxDate) { const yearMonth = formatDate(currentDate, "yyyy-MM"); const week = formatDate(currentDate, "w"); const daysOfWeek = eachDayOfInterval({ start: currentDate, end: endOfWeek(currentDate, { weekStartsOn: 1 }), }); if (!eventsByDate[yearMonth]) { eventsByDate[yearMonth] = {}; } if (!eventsByDate[yearMonth][week]) { eventsByDate[yearMonth][week] = {}; } daysOfWeek.forEach((day) => { const formattedDay = formatDate(day, "yyyy-MM-dd"); if (!eventsByDate[yearMonth][week][formattedDay]) { eventsByDate[yearMonth][week][formattedDay] = []; } }); currentDate = addWeeks(currentDate, 1); } sortedEvents.forEach((event) => { const start = toLocalTime(event.occurrence.start); const yearMonth = formatDate(start, "yyyy-MM"); const week = formatDate(start, "w"); const day = formatDate(start, "yyyy-MM-dd"); eventsByDate[yearMonth][week][day].push(event); }); return eventsByDate; }