import type { Metadata } from "next"; import { defineQuery } from "next-sanity"; import { sanity } from "@/lib/sanity"; import { ArtistCard } from "@/components/artist/ArtistCard"; import { formatYears, type ComposerCardData } from "@/components/artist/types"; import { AnimatedText } from "@/components/AnimatedText"; import { SearchBar } from "@/components/SearchBar"; import { PaginationNav } from "@/components/PaginationNav"; import { CARD_GRID_CLASSES_4 } from "@/lib/constants"; import { PAGE_SIZE, clampInt, normalizeQuery, groqLikeParam } from "@/lib/listHelpers"; import type { SortOption } from "@/components/SortDropdown"; export const revalidate = 86400; type SortMode = "name" | "sortKey" | "birthYearAsc" | "birthYearDesc"; const SORT_OPTIONS: SortOption[] = [ { value: "name", label: "Sort by first name" }, { value: "sortKey", label: "Sort by last name" }, { value: "birthYearAsc", label: "Sort by birth year", iconDirection: "asc" }, { value: "birthYearDesc", label: "Sort by birth year", iconDirection: "desc" }, ]; function normalizeSort(s: string | undefined): SortMode { switch (s) { case "name": case "sortKey": case "birthYearAsc": case "birthYearDesc": return s; default: return "sortKey"; } } const COMPOSER_FILTER_CLAUSE = ` _type == "composer" && ( $q == "" || name match $qPattern || role match $qPattern ) `; const COMPOSER_PROJECTION = `{ _id, name, birthYear, deathYear, "slug": slug.current, image }`; const COMPOSERS_BY_NAME_QUERY = defineQuery(` *[ ${COMPOSER_FILTER_CLAUSE} ] | order(lower(name) asc) [$start...$end]${COMPOSER_PROJECTION} `); const COMPOSERS_BY_SORT_KEY_QUERY = defineQuery(` *[ ${COMPOSER_FILTER_CLAUSE} ] | order(lower(coalesce(sortKey, name)) asc, lower(name) asc) [$start...$end]${COMPOSER_PROJECTION} `); const COMPOSERS_BY_BIRTH_YEAR_ASC_QUERY = defineQuery(` *[ ${COMPOSER_FILTER_CLAUSE} ] | order(coalesce(birthYear, 999999) asc, lower(name) asc) [$start...$end]${COMPOSER_PROJECTION} `); const COMPOSERS_BY_BIRTH_YEAR_DESC_QUERY = defineQuery(` *[ ${COMPOSER_FILTER_CLAUSE} ] | order(coalesce(birthYear, -999999) desc, lower(name) asc) [$start...$end]${COMPOSER_PROJECTION} `); const COMPOSERS_COUNT_QUERY = defineQuery(` count(*[ ${COMPOSER_FILTER_CLAUSE} ]) `); export const metadata: Metadata = { title: "Composers", description: "Discover composers featured on TRPTK recordings, from early music to contemporary works.", }; export default async function ComposersPage({ searchParams, }: { searchParams: Promise<{ page?: string; q?: string; sort?: string }>; }) { const sp = await searchParams; const q = normalizeQuery(sp.q); const sort = normalizeSort(sp.sort); const page = clampInt(sp.page, 1, 1, 9999); const start = (page - 1) * PAGE_SIZE; const end = start + PAGE_SIZE; const qPattern = q ? `${groqLikeParam(q)}*` : ""; const listQuery = sort === "sortKey" ? COMPOSERS_BY_SORT_KEY_QUERY : sort === "birthYearAsc" ? COMPOSERS_BY_BIRTH_YEAR_ASC_QUERY : sort === "birthYearDesc" ? COMPOSERS_BY_BIRTH_YEAR_DESC_QUERY : COMPOSERS_BY_NAME_QUERY; const [composers, total] = await Promise.all([ sanity.fetch(listQuery, { start, end, q, qPattern }), sanity.fetch(COMPOSERS_COUNT_QUERY, { q, qPattern }), ]); const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)); const safePage = Math.min(page, totalPages); const buildHref = (nextPage: number) => { const params = new URLSearchParams(); if (q) params.set("q", q); if (sort !== "sortKey") params.set("sort", sort); if (nextPage > 1) params.set("page", String(nextPage)); const qs = params.toString(); return qs ? `/composers?${qs}` : "/composers"; }; return (

{total ? `${total} composer(s)` : "No composers found."}

{composers.map((composer) => ( ))}
{totalPages > 1 ? ( ) : null}
); }