Skip to content

Commit b66e171

Browse files
committed
table -> component table
1 parent 1e0377a commit b66e171

File tree

2 files changed

+251
-234
lines changed

2 files changed

+251
-234
lines changed

src/app/contests/page.tsx

Lines changed: 16 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,16 @@
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';
156
import { 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';
198
import Footer from '@/components/Footer';
20-
21-
type SortKey = 'id' | 'name' | 'owner' | 'modificationTime' | 'creationTime';
9+
// import contests from '@/data/contests';
2210

2311
export 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

Comments
 (0)