import type { Metadata } from "next"; import { defineQuery } from "next-sanity"; import { sanity } from "@/lib/sanity"; import { BlogCard, type BlogCardData } from "@/components/blog/BlogCard"; 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"; import type { FilterGroup } from "@/components/FilterDropdown"; export const revalidate = 86400; type SortMode = "dateDesc" | "dateAsc"; const CATEGORY_OPTIONS = [ { value: "News", label: "News" }, { value: "Behind the Scenes", label: "Behind the Scenes" }, { value: "Music History", label: "Music History" }, { value: "Tech Talk", label: "Tech Talk" }, ]; const FILTER_GROUPS: FilterGroup[] = [ { label: "Category", param: "category", options: CATEGORY_OPTIONS }, ]; const VALID_CATEGORIES = new Set(CATEGORY_OPTIONS.map((o) => o.value)); function parseFilterParam(raw: string | undefined, valid: Set): string[] { if (!raw) return []; return raw.split(",").filter((v) => valid.has(v)); } const SORT_OPTIONS: SortOption[] = [ { value: "dateDesc", label: "Date", iconDirection: "desc" }, { value: "dateAsc", label: "Date", iconDirection: "asc" }, ]; function normalizeSort(s: string | undefined): SortMode { return s === "dateAsc" ? "dateAsc" : "dateDesc"; } const BLOG_FILTER_CLAUSE = ` _type == "blog" && ( $q == "" || title match $qPattern || subtitle match $qPattern || author match $qPattern ) && (count($categories) == 0 || category in $categories) `; const BLOG_PROJECTION = `{ _id, title, subtitle, author, publishDate, category, "slug": slug.current, featuredImage }`; const BLOGS_BY_DATE_DESC_QUERY = defineQuery(` *[ ${BLOG_FILTER_CLAUSE} ] | order(coalesce(publishDate, "0000-01-01") desc, lower(title) asc) [$start...$end]${BLOG_PROJECTION} `); const BLOGS_BY_DATE_ASC_QUERY = defineQuery(` *[ ${BLOG_FILTER_CLAUSE} ] | order(coalesce(publishDate, "9999-12-31") asc, lower(title) asc) [$start...$end]${BLOG_PROJECTION} `); const BLOGS_COUNT_QUERY = defineQuery(` count(*[ ${BLOG_FILTER_CLAUSE} ]) `); export const metadata: Metadata = { title: "Blog", description: "News, behind-the-scenes stories, and insights from TRPTK.", }; export default async function BlogArchivePage({ searchParams, }: { searchParams: Promise<{ page?: string; q?: string; sort?: string; category?: string; }>; }) { const sp = await searchParams; const q = normalizeQuery(sp.q); const sort = normalizeSort(sp.sort); const page = clampInt(sp.page, 1, 1, 9999); const categories = parseFilterParam(sp.category, VALID_CATEGORIES); const start = (page - 1) * PAGE_SIZE; const end = start + PAGE_SIZE; const qPattern = q ? `${groqLikeParam(q)}*` : ""; const listQuery = sort === "dateAsc" ? BLOGS_BY_DATE_ASC_QUERY : BLOGS_BY_DATE_DESC_QUERY; const queryParams = { start, end, q, qPattern, categories }; const [blogs, total] = await Promise.all([ sanity.fetch(listQuery, queryParams), sanity.fetch(BLOGS_COUNT_QUERY, queryParams), ]); const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)); const safePage = Math.min(page, totalPages); const initialFilters: Record = {}; if (categories.length) initialFilters.category = categories; const buildHref = (nextPage: number) => { const params = new URLSearchParams(); if (q) params.set("q", q); if (sort !== "dateDesc") params.set("sort", sort); if (categories.length) params.set("category", categories.join(",")); if (nextPage > 1) params.set("page", String(nextPage)); const qs = params.toString(); return qs ? `/blog?${qs}` : "/blog"; }; return (

{total ? `${total} post(s)` : "No posts found."}

{blogs.map((blog) => ( ))}
{totalPages > 1 ? ( ) : null}
); }