22
33import { Card } from '@/components/ui/card' ;
44import { Badge } from '@/components/ui/badge' ;
5+ import { Button } from '@/components/ui/button' ;
56import { formatDate } from '@/lib/utils' ;
6- import { useEffect , useState } from 'react' ;
7+ import { useEffect , useState , useRef } from 'react' ;
8+ import { FileText , Users , ArrowRight } from 'lucide-react' ;
79// import { sanitizeHtml } from '@/lib/utils/renderHtml';
810
911interface HackathonBannerProps {
@@ -17,6 +19,15 @@ interface HackathonBannerProps {
1719 status ?: string ;
1820 participants ?: number ;
1921 totalPrizePool ?: string ;
22+ // Action buttons
23+ isRegistered ?: boolean ;
24+ hasSubmitted ?: boolean ;
25+ isEnded ?: boolean ;
26+ isTeamFormationEnabled ?: boolean ;
27+ onJoinClick ?: ( ) => void ;
28+ onSubmitClick ?: ( ) => void ;
29+ onViewSubmissionClick ?: ( ) => void ;
30+ onFindTeamClick ?: ( ) => void ;
2031}
2132
2233interface TimeRemaining {
@@ -59,6 +70,47 @@ function formatCountdown(time: TimeRemaining): string {
5970 }
6071}
6172
73+ const CategoriesDisplay = ( {
74+ categoriesList,
75+ } : {
76+ categoriesList : string [ ] ;
77+ } ) => {
78+ const ref = useRef < HTMLDivElement > ( null ) ;
79+ const [ showEllipsis , setShowEllipsis ] = useState ( false ) ;
80+
81+ useEffect ( ( ) => {
82+ const el = ref . current ;
83+ if ( ! el ) return ;
84+
85+ const check = ( ) => setShowEllipsis ( el . scrollWidth > el . clientWidth ) ;
86+
87+ check ( ) ;
88+
89+ window . addEventListener ( 'resize' , check ) ;
90+ return ( ) => window . removeEventListener ( 'resize' , check ) ;
91+ } , [ categoriesList ] ) ;
92+
93+ return (
94+ < div className = { `relative flex items-center overflow-hidden` } >
95+ < div ref = { ref } className = 'scrollbar-hide flex gap-1.5 overflow-x-auto' >
96+ { categoriesList . map ( ( cat , i ) => (
97+ < span
98+ key = { i }
99+ className = 'rounded-md bg-neutral-800/70 px-2 py-0.5 text-[11px] font-medium whitespace-nowrap text-gray-300'
100+ >
101+ { cat }
102+ </ span >
103+ ) ) }
104+ </ div >
105+ { showEllipsis && (
106+ < div className = 'pointer-events-none absolute top-0 right-0 bottom-0 flex w-6 items-center justify-end bg-gradient-to-l from-[#030303] via-[#030303]/80 to-transparent pr-1' >
107+ < span className = 'text-xs font-medium text-gray-500' > ...</ span >
108+ </ div >
109+ ) }
110+ </ div >
111+ ) ;
112+ } ;
113+
62114export function HackathonBanner ( {
63115 title,
64116 tagline,
@@ -70,6 +122,14 @@ export function HackathonBanner({
70122 status,
71123 participants,
72124 totalPrizePool,
125+ isRegistered = false ,
126+ hasSubmitted = false ,
127+ isEnded = false ,
128+ isTeamFormationEnabled = false ,
129+ onJoinClick,
130+ onSubmitClick,
131+ onViewSubmissionClick,
132+ onFindTeamClick,
73133} : HackathonBannerProps ) {
74134 const [ timeRemaining , setTimeRemaining ] = useState < TimeRemaining > ( {
75135 days : 0 ,
@@ -203,9 +263,9 @@ export function HackathonBanner({
203263 } ;
204264
205265 return (
206- < Card className = 'relative w-full overflow-hidden rounded-none border-0 p-0 shadow-none' >
266+ < Card className = 'relative w-full overflow-hidden rounded-none border-0 bg-transparent p-0 shadow-none' >
207267 < div
208- className = 'relative h-64 md:h-80 lg:h-96'
268+ className = 'relative h-64 rounded-4xl md:h-80 lg:h-96'
209269 style = { {
210270 backgroundImage : imageUrl ? `url(${ imageUrl } )` : 'none' ,
211271 backgroundSize : 'cover' ,
@@ -260,23 +320,62 @@ export function HackathonBanner({
260320 ) }
261321 </ div >
262322
263- { /* Bottom section: Deadline/Start/Ended and categories */ }
323+ { /* Bottom section: Deadline/Start/Ended, categories, and action buttons */ }
264324 < div className = 'flex flex-col gap-2 md:gap-3' >
265325 { renderDateSection ( ) }
266326 { categories && categories . length > 0 && (
267327 < div className = 'flex flex-wrap items-center gap-2' >
268328 < span className = 'text-sm font-semibold text-[#a7f950]' >
269329 Categories:
270330 </ span >
271- { categories . map ( category => (
272- < Badge
273- key = { category }
331+ < CategoriesDisplay categoriesList = { categories } />
332+ </ div >
333+ ) }
334+
335+ { /* Action Buttons */ }
336+ { ! isEnded && (
337+ < div className = 'mt-2 flex flex-wrap items-center gap-3' >
338+ { ! isRegistered && onJoinClick && (
339+ < Button
340+ onClick = { onJoinClick }
341+ className = 'bg-[#a7f950] font-semibold text-black hover:bg-[#8fd93f]'
342+ >
343+ Join Hackathon
344+ < ArrowRight className = 'ml-2 h-4 w-4' />
345+ </ Button >
346+ ) }
347+
348+ { isRegistered && ! hasSubmitted && onSubmitClick && (
349+ < Button
350+ onClick = { onSubmitClick }
351+ className = 'bg-[#a7f950] font-semibold text-black hover:bg-[#8fd93f]'
352+ >
353+ < FileText className = 'mr-2 h-4 w-4' />
354+ Submit Project
355+ < ArrowRight className = 'ml-2 h-4 w-4' />
356+ </ Button >
357+ ) }
358+
359+ { isRegistered && hasSubmitted && onViewSubmissionClick && (
360+ < Button
361+ onClick = { onViewSubmissionClick }
362+ className = 'bg-[#a7f950] font-semibold text-black hover:bg-[#8fd93f]'
363+ >
364+ < FileText className = 'mr-2 h-4 w-4' />
365+ View Submission
366+ </ Button >
367+ ) }
368+
369+ { isRegistered && isTeamFormationEnabled && onFindTeamClick && (
370+ < Button
371+ onClick = { onFindTeamClick }
274372 variant = 'outline'
275- className = 'border-gray-400 text-xs text-gray-100 '
373+ className = 'border-blue-500/50 font-semibold text-blue-400 hover:bg-blue-500/20 '
276374 >
277- { category }
278- </ Badge >
279- ) ) }
375+ < Users className = 'mr-2 h-4 w-4' />
376+ Find Team
377+ </ Button >
378+ ) }
280379 </ div >
281380 ) }
282381 </ div >
0 commit comments