feat: add game cover page with table of contents and dynamic routing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
consultoria-as
2026-02-17 07:39:58 +00:00
parent 16905ecb6f
commit bf6a5f21e1
2 changed files with 124 additions and 0 deletions

33
src/app/[game]/page.tsx Normal file
View File

@@ -0,0 +1,33 @@
import { notFound } from "next/navigation";
import { getAllGames, getGame } from "@/lib/content";
import BookCover from "@/components/BookCover";
interface PageProps {
params: { game: string };
}
export async function generateStaticParams() {
const games = getAllGames();
return games.map((game) => ({ game: game.slug }));
}
export async function generateMetadata({ params }: PageProps) {
try {
const game = getGame(params.game);
return {
title: `${game.title} | Cronicas de los Reinos`,
description: game.description,
};
} catch {
return { title: "No encontrado" };
}
}
export default function GamePage({ params }: PageProps) {
try {
const game = getGame(params.game);
return <BookCover game={game} />;
} catch {
notFound();
}
}

View File

@@ -0,0 +1,91 @@
import Link from "next/link";
import { GameWithChapters } from "@/types";
export default function BookCover({ game }: { game: GameWithChapters }) {
return (
<div className="min-h-screen bg-parchment text-stone-800">
{/* Back to shelf */}
<div className="p-4 md:p-6">
<Link
href="/"
className="inline-flex items-center text-stone-500 hover:text-stone-700 font-lora text-sm transition-colors"
>
<span className="mr-2">&larr;</span> Volver a la estanteria
</Link>
</div>
{/* Cover */}
<div className="max-w-2xl mx-auto px-6 py-12 text-center">
{/* Decorative top */}
<div
className="w-24 h-1 mx-auto mb-8 rounded"
style={{ backgroundColor: game.accentColor }}
/>
<h1
className="font-playfair text-4xl md:text-5xl font-bold mb-3"
style={{ color: game.colorDark }}
>
{game.title}
</h1>
<p
className="font-lora italic text-lg mb-8"
style={{ color: game.color }}
>
{game.subtitle}
</p>
{/* Decorative divider */}
<div className="flex items-center justify-center gap-4 mb-8">
<div className="w-16 h-px bg-stone-300" />
<div
className="w-2 h-2 rotate-45"
style={{ backgroundColor: game.accentColor }}
/>
<div className="w-16 h-px bg-stone-300" />
</div>
<p className="font-lora text-stone-600 text-base leading-relaxed max-w-lg mx-auto mb-16">
{game.description}
</p>
{/* Table of Contents */}
<div className="text-left max-w-md mx-auto">
<h2 className="font-playfair text-xl font-semibold text-stone-700 mb-6 text-center tracking-wide uppercase">
Indice
</h2>
<div className="space-y-1">
{game.chapters.map((chapter) => (
<Link
key={chapter.slug}
href={`/${game.slug}/${chapter.slug}`}
className="flex items-baseline gap-3 py-3 px-2 rounded hover:bg-parchment-dark transition-colors group"
>
<span
className="font-playfair text-sm font-bold min-w-[2rem]"
style={{ color: game.color }}
>
{String(chapter.number).padStart(2, "0")}
</span>
<span className="font-lora text-stone-700 group-hover:text-stone-900 flex-1">
{chapter.title}
</span>
<span className="text-stone-300 group-hover:text-stone-500 text-sm">
&rarr;
</span>
</Link>
))}
</div>
</div>
{/* Decorative bottom */}
<div
className="w-24 h-1 mx-auto mt-16 rounded"
style={{ backgroundColor: game.accentColor }}
/>
</div>
</div>
);
}