feat: refine letters UI
This commit is contained in:
+1
-1
@@ -61,7 +61,7 @@ export default function AboutPage() {
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-10 space-y-14">
|
<main className="max-w-5xl mx-auto px-6 py-10 space-y-14">
|
||||||
{/* ---- Mission ---- */}
|
{/* ---- Mission ---- */}
|
||||||
<section className="space-y-4">
|
<section className="space-y-4">
|
||||||
<h2 className="text-2xl font-bold text-foreground">Our Mission</h2>
|
<h2 className="text-2xl font-bold text-foreground">Our Mission</h2>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function BbdChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/bbd"
|
href="/books/bbd"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function BbdChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<BbdSidebar items={sidebarItems} currentSlug={slug} />
|
<BbdSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function BbdPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export default async function VersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/bg"
|
href="/books/bg"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -72,7 +72,7 @@ export default async function VersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verse}
|
initialVerse={verse}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default async function BgPage() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card className="divide-y divide-border py-0">
|
<Card className="divide-y divide-border py-0">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ul className="divide-y divide-border">
|
<ul className="divide-y divide-border">
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default async function BkChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/bk"
|
href="/books/bk"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -56,7 +56,7 @@ export default async function BkChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<BkSidebar items={sidebarItems} currentSlug={slug} />
|
<BkSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function BkPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export default async function VersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/bs"
|
href="/books/bs"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -163,7 +163,7 @@ export default async function VersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verse}
|
initialVerse={verse}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function BsChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/bs"
|
href="/books/bs"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -50,7 +50,7 @@ export default async function BsChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="py-8 px-6 sm:px-8">
|
<CardContent className="py-8 px-6 sm:px-8">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export default async function BsPage() {
|
|||||||
)}
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Front Matter */}
|
{/* Front Matter */}
|
||||||
{(introduction || foreword) && (
|
{(introduction || foreword) && (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default async function CbhcChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/cbhc"
|
href="/books/cbhc"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -56,7 +56,7 @@ export default async function CbhcChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<CbhcSidebar items={sidebarItems} currentSlug={slug} />
|
<CbhcSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function CbhcPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default async function CcVersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href={`/books/cc/${lila}`}
|
href={`/books/cc/${lila}`}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -80,7 +80,7 @@ export default async function CcVersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verseData}
|
initialVerse={verseData}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default async function LilaPage({
|
|||||||
<ExtractedVerseCount count={totalVerses} label="extracted verses" />
|
<ExtractedVerseCount count={totalVerses} label="extracted verses" />
|
||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card className="divide-y divide-border">
|
<Card className="divide-y divide-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ul className="divide-y divide-border">
|
<ul className="divide-y divide-border">
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default async function CcPage() {
|
|||||||
<h1 className="mt-3 text-3xl font-bold text-foreground">{title}</h1>
|
<h1 className="mt-3 text-3xl font-bold text-foreground">{title}</h1>
|
||||||
<p className="mt-1 text-muted-foreground">{author}</p>
|
<p className="mt-1 text-muted-foreground">{author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card className="divide-y divide-border">
|
<Card className="divide-y divide-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ul className="divide-y divide-border">
|
<ul className="divide-y divide-border">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default async function DsChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/ds"
|
href="/books/ds"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -64,7 +64,7 @@ export default async function DsChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<DsSidebar items={sidebarItems} currentSlug={slug} />
|
<DsSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default async function DsPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function EjChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/ej"
|
href="/books/ej"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function EjChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<EjSidebar items={sidebarItems} currentSlug={slug} />
|
<EjSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default async function EjPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function EkChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/ek"
|
href="/books/ek"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function EkChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<EkSidebar items={sidebarItems} currentSlug={slug} />
|
<EkSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function EkPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default async function GgChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/gg"
|
href="/books/gg"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -60,7 +60,7 @@ export default async function GgChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<GgSidebar items={sidebarItems} currentSlug={slug} />
|
<GgSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function GgPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function HkhChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/hkh"
|
href="/books/hkh"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function HkhChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<HkhSidebar items={sidebarItems} currentSlug={slug} />
|
<HkhSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function HkhPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default async function IsoMantraPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/iso"
|
href="/books/iso"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -73,7 +73,7 @@ export default async function IsoMantraPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<IsoSidebar items={sidebarItems} currentSlug={slug} />
|
<IsoSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function IsoPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function KbChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href={`/books/kb/${vol}`}
|
href={`/books/kb/${vol}`}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -50,7 +50,7 @@ export default async function KbChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar — chapter index */}
|
{/* Left sidebar — chapter index */}
|
||||||
<ChapterSidebar
|
<ChapterSidebar
|
||||||
chapters={allChapters}
|
chapters={allChapters}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default async function KbVolumePage({
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="mt-1 text-muted-foreground">{chapters.length} chapters</p>
|
<p className="mt-1 text-muted-foreground">{chapters.length} chapters</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Front Matter */}
|
{/* Front Matter */}
|
||||||
{introSegments.length > 0 && (
|
{introSegments.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function KbPage() {
|
|||||||
<h1 className="mt-3 text-3xl font-bold text-foreground">{title}</h1>
|
<h1 className="mt-3 text-3xl font-bold text-foreground">{title}</h1>
|
||||||
<p className="mt-1 text-muted-foreground">{author}</p>
|
<p className="mt-1 text-muted-foreground">{author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
{description && (
|
{description && (
|
||||||
<p className="text-muted-foreground mb-6">{description}</p>
|
<p className="text-muted-foreground mb-6">{description}</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function LclChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/lcl"
|
href="/books/lcl"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function LclChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<LclSidebar items={sidebarItems} currentSlug={slug} />
|
<LclSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function LclPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function LobChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/lob"
|
href="/books/lob"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function LobChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<LobSidebar items={sidebarItems} currentSlug={slug} />
|
<LobSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function LobPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function MgChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/mg"
|
href="/books/mg"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function MgChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<MgSidebar items={sidebarItems} currentSlug={slug} />
|
<MgSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function MgPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export default async function VersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/mm"
|
href="/books/mm"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -163,7 +163,7 @@ export default async function VersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verse}
|
initialVerse={verse}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function MmChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/mm"
|
href="/books/mm"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -50,7 +50,7 @@ export default async function MmChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="py-8 px-6 sm:px-8">
|
<CardContent className="py-8 px-6 sm:px-8">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default async function MmPage() {
|
|||||||
)}
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Front Matter */}
|
{/* Front Matter */}
|
||||||
{introduction && (
|
{introduction && (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default async function MogChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/mog"
|
href="/books/mog"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -60,7 +60,7 @@ export default async function MogChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<MogSidebar items={sidebarItems} currentSlug={slug} />
|
<MogSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function MogPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export default async function VersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/nbs"
|
href="/books/nbs"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -163,7 +163,7 @@ export default async function VersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verse}
|
initialVerse={verse}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function NbsChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/nbs"
|
href="/books/nbs"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -50,7 +50,7 @@ export default async function NbsChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="py-8 px-6 sm:px-8">
|
<CardContent className="py-8 px-6 sm:px-8">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default async function NbsPage() {
|
|||||||
)}
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Front Matter */}
|
{/* Front Matter */}
|
||||||
{introduction && (
|
{introduction && (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function NodChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/nod"
|
href="/books/nod"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function NodChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<NodSidebar items={sidebarItems} currentSlug={slug} />
|
<NodSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default async function NodPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default async function NoiTextPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/noi"
|
href="/books/noi"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -73,7 +73,7 @@ export default async function NoiTextPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<NoiSidebar items={sidebarItems} currentSlug={slug} />
|
<NoiSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default async function NoiPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function OchkmChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/ochkm"
|
href="/books/ochkm"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function OchkmChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<OchkmSidebar items={sidebarItems} currentSlug={slug} />
|
<OchkmSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function OchkmPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function OwkChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/owk"
|
href="/books/owk"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function OwkChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<OwkSidebar items={sidebarItems} currentSlug={slug} />
|
<OwkSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function OwkPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
+1
-1
@@ -155,7 +155,7 @@ export default async function BooksPage() {
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<BooksGrid books={books} />
|
<BooksGrid books={books} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default async function PkChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/pk"
|
href="/books/pk"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -55,7 +55,7 @@ export default async function PkChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<PkSidebar items={sidebarItems} currentSlug={slug} />
|
<PkSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function PkPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function PoyChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/poy"
|
href="/books/poy"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function PoyChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<PoySidebar items={sidebarItems} currentSlug={slug} />
|
<PoySidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function PoyPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function PqpaChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/pqpa"
|
href="/books/pqpa"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function PqpaChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<PqpaSidebar items={sidebarItems} currentSlug={slug} />
|
<PqpaSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function PqpaPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function RvChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/rv"
|
href="/books/rv"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function RvChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<RvSidebar items={sidebarItems} currentSlug={slug} />
|
<RvSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function RvPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default async function SbVersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href={`/books/sb/${canto}`}
|
href={`/books/sb/${canto}`}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -77,7 +77,7 @@ export default async function SbVersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verseData}
|
initialVerse={verseData}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default async function CantoPage({
|
|||||||
<ExtractedVerseCount count={totalVerses} label="extracted verses" />
|
<ExtractedVerseCount count={totalVerses} label="extracted verses" />
|
||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card className="divide-y divide-border">
|
<Card className="divide-y divide-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ul className="divide-y divide-border">
|
<ul className="divide-y divide-border">
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default async function SbPage() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card className="divide-y divide-border">
|
<Card className="divide-y divide-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ul className="divide-y divide-border">
|
<ul className="divide-y divide-border">
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default async function VersePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/sb1962"
|
href="/books/sb1962"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
className="text-sm text-muted-foreground hover:text-foreground"
|
||||||
@@ -75,7 +75,7 @@ export default async function VersePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-7xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterViewer
|
<ChapterViewer
|
||||||
initialVerse={verse}
|
initialVerse={verse}
|
||||||
initialVerseNum={requestedVerse}
|
initialVerseNum={requestedVerse}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function Sb1962Page() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<Card className="divide-y divide-border py-0">
|
<Card className="divide-y divide-border py-0">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<ul className="divide-y divide-border">
|
<ul className="divide-y divide-border">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default async function SsrChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/ssr"
|
href="/books/ssr"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -64,7 +64,7 @@ export default async function SsrChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<SsrSidebar items={sidebarItems} currentSlug={slug} />
|
<SsrSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function SsrPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default async function SvaChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/sva"
|
href="/books/sva"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -65,7 +65,7 @@ export default async function SvaChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<SvaSidebar items={sidebarItems} currentSlug={slug} />
|
<SvaSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function SvaPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function TlcChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/tlc"
|
href="/books/tlc"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function TlcChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<TlcSidebar items={sidebarItems} currentSlug={slug} />
|
<TlcSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function TlcPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function TlkChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/tlk"
|
href="/books/tlk"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function TlkChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<TlkSidebar items={sidebarItems} currentSlug={slug} />
|
<TlkSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function TlkPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function TqkChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/tqk"
|
href="/books/tqk"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function TqkChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<TqkSidebar items={sidebarItems} currentSlug={slug} />
|
<TqkSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function TqkPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default async function TtpChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/ttp"
|
href="/books/ttp"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function TtpChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<TtpSidebar items={sidebarItems} currentSlug={slug} />
|
<TtpSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function TtpPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default async function VaChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/books/va"
|
href="/books/va"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -48,7 +48,7 @@ export default async function VaChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<VaSidebar items={sidebarItems} currentSlug={slug} />
|
<VaSidebar items={sidebarItems} currentSlug={slug} />
|
||||||
|
|
||||||
<main className="flex-1 min-w-0 px-6 py-8 space-y-8">
|
<main className="flex-1 min-w-0 px-6 py-8 space-y-8">
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default async function VaPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default async function DisciplesPage({
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<DisciplesBrowser
|
<DisciplesBrowser
|
||||||
initialDisciples={serializedDisciples}
|
initialDisciples={serializedDisciples}
|
||||||
initialLetter={activeLetter}
|
initialLetter={activeLetter}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export default async function EssayPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href="/essays"
|
href="/essays"
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -121,7 +121,7 @@ export default async function EssayPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<EssaySidebar items={allEssays} currentSlug={slug} />
|
<EssaySidebar items={allEssays} currentSlug={slug} />
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ export default async function EssaysPage() {
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<EssaysGrid essays={items} />
|
<EssaysGrid essays={items} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default async function GlossaryPage() {
|
|||||||
<p className="mt-1 text-muted-foreground">{data.description}</p>
|
<p className="mt-1 text-muted-foreground">{data.description}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<GlossaryClient data={data} />
|
<GlossaryClient data={data} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default async function JournalChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href={`/journals/${slug}`}
|
href={`/journals/${slug}`}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -53,7 +53,7 @@ export default async function JournalChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<ChapterSidebar
|
<ChapterSidebar
|
||||||
items={sidebarItems}
|
items={sidebarItems}
|
||||||
currentSlug={chapterSlug}
|
currentSlug={chapterSlug}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function JournalIndexPage({
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default async function JournalsPage() {
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{journals.map((journal) => (
|
{journals.map((journal) => (
|
||||||
<Link key={journal.book} href={`/journals/${bookCodeToSlug(journal.book)}`} className="block h-full">
|
<Link key={journal.book} href={`/journals/${bookCodeToSlug(journal.book)}`} className="block h-full">
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default async function LetterPage({
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Top nav */}
|
{/* Top nav */}
|
||||||
<LetterNav prev={prev} next={next} />
|
<LetterNav prev={prev} next={next} />
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,536 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useMemo, useRef, useEffect } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
|
import { ReadingSettings } from "@/components/reading-settings";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ChevronLeft, ChevronRight, Menu } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/components/ui/drawer";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Types
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export type LetterItem = {
|
||||||
|
slug: string;
|
||||||
|
recipientDisplay: string;
|
||||||
|
date: string | null;
|
||||||
|
year: number | null;
|
||||||
|
month: number | null;
|
||||||
|
location: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type YearGroup = {
|
||||||
|
year: number;
|
||||||
|
label: string;
|
||||||
|
count: number;
|
||||||
|
letters: LetterItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type MonthGroup = {
|
||||||
|
month: number;
|
||||||
|
label: string;
|
||||||
|
letters: LetterItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const MONTH_NAMES = [
|
||||||
|
"",
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
];
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Grouping helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function buildYearGroups(letters: LetterItem[]): YearGroup[] {
|
||||||
|
const map = new Map<number, LetterItem[]>();
|
||||||
|
|
||||||
|
for (const letter of letters) {
|
||||||
|
const y = letter.year ?? 0;
|
||||||
|
if (!map.has(y)) map.set(y, []);
|
||||||
|
map.get(y)!.push(letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups: YearGroup[] = [];
|
||||||
|
|
||||||
|
// Sorted years first, then undated (0) at the end
|
||||||
|
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,
|
||||||
|
letters: items,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const undated = map.get(0);
|
||||||
|
if (undated && undated.length > 0) {
|
||||||
|
groups.push({
|
||||||
|
year: 0,
|
||||||
|
label: "Undated",
|
||||||
|
count: undated.length,
|
||||||
|
letters: undated,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMonthGroups(letters: LetterItem[]): MonthGroup[] {
|
||||||
|
const map = new Map<number, LetterItem[]>();
|
||||||
|
|
||||||
|
for (const letter of letters) {
|
||||||
|
const m =
|
||||||
|
letter.month && letter.month >= 1 && letter.month <= 12
|
||||||
|
? letter.month
|
||||||
|
: 0;
|
||||||
|
if (!map.has(m)) map.set(m, []);
|
||||||
|
map.get(m)!.push(letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups: MonthGroup[] = [];
|
||||||
|
for (let m = 1; m <= 12; m++) {
|
||||||
|
const items = map.get(m);
|
||||||
|
if (items && items.length > 0) {
|
||||||
|
groups.push({ month: m, label: MONTH_NAMES[m], letters: items });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const unknown = map.get(0);
|
||||||
|
if (unknown && unknown.length > 0) {
|
||||||
|
groups.push({ month: 0, label: "Unknown Month", letters: unknown });
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Year Sidebar — matches book chapter sidebar
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function YearSidebar({
|
||||||
|
groups,
|
||||||
|
activeYear,
|
||||||
|
onSelect,
|
||||||
|
}: {
|
||||||
|
groups: YearGroup[];
|
||||||
|
activeYear: number;
|
||||||
|
onSelect: (year: number) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<aside className="hidden lg:block w-56 shrink-0 sticky top-[89px] self-start max-h-[calc(100vh-89px)] overflow-y-auto py-8 pr-4">
|
||||||
|
<h2 className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-3 px-2">
|
||||||
|
Years
|
||||||
|
</h2>
|
||||||
|
<nav className="flex flex-col gap-0.5">
|
||||||
|
{groups.map((g) => {
|
||||||
|
const isActive = g.year === activeYear;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={g.year}
|
||||||
|
onClick={() => onSelect(g.year)}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-between gap-2 px-2 py-1.5 rounded-md text-sm transition-colors text-left",
|
||||||
|
isActive
|
||||||
|
? "bg-primary/10 text-primary font-medium"
|
||||||
|
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="truncate">{g.label}</span>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-xs tabular-nums shrink-0",
|
||||||
|
isActive ? "text-primary/70" : "text-muted-foreground/60",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{g.count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Mobile drawer — matches MobileNav
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function YearMobileNav({
|
||||||
|
groups,
|
||||||
|
activeYear,
|
||||||
|
onSelect,
|
||||||
|
title,
|
||||||
|
}: {
|
||||||
|
groups: YearGroup[];
|
||||||
|
activeYear: number;
|
||||||
|
onSelect: (year: number) => void;
|
||||||
|
title: string;
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer open={open} onOpenChange={setOpen} direction="left">
|
||||||
|
<DrawerTrigger
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline", size: "sm" }),
|
||||||
|
"lg:hidden gap-1.5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Menu className="size-4" />
|
||||||
|
<span className="hidden sm:inline">Years</span>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent direction="left" className="h-full">
|
||||||
|
<DrawerHeader className="text-left border-b border-border pb-4 mb-2">
|
||||||
|
<DrawerTitle>{title}</DrawerTitle>
|
||||||
|
</DrawerHeader>
|
||||||
|
<nav className="overflow-y-auto px-4 pb-6 pt-2">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
{groups.map((g) => {
|
||||||
|
const isActive = g.year === activeYear;
|
||||||
|
return (
|
||||||
|
<DrawerClose key={g.year} asChild>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
onSelect(g.year);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-between gap-2 px-2 py-2 rounded-md text-sm transition-colors text-left",
|
||||||
|
isActive
|
||||||
|
? "bg-primary/10 text-primary font-medium"
|
||||||
|
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{g.label}</span>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-xs tabular-nums",
|
||||||
|
isActive
|
||||||
|
? "text-primary/70"
|
||||||
|
: "text-muted-foreground/60",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{g.count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</DrawerClose>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Prev / Next nav — matches BbdNav
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<YearMobileNav
|
||||||
|
groups={groups}
|
||||||
|
activeYear={activeYear}
|
||||||
|
onSelect={onSelect}
|
||||||
|
title={drawerTitle}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{prev ? (
|
||||||
|
<button
|
||||||
|
onClick={() => onSelect(prev.year)}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline", size: "sm" }),
|
||||||
|
"gap-1.5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="size-4" />
|
||||||
|
<span className="hidden sm:inline truncate max-w-[180px]">
|
||||||
|
{prev.label}
|
||||||
|
</span>
|
||||||
|
<span className="sm:hidden">Previous</span>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div className="hidden lg:block" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{next ? (
|
||||||
|
<button
|
||||||
|
onClick={() => onSelect(next.year)}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline", size: "sm" }),
|
||||||
|
"gap-1.5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="hidden sm:inline truncate max-w-[180px]">
|
||||||
|
{next.label}
|
||||||
|
</span>
|
||||||
|
<span className="sm:hidden">Next</span>
|
||||||
|
<ChevronRight className="size-4" />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Month section — collapsible group of letters for a single month
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function MonthSection({
|
||||||
|
group,
|
||||||
|
defaultOpen,
|
||||||
|
}: {
|
||||||
|
group: MonthGroup;
|
||||||
|
defaultOpen: boolean;
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(defaultOpen);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
className="flex items-center gap-3 w-full px-5 py-3 text-left hover:bg-muted/50 transition-colors"
|
||||||
|
>
|
||||||
|
<ChevronRight
|
||||||
|
className={cn(
|
||||||
|
"size-4 text-muted-foreground shrink-0 transition-transform",
|
||||||
|
open && "rotate-90",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span className="font-semibold text-foreground text-sm">
|
||||||
|
{group.label}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground tabular-nums">
|
||||||
|
{group.letters.length}{" "}
|
||||||
|
{group.letters.length === 1 ? "letter" : "letters"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{open && (
|
||||||
|
<ul className="divide-y divide-border">
|
||||||
|
{group.letters.map((letter) => (
|
||||||
|
<li key={letter.slug}>
|
||||||
|
<Link
|
||||||
|
href={`/letters/${letter.slug}`}
|
||||||
|
className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-5 pl-12 py-3.5 gap-1 sm:gap-4 hover:bg-muted transition-colors"
|
||||||
|
>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<p className="font-medium text-foreground text-sm">
|
||||||
|
To: {letter.recipientDisplay}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-0.5">
|
||||||
|
{letter.date || "Undated"}
|
||||||
|
{letter.location ? ` · ${letter.location}` : ""}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Main component — header + sidebar (years) + content (month-grouped letters)
|
||||||
|
//
|
||||||
|
// Props:
|
||||||
|
// letters — all letters for this filter (recipient or location)
|
||||||
|
// filterName — display name for the filter ("Mukunda", "New York", etc.)
|
||||||
|
// filterType — "recipient" | "location" (used for back link and title)
|
||||||
|
// totalCount — total letter count
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function FilterYearBrowser({
|
||||||
|
letters,
|
||||||
|
filterName,
|
||||||
|
filterType,
|
||||||
|
totalCount,
|
||||||
|
}: {
|
||||||
|
letters: LetterItem[];
|
||||||
|
filterName: string;
|
||||||
|
filterType: "recipient" | "location";
|
||||||
|
totalCount: number;
|
||||||
|
}) {
|
||||||
|
const yearGroups = useMemo(() => buildYearGroups(letters), [letters]);
|
||||||
|
|
||||||
|
const [activeYear, setActiveYear] = useState<number>(
|
||||||
|
yearGroups.length > 0 ? yearGroups[0].year : 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Keep active year valid
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
yearGroups.length > 0 &&
|
||||||
|
!yearGroups.find((g) => g.year === activeYear)
|
||||||
|
) {
|
||||||
|
setActiveYear(yearGroups[0].year);
|
||||||
|
}
|
||||||
|
}, [yearGroups, activeYear]);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Month groups within the active year
|
||||||
|
const monthGroups = useMemo(
|
||||||
|
() => buildMonthGroups(activeGroup?.letters ?? []),
|
||||||
|
[activeGroup],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Header text
|
||||||
|
const backHref =
|
||||||
|
filterType === "recipient"
|
||||||
|
? "/letters?view=recipient"
|
||||||
|
: "/letters?view=location";
|
||||||
|
const backLabel =
|
||||||
|
filterType === "recipient" ? "Letters by Recipient" : "Letters by Location";
|
||||||
|
const titlePrefix =
|
||||||
|
filterType === "recipient" ? "Letters to" : "Letters from";
|
||||||
|
const drawerTitle = `${titlePrefix} ${filterName}`;
|
||||||
|
const activeYearLabel = activeGroup?.label ?? "Unknown";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Header */}
|
||||||
|
<header className="border-b border-border bg-card px-6 py-4 sticky top-0 z-20">
|
||||||
|
<div className="max-w-5xl mx-auto flex items-start justify-between gap-4">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<Link
|
||||||
|
href={backHref}
|
||||||
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
← {backLabel}
|
||||||
|
</Link>
|
||||||
|
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||||
|
<span className="text-primary">
|
||||||
|
{titlePrefix} {filterName}:{" "}
|
||||||
|
</span>
|
||||||
|
{activeYearLabel}
|
||||||
|
</h1>
|
||||||
|
<p className="mt-1 text-muted-foreground">
|
||||||
|
{activeGroup?.count ?? 0}{" "}
|
||||||
|
{(activeGroup?.count ?? 0) === 1 ? "letter" : "letters"} in{" "}
|
||||||
|
{activeYearLabel} · {totalCount} total
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0 pt-1">
|
||||||
|
<ReadingSettings />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Sidebar + Main */}
|
||||||
|
<div className="max-w-5xl mx-auto flex">
|
||||||
|
{/* Left sidebar — years */}
|
||||||
|
<YearSidebar
|
||||||
|
groups={yearGroups}
|
||||||
|
activeYear={activeYear}
|
||||||
|
onSelect={handleYearSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<main ref={contentRef} className="flex-1 min-w-0 px-6 py-8 space-y-8">
|
||||||
|
{/* Top navigation */}
|
||||||
|
<YearNav
|
||||||
|
prev={prev}
|
||||||
|
next={next}
|
||||||
|
groups={yearGroups}
|
||||||
|
activeYear={activeYear}
|
||||||
|
onSelect={handleYearSelect}
|
||||||
|
drawerTitle={drawerTitle}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Content — letters grouped by month */}
|
||||||
|
{monthGroups.length === 0 ? (
|
||||||
|
<div className="py-12 text-center text-muted-foreground">
|
||||||
|
<p className="text-lg font-medium">No letters for this year</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Card className="overflow-hidden">
|
||||||
|
<CardContent className="p-0 divide-y divide-border">
|
||||||
|
{monthGroups.map((mg, i) => (
|
||||||
|
<MonthSection
|
||||||
|
key={mg.month}
|
||||||
|
group={mg}
|
||||||
|
defaultOpen={i === 0 || monthGroups.length <= 3}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Bottom navigation */}
|
||||||
|
<YearNav
|
||||||
|
prev={prev}
|
||||||
|
next={next}
|
||||||
|
groups={yearGroups}
|
||||||
|
activeYear={activeYear}
|
||||||
|
onSelect={handleYearSelect}
|
||||||
|
drawerTitle={drawerTitle}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getLettersByLocation } from "@/lib/letters";
|
import { getLettersByLocation } from "@/lib/letters";
|
||||||
import { PageHeader } from "@/components/page-header";
|
import { FilterYearBrowser } from "../../filter-year-browser";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
|
|
||||||
export default async function LettersByLocationPage({
|
export default async function LettersByLocationPage({
|
||||||
params,
|
params,
|
||||||
@@ -17,48 +15,23 @@ export default async function LettersByLocationPage({
|
|||||||
const letters = await getLettersByLocation(name);
|
const letters = await getLettersByLocation(name);
|
||||||
if (letters.length === 0) notFound();
|
if (letters.length === 0) notFound();
|
||||||
|
|
||||||
|
const serialized = letters.map((l) => ({
|
||||||
|
slug: l.slug,
|
||||||
|
recipientDisplay: l.recipientDisplay,
|
||||||
|
date: l.date,
|
||||||
|
year: l.year,
|
||||||
|
month: l.month,
|
||||||
|
location: l.location,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader>
|
<FilterYearBrowser
|
||||||
<Link
|
letters={serialized}
|
||||||
href="/letters?view=location"
|
filterName={name}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
filterType="location"
|
||||||
>
|
totalCount={letters.length}
|
||||||
← Letters by Location
|
/>
|
||||||
</Link>
|
|
||||||
<h1 className="mt-3 text-3xl font-bold text-foreground">
|
|
||||||
Letters from {name}
|
|
||||||
</h1>
|
|
||||||
<p className="mt-1 text-muted-foreground">
|
|
||||||
{letters.length} {letters.length === 1 ? "letter" : "letters"}
|
|
||||||
</p>
|
|
||||||
</PageHeader>
|
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
|
||||||
<Card className="divide-y divide-border">
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<ul className="divide-y divide-border">
|
|
||||||
{letters.map((letter) => (
|
|
||||||
<li key={letter.slug}>
|
|
||||||
<Link
|
|
||||||
href={`/letters/${letter.slug}`}
|
|
||||||
className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-5 py-4 gap-1 sm:gap-4 hover:bg-muted transition-colors"
|
|
||||||
>
|
|
||||||
<div className="min-w-0">
|
|
||||||
<p className="font-medium text-foreground">
|
|
||||||
To: {letter.recipientDisplay}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{letter.date || "Undated"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getLettersByRecipient } from "@/lib/letters";
|
import { getLettersByRecipient } from "@/lib/letters";
|
||||||
import { PageHeader } from "@/components/page-header";
|
import { FilterYearBrowser } from "../../filter-year-browser";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
|
|
||||||
export default async function LettersByRecipientPage({
|
export default async function LettersByRecipientPage({
|
||||||
params,
|
params,
|
||||||
@@ -17,49 +15,23 @@ export default async function LettersByRecipientPage({
|
|||||||
const letters = await getLettersByRecipient(name);
|
const letters = await getLettersByRecipient(name);
|
||||||
if (letters.length === 0) notFound();
|
if (letters.length === 0) notFound();
|
||||||
|
|
||||||
|
const serialized = letters.map((l) => ({
|
||||||
|
slug: l.slug,
|
||||||
|
recipientDisplay: l.recipientDisplay,
|
||||||
|
date: l.date,
|
||||||
|
year: l.year,
|
||||||
|
month: l.month,
|
||||||
|
location: l.location,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader>
|
<FilterYearBrowser
|
||||||
<Link
|
letters={serialized}
|
||||||
href="/letters?view=recipient"
|
filterName={name}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
filterType="recipient"
|
||||||
>
|
totalCount={letters.length}
|
||||||
← Letters by Recipient
|
/>
|
||||||
</Link>
|
|
||||||
<h1 className="mt-3 text-3xl font-bold text-foreground">
|
|
||||||
Letters to or including {name}
|
|
||||||
</h1>
|
|
||||||
<p className="mt-1 text-muted-foreground">
|
|
||||||
{letters.length} {letters.length === 1 ? "letter" : "letters"}
|
|
||||||
</p>
|
|
||||||
</PageHeader>
|
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
|
||||||
<Card className="divide-y divide-border">
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<ul className="divide-y divide-border">
|
|
||||||
{letters.map((letter) => (
|
|
||||||
<li key={letter.slug}>
|
|
||||||
<Link
|
|
||||||
href={`/letters/${letter.slug}`}
|
|
||||||
className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-5 py-4 gap-1 sm:gap-4 hover:bg-muted transition-colors"
|
|
||||||
>
|
|
||||||
<div className="min-w-0">
|
|
||||||
<p className="font-medium text-foreground">
|
|
||||||
To: {letter.recipientDisplay}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{letter.date || "Undated"}
|
|
||||||
{letter.location ? ` · ${letter.location}` : ""}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getLettersByYear } from "@/lib/letters";
|
import { getLettersByYear } from "@/lib/letters";
|
||||||
import { PageHeader } from "@/components/page-header";
|
import { YearMonthBrowser } from "./year-month-browser";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
|
||||||
|
|
||||||
export default async function LettersByYearPage({
|
export default async function LettersByYearPage({
|
||||||
params,
|
params,
|
||||||
@@ -18,49 +16,22 @@ export default async function LettersByYearPage({
|
|||||||
const letters = await getLettersByYear(year);
|
const letters = await getLettersByYear(year);
|
||||||
if (letters.length === 0) notFound();
|
if (letters.length === 0) notFound();
|
||||||
|
|
||||||
|
const serialized = letters.map((l) => ({
|
||||||
|
slug: l.slug,
|
||||||
|
recipientDisplay: l.recipientDisplay,
|
||||||
|
date: l.date,
|
||||||
|
year: l.year,
|
||||||
|
month: l.month,
|
||||||
|
location: l.location,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader>
|
<YearMonthBrowser
|
||||||
<Link
|
letters={serialized}
|
||||||
href="/letters?view=year"
|
year={year}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
totalCount={letters.length}
|
||||||
>
|
/>
|
||||||
← Letters by Year
|
|
||||||
</Link>
|
|
||||||
<h1 className="mt-3 text-3xl font-bold text-foreground">
|
|
||||||
Letters from {year}
|
|
||||||
</h1>
|
|
||||||
<p className="mt-1 text-muted-foreground">
|
|
||||||
{letters.length} {letters.length === 1 ? "letter" : "letters"}
|
|
||||||
</p>
|
|
||||||
</PageHeader>
|
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8">
|
|
||||||
<Card className="divide-y divide-border">
|
|
||||||
<CardContent className="p-0">
|
|
||||||
<ul className="divide-y divide-border">
|
|
||||||
{letters.map((letter) => (
|
|
||||||
<li key={letter.slug}>
|
|
||||||
<Link
|
|
||||||
href={`/letters/${letter.slug}`}
|
|
||||||
className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-5 py-4 gap-1 sm:gap-4 hover:bg-muted transition-colors"
|
|
||||||
>
|
|
||||||
<div className="min-w-0">
|
|
||||||
<p className="font-medium text-foreground">
|
|
||||||
To: {letter.recipientDisplay}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{letter.date}
|
|
||||||
{letter.location ? ` · ${letter.location}` : ""}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,451 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useMemo, useRef, useEffect } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
|
import { ReadingSettings } from "@/components/reading-settings";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ChevronLeft, ChevronRight, Menu } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/components/ui/drawer";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Types
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export type LetterItem = {
|
||||||
|
slug: string;
|
||||||
|
recipientDisplay: string;
|
||||||
|
date: string | null;
|
||||||
|
year: number | null;
|
||||||
|
month: number | null;
|
||||||
|
location: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MonthGroup = {
|
||||||
|
month: number;
|
||||||
|
label: string;
|
||||||
|
shortLabel: string;
|
||||||
|
count: number;
|
||||||
|
letters: LetterItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const MONTH_NAMES = [
|
||||||
|
"",
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
];
|
||||||
|
|
||||||
|
const MONTH_SHORT = [
|
||||||
|
"",
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
];
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Build month groups from flat letter list
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function buildMonthGroups(letters: LetterItem[]): MonthGroup[] {
|
||||||
|
const map = new Map<number, LetterItem[]>();
|
||||||
|
|
||||||
|
for (const letter of letters) {
|
||||||
|
const m =
|
||||||
|
letter.month && letter.month >= 1 && letter.month <= 12
|
||||||
|
? letter.month
|
||||||
|
: 0;
|
||||||
|
if (!map.has(m)) map.set(m, []);
|
||||||
|
map.get(m)!.push(letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups: MonthGroup[] = [];
|
||||||
|
for (let m = 1; m <= 12; m++) {
|
||||||
|
const items = map.get(m);
|
||||||
|
if (items && items.length > 0) {
|
||||||
|
groups.push({
|
||||||
|
month: m,
|
||||||
|
label: MONTH_NAMES[m],
|
||||||
|
shortLabel: MONTH_SHORT[m],
|
||||||
|
count: items.length,
|
||||||
|
letters: items,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const unknown = map.get(0);
|
||||||
|
if (unknown && unknown.length > 0) {
|
||||||
|
groups.push({
|
||||||
|
month: 0,
|
||||||
|
label: "Unknown Month",
|
||||||
|
shortLabel: "Unknown",
|
||||||
|
count: unknown.length,
|
||||||
|
letters: unknown,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Sidebar — matches book chapter sidebar
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function MonthSidebar({
|
||||||
|
groups,
|
||||||
|
activeMonth,
|
||||||
|
onSelect,
|
||||||
|
}: {
|
||||||
|
groups: MonthGroup[];
|
||||||
|
activeMonth: number;
|
||||||
|
onSelect: (month: number) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<aside className="hidden lg:block w-56 shrink-0 sticky top-[89px] self-start max-h-[calc(100vh-89px)] overflow-y-auto py-8 pr-4">
|
||||||
|
<h2 className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-3 px-2">
|
||||||
|
Months
|
||||||
|
</h2>
|
||||||
|
<nav className="flex flex-col gap-0.5">
|
||||||
|
{groups.map((g) => {
|
||||||
|
const isActive = g.month === activeMonth;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={g.month}
|
||||||
|
onClick={() => onSelect(g.month)}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-between gap-2 px-2 py-1.5 rounded-md text-sm transition-colors text-left",
|
||||||
|
isActive
|
||||||
|
? "bg-primary/10 text-primary font-medium"
|
||||||
|
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="truncate">{g.label}</span>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-xs tabular-nums shrink-0",
|
||||||
|
isActive ? "text-primary/70" : "text-muted-foreground/60",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{g.count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Mobile drawer — matches MobileNav
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function MonthMobileNav({
|
||||||
|
groups,
|
||||||
|
activeMonth,
|
||||||
|
onSelect,
|
||||||
|
year,
|
||||||
|
}: {
|
||||||
|
groups: MonthGroup[];
|
||||||
|
activeMonth: number;
|
||||||
|
onSelect: (month: number) => void;
|
||||||
|
year: number;
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer open={open} onOpenChange={setOpen} direction="left">
|
||||||
|
<DrawerTrigger
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline", size: "sm" }),
|
||||||
|
"lg:hidden gap-1.5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Menu className="size-4" />
|
||||||
|
<span className="hidden sm:inline">Months</span>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent direction="left" className="h-full">
|
||||||
|
<DrawerHeader className="text-left border-b border-border pb-4 mb-2">
|
||||||
|
<DrawerTitle>Letters from {year}</DrawerTitle>
|
||||||
|
</DrawerHeader>
|
||||||
|
<nav className="overflow-y-auto px-4 pb-6 pt-2">
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
{groups.map((g) => {
|
||||||
|
const isActive = g.month === activeMonth;
|
||||||
|
return (
|
||||||
|
<DrawerClose key={g.month} asChild>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
onSelect(g.month);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-between gap-2 px-2 py-2 rounded-md text-sm transition-colors text-left",
|
||||||
|
isActive
|
||||||
|
? "bg-primary/10 text-primary font-medium"
|
||||||
|
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{g.label}</span>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-xs tabular-nums",
|
||||||
|
isActive
|
||||||
|
? "text-primary/70"
|
||||||
|
: "text-muted-foreground/60",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{g.count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</DrawerClose>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Prev / Next nav — matches BbdNav
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function MonthNav({
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
groups,
|
||||||
|
activeMonth,
|
||||||
|
onSelect,
|
||||||
|
year,
|
||||||
|
}: {
|
||||||
|
prev: MonthGroup | null;
|
||||||
|
next: MonthGroup | null;
|
||||||
|
groups: MonthGroup[];
|
||||||
|
activeMonth: number;
|
||||||
|
onSelect: (month: number) => void;
|
||||||
|
year: number;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<MonthMobileNav
|
||||||
|
groups={groups}
|
||||||
|
activeMonth={activeMonth}
|
||||||
|
onSelect={onSelect}
|
||||||
|
year={year}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{prev ? (
|
||||||
|
<button
|
||||||
|
onClick={() => onSelect(prev.month)}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline", size: "sm" }),
|
||||||
|
"gap-1.5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="size-4" />
|
||||||
|
<span className="hidden sm:inline truncate max-w-[180px]">
|
||||||
|
{prev.shortLabel}
|
||||||
|
</span>
|
||||||
|
<span className="sm:hidden">Previous</span>
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div className="hidden lg:block" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{next ? (
|
||||||
|
<button
|
||||||
|
onClick={() => onSelect(next.month)}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({ variant: "outline", size: "sm" }),
|
||||||
|
"gap-1.5",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="hidden sm:inline truncate max-w-[180px]">
|
||||||
|
{next.shortLabel}
|
||||||
|
</span>
|
||||||
|
<span className="sm:hidden">Next</span>
|
||||||
|
<ChevronRight className="size-4" />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Main component — renders header + sidebar + content (entire page layout)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function YearMonthBrowser({
|
||||||
|
letters,
|
||||||
|
year,
|
||||||
|
totalCount,
|
||||||
|
}: {
|
||||||
|
letters: LetterItem[];
|
||||||
|
year: number;
|
||||||
|
totalCount: number;
|
||||||
|
}) {
|
||||||
|
const allGroups = useMemo(() => buildMonthGroups(letters), [letters]);
|
||||||
|
|
||||||
|
const [activeMonth, setActiveMonth] = useState<number>(
|
||||||
|
allGroups.length > 0 ? allGroups[0].month : 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Keep active month valid
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
allGroups.length > 0 &&
|
||||||
|
!allGroups.find((g) => g.month === activeMonth)
|
||||||
|
) {
|
||||||
|
setActiveMonth(allGroups[0].month);
|
||||||
|
}
|
||||||
|
}, [allGroups, activeMonth]);
|
||||||
|
|
||||||
|
const handleMonthSelect = (month: number) => {
|
||||||
|
setActiveMonth(month);
|
||||||
|
contentRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Active group + neighbors
|
||||||
|
const activeIdx = allGroups.findIndex((g) => g.month === activeMonth);
|
||||||
|
const activeGroup = activeIdx >= 0 ? allGroups[activeIdx] : null;
|
||||||
|
const prev = activeIdx > 0 ? allGroups[activeIdx - 1] : null;
|
||||||
|
const next =
|
||||||
|
activeIdx >= 0 && activeIdx < allGroups.length - 1
|
||||||
|
? allGroups[activeIdx + 1]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const activeLetters = activeGroup?.letters ?? [];
|
||||||
|
const activeLabel = activeGroup?.label ?? "Unknown Month";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Header */}
|
||||||
|
<header className="border-b border-border bg-card px-6 py-4 sticky top-0 z-20">
|
||||||
|
<div className="max-w-5xl mx-auto flex items-start justify-between gap-4">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<Link
|
||||||
|
href="/letters?view=year"
|
||||||
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
← Letters by Year
|
||||||
|
</Link>
|
||||||
|
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||||
|
<span className="text-primary">Letters: </span>
|
||||||
|
{activeLabel} {year}
|
||||||
|
</h1>
|
||||||
|
<p className="mt-1 text-muted-foreground">
|
||||||
|
{activeGroup?.count ?? 0}{" "}
|
||||||
|
{(activeGroup?.count ?? 0) === 1 ? "letter" : "letters"} ·{" "}
|
||||||
|
{totalCount} total in {year}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0 pt-1">
|
||||||
|
<ReadingSettings />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Sidebar + Main */}
|
||||||
|
<div className="max-w-5xl mx-auto flex">
|
||||||
|
{/* Left sidebar */}
|
||||||
|
<MonthSidebar
|
||||||
|
groups={allGroups}
|
||||||
|
activeMonth={activeMonth}
|
||||||
|
onSelect={handleMonthSelect}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<main ref={contentRef} className="flex-1 min-w-0 px-6 py-8 space-y-8">
|
||||||
|
{/* Top navigation */}
|
||||||
|
<MonthNav
|
||||||
|
prev={prev}
|
||||||
|
next={next}
|
||||||
|
groups={allGroups}
|
||||||
|
activeMonth={activeMonth}
|
||||||
|
onSelect={handleMonthSelect}
|
||||||
|
year={year}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
{activeLetters.length === 0 ? (
|
||||||
|
<div className="py-12 text-center text-muted-foreground">
|
||||||
|
<p className="text-lg font-medium">No letters for this month</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-0">
|
||||||
|
<ul className="divide-y divide-border">
|
||||||
|
{activeLetters.map((letter) => (
|
||||||
|
<li key={letter.slug}>
|
||||||
|
<Link
|
||||||
|
href={`/letters/${letter.slug}`}
|
||||||
|
className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-5 py-4 gap-1 sm:gap-4 hover:bg-muted transition-colors"
|
||||||
|
>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="font-medium text-foreground">
|
||||||
|
To: {letter.recipientDisplay}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{letter.date || "Undated"}
|
||||||
|
{letter.location ? ` · ${letter.location}` : ""}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Bottom navigation */}
|
||||||
|
<MonthNav
|
||||||
|
prev={prev}
|
||||||
|
next={next}
|
||||||
|
groups={allGroups}
|
||||||
|
activeMonth={activeMonth}
|
||||||
|
onSelect={handleMonthSelect}
|
||||||
|
year={year}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ export default async function MagazineIssuePage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href={`/magazines/${slug}`}
|
href={`/magazines/${slug}`}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -52,7 +52,7 @@ export default async function MagazineIssuePage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
<IssueSidebar
|
<IssueSidebar
|
||||||
items={sidebarItems}
|
items={sidebarItems}
|
||||||
currentSlug={issueSlug}
|
currentSlug={issueSlug}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function MagazineIndexPage({
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default async function MagazinesPage() {
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8 space-y-10">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-10">
|
||||||
{sortedGroups.map((group) => (
|
{sortedGroups.map((group) => (
|
||||||
<div key={group}>
|
<div key={group}>
|
||||||
{showGroupHeadings && (
|
{showGroupHeadings && (
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default async function OtherDocumentChapterPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<PageHeader sticky wide>
|
<PageHeader sticky>
|
||||||
<Link
|
<Link
|
||||||
href={`/other-documents/${slug}`}
|
href={`/other-documents/${slug}`}
|
||||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||||
@@ -58,7 +58,7 @@ export default async function OtherDocumentChapterPage({
|
|||||||
</h1>
|
</h1>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="w-full md:max-w-6xl mx-auto flex">
|
<div className="max-w-5xl mx-auto flex">
|
||||||
{/* Left sidebar */}
|
{/* Left sidebar */}
|
||||||
<ChapterSidebar
|
<ChapterSidebar
|
||||||
items={sidebarItems}
|
items={sidebarItems}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default async function OtherDocumentIndexPage({
|
|||||||
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
<p className="mt-1 text-muted-foreground">{meta.author}</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-8 space-y-8">
|
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{meta.description && (
|
{meta.description && (
|
||||||
<p className="text-muted-foreground">{meta.description}</p>
|
<p className="text-muted-foreground">{meta.description}</p>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default async function OtherDocumentsPage() {
|
|||||||
</p>
|
</p>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<OtherDocumentsGrid items={items} />
|
<OtherDocumentsGrid items={items} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ export default function Home() {
|
|||||||
<Logo height={56} className="mb-1" />
|
<Logo height={56} className="mb-1" />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{/* Structured Books (JSON) */}
|
{/* Structured Books (JSON) */}
|
||||||
<Link href="/books" className="block group h-full">
|
<Link href="/books" className="block group h-full">
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user