From 3283ce3b8dda9272cb1658fb6de8ea729f1b7777 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 10:48:58 +0000 Subject: [PATCH 01/13] add save to model --- server/api/match/models.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/server/api/match/models.py b/server/api/match/models.py index 03ccd6d..bcac03b 100644 --- a/server/api/match/models.py +++ b/server/api/match/models.py @@ -36,9 +36,26 @@ class MatchResult(models.Model): created_at = models.DateTimeField(auto_now_add=True) p1_position = models.IntegerField(null=True, blank=True) p2_position = models.IntegerField(null=True, blank=True) - honor_point = models.IntegerField(null=True, blank=True) - point = models.IntegerField(null=True, blank=True) + honor_point = models.IntegerField(null=True, blank=True, editable=False) + point = models.IntegerField(null=True, blank=True, editable=False) completed_time_second = models.IntegerField(null=True, blank=True) def __str__(self): return f"{self.team} in {self.match}" + + def save(self, *args, **kwargs): + # Default to 0 if any field is None + wp = self.white_pins or 0 + pp = self.penalty_pins or 0 + yc = self.yellow_cards or 0 + rc = self.red_cards or 0 + + self.point = wp * 2 + pp * -3 + yc * -3 + rc * -10 + self.honor_point = self._get_honor_point( + self.p1_position, self.p2_position) + super().save(*args, **kwargs) + + def _get_honor_point(self, p1_position, p2_position): + if not p1_position or p1_position != 1: + return 0 + return 1 + (4 - (p2_position or 0)) From f7b15ef453e54e309b728407c1562056d27851a4 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 10:49:51 +0000 Subject: [PATCH 02/13] add GroupLeaderboard for view --- server/api/details/views.py | 56 +++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/server/api/details/views.py b/server/api/details/views.py index ed5b0d1..524b8ac 100644 --- a/server/api/details/views.py +++ b/server/api/details/views.py @@ -23,7 +23,13 @@ def get(self, request, stageid): if stageid == 0: return Response("This route is not for group stage, use group_stage// instead") + group_id = request.GET.get("group_id") + queryset = MatchResult.objects.filter(match__stage_id=stageid) + + if group_id: + queryset = queryset.filter(match__group_id=group_id) + serializer = MatchResultSerializer(queryset, many=True) grouped_data = defaultdict(list) @@ -34,10 +40,56 @@ def get(self, request, stageid): def get_groups(request): - group_ids = sorted(list(Match.objects.values_list('group_id', flat=True).distinct())) + group_ids = sorted( + list(Match.objects.values_list('group_id', flat=True).distinct())) return JsonResponse(group_ids, safe=False) def get_stages(request): - stage_ids = sorted(list(Match.objects.values_list('stage_id', flat=True).distinct())) + stage_ids = sorted( + list(Match.objects.values_list('stage_id', flat=True).distinct())) return JsonResponse(stage_ids, safe=False) + + +class GroupLeaderboard(APIView): + def get(self, request, groupid): + queryset = MatchResult.objects.filter(match__group_id=groupid) + serializer = MatchResultSerializer(queryset, many=True) + + # Group and aggregate by team_name + team_summary = defaultdict(lambda: { + "team_name": "", + "honor_point": 0, + "point": 0, + "completed_time_second": 0, + "entries": 0, + "group": groupid, + }) + + for item in serializer.data: + team = team_summary[item["team_name"]] + team["team_name"] = item["team_name"] + team["honor_point"] += item["honor_point"] or 0 + team["point"] += item["point"] or 0 + team["completed_time_second"] += item["completed_time_second"] or 0 + team["entries"] += 1 + + # Use accumulated mission time (not average) + teams = list(team_summary.values()) + + # Sort by: regular points desc, then honor point desc, then mission time asc + teams_sorted = sorted( + teams, + key=lambda x: ( + -x["honor_point"], + -x["point"], + x["completed_time_second"] + ) + ) + + # Assign rank + qualification + for i, team in enumerate(teams_sorted): + team["group_rank"] = i + 1 + team["qualified"] = team["group_rank"] == 1 + + return Response(teams_sorted) From 9a46f4b5c08d07523edefb1f67cc56c87510310d Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 10:51:12 +0000 Subject: [PATCH 03/13] import GroupLeaderboard --- server/api/details/urls.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/server/api/details/urls.py b/server/api/details/urls.py index bd8aa0f..ddb6bab 100644 --- a/server/api/details/urls.py +++ b/server/api/details/urls.py @@ -1,9 +1,19 @@ from django.urls import path -from .views import MatchResultsByGroup, MatchResultsByStage, get_groups, get_stages +from .views import ( + MatchResultsByGroup, + MatchResultsByStage, + get_groups, + get_stages, + GroupLeaderboard, +) urlpatterns = [ - path('group_stage//', MatchResultsByGroup.as_view(), name='match-results-by-group'), - path('knockout_stage//', MatchResultsByStage.as_view(), name='match-results-by-stage'), + path('group_stage//', MatchResultsByGroup.as_view(), + name='match-results-by-group'), + path('knockout_stage//', + MatchResultsByStage.as_view(), name='match-results-by-stage'), path('groups/', get_groups, name='get-groups'), - path('stages/', get_stages, name='get-stages') + path('stages/', get_stages, name='get-stages'), + path('group_leaderboard//', + GroupLeaderboard.as_view(), name='group-leaderboard'), ] From 306b6b01b261bf2ddb2c189c4db46691602a93a0 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 10:54:06 +0000 Subject: [PATCH 04/13] adding team_name match_id match_name group and stage --- server/api/details/serializers.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/server/api/details/serializers.py b/server/api/details/serializers.py index df3bca2..8a442b9 100644 --- a/server/api/details/serializers.py +++ b/server/api/details/serializers.py @@ -1,10 +1,31 @@ +# details/serializers.py + from rest_framework import serializers from api.match.models import MatchResult class MatchResultSerializer(serializers.ModelSerializer): + team_name = serializers.CharField(source='team.team_name') match_id = serializers.IntegerField(source='match.match_id') + match_name = serializers.CharField(source='match.match_name') + group = serializers.IntegerField(source='match.group_id') + stage = serializers.IntegerField(source='match.stage_id') class Meta: model = MatchResult - fields = '__all__' + fields = [ + 'match_id', + 'match_name', + 'group', + 'stage', + 'team_name', + 'white_pins', + 'penalty_pins', + 'yellow_cards', + 'red_cards', + 'p1_position', + 'p2_position', + 'honor_point', + 'point', + 'completed_time_second', + ] From 8957bc3955fd8a1e55446115ca3ef232e890ca2d Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 10:55:09 +0000 Subject: [PATCH 05/13] move corsheaders --- server/api/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/api/settings.py b/server/api/settings.py index d2931a7..395a421 100644 --- a/server/api/settings.py +++ b/server/api/settings.py @@ -64,6 +64,7 @@ ] MIDDLEWARE = [ + "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", @@ -71,7 +72,6 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", - "corsheaders.middleware.CorsMiddleware", ] CORS_ALLOWED_ORIGINS = [ From c6450c613add5a2277ed042c1c5cfe5b5b153e55 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 11:01:11 +0000 Subject: [PATCH 06/13] move matchdetails to match-details --- client/src/pages/match-details.tsx | 26 ++++++++++++- client/src/pages/matchdetails.tsx | 59 ++---------------------------- 2 files changed, 28 insertions(+), 57 deletions(-) diff --git a/client/src/pages/match-details.tsx b/client/src/pages/match-details.tsx index 6ff5373..3c51cea 100644 --- a/client/src/pages/match-details.tsx +++ b/client/src/pages/match-details.tsx @@ -1,3 +1,25 @@ -export default function Home() { - return <>; +import Image from "next/image"; + +import MatchDetailsList from "@/components/ui/Matchdetails/List"; + +export default function MatchDetailsPage() { + return ( +
+
+

Match Details

+
+ Progress Bar +
+
+
+ +
+
+ ); } diff --git a/client/src/pages/matchdetails.tsx b/client/src/pages/matchdetails.tsx index 6783450..3c51cea 100644 --- a/client/src/pages/matchdetails.tsx +++ b/client/src/pages/matchdetails.tsx @@ -1,59 +1,8 @@ import Image from "next/image"; -import { useEffect, useState } from "react"; -import { MatchDetailsData } from "../components/ui/Common/types"; -import List, { MatchDetailsProps } from "../components/ui/Matchdetails/List"; - -const columns = [ - "Team name", - "Opponent", - "White Pins", - "Penalty Pins", - "Yellow Card", - "Red Card", - "P1 position", - "P2 position", - "Honour Point", - "Regular Point", - "Completed Time", -]; - -const mockData: MatchDetailsData[] = [ - { - teamName: "Red Falcons", - opponent: "Blue Hawks", - whitePins: 2, - penaltyPins: 0, - yellowCard: 1, - redCard: 0, - p1_postion: 1, - p2_postion: 0, - honorPoint: 3, - regularPoint: 3, - completedTime: 240, - }, - { - teamName: "Blue Hawks", - opponent: "Red Falcons", - whitePins: 2, - penaltyPins: 0, - yellowCard: 1, - redCard: 0, - p1_postion: 1, - p2_postion: 0, - honorPoint: 3, - regularPoint: 3, - completedTime: 240, - }, -]; - -export default function MatchDetails() { - const [data, setData] = useState(mockData); - - useEffect(() => { - // Send API Request - }, []); +import MatchDetailsList from "@/components/ui/Matchdetails/List"; +export default function MatchDetailsPage() { return (
@@ -68,8 +17,8 @@ export default function MatchDetails() { />
-
- +
+
); From 06f985190bfb0bdfdaa136eca2183373e93ca814 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 11:03:27 +0000 Subject: [PATCH 07/13] connect leaderboard to backend --- client/src/components/ui/Leaderboard/List.tsx | 303 +++++++----------- 1 file changed, 115 insertions(+), 188 deletions(-) diff --git a/client/src/components/ui/Leaderboard/List.tsx b/client/src/components/ui/Leaderboard/List.tsx index c6dcbab..4504486 100644 --- a/client/src/components/ui/Leaderboard/List.tsx +++ b/client/src/components/ui/Leaderboard/List.tsx @@ -1,156 +1,68 @@ -import { useEffect, useState } from "react"; - -const groupNames: String[] = ["", "Group A", "Group B", "Group C"]; - -export default function List({ data = [] }) { - // Mock data for development (with four teams per group) - const mockData = [ - // Group A teams - { - team_name: "Drone Rangers", - honor_points: 350, - regular_points: 420, - total_time_seconds: 154, - group: 1, - }, - { - team_name: "RoboPilots", - honor_points: 290, - regular_points: 365, - total_time_seconds: 234, - group: 1, - }, - { - team_name: "Airborne Coders", - honor_points: 210, - regular_points: 280, - total_time_seconds: 163, - group: 1, - }, - { - team_name: "Flying Algorithms", - honor_points: 180, - regular_points: 240, - total_time_seconds: 274, - group: 1, - }, - - // Group B teams - { - team_name: "Sky Navigators", - honor_points: 320, - regular_points: 380, - total_time_seconds: 198, - group: 2, - }, - { - team_name: "Circuit Flyers", - honor_points: 250, - regular_points: 310, - total_time_seconds: 193, - group: 2, - }, - { - team_name: "Byte Squadron", - honor_points: 220, - regular_points: 290, - total_time_seconds: 198, - group: 2, - }, - { - team_name: "Quantum Wings", - honor_points: 190, - regular_points: 250, - total_time_seconds: 183, - group: 2, - }, - - // Group C teams - { - team_name: "Aerial Mechanics", - honor_points: 270, - regular_points: 340, - total_time_seconds: 348, - group: 3, - }, - { - team_name: "PropCoders", - honor_points: 240, - regular_points: 300, - total_time_seconds: 314, - group: 3, - }, - { - team_name: "Binary Flyers", - honor_points: 200, - regular_points: 270, - total_time_seconds: 109, - group: 3, - }, - { - team_name: "Aerodynamic Devs", - honor_points: 170, - regular_points: 230, - total_time_seconds: 54, - group: 3, - }, - ]; - - // TODO: Fetch real data from api/match/group - - // Use provided data or fallback to mock data - const rawData = data.length > 0 ? data : mockData; - - // Get unique groups - const groupIds = [...new Set(rawData.map((item) => item.group))]; +"use client"; - function processData(rawData: Array) { - let teamsData: Array = []; - - // For each group - for (let i = 0; i < groupIds.length; i++) { - // Get all the teams in that group - let groupData = rawData.filter((team) => { - return team.group === groupIds[i]; - }); - - // Sort them descending by points - groupData.sort((teamA, teamB) => { - return teamB.regular_points - teamA.regular_points; - // TODO: Implement sorting by honor points in case of draw, then by time to finish - }); +import { useEffect, useState } from "react"; - // Add each of those teams into the teamData with calculated fields - for (let j = 0; j < groupData.length; j++) { - let team = groupData[j]; - team.group = groupNames[team.group]; - team.group_rank = j + 1; // TODO: Make resistant to ties - team.qualified = team.group_rank <= 2; // TODO: Make resistant to ties - team.mission_time = - "" + - Math.floor(team.total_time_seconds / 60) + - ":" + - (team.total_time_seconds % 60); - teamsData.push(team); +const API_BASE = process.env.NEXT_PUBLIC_BACKEND_URL; + +type TeamResult = { + team_name: string; + honor_point: number; + point: number; + completed_time_second: number; + group: number; + group_rank: number; + qualified: boolean; +}; + +export default function List() { + const [groups, setGroups] = useState([]); + const [selectedGroup, setSelectedGroup] = useState(null); + const [rawData, setRawData] = useState([]); + const [loading, setLoading] = useState(false); + + // Fetch group list on first load + useEffect(() => { + async function fetchGroups() { + try { + const res = await fetch(`${API_BASE}/details/groups/`); + const data: number[] = await res.json(); + setGroups(data); + if (data.length > 0) { + setSelectedGroup(data[0]); + } + } catch (err) { + console.error("Failed to fetch groups:", err); } } - return teamsData; - } - - // Add calculated fields group_rank and qualified to the data - const teamsData = processData(rawData); - // Get unique groups for dropdown - const groups = [...new Set(teamsData.map((item) => item.group))]; - - // Initialize selectedGroup with the first group instead of "All Groups" - const [selectedGroup, setSelectedGroup] = useState(groups[0] || 1); + fetchGroups(); + }, []); + + // Fetch leaderboard for selected group + useEffect(() => { + if (selectedGroup === null) return; + + async function fetchLeaderboard() { + setLoading(true); + try { + const res = await fetch( + `${API_BASE}/details/group_leaderboard/${selectedGroup}/`, + ); + const data = await res.json(); + setRawData(data); + } catch (err) { + console.error("Failed to fetch leaderboard:", err); + } finally { + setLoading(false); + } + } - // Filter data for the selected group only - const displayData = teamsData.filter((item) => item.group === selectedGroup); + fetchLeaderboard(); + }, [selectedGroup]); - const handleGroupChange = (e: any) => { - setSelectedGroup(e.target.value); + const handleGroupChange = (e: React.ChangeEvent) => { + const newGroup = parseInt(e.target.value, 10); + setSelectedGroup(newGroup); }; return ( @@ -167,58 +79,73 @@ export default function List({ data = [] }) { - - - - - - - - - - - - - {displayData.map((row, index) => ( - - - - - - - + {loading ? ( +

Loading leaderboard...

+ ) : ( +
Group RankTeamHonour PointsRegular PointsMission TimeStatus
- {row.group_rank} - {row.team_name}{row.honor_points}{row.regular_points}{row.mission_time} - {row.qualified ? ( - - Qualified - - ) : ( - Not Qualified - )} -
+ + + + + + + + - ))} - -
Group RankTeamHonour PointsRegular PointsMission TimeStatus
+ + + {rawData.length === 0 ? ( + + + No results yet + + + ) : ( + rawData.map((row, index) => ( + + + {row.group_rank} + + {row.team_name} + {row.honor_point} + {row.point} + {row.completed_time_second} + + {row.qualified ? ( + + Qualified + + ) : ( + Not Qualified + )} + + + )) + )} + + + )} ); From 2c117fdd14d4d25cf55cab9c1f03ca794f802ba9 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 11:03:43 +0000 Subject: [PATCH 08/13] connect matchdetails to backend --- .../src/components/ui/Matchdetails/List.tsx | 229 +++++++++++++++--- 1 file changed, 194 insertions(+), 35 deletions(-) diff --git a/client/src/components/ui/Matchdetails/List.tsx b/client/src/components/ui/Matchdetails/List.tsx index 904134e..3b74d9d 100644 --- a/client/src/components/ui/Matchdetails/List.tsx +++ b/client/src/components/ui/Matchdetails/List.tsx @@ -1,47 +1,206 @@ +"use client"; + import { useEffect, useState } from "react"; import { MatchDetailsData } from "../Common/types"; -export type MatchDetailsProps = { - matchData: MatchDetailsData[]; - header: string[]; -}; +export default function MatchDetailsList() { + const BASE_URL = process.env.NEXT_PUBLIC_BACKEND_URL; + + const [matchData, setMatchData] = useState([]); + const [groups, setGroups] = useState([]); + const [stages, setStages] = useState([]); + const [selectedGroup, setSelectedGroup] = useState(null); + const [selectedStage, setSelectedStage] = useState(null); + const [loading, setLoading] = useState(false); + + useEffect(() => { + async function fetchDropdownOptions() { + try { + const [groupRes, stageRes] = await Promise.all([ + fetch(`${BASE_URL}/details/groups/`), + fetch(`${BASE_URL}/details/stages/`), + ]); + const groupData = await groupRes.json(); + const stageData = await stageRes.json(); + setGroups(groupData); + setStages(stageData); + if (stageData.length > 0) setSelectedStage(stageData[0]); + } catch (err) { + console.error("Failed to fetch dropdown options", err); + } + } + + fetchDropdownOptions(); + }, []); + + useEffect(() => { + if (!selectedStage) return; + + async function fetchMatchDetails() { + setLoading(true); + try { + let url = `${BASE_URL}/details/knockout_stage/${selectedStage}/`; + if (selectedGroup !== null) url += `?group_id=${selectedGroup}`; + + const res = await fetch(url); + const data = await res.json(); + + const transformed = data.map((match: any[]) => + match.map((item: any) => ({ + teamName: item.team_name, + matchId: item.match_id, + matchName: item.match_name, + group: item.group, + stage: item.stage, + honorPoint: item.honor_point, + point: item.point, + completedTime: item.completed_time_second, + p1_postion: item.p1_position, + p2_postion: item.p2_position, + whitePins: item.white_pins, + penaltyPins: item.penalty_pins, + yellowCard: item.yellow_cards, + redCard: item.red_cards, + })), + ); + + setMatchData(transformed); + } catch (err) { + console.error("Failed to fetch match details", err); + setMatchData([]); + } finally { + setLoading(false); + } + } + + fetchMatchDetails(); + }, [selectedGroup, selectedStage]); + + const header = [ + "Team Name", + "White Pins", + "Penalty Pins", + "Yellow Card", + "Red Card", + "P1 Position", + "P2 Position", + "Honor Points", + "Regular Points", + "Completed Time", + ]; + + const sortedMatchData = [...matchData].sort((a, b) => { + const aId = a[0]?.matchId ?? 0; + const bId = b[0]?.matchId ?? 0; + return aId - bId; + }); -export default function List({ matchData, header }: MatchDetailsProps) { return (
- - - - {header.map((row, index) => ( - +
+ {/* Dropdowns */} +
+ + +
+ +
+ + +
+
+ + {loading ? ( +

Loading match details...

+ ) : selectedStage ? ( + matchData.length === 0 ? ( +

+ No match data available. +

+ ) : ( +
+ {sortedMatchData.map((match, idx) => ( +
+

+ {match[0]?.matchName ?? `Match ${idx + 1}`} +

+
- {row} -
+ + + {header.map((col, i) => ( + + ))} + + + + {match.map((row, index) => ( + + + + + + + + + + + + + ))} + +
+ {col} +
{row.teamName}{row.whitePins}{row.penaltyPins}{row.yellowCard}{row.redCard}{row.p1_postion}{row.p2_postion}{row.honorPoint}{row.point}{row.completedTime}
+
))} - - - - {matchData.map((row, index) => ( - - {row.teamName} - {row.opponent} - {row.whitePins} - {row.penaltyPins} - {row.yellowCard} - {row.redCard} - {row.p1_postion} - {row.p2_postion} - {row.honorPoint} - {row.regularPoint} - {row.completedTime} - - ))} - - +
+ ) + ) : ( +

+ Select a stage to view results +

+ )} ); From 6c48767ca9006cfe1d2efb6514fa279a88462db5 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 11:04:10 +0000 Subject: [PATCH 09/13] add matchId matchName group and stage --- client/src/components/ui/Common/types.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/components/ui/Common/types.tsx b/client/src/components/ui/Common/types.tsx index c68b439..9d7c43b 100644 --- a/client/src/components/ui/Common/types.tsx +++ b/client/src/components/ui/Common/types.tsx @@ -1,6 +1,5 @@ export type MatchDetailsData = { teamName: string; - opponent: string; whitePins: number; penaltyPins: number; yellowCard: number; @@ -10,4 +9,9 @@ export type MatchDetailsData = { honorPoint: number; regularPoint: number; completedTime: number; + + matchId: number; + matchName: string; + group: number; + stage: number; }; From 443155b1d274ae198419b7beba37965afead98c8 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 11:08:29 +0000 Subject: [PATCH 10/13] fixing typo and point --- client/src/components/ui/Common/types.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/ui/Common/types.tsx b/client/src/components/ui/Common/types.tsx index 9d7c43b..552d9f5 100644 --- a/client/src/components/ui/Common/types.tsx +++ b/client/src/components/ui/Common/types.tsx @@ -4,10 +4,10 @@ export type MatchDetailsData = { penaltyPins: number; yellowCard: number; redCard: number; - p1_postion: number; - p2_postion: number; + p1_position: number; + p2_position: number; honorPoint: number; - regularPoint: number; + point: number; completedTime: number; matchId: number; From 4194db36051a8fbc5bf6bd677bf203027a0706ee Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 11:44:10 +0000 Subject: [PATCH 11/13] rank the team based on honorPoint-point-time --- .../src/components/ui/Matchdetails/List.tsx | 90 +++++++++++-------- 1 file changed, 52 insertions(+), 38 deletions(-) diff --git a/client/src/components/ui/Matchdetails/List.tsx b/client/src/components/ui/Matchdetails/List.tsx index 3b74d9d..7d6b5fb 100644 --- a/client/src/components/ui/Matchdetails/List.tsx +++ b/client/src/components/ui/Matchdetails/List.tsx @@ -56,8 +56,8 @@ export default function MatchDetailsList() { honorPoint: item.honor_point, point: item.point, completedTime: item.completed_time_second, - p1_postion: item.p1_position, - p2_postion: item.p2_position, + p1_position: item.p1_position, + p2_position: item.p2_position, whitePins: item.white_pins, penaltyPins: item.penalty_pins, yellowCard: item.yellow_cards, @@ -157,43 +157,57 @@ export default function MatchDetailsList() {

) : (
- {sortedMatchData.map((match, idx) => ( -
-

- {match[0]?.matchName ?? `Match ${idx + 1}`} -

- - - - {header.map((col, i) => ( - - ))} - - - - {match.map((row, index) => ( - - - - - - - - - - - + {sortedMatchData.map((match, idx) => { + // Sort teams within each match + const sortedTeams = [...match].sort((a, b) => { + // Sort by: honor point DESC, point DESC, completed time ASC + if (b.honorPoint !== a.honorPoint) { + return b.honorPoint - a.honorPoint; + } else if (b.point !== a.point) { + return b.point - a.point; + } else { + return a.completedTime - b.completedTime; + } + }); + + return ( +
+

+ {match[0]?.matchName ?? `Match ${idx + 1}`} +

+
- {col} -
{row.teamName}{row.whitePins}{row.penaltyPins}{row.yellowCard}{row.redCard}{row.p1_postion}{row.p2_postion}{row.honorPoint}{row.point}{row.completedTime}
+ + + {header.map((col, i) => ( + + ))} - ))} - -
+ {col} +
-
- ))} + + + {sortedTeams.map((row, index) => ( + + {row.teamName} + {row.whitePins} + {row.penaltyPins} + {row.yellowCard} + {row.redCard} + {row.p1_position} + {row.p2_position} + {row.honorPoint} + {row.point} + {row.completedTime} + + ))} + + +
+ ); + })} ) ) : ( From 1f7d4e94e4e7f0cea2acebbef24ed0ff8781e3f6 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 12:05:38 +0000 Subject: [PATCH 12/13] fixing font size for leaderboard and matchdetails --- client/src/components/ui/Leaderboard/List.tsx | 4 ++-- client/src/components/ui/Matchdetails/List.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/ui/Leaderboard/List.tsx b/client/src/components/ui/Leaderboard/List.tsx index 4504486..2fc2222 100644 --- a/client/src/components/ui/Leaderboard/List.tsx +++ b/client/src/components/ui/Leaderboard/List.tsx @@ -100,8 +100,8 @@ export default function List() { ) : ( - - + + diff --git a/client/src/components/ui/Matchdetails/List.tsx b/client/src/components/ui/Matchdetails/List.tsx index 7d6b5fb..1dd7524 100644 --- a/client/src/components/ui/Matchdetails/List.tsx +++ b/client/src/components/ui/Matchdetails/List.tsx @@ -177,7 +177,7 @@ export default function MatchDetailsList() {
Group Rank
Group Rank Team Honour Points Regular Points
- + {header.map((col, i) => (
{col} From b115e9166163f8c6ea6463fece25a04e3948d1a7 Mon Sep 17 00:00:00 2001 From: samueltl21 Date: Tue, 22 Jul 2025 14:06:23 +0000 Subject: [PATCH 13/13] delete matchdetails page --- client/src/pages/matchdetails.tsx | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 client/src/pages/matchdetails.tsx diff --git a/client/src/pages/matchdetails.tsx b/client/src/pages/matchdetails.tsx deleted file mode 100644 index 3c51cea..0000000 --- a/client/src/pages/matchdetails.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Image from "next/image"; - -import MatchDetailsList from "@/components/ui/Matchdetails/List"; - -export default function MatchDetailsPage() { - return ( -
-
-

Match Details

-
- Progress Bar -
-
-
- -
-
- ); -}