Skip to content

Commit 9c57806

Browse files
committed
feat(terracotta): add TerracottaProjectsPage
1 parent 2a82cb6 commit 9c57806

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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 { CpuIcon, ArrowLeftIcon } from '@phosphor-icons/react';
9+
import { useAchievements } from '../../context/AchievementContext';
10+
11+
const TerracottaProjectsPage = () => {
12+
const { projects, loading, error } = useProjects();
13+
const { unlockAchievement } = useAchievements();
14+
const [activeProject, setActiveProject] = useState(null);
15+
16+
useEffect(() => {
17+
if (projects.length > 0 && !activeProject) {
18+
setActiveProject(projects[0]);
19+
}
20+
}, [projects, activeProject]);
21+
22+
useEffect(() => {
23+
unlockAchievement('project_pioneer');
24+
}, [unlockAchievement]);
25+
26+
if (loading) {
27+
return (
28+
<div className="flex h-screen items-center justify-center bg-[#F3ECE0] text-[#1A1613]">
29+
<div className="flex flex-col items-center gap-4">
30+
<div className="h-px w-24 bg-[#1A161320] relative overflow-hidden">
31+
<div className="absolute inset-0 bg-[#C96442] animate-progress origin-left"></div>
32+
</div>
33+
<span className="font-mono text-xs text-[#2E2620]/60 uppercase tracking-widest">
34+
Loading Archive
35+
</span>
36+
</div>
37+
</div>
38+
);
39+
}
40+
41+
if (error) {
42+
return (
43+
<div className="flex h-screen items-center justify-center bg-[#F3ECE0] text-[#9E4A2F] font-mono">
44+
ERR: {error.message}
45+
</div>
46+
);
47+
}
48+
49+
const isPlaceholder = (project) =>
50+
!project?.image || project.image.includes('placeholder');
51+
52+
return (
53+
<div className="flex min-h-screen bg-[#F3ECE0] text-[#1A1613] overflow-hidden relative selection:bg-[#C96442]/25">
54+
<Seo
55+
title="Archive | Fezcodex"
56+
description="A curated collection of digital experiments and deployed systems."
57+
/>
58+
59+
<div className="absolute inset-0 opacity-20 pointer-events-none z-0">
60+
{activeProject &&
61+
(isPlaceholder(activeProject) ? (
62+
<GenerativeArt seed={activeProject.title} className="w-full h-full filter blur-3xl" />
63+
) : (
64+
<img src={activeProject.image} alt="bg" className="w-full h-full object-cover filter blur-3xl" />
65+
))}
66+
</div>
67+
68+
<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">
69+
<header className="mb-20">
70+
<Link
71+
to="/"
72+
className="mb-8 inline-flex items-center gap-2 text-xs font-mono text-[#2E2620]/60 hover:text-[#1A1613] transition-colors uppercase tracking-widest"
73+
>
74+
<ArrowLeftIcon weight="bold" />
75+
<span>Home</span>
76+
</Link>
77+
<h1 className="text-6xl md:text-8xl font-playfairDisplay italic tracking-tight text-[#1A1613] mb-4 leading-none">
78+
Work
79+
</h1>
80+
<p className="text-[#2E2620]/70 font-mono text-sm max-w-sm uppercase tracking-widest">
81+
{'//'} SELECTED WORKS
82+
</p>
83+
</header>
84+
85+
<div className="flex flex-col pb-32">
86+
{projects.map((project, index) => (
87+
<ProjectCard
88+
key={project.slug}
89+
index={index}
90+
project={project}
91+
isActive={activeProject?.slug === project.slug}
92+
onHover={setActiveProject}
93+
/>
94+
))}
95+
</div>
96+
97+
<div className="mt-auto pt-20 border-t border-[#1A161320] text-[#2E2620]/50 font-mono text-xs uppercase tracking-widest">
98+
Total Entries: {projects.length}
99+
</div>
100+
</div>
101+
102+
<div className="hidden 4xl:block fixed right-0 top-0 h-screen w-1/2 bg-[#1A1613] overflow-hidden border-l border-[#1A161320] z-20">
103+
<AnimatePresence mode="wait">
104+
{activeProject && (
105+
<motion.div
106+
key={activeProject.slug}
107+
initial={{ opacity: 0, scale: 1.05 }}
108+
animate={{ opacity: 1, scale: 1 }}
109+
exit={{ opacity: 0 }}
110+
transition={{ duration: 0.6, ease: 'circOut' }}
111+
className="absolute inset-0"
112+
>
113+
<div className="absolute inset-0 z-0">
114+
<GenerativeArt seed={activeProject.title} className="w-full h-full opacity-80" />
115+
<div className="absolute inset-0 bg-gradient-to-t from-[#1A1613] via-transparent to-[#1A1613]/40" />
116+
<div className="absolute inset-0 bg-noise opacity-10 mix-blend-overlay" />
117+
</div>
118+
119+
<div className="absolute bottom-0 left-0 w-full p-16 z-10 flex flex-col gap-6">
120+
<div className="flex items-center gap-4 text-[#C96442] font-mono text-sm tracking-widest uppercase">
121+
<span>ID: {activeProject.slug.split('-')[0]}</span>
122+
<span className="h-1 w-1 bg-current rounded-full" />
123+
<span>{new Date(activeProject.date).getFullYear()}</span>
124+
</div>
125+
126+
<p className="text-xl md:text-2xl text-[#F3ECE0] font-playfairDisplay italic leading-relaxed max-w-2xl">
127+
{activeProject.shortDescription}
128+
</p>
129+
130+
<div className="flex flex-wrap gap-2 mt-2">
131+
{activeProject.technologies?.map((tech) => (
132+
<span
133+
key={tech}
134+
className="px-3 py-1 bg-[#F3ECE0]/10 backdrop-blur-md border border-[#F3ECE0]/10 rounded-full text-xs font-mono text-[#F3ECE0] uppercase tracking-wider"
135+
>
136+
{tech}
137+
</span>
138+
))}
139+
</div>
140+
141+
{(activeProject.redirect_url || activeProject.demo_link || activeProject.repo_link) && (
142+
<motion.div
143+
initial={{ opacity: 0, y: 20 }}
144+
animate={{ opacity: 1, y: 0 }}
145+
transition={{ delay: 0.2 }}
146+
className="mt-4"
147+
>
148+
<a
149+
href={activeProject.redirect_url || activeProject.demo_link || activeProject.repo_link}
150+
target="_blank"
151+
rel="noopener noreferrer"
152+
className="inline-flex items-center gap-3 text-[#F3ECE0] border-b border-[#F3ECE0] pb-1 hover:text-[#C96442] hover:border-[#C96442] transition-colors"
153+
>
154+
<span className="text-sm font-bold uppercase tracking-widest">
155+
{activeProject.redirect_url || activeProject.demo_link ? 'Visit Live Site' : 'View Source'}
156+
</span>
157+
<ArrowLeftIcon className="rotate-135" weight="bold" />
158+
</a>
159+
</motion.div>
160+
)}
161+
</div>
162+
</motion.div>
163+
)}
164+
</AnimatePresence>
165+
166+
{!activeProject && (
167+
<div className="absolute inset-0 flex items-center justify-center text-[#2E2620]/40">
168+
<CpuIcon size={64} weight="thin" className="animate-pulse" />
169+
</div>
170+
)}
171+
</div>
172+
</div>
173+
);
174+
};
175+
176+
export default TerracottaProjectsPage;

0 commit comments

Comments
 (0)