-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDndBookPage.jsx
More file actions
139 lines (130 loc) · 5.62 KB
/
DndBookPage.jsx
File metadata and controls
139 lines (130 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import React, { useState, useEffect, useContext } from 'react';
import { useParams, Link } from 'react-router-dom';
import { motion } from 'framer-motion';
import { DndContext } from '../../context/DndContext';
import DndLayout from '../../components/dnd/DndLayout';
import Seo from '../../components/Seo';
import piml from 'piml';
import { ScrollIcon, BookmarkSimpleIcon } from '@phosphor-icons/react';
function DndBookPage() {
const { bookId } = useParams();
const [book, setBook] = useState(null);
const [pageTitle, setPageTitle] = useState('Loading Book...');
const { setBreadcrumbs, language } = useContext(DndContext);
useEffect(() => {
const fetchBookData = async () => {
try {
const response = await fetch(
`${process.env.PUBLIC_URL}/stories/books_${language || 'en'}.piml`,
);
if (response.ok) {
const pimlText = await response.text();
const data = piml.parse(pimlText);
const foundBook = data.books.find(
(b) => b.bookId === parseInt(bookId),
);
if (foundBook) {
setBook(foundBook);
setPageTitle(foundBook.bookTitle);
setBreadcrumbs([
{ label: 'S&F', path: '/stories' },
{ label: 'The Lore', path: '/stories/lore' },
{
label: foundBook.bookTitle,
path: `/stories/books/${foundBook.bookId}`,
},
]);
} else {
setPageTitle('Book Not Found');
}
}
} catch (error) {
console.error('Failed to fetch book data:', error);
setPageTitle('Error Loading Book');
}
};
fetchBookData();
}, [bookId, setBreadcrumbs, language]);
return (
<DndLayout>
<Seo
title={`${pageTitle} | From Serfs and Frauds`}
description={`Explore the episodes of ${pageTitle}, a book in the From Serfs and Frauds D&D campaign.`}
keywords={[
'Fezcodex',
'd&d',
'dnd',
'from serfs and frauds',
'book',
pageTitle,
]}
/>
<div className="max-w-7xl mx-auto px-4 md:px-6 py-8 md:py-12">
<header className="text-center mb-12 md:mb-24">
<div className="flex justify-center mb-6">
<BookmarkSimpleIcon
size={48}
className="text-dnd-gold"
weight="duotone"
/>
</div>
<h1 className="text-4xl md:text-8xl font-playfairDisplay italic font-black dnd-gold-gradient-text uppercase tracking-tighter mb-4 leading-none dnd-header-pulse">
{pageTitle}
</h1>
<div className="h-px w-32 bg-dnd-gold mx-auto opacity-40" />
</header>
{book && (
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
className="dnd-parchment-container p-6 md:p-24 shadow-2xl border-2 border-black/10 dnd-parchment-glow relative"
>
{/* Scroll Ornaments */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-64 h-8 bg-dnd-crimson/5 rounded-b-full blur-xl dnd-scroll-accent" />
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-64 h-8 bg-dnd-crimson/5 rounded-t-full blur-xl dnd-scroll-accent" />
<div className="dnd-ornate-corner dnd-ornate-corner-tl !w-16 !h-16" />
<div className="dnd-ornate-corner dnd-ornate-corner-tr !w-16 !h-16" />
<div className="dnd-ornate-corner dnd-ornate-corner-bl !w-16 !h-16" />
<div className="dnd-ornate-corner dnd-ornate-corner-br !w-16 !h-16" />
<div className="relative z-10">
<div className="flex items-center gap-4 mb-8 pb-4 border-b border-dnd-crimson/10">
<ScrollIcon
size={32}
className="text-dnd-crimson"
weight="duotone"
/>
<h2 className="text-3xl font-playfairDisplay italic font-black text-dnd-crimson uppercase tracking-tighter">
Recorded Episodes
</h2>
</div>
<div className="dnd-mystic-divider mb-12 opacity-20" />
<div className="grid gap-4">
{book.episodes.map((episode, idx) => (
<Link
key={episode.id}
to={`/stories/books/${book.bookId}/pages/${episode.id}`}
className="group flex items-center justify-between p-6 bg-white/5 border border-transparent hover:border-dnd-crimson/20 hover:bg-dnd-crimson/5 transition-all duration-300"
>
<div className="flex items-center gap-6">
<span className="font-mono text-xs text-dnd-crimson/40 group-hover:text-dnd-crimson/100 transition-colors">
{String(idx + 1).padStart(2, '0')}
</span>
<span className="text-2xl font-arvo font-bold text-dnd-crimson/80 group-hover:text-dnd-crimson transition-colors">
{episode.title}
</span>
</div>
<div className="h-px flex-grow mx-8 bg-dnd-crimson/5 group-hover:bg-dnd-crimson/20 transition-colors" />
<span className="font-mono text-[10px] uppercase tracking-[0.3em] text-dnd-crimson/40 group-hover:text-dnd-crimson transition-colors">
View Script
</span>
</Link>
))}
</div>
</div>
</motion.div>
)}
</div>
</DndLayout>
);
}
export default DndBookPage;