"use client"; import { useState, useMemo } from "react"; import Link from "next/link"; import { Card, CardContent, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { cn } from "@/lib/utils"; import { Search, ChevronLeft, ChevronRight } from "lucide-react"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type YearGroup = { year: number; count: number }; export type RecipientGroup = { name: string; count: number }; export type LocationGroup = { name: string; count: number }; // --------------------------------------------------------------------------- // Diacritic-aware normalize (matches books-grid pattern) // --------------------------------------------------------------------------- function normalize(text: string): string { return text .toLowerCase() .replace(/ṛ/g, "r") .replace(/ṝ/g, "r") .replace(/ṣ/g, "s") .replace(/ś/g, "s") .replace(/ṭ/g, "t") .replace(/ḍ/g, "d") .replace(/ṇ/g, "n") .replace(/ñ/g, "n") .replace(/ṅ/g, "n") .replace(/ḥ/g, "h") .replace(/ṁ/g, "m") .replace(/ṃ/g, "m") .replace(/ḷ/g, "l") .replace(/ā/g, "a") .replace(/ī/g, "i") .replace(/ū/g, "u") .normalize("NFD") .replace(/[\u0300-\u036f]/g, ""); } // --------------------------------------------------------------------------- // Pagination // --------------------------------------------------------------------------- const PAGE_SIZE_RECIPIENTS = 50; const PAGE_SIZE_LOCATIONS = 60; const PAGE_SIZE_YEARS = 100; // years are small cards, show all usually function Pagination({ page, totalPages, onPageChange, }: { page: number; totalPages: number; onPageChange: (p: number) => void; }) { if (totalPages <= 1) return null; return (
Page {page} of {totalPages}
); } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function LettersBrowser({ currentView, years, recipients, locations, }: { currentView: string; years: YearGroup[]; recipients: RecipientGroup[]; locations: LocationGroup[]; }) { const [query, setQuery] = useState(""); const [page, setPage] = useState(1); // Reset page when query or view changes const handleQueryChange = (q: string) => { setQuery(q); setPage(1); }; // --- Filtered data --- const filteredYears = useMemo(() => { if (!query.trim()) return years; const q = normalize(query.trim()); return years.filter((yg) => String(yg.year).includes(q)); }, [years, query]); const filteredRecipients = useMemo(() => { if (!query.trim()) return recipients; const q = normalize(query.trim()); return recipients.filter((rg) => normalize(rg.name).includes(q)); }, [recipients, query]); const filteredLocations = useMemo(() => { if (!query.trim()) return locations; const q = normalize(query.trim()); return locations.filter((lg) => normalize(lg.name).includes(q)); }, [locations, query]); // --- Paginated data --- const activeData = currentView === "year" ? filteredYears : currentView === "recipient" ? filteredRecipients : filteredLocations; const pageSize = currentView === "year" ? PAGE_SIZE_YEARS : currentView === "recipient" ? PAGE_SIZE_RECIPIENTS : PAGE_SIZE_LOCATIONS; const totalPages = Math.max(1, Math.ceil(activeData.length / pageSize)); const safePage = Math.min(page, totalPages); const startIdx = (safePage - 1) * pageSize; const endIdx = startIdx + pageSize; const placeholder = currentView === "year" ? "Search years..." : currentView === "recipient" ? "Search recipients..." : "Search locations..."; const resultCount = activeData.length; const totalCount = currentView === "year" ? years.length : currentView === "recipient" ? recipients.length : locations.length; return (
{/* Search */}
handleQueryChange(e.target.value)} className="pl-9" />
{/* Result count */} {query.trim() && (

{resultCount === 0 ? "No results found" : `${resultCount} of ${totalCount} ${currentView === "year" ? "years" : currentView === "recipient" ? "recipients" : "locations"}`}

)} {/* Year view */} {currentView === "year" && (
{(filteredYears.slice(startIdx, endIdx) as YearGroup[]).map((yg) => (

{yg.year}

{yg.count} {yg.count === 1 ? "letter" : "letters"}

))}
)} {/* Recipient view */} {currentView === "recipient" && (
{(filteredRecipients.slice(startIdx, endIdx) as RecipientGroup[]).map( (rg) => (

{rg.name}

{rg.count}
), )}
)} {/* Location view */} {currentView === "location" && (
{(filteredLocations.slice(startIdx, endIdx) as LocationGroup[]).map( (lg) => (

{lg.name}

{lg.count} {lg.count === 1 ? "letter" : "letters"}

), )}
)} {/* Pagination */}
); }