support filtering multiple categories
This commit is contained in:
@ -1,6 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useQueryState, parseAsStringLiteral, parseAsString } from "nuqs";
|
import {
|
||||||
|
useQueryState,
|
||||||
|
parseAsStringLiteral,
|
||||||
|
parseAsString,
|
||||||
|
parseAsArrayOf,
|
||||||
|
} from "nuqs";
|
||||||
import { EventItem } from "./EventItem";
|
import { EventItem } from "./EventItem";
|
||||||
import { EventFilter, EventFilterExplained } from "./EventFilter";
|
import { EventFilter, EventFilterExplained } from "./EventFilter";
|
||||||
import {
|
import {
|
||||||
@ -44,12 +49,15 @@ export const EventContainer = ({
|
|||||||
"mode",
|
"mode",
|
||||||
parseAsStringLiteral(["list", "calendar"]).withDefault("list")
|
parseAsStringLiteral(["list", "calendar"]).withDefault("list")
|
||||||
);
|
);
|
||||||
const [category, setCategory] = useQueryState("category", parseAsString);
|
const [categories, setCategories] = useQueryState(
|
||||||
|
"category",
|
||||||
|
parseAsArrayOf(parseAsString, ",")
|
||||||
|
);
|
||||||
const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
|
const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
|
||||||
const [venue, setVenue] = useQueryState("venue", parseAsString);
|
const [venue, setVenue] = useQueryState("venue", parseAsString);
|
||||||
|
|
||||||
const resetFilters = () => {
|
const resetFilters = () => {
|
||||||
setCategory(null);
|
setCategories(null);
|
||||||
setOrganizer(null);
|
setOrganizer(null);
|
||||||
setVenue(null);
|
setVenue(null);
|
||||||
};
|
};
|
||||||
@ -105,10 +113,10 @@ export const EventContainer = ({
|
|||||||
)
|
)
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
!category ||
|
!categories ||
|
||||||
x.categories
|
x.categories
|
||||||
.map((eventCategory) => eventCategory.slug)
|
.map((eventCategory) => eventCategory.slug)
|
||||||
.includes(category)
|
.filter((x) => categories.includes(x)).length !== 0
|
||||||
)
|
)
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
@ -147,7 +155,7 @@ export const EventContainer = ({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.filterButtons}>
|
<div className={styles.filterButtons}>
|
||||||
{(category || organizer || venue) && (
|
{(categories || organizer || venue) && (
|
||||||
<button onClick={onResetFilters} className="button tertiary">
|
<button onClick={onResetFilters} className="button tertiary">
|
||||||
<span>Vis alle</span>
|
<span>Vis alle</span>
|
||||||
<Icon type="noFilter" />
|
<Icon type="noFilter" />
|
||||||
@ -161,8 +169,8 @@ export const EventContainer = ({
|
|||||||
</div>
|
</div>
|
||||||
<EventFilter
|
<EventFilter
|
||||||
eventCategories={filterableCategories}
|
eventCategories={filterableCategories}
|
||||||
setCategory={setCategory}
|
setCategories={setCategories}
|
||||||
activeCategory={category}
|
activeCategories={categories}
|
||||||
eventOrganizers={filterableOrganizers}
|
eventOrganizers={filterableOrganizers}
|
||||||
setOrganizer={setOrganizer}
|
setOrganizer={setOrganizer}
|
||||||
activeOrganizer={organizer}
|
activeOrganizer={organizer}
|
||||||
@ -173,7 +181,7 @@ export const EventContainer = ({
|
|||||||
/>
|
/>
|
||||||
<EventFilterExplained
|
<EventFilterExplained
|
||||||
eventCategories={filterableCategories}
|
eventCategories={filterableCategories}
|
||||||
activeCategory={category}
|
activeCategories={categories}
|
||||||
eventOrganizers={filterableOrganizers}
|
eventOrganizers={filterableOrganizers}
|
||||||
activeOrganizer={organizer}
|
activeOrganizer={organizer}
|
||||||
venues={filterableVenues}
|
venues={filterableVenues}
|
||||||
@ -188,9 +196,7 @@ export const EventContainer = ({
|
|||||||
|
|
||||||
const EventList = ({ events }: { events: EventFragment[] }) => {
|
const EventList = ({ events }: { events: EventFragment[] }) => {
|
||||||
if (events.length === 0) {
|
if (events.length === 0) {
|
||||||
return (
|
return <div className={styles.noEvents}>Ingen kommende arrangementer.</div>;
|
||||||
<div className={styles.noEvents}>Ingen kommende arrangementer.</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ul className={styles.eventList}>
|
<ul className={styles.eventList}>
|
||||||
|
@ -2,12 +2,12 @@ import { EventCategory, EventOrganizer } from "@/lib/event";
|
|||||||
|
|
||||||
import styles from "./eventFilter.module.scss";
|
import styles from "./eventFilter.module.scss";
|
||||||
import { VenueFragment } from "@/gql/graphql";
|
import { VenueFragment } from "@/gql/graphql";
|
||||||
import { Icon } from "../general/Icon";
|
import { formatHumanReadableList } from "@/lib/common";
|
||||||
|
|
||||||
export const EventFilter = ({
|
export const EventFilter = ({
|
||||||
eventCategories,
|
eventCategories,
|
||||||
setCategory,
|
setCategories,
|
||||||
activeCategory,
|
activeCategories,
|
||||||
eventOrganizers,
|
eventOrganizers,
|
||||||
setOrganizer,
|
setOrganizer,
|
||||||
activeOrganizer,
|
activeOrganizer,
|
||||||
@ -17,8 +17,8 @@ export const EventFilter = ({
|
|||||||
isVisible,
|
isVisible,
|
||||||
}: {
|
}: {
|
||||||
eventCategories: EventCategory[];
|
eventCategories: EventCategory[];
|
||||||
setCategory: (slug: string | null) => void;
|
setCategories: (slug: string[] | null) => void;
|
||||||
activeCategory: string | null;
|
activeCategories: string[] | null;
|
||||||
eventOrganizers: EventOrganizer[];
|
eventOrganizers: EventOrganizer[];
|
||||||
setOrganizer: (slug: string | null) => void;
|
setOrganizer: (slug: string | null) => void;
|
||||||
activeOrganizer: string | null;
|
activeOrganizer: string | null;
|
||||||
@ -35,6 +35,12 @@ export const EventFilter = ({
|
|||||||
const venue = e.target.value;
|
const venue = e.target.value;
|
||||||
setVenue(venue === "" ? null : venue);
|
setVenue(venue === "" ? null : venue);
|
||||||
};
|
};
|
||||||
|
const toggleCategory = (category: string) => {
|
||||||
|
const newCategories = activeCategories?.includes(category)
|
||||||
|
? activeCategories?.filter((x) => x !== category)
|
||||||
|
: [...(activeCategories ?? []), category];
|
||||||
|
setCategories(newCategories.length === 0 ? null : newCategories);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.eventFilter} data-show={isVisible}>
|
<div className={styles.eventFilter} data-show={isVisible}>
|
||||||
@ -48,12 +54,8 @@ export const EventFilter = ({
|
|||||||
<li key={category.slug}>
|
<li key={category.slug}>
|
||||||
<button
|
<button
|
||||||
className="button toggler"
|
className="button toggler"
|
||||||
data-active={activeCategory === category.slug}
|
data-active={activeCategories?.includes(category.slug)}
|
||||||
onClick={() =>
|
onClick={() => toggleCategory(category.slug)}
|
||||||
setCategory(
|
|
||||||
activeCategory === category.slug ? null : category.slug
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</button>
|
</button>
|
||||||
@ -102,34 +104,38 @@ export const EventFilter = ({
|
|||||||
|
|
||||||
export const EventFilterExplained = ({
|
export const EventFilterExplained = ({
|
||||||
eventCategories,
|
eventCategories,
|
||||||
activeCategory,
|
activeCategories,
|
||||||
eventOrganizers,
|
eventOrganizers,
|
||||||
activeOrganizer,
|
activeOrganizer,
|
||||||
venues,
|
venues,
|
||||||
activeVenue,
|
activeVenue,
|
||||||
}: {
|
}: {
|
||||||
eventCategories: EventCategory[];
|
eventCategories: EventCategory[];
|
||||||
activeCategory: string | null;
|
activeCategories: string[] | null;
|
||||||
eventOrganizers: EventOrganizer[];
|
eventOrganizers: EventOrganizer[];
|
||||||
activeOrganizer: string | null;
|
activeOrganizer: string | null;
|
||||||
venues: VenueFragment[];
|
venues: VenueFragment[];
|
||||||
activeVenue: string | null;
|
activeVenue: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
const category =
|
const categories = eventCategories.filter((x) =>
|
||||||
activeCategory && eventCategories.find((x) => x.slug === activeCategory);
|
activeCategories?.includes(x.slug)
|
||||||
|
);
|
||||||
const organizer =
|
const organizer =
|
||||||
activeOrganizer && eventOrganizers.find((x) => x.slug === activeOrganizer);
|
activeOrganizer && eventOrganizers.find((x) => x.slug === activeOrganizer);
|
||||||
const venue = activeVenue && venues.find((x) => x.slug === activeVenue);
|
const venue = activeVenue && venues.find((x) => x.slug === activeVenue);
|
||||||
|
|
||||||
|
const categoryList = formatHumanReadableList(categories.map((x) => x.name));
|
||||||
|
const hasCategories = categoryList.length !== 0;
|
||||||
|
|
||||||
let text = "";
|
let text = "";
|
||||||
if (category && organizer && venue) {
|
if (hasCategories && organizer && venue) {
|
||||||
text = `${category.name} av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
text = `${categoryList} av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
||||||
} else if (category && organizer) {
|
} else if (hasCategories && organizer) {
|
||||||
text = `${category.name} av ${organizer.name}`;
|
text = `${categoryList} av ${organizer.name}`;
|
||||||
} else if (category && venue) {
|
} else if (hasCategories && venue) {
|
||||||
text = `${category.name} ${venue.preposition} ${venue.title}`;
|
text = `${categoryList} ${venue.preposition} ${venue.title}`;
|
||||||
} else if (category) {
|
} else if (hasCategories) {
|
||||||
text = `${category.name}`;
|
text = `${categoryList}`;
|
||||||
} else if (organizer && venue) {
|
} else if (organizer && venue) {
|
||||||
text = `Arrangeres av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
text = `Arrangeres av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
||||||
} else if (organizer) {
|
} else if (organizer) {
|
||||||
|
@ -76,6 +76,21 @@ export function formatNorwegianPhoneNumber(phone: string): string {
|
|||||||
return phone;
|
return phone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatHumanReadableList(array: (string | number)[]): string {
|
||||||
|
const length = array.length;
|
||||||
|
|
||||||
|
if (length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (length === 1) {
|
||||||
|
return array[0].toString();
|
||||||
|
}
|
||||||
|
if (length === 2) {
|
||||||
|
return array.join(" og ");
|
||||||
|
}
|
||||||
|
return array.slice(0, -1).join(", ") + " og " + array[length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
const OneLevelOfBlocksFragmentDefinition = graphql(`
|
const OneLevelOfBlocksFragmentDefinition = graphql(`
|
||||||
fragment OneLevelOfBlocks on StreamFieldInterface {
|
fragment OneLevelOfBlocks on StreamFieldInterface {
|
||||||
id
|
id
|
||||||
|
Reference in New Issue
Block a user