@@ -4,6 +4,7 @@ import { motion, AnimatePresence } from 'framer-motion';
44import { Github , Mail , ExternalLink , ArrowRight , ArrowLeft , Search , Moon , Sun , X } from 'lucide-react' ;
55import Fuse from 'fuse.js' ;
66import { getAllPosts , getPostBySlug , getFeaturedPost , getAllTags , getAllCategories , type Post } from './lib/posts' ;
7+ import { getAllProjects , getFeaturedProjects } from './lib/projects' ;
78
89// Fix for framer-motion v12+ type issues
910const MotionDiv = motion . div as React . FC < React . HTMLAttributes < HTMLDivElement > & {
@@ -134,10 +135,61 @@ const SearchBox = ({ posts }: { posts: Post[] }) => {
134135 ) ;
135136} ;
136137
138+ const ProjectsView = ( ) => {
139+ const navigate = useNavigate ( ) ;
140+ const projects = getAllProjects ( ) ;
141+
142+ return (
143+ < MotionDiv
144+ initial = { { opacity : 0 , y : 20 } }
145+ animate = { { opacity : 1 , y : 0 } }
146+ exit = { { opacity : 0 , y : - 20 } }
147+ style = { { padding : '2rem 0' } }
148+ >
149+ < button
150+ onClick = { ( ) => navigate ( '/' ) }
151+ className = "pill"
152+ style = { { marginBottom : '2rem' , cursor : 'pointer' , display : 'flex' , alignItems : 'center' , gap : '0.5rem' } }
153+ >
154+ < ArrowLeft size = { 18 } /> BACK HOME
155+ </ button >
156+
157+ < h1 style = { { fontSize : '3.5rem' , fontWeight : 900 , marginBottom : '2rem' } } > PROJECTS</ h1 >
158+
159+ < div className = "bento-grid" >
160+ { projects . map ( ( project ) => (
161+ < MotionDiv
162+ key = { project . slug }
163+ whileHover = { { y : - 5 } }
164+ className = "bento-item span-2"
165+ >
166+ < div >
167+ < div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'flex-start' } } >
168+ < div className = "pill" > { project . status } </ div >
169+ < a href = { project . link } target = "_blank" rel = "noopener noreferrer" style = { { color : 'inherit' } } >
170+ < ExternalLink size = { 20 } />
171+ </ a >
172+ </ div >
173+ < h2 style = { { marginTop : '1rem' } } > { project . title } </ h2 >
174+ < p > { project . description } </ p >
175+ < div className = "tag-cloud" style = { { marginTop : '1rem' } } >
176+ { project . tags . map ( tag => (
177+ < span key = { tag } className = "pill" style = { { opacity : 0.7 , fontSize : '0.7rem' } } > { tag } </ span >
178+ ) ) }
179+ </ div >
180+ </ div >
181+ </ MotionDiv >
182+ ) ) }
183+ </ div >
184+ </ MotionDiv >
185+ ) ;
186+ } ;
187+
137188const GridView = ( ) => {
138189 const navigate = useNavigate ( ) ;
139190 const posts = getAllPosts ( ) ;
140191 const featuredPost = getFeaturedPost ( ) ;
192+ const featuredProjects = getFeaturedProjects ( ) ;
141193 const tags = getAllTags ( ) ;
142194 const categories = getAllCategories ( ) ;
143195 const [ selectedTag , setSelectedTag ] = useState < string | null > ( null ) ;
@@ -309,10 +361,27 @@ const GridView = () => {
309361 </ MotionDiv >
310362
311363 { /* Projects */ }
312- < MotionDiv variants = { item } className = "bento-item bg-yellow" >
313- < h2 style = { { fontSize : '1.2rem' } } > Projects</ h2 >
314- < p > UEFI Shell prototype in Rust.</ p >
315- < div style = { { marginTop : 'auto' } } > < ExternalLink size = { 20 } /> </ div >
364+ < MotionDiv
365+ variants = { item }
366+ className = "bento-item bg-yellow"
367+ onClick = { ( ) => navigate ( '/projects' ) }
368+ style = { { cursor : 'pointer' } }
369+ >
370+ < div >
371+ < h2 style = { { fontSize : '1.2rem' } } > Projects</ h2 >
372+ { featuredProjects . length > 0 ? (
373+ < div >
374+ < p style = { { fontWeight : 800 } } > { featuredProjects [ 0 ] . title } </ p >
375+ < p style = { { fontSize : '0.9rem' } } > { featuredProjects [ 0 ] . description } </ p >
376+ </ div >
377+ ) : (
378+ < p > Check out my latest experimental works.</ p >
379+ ) }
380+ </ div >
381+ < div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'center' , marginTop : '1rem' } } >
382+ < span style = { { fontWeight : 900 , fontSize : '0.8rem' } } > VIEW ALL</ span >
383+ < ArrowRight size = { 20 } />
384+ </ div >
316385 </ MotionDiv >
317386 </ MotionDiv >
318387 </ MotionDiv >
@@ -392,6 +461,7 @@ const App = () => {
392461 < Routes >
393462 < Route path = "/" element = { < GridView /> } />
394463 < Route path = "/post/:slug" element = { < PostView /> } />
464+ < Route path = "/projects" element = { < ProjectsView /> } />
395465 </ Routes >
396466 </ AnimatePresence >
397467
0 commit comments