Skip to content

Commit 8b68d9e

Browse files
committed
feat: new project pages
1 parent aafa039 commit 8b68d9e

File tree

5 files changed

+443
-190
lines changed

5 files changed

+443
-190
lines changed

src/components/ProjectRouteHandler.jsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import React, { lazy, Suspense } from 'react';
22
import { useParams } from 'react-router-dom';
33
import { useProjects } from '../utils/projectParser';
4+
import { useVisualSettings } from '../context/VisualSettingsContext';
45
import Loading from './Loading';
56

67
const ProjectPage = lazy(() => import('../pages/ProjectPage'));
78
const StylishProjectDetailsPage = lazy(() => import('../pages/StylishProjectDetailsPage'));
89
const EditorialProjectDetailsPage = lazy(() => import('../pages/EditorialProjectDetailsPage'));
910
const MinimalModernProjectPage = lazy(() => import('../pages/MinimalModernProjectPage'));
1011
const MuseumProjectPage = lazy(() => import('../pages/MuseumProjectPage'));
12+
const LuxeProjectDetailPage = lazy(() => import('../pages/luxe-views/LuxeProjectDetailPage'));
1113

1214
const ProjectRouteHandler = () => {
1315
const { slug } = useParams();
1416
const { projects, loading } = useProjects();
17+
const { fezcodexTheme } = useVisualSettings();
1518

1619
if (loading) return <Loading />;
1720

@@ -21,7 +24,7 @@ const ProjectRouteHandler = () => {
2124
return <Suspense fallback={<Loading />}><ProjectPage /></Suspense>;
2225
}
2326

24-
// Handle different project styles
27+
// Handle different project styles first
2528
const projectStyle = project.style || 'default';
2629

2730
if (projectStyle === 'stylish') {
@@ -56,6 +59,15 @@ const ProjectRouteHandler = () => {
5659
);
5760
}
5861

62+
// Fallback to theme based routing if style is default
63+
if (fezcodexTheme === 'luxe') {
64+
return (
65+
<Suspense fallback={<Loading />}>
66+
<LuxeProjectDetailPage />
67+
</Suspense>
68+
);
69+
}
70+
5971
return (
6072
<Suspense fallback={<Loading />}>
6173
<ProjectPage />

src/pages/ProjectsPage.jsx

Lines changed: 8 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,16 @@
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';
105

116
const ProjectsPage = () => {
12-
const { projects, loading, error } = useProjects();
13-
const { unlockAchievement } = useAchievements();
14-
const [activeProject, setActiveProject] = useState(null);
7+
const { fezcodexTheme } = useVisualSettings();
158

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 />;
4811
}
4912

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 />;
19514
};
19615

19716
export default ProjectsPage;

0 commit comments

Comments
 (0)