|
1 | | -import React, { useEffect, useState } from 'react'; |
2 | | -import { Link } from 'react-router-dom'; |
3 | | -import { motion, AnimatePresence } from 'framer-motion'; |
4 | | -import ProjectCard from '../components/ProjectCard'; |
5 | | -import GenerativeArt from '../components/GenerativeArt'; |
6 | | -import { useProjects } from '../utils/projectParser'; |
7 | | -import Seo from '../components/Seo'; |
8 | | -import { ArrowLeftIcon, CpuIcon } from '@phosphor-icons/react'; |
9 | | -import { useAchievements } from '../context/AchievementContext'; |
| 1 | +import React from 'react'; |
| 2 | +import { useVisualSettings } from '../context/VisualSettingsContext'; |
| 3 | +import BrutalistProjectsPage from './brutalist-views/BrutalistProjectsPage'; |
| 4 | +import LuxeProjectsPage from './luxe-views/LuxeProjectsPage'; |
10 | 5 |
|
11 | 6 | const ProjectsPage = () => { |
12 | | - const { projects, loading, error } = useProjects(); |
13 | | - const { unlockAchievement } = useAchievements(); |
14 | | - const [activeProject, setActiveProject] = useState(null); |
| 7 | + const { fezcodexTheme } = useVisualSettings(); |
15 | 8 |
|
16 | | - // Set initial active project |
17 | | - useEffect(() => { |
18 | | - if (projects.length > 0 && !activeProject) { |
19 | | - setActiveProject(projects[0]); |
20 | | - } |
21 | | - }, [projects, activeProject]); |
22 | | - |
23 | | - useEffect(() => { |
24 | | - unlockAchievement('project_pioneer'); |
25 | | - }, [unlockAchievement]); |
26 | | - |
27 | | - if (loading) { |
28 | | - return ( |
29 | | - <div className="flex h-screen items-center justify-center bg-[#050505] text-white"> |
30 | | - <div className="flex flex-col items-center gap-4"> |
31 | | - <div className="h-px w-24 bg-gray-800 relative overflow-hidden"> |
32 | | - <div className="absolute inset-0 bg-white animate-progress origin-left"></div> |
33 | | - </div> |
34 | | - <span className="font-mono text-xs text-gray-500 uppercase tracking-widest"> |
35 | | - Loading Archive |
36 | | - </span> |
37 | | - </div> |
38 | | - </div> |
39 | | - ); |
40 | | - } |
41 | | - |
42 | | - if (error) { |
43 | | - return ( |
44 | | - <div className="flex h-screen items-center justify-center bg-[#050505] text-red-500 font-mono"> |
45 | | - ERR: {error.message} |
46 | | - </div> |
47 | | - ); |
| 9 | + if (fezcodexTheme === 'luxe') { |
| 10 | + return <LuxeProjectsPage />; |
48 | 11 | } |
49 | 12 |
|
50 | | - const isPlaceholder = (project) => |
51 | | - !project?.image || project.image.includes('placeholder'); |
52 | | - |
53 | | - return ( |
54 | | - <div className="flex min-h-screen bg-[#050505] text-white overflow-hidden relative selection:bg-emerald-500/30"> |
55 | | - <Seo |
56 | | - title="Archive | Fezcodex" |
57 | | - description="A curated collection of digital experiments and deployed systems." |
58 | | - keywords={['Fezcodex', 'projects', 'portfolio', 'developer', 'editorial']} |
59 | | - /> |
60 | | - {/* Dynamic Background (Static or Active Project Blur) */} |
61 | | - <div className="absolute inset-0 opacity-20 pointer-events-none z-0"> |
62 | | - {activeProject && |
63 | | - (isPlaceholder(activeProject) ? ( |
64 | | - <GenerativeArt |
65 | | - seed={activeProject.title} |
66 | | - className="w-full h-full filter blur-3xl" |
67 | | - /> |
68 | | - ) : ( |
69 | | - <img |
70 | | - src={activeProject.image} |
71 | | - alt="bg" |
72 | | - className="w-full h-full object-cover filter blur-3xl" |
73 | | - /> |
74 | | - ))} |
75 | | - </div> |
76 | | - |
77 | | - {/* LEFT PANEL: The Index */} |
78 | | - <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"> |
79 | | - <header className="mb-20"> |
80 | | - <Link |
81 | | - to="/" |
82 | | - className="mb-8 inline-flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-white transition-colors uppercase tracking-widest" |
83 | | - > |
84 | | - <ArrowLeftIcon weight="bold" /> |
85 | | - <span>Home</span> |
86 | | - </Link> |
87 | | - <h1 className="text-6xl md:text-8xl font-black tracking-tighter text-white mb-4 leading-none"> |
88 | | - WORK |
89 | | - </h1> |
90 | | - <p className="text-gray-400 font-mono text-sm max-w-sm"> |
91 | | - {'//'} SELECTED WORKS |
92 | | - </p> |
93 | | - </header> |
94 | | - |
95 | | - <div className="flex flex-col pb-32"> |
96 | | - {projects.map((project, index) => ( |
97 | | - <ProjectCard |
98 | | - key={project.slug} |
99 | | - index={index} |
100 | | - project={project} |
101 | | - isActive={activeProject?.slug === project.slug} |
102 | | - onHover={setActiveProject} |
103 | | - /> |
104 | | - ))} |
105 | | - </div> |
106 | | - |
107 | | - <div className="mt-auto pt-20 border-t border-white/10 text-gray-600 font-mono text-xs uppercase tracking-widest"> |
108 | | - Total Entries: {projects.length} |
109 | | - </div> |
110 | | - </div> |
111 | | - |
112 | | - {/* RIGHT PANEL: The Stage (Desktop Only) */} |
113 | | - <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"> |
114 | | - <AnimatePresence mode="wait"> |
115 | | - {activeProject && ( |
116 | | - <motion.div |
117 | | - key={activeProject.slug} |
118 | | - initial={{ opacity: 0, scale: 1.05 }} |
119 | | - animate={{ opacity: 1, scale: 1 }} |
120 | | - exit={{ opacity: 0 }} |
121 | | - transition={{ duration: 0.6, ease: 'circOut' }} |
122 | | - className="absolute inset-0" |
123 | | - > |
124 | | - {/* Image */} |
125 | | - <div className="absolute inset-0 z-0"> |
126 | | - <GenerativeArt |
127 | | - seed={activeProject.title} |
128 | | - className="w-full h-full opacity-80" |
129 | | - /> |
130 | | - <div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-black/40" /> |
131 | | - <div className="absolute inset-0 bg-noise opacity-10 mix-blend-overlay" /> |
132 | | - </div> |
133 | | - |
134 | | - {/* Details Overlay */} |
135 | | - <div className="absolute bottom-0 left-0 w-full p-16 z-10 flex flex-col gap-6"> |
136 | | - {/* ID & Date */} |
137 | | - <div className="flex items-center gap-4 text-emerald-400 font-mono text-sm tracking-widest uppercase"> |
138 | | - <span>ID: {activeProject.slug.split('-')[0]}</span> |
139 | | - <span className="h-1 w-1 bg-current rounded-full" /> |
140 | | - <span>{new Date(activeProject.date).getFullYear()}</span> |
141 | | - </div> |
142 | | - |
143 | | - {/* Description */} |
144 | | - <p className="text-xl md:text-2xl text-gray-200 font-light leading-relaxed max-w-2xl"> |
145 | | - {activeProject.shortDescription} |
146 | | - </p> |
147 | | - |
148 | | - {/* Tech Stack */} |
149 | | - <div className="flex flex-wrap gap-2 mt-2"> |
150 | | - {activeProject.technologies?.map((tech) => ( |
151 | | - <span |
152 | | - key={tech} |
153 | | - className="px-3 py-1 bg-white/10 backdrop-blur-md border border-white/10 rounded-full text-xs font-mono text-white uppercase tracking-wider" |
154 | | - > |
155 | | - {tech} |
156 | | - </span> |
157 | | - ))} |
158 | | - </div> |
159 | | - |
160 | | - {/* External Link Button (Optional) */} |
161 | | - {(activeProject.demo_link || activeProject.repo_link) && ( |
162 | | - <motion.div |
163 | | - initial={{ opacity: 0, y: 20 }} |
164 | | - animate={{ opacity: 1, y: 0 }} |
165 | | - transition={{ delay: 0.2 }} |
166 | | - className="mt-4" |
167 | | - > |
168 | | - <a |
169 | | - href={activeProject.demo_link || activeProject.repo_link} |
170 | | - target="_blank" |
171 | | - rel="noopener noreferrer" |
172 | | - className="inline-flex items-center gap-3 text-white border-b border-white pb-1 hover:text-emerald-400 hover:border-emerald-400 transition-colors" |
173 | | - > |
174 | | - <span className="text-sm font-bold uppercase tracking-widest"> |
175 | | - {activeProject.demo_link ? 'Visit Live Site' : 'View Source'} |
176 | | - </span> |
177 | | - <ArrowLeftIcon className="rotate-135" weight="bold" /> |
178 | | - </a> |
179 | | - </motion.div> |
180 | | - )} |
181 | | - </div> |
182 | | - </motion.div> |
183 | | - )} |
184 | | - </AnimatePresence> |
185 | | - |
186 | | - {/* Placeholder / Empty State */} |
187 | | - {!activeProject && ( |
188 | | - <div className="absolute inset-0 flex items-center justify-center text-gray-700"> |
189 | | - <CpuIcon size={64} weight="thin" className="animate-pulse" /> |
190 | | - </div> |
191 | | - )} |
192 | | - </div> |
193 | | - </div> |
194 | | - ); |
| 13 | + return <BrutalistProjectsPage />; |
195 | 14 | }; |
196 | 15 |
|
197 | 16 | export default ProjectsPage; |
0 commit comments