From 23ae85020638342324e2e24d42b5da00a671efd5 Mon Sep 17 00:00:00 2001 From: rngsurrounded Date: Tue, 4 Mar 2025 02:24:40 +0900 Subject: [PATCH] results modal --- .../CompetitionResultModal/index.tsx | 88 +++++++++++++++++ .../frontend/src/pages/Competition/index.tsx | 95 ++++++++++++------- .../frontend/src/shared/api/competitions.ts | 2 +- .../frontend/src/shared/types/competition.ts | 1 + 4 files changed, 151 insertions(+), 35 deletions(-) create mode 100644 services/frontend/src/pages/Competition/components/CompetitionResultModal/index.tsx diff --git a/services/frontend/src/pages/Competition/components/CompetitionResultModal/index.tsx b/services/frontend/src/pages/Competition/components/CompetitionResultModal/index.tsx new file mode 100644 index 0000000..565354e --- /dev/null +++ b/services/frontend/src/pages/Competition/components/CompetitionResultModal/index.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { Loader2 } from 'lucide-react'; + +export interface CompetitionResult { + task_name: string; + result: number; + max_points: number +} + +interface CompetitionResultsModalProps { + competitionTitle: string; + results: CompetitionResult[] | undefined; + isLoading: boolean; + error: unknown; + isOpen: boolean; + onOpenChange: (open: boolean) => void; +} + +export const CompetitionResultsModal: React.FC = ({ + competitionTitle, + results, + isLoading, + error, + isOpen, + onOpenChange, +}) => { + const renderResultValue = (result: number, maxPoints: number) => { + if (result === -1) { + return На проверке; + } else if (result === -2) { + return Нет ответа; + } else { + return ( + + Зачтено {result}/{maxPoints} баллов + + ); + } + }; + + return ( + + + + Результаты + + Ваши результаты по соревнованию "{competitionTitle}" + + + +
+ {isLoading ? ( +
+ +
+ ) : error ? ( +
+ Произошла ошибка при загрузке результатов +
+ ) : results && results.length > 0 ? ( + results.map((result, index) => ( +
+
{result.task_name}
+
+ {renderResultValue(result.result, result.max_points)} +
+
+ )) + ) : ( +
+ Нет доступных результатов +
+ )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/services/frontend/src/pages/Competition/index.tsx b/services/frontend/src/pages/Competition/index.tsx index ed1320c..6571c4f 100644 --- a/services/frontend/src/pages/Competition/index.tsx +++ b/services/frontend/src/pages/Competition/index.tsx @@ -1,20 +1,23 @@ +import { useState } from "react"; import { useParams, Link, useNavigate } from "react-router-dom"; import { Button } from "@/components/ui/button"; -import { ArrowLeft, Clock, Trophy, BookOpen, BarChart2, AlertCircle } from "lucide-react"; +import { ArrowLeft, Clock, Trophy, BookOpen, AlertCircle, BarChart2 } from "lucide-react"; import ReactMarkdown from "react-markdown"; import { useQuery, useMutation } from "@tanstack/react-query"; -import { getCompetition, startCompetition } from "@/shared/api/competitions"; +import { getCompetition, startCompetition, getCompetitionResults } from "@/shared/api/competitions"; import { getCompetitionTasks } from "@/shared/api/session"; import { Loading } from "@/components/ui/loading"; import { CompetitionType } from "@/shared/types/competition"; import remarkMath from "remark-math"; import remarkGfm from "remark-gfm"; import rehypeKatex from "rehype-katex"; +import { CompetitionResultsModal } from "./components/CompetitionResultModal"; const CompetitionPage = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const competitionId = id || ""; + const [isResultsModalOpen, setIsResultsModalOpen] = useState(false); const competitionQuery = useQuery({ queryKey: ["competition", competitionId], @@ -22,6 +25,12 @@ const CompetitionPage = () => { enabled: !!competitionId, }); + const resultsQuery = useQuery({ + queryKey: ["competitionResults", competitionId], + queryFn: () => getCompetitionResults(competitionId), + enabled: !!competitionId, + }); + const startMutation = useMutation({ mutationFn: () => startCompetition(competitionId), onSuccess: async () => { @@ -60,10 +69,10 @@ const CompetitionPage = () => { const handleStart = () => { startMutation.mutate(); }; - - const handleViewResults = () => { - console.log("sorryan"); - }; + + const hasResults = resultsQuery.data && + resultsQuery.data.length > 0 && + resultsQuery.data.some(result => result.result !== -2); if (competitionQuery.isLoading) { return ; @@ -75,15 +84,6 @@ const CompetitionPage = () => { const competition = competitionQuery.data; - const isCompetitionEnded = () => { - if (!competition?.end_date) return false; - - const endDate = new Date(competition.end_date); - const now = new Date(); - - return now > endDate; - }; - const isCompetitionNotStarted = () => { if (!competition?.start_date) return false; @@ -92,9 +92,19 @@ const CompetitionPage = () => { return now < startDate; }; + + // Check if competition has ended + const isCompetitionEnded = () => { + if (!competition?.end_date) return false; + + const endDate = new Date(competition.end_date); + const now = new Date(); + + return now > endDate; + }; - const competitionEnded = isCompetitionEnded(); const competitionNotStarted = isCompetitionNotStarted(); + const competitionEnded = isCompetitionEnded(); return (
@@ -133,17 +143,17 @@ const CompetitionPage = () => { )}
- {competitionEnded && competition.type === CompetitionType.COMPETITIVE && ( -
- Завершено -
- )} - {competitionNotStarted && competition.type === CompetitionType.COMPETITIVE && (
Скоро начнется
)} + + {competitionEnded && competition.type === CompetitionType.COMPETITIVE && ( +
+ Завершено +
+ )}

@@ -178,17 +188,8 @@ const CompetitionPage = () => { -
- {competitionEnded && competition.type === CompetitionType.COMPETITIVE ? ( - - ) : competitionNotStarted && competition.type === CompetitionType.COMPETITIVE ? ( +
+ {competitionNotStarted && competition.type === CompetitionType.COMPETITIVE ? ( - ) : ( + ) : !competitionEnded ? ( + ) : null} + + {hasResults && ( + + )} + + {competitionEnded && !hasResults && competition.type === CompetitionType.COMPETITIVE && !resultsQuery.isLoading && ( +
+

Соревнование завершено. Результаты пока не доступны.

+
)}
+ + ); }; diff --git a/services/frontend/src/shared/api/competitions.ts b/services/frontend/src/shared/api/competitions.ts index 2c96f4c..4c46882 100644 --- a/services/frontend/src/shared/api/competitions.ts +++ b/services/frontend/src/shared/api/competitions.ts @@ -14,7 +14,7 @@ export const getCompetition = async (id: string) => { }; export const getCompetitionResults = async (id: string) => { - return await userFetch(`/competitions/${id}/results`); + return await userFetch(`/competitions/${id}/results`); } export const startCompetition = async (competitionId: string) => { diff --git a/services/frontend/src/shared/types/competition.ts b/services/frontend/src/shared/types/competition.ts index cff4cd9..2326419 100644 --- a/services/frontend/src/shared/types/competition.ts +++ b/services/frontend/src/shared/types/competition.ts @@ -28,4 +28,5 @@ export enum CompetitionParticipationType { export interface CompetitionResult { task_name: string; result: number; + max_points: number; } \ No newline at end of file