Compare commits
123
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f855699e72 | ||
|
|
52cd48b12e | ||
|
|
0a23ab59f7 | ||
|
|
3af4e1bcd3 | ||
|
|
fed12b0d46 | ||
|
|
d64485daa4 | ||
|
|
0968b6046d | ||
|
|
8dd72de359 | ||
|
|
8537eb6ce2 | ||
|
|
09dc734613 | ||
|
|
5832125ab2 | ||
|
|
0e2e0d7300 | ||
|
|
6a6143ceb2 | ||
|
|
9f67da37a6 | ||
|
|
cce8a4c05f | ||
|
|
b391e1c575 | ||
|
|
3ce08428c8 | ||
|
|
bf8db28a7c | ||
|
|
cc9ea45c61 | ||
|
|
7a9d362b15 | ||
|
|
c001eeab45 | ||
|
|
2b0f966f90 | ||
|
|
2597c6f2ec | ||
|
|
8c7afe596d | ||
|
|
0f382ed885 | ||
|
|
03c8799f22 | ||
|
|
011b5bba91 | ||
|
|
3e6a1c6f39 | ||
|
|
76cd106ef7 | ||
|
|
028a8d7e1c | ||
|
|
290ceb048f | ||
|
|
523b6fa7e8 | ||
|
|
3257efb06a | ||
|
|
6083a485de | ||
|
|
baf36d3d67 | ||
|
|
df49e7afcc | ||
|
|
ce373e3e03 | ||
|
|
9b7758da99 | ||
|
|
6ff29db68a | ||
|
|
274c735b4d | ||
|
|
1984797ad3 | ||
|
|
1582df0ed5 | ||
|
|
1683abc2e9 | ||
|
|
12a5c15121 | ||
|
|
8bc27bb8a1 | ||
|
|
89391279bb | ||
|
|
7f716397a0 | ||
|
|
4586f1c22c | ||
|
|
b6471f9430 | ||
|
|
380c23e469 | ||
|
|
0b67a1142f | ||
|
|
be28585294 | ||
|
|
1462844e95 | ||
|
|
9916810d18 | ||
|
|
7b81bbc791 | ||
|
|
f92b41384e | ||
|
|
5cd75c27f9 | ||
|
|
5679f7a639 | ||
|
|
25db9d0eee | ||
|
|
0178f89f2b | ||
|
|
b6dc762984 | ||
|
|
1fe10adb1e | ||
|
|
84cb273941 | ||
|
|
071eb4d58a | ||
|
|
bc1798b31b | ||
|
|
fc2aef0c3d | ||
|
|
89f94c1a6e | ||
|
|
f5abe33c09 | ||
|
|
f96e7dd2f1 | ||
|
|
0cf6df5942 | ||
|
|
5c72416ae8 | ||
|
|
197e13c0ef | ||
|
|
0608e2b6f6 | ||
|
|
cb376f8497 | ||
|
|
eac56b7974 | ||
|
|
be4c2f17fe | ||
|
|
6cc9285bad | ||
|
|
ab35767374 | ||
|
|
c551d2f7a8 | ||
|
|
a4fd63695a | ||
|
|
91621693a6 | ||
|
|
79f0bd9223 | ||
|
|
7585e6cb31 | ||
|
|
c8de98c8de | ||
|
|
72a092b3c9 | ||
|
|
752ad20fed | ||
|
|
0280a81777 | ||
|
|
69e5f2c45c | ||
|
|
45c7cf047e | ||
|
|
a4baf6d215 | ||
|
|
455ed0f7cb | ||
|
|
7c3cf656b5 | ||
|
|
23d06a3db5 | ||
|
|
3accc753d0 | ||
|
|
b04ed1d3aa | ||
|
|
9808dd3033 | ||
|
|
b248f962a1 | ||
|
|
61581b67a7 | ||
|
|
eb0c981d3c | ||
|
|
37305201bd | ||
|
|
4471ef10aa | ||
|
|
397a09e814 | ||
|
|
9e68184750 | ||
|
|
439664a97d | ||
|
|
cc5633c37a | ||
|
|
6a0559e608 | ||
|
|
25a729959f | ||
|
|
af5ab9cf6b | ||
|
|
884a984e50 | ||
|
|
ead8ad02e8 | ||
|
|
4b159cb4d9 | ||
|
|
3903423d06 | ||
|
|
a46e29d998 | ||
|
|
922d76e21f | ||
|
|
909ae67320 | ||
|
|
482a845f37 | ||
|
|
647a803b96 | ||
|
|
e36afdd1a5 | ||
|
|
ef6e280524 | ||
|
|
cf733062f8 | ||
|
|
421b41cade | ||
|
|
deeda40c9d | ||
|
|
90df5bc784 |
@@ -1,3 +1,5 @@
|
||||
NEXT_PUBLIC_SITE_URL=https://prabhupadasays.org
|
||||
|
||||
# BFF API Server (server-side fetches)
|
||||
BFF_URL=http://localhost:8080
|
||||
|
||||
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function BbdChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/bbd/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Beyond Birth and Death" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/bbd"
|
||||
@@ -77,12 +81,15 @@ export default async function BbdChapterPage({
|
||||
>
|
||||
← Beyond Birth and Death
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function BbdChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBbdMeta, getBbdIntroSegments } from "@/lib/bbd";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function BbdPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/bbd", name: "Beyond Birth and Death" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+10
-6
@@ -1,10 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -101,9 +102,12 @@ export default async function VersePage({
|
||||
>
|
||||
← Bhagavad-gītā
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -0,0 +1,86 @@
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { notFound } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { getChapterVerseIndex, getProseChapter } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ chapter: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { chapter } = await params;
|
||||
// Non-numeric segments are front matter (dedication, foreword, …).
|
||||
if (isNaN(parseInt(chapter))) {
|
||||
const segment = await getProseChapter("bg", 0, chapter);
|
||||
return buildOgMeta({
|
||||
title: BOOK_NAMES["bg"],
|
||||
detail: segment?.title ?? "Front Matter",
|
||||
});
|
||||
}
|
||||
return buildOgMeta({ title: BOOK_NAMES["bg"], detail: `Chapter ${chapter}` });
|
||||
}
|
||||
|
||||
export default async function ChapterPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ chapter: string }>;
|
||||
}) {
|
||||
const { chapter } = await params;
|
||||
|
||||
// Front matter — non-numeric slugs (dedication, foreword, preface,
|
||||
// introduction, disciplic-succession) resolve to prose intro-segments.
|
||||
if (isNaN(parseInt(chapter))) {
|
||||
const segment = await getProseChapter("bg", 0, chapter);
|
||||
if (!segment) notFound();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/bg"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
← {BOOK_NAMES["bg"]}
|
||||
</Link>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{segment.paragraphs.map((paragraph, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="text-base text-foreground leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: paragraph }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Numeric chapters — redirect to the first verse.
|
||||
const verseIndex = await getChapterVerseIndex("bg", null, parseInt(chapter));
|
||||
if (verseIndex.length === 0) {
|
||||
notFound();
|
||||
}
|
||||
redirect(`/books/bg/${chapter}/${verseIndex[0].verse}`);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta, getProseChapters } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
return ogForBook("bg");
|
||||
}
|
||||
|
||||
type ChapterInfo = {
|
||||
number: number;
|
||||
title: string;
|
||||
totalVerses: number;
|
||||
};
|
||||
|
||||
export default async function BgPage() {
|
||||
const [meta, introSegments] = await Promise.all([
|
||||
getBookMeta("bg"),
|
||||
getProseChapters("bg", 0, "intro-segment"),
|
||||
]);
|
||||
if (!meta) notFound();
|
||||
|
||||
const intro = meta as unknown as {
|
||||
book: string;
|
||||
title: string;
|
||||
author: string;
|
||||
totalChapters: number;
|
||||
chapters: ChapterInfo[];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/bg", name: "Bhagavad-gītā As It Is" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
← Books
|
||||
</Link>
|
||||
<h1 className="mt-3 text-3xl font-bold text-foreground">
|
||||
{intro.title}
|
||||
</h1>
|
||||
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
||||
</PageHeader>
|
||||
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||
{/* Front Matter */}
|
||||
{introSegments.length > 0 && (
|
||||
<div>
|
||||
<h2 className="text-sm font-bold uppercase tracking-wider text-muted-foreground mb-3">
|
||||
Front Matter
|
||||
</h2>
|
||||
<Card className="divide-y divide-border py-0">
|
||||
<CardContent className="p-0">
|
||||
<ul className="divide-y divide-border">
|
||||
{introSegments.map((seg) => (
|
||||
<li key={seg.slug}>
|
||||
<Link
|
||||
href={`/books/bg/${seg.slug}`}
|
||||
className="flex px-5 py-4 hover:bg-muted transition-colors"
|
||||
>
|
||||
<p className="font-medium text-foreground">
|
||||
{seg.title}
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Chapters */}
|
||||
<div>
|
||||
<h2 className="text-sm font-bold uppercase tracking-wider text-muted-foreground mb-3">
|
||||
Chapters
|
||||
</h2>
|
||||
<Card className="divide-y divide-border py-0">
|
||||
<CardContent className="p-0">
|
||||
<ul className="divide-y divide-border">
|
||||
{intro.chapters.map((ch) => (
|
||||
<li key={ch.number}>
|
||||
<Link
|
||||
href={`/books/bg/${ch.number}`}
|
||||
className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-5 py-4 gap-1 sm:gap-0 hover:bg-muted transition-colors"
|
||||
>
|
||||
<div>
|
||||
<span className="text-sm text-primary font-medium">
|
||||
Chapter {ch.number}
|
||||
</span>
|
||||
<p className="font-medium text-foreground">
|
||||
{ch.title}
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground sm:shrink-0 sm:ml-4">
|
||||
<ExtractedVerseCount count={ch.totalVerses} />
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+16
-9
@@ -10,12 +10,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -62,6 +65,7 @@ export default async function BkChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/bk/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Bhagavāner Kathā" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/bk"
|
||||
@@ -69,12 +73,15 @@ export default async function BkChapterPage({
|
||||
>
|
||||
← Bhagavāner Kathā
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -92,7 +99,7 @@ export default async function BkChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBkMeta } from "@/lib/bk";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -18,6 +20,7 @@ export default async function BkPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/bk", name: "Bhagavāner Kathā" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
@@ -72,11 +72,16 @@ export function BooksGrid({ books }: { books: BookInfo[] }) {
|
||||
);
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
if (!query.trim()) return books;
|
||||
const q = normalize(query.trim());
|
||||
return normalizedBooks
|
||||
.filter((entry) => entry.searchText.includes(q))
|
||||
.map((entry) => entry.book);
|
||||
const base = query.trim()
|
||||
? normalizedBooks
|
||||
.filter((entry) =>
|
||||
entry.searchText.includes(normalize(query.trim())),
|
||||
)
|
||||
.map((entry) => entry.book)
|
||||
: books;
|
||||
return [...base].sort((a, b) =>
|
||||
normalize(a.title).localeCompare(normalize(b.title)),
|
||||
);
|
||||
}, [books, normalizedBooks, query]);
|
||||
|
||||
return (
|
||||
+10
-6
@@ -2,7 +2,8 @@ import type { Metadata } from "next";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import {
|
||||
getBookMeta,
|
||||
@@ -11,8 +12,8 @@ import {
|
||||
type VerseIndexEntry,
|
||||
type VerseRecord,
|
||||
} from "@/lib/api";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -187,9 +188,12 @@ export default async function VersePage({
|
||||
>
|
||||
← Śrī Brahma-saṁhitā
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
+9
-5
@@ -7,7 +7,8 @@ import { getChapterVerseIndex } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import Link from "next/link";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
@@ -59,13 +60,16 @@ export default async function BsChapterPage({
|
||||
>
|
||||
← Śrī Brahma-saṁhitā
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{segment.paragraphs.map((paragraph: string, i: number) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
import fs from "fs";
|
||||
@@ -80,6 +82,7 @@ export default async function BsPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/bs", name: "Śrī Brahma-saṁhitā" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -10,12 +10,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -62,6 +65,7 @@ export default async function CbhcChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/cbhc/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Śrī Caitanya-bhāgavata Commentary" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/cbhc"
|
||||
@@ -69,12 +73,15 @@ export default async function CbhcChapterPage({
|
||||
>
|
||||
← Śrī Caitanya-bhāgavata Commentary
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -92,7 +99,7 @@ export default async function CbhcChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getCbhcMeta } from "@/lib/cbhc";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -18,6 +20,7 @@ export default async function CbhcPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/cbhc", name: "Śrī Caitanya-bhāgavata Commentary" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+10
-6
@@ -2,11 +2,12 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
|
||||
type ChapterInfo = {
|
||||
@@ -111,9 +112,12 @@ export default async function CcVersePage({
|
||||
>
|
||||
← {lilaInfo.name}
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {chapterInfo.title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {chapterInfo.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -0,0 +1,227 @@
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { redirect, notFound } from "next/navigation";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { getBookMeta, getProseChapter, getChapterVerseIndex } from "@/lib/api";
|
||||
import { CC_LILAS } from "@/lib/constants";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type ChapterInfo = { number: number; title: string; totalVerses: number };
|
||||
type LilaInfo = {
|
||||
key: string;
|
||||
name: string;
|
||||
totalChapters: number;
|
||||
totalVerses: number;
|
||||
chapters: ChapterInfo[];
|
||||
};
|
||||
type VerseEntry = { verse: number; verseEnd?: number | null; reference: string };
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ lila: string; chapter: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { lila, chapter } = await params;
|
||||
const lilaName = CC_LILAS[lila] || lila;
|
||||
return buildOgMeta({
|
||||
title: "Śrī Caitanya-caritāmṛta",
|
||||
detail: `${lilaName}, Chapter ${chapter}`,
|
||||
});
|
||||
}
|
||||
|
||||
function verseLabel(v: VerseEntry): string {
|
||||
return v.verseEnd ? `${v.verse}–${v.verseEnd}` : String(v.verse);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Top/bottom navigation — mirrors the verse pages' VerseNav: a Contents
|
||||
// toggle (mobile) listing this chapter's verses, Previous/Next (chapter), and
|
||||
// a "Chapter X of N" label.
|
||||
// ---------------------------------------------------------------------------
|
||||
function ChapterNav({
|
||||
lila,
|
||||
chapters,
|
||||
current,
|
||||
chapterTitle,
|
||||
verseIndex,
|
||||
}: {
|
||||
lila: string;
|
||||
chapters: ChapterInfo[];
|
||||
current: number;
|
||||
chapterTitle: string;
|
||||
verseIndex: VerseEntry[];
|
||||
}) {
|
||||
const idx = chapters.findIndex((c) => c.number === current);
|
||||
const prev = idx > 0 ? chapters[idx - 1] : null;
|
||||
const next = idx < chapters.length - 1 ? chapters[idx + 1] : null;
|
||||
|
||||
// The contents drawer lists the active chapter's verses (same as the
|
||||
// verse-page sidebar) so the reader can jump straight in.
|
||||
const groups: MobileNavGroup[] = [
|
||||
{
|
||||
title: "Verses",
|
||||
items: verseIndex.map((v) => ({
|
||||
slug: String(v.verse),
|
||||
label: `Verse ${verseLabel(v)}`,
|
||||
href: `/books/cc/${lila}/${current}/${v.verse}`,
|
||||
type: "verse",
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<MobileNav
|
||||
groups={groups}
|
||||
currentSlug=""
|
||||
bookTitle={`Chapter ${current}: ${chapterTitle}`}
|
||||
/>
|
||||
{prev ? (
|
||||
<Link
|
||||
href={`/books/cc/${lila}/${prev.number}`}
|
||||
className={cn(buttonVariants({ variant: "outline", size: "sm" }), "gap-1.5")}
|
||||
>
|
||||
<ChevronLeft className="size-4" />
|
||||
<span className="hidden sm:inline">Previous</span>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="hidden lg:block" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Chapter {current} of {chapters.length}
|
||||
</span>
|
||||
|
||||
{next ? (
|
||||
<Link
|
||||
href={`/books/cc/${lila}/${next.number}`}
|
||||
className={cn(buttonVariants({ variant: "outline", size: "sm" }), "gap-1.5")}
|
||||
>
|
||||
<span className="hidden sm:inline">Next</span>
|
||||
<ChevronRight className="size-4" />
|
||||
</Link>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function CcChapterPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ lila: string; chapter: string }>;
|
||||
}) {
|
||||
const { lila, chapter } = await params;
|
||||
const chapterNum = parseInt(chapter);
|
||||
|
||||
const [meta, summary, verseIndex] = await Promise.all([
|
||||
getBookMeta("cc"),
|
||||
getProseChapter("cc", 0, `${lila}-${chapterNum}`),
|
||||
getChapterVerseIndex("cc", lila, chapterNum),
|
||||
]);
|
||||
|
||||
if (!meta) notFound();
|
||||
const lilaInfo = (meta.lilas as LilaInfo[] | undefined)?.find(
|
||||
(l) => l.key === lila,
|
||||
);
|
||||
if (!lilaInfo) notFound();
|
||||
const chapterInfo = lilaInfo.chapters.find((ch) => ch.number === chapterNum);
|
||||
if (!chapterInfo) notFound();
|
||||
if (verseIndex.length === 0) notFound();
|
||||
|
||||
const firstVerse = verseIndex[0].verse;
|
||||
|
||||
// Chapters without a summary (e.g. Ādi 7) go straight to the verses.
|
||||
if (!summary || summary.paragraphs.length === 0) {
|
||||
redirect(`/books/cc/${lila}/${chapterNum}/${firstVerse}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href={`/books/cc/${lila}`}
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
← {lilaInfo.name}
|
||||
</Link>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapterNum}: {chapterInfo.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
{/* Desktop sidebar — verses of the active chapter (matches the
|
||||
verse-page sidebar) so the reader can jump to any verse. */}
|
||||
<aside className="hidden lg:block w-64 shrink-0 sticky top-[89px] self-start max-h-[calc(100vh-165px)] overflow-y-auto py-8 pr-4">
|
||||
<h2 className="text-sm font-bold uppercase tracking-wider text-muted-foreground mb-3 px-2">
|
||||
Verses
|
||||
</h2>
|
||||
<nav className="grid grid-cols-3 gap-1">
|
||||
{verseIndex.map((v) => (
|
||||
<Link
|
||||
key={v.verse}
|
||||
href={`/books/cc/${lila}/${chapterNum}/${v.verse}`}
|
||||
className="px-2 py-1 rounded-md text-sm tabular-nums text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
|
||||
>
|
||||
{verseLabel(v)}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
{/* Main — chapter summary */}
|
||||
<main className="flex-1 min-w-0 px-6 py-8 space-y-6">
|
||||
{/* Top navigation */}
|
||||
<ChapterNav
|
||||
lila={lila}
|
||||
chapters={lilaInfo.chapters}
|
||||
current={chapterNum}
|
||||
chapterTitle={chapterInfo.title}
|
||||
verseIndex={verseIndex}
|
||||
/>
|
||||
|
||||
<p className="text-sm font-bold uppercase tracking-wider text-muted-foreground">
|
||||
Chapter Summary
|
||||
</p>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{summary.paragraphs.map((paragraph, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="text-base text-foreground leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: paragraph }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Bottom navigation */}
|
||||
<ChapterNav
|
||||
lila={lila}
|
||||
chapters={lilaInfo.chapters}
|
||||
current={chapterNum}
|
||||
chapterTitle={chapterInfo.title}
|
||||
verseIndex={verseIndex}
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { ExtractedVerseCount } from "../../extracted-verse-count";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { getBookMeta, getProseChapter } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES, CC_LILAS } from "@/lib/constants";
|
||||
|
||||
@@ -14,12 +15,15 @@ export async function generateMetadata({
|
||||
params: Promise<{ lila: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { lila } = await params;
|
||||
const lilaName = CC_LILAS[lila] || lila;
|
||||
const bookTitle = BOOK_NAMES["cc"] || "Śrī Caitanya-caritāmṛta";
|
||||
return buildOgMeta({
|
||||
title: bookTitle,
|
||||
detail: lilaName,
|
||||
});
|
||||
// A non-līlā segment (front-flap, preface, introduction) is front matter.
|
||||
if (!CC_LILAS[lila]) {
|
||||
const segment = await getProseChapter("cc", 0, lila);
|
||||
if (segment) {
|
||||
return buildOgMeta({ title: bookTitle, detail: segment.title });
|
||||
}
|
||||
}
|
||||
return buildOgMeta({ title: bookTitle, detail: CC_LILAS[lila] || lila });
|
||||
}
|
||||
|
||||
type ChapterInfo = {
|
||||
@@ -47,7 +51,48 @@ export default async function LilaPage({
|
||||
|
||||
const lilas = meta.lilas as LilaInfo[];
|
||||
const lilaInfo = lilas.find((l) => l.key === lila);
|
||||
if (!lilaInfo) notFound();
|
||||
|
||||
// Front matter — the param is not a līlā key, so resolve it as a prose
|
||||
// intro-segment (front-flap, preface, introduction).
|
||||
if (!lilaInfo) {
|
||||
const segment = await getProseChapter("cc", 0, lila);
|
||||
if (!segment) notFound();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/cc"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
← Śrī Caitanya-caritāmṛta
|
||||
</Link>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{segment.paragraphs.map((paragraph, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="text-base text-foreground leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: paragraph }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const totalVerses = lilaInfo.chapters.reduce(
|
||||
(sum, ch) => sum + ch.totalVerses,
|
||||
@@ -1,8 +1,10 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { getBookMeta, getProseChapters } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
@@ -26,7 +28,10 @@ export function generateMetadata(): Metadata {
|
||||
}
|
||||
|
||||
export default async function CcPage() {
|
||||
const meta = await getBookMeta("cc");
|
||||
const [meta, introSegments] = await Promise.all([
|
||||
getBookMeta("cc"),
|
||||
getProseChapters("cc", 0, "intro-segment"),
|
||||
]);
|
||||
if (!meta) {
|
||||
return (
|
||||
<div className="p-8 text-center text-muted-foreground">
|
||||
@@ -41,6 +46,7 @@ export default async function CcPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/cc", name: "Śrī Caitanya-caritāmṛta" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
@@ -51,7 +57,33 @@ export default async function CcPage() {
|
||||
<h1 className="mt-3 text-3xl font-bold text-foreground">{title}</h1>
|
||||
<p className="mt-1 text-muted-foreground">{author}</p>
|
||||
</PageHeader>
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||
{introSegments.length > 0 && (
|
||||
<div>
|
||||
<h2 className="text-sm font-bold uppercase tracking-wider text-muted-foreground mb-3">
|
||||
Front Matter
|
||||
</h2>
|
||||
<Card className="divide-y divide-border py-0">
|
||||
<CardContent className="p-0">
|
||||
<ul className="divide-y divide-border">
|
||||
{introSegments.map((seg) => (
|
||||
<li key={seg.slug}>
|
||||
<Link
|
||||
href={`/books/cc/${seg.slug}`}
|
||||
className="flex px-5 py-4 hover:bg-muted transition-colors"
|
||||
>
|
||||
<p className="font-medium text-foreground">
|
||||
{seg.title}
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Card className="divide-y divide-border">
|
||||
<CardContent className="p-0">
|
||||
<ul className="divide-y divide-border">
|
||||
@@ -6,7 +6,10 @@ import { Card, CardHeader, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight, Loader2 } from "lucide-react";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
@@ -216,9 +219,17 @@ function VerseCard({
|
||||
Purport
|
||||
</h3>
|
||||
<div className="text-base text-foreground leading-relaxed space-y-3">
|
||||
{verse.purport.map((p, i) => (
|
||||
<p key={i} dangerouslySetInnerHTML={{ __html: p }} />
|
||||
))}
|
||||
{verse.purport.map((p, i) =>
|
||||
// Cited ślokas come wrapped in <blockquote class="shloka"> — a
|
||||
// block element that can't live inside a <p>, so render those
|
||||
// in a <div> (styled centered + accent via globals.css); plain
|
||||
// prose paragraphs stay as <p>.
|
||||
/^\s*<blockquote/i.test(p) ? (
|
||||
<div key={i} dangerouslySetInnerHTML={{ __html: p }} />
|
||||
) : (
|
||||
<p key={i} dangerouslySetInnerHTML={{ __html: p }} />
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -389,6 +400,19 @@ export function ChapterViewer({
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Structured data for the currently-shown verse (SSR'd for the initial
|
||||
verse, updates on client navigation). */}
|
||||
{currentVerse ? (
|
||||
<JsonLd
|
||||
data={articleJsonLd({
|
||||
path: `${basePath}/${currentVerseNum}`,
|
||||
headline: `${BOOK_NAMES[book] ?? book} ${currentVerse.reference}`,
|
||||
description: currentVerse.translation,
|
||||
isPartOf: BOOK_NAMES[book] ?? book,
|
||||
})}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{/* ---- Sidebar: Verse Index ---- */}
|
||||
{/* Sticky sidebar. The max-h leaves 76px at the viewport bottom for
|
||||
the site footer so the sidebar doesn't slide behind it when the
|
||||
@@ -440,7 +464,8 @@ export function ChapterViewer({
|
||||
mobileNavBookTitle={mobileNavBookTitle}
|
||||
/>
|
||||
|
||||
{/* Verse content */}
|
||||
{/* Verse content — id targeted by each page header's FindInPage. */}
|
||||
<div id="book-content">
|
||||
{loading && !currentVerse ? (
|
||||
<div className="flex items-center justify-center py-24 text-muted-foreground gap-2">
|
||||
<Loader2 className="size-5 animate-spin" />
|
||||
@@ -453,6 +478,7 @@ export function ChapterViewer({
|
||||
Verse not found.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom navigation */}
|
||||
<VerseNav
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -76,6 +79,7 @@ export default async function DsChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/ds/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Dialectic Spiritualism" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/ds"
|
||||
@@ -83,12 +87,15 @@ export default async function DsChapterPage({
|
||||
>
|
||||
← Dialectic Spiritualism
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{parentChapterNum != null && (
|
||||
<span className="text-primary">Chapter {parentChapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{parentChapterNum != null && (
|
||||
<span className="text-primary">Chapter {parentChapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -106,7 +113,7 @@ export default async function DsChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getDsMeta, getDsIntroSegments } from "@/lib/ds";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function DsPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/ds", name: "Dialectic Spiritualism" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function EjChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/ej/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Easy Journey to Other Planets" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/ej"
|
||||
@@ -77,12 +81,15 @@ export default async function EjChapterPage({
|
||||
>
|
||||
← Easy Journey to Other Planets
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function EjChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getEjMeta, getEjIntroSegments } from "@/lib/ej";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function EjPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/ej", name: "Easy Journey to Other Planets" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function EkChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/ek/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Elevation to Kṛṣṇa Consciousness" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/ek"
|
||||
@@ -77,12 +81,15 @@ export default async function EkChapterPage({
|
||||
>
|
||||
← Elevation to Kṛṣṇa Consciousness
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function EkChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getEkMeta, getEkIntroSegments } from "@/lib/ek";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function EkPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/ek", name: "Elevation to Kṛṣṇa Consciousness" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -10,12 +10,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -66,6 +69,7 @@ export default async function GgChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/gg/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Gītār Gān" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/gg"
|
||||
@@ -73,12 +77,15 @@ export default async function GgChapterPage({
|
||||
>
|
||||
← Gītār Gān
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -96,7 +103,7 @@ export default async function GgChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getGgMeta, getGgIntroSegments } from "@/lib/gg";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function GgPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/gg", name: "Gītār Gān" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function HkhChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/hkh/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "The Kṛṣṇa Consciousness Handbook" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/hkh"
|
||||
@@ -77,12 +81,15 @@ export default async function HkhChapterPage({
|
||||
>
|
||||
← The Kṛṣṇa Consciousness Handbook
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function HkhChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getHkhMeta, getHkhIntroSegments } from "@/lib/hkh";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function HkhPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/hkh", name: "The Kṛṣṇa Consciousness Handbook" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+20
-13
@@ -11,12 +11,15 @@ import {
|
||||
type IsoSidebarItem,
|
||||
} from "@/lib/iso";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type guards
|
||||
@@ -102,6 +105,7 @@ export default async function IsoMantraPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/iso/${slug}`, headline: content.title ?? "Śrī Īśopaniṣad", isPartOf: "Śrī Īśopaniṣad" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/iso"
|
||||
@@ -109,15 +113,18 @@ export default async function IsoMantraPage({
|
||||
>
|
||||
← Śrī Īśopaniṣad
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{verse && (
|
||||
<>
|
||||
<span className="text-primary">{verse.reference}: </span>
|
||||
{verse.bookTitle}
|
||||
</>
|
||||
)}
|
||||
{intro && intro.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{verse && (
|
||||
<>
|
||||
<span className="text-primary">{verse.reference}: </span>
|
||||
{verse.bookTitle}
|
||||
</>
|
||||
)}
|
||||
{intro && intro.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -157,7 +164,7 @@ export default async function IsoMantraPage({
|
||||
|
||||
function VerseContent({ verse }: { verse: IsoVerse }) {
|
||||
return (
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8 space-y-8">
|
||||
{/* Devanagari */}
|
||||
<section>
|
||||
@@ -233,7 +240,7 @@ function VerseContent({ verse }: { verse: IsoVerse }) {
|
||||
|
||||
function IntroContent({ intro }: { intro: IsoIntroSegment }) {
|
||||
return (
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{intro.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getIsoMeta, getIsoIntroSegments } from "@/lib/iso";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -28,6 +30,7 @@ export default async function IsoPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/iso", name: "Śrī Īśopaniṣad" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -6,12 +6,15 @@ import { notFound } from "next/navigation";
|
||||
import { getProseChapter, getProseChapters } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -65,6 +68,7 @@ export default async function KbChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/kb/${vol}/${slug}`, headline: chapter.title, description: chapter.paragraphs?.[0], isPartOf: "Kṛṣṇa, The Supreme Personality of Godhead" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href={`/books/kb/${vol}`}
|
||||
@@ -72,12 +76,15 @@ export default async function KbChapterPage({
|
||||
>
|
||||
← Volume {vol}
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterLabel && (
|
||||
<span className="text-primary">{chapterLabel}: </span>
|
||||
)}
|
||||
{chapter.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterLabel && (
|
||||
<span className="text-primary">{chapterLabel}: </span>
|
||||
)}
|
||||
{chapter.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function KbChapterPage({
|
||||
/>
|
||||
|
||||
{/* Chapter content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{chapter.paragraphs.map((paragraph, i) => (
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { notFound } from "next/navigation";
|
||||
import { getBookMeta, getProseChapters } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -3,7 +3,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
@@ -27,6 +29,7 @@ export default async function KbPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/kb", name: "Kṛṣṇa, The Supreme Personality of Godhead" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function LclChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/lcl/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Life Comes from Life" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/lcl"
|
||||
@@ -77,12 +81,15 @@ export default async function LclChapterPage({
|
||||
>
|
||||
← Life Comes from Life
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function LclChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getLclMeta, getLclIntroSegments } from "@/lib/lcl";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function LclPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/lcl", name: "Life Comes from Life" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -66,6 +69,7 @@ export default async function LobChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/lob/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Light of the Bhāgavata" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/lob"
|
||||
@@ -73,12 +77,15 @@ export default async function LobChapterPage({
|
||||
>
|
||||
← Light of the Bhāgavata
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Text {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Text {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -96,7 +103,7 @@ export default async function LobChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) =>
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getLobMeta, getLobIntroSegments } from "@/lib/lob";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function LobPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/lob", name: "Light of the Bhāgavata" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -66,6 +69,7 @@ export default async function MgChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/mg/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Kṛṣṇa Consciousness: The Matchless Gift" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/mg"
|
||||
@@ -73,12 +77,15 @@ export default async function MgChapterPage({
|
||||
>
|
||||
← Kṛṣṇa Consciousness: The Matchless Gift
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -96,7 +103,7 @@ export default async function MgChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getMgMeta, getMgIntroSegments } from "@/lib/mg";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function MgPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/mg", name: "Kṛṣṇa Consciousness: The Matchless Gift" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+10
-6
@@ -1,7 +1,8 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import {
|
||||
getBookMeta,
|
||||
@@ -10,8 +11,8 @@ import {
|
||||
type VerseIndexEntry,
|
||||
type VerseRecord,
|
||||
} from "@/lib/api";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import type { Metadata } from "next";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
|
||||
@@ -187,9 +188,12 @@ export default async function VersePage({
|
||||
>
|
||||
← Mukunda-mālā-stotra
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
+9
-5
@@ -7,7 +7,8 @@ import { getChapterVerseIndex } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import Link from "next/link";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
@@ -59,13 +60,16 @@ export default async function MmChapterPage({
|
||||
>
|
||||
← Mukunda-mālā-stotra
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{segment.paragraphs.map((paragraph: string, i: number) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
import fs from "fs";
|
||||
@@ -79,6 +81,7 @@ export default async function MmPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/mm", name: "Mukunda-mālā-stotra" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function MogChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/mog/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Message of Godhead" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/mog"
|
||||
@@ -77,12 +81,15 @@ export default async function MogChapterPage({
|
||||
>
|
||||
← Message of Godhead
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function MogChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getMogMeta, getMogIntroSegments } from "@/lib/mog";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function MogPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/mog", name: "Message of Godhead" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+10
-6
@@ -4,7 +4,8 @@ import type { Metadata } from "next";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import {
|
||||
getBookMeta,
|
||||
@@ -14,8 +15,8 @@ import {
|
||||
type VerseRecord,
|
||||
} from "@/lib/api";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
|
||||
type NbsMeta = {
|
||||
book: string;
|
||||
@@ -192,9 +193,12 @@ export default async function VersePage({
|
||||
>
|
||||
← Nārada-bhakti-sūtra
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
+9
-5
@@ -7,7 +7,8 @@ import { getChapterVerseIndex } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import Link from "next/link";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
@@ -62,13 +63,16 @@ export default async function NbsChapterPage({
|
||||
>
|
||||
← Nārada-bhakti-sūtra
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{segment.paragraphs.map((paragraph: string, i: number) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
import fs from "fs";
|
||||
@@ -79,6 +81,7 @@ export default async function NbsPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/nbs", name: "Nārada-bhakti-sūtra" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function NodChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/nod/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "The Nectar of Devotion" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/nod"
|
||||
@@ -77,12 +81,15 @@ export default async function NodChapterPage({
|
||||
>
|
||||
← The Nectar of Devotion
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function NodChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getNodMeta, getNodIntroSegments } from "@/lib/nod";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -27,6 +29,7 @@ export default async function NodPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/nod", name: "The Nectar of Devotion" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+20
-13
@@ -11,12 +11,15 @@ import {
|
||||
type NoiSidebarItem,
|
||||
} from "@/lib/noi";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type guards
|
||||
@@ -93,6 +96,7 @@ export default async function NoiTextPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/noi/${slug}`, headline: content.title ?? "The Nectar of Instruction", isPartOf: "The Nectar of Instruction" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/noi"
|
||||
@@ -100,15 +104,18 @@ export default async function NoiTextPage({
|
||||
>
|
||||
← The Nectar of Instruction
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{verse && (
|
||||
<>
|
||||
<span className="text-primary">Text {verse.text}: </span>
|
||||
{verse.title}
|
||||
</>
|
||||
)}
|
||||
{intro && intro.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{verse && (
|
||||
<>
|
||||
<span className="text-primary">Text {verse.text}: </span>
|
||||
{verse.title}
|
||||
</>
|
||||
)}
|
||||
{intro && intro.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -148,7 +155,7 @@ export default async function NoiTextPage({
|
||||
|
||||
function VerseContent({ verse }: { verse: NoiVerse }) {
|
||||
return (
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8 space-y-8">
|
||||
{/* Devanagari */}
|
||||
<section>
|
||||
@@ -224,7 +231,7 @@ function VerseContent({ verse }: { verse: NoiVerse }) {
|
||||
|
||||
function IntroContent({ intro }: { intro: NoiIntroSegment }) {
|
||||
return (
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{intro.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getNoiMeta, getNoiIntroSegments } from "@/lib/noi";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function NoiPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/noi", name: "The Nectar of Instruction" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function OchkmChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/ochkm/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "On Chanting Hare Kṛṣṇa" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/ochkm"
|
||||
@@ -77,12 +81,15 @@ export default async function OchkmChapterPage({
|
||||
>
|
||||
← On Chanting the Hare Kṛṣṇa Mantra
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function OchkmChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getOchkmMeta, getOchkmIntroSegments } from "@/lib/ochkm";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function OchkmPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/ochkm", name: "On Chanting Hare Kṛṣṇa" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function OwkChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/owk/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "On the Way to Kṛṣṇa" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/owk"
|
||||
@@ -77,12 +81,15 @@ export default async function OwkChapterPage({
|
||||
>
|
||||
← On the Way to Kṛṣṇa
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function OwkChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getOwkMeta, getOwkIntroSegments } from "@/lib/owk";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function OwkPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/owk", name: "On the Way to Kṛṣṇa" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { getAllBooks, type BookMeta } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BooksGrid, type BookInfo } from "./books-grid";
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -65,6 +68,7 @@ export default async function PkChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/pk/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Prabhupāda-Kṛpā" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/pk"
|
||||
@@ -72,12 +76,15 @@ export default async function PkChapterPage({
|
||||
>
|
||||
← Prayers of King Kulaśekhara
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Verse {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Verse {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -95,7 +102,7 @@ export default async function PkChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getPkMeta } from "@/lib/pk";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -18,6 +20,7 @@ export default async function PkPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/pk", name: "Prabhupāda-Kṛpā" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function PoyChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/poy/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "The Perfection of Yoga" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/poy"
|
||||
@@ -77,12 +81,15 @@ export default async function PoyChapterPage({
|
||||
>
|
||||
← The Perfection of Yoga
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function PoyChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getPoyMeta, getPoyIntroSegments } from "@/lib/poy";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function PoyPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/poy", name: "The Perfection of Yoga" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function PqpaChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/pqpa/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Perfect Questions, Perfect Answers" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/pqpa"
|
||||
@@ -77,12 +81,15 @@ export default async function PqpaChapterPage({
|
||||
>
|
||||
← Perfect Questions Perfect Answers
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function PqpaChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getPqpaMeta, getPqpaIntroSegments } from "@/lib/pqpa";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function PqpaPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/pqpa", name: "Perfect Questions, Perfect Answers" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function RvChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/rv/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Rāja-vidyā: The King of Knowledge" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/rv"
|
||||
@@ -77,12 +81,15 @@ export default async function RvChapterPage({
|
||||
>
|
||||
← Rāja-vidyā: The King of Knowledge
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function RvChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getRvMeta, getRvIntroSegments } from "@/lib/rv";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -20,6 +22,7 @@ export default async function RvPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/rv", name: "Rāja-vidyā: The King of Knowledge" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+10
-6
@@ -2,11 +2,12 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
|
||||
type ChapterInfo = {
|
||||
@@ -107,9 +108,12 @@ export default async function SbVersePage({
|
||||
>
|
||||
← Canto {cantoInfo.number}: {cantoInfo.title}
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {chapterInfo.title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {chapterInfo.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { ExtractedVerseCount } from "../../extracted-verse-count";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { getBookMeta, getProseChapter } from "@/lib/api";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
|
||||
@@ -15,6 +16,11 @@ export async function generateMetadata({
|
||||
}): Promise<Metadata> {
|
||||
const { canto } = await params;
|
||||
const title = BOOK_NAMES["sb"] || "Śrīmad-Bhāgavatam";
|
||||
// A non-numeric segment (preface, introduction) is front matter.
|
||||
if (isNaN(parseInt(canto))) {
|
||||
const segment = await getProseChapter("sb", 0, canto);
|
||||
if (segment) return buildOgMeta({ title, detail: segment.title });
|
||||
}
|
||||
return buildOgMeta({ title, detail: `Canto ${canto}` });
|
||||
}
|
||||
|
||||
@@ -38,6 +44,49 @@ export default async function CantoPage({
|
||||
}) {
|
||||
const { canto } = await params;
|
||||
const cantoNum = parseInt(canto);
|
||||
|
||||
// Front matter — a non-numeric param (preface, introduction) resolves to a
|
||||
// prose intro-segment.
|
||||
if (isNaN(cantoNum)) {
|
||||
const segment = await getProseChapter("sb", 0, canto);
|
||||
if (!segment) notFound();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/sb"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
← Śrīmad-Bhāgavatam
|
||||
</Link>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{segment.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{segment.paragraphs.map((paragraph, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="text-base text-foreground leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: paragraph }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const meta = await getBookMeta("sb");
|
||||
if (!meta) notFound();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { getBookMeta, getProseChapters } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
@@ -26,7 +28,10 @@ type CantoInfo = {
|
||||
};
|
||||
|
||||
export default async function SbPage() {
|
||||
const meta = await getBookMeta("sb");
|
||||
const [meta, introSegments] = await Promise.all([
|
||||
getBookMeta("sb"),
|
||||
getProseChapters("sb", 0, "intro-segment"),
|
||||
]);
|
||||
if (!meta) notFound();
|
||||
|
||||
const intro = meta as {
|
||||
@@ -39,6 +44,7 @@ export default async function SbPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/sb", name: "Śrīmad-Bhāgavatam" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
@@ -51,7 +57,33 @@ export default async function SbPage() {
|
||||
</h1>
|
||||
<p className="mt-1 text-muted-foreground">{intro.author}</p>
|
||||
</PageHeader>
|
||||
<main className="max-w-5xl mx-auto px-6 py-8">
|
||||
<main className="max-w-5xl mx-auto px-6 py-8 space-y-8">
|
||||
{introSegments.length > 0 && (
|
||||
<div>
|
||||
<h2 className="text-sm font-bold uppercase tracking-wider text-muted-foreground mb-3">
|
||||
Front Matter
|
||||
</h2>
|
||||
<Card className="divide-y divide-border py-0">
|
||||
<CardContent className="p-0">
|
||||
<ul className="divide-y divide-border">
|
||||
{introSegments.map((seg) => (
|
||||
<li key={seg.slug}>
|
||||
<Link
|
||||
href={`/books/sb/${seg.slug}`}
|
||||
className="flex px-5 py-4 hover:bg-muted transition-colors"
|
||||
>
|
||||
<p className="font-medium text-foreground">
|
||||
{seg.title}
|
||||
</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Card className="divide-y divide-border">
|
||||
<CardContent className="p-0">
|
||||
<ul className="divide-y divide-border">
|
||||
+10
-6
@@ -1,10 +1,11 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
|
||||
import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
|
||||
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -104,9 +105,12 @@ export default async function VersePage({
|
||||
>
|
||||
← Śrīmad-Bhāgavatam (1962)
|
||||
</Link>
|
||||
<h1 className="mt-2 text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<div className="mt-2 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
Chapter {chapter}: {title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -3,7 +3,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getBookMeta } from "@/lib/api";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ExtractedVerseCount } from "../extracted-verse-count";
|
||||
|
||||
@@ -32,6 +34,7 @@ export default async function Sb1962Page() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/sb1962", name: "Śrīmad-Bhāgavatam (1962 Edition)" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -76,6 +79,7 @@ export default async function SsrChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/ssr/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "The Science of Self-Realization" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/ssr"
|
||||
@@ -83,12 +87,15 @@ export default async function SsrChapterPage({
|
||||
>
|
||||
← The Science of Self-Realization
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{parentChapterNum != null && (
|
||||
<span className="text-primary">Chapter {parentChapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{parentChapterNum != null && (
|
||||
<span className="text-primary">Chapter {parentChapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -106,7 +113,7 @@ export default async function SsrChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getSsrMeta, getSsrIntroSegments } from "@/lib/ssr";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function SsrPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/ssr", name: "The Science of Self-Realization" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -13,12 +13,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -77,6 +80,7 @@ export default async function SvaChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/sva/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Songs of the Vaiṣṇava Ācāryas" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/sva"
|
||||
@@ -84,12 +88,15 @@ export default async function SvaChapterPage({
|
||||
>
|
||||
← Songs of the Vaiṣṇava Ācāryas
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{parentChapterNum != null && (
|
||||
<span className="text-primary">Part {parentChapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{parentChapterNum != null && (
|
||||
<span className="text-primary">Part {parentChapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -107,7 +114,7 @@ export default async function SvaChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getSvaMeta, getSvaIntroSegments } from "@/lib/sva";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function SvaPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/sva", name: "Songs of the Vaiṣṇava Ācāryas" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function TlcChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/tlc/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Teachings of Lord Caitanya" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/tlc"
|
||||
@@ -77,12 +81,15 @@ export default async function TlcChapterPage({
|
||||
>
|
||||
← Teachings of Lord Caitanya
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function TlcChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getTlcMeta, getTlcIntroSegments } from "@/lib/tlc";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -25,6 +27,7 @@ export default async function TlcPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/tlc", name: "Teachings of Lord Caitanya" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function TlkChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/tlk/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Teachings of Lord Kapila" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/tlk"
|
||||
@@ -77,12 +81,15 @@ export default async function TlkChapterPage({
|
||||
>
|
||||
← Teachings of Lord Kapila
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function TlkChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getTlkMeta, getTlkIntroSegments } from "@/lib/tlk";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function TlkPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/tlk", name: "Teachings of Lord Kapila" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function TqkChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/tqk/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Teachings of Queen Kuntī" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/tqk"
|
||||
@@ -77,12 +81,15 @@ export default async function TqkChapterPage({
|
||||
>
|
||||
← Teachings of Queen Kuntī
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function TqkChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getTqkMeta, getTqkIntroSegments } from "@/lib/tqk";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -24,6 +26,7 @@ export default async function TqkPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/tqk", name: "Teachings of Queen Kuntī" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+16
-9
@@ -12,12 +12,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -70,6 +73,7 @@ export default async function TtpChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/ttp/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Teachings of Lord Kapila" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/ttp"
|
||||
@@ -77,12 +81,15 @@ export default async function TtpChapterPage({
|
||||
>
|
||||
← Transcendental Teachings of Prahlāda Mahārāja
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{chapterNum != null && (
|
||||
<span className="text-primary">Chapter {chapterNum}: </span>
|
||||
)}
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -100,7 +107,7 @@ export default async function TtpChapterPage({
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getTtpMeta } from "@/lib/ttp";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -18,6 +20,7 @@ export default async function TtpPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/ttp", name: "Teachings of Lord Kapila" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+13
-6
@@ -11,12 +11,15 @@ import {
|
||||
import { getProseChapter } from "@/lib/api";
|
||||
import { ogForChapter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { BOOK_NAMES } from "@/lib/constants";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { FindInPage } from "@/components/layout/find-in-page";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Metadata
|
||||
@@ -62,6 +65,7 @@ export default async function VaChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/books/va/${slug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: "Vedānta-sūtra Transcript" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/books/va"
|
||||
@@ -69,9 +73,12 @@ export default async function VaChapterPage({
|
||||
>
|
||||
← Vaiśiṣṭyāṣṭakam
|
||||
</Link>
|
||||
<h1 className="mt-3 text-2xl font-bold text-foreground">
|
||||
{content.title}
|
||||
</h1>
|
||||
<div className="mt-3 flex items-start justify-between gap-3">
|
||||
<h1 className="text-2xl font-bold text-foreground">
|
||||
{content.title}
|
||||
</h1>
|
||||
<FindInPage targetId="book-content" className="shrink-0 mt-0.5" />
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<div className="max-w-5xl mx-auto flex">
|
||||
@@ -85,7 +92,7 @@ export default async function VaChapterPage({
|
||||
currentSlug={slug}
|
||||
/>
|
||||
|
||||
<Card>
|
||||
<Card id="book-content">
|
||||
<CardContent className="py-8 px-6 sm:px-8">
|
||||
<div className="space-y-4">
|
||||
{content.paragraphs.map((paragraph, i) => (
|
||||
@@ -5,7 +5,9 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getVaMeta } from "@/lib/va";
|
||||
import { ogForBook } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { bookJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -18,6 +20,7 @@ export default async function VaPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={bookJsonLd({ path: "/books/va", name: "Vedānta-sūtra Transcript" })} />
|
||||
<PageHeader>
|
||||
<Link
|
||||
href="/books"
|
||||
+118
-35
@@ -5,6 +5,7 @@ import Link from "next/link";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { UserAvatar } from "@/components/ui/user-avatar";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Search,
|
||||
@@ -32,6 +33,14 @@ export type DiscipleSummary = {
|
||||
isVerified: boolean;
|
||||
};
|
||||
|
||||
/** A disciple who has a platform account (linked by an admin). */
|
||||
export type DiscipleLink = {
|
||||
discipleId: string;
|
||||
handle: string;
|
||||
name: string;
|
||||
avatarURL: string | null;
|
||||
};
|
||||
|
||||
export type LetterCount = { letter: string; count: number };
|
||||
export type PlaceGroup = { name: string; count: number };
|
||||
export type YearGroup = { year: number; count: number };
|
||||
@@ -49,6 +58,9 @@ type Props = {
|
||||
years: YearGroup[];
|
||||
totalCount: number;
|
||||
filteredCount: number;
|
||||
/** Disciples who have joined the platform — their cards are highlighted and
|
||||
* link through to the profile. Small set (dozens at most). */
|
||||
links: DiscipleLink[];
|
||||
};
|
||||
|
||||
type SortMode = "name-asc" | "name-desc" | "date-asc" | "date-desc" | "place";
|
||||
@@ -306,6 +318,97 @@ function Pagination({
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Disciple card
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* One disciple in the grid.
|
||||
*
|
||||
* When `link` is set the disciple has joined the platform, and the card is
|
||||
* highlighted and becomes a link to their profile. The highlight is drawn
|
||||
* entirely from theme tokens (`--primary`, `--ring`) rather than fixed colours,
|
||||
* so it picks up whichever palette the reader has chosen — peacock green under
|
||||
* Tulasī Garden, deep indigo under Yamunā Mist, and so on.
|
||||
*/
|
||||
function DiscipleCard({
|
||||
disciple,
|
||||
link,
|
||||
}: {
|
||||
disciple: DiscipleSummary;
|
||||
link?: DiscipleLink;
|
||||
}) {
|
||||
const locationYear = formatLocationYear(
|
||||
disciple.initiationPlace,
|
||||
disciple.initiationDate,
|
||||
);
|
||||
|
||||
const card = (
|
||||
<Card
|
||||
size="sm"
|
||||
className={cn(
|
||||
"h-full transition-all",
|
||||
link
|
||||
? // On the platform: tinted surface + primary ring so it reads as
|
||||
// "one of us" at a glance across the grid.
|
||||
"bg-primary/[0.06] ring-2 ring-primary/40 hover:ring-primary/70 hover:shadow-md"
|
||||
: "hover:shadow-md hover:ring-foreground/20",
|
||||
)}
|
||||
>
|
||||
<CardContent className="py-3 px-4 space-y-1.5">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p
|
||||
className={cn(
|
||||
"font-semibold leading-snug truncate",
|
||||
link ? "text-primary" : "text-foreground",
|
||||
)}
|
||||
>
|
||||
{disciple.initiatedName}
|
||||
</p>
|
||||
{disciple.suffix && (
|
||||
<Badge
|
||||
variant={link ? "default" : "secondary"}
|
||||
className="shrink-0 text-[10px]"
|
||||
>
|
||||
{disciple.suffix}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{locationYear && (
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{locationYear}
|
||||
</p>
|
||||
)}
|
||||
{link && (
|
||||
<div className="flex items-center gap-1.5 pt-0.5">
|
||||
<UserAvatar
|
||||
src={link.avatarURL}
|
||||
name={link.name}
|
||||
className="size-5"
|
||||
textClassName="text-[9px]"
|
||||
/>
|
||||
<span className="truncate text-xs font-medium text-primary">
|
||||
@{link.handle}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
if (!link) return card;
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={`/u/${link.handle}`}
|
||||
aria-label={`${disciple.initiatedName} — view @${link.handle}'s profile`}
|
||||
className="block rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||
>
|
||||
{card}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Component
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -318,6 +421,7 @@ export function DisciplesBrowser({
|
||||
suffixCounts,
|
||||
totalCount,
|
||||
filteredCount,
|
||||
links,
|
||||
}: Props) {
|
||||
const [query, setQuery] = useState("");
|
||||
const [sort, setSort] = useState<SortMode>("name-asc");
|
||||
@@ -334,6 +438,13 @@ export function DisciplesBrowser({
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
// Disciple id → their platform account, for the highlight + profile link.
|
||||
const linkByDiscipleId = useMemo(() => {
|
||||
const map = new Map<string, DiscipleLink>();
|
||||
for (const l of links) map.set(l.discipleId, l);
|
||||
return map;
|
||||
}, [links]);
|
||||
|
||||
// Build a lookup map for letter counts
|
||||
const countByLetter = useMemo(() => {
|
||||
const map = new Map<string, number>();
|
||||
@@ -539,41 +650,13 @@ export function DisciplesBrowser({
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
|
||||
{pageData.map((disciple) => {
|
||||
const locationYear = formatLocationYear(
|
||||
disciple.initiationPlace,
|
||||
disciple.initiationDate,
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={disciple.id}
|
||||
size="sm"
|
||||
className="transition-all hover:shadow-md hover:ring-foreground/20"
|
||||
>
|
||||
<CardContent className="py-3 px-4 space-y-1.5">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="font-semibold text-foreground leading-snug truncate">
|
||||
{disciple.initiatedName}
|
||||
</p>
|
||||
{disciple.suffix && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="shrink-0 text-[10px]"
|
||||
>
|
||||
{disciple.suffix}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{locationYear && (
|
||||
<p className="text-xs text-muted-foreground truncate">
|
||||
{locationYear}
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
{pageData.map((disciple) => (
|
||||
<DiscipleCard
|
||||
key={disciple.id}
|
||||
disciple={disciple}
|
||||
link={linkByDiscipleId.get(disciple.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { ogForDisciples } from "@/lib/og";
|
||||
import {
|
||||
getTotalDiscipleCount,
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getDiscipleYears,
|
||||
getDiscipleSuffixCounts,
|
||||
getFilteredDiscipleCount,
|
||||
getDiscipleLinks,
|
||||
} from "@/lib/disciples";
|
||||
import type { SuffixFilter } from "@/lib/disciples";
|
||||
import { DisciplesBrowser } from "./disciples-browser";
|
||||
@@ -62,6 +63,7 @@ export default async function DisciplesPage({
|
||||
places,
|
||||
years,
|
||||
suffixCounts,
|
||||
links,
|
||||
] = await Promise.all([
|
||||
getTotalDiscipleCount(),
|
||||
getFilteredDisciples(letterFilter, activeSuffix),
|
||||
@@ -70,6 +72,7 @@ export default async function DisciplesPage({
|
||||
getDisciplePlaces(),
|
||||
getDiscipleYears(),
|
||||
getDiscipleSuffixCounts(),
|
||||
getDiscipleLinks(),
|
||||
]);
|
||||
|
||||
// Serialize dates to ISO strings for the client component
|
||||
@@ -105,6 +108,7 @@ export default async function DisciplesPage({
|
||||
years={years}
|
||||
totalCount={totalCount}
|
||||
filteredCount={filteredCount}
|
||||
links={links}
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
@@ -4,12 +4,14 @@ import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { ogForEssay, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
import { bff, bffList } from "@/lib/bff";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -119,6 +121,7 @@ export default async function EssayPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/essays/${slug}`, headline: essay.title, description: essay.paragraphs?.[0], isPartOf: "Essays & Talks" })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href="/essays"
|
||||
@@ -2,7 +2,7 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import { bffList } from "@/lib/bff";
|
||||
import { EssaysGrid } from "./essays-grid";
|
||||
@@ -5,6 +5,8 @@ import { notFound } from "next/navigation";
|
||||
import { getAllGlossaryEntries } from "@/lib/api";
|
||||
import { buildOgMeta, extractQuoteFromHl } from "@/lib/og";
|
||||
import { GlossaryClient } from "../glossary-client";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Diacritic → base letter mapping
|
||||
@@ -145,6 +147,13 @@ export default async function GlossaryLetterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd
|
||||
data={articleJsonLd({
|
||||
path: `/glossary/${rawLetter}`,
|
||||
headline: `Glossary — ${activeLetter}`,
|
||||
isPartOf: "Glossary of Sanskrit & Vedic Terms",
|
||||
})}
|
||||
/>
|
||||
<GlossaryClient
|
||||
entries={clientEntries}
|
||||
activeLetter={activeLetter}
|
||||
+5
-2
@@ -11,12 +11,14 @@ import {
|
||||
type JournalSidebarItem,
|
||||
} from "@/lib/journals";
|
||||
import { ogForJournal, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/mobile-nav";
|
||||
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -74,6 +76,7 @@ export default async function JournalChapterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd data={articleJsonLd({ path: `/journals/${slug}/${chapterSlug}`, headline: content.title, description: content.paragraphs?.[0], isPartOf: meta.title })} />
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href={`/journals/${slug}`}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
type JournalChapterEntry,
|
||||
} from "@/lib/journals";
|
||||
import { ogForJournal } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -2,7 +2,7 @@ export const dynamic = "force-dynamic";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { buildOgMeta } from "@/lib/og";
|
||||
import {
|
||||
Card,
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Suspense } from "react";
|
||||
import { TextSelectionToolbar } from "@/components/text-selection-toolbar";
|
||||
import { TextHighlighter } from "@/components/text-highlighter";
|
||||
import { PageBookmarks } from "@/components/social/page-bookmarks";
|
||||
|
||||
/**
|
||||
* Reader layout — the annotation surface for the actual scripture library
|
||||
* (books, letters, lectures, essays, glossary, journals, magazines, other
|
||||
* documents, disciples).
|
||||
*
|
||||
* The text-selection toolbar and its two companions (the `?hl=` highlighter
|
||||
* and the persistent in-text bookmark marks) only make sense over long-form
|
||||
* scripture prose. Mounting them in this nested route group — rather than the
|
||||
* parent `(app)` layout — keeps them off the home feed, search, profiles,
|
||||
* settings, downloads, Kautilya, etc., so a stray text selection on chrome
|
||||
* text no longer pops the toolbar.
|
||||
*
|
||||
* This is a route group: it adds NO path segment, so every URL underneath is
|
||||
* unchanged (`/books/...`, `/letters/...`, …). All the outer `(app)` chrome
|
||||
* (header, sidebar, footer, bottom nav) still wraps these pages.
|
||||
*/
|
||||
export default function ReaderLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<TextSelectionToolbar />
|
||||
<Suspense fallback={null}>
|
||||
<TextHighlighter />
|
||||
</Suspense>
|
||||
<PageBookmarks />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getLetter, getAdjacentLetters } from "@/lib/letters";
|
||||
import { ogForLetter, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
|
||||
import { PageHeader } from "@/components/page-header";
|
||||
import { PageHeader } from "@/components/layout/page-header";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { JsonLd } from "@/components/seo/json-ld";
|
||||
import { articleJsonLd } from "@/lib/jsonld";
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -54,6 +56,15 @@ export default async function LetterPage({
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<JsonLd
|
||||
data={articleJsonLd({
|
||||
path: `/letters/${slug}`,
|
||||
headline: `Letter to ${letter.recipientDisplay}`,
|
||||
description: letter.paragraphs[0],
|
||||
datePublished: letter.year ? String(letter.year) : undefined,
|
||||
isPartOf: "Letters of Śrīla Prabhupāda",
|
||||
})}
|
||||
/>
|
||||
<PageHeader sticky>
|
||||
<Link
|
||||
href={backHref}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user