11'use client' ;
22
3- import { useMemo , useState } from 'react' ;
4- import {
5- Star ,
6- ArrowUpDown ,
7- Trophy ,
8- Users ,
9- FileCode2 ,
10- Trash2 ,
11- Edit ,
12- } from 'lucide-react' ;
3+ import { Trophy , Users , FileCode2 , Plus } from 'lucide-react' ;
4+ import { Button } from 'react-bootstrap' ;
135
14- import { Button } from '@/components/ui/button' ;
156import { Checkbox } from '@/components/ui/checkbox' ;
16- import { Badge } from '@/components/ui/badge' ;
17- import { DuckCreateButton } from '@/components/duck-create-button' ;
18- // import contests from '@/data/contests';
7+ import ContestsTable from '@/components/ContestsTable' ;
198import Footer from '@/components/Footer' ;
20-
21- type SortKey = 'id' | 'name' | 'owner' | 'modificationTime' | 'creationTime' ;
9+ // import contests from '@/data/contests';
2210
2311export default function ContestsPage ( ) {
24- const [ sortKey , setSortKey ] = useState < SortKey > ( 'id' ) ;
25- const [ sortOrder , setSortOrder ] = useState < 'asc' | 'desc' > ( 'asc' ) ;
26-
27- const sortedContests = useMemo ( ( ) => {
28- return [ ...contests ] . sort ( ( a , b ) => {
29- const aVal = a [ sortKey ] ;
30- const bVal = b [ sortKey ] ;
31-
32- if ( aVal < bVal ) return sortOrder === 'asc' ? - 1 : 1 ;
33- if ( aVal > bVal ) return sortOrder === 'asc' ? 1 : - 1 ;
34- return 0 ;
35- } ) ;
36- } , [ sortKey , sortOrder ] ) ;
37-
38- const handleSort = ( key : SortKey ) => {
39- if ( key === sortKey ) {
40- setSortOrder ( ( prev ) => ( prev === 'asc' ? 'desc' : 'asc' ) ) ;
41- } else {
42- setSortKey ( key ) ;
43- setSortOrder ( 'asc' ) ;
44- }
45- } ;
46-
4712 return (
4813 < div className = 'min-h-screen bg-gradient-to-br from-gray-50 to-gray-100' >
49- { /* Main Content */ }
5014 < div className = 'container px-4 py-8 mx-auto' >
5115 < div className = 'flex items-center justify-between mb-8' >
5216 < div >
@@ -59,7 +23,7 @@ export default function ContestsPage() {
5923 < div className = 'flex items-center gap-6' >
6024 < div className = 'flex items-center gap-6' >
6125 < div className = 'flex items-center gap-2' >
62- < Checkbox id = 'favorites' />
26+ < Checkbox id = 'favorites' /> { /* TODO: Implement favorite filter */ }
6327 < label
6428 htmlFor = 'favorites'
6529 className = 'text-sm font-medium text-gray-700'
@@ -68,208 +32,26 @@ export default function ContestsPage() {
6832 </ label >
6933 </ div >
7034 < div className = 'flex items-center gap-2' >
71- < Checkbox id = 'activity' />
35+ < Checkbox id = 'activity' /> { ' ' }
36+ { /* TODO: Implement view only my-activity filter */ }
7237 < label htmlFor = 'activity' className = 'text-sm font-medium text-gray-700' >
7338 Only my activity
7439 </ label >
7540 </ div >
7641 </ div >
77- { /* 예쁜 Create Contest 버튼 */ }
78- { /* <Button className='bg-gradient-to-r from-primary to-primary/80 hover:from-primary/90 hover:to-primary/70 text-white shadow-lg hover:shadow-xl transition-all duration-200 px-6 py-3 h-auto'>
79- <Plus className='h-5 w-5 mr-2' />
80- <span className='font-semibold'>Create Contest</span>
81- </Button> */ }
82- < DuckCreateButton onClick = { ( ) => console . log ( 'Creating new contest!' ) } >
83- Create Contest
84- </ DuckCreateButton >
42+ < Button
43+ className = 'inline-flex flex-row flex-nowrap items-center gap-2 whitespace-nowrap leading-none
44+ bg-amber-400 hover:bg-amber-500 text-white px-4 py-2 h-auto rounded-xl shadow'
45+ >
46+ < Plus className = 'h-4 w-4 shrink-0' />
47+ < span className = 'font-medium inline-block' > New Contest</ span >
48+ </ Button >
8549 </ div >
8650 </ div >
8751
88- { /* Contests Table */ }
89- < div className = 'bg-white rounded-2xl border border-gray-200/60 shadow-xl shadow-gray-900/5 overflow-hidden' >
90- < div className = 'overflow-x-auto' >
91- < table className = 'w-full' >
92- < thead >
93- < tr className = 'bg-gradient-to-r from-gray-50 to-gray-100/50 border-b border-gray-200/60' >
94- < th className = 'px-6 py-4 text-left' >
95- < div className = 'flex items-center gap-2 cursor-pointer hover:text-primary transition-colors group' >
96- < span className = 'text-sm font-semibold text-gray-700' > </ span >
97- </ div >
98- </ th >
99- < th className = 'px-6 py-4 text-left' >
100- < div
101- className = 'flex items-center gap-2 cursor-pointer hover:text-primary'
102- onClick = { ( ) => handleSort ( 'id' ) }
103- >
104- < div className = 'flex items-center gap-2 cursor-pointer hover:text-primary transition-colors group' >
105- < span className = 'text-sm font-semibold text-black-600' >
106- Contest
107- </ span >
108- < ArrowUpDown className = 'h-4 w-4 text-gray-400 group-hover:text-primary' />
109- </ div >
110- </ div >
111- </ th >
112- < th className = 'px-6 py-4 text-left' >
113- < div
114- className = 'flex items-center gap-2 cursor-pointer hover:text-primary'
115- onClick = { ( ) => handleSort ( 'name' ) }
116- >
117- < div className = 'flex items-center gap-2 cursor-pointer hover:text-primary transition-colors group' >
118- { /* <Trophy className='h-4 w-4 text-gray-400' /> */ }
119- < span className = 'text-sm font-semibold text-black-600' >
120- Contest Name
121- </ span >
122- < ArrowUpDown className = 'h-4 w-4 text-gray-400 group-hover:text-primary' />
123- </ div >
124- </ div >
125- </ th >
126- < th className = 'px-6 py-4 text-left' >
127- < div
128- className = 'flex items-center gap-2 cursor-pointer hover:text-primary'
129- onClick = { ( ) => handleSort ( 'owner' ) }
130- >
131- < div className = 'flex items-center gap-2 cursor-pointer hover:text-primary transition-colors group' >
132- { /* <User className='h-4 w-4 text-gray-400' /> */ }
133- < span className = 'text-sm font-semibold text-black-600' >
134- Owner
135- </ span >
136- < ArrowUpDown className = 'h-4 w-4 text-gray-400 group-hover:text-primary' />
137- </ div >
138- </ div >
139- </ th >
140- < th className = 'px-6 py-4 text-left' >
141- < div
142- className = 'flex items-center gap-2 cursor-pointer hover:text-primary'
143- onClick = { ( ) => handleSort ( 'modificationTime' ) }
144- >
145- < div className = 'flex items-center gap-2 cursor-pointer hover:text-primary transition-colors group' >
146- { /* <Clock className='h-4 w-4 text-gray-400' /> */ }
147- < span className = 'text-sm font-semibold text-black-600' >
148- Modification
149- </ span >
150- < ArrowUpDown className = 'h-4 w-4 text-gray-400 group-hover:text-primary' />
151- </ div >
152- </ div >
153- </ th >
154- < th className = 'px-6 py-4 text-left' >
155- < div
156- className = 'flex items-center gap-2 cursor-pointer hover:text-primary'
157- onClick = { ( ) => handleSort ( 'creationTime' ) }
158- >
159- < div className = 'flex items-center gap-2 cursor-pointer hover:text-primary transition-colors group' >
160- { /* <Calendar className='h-4 w-4 text-gray-400' /> */ }
161- < span className = 'text-sm font-semibold text-black-600' >
162- Creation
163- </ span >
164- < ArrowUpDown className = 'h-4 w-4 text-gray-400 group-hover:text-primary' />
165- </ div >
166- </ div >
167- </ th >
168- < th className = 'px-6 py-4 text-center' >
169- < span className = 'text-sm font-semibold text-black-600' >
170- Actions
171- </ span >
172- </ th >
173- </ tr >
174- </ thead >
175- < tbody className = 'divide-y divide-gray-100' >
176- { sortedContests . map ( ( contest , index ) => (
177- < tr
178- key = { contest . id }
179- className = { `
180- transition-all duration-200 hover:bg-gradient-to-r hover:from-black-50/50 hover:to-indigo-50/30 hover:shadow-sm
181- ${ index % 2 === 1 ? 'bg-gradient-to-r from-purple-50/20 to-pink-50/20' : 'bg-white' }
182- ` }
183- >
184- < td className = 'px-6 py-6' >
185- < Button
186- variant = 'ghost'
187- size = 'icon'
188- className = 'h-8 w-8 hover:bg-yellow-100 transition-colors'
189- >
190- < Star
191- className = { `h-4 w-4 transition-colors ${ contest . isFavorite ? 'text-yellow-500 fill-yellow-500' : 'text-gray-400 hover:text-yellow-500' } ` }
192- />
193- </ Button >
194- </ td >
195- < td className = 'px-4 py-4' >
196- < span className = 'font-mono text-sm ' > { contest . id } </ span >
197- </ td >
198- < td className = 'px-6 py-6' >
199- < div className = 'space-y-2 max-w-md' >
200- < div className = 'font-semibold text-gray-900 hover:text-black-600 cursor-pointer transition-colors' >
201- { contest . name }
202- </ div >
203- < div className = 'text-sm text-gray-600 bg-gray-50 p-3 rounded-lg border' >
204- { contest . description }
205- </ div >
206- < div className = 'flex items-center gap-3' >
207- < Badge
208- variant = 'secondary'
209- className = { `text-xs ${
210- contest . status === 'active'
211- ? 'bg-green-100 text-green-800'
212- : contest . status === 'upcoming'
213- ? 'bg-blue-100 text-blue-800'
214- : 'bg-gray-100 text-gray-800'
215- } `}
216- >
217- { contest . status }
218- </ Badge >
219- < div className = 'flex items-center gap-1 text-xs text-gray-600' >
220- < Users className = 'h-3 w-3' />
221- < span > { contest . participants } participants</ span >
222- </ div >
223- < div className = 'flex items-center gap-1 text-xs text-gray-600' >
224- < FileCode2 className = 'h-3 w-3' />
225- < span > { contest . problems } problems</ span >
226- </ div >
227- </ div >
228- </ div >
229- </ td >
230- < td className = 'px-4 py-4' >
231- < span className = 'text-sm' > { contest . owner } </ span >
232- </ td >
233- < td className = 'px-6 py-6' >
234- < div className = 'text-sm' >
235- < div className = 'font-medium text-gray-900' >
236- { contest . modificationTime . split ( ' ' ) [ 0 ] }
237- </ div >
238- < div className = 'text-gray-500 text-xs mt-1' >
239- { contest . modificationTime . split ( ' ' ) [ 1 ] }
240- </ div >
241- </ div >
242- </ td >
243- < td className = 'px-6 py-6' >
244- < div className = 'text-sm' >
245- < div className = 'font-medium text-gray-900' >
246- { contest . creationTime . split ( ' ' ) [ 0 ] }
247- </ div >
248- < div className = 'text-gray-500 text-xs mt-1' >
249- { contest . creationTime . split ( ' ' ) [ 1 ] }
250- </ div >
251- </ div >
252- </ td >
253- < td className = 'px-6 py-6' >
254- < div className = 'flex items-center justify-center gap-3' >
255- < div className = 'flex flex-col items-center gap-1 cursor-pointer hover:bg-gray-100 p-1 rounded' >
256- < Edit className = 'h-4 w-4 text-gray-600' />
257- < span className = 'text-xs text-gray-600' > Enter</ span >
258- </ div >
259- < div className = 'flex flex-col items-center gap-1 cursor-pointer hover:bg-gray-100 p-1 rounded' >
260- < Trash2 className = 'h-4 w-4 text-gray-600' />
261- < span className = 'text-xs text-gray-600' > Delete</ span >
262- </ div >
263- </ div >
264- </ td >
265- </ tr >
266- ) ) }
267- </ tbody >
268- </ table >
269- </ div >
270- </ div >
52+ < ContestsTable contests = { contests } />
27153
272- { /* 추가 카드 디자인 ... 흠 */ }
54+ { /* TODO: Make Components */ }
27355 < div className = 'grid grid-cols-1 md:grid-cols-3 gap-6 mt-8' >
27456 < div className = 'bg-white rounded-xl p-6 border border-gray-200/60 shadow-lg' >
27557 < div className = 'flex items-center gap-3' >
0 commit comments