add filter on venue

This commit is contained in:
2024-07-10 18:10:44 +02:00
parent f77a4ee943
commit 6dc5c26165
6 changed files with 84 additions and 4 deletions

View File

@ -18,6 +18,7 @@ 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
@ -27,10 +28,12 @@ export const EventContainer = ({
events,
eventCategories,
eventOrganizers,
venues,
}: {
events: EventFragment[];
eventCategories: EventCategory[];
eventOrganizers: EventOrganizer[];
venues: VenueFragment[];
}) => {
const [mode, setMode] = useQueryState(
"mode",
@ -38,20 +41,46 @@ export const EventContainer = ({
);
const [category, setCategory] = useQueryState("category", parseAsString);
const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
const [venue, setVenue] = useQueryState("venue", parseAsString);
/* 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) =>
@ -64,6 +93,14 @@ export const EventContainer = ({
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);
@ -98,6 +135,9 @@ export const EventContainer = ({
eventOrganizers={filterableOrganizers}
setOrganizer={setOrganizer}
activeOrganizer={organizer}
venues={filterableVenues}
setVenue={setVenue}
activeVenue={venue}
isVisible={showFilter}
/>
{mode === "list" && <EventList events={filteredEvents} />}

View File

@ -1,6 +1,7 @@
import { EventCategory, EventOrganizer } from "@/lib/event";
import styles from "./eventFilter.module.scss";
import { VenueFragment } from "@/gql/graphql";
export const EventFilter = ({
eventCategories,
@ -9,6 +10,9 @@ export const EventFilter = ({
eventOrganizers,
setOrganizer,
activeOrganizer,
venues,
activeVenue,
setVenue,
isVisible,
}: {
eventCategories: EventCategory[];
@ -17,12 +21,19 @@ export const EventFilter = ({
eventOrganizers: EventOrganizer[];
setOrganizer: (slug: string | null) => void;
activeOrganizer: string | null;
venues: VenueFragment[];
activeVenue: string | null;
setVenue: (slug: string | null) => void;
isVisible: boolean;
}) => {
const onOrganizerSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
const organizer = e.target.value;
setOrganizer(organizer === "" ? null : organizer);
};
const onVenueSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
const venue = e.target.value;
setVenue(venue === "" ? null : venue);
};
return (
<div className={styles.eventFilter} data-show={isVisible}>
@ -52,6 +63,25 @@ export const EventFilter = ({
))}
</ul>
</div>
<div className={styles.filterItem}>
<label htmlFor="venue" className={`suphead ${styles.heading}`}>
Lokale
</label>
<div>
<select
name="venue"
value={activeVenue ?? ""}
onChange={onVenueSelect}
>
<option value="">Vis alle</option>
{venues.map((venue) => (
<option key={venue.slug} value={venue.slug}>
{venue.title}
</option>
))}
</select>
</div>
</div>
<div className={styles.filterItem}>
<label htmlFor="organizer" className={`suphead ${styles.heading}`}>
Arrangør