"use client"; import { useQueryState, parseAsStringLiteral, parseAsString } from "nuqs"; import { EventItem } from "./EventItem"; import { EventFilter, EventFilterExplained } from "./EventFilter"; import { EventFragment, EventCategory, SingularEvent, getSingularEvents, organizeEventsInCalendar, EventOrganizer, } from "@/lib/event"; import { isTodayOrFuture } from "@/lib/date"; import styles from "./eventContainer.module.scss"; import { formatDate, formatYearMonth } from "@/lib/date"; import { parse } from "date-fns"; import { unique } from "@/lib/common"; import Icon from "../general/Icon"; import { useState } from "react"; import { VenueFragment } from "@/gql/graphql"; /* TODO: canonical / alternate URLs https://github.com/47ng/nuqs?tab=readme-ov-file#seo */ export const EventContainer = ({ events, eventCategories, eventOrganizers, venues, }: { events: EventFragment[]; eventCategories: EventCategory[]; eventOrganizers: EventOrganizer[]; venues: VenueFragment[]; }) => { const [mode, setMode] = useQueryState( "mode", parseAsStringLiteral(["list", "calendar"]).withDefault("list") ); const [category, setCategory] = useQueryState("category", parseAsString); const [organizer, setOrganizer] = useQueryState("organizer", parseAsString); const [venue, setVenue] = useQueryState("venue", parseAsString); const resetFilters = () => { setCategory(null); setOrganizer(null); setVenue(null); }; /* Allow filtering on all categories that are configured to be shown */ const filterableCategories = eventCategories.filter((x) => x.showInFilters); /* Allow filtering on all organizers that have upcoming events Filtering on an organizer with no upcoming events will work, but be hidden from dropdown */ const uniqueOrganizers: string[] = unique( events .map((x) => x.organizers) .flat() .filter((x) => x.__typename === "EventOrganizer") .map((x) => x.slug) .filter((x) => typeof x === "string") ); const filterableOrganizers = uniqueOrganizers .map((slug) => eventOrganizers.find((haystack) => haystack.slug === slug)) .filter((x) => x !== undefined) as EventOrganizer[]; /* Allow filtering on all venues that have upcoming events Filtering on a venue with no upcoming events will work, and in that case it's included in the dropdown */ const venueSlugsWithUpcomingEvents = events .map((x) => x.occurrences) .flat() .filter((x) => x.venue?.__typename === "VenuePage") .map((x) => x.venue?.slug) .filter((x) => typeof x === "string"); const filterableVenues = venues .map((x) => venues.find( (haystack) => haystack.slug === x.slug || haystack.slug === venue ) ) .filter((x) => x !== undefined) as VenueFragment[]; const filteredEvents = events .filter( (x) => !organizer || x.organizers.map((organizer) => organizer.slug).includes(organizer) ) .filter( (x) => !category || x.categories .map((eventCategory) => eventCategory.slug) .includes(category) ) .filter( (x) => !venue || x.occurrences .map((occurrence) => occurrence.venue?.slug) .filter((x) => typeof x === "string") .includes(venue) ); const [showFilter, setShowFilter] = useState(false); function toggleFilter() { setShowFilter(!showFilter); } return (