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 (
+
+ );
}
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 = [] }) {
-
-
-
- | Group Rank |
- Team |
- Honour Points |
- Regular Points |
- Mission Time |
- Status |
-
-
-
- {displayData.map((row, index) => (
-
- |
- {row.group_rank}
- |
- {row.team_name} |
- {row.honor_points} |
- {row.regular_points} |
- {row.mission_time} |
-
- {row.qualified ? (
-
- Qualified
-
- ) : (
- Not Qualified
- )}
- |
+ {loading ? (
+ Loading leaderboard...
+ ) : (
+
+
+
+ | Group Rank |
+ Team |
+ Honour Points |
+ Regular Points |
+ Mission Time |
+ Status |
- ))}
-
-
+
+
+ {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) => (
- |
- {row}
- |
+
+ {/* Dropdowns */}
+
+
+
+
+
+
+
+
+
+
+
+ {loading ? (
+ Loading match details...
+ ) : selectedStage ? (
+ matchData.length === 0 ? (
+
+ No match data available.
+
+ ) : (
+
+ {sortedMatchData.map((match, idx) => (
+
+
+ {match[0]?.matchName ?? `Match ${idx + 1}`}
+
+
+
+
+ {header.map((col, i) => (
+ |
+ {col}
+ |
+ ))}
+
+
+
+ {match.map((row, index) => (
+
+ | {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) => (
- |
- {col}
- |
- ))}
-
-
-
- {match.map((row, index) => (
-
- | {row.teamName} |
- {row.whitePins} |
- {row.penaltyPins} |
- {row.yellowCard} |
- {row.redCard} |
- {row.p1_postion} |
- {row.p2_postion} |
- {row.honorPoint} |
- {row.point} |
- {row.completedTime} |
+ {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}`}
+
+
+
+
+ {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() {
) : (
-
- | Group Rank |
+
+ | Group Rank |
Team |
Honour Points |
Regular Points |
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() {
-
+
{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 (
-
- );
-}
|