import { compareAsc, endOfWeek, startOfToday, startOfWeek, isAfter, eachDayOfInterval, addWeeks, parseISO, } from "date-fns"; import { toLocalTime, formatDate } from "./date"; import { graphql } from "@/gql"; import { EventFragment, EventCategory, EventOccurrence } from "@/gql/graphql"; export type { EventFragment, EventCategory, EventOccurrence, } 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 { ...Image } facebookUrl ticketUrl priceRegular priceMember priceStudent categories { ... on EventCategory { name slug } } occurrences { ... on EventOccurrence { __typename id start end venue { __typename id slug title } } } } `); export const futureEventsQuery = graphql(` query futureEvents { events: eventIndex { ... on EventIndex { futureEvents { ... on EventPage { ...Event } } } } eventCategories: eventCategories { ... on EventCategory { name slug showInFilters } } } `); export function getSingularEvents(events: EventFragment[]): SingularEvent[] { return events .map((event) => { return event.occurrences.map((occurrence) => { const eventOccurrence: any = structuredClone(event); eventOccurrence.occurrence = occurrence; return eventOccurrence; }); }) .flat(); } export function sortSingularEvents(events: SingularEvent[]) { return events.sort((a, b) => compareAsc(new Date(a.occurrence.start), new Date(b.occurrence.start)) ); } interface EventsByDate { [yearMonth: string]: { [week: string]: { [day: string]: SingularEvent[]; }; }; } export function organizeEventsByDate(events: SingularEvent[]): EventsByDate { const sortedEvents = sortSingularEvents(events); 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; } export function getClosestOccurrence(event: EventFragment) { const today = startOfToday(); const occurrences = event?.occurrences ?? []; const futureOccurrences = occurrences.filter((occurrence) => isAfter(toLocalTime(occurrence.start), today) ); futureOccurrences.sort( (a, b) => parseISO(a.start).getTime() - parseISO(b.start).getTime() ); return futureOccurrences[0]; }