"use client"; import { useState, useMemo, useRef, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { Search, X, Menu, ChevronLeft, ChevronRight } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Card, CardContent } from "@/components/ui/card"; import { cn } from "@/lib/utils"; import { Button, buttonVariants } from "@/components/ui/button"; import { ReadingSettings } from "@/components/reading-settings"; import { Drawer, DrawerClose, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- type GlossaryEntry = { term: string; definition: string; definition_html: string; }; type Props = { entries: GlossaryEntry[]; activeLetter: string; letters: string[]; letterCounts: Record; }; // --------------------------------------------------------------------------- // Normalize for search // --------------------------------------------------------------------------- function normalizeForSearch(text: string): string { return text .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .toLowerCase(); } // --------------------------------------------------------------------------- // Letter Navigation — horizontal row matching disciples pattern // --------------------------------------------------------------------------- function LetterNav({ letters, activeLetter, letterCounts, }: { letters: string[]; activeLetter: string; letterCounts: Record; }) { return ( ); } // --------------------------------------------------------------------------- // Sidebar (desktop) — shows terms for active letter only // --------------------------------------------------------------------------- function TermSidebar({ terms, activeTerm, filteredTerms, onSelectTerm, }: { terms: string[]; activeTerm: string; filteredTerms: string[] | null; onSelectTerm: (term: string) => void; }) { const activeRef = useRef(null); useEffect(() => { activeRef.current?.scrollIntoView({ block: "nearest" }); }, [activeTerm]); const displayTerms = filteredTerms ?? terms; return ( ); } // --------------------------------------------------------------------------- // Mobile drawer // --------------------------------------------------------------------------- function TermMobileNav({ terms, activeTerm, filteredTerms, onSelectTerm, activeLetter, }: { terms: string[]; activeTerm: string; filteredTerms: string[] | null; onSelectTerm: (term: string) => void; activeLetter: string; }) { const [open, setOpen] = useState(false); const displayTerms = filteredTerms ?? terms; return ( Terms Glossary — {activeLetter} ); } // --------------------------------------------------------------------------- // Prev / Next nav — matches BbdNav pattern // --------------------------------------------------------------------------- function TermNav({ prev, next, terms, activeTerm, filteredTerms, onSelectTerm, activeLetter, }: { prev: string | null; next: string | null; terms: string[]; activeTerm: string; filteredTerms: string[] | null; onSelectTerm: (term: string) => void; activeLetter: string; }) { return (
{prev ? ( ) : (
)}
{next ? ( ) : (
)}
); } // --------------------------------------------------------------------------- // Main component // --------------------------------------------------------------------------- export function GlossaryClient({ entries, activeLetter, letters, letterCounts, }: Props) { const router = useRouter(); const [query, setQuery] = useState(""); const [activeTermRaw, setActiveTerm] = useState(entries[0]?.term || ""); const contentRef = useRef(null); // All term names const allTerms = useMemo(() => entries.map((e) => e.term), [entries]); // Term → entry index map const termIndex = useMemo(() => { const map = new Map(); entries.forEach((e, i) => map.set(e.term, i)); return map; }, [entries]); // Search filter const searchResults = useMemo(() => { if (!query.trim()) return null; const q = normalizeForSearch(query.trim()); return entries .filter((e) => { const normTerm = normalizeForSearch(e.term); const normDef = normalizeForSearch(e.definition); return normTerm.includes(q) || normDef.includes(q); }) .map((e) => e.term); }, [entries, query]); // Safe activeTerm — if search is active and current term isn't in results, use first result const activeTerm = useMemo(() => { if ( searchResults && searchResults.length > 0 && !searchResults.includes(activeTermRaw) ) { return searchResults[0]; } return activeTermRaw; }, [searchResults, activeTermRaw]); // Navigation list — search results or all terms const navList = useMemo(() => { return searchResults ?? allTerms; }, [searchResults, allTerms]); // Prev / next within nav list const currentIdx = navList.indexOf(activeTerm); const prev = currentIdx > 0 ? navList[currentIdx - 1] : null; const next = currentIdx >= 0 && currentIdx < navList.length - 1 ? navList[currentIdx + 1] : null; // Active entry data const activeEntry = useMemo(() => { const idx = termIndex.get(activeTerm); return idx !== undefined ? entries[idx] : null; }, [entries, termIndex, activeTerm]); // Update URL with ?term= when active term changes (for OG + shareable links) useEffect(() => { const url = new URL(window.location.href); if (activeTerm) { url.searchParams.set("term", activeTerm); } else { url.searchParams.delete("term"); } window.history.replaceState({}, "", url.toString()); }, [activeTerm]); const handleSelectTerm = useCallback((term: string) => { setActiveTerm(term); contentRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); }, []); return ( <> {/* Sticky header */}
← Home

Glossary: {activeEntry?.term || activeLetter}

{/* Letter navigation row */}
{/* Sidebar + Main */}
{/* Sidebar — terms for active letter */} {/* Main content */}
{/* Search bar */}
setQuery(e.target.value)} className="pl-10 pr-10" /> {query && ( )}
{/* Top nav */} {/* Search hint */} {searchResults && (

{searchResults.length === 0 ? "No entries match your search." : `${searchResults.length} result${searchResults.length !== 1 ? "s" : ""} for "${query}"`}

)} {/* Single entry view */} {activeEntry ? (

{activeEntry.term}

) : (

No entry selected

)} {/* Bottom nav */}
); }