"use client"; import { useState, useMemo, useRef } from "react"; import Link from "next/link"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { ChevronLeft, ChevronRight, Menu, Calendar, Clock, MapPin } from "lucide-react"; import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button"; import { parseChapter } from "@/lib/book-reference"; import { Drawer, DrawerClose, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type TranscriptionItem = { slug: string; name: string; type: string; date: string | null; year: number | null; month: number | null; day: number | null; place: string | null; context: string | null; reference: string | null; durationMinutes: number | null; participants: string[]; audioUrl: string | null; }; type YearGroup = { year: number; label: string; count: number; transcriptions: TranscriptionItem[]; }; type ChapterGroup = { key: string; label: string; transcriptions: TranscriptionItem[]; }; // A section is a canto (SB) or līlā (CC). Books without that level produce a // single section with a null label (rendered without a heading). type SectionGroup = { key: string; label: string | null; chapters: ChapterGroup[]; }; // --------------------------------------------------------------------------- // Grouping helpers // --------------------------------------------------------------------------- function buildYearGroups(transcriptions: TranscriptionItem[]): YearGroup[] { const map = new Map(); for (const t of transcriptions) { const y = t.year ?? 0; if (!map.has(y)) map.set(y, []); map.get(y)!.push(t); } const groups: YearGroup[] = []; const years = Array.from(map.keys()).sort((a, b) => a - b); for (const y of years) { if (y === 0) continue; const items = map.get(y)!; groups.push({ year: y, label: String(y), count: items.length, transcriptions: items, }); } const undated = map.get(0); if (undated && undated.length > 0) { groups.push({ year: 0, label: "Undated", count: undated.length, transcriptions: undated, }); } return groups; } // Group a year's transcripts into sections (canto/līlā) → chapter accordions, // ordered by the parsed sort tuple. Books without a canto/līlā level collapse // into a single section with a null label. Only sections/chapters that have // transcripts appear. function buildSections( transcriptions: TranscriptionItem[], code: string, ): SectionGroup[] { const sections = new Map< string, { label: string | null; order: number; chapters: Map< string, { label: string; sort: [number, number]; items: TranscriptionItem[] } >; } >(); for (const t of transcriptions) { const ch = parseChapter(code, t.reference); const secKey = ch.section ? ch.section.key : "__flat__"; if (!sections.has(secKey)) { sections.set(secKey, { label: ch.section ? ch.section.label : null, order: ch.section ? ch.section.order : 0, chapters: new Map(), }); } const sec = sections.get(secKey)!; if (!sec.chapters.has(ch.key)) { sec.chapters.set(ch.key, { label: ch.label, sort: ch.sort, items: [] }); } sec.chapters.get(ch.key)!.items.push(t); } return Array.from(sections.entries()) .sort((a, b) => a[1].order - b[1].order) .map(([key, s]) => ({ key, label: s.label, chapters: Array.from(s.chapters.entries()) .sort( (a, b) => a[1].sort[0] - b[1].sort[0] || a[1].sort[1] - b[1].sort[1], ) .map(([k, v]) => ({ key: k, label: v.label, transcriptions: v.items, })), })); } // --------------------------------------------------------------------------- // Year Sidebar // --------------------------------------------------------------------------- function YearSidebar({ groups, activeYear, onSelect, }: { groups: YearGroup[]; activeYear: number; onSelect: (year: number) => void; }) { return ( ); } // --------------------------------------------------------------------------- // Mobile drawer // --------------------------------------------------------------------------- function YearMobileNav({ groups, activeYear, onSelect, title, }: { groups: YearGroup[]; activeYear: number; onSelect: (year: number) => void; title: string; }) { const [open, setOpen] = useState(false); return ( Years {title} ); } // --------------------------------------------------------------------------- // Prev / Next nav // --------------------------------------------------------------------------- function YearNav({ prev, next, groups, activeYear, onSelect, drawerTitle, }: { prev: YearGroup | null; next: YearGroup | null; groups: YearGroup[]; activeYear: number; onSelect: (year: number) => void; drawerTitle: string; }) { return (
{prev ? ( ) : (
)}
{next ? ( ) : (
)}
); } // --------------------------------------------------------------------------- // Chapter section — collapsible group of transcriptions for a single chapter // --------------------------------------------------------------------------- function ChapterSection({ group, defaultOpen, }: { group: ChapterGroup; defaultOpen: boolean; }) { const [open, setOpen] = useState(defaultOpen); return (
{open && (
    {group.transcriptions.map((t) => (
  • {t.name}

    {t.type === "lecture" ? "Lecture" : "Conversation"}
    {t.date && ( {t.date} )} {t.place && ( {t.place} )} {t.reference && ( {t.reference} )} {t.durationMinutes && t.durationMinutes > 0 && ( {t.durationMinutes} min )}
    {t.audioUrl && ( )}
  • ))}
)}
); } // --------------------------------------------------------------------------- // Main component // --------------------------------------------------------------------------- export function TranscriptionBookBrowser({ transcriptions, code, title, totalCount, }: { transcriptions: TranscriptionItem[]; code: string; title: string; totalCount: number; }) { const yearGroups = useMemo( () => buildYearGroups(transcriptions), [transcriptions], ); const [activeYearRaw, setActiveYear] = useState( yearGroups.length > 0 ? yearGroups[0].year : 0, ); // Derive a safe active year const activeYear = useMemo(() => { if (yearGroups.length === 0) return activeYearRaw; if (yearGroups.find((g) => g.year === activeYearRaw)) return activeYearRaw; return yearGroups[0].year; }, [yearGroups, activeYearRaw]); const contentRef = useRef(null); const handleYearSelect = (year: number) => { setActiveYear(year); contentRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); }; // Active group + neighbors const activeIdx = yearGroups.findIndex((g) => g.year === activeYear); const activeGroup = activeIdx >= 0 ? yearGroups[activeIdx] : null; const prev = activeIdx > 0 ? yearGroups[activeIdx - 1] : null; const next = activeIdx >= 0 && activeIdx < yearGroups.length - 1 ? yearGroups[activeIdx + 1] : null; // Sections (canto/līlā) → chapters within the active year const sections = useMemo( () => buildSections(activeGroup?.transcriptions ?? [], code), [activeGroup, code], ); const activeYearLabel = activeGroup?.label ?? "Unknown"; return ( <> {/* Header */}
← Transcriptions by Book

{title}: {activeYearLabel}

{activeGroup?.count ?? 0}{" "} {(activeGroup?.count ?? 0) === 1 ? "transcription" : "transcriptions"}{" "} in {activeYearLabel} · {totalCount} total

{/* Sidebar + Main */}
{/* Left sidebar — years */} {/* Main content */}
{/* Top navigation */} {/* Content — sections (canto/līlā) → chapter accordions */} {sections.length === 0 ? (

No transcriptions for this year

) : (
{sections.map((section) => (
{section.label && (

{section.label}

)} {section.chapters.map((cg, i) => ( ))}
))}
)} {/* Bottom navigation */}
); }