"use client"; import { useEffect } from "react"; import { useQueryState, parseAsStringLiteral, parseAsString, parseAsArrayOf, } from "nuqs"; import { EventItem } from "./EventItem"; import { EventFilter, EventFilterExplained } from "./EventFilter"; import { EventOverviewItemFragment, EventCategory, SingularEvent, getSingularEvents, organizeEventsInCalendar, EventOrganizer, } from "@/lib/event"; import { filterEvents, getFilterableCategories, getFilterableOrganizers, getFilterableVenues, } from "@/lib/eventFilter"; import { isTodayOrFuture } from "@/lib/date"; import { useToday } from "@/components/general/Today"; import styles from "./eventContainer.module.scss"; import { formatDate, formatYearMonth } from "@/lib/date"; import { startOfWeek, endOfWeek, differenceInCalendarDays, parse, } from "date-fns"; 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: EventOverviewItemFragment[]; eventCategories: EventCategory[]; eventOrganizers: EventOrganizer[]; venues: VenueFragment[]; }) => { const [mode, setMode] = useQueryState( "mode", parseAsStringLiteral(["list", "calendar"]).withDefault("list"), ); const [categories, setCategories] = useQueryState( "category", parseAsArrayOf(parseAsString, ","), ); const [organizer, setOrganizer] = useQueryState("organizer", parseAsString); const [venue, setVenue] = useQueryState("venue", parseAsString); const resetFilters = () => { setCategories(null); setOrganizer(null); setVenue(null); }; const onResetFilters = (e: React.MouseEvent) => { resetFilters(); }; const filterableCategories = getFilterableCategories(eventCategories); const filterableOrganizers = getFilterableOrganizers( events, eventOrganizers, organizer, ); const filterableVenues = getFilterableVenues(events, venues, venue); /* Reset organizer if slug is invalid */ useEffect(() => { if (!eventOrganizers.find((haystack) => haystack.slug === organizer)) { setOrganizer(null); } }, [eventOrganizers, organizer]); /* Reset venue if slug is invalid */ useEffect(() => { if (!venues.find((haystack) => haystack.slug === venue)) { setVenue(null); } }, [venues, venue]); const filteredEvents = filterEvents(events, { categories, organizer, venue }); const [showFilter, setShowFilter] = useState(false); function toggleFilter() { setShowFilter(!showFilter); } return (
{(categories || organizer || venue) && ( )}
{mode === "list" && } {mode === "calendar" && }
); }; const EventList = ({ events }: { events: EventOverviewItemFragment[] }) => { if (events.length === 0) { return
Ingen kommende arrangementer.
; } return ( ); }; const EmptyCalendarCell = () => (
); const CalendarDay = ({ day, events, }: { day: string; events: SingularEvent[]; }) => (

{formatDate(parse(day, "yyyy-MM-dd", new Date(0)), "eeee dd.MM.")}

    {events.map((event) => ( ))}
); const CalendarWeek = ({ days, }: { days: Record[]>; }) => { const daysInWeek = Object.keys(days); const firstDay = daysInWeek[0]; const lastDay = daysInWeek[daysInWeek.length - 1]; const weekStart = startOfWeek(firstDay, { weekStartsOn: 1 }); const weekEnd = endOfWeek(lastDay, { weekStartsOn: 1 }); const emptyCellsBefore = differenceInCalendarDays(firstDay, weekStart); const emptyCellsAfter = differenceInCalendarDays(weekEnd, lastDay); return (
{[...Array(emptyCellsBefore)].map((_, idx) => ( ))} {Object.keys(days).map((day) => ( ))} {[...Array(emptyCellsAfter)].map((_, idx) => ( ))}
); }; function srOnlyYear(yearMonthString: string, today: Date) { // formatYearMonth only shows the year for non-current years; always expose it to screen readers const yearMonth = new Date(yearMonthString); const isCurrentYear = yearMonth.getFullYear() === today.getFullYear(); if (!isCurrentYear) { return null; } return {yearMonth.getFullYear()}; } const EventCalendar = ({ events }: { events: EventOverviewItemFragment[] }) => { const today = useToday(); const futureSingularEvents = getSingularEvents(events).filter( (x) => x.occurrence?.start && isTodayOrFuture(x.occurrence.start, today), ); const eventsByDate = organizeEventsInCalendar(futureSingularEvents); const yearMonths = Object.keys(eventsByDate); const [visibleYearMonths, setVisibleYearMonths] = useState( yearMonths.slice(0, 2), ); const toggleYearMonth = (yearMonth: string) => { if (visibleYearMonths.includes(yearMonth)) { setVisibleYearMonths(visibleYearMonths.filter((x) => x != yearMonth)); } else { setVisibleYearMonths([...visibleYearMonths, yearMonth]); } }; return ( <>
{Object.keys(eventsByDate).map((yearMonth) => { const eventCount = Object.values(eventsByDate[yearMonth]).reduce( (yearMonthSum, week) => yearMonthSum + Object.values(week).reduce( (weekSum, day) => weekSum + day.length, 0, ), 0, ); return (

toggleYearMonth(yearMonth)}> {formatYearMonth(yearMonth, today)} {srOnlyYear(yearMonth, today)}{" "} ({eventCount})

{eventCount === 0 && (
Ingen arrangementer denne måneden.
)} {eventCount !== 0 && Object.keys(eventsByDate[yearMonth]).map((week) => ( ))}
); })}
); };