107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
export const dynamic = "force-dynamic";
|
|
|
|
import type { Metadata } from "next";
|
|
import { notFound } from "next/navigation";
|
|
import { getAllGlossaryEntries } from "@/lib/api";
|
|
import { buildOgMeta } from "@/lib/og";
|
|
import { GlossaryClient } from "../glossary-client";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Diacritic → base letter mapping
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const DIACRITIC_TO_BASE: Record<string, string> = {
|
|
Ā: "A", Ä: "A",
|
|
Ī: "I", É: "I",
|
|
Ū: "U", Ü: "U",
|
|
Ṛ: "R", Ṝ: "R",
|
|
Ś: "S", Ç: "S", Ṣ: "S",
|
|
Ñ: "N", Ṇ: "N", Ṅ: "N",
|
|
Ṭ: "T",
|
|
Ḍ: "D",
|
|
Ḥ: "H",
|
|
};
|
|
|
|
function getBaseLetter(term: string): string {
|
|
if (!term) return "?";
|
|
const first = term[0].toUpperCase();
|
|
return DIACRITIC_TO_BASE[first] || first;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Metadata
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ letter: string }>;
|
|
}): Promise<Metadata> {
|
|
const { letter } = await params;
|
|
const l = letter.toUpperCase();
|
|
return buildOgMeta({
|
|
title: `Glossary — ${l}`,
|
|
detail: `Sanskrit & Vedic terms starting with ${l}`,
|
|
quote:
|
|
"A comprehensive glossary of Sanskrit and Vedic terms from the books of Śrīla Prabhupāda.",
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Page
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export default async function GlossaryLetterPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ letter: string }>;
|
|
}) {
|
|
const { letter: rawLetter } = await params;
|
|
const activeLetter = rawLetter.toUpperCase();
|
|
|
|
// Validate — single letter A-Z
|
|
if (!/^[A-Z]$/.test(activeLetter)) notFound();
|
|
|
|
// Fetch all entries (cached by Next.js request dedup within a single render)
|
|
const allEntries = await getAllGlossaryEntries();
|
|
|
|
// Group all entries by base letter
|
|
const byLetter: Record<string, typeof allEntries> = {};
|
|
for (const entry of allEntries) {
|
|
const base = getBaseLetter(entry.term);
|
|
if (!byLetter[base]) byLetter[base] = [];
|
|
byLetter[base].push(entry);
|
|
}
|
|
|
|
// All available letters (sorted)
|
|
const letters = Object.keys(byLetter).sort();
|
|
|
|
// Entries for the active letter
|
|
const letterEntries = byLetter[activeLetter] || [];
|
|
if (letterEntries.length === 0) notFound();
|
|
|
|
// Prepare client data — only the active letter's entries
|
|
const clientEntries = letterEntries.map((e) => ({
|
|
term: e.term,
|
|
definition: e.definition,
|
|
definition_html: e.definitionHtml,
|
|
}));
|
|
|
|
// Letter counts for the nav
|
|
const letterCounts: Record<string, number> = {};
|
|
for (const [l, entries] of Object.entries(byLetter)) {
|
|
letterCounts[l] = entries.length;
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background">
|
|
<GlossaryClient
|
|
entries={clientEntries}
|
|
activeLetter={activeLetter}
|
|
letters={letters}
|
|
letterCounts={letterCounts}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|