|
1 | 1 | 'use client'; |
2 | 2 |
|
3 | | -import { Trophy, Users, FileCode2, Plus } from 'lucide-react'; |
4 | | -import { Button } from 'react-bootstrap'; |
| 3 | +import React from 'react'; |
| 4 | +import { Plus, Trophy } from 'lucide-react'; |
5 | 5 |
|
| 6 | +import ContestTable, { Contest } from '@/components/ContestTable'; |
| 7 | +import contestsData from '@/data/contestsData'; |
6 | 8 | import { Checkbox } from '@/components/ui/checkbox'; |
7 | | -import ContestsTable from '@/components/ContestsTable'; |
| 9 | +import { Button } from '@/components/ui/button'; |
| 10 | +import ContestCards from '@/components/ContestCards'; |
8 | 11 | import Footer from '@/components/Footer'; |
9 | | -// import contests from '@/data/contests'; |
10 | 12 |
|
11 | 13 | export default function ContestsPage() { |
| 14 | + const [contests, setContests] = React.useState<Contest[]>(contestsData); |
| 15 | + |
| 16 | + const [favoriteOnly, setFavoriteOnly] = React.useState(false); |
| 17 | + |
| 18 | + const onClickFavorite = (id: Contest['id']) => { |
| 19 | + setContests((prev) => |
| 20 | + prev.map((c) => (c.id === id ? { ...c, isFavorite: !c.isFavorite } : c)), |
| 21 | + ); |
| 22 | + }; |
| 23 | + |
| 24 | + const filteredContests = React.useMemo( |
| 25 | + () => (favoriteOnly ? contests.filter((p) => p.isFavorite) : contests), |
| 26 | + [contests, favoriteOnly], |
| 27 | + ); |
| 28 | + |
| 29 | + const onEnter = (id: Contest['id']) => { |
| 30 | + // TODO: enter the contest/{id} detail edit page |
| 31 | + console.log('enter', id); |
| 32 | + }; |
| 33 | + |
| 34 | + const onDelete = (id: Contest['id']) => { |
| 35 | + // TODO: delete the contest/{id} immediately |
| 36 | + const ok = window.confirm('Are you sure you want to delete this?'); |
| 37 | + if (!ok) return; |
| 38 | + setContests((prev) => prev.filter((c) => c.id !== id)); |
| 39 | + }; |
| 40 | + |
12 | 41 | return ( |
13 | 42 | <div className='min-h-screen bg-gradient-to-br from-gray-50 to-gray-100'> |
14 | | - <div className='container px-4 py-8 mx-auto'> |
15 | | - <div className='flex items-center justify-between mb-8'> |
| 43 | + <div className='container px-4 py-6 mx-auto'> |
| 44 | + <div className='flex items-center justify-between mb-6'> |
16 | 45 | <div> |
17 | 46 | <h1 className='text-3xl font-bold text-gray-900 flex items-center gap-3'> |
18 | 47 | <Trophy className='h-8 w-8 text-primary' /> |
19 | 48 | Contests |
20 | 49 | </h1> |
21 | | - <p className='text-gray-600 mt-1'>{contests.length} contests available</p> |
| 50 | + <p className='text-gray-600 mt-1'> |
| 51 | + {filteredContests.length} contests available |
| 52 | + </p> |
22 | 53 | </div> |
23 | | - <div className='flex items-center gap-6'> |
24 | | - <div className='flex items-center gap-6'> |
25 | | - <div className='flex items-center gap-2'> |
26 | | - <Checkbox id='favorites' /> {/* TODO: Implement favorite filter */} |
27 | | - <label |
28 | | - htmlFor='favorites' |
29 | | - className='text-sm font-medium text-gray-700' |
30 | | - > |
31 | | - Only favorite |
32 | | - </label> |
33 | | - </div> |
34 | | - <div className='flex items-center gap-2'> |
35 | | - <Checkbox id='activity' />{' '} |
36 | | - {/* TODO: Implement view only my-activity filter */} |
37 | | - <label htmlFor='activity' className='text-sm font-medium text-gray-700'> |
38 | | - Only my activity |
39 | | - </label> |
40 | | - </div> |
| 54 | + <div className='flex items-center gap-4'> |
| 55 | + <div className='flex items-center gap-2'> |
| 56 | + <Checkbox |
| 57 | + id='favorites' |
| 58 | + checked={favoriteOnly} |
| 59 | + onCheckedChange={(v) => setFavoriteOnly(Boolean(v))} |
| 60 | + aria-controls='contests-table' |
| 61 | + /> |
| 62 | + <label htmlFor='favorites' className='text-sm cursor-pointer'> |
| 63 | + Only favorite |
| 64 | + </label> |
| 65 | + </div> |
| 66 | + {/* TODO: get user-info, and add filter function and onClick state */} |
| 67 | + <div className='flex items-center gap-2'> |
| 68 | + <Checkbox id='activity' /> |
| 69 | + <label htmlFor='activity' className='text-sm'> |
| 70 | + Only my activity |
| 71 | + </label> |
41 | 72 | </div> |
42 | 73 | <Button |
| 74 | + onClick={() => console.log('Creating new contests!')} |
43 | 75 | className='inline-flex flex-row flex-nowrap items-center gap-2 whitespace-nowrap leading-none |
44 | 76 | bg-amber-400 hover:bg-amber-500 text-white px-4 py-2 h-auto rounded-xl shadow' |
45 | 77 | > |
46 | 78 | <Plus className='h-4 w-4 shrink-0' /> |
47 | | - <span className='font-medium inline-block'>New Contest</span> |
| 79 | + <span className='font-medium inline-block'>Create Contest</span> |
48 | 80 | </Button> |
49 | 81 | </div> |
50 | 82 | </div> |
51 | 83 |
|
52 | | - <ContestsTable contests={contests} /> |
53 | | - |
54 | | - {/* TODO: Make Components */} |
55 | | - <div className='grid grid-cols-1 md:grid-cols-3 gap-6 mt-8'> |
56 | | - <div className='bg-white rounded-xl p-6 border border-gray-200/60 shadow-lg'> |
57 | | - <div className='flex items-center gap-3'> |
58 | | - <div className='w-12 h-12 bg-gradient-to-br from-green-500 to-emerald-600 rounded-lg flex items-center justify-center'> |
59 | | - <Trophy className='h-6 w-6 text-white' /> |
60 | | - </div> |
61 | | - <div> |
62 | | - <p className='text-2xl font-bold text-gray-900'>3</p> |
63 | | - <p className='text-sm text-gray-600'>Total Contests</p> |
64 | | - </div> |
65 | | - </div> |
66 | | - </div> |
67 | | - <div className='bg-white rounded-xl p-6 border border-gray-200/60 shadow-lg'> |
68 | | - <div className='flex items-center gap-3'> |
69 | | - <div className='w-12 h-12 bg-gradient-to-br from-purple-500 to-pink-600 rounded-lg flex items-center justify-center'> |
70 | | - <FileCode2 className='h-6 w-6 text-white' /> |
71 | | - </div> |
72 | | - <div> |
73 | | - <p className='text-2xl font-bold text-gray-900'>45</p> |
74 | | - <p className='text-sm text-gray-600'>Total Problems</p> |
75 | | - </div> |
76 | | - </div> |
77 | | - </div> |
78 | | - <div className='bg-white rounded-xl p-6 border border-gray-200/60 shadow-lg'> |
79 | | - <div className='flex items-center gap-3'> |
80 | | - <div className='w-12 h-12 bg-gradient-to-br from-blue-500 to-cyan-600 rounded-lg flex items-center justify-center'> |
81 | | - <Users className='h-6 w-6 text-white' /> |
82 | | - </div> |
83 | | - <div> |
84 | | - <p className='text-2xl font-bold text-gray-900'>479</p> |
85 | | - <p className='text-sm text-gray-600'>Total Participants</p> |
86 | | - </div> |
87 | | - </div> |
88 | | - </div> |
89 | | - </div> |
| 84 | + <ContestTable |
| 85 | + contests={filteredContests} |
| 86 | + onClickFavorite={onClickFavorite} |
| 87 | + onEnter={onEnter} |
| 88 | + onDelete={onDelete} |
| 89 | + /> |
| 90 | + <ContestCards contests={filteredContests} /> |
90 | 91 | </div> |
91 | 92 | <Footer /> |
92 | 93 | </div> |
|
0 commit comments