Skip to content

Commit 8459046

Browse files
authored
Merge pull request #34 from codersforcauses/issue-23-Create_the_leaderboard_page
Issue 23 create the leaderboard page
2 parents 5e867e8 + 5d47808 commit 8459046

4 files changed

Lines changed: 237 additions & 19 deletions

File tree

client/public/ProgressBar4.svg

Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { useEffect, useState } from "react";
2+
3+
export default function List({ data = [] }) {
4+
// Mock data for development (with four teams per group)
5+
const mockData = [
6+
// Group A teams
7+
{
8+
groupRank: 1,
9+
teamName: "Drone Rangers",
10+
honourPoints: 350,
11+
regularPoints: 420,
12+
missionTime: "01:45:22",
13+
group: "Group A",
14+
qualified: true,
15+
},
16+
{
17+
groupRank: 2,
18+
teamName: "RoboPilots",
19+
honourPoints: 290,
20+
regularPoints: 365,
21+
missionTime: "02:05:33",
22+
group: "Group A",
23+
qualified: true,
24+
},
25+
{
26+
groupRank: 3,
27+
teamName: "Airborne Coders",
28+
honourPoints: 210,
29+
regularPoints: 280,
30+
missionTime: "02:15:40",
31+
group: "Group A",
32+
qualified: false,
33+
},
34+
{
35+
groupRank: 4,
36+
teamName: "Flying Algorithms",
37+
honourPoints: 180,
38+
regularPoints: 240,
39+
missionTime: "02:30:15",
40+
group: "Group A",
41+
qualified: false,
42+
},
43+
44+
// Group B teams
45+
{
46+
groupRank: 1,
47+
teamName: "Sky Navigators",
48+
honourPoints: 320,
49+
regularPoints: 380,
50+
missionTime: "01:52:10",
51+
group: "Group B",
52+
qualified: true,
53+
},
54+
{
55+
groupRank: 2,
56+
teamName: "Circuit Flyers",
57+
honourPoints: 250,
58+
regularPoints: 310,
59+
missionTime: "02:18:20",
60+
group: "Group B",
61+
qualified: true,
62+
},
63+
{
64+
groupRank: 3,
65+
teamName: "Byte Squadron",
66+
honourPoints: 220,
67+
regularPoints: 290,
68+
missionTime: "02:25:17",
69+
group: "Group B",
70+
qualified: false,
71+
},
72+
{
73+
groupRank: 4,
74+
teamName: "Quantum Wings",
75+
honourPoints: 190,
76+
regularPoints: 250,
77+
missionTime: "02:35:45",
78+
group: "Group B",
79+
qualified: false,
80+
},
81+
82+
// Group C teams
83+
{
84+
groupRank: 1,
85+
teamName: "Aerial Mechanics",
86+
honourPoints: 270,
87+
regularPoints: 340,
88+
missionTime: "02:11:45",
89+
group: "Group C",
90+
qualified: true,
91+
},
92+
{
93+
groupRank: 2,
94+
teamName: "PropCoders",
95+
honourPoints: 240,
96+
regularPoints: 300,
97+
missionTime: "02:20:30",
98+
group: "Group C",
99+
qualified: true,
100+
},
101+
{
102+
groupRank: 3,
103+
teamName: "Binary Flyers",
104+
honourPoints: 200,
105+
regularPoints: 270,
106+
missionTime: "02:28:55",
107+
group: "Group C",
108+
qualified: false,
109+
},
110+
{
111+
groupRank: 4,
112+
teamName: "Aerodynamic Devs",
113+
honourPoints: 170,
114+
regularPoints: 230,
115+
missionTime: "02:38:20",
116+
group: "Group C",
117+
qualified: false,
118+
},
119+
];
120+
121+
// Use provided data or fallback to mock data
122+
const teamsData = data.length > 0 ? data : mockData;
123+
124+
// Get unique groups for dropdown
125+
const groups = [...new Set(teamsData.map((item) => item.group))];
126+
127+
// Initialize selectedGroup with the first group instead of "All Groups"
128+
const [selectedGroup, setSelectedGroup] = useState(groups[0] || "Group A");
129+
130+
// Filter data for the selected group only
131+
const displayData = teamsData.filter((item) => item.group === selectedGroup);
132+
133+
const handleGroupChange = (e) => {
134+
setSelectedGroup(e.target.value);
135+
};
136+
137+
return (
138+
<div className="w-full overflow-hidden rounded-lg shadow-lg">
139+
<div className="overflow-x-auto">
140+
{/* Group selection dropdown */}
141+
<div className="flex bg-white p-4">
142+
<div className="relative">
143+
<label
144+
htmlFor="groupSelect"
145+
className="body-sm absolute -top-2 left-3 z-10 bg-white px-1 text-xs"
146+
>
147+
Select Group:
148+
</label>
149+
<select
150+
id="groupSelect"
151+
value={selectedGroup}
152+
onChange={handleGroupChange}
153+
className="body-sm rounded-md border border-gray-300 bg-white px-4 py-2 pt-3 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary"
154+
>
155+
{groups.map((group) => (
156+
<option key={group} value={group}>
157+
{group}
158+
</option>
159+
))}
160+
</select>
161+
</div>
162+
</div>
163+
164+
<table className="min-w-full bg-white">
165+
<thead>
166+
<tr className="title bg-primary text-center font-bold text-white">
167+
<th className="px-6 py-4 md:text-lg">Group Rank</th>
168+
<th className="px-6 py-4 md:text-lg">Team</th>
169+
<th className="px-6 py-4 md:text-lg">Honour Points</th>
170+
<th className="px-6 py-4 md:text-lg">Regular Points</th>
171+
<th className="px-6 py-4 md:text-lg">Mission Time</th>
172+
<th className="px-6 py-4 md:text-lg">Status</th>
173+
</tr>
174+
</thead>
175+
<tbody className="body-sm">
176+
{displayData.map((row, index) => (
177+
<tr
178+
key={index}
179+
className={`border-b border-gray-200 text-center hover:bg-gray-100 ${
180+
row.qualified ? "bg-green-50" : ""
181+
}`}
182+
>
183+
<td className="body-lg px-6 py-4 font-bold italic">
184+
{row.groupRank}
185+
</td>
186+
<td className="px-6 py-4">{row.teamName}</td>
187+
<td className="px-6 py-4">{row.honourPoints}</td>
188+
<td className="px-6 py-4">{row.regularPoints}</td>
189+
<td className="px-6 py-4">{row.missionTime}</td>
190+
<td className="px-6 py-4">
191+
{row.qualified ? (
192+
<span className="font-medium text-green-600">
193+
Qualified
194+
</span>
195+
) : (
196+
<span className="text-gray-500">Not Qualified</span>
197+
)}
198+
</td>
199+
</tr>
200+
))}
201+
</tbody>
202+
</table>
203+
</div>
204+
</div>
205+
);
206+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import List from "./List";
2+
3+
export default function Leaderboard() {
4+
return <List />;
5+
}

client/src/pages/leaderboard.tsx

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
1-
import { useState } from "react";
1+
import Image from "next/image";
22

3-
import { usePings } from "@/hooks/pings";
4-
5-
import { Button } from "../components/ui/button";
6-
7-
export default function Home() {
8-
const [clicked, setClicked] = useState(false);
9-
const { data, isLoading } = usePings({
10-
enabled: clicked,
11-
});
3+
import Dropdown from "../components/ui/Leaderboard/Dropdown";
4+
import List from "../components/ui/Leaderboard/List";
125

6+
export default function LeaderboardPage() {
137
return (
14-
<>
15-
<h1 className="title-large text-dark">Title Test</h1>
16-
<Button onClick={() => setClicked(true)}>
17-
{isLoading ? "Loading" : "Ping"}
18-
</Button>
19-
<p>
20-
Response from server: <span>{data as string}</span>
21-
</p>
22-
</>
8+
<div className="flex min-h-screen flex-col items-center bg-light pt-8">
9+
<div>
10+
<h1 className="title-large text-center font-bold">Leaderboard</h1>
11+
<div className="relative mx-auto h-[80px] w-[1100px]">
12+
<Image
13+
src="/ProgressBar4.svg"
14+
alt="Progress Bar"
15+
fill
16+
className="object-contain"
17+
priority
18+
/>
19+
</div>
20+
</div>
21+
<div className="">
22+
<List />
23+
</div>
24+
</div>
2325
);
2426
}

0 commit comments

Comments
 (0)