11import { community } from "@/config/community" ;
22import { useI18n } from "@/i18n/useI18n.ts" ;
33import { translations } from "@/i18n/translations" ;
4- import { ChevronLeft , ChevronRight } from "lucide-react" ;
4+ import { ExternalLink , ChevronLeft , ChevronRight , Star } from "lucide-react" ;
5+ import { Badge } from "@/components/ui/badge" ;
56import { motion , AnimatePresence } from "framer-motion" ;
67import { useState , useEffect , useCallback , useRef } from "react" ;
8+ import { useGithubRepositories } from "@/hooks/useGithubRepositories" ;
79
810const WorkSection = ( ) => {
911 const { t, locale } = useI18n ( ) ;
10- const configEvents = community . events ;
12+ const { projects, loading } = useGithubRepositories ( ) ;
13+ const configEvents = community . events as readonly ( { readonly image : string ; readonly link ?: string } ) [ ] ;
1114 const events = translations [ locale ] . events . items as readonly { title : string ; date : string ; description : string } [ ] ;
1215
1316 const [ current , setCurrent ] = useState ( 0 ) ;
@@ -69,71 +72,82 @@ const WorkSection = () => {
6972 { t ( "work.title" ) }
7073 </ motion . h2 >
7174
72- { /* Projects — commented out until ready to announce */ }
73- { /*
74- {community.projects.map((project, i) => (
75- <motion.div
76- key={project.title}
77- initial={{ opacity: 0, y: 30 }}
78- whileInView={{ opacity: 1, y: 0 }}
79- viewport={{ once: true }}
80- transition={{ duration: 0.5, delay: i * 0.1 }}
81- className="group relative flex flex-col rounded-xl border border-border bg-card p-6 transition-all duration-300 hover:border-foreground/20"
82- >
83- <div className="mb-3 flex items-start justify-between">
84- <h3 className="text-lg font-semibold text-foreground">{project.title}</h3>
85- {project.link && (
86- <a href={project.link} target="_blank" rel="noopener noreferrer" className="text-muted-foreground transition-colors hover:text-foreground">
87- <ExternalLink className="h-4 w-4" />
88- </a>
89- )}
90- </div>
91- <p className="mb-4 flex-1 text-sm text-muted-foreground">{project.description}</p>
92- <div className="flex flex-wrap gap-2">
93- {project.tags.map((tag) => (
94- <Badge key={tag} variant="secondary" className="border-border bg-secondary text-muted-foreground text-xs">
95- {tag}
96- </Badge>
97- ))}
98- </div>
99- </motion.div>
100- ))}
101- */ }
102- < div className = "grid grid-cols-1 gap-6 md:grid-cols-2 mb-8" >
103- { [ 0 , 1 , 2 , 3 ] . map ( ( i ) => (
104- < motion . div
105- key = { i }
106- initial = { { opacity : 0 , y : 30 } }
107- whileInView = { { opacity : 1 , y : 0 } }
108- viewport = { { once : true } }
109- transition = { { duration : 0.5 , delay : i * 0.1 } }
110- className = "group relative flex flex-col rounded-xl border border-border bg-card p-6"
111- >
112- < div className = "mb-3 flex items-start justify-between" >
113- < div className = "h-4 w-2/5 rounded bg-border/60" />
114- < div className = "h-4 w-4 rounded bg-border/40" />
115- </ div >
116- < div className = "mb-2 h-3 w-full rounded bg-border/30" />
117- < div className = "mb-4 h-3 w-4/5 rounded bg-border/30" />
118- < div className = "flex gap-2" >
119- < div className = "h-5 w-14 rounded-full bg-border/40" />
120- < div className = "h-5 w-14 rounded-full bg-border/40" />
121- < div className = "h-5 w-14 rounded-full bg-border/40" />
122- </ div >
123- </ motion . div >
124- ) ) }
125- </ div >
126-
127- < motion . div
128- initial = { { opacity : 0 , y : 20 } }
129- whileInView = { { opacity : 1 , y : 0 } }
130- viewport = { { once : true } }
131- transition = { { duration : 0.6 } }
132- className = "mb-16 flex flex-col items-center gap-1 text-center"
133- >
134- < p className = "text-base font-semibold text-foreground" > { t ( "projects.stayTuned" ) } </ p >
135- < p className = "text-sm text-muted-foreground" > { t ( "projects.stayTunedSub" ) } </ p >
136- </ motion . div >
75+ { /* Projects from GitHub */ }
76+ { loading ? (
77+ < div className = "grid grid-cols-1 gap-6 md:grid-cols-2 mb-8" >
78+ { [ 0 , 1 , 2 , 3 ] . map ( ( i ) => (
79+ < motion . div
80+ key = { i }
81+ initial = { { opacity : 0 , y : 30 } }
82+ whileInView = { { opacity : 1 , y : 0 } }
83+ viewport = { { once : true } }
84+ transition = { { duration : 0.5 , delay : i * 0.1 } }
85+ className = "group relative flex flex-col rounded-xl border border-border bg-card p-6"
86+ >
87+ < div className = "mb-3 flex items-start justify-between" >
88+ < div className = "h-4 w-2/5 rounded bg-border/60" />
89+ < div className = "h-4 w-4 rounded bg-border/40" />
90+ </ div >
91+ < div className = "mb-2 h-3 w-full rounded bg-border/30" />
92+ < div className = "mb-4 h-3 w-4/5 rounded bg-border/30" />
93+ < div className = "flex gap-2" >
94+ < div className = "h-5 w-14 rounded-full bg-border/40" />
95+ < div className = "h-5 w-14 rounded-full bg-border/40" />
96+ < div className = "h-5 w-14 rounded-full bg-border/40" />
97+ </ div >
98+ </ motion . div >
99+ ) ) }
100+ </ div >
101+ ) : projects . length > 0 ? (
102+ < div className = "grid grid-cols-1 gap-6 md:grid-cols-2 mb-8" >
103+ { projects . map ( ( project , i ) => (
104+ < motion . a
105+ href = { project . link }
106+ target = "_blank"
107+ rel = "noopener noreferrer"
108+ key = { project . id }
109+ initial = { { opacity : 0 , y : 30 } }
110+ whileInView = { { opacity : 1 , y : 0 } }
111+ viewport = { { once : true } }
112+ transition = { { duration : 0.5 , delay : i * 0.1 } }
113+ className = "group relative flex flex-col rounded-xl border border-border bg-card p-6 transition-all duration-300 hover:border-foreground/20 hover:shadow-lg"
114+ >
115+ < div className = "mb-3 flex items-start justify-between" >
116+ < h3 className = "text-lg font-semibold text-foreground max-w-xs" > { project . title } </ h3 >
117+ < ExternalLink className = "h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground flex-shrink-0" />
118+ </ div >
119+ < p className = "mb-4 flex-1 text-sm text-muted-foreground line-clamp-2" > { project . description } </ p >
120+
121+ < div className = "flex items-center justify-between" >
122+ < div className = "flex flex-wrap gap-2" >
123+ { project . tags . slice ( 0 , 3 ) . map ( ( tag ) => (
124+ < Badge key = { tag } variant = "secondary" className = "border-border bg-secondary text-muted-foreground text-xs" >
125+ { tag }
126+ </ Badge >
127+ ) ) }
128+ </ div >
129+ { project . stars > 0 && (
130+ < div className = "flex items-center gap-1 ml-2 text-xs text-muted-foreground" >
131+ < Star className = "h-3 w-3" fill = "currentColor" />
132+ < span > { project . stars } </ span >
133+ </ div >
134+ ) }
135+ </ div >
136+ </ motion . a >
137+ ) ) }
138+ </ div >
139+ ) : (
140+ < motion . div
141+ initial = { { opacity : 0 , y : 20 } }
142+ whileInView = { { opacity : 1 , y : 0 } }
143+ viewport = { { once : true } }
144+ transition = { { duration : 0.6 } }
145+ className = "mb-16 flex flex-col items-center gap-1 text-center"
146+ >
147+ < p className = "text-base font-semibold text-foreground" > { t ( "projects.stayTuned" ) } </ p >
148+ < p className = "text-sm text-muted-foreground" > { t ( "projects.stayTunedSub" ) } </ p >
149+ </ motion . div >
150+ ) }
137151
138152 { /* Events subtitle */ }
139153 < motion . h3
@@ -157,15 +171,28 @@ const WorkSection = () => {
157171 animate = "center"
158172 exit = "exit"
159173 transition = { { duration : 0.4 , ease : "easeInOut" } }
160- className = "flex flex-col md:flex-row"
174+ className = "flex flex-col md:flex-row cursor-pointer group"
175+ onClick = { ( ) => {
176+ if ( event . link ) {
177+ window . open ( event . link , '_blank' , 'noopener,noreferrer' ) ;
178+ }
179+ } }
161180 >
162- < div className = "relative h-56 w-full md:h-auto md:w-1/2 shrink-0" >
181+ < div className = "relative h-56 w-full md:h-auto md:w-1/2 shrink-0 overflow-hidden " >
163182 < img
164183 src = { event . image }
165184 alt = { translated ?. title ?? "" }
166- className = "h-full w-full object-cover"
185+ className = "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105 "
167186 loading = "lazy"
168187 />
188+ { event . link && (
189+ < div className = "absolute inset-0 flex items-center justify-center bg-black/0 transition-colors duration-300 group-hover:bg-black/30 flex-col gap-2" >
190+ < ExternalLink className = "h-6 w-6 text-white opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
191+ < span className = "text-white text-sm font-semibold opacity-0 transition-opacity duration-300 group-hover:opacity-100" >
192+ { t ( "common.viewMore" ) || "View More" }
193+ </ span >
194+ </ div >
195+ ) }
169196 </ div >
170197 < div className = "flex flex-col justify-center p-6 md:p-10 flex-1" >
171198 < span className = "mb-2 text-xs font-medium uppercase tracking-widest text-muted-foreground" >
0 commit comments