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"; import { PIG_NAMES, PigName, randomElement } from "@/lib/common"; export type { EventFragment, EventCategory, EventOccurrence, EventOrganizer, } 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 } pig facebookUrl ticketUrl free priceRegular priceMember priceStudent categories { ... on EventCategory { name slug pig } } occurrences { ... on EventOccurrence { __typename id start end venue { __typename id slug title preposition url } } } organizers { ... on EventOrganizer { name externalUrl association { ... on AssociationPage { url } } } } } `); 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 compareDates(a: Date | string, b: Date | string) { return compareAsc(new Date(a), new Date(b)); } export function sortSingularEvents(events: SingularEvent[]) { return events.sort((a, b) => compareDates(a.occurrence.start, 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]; } export function getEventPig(event: EventFragment): string | null { if (event.pig === "" || typeof event.pig !== "string") { return null; } if (PIG_NAMES.includes(event.pig)) { return event.pig; } if (event.pig === "automatic") { const categoryPigs = event.categories ?.map((category) => category.pig) .filter((pig) => PIG_NAMES.includes(pig)); const chosenPig = randomElement(categoryPigs ?? []); return typeof chosenPig === "string" ? chosenPig : null; } return null; }