fix: ui fixes
This commit is contained in:
@@ -3,7 +3,7 @@ export const dynamic = "force-dynamic";
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect, notFound } from "next/navigation";
|
import { redirect, notFound } from "next/navigation";
|
||||||
import { ChevronRight } from "lucide-react";
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
import { getBookMeta, getProseChapter, getChapterVerseIndex } from "@/lib/api";
|
import { getBookMeta, getProseChapter, getChapterVerseIndex } from "@/lib/api";
|
||||||
import { CC_LILAS } from "@/lib/constants";
|
import { CC_LILAS } from "@/lib/constants";
|
||||||
import { buildOgMeta } from "@/lib/og";
|
import { buildOgMeta } from "@/lib/og";
|
||||||
@@ -11,6 +11,7 @@ import { PageHeader } from "@/components/layout/page-header";
|
|||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { buttonVariants } from "@/components/ui/button";
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
import { FindInPage } from "@/components/layout/find-in-page";
|
import { FindInPage } from "@/components/layout/find-in-page";
|
||||||
|
import { MobileNav, type MobileNavGroup } from "@/components/layout/mobile-nav";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
type ChapterInfo = { number: number; title: string; totalVerses: number };
|
type ChapterInfo = { number: number; title: string; totalVerses: number };
|
||||||
@@ -21,6 +22,7 @@ type LilaInfo = {
|
|||||||
totalVerses: number;
|
totalVerses: number;
|
||||||
chapters: ChapterInfo[];
|
chapters: ChapterInfo[];
|
||||||
};
|
};
|
||||||
|
type VerseEntry = { verse: number; verseEnd?: number | null; reference: string };
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
params,
|
params,
|
||||||
@@ -35,6 +37,86 @@ export async function generateMetadata({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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({
|
export default async function CcChapterPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
@@ -82,7 +164,37 @@ export default async function CcChapterPage({
|
|||||||
</div>
|
</div>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<main className="max-w-5xl mx-auto px-6 py-8 space-y-6">
|
<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">
|
<p className="text-sm font-bold uppercase tracking-wider text-muted-foreground">
|
||||||
Chapter Summary
|
Chapter Summary
|
||||||
</p>
|
</p>
|
||||||
@@ -107,7 +219,17 @@ export default async function CcChapterPage({
|
|||||||
Read the chapter
|
Read the chapter
|
||||||
<ChevronRight className="size-4" />
|
<ChevronRight className="size-4" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
{/* Bottom navigation */}
|
||||||
|
<ChapterNav
|
||||||
|
lila={lila}
|
||||||
|
chapters={lilaInfo.chapters}
|
||||||
|
current={chapterNum}
|
||||||
|
chapterTitle={chapterInfo.title}
|
||||||
|
verseIndex={verseIndex}
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { buildOgMeta } from "@/lib/og";
|
|||||||
import { PageHeader } from "@/components/layout/page-header";
|
import { PageHeader } from "@/components/layout/page-header";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Clock } from "lucide-react";
|
import { Calendar, Clock } from "lucide-react";
|
||||||
import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
|
import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
|
||||||
|
|
||||||
export async function generateMetadata({
|
export async function generateMetadata({
|
||||||
@@ -75,8 +75,13 @@ export default async function TranscriptionsByPlacePage({
|
|||||||
{t.type === "lecture" ? "Lecture" : "Conversation"}
|
{t.type === "lecture" ? "Lecture" : "Conversation"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 mt-1 text-xs text-muted-foreground">
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-xs text-muted-foreground">
|
||||||
{t.date && <span>{t.date}</span>}
|
{t.date && (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Calendar className="size-3" />
|
||||||
|
{t.date}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{t.reference && (
|
{t.reference && (
|
||||||
<span className="text-primary/70">{t.reference}</span>
|
<span className="text-primary/70">{t.reference}</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ import { Card, CardContent } from "@/components/ui/card";
|
|||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { buttonVariants } from "@/components/ui/button";
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ChevronLeft, ChevronRight, Menu, Clock, MapPin } from "lucide-react";
|
import {
|
||||||
|
Calendar,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
Menu,
|
||||||
|
Clock,
|
||||||
|
MapPin,
|
||||||
|
} from "lucide-react";
|
||||||
import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
|
import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
@@ -379,7 +386,13 @@ function MonthSection({
|
|||||||
{t.type === "lecture" ? "Lecture" : "Conversation"}
|
{t.type === "lecture" ? "Lecture" : "Conversation"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 mt-1 text-xs text-muted-foreground">
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-xs text-muted-foreground">
|
||||||
|
{t.date && (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Calendar className="size-3" />
|
||||||
|
{t.date}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{t.place && (
|
{t.place && (
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<MapPin className="size-3" />
|
<MapPin className="size-3" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { PageHeader } from "@/components/layout/page-header";
|
|||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ChevronRight, Clock, MapPin } from "lucide-react";
|
import { Calendar, ChevronRight, Clock, MapPin } from "lucide-react";
|
||||||
import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
|
import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
|
||||||
|
|
||||||
type TranscriptionItem = {
|
type TranscriptionItem = {
|
||||||
@@ -92,7 +92,13 @@ function MonthSection({
|
|||||||
{t.type === "lecture" ? "Lecture" : "Conversation"}
|
{t.type === "lecture" ? "Lecture" : "Conversation"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 mt-1 text-xs text-muted-foreground">
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 mt-1 text-xs text-muted-foreground">
|
||||||
|
{t.date && (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Calendar className="size-3" />
|
||||||
|
{t.date}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{t.place && (
|
{t.place && (
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
<MapPin className="size-3" />
|
<MapPin className="size-3" />
|
||||||
|
|||||||
Reference in New Issue
Block a user