|
1 | | -import React, { useState, useEffect } from 'react'; |
2 | | -import { useParams, Link } from 'react-router-dom'; |
3 | | -import { motion, AnimatePresence } from 'framer-motion'; |
4 | | -import PostItem from '../components/PostItem'; |
5 | | -import { ArrowLeft, Clock, Tag, BookOpen } from '@phosphor-icons/react'; |
6 | | -import Seo from '../components/Seo'; |
7 | | -import { fetchAllBlogPosts } from '../utils/dataUtils'; |
8 | | -import GenerativeArt from '../components/GenerativeArt'; |
| 1 | +import React from 'react'; |
| 2 | +import { useVisualSettings } from '../context/VisualSettingsContext'; |
| 3 | +import BrutalistSeriesPage from './brutalist-views/BrutalistSeriesPage'; |
| 4 | +import LuxeSeriesPage from './luxe-views/LuxeSeriesPage'; |
9 | 5 |
|
10 | 6 | const SeriesPage = () => { |
11 | | - const { seriesSlug } = useParams(); |
12 | | - const [seriesPosts, setSeriesPosts] = useState([]); |
13 | | - const [seriesTitle, setSeriesTitle] = useState(''); |
14 | | - const [loading, setLoading] = useState(true); |
15 | | - const [activePost, setActivePost] = useState(null); |
| 7 | + const { fezcodexTheme } = useVisualSettings(); |
16 | 8 |
|
17 | | - useEffect(() => { |
18 | | - const fetchSeriesPosts = async () => { |
19 | | - try { |
20 | | - const { processedPosts } = await fetchAllBlogPosts(); |
21 | | - |
22 | | - const filteredPosts = processedPosts |
23 | | - .filter((post) => post.series && post.series.slug === seriesSlug) |
24 | | - .sort((a, b) => { |
25 | | - const dateA = new Date(a.updated || a.date); |
26 | | - const dateB = new Date(b.updated || b.date); |
27 | | - return dateB - dateA; |
28 | | - }); |
29 | | - |
30 | | - if (filteredPosts.length > 0) { |
31 | | - setSeriesPosts(filteredPosts); |
32 | | - setSeriesTitle(filteredPosts[0].series.title); |
33 | | - setActivePost(filteredPosts[0]); |
34 | | - } else { |
35 | | - setSeriesPosts([]); |
36 | | - setSeriesTitle('Series Not Found'); |
37 | | - } |
38 | | - } catch (error) { |
39 | | - console.error('Error fetching series posts:', error); |
40 | | - setSeriesPosts([]); |
41 | | - setSeriesTitle('Error'); |
42 | | - } finally { |
43 | | - setLoading(false); |
44 | | - } |
45 | | - }; |
46 | | - |
47 | | - fetchSeriesPosts(); |
48 | | - }, [seriesSlug]); |
49 | | - |
50 | | - const isPlaceholder = (post) => |
51 | | - !post?.image || post.image.includes('placeholder'); |
52 | | - |
53 | | - if (loading) { |
54 | | - return ( |
55 | | - <div className="flex h-screen items-center justify-center bg-[#050505] text-white"> |
56 | | - <div className="flex flex-col items-center gap-4"> |
57 | | - <div className="h-px w-24 bg-white/10 relative overflow-hidden"> |
58 | | - <div className="absolute inset-0 bg-emerald-400 animate-progress origin-left"></div> |
59 | | - </div> |
60 | | - <span className="font-mono text-[10px] text-gray-500 uppercase tracking-[0.3em]"> |
61 | | - Accessing_Series_Data |
62 | | - </span> |
63 | | - </div> |
64 | | - </div> |
65 | | - ); |
| 9 | + if (fezcodexTheme === 'luxe') { |
| 10 | + return <LuxeSeriesPage />; |
66 | 11 | } |
67 | 12 |
|
68 | | - return ( |
69 | | - <div className="flex min-h-screen bg-[#050505] text-white overflow-hidden relative selection:bg-emerald-500/30"> |
70 | | - <Seo |
71 | | - title={`${seriesTitle} | Fezcodex Series`} |
72 | | - description={`Explore the sequential entries in the "${seriesTitle}" series.`} |
73 | | - keywords={['Fezcodex', 'blog', 'series', seriesTitle]} |
74 | | - /> |
75 | | - {/* Dynamic Background (Static or Active Post Blur) */} |
76 | | - <div className="absolute inset-0 opacity-20 pointer-events-none z-0"> |
77 | | - {activePost && |
78 | | - (isPlaceholder(activePost) ? ( |
79 | | - <GenerativeArt |
80 | | - seed={activePost.title} |
81 | | - className="w-full h-full filter blur-3xl" |
82 | | - /> |
83 | | - ) : ( |
84 | | - <img |
85 | | - src={activePost.image} |
86 | | - alt="bg" |
87 | | - className="w-full h-full object-cover filter blur-3xl" |
88 | | - /> |
89 | | - ))} |
90 | | - </div> |
91 | | - |
92 | | - {/* LEFT PANEL: The Index */} |
93 | | - <div className="w-full 4xl:pr-[50vw] relative z-10 flex flex-col min-h-screen py-24 px-6 md:pl-20 overflow-y-auto overflow-x-hidden no-scrollbar transition-all duration-300"> |
94 | | - <header className="mb-16"> |
95 | | - <Link |
96 | | - to="/blog" |
97 | | - className="mb-8 inline-flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-white transition-colors uppercase tracking-widest" |
98 | | - > |
99 | | - <ArrowLeft weight="bold" /> |
100 | | - <span>Archive</span> |
101 | | - </Link> |
102 | | - <h1 className="text-6xl md:text-8xl font-black tracking-tighter text-white mb-4 leading-none uppercase"> |
103 | | - SERIES |
104 | | - </h1> |
105 | | - <p className="text-gray-400 font-mono text-[10px] uppercase tracking-[0.2em]"> |
106 | | - {'//'} SEQUENTIAL_ENTRIES: {seriesTitle.toUpperCase()} |
107 | | - </p> |
108 | | - </header> |
109 | | - |
110 | | - <div className="flex flex-col pb-32"> |
111 | | - {seriesPosts.map((post) => ( |
112 | | - <PostItem |
113 | | - key={post.slug} |
114 | | - {...post} |
115 | | - slug={`series/${seriesSlug}/${post.slug}`} |
116 | | - isActive={activePost?.slug === post.slug} |
117 | | - onHover={setActivePost} |
118 | | - isSeries={false} |
119 | | - /> |
120 | | - ))} |
121 | | - </div> |
122 | | - |
123 | | - <div className="mt-auto pt-20 border-t border-white/10 text-gray-600 font-mono text-[10px] uppercase tracking-widest"> |
124 | | - Stored_Episodes: {seriesPosts.length} |
125 | | - </div> |
126 | | - </div> |
127 | | - |
128 | | - {/* RIGHT PANEL: The Stage */} |
129 | | - <div className="hidden 4xl:block fixed right-0 top-0 h-screen w-1/2 bg-neutral-900 overflow-hidden border-l border-white/10 z-20"> |
130 | | - <AnimatePresence mode="wait"> |
131 | | - {activePost && ( |
132 | | - <motion.div |
133 | | - key={activePost.slug} |
134 | | - initial={{ opacity: 0 }} |
135 | | - animate={{ opacity: 1 }} |
136 | | - exit={{ opacity: 0 }} |
137 | | - transition={{ duration: 0.4 }} |
138 | | - className="absolute inset-0" |
139 | | - > |
140 | | - {/* Art */} |
141 | | - <div className="absolute inset-0 z-0"> |
142 | | - <GenerativeArt |
143 | | - seed={activePost.title} |
144 | | - className="w-full h-full opacity-60" |
145 | | - /> |
146 | | - <div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-black/40" /> |
147 | | - </div> |
148 | | - |
149 | | - {/* Details Overlay */} |
150 | | - <div className="absolute bottom-0 left-0 w-full p-16 z-10 flex flex-col gap-8"> |
151 | | - <div className="flex items-center gap-6"> |
152 | | - <div className="flex items-center gap-2 text-emerald-400 font-mono text-[10px] tracking-widest uppercase"> |
153 | | - <Clock size={16} /> |
154 | | - <span> |
155 | | - {new Date( |
156 | | - activePost.updated || activePost.date, |
157 | | - ).toLocaleDateString('en-GB')} |
158 | | - </span> |
159 | | - </div> |
160 | | - <div className="flex items-center gap-2 text-white font-mono text-[10px] tracking-widest uppercase bg-white/10 px-2 py-1 border border-white/10 rounded-sm"> |
161 | | - <Tag size={14} /> |
162 | | - <span>{activePost.category || 'Episode'}</span> |
163 | | - </div> |
164 | | - </div> |
165 | | - |
166 | | - <div className="flex flex-col gap-4"> |
167 | | - <h2 className="text-4xl font-black text-white uppercase tracking-tighter leading-none"> |
168 | | - {activePost.title} |
169 | | - </h2> |
170 | | - <p className="text-lg text-gray-300 font-light leading-relaxed max-w-xl"> |
171 | | - {activePost.description || |
172 | | - 'Part of a sequential data stream. Analysis and implementation logs curated for technical review.'} |
173 | | - </p> |
174 | | - </div> |
175 | | - |
176 | | - <motion.div |
177 | | - initial={{ opacity: 0, y: 10 }} |
178 | | - animate={{ opacity: 1, y: 0 }} |
179 | | - transition={{ delay: 0.2 }} |
180 | | - className="mt-8" |
181 | | - > |
182 | | - <Link |
183 | | - to={`/blog/series/${seriesSlug}/${activePost.slug}`} |
184 | | - className="inline-flex items-center gap-4 text-white border-b-2 border-emerald-500 pb-2 hover:bg-emerald-500 hover:text-black transition-all px-1" |
185 | | - > |
186 | | - <span className="text-sm font-black uppercase tracking-[0.2em]"> |
187 | | - Access_Episode |
188 | | - </span> |
189 | | - <BookOpen weight="bold" size={20} /> |
190 | | - </Link> |
191 | | - </motion.div> |
192 | | - </div> |
193 | | - </motion.div> |
194 | | - )} |
195 | | - </AnimatePresence> |
196 | | - </div> |
197 | | - </div> |
198 | | - ); |
| 13 | + return <BrutalistSeriesPage />; |
199 | 14 | }; |
200 | 15 |
|
201 | 16 | export default SeriesPage; |
0 commit comments