Skip to content

Commit d29db14

Browse files
committed
Added scores to team page route
1 parent 297ff3d commit d29db14

1 file changed

Lines changed: 280 additions & 1 deletion

File tree

src/app/team/[id]/page.tsx

Lines changed: 280 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,286 @@
11
"use client";
22
import { fetchTeamDetails } from "@/api/fetchTeamDetails";
3-
import { useQuery } from "@tanstack/react-query";
43
import Image from "next/image";
54
import { useParams } from "next/navigation";
65
import loading from "@/assets/images/loading.gif";
76
import Link from "next/link";
7+
import { Button } from "@/components/ui/button";
8+
import { Input } from "@/components/ui/input";
9+
import { Label } from "@/components/ui/label";
10+
import { useState } from "react";
11+
import useToast from "@/lib/toast";
12+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
13+
import { createScore, deleteScore, fetchScores, updateScore } from "@/api/fetchScores";
14+
15+
16+
interface Score {
17+
id: string;
18+
team_id: string;
19+
design: number;
20+
implementation: number;
21+
presentation: number;
22+
innovation: number;
23+
teamwork: number;
24+
comment: string;
25+
round: number;
26+
}
27+
28+
type ApiError = {
29+
message: string;
30+
};
31+
32+
function ScoreSection({ teamId }: { teamId: string }) {
33+
const [design, setDesign] = useState(0);
34+
const [implementation, setImplementation] = useState(0);
35+
const [presentation, setPresentation] = useState(0);
36+
const [innovation, setInnovation] = useState(0);
37+
const [teamwork, setTeamwork] = useState(0);
38+
const [comment, setComment] = useState("");
39+
const [round, setRound] = useState(0);
40+
const [editMode, setEditMode] = useState(false);
41+
const [currentScoreId, setCurrentScoreId] = useState<string | null>(null);
42+
43+
const queryClient = useQueryClient();
44+
const { create } = useToast();
45+
46+
const { data: scores = [], isLoading: scoresLoading, isError: scoresError } = useQuery({
47+
queryKey: ["scores", teamId],
48+
queryFn: () => fetchScores(teamId ?? ""),
49+
enabled: !!teamId,
50+
});
51+
52+
const createScoreMutation = useMutation({
53+
mutationFn: ({ teamId, design, implementation, presentation, innovation, teamwork, comment, round }: {
54+
teamId: string;
55+
design: number;
56+
implementation: number;
57+
presentation: number;
58+
innovation: number;
59+
teamwork: number;
60+
comment: string;
61+
round: number;
62+
}) => createScore({ team_id: teamId, design, implementation, presentation, innovation, teamwork, comment, round }),
63+
onError: (err: ApiError) => {
64+
create(`Error creating score: ${err?.message ?? "unknown error"}`, "error");
65+
},
66+
onSuccess: () => {
67+
void queryClient.invalidateQueries({ queryKey: ["scores", teamId] });
68+
resetForm();
69+
},
70+
});
71+
72+
const deleteScoreMutation = useMutation({
73+
mutationFn: (scoreId: string) => deleteScore(scoreId),
74+
onError: (err: ApiError) => {
75+
create(`Error deleting score: ${err?.message ?? "unknown error"}`, "error");
76+
},
77+
onSuccess: () => {
78+
void queryClient.invalidateQueries({ queryKey: ["scores", teamId] });
79+
setEditMode(false);
80+
resetForm();
81+
},
82+
});
83+
84+
const updateScoreMutation = useMutation({
85+
mutationFn: ({
86+
scoreId,
87+
design,
88+
implementation,
89+
presentation,
90+
innovation,
91+
teamwork,
92+
comment,
93+
round,
94+
}: {
95+
scoreId: string;
96+
design: number;
97+
implementation: number;
98+
presentation: number;
99+
innovation: number;
100+
teamwork: number;
101+
comment: string;
102+
round: number;
103+
}) => updateScore({
104+
scoreId,
105+
design,
106+
implementation,
107+
presentation,
108+
innovation,
109+
teamwork,
110+
comment,
111+
round
112+
}),
113+
onError: (err: ApiError) => {
114+
create(`Error updating score: ${err?.message ?? "unknown error"}`, "error");
115+
},
116+
onSuccess: () => {
117+
void queryClient.invalidateQueries({ queryKey: ["scores", teamId] });
118+
setEditMode(false);
119+
resetForm();
120+
},
121+
});
122+
123+
const resetForm = () => {
124+
setDesign(0);
125+
setImplementation(0);
126+
setPresentation(0);
127+
setInnovation(0);
128+
setTeamwork(0);
129+
setComment("");
130+
setRound(0);
131+
setCurrentScoreId(null);
132+
setEditMode(false);
133+
};
134+
135+
const handleEdit = (score: Score) => {
136+
setCurrentScoreId(score.id);
137+
setDesign(score.design);
138+
setImplementation(score.implementation);
139+
setPresentation(score.presentation);
140+
setInnovation(score.innovation);
141+
setTeamwork(score.teamwork);
142+
setComment(score.comment);
143+
setRound(score.round);
144+
setEditMode(true);
145+
};
146+
147+
const handleSubmit = () => {
148+
if (!teamId) return;
149+
150+
if (editMode && currentScoreId) {
151+
void updateScoreMutation.mutateAsync({
152+
scoreId: currentScoreId,
153+
design,
154+
implementation,
155+
presentation,
156+
innovation,
157+
teamwork,
158+
comment,
159+
round
160+
});
161+
} else {
162+
void createScoreMutation.mutateAsync({
163+
teamId,
164+
design,
165+
implementation,
166+
presentation,
167+
innovation,
168+
teamwork,
169+
comment,
170+
round
171+
});
172+
}
173+
};
174+
175+
const handleDelete = (scoreId: string) => {
176+
if (window.confirm("Are you sure you want to delete this score?")) {
177+
void deleteScoreMutation.mutateAsync(scoreId);
178+
}
179+
};
180+
181+
const calculateTotalScore = (score: Score) => {
182+
return score.design + score.implementation + score.presentation +
183+
score.innovation + score.teamwork;
184+
};
185+
186+
if (scoresLoading) return <div>Loading scores...</div>;
187+
if (scoresError) return <div>Error loading scores</div>;
188+
189+
return (
190+
<div className="mt-8">
191+
<h2 className="text-xl font-bold mb-4">Team Scores</h2>
192+
193+
{((!scores || scores.length === 0) || editMode) && (
194+
<div className="space-y-4 bg-gray-900 p-4 rounded-lg mb-4">
195+
<h3 className="text-lg font-semibold">{editMode ? "Edit Score" : "Add Score"}</h3>
196+
<div className="grid grid-cols-2 gap-4">
197+
<div className="space-y-2">
198+
<Label>Design</Label>
199+
<Input type="number" value={design} onChange={(e) => setDesign(Number(e.target.value))} />
200+
</div>
201+
<div className="space-y-2">
202+
<Label>Implementation</Label>
203+
<Input type="number" value={implementation} onChange={(e) => setImplementation(Number(e.target.value))} />
204+
</div>
205+
<div className="space-y-2">
206+
<Label>Presentation</Label>
207+
<Input type="number" value={presentation} onChange={(e) => setPresentation(Number(e.target.value))} />
208+
</div>
209+
<div className="space-y-2">
210+
<Label>Innovation</Label>
211+
<Input type="number" value={innovation} onChange={(e) => setInnovation(Number(e.target.value))} />
212+
</div>
213+
<div className="space-y-2">
214+
<Label>Teamwork</Label>
215+
<Input type="number" value={teamwork} onChange={(e) => setTeamwork(Number(e.target.value))} />
216+
</div>
217+
<div className="space-y-2">
218+
<Label>Round</Label>
219+
<Input type="number" value={round} onChange={(e) => setRound(Number(e.target.value))} />
220+
</div>
221+
</div>
222+
<div className="space-y-2">
223+
<Label>Comment</Label>
224+
<textarea
225+
value={comment}
226+
onChange={(e) => setComment(e.target.value)}
227+
className="w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 h-24 resize-none"
228+
/>
229+
</div>
230+
<div className="space-x-2">
231+
<Button onClick={handleSubmit}>
232+
{editMode ? "Update Score" : "Add Score"}
233+
</Button>
234+
{editMode && (
235+
<Button variant="secondary" onClick={resetForm}>
236+
Cancel
237+
</Button>
238+
)}
239+
</div>
240+
</div>
241+
)}
242+
243+
{scores.map((score: Score) => (
244+
<div key={score.id} className="bg-gray-800 rounded-lg p-4 space-y-4 mt-4">
245+
<div className="flex justify-between items-center">
246+
<div className="text-xl font-bold">
247+
Total Score: {calculateTotalScore(score)}
248+
</div>
249+
<div className="space-x-2">
250+
<Button
251+
size="sm"
252+
variant="secondary"
253+
onClick={() => handleEdit(score)}
254+
>
255+
Edit
256+
</Button>
257+
<Button
258+
size="sm"
259+
variant="destructive"
260+
onClick={() => handleDelete(score.id)}
261+
>
262+
Delete
263+
</Button>
264+
</div>
265+
</div>
266+
<div className="grid grid-cols-3 gap-4">
267+
<div>Design: {score.design}</div>
268+
<div>Implementation: {score.implementation}</div>
269+
<div>Presentation: {score.presentation}</div>
270+
<div>Innovation: {score.innovation}</div>
271+
<div>Teamwork: {score.teamwork}</div>
272+
<div>Round: {score.round}</div>
273+
</div>
274+
{score.comment && (
275+
<div>
276+
<span className="font-medium">Comment:</span> {score.comment}
277+
</div>
278+
)}
279+
</div>
280+
))}
281+
</div>
282+
);
283+
}
8284

9285
interface Member {
10286
FirstName?: string | undefined;
@@ -80,6 +356,8 @@ export default function TheTeam() {
80356
});
81357
return (
82358
<>
359+
<div className="mx-auto w-[100%] space-y-4">
360+
{id && <ScoreSection teamId={String(id)} />}
83361
<div
84362
className={`${!teamList ? "h-[70vh]" : "h-auto"} mx-auto w-[100%] rounded-md border bg-black p-4 text-white shadow-lg`}
85363
>
@@ -164,6 +442,7 @@ export default function TheTeam() {
164442
</div>
165443
)}
166444
</div>
445+
</div>
167446
</>
168447
);
169448
}

0 commit comments

Comments
 (0)