"use client"; import { useState, useMemo, useRef, useEffect } from "react"; import Link from "next/link"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { cn } from "@/lib/utils"; import { Search, ChevronLeft, ChevronRight, ArrowUpDown, ArrowDownAZ, ArrowUpAZ, Hash, Check, BookOpen, MessageCircle, } from "lucide-react"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type YearGroup = { year: number; count: number }; export type PlaceGroup = { name: string; count: number }; type SortMode = "count" | "asc" | "desc"; // --------------------------------------------------------------------------- // 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, ""); } // --------------------------------------------------------------------------- // Sort Popover // --------------------------------------------------------------------------- const SORT_OPTIONS: { key: SortMode; label: string; icon: typeof Hash }[] = [ { key: "count", label: "Most transcriptions first", icon: Hash }, { key: "asc", label: "A → Z", icon: ArrowDownAZ }, { key: "desc", label: "Z → A", icon: ArrowUpAZ }, ]; const SORT_OPTIONS_YEAR: { key: SortMode; label: string; icon: typeof Hash }[] = [ { key: "count", label: "Most transcriptions first", icon: Hash }, { key: "asc", label: "Oldest first", icon: ArrowDownAZ }, { key: "desc", label: "Newest first", icon: ArrowUpAZ }, ]; function SortPopover({ sort, onSortChange, isYear, }: { sort: SortMode; onSortChange: (s: SortMode) => void; isYear?: boolean; }) { const [open, setOpen] = useState(false); const ref = useRef(null); // Close on outside click useEffect(() => { if (!open) return; function handleClick(e: MouseEvent) { if (ref.current && !ref.current.contains(e.target as Node)) { setOpen(false); } } document.addEventListener("mousedown", handleClick); return () => document.removeEventListener("mousedown", handleClick); }, [open]); const options = isYear ? SORT_OPTIONS_YEAR : SORT_OPTIONS; return (
{open && (
{options.map((opt) => { const Icon = opt.icon; const isActive = sort === opt.key; return ( ); })}
)}
); } // --------------------------------------------------------------------------- // Pagination // --------------------------------------------------------------------------- const PAGE_SIZE_PLACES = 60; const PAGE_SIZE_YEARS = 100; function Pagination({ page, totalPages, onPageChange, }: { page: number; totalPages: number; onPageChange: (p: number) => void; }) { if (totalPages <= 1) return null; return (
Page {page} of {totalPages}
); } // --------------------------------------------------------------------------- // Sorting helpers // --------------------------------------------------------------------------- function sortYears(data: YearGroup[], mode: SortMode): YearGroup[] { const sorted = [...data]; switch (mode) { case "count": return sorted.sort((a, b) => b.count - a.count); case "asc": return sorted.sort((a, b) => a.year - b.year); case "desc": return sorted.sort((a, b) => b.year - a.year); } } function sortPlaces(data: PlaceGroup[], mode: SortMode): PlaceGroup[] { const sorted = [...data]; switch (mode) { case "count": return sorted.sort((a, b) => b.count - a.count); case "asc": return sorted.sort((a, b) => a.name.localeCompare(b.name)); case "desc": return sorted.sort((a, b) => b.name.localeCompare(a.name)); } } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function TranscriptionsBrowser({ currentView, years, places, lectureCount, conversationCount, }: { currentView: string; years: YearGroup[]; places: PlaceGroup[]; lectureCount: number; conversationCount: number; }) { const [query, setQuery] = useState(""); const [page, setPage] = useState(1); const [sortYear, setSortYear] = useState("asc"); const [sortPlace, setSortPlace] = useState("count"); const handleQueryChange = (q: string) => { setQuery(q); setPage(1); }; const handleSortChange = (mode: SortMode) => { if (currentView === "year") setSortYear(mode); else setSortPlace(mode); setPage(1); }; const currentSort = currentView === "year" ? sortYear : sortPlace; // --- Filtered + sorted data --- const filteredYears = useMemo(() => { let data = years; if (query.trim()) { const q = normalize(query.trim()); data = data.filter((yg) => String(yg.year).includes(q)); } return sortYears(data, sortYear); }, [years, query, sortYear]); const filteredPlaces = useMemo(() => { let data = places; if (query.trim()) { const q = normalize(query.trim()); data = data.filter((pg) => normalize(pg.name).includes(q)); } return sortPlaces(data, sortPlace); }, [places, query, sortPlace]); // --- Paginated data --- const activeData = currentView === "year" ? filteredYears : filteredPlaces; const pageSize = currentView === "year" ? PAGE_SIZE_YEARS : PAGE_SIZE_PLACES; 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..." : "Search places..."; const resultCount = activeData.length; const totalCount = currentView === "year" ? years.length : places.length; // --- Type view (no search/sort/pagination needed) --- if (currentView === "type") { return (

Lectures

{lectureCount.toLocaleString()} transcriptions

Bhagavad-gītā, Śrīmad-Bhāgavatam, Caitanya-caritāmṛta & more

Conversations

{conversationCount.toLocaleString()} transcriptions

Morning walks, room conversations, interviews & more

); } // --- Year / Place views --- return (
{/* Search + Sort */}
handleQueryChange(e.target.value)} className="pl-9" />
{/* Result count */} {query.trim() && (

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

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

{yg.year}

{yg.count}{" "} {yg.count === 1 ? "transcription" : "transcriptions"}

))}
)} {/* Place view */} {currentView === "place" && (
{(filteredPlaces.slice(startIdx, endIdx) as PlaceGroup[]).map( (pg) => (

{pg.name}

{pg.count}{" "} {pg.count === 1 ? "transcription" : "transcriptions"}

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