contests
This commit is contained in:
@@ -1,19 +1,66 @@
|
|||||||
import { useEffect } from 'react';
|
import { FC, useEffect } from "react";
|
||||||
import { useAppDispatch } from '../../../../redux/hooks';
|
import { useAppDispatch } from "../../../../redux/hooks";
|
||||||
import { setMenuActiveProfilePage } from '../../../../redux/slices/store';
|
import { setMenuActiveProfilePage } from "../../../../redux/slices/store";
|
||||||
|
import { cn } from "../../../../lib/cn";
|
||||||
|
|
||||||
|
|
||||||
|
interface ItemProps {
|
||||||
|
count: number;
|
||||||
|
totalCount: number;
|
||||||
|
title: string;
|
||||||
|
color?: "default" | "red" | "green" | "orange";
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item: FC<ItemProps> = ({count, totalCount, title, color = "default"}) => {
|
||||||
|
|
||||||
|
return <div className={cn("flex flex-row rounded-full bg-liquid-lighter px-[16px] py-[8px] gap-[10px] text-[14px]",
|
||||||
|
color == "default" && "text-liquid-light",
|
||||||
|
color == "red" && "text-liquid-red",
|
||||||
|
color == "green" && "text-liquid-green",
|
||||||
|
color == "orange" && "text-liquid-orange",
|
||||||
|
)}>
|
||||||
|
<div>{count}/{totalCount}</div>
|
||||||
|
<div>{title}</div>
|
||||||
|
</div>
|
||||||
|
};
|
||||||
|
|
||||||
const MissionsBlock = () => {
|
const MissionsBlock = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setMenuActiveProfilePage('missions'));
|
dispatch(setMenuActiveProfilePage("missions"));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full relative flex items-center justify-center text-[60px] font-bold">
|
<div className="h-full w-full relative overflow-y-scroll medium-scrollbar">
|
||||||
Пока пусто :(
|
<div className="w-full flex flex-col">
|
||||||
|
<div className="p-[20px] flex flex-col gap-[20px]">
|
||||||
|
<div className="text-[24px] font-bold text-liquid-white">Решенные задачи</div>
|
||||||
|
<div className="flex flex-row justify-between items-start">
|
||||||
|
|
||||||
|
<div className="flex gap-[10px]">
|
||||||
|
<Item count={14} totalCount={123} title="Задачи"/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-[20px]">
|
||||||
|
<Item count={14} totalCount={123} title="Easy" color="green"/>
|
||||||
|
<Item count={14} totalCount={123} title="Medium" color="orange"/>
|
||||||
|
<Item count={14} totalCount={123} title="Hard" color="red"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="text-[24px] font-bold text-liquid-white">Компетенции</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-[10px]">
|
||||||
|
<Item count={14} totalCount={123} title="Массивы"/>
|
||||||
|
<Item count={14} totalCount={123} title="Списки"/>
|
||||||
|
<Item count={14} totalCount={123} title="Стэк"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
<div>Недавиние задачи</div>
|
||||||
|
<div>Мои задачи</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MissionsBlock;
|
export default MissionsBlock;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const Contest = () => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const contest = useAppSelector((state) => state.contests.fetchContestById.contest);
|
const contest = useAppSelector((state) => state.contests.fetchContestById.contest);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setMenuActivePage('contest'));
|
dispatch(setMenuActivePage('contest'));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -33,17 +34,16 @@ const Contest = () => {
|
|||||||
}, [contestIdNumber]);
|
}, [contestIdNumber]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='w-full h-full'>
|
||||||
<PrimaryButton onClick={() => {navigate(`/contest/${contestIdNumber}/submissions`)}} text='Мои посылки' />
|
|
||||||
<Routes>
|
<Routes>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="submissions"
|
path="submissions"
|
||||||
element={<Submissions contestId={contestIdNumber} />}
|
element={<Submissions contest={contest}/>}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="*"
|
path="*"
|
||||||
element={<ContestMissions contest={contest} />}
|
element={<ContestMissions contest={contest}/>}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export interface MissionItemProps {
|
|||||||
timeLimit?: number;
|
timeLimit?: number;
|
||||||
memoryLimit?: number;
|
memoryLimit?: number;
|
||||||
type?: 'first' | 'second';
|
type?: 'first' | 'second';
|
||||||
status?: 'empty' | 'success' | 'error';
|
status?: 'success' | 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatMilliseconds(ms: number): string {
|
export function formatMilliseconds(ms: number): string {
|
||||||
|
|||||||
@@ -1,45 +1,124 @@
|
|||||||
import { FC } from 'react';
|
import { FC, useEffect } from "react";
|
||||||
import MissionItem from './MissionItem';
|
import MissionItem from "./MissionItem";
|
||||||
import { Contest } from '../../../redux/slices/contests';
|
import {
|
||||||
|
Contest,
|
||||||
|
fetchMySubmissions,
|
||||||
|
setContestStatus,
|
||||||
|
} from "../../../redux/slices/contests";
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../../redux/hooks";
|
||||||
|
import { PrimaryButton } from "../../../components/button/PrimaryButton";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { arrowLeft } from "../../../assets/icons/header";
|
||||||
|
|
||||||
export interface Article {
|
export interface Article {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContestMissionsProps {
|
interface ContestMissionsProps {
|
||||||
contest?: Contest;
|
contest?: Contest;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContestMissions: FC<ContestMissionsProps> = ({ contest }) => {
|
const ContestMissions: FC<ContestMissionsProps> = ({ contest }) => {
|
||||||
if (!contest) {
|
const navigate = useNavigate();
|
||||||
return <></>;
|
const dispatch = useAppDispatch();
|
||||||
}
|
const { submissions, status } = useAppSelector(
|
||||||
|
(state) => state.contests.fetchMySubmissions
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
<div className=" h-screen grid grid-rows-[74px,1fr] p-[20px] gap-[20px]">
|
if (contest) dispatch(fetchMySubmissions(contest.id));
|
||||||
<div className=""></div>
|
}, [contest]);
|
||||||
<div className="h-full min-h-0 overflow-y-scroll medium-scrollbar flex flex-col gap-[20px]">
|
|
||||||
<div className="h-[40px] w-ufll ">
|
useEffect(() => {
|
||||||
{contest?.name} {contest.id}
|
if (status == "successful") {
|
||||||
</div>
|
dispatch(setContestStatus({ key: "fetchMySubmissions", status: "idle" }));
|
||||||
<div className="w-full">
|
}
|
||||||
{(contest.missions ?? []).map((v, i) => (
|
}, [status]);
|
||||||
<MissionItem
|
|
||||||
contestId={contest.id}
|
if (!contest) {
|
||||||
key={i}
|
return <></>;
|
||||||
id={v.id}
|
}
|
||||||
name={v.name}
|
|
||||||
timeLimit={v.timeLimitMilliseconds}
|
const solvedCount = (contest.missions ?? []).filter((mission) =>
|
||||||
memoryLimit={v.memoryLimitBytes}
|
submissions.some(
|
||||||
type={i % 2 ? 'second' : 'first'}
|
(s) =>
|
||||||
/>
|
s.solution.missionId === mission.id &&
|
||||||
))}
|
s.solution.status === "Accepted: All tests passed"
|
||||||
</div>
|
)
|
||||||
</div>
|
).length;
|
||||||
|
|
||||||
|
const totalCount = contest.missions?.length ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className=" h-screen grid grid-rows-[74px,40px,1fr] p-[20px] gap-[20px]">
|
||||||
|
<div className="">
|
||||||
|
<div className="h-[50px] text-[40px] text-liquid-white font-bold">
|
||||||
|
{contest.name}
|
||||||
</div>
|
</div>
|
||||||
);
|
<div className="flex justify-between h-[24px] items-center gap-[10px]">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<img
|
||||||
|
src={arrowLeft}
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/home/contests`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="text-liquid-light font-bold text-[18px]">
|
||||||
|
Контест #{contest.id}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>{contest.attemptDurationMinutes ?? 0} минут</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="text-liquid-white text-[16px] font-bold">{`${solvedCount}/${totalCount} Решено`}</div>
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/contest/${contest.id}/submissions`);
|
||||||
|
}}
|
||||||
|
text="Мои посылки"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-full min-h-0 overflow-y-scroll medium-scrollbar flex flex-col gap-[20px]">
|
||||||
|
<div className="w-full">
|
||||||
|
{(contest.missions ?? []).map((v, i) => {
|
||||||
|
const missionSubmissions = submissions.filter(
|
||||||
|
(s) => s.solution.missionId === v.id
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasSuccess = missionSubmissions.some(
|
||||||
|
(s) => s.solution.status == "Accepted: All tests passed"
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(missionSubmissions);
|
||||||
|
|
||||||
|
const status = hasSuccess
|
||||||
|
? "success"
|
||||||
|
: missionSubmissions.length > 0
|
||||||
|
? "error"
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MissionItem
|
||||||
|
contestId={contest.id}
|
||||||
|
key={i}
|
||||||
|
id={v.id}
|
||||||
|
name={v.name}
|
||||||
|
timeLimit={v.timeLimitMilliseconds}
|
||||||
|
memoryLimit={v.memoryLimitBytes}
|
||||||
|
status={status}
|
||||||
|
type={i % 2 ? "second" : "first"}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ContestMissions;
|
export default ContestMissions;
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import { cn } from '../../../lib/cn';
|
|||||||
|
|
||||||
export interface SubmissionItemProps {
|
export interface SubmissionItemProps {
|
||||||
id: number;
|
id: number;
|
||||||
|
datetime: string;
|
||||||
|
missionId: number;
|
||||||
language: string;
|
language: string;
|
||||||
time: string;
|
|
||||||
verdict: string;
|
verdict: string;
|
||||||
|
duration: number;
|
||||||
|
memory: number;
|
||||||
type: 'first' | 'second';
|
type: 'first' | 'second';
|
||||||
status?: 'success' | 'wronganswer' | 'timelimit';
|
status?: 'success' | 'wronganswer' | 'timelimit';
|
||||||
}
|
}
|
||||||
@@ -37,20 +40,23 @@ function formatDate(dateString: string): string {
|
|||||||
|
|
||||||
const SubmissionItem: React.FC<SubmissionItemProps> = ({
|
const SubmissionItem: React.FC<SubmissionItemProps> = ({
|
||||||
id,
|
id,
|
||||||
|
datetime,
|
||||||
|
missionId,
|
||||||
language,
|
language,
|
||||||
time,
|
|
||||||
verdict,
|
verdict,
|
||||||
|
duration,
|
||||||
|
memory,
|
||||||
type,
|
type,
|
||||||
status,
|
status
|
||||||
}) => {
|
}) => {
|
||||||
// const navigate = useNavigate();
|
// const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
' w-full relative rounded-[10px] text-liquid-white',
|
' w-full relative rounded-[10px] text-liquid-white text-center text-bold text-[16px] py-[8px]',
|
||||||
type == 'first' ? 'bg-liquid-lighter' : 'bg-liquid-background',
|
type == 'first' ? 'bg-liquid-lighter' : 'bg-liquid-background',
|
||||||
'grid grid-cols-[80px,1fr,1fr,2fr] grid-flow-col gap-[20px] px-[20px] box-border items-center',
|
'grid grid-cols-7 grid-flow-col gap-[20px] px-[20px] box-border items-center',
|
||||||
status == 'wronganswer' &&
|
status == 'wronganswer' &&
|
||||||
'border-l-[11px] border-l-liquid-red pl-[9px]',
|
'border-l-[11px] border-l-liquid-red pl-[9px]',
|
||||||
status == 'timelimit' &&
|
status == 'timelimit' &&
|
||||||
@@ -63,8 +69,9 @@ const SubmissionItem: React.FC<SubmissionItemProps> = ({
|
|||||||
>
|
>
|
||||||
<div className="text-[18px] font-bold">#{id}</div>
|
<div className="text-[18px] font-bold">#{id}</div>
|
||||||
<div className="text-[18px] font-bold text-center">
|
<div className="text-[18px] font-bold text-center">
|
||||||
{formatDate(time)}
|
{formatDate(datetime)}
|
||||||
</div>
|
</div>
|
||||||
|
<div>{missionId} </div>
|
||||||
<div className="text-[18px] font-bold text-center">{language}</div>
|
<div className="text-[18px] font-bold text-center">{language}</div>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -75,6 +82,10 @@ const SubmissionItem: React.FC<SubmissionItemProps> = ({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{verdict}
|
{verdict}
|
||||||
|
</div>
|
||||||
|
<div>{formatMilliseconds(duration)}</div>
|
||||||
|
<div>
|
||||||
|
{formatBytesToMB(memory)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,73 +1,129 @@
|
|||||||
import SubmissionItem from './SubmissionItem';
|
import SubmissionItem from "./SubmissionItem";
|
||||||
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
|
import { useAppDispatch, useAppSelector } from "../../../redux/hooks";
|
||||||
import { FC, useEffect } from 'react';
|
import { FC, useEffect } from "react";
|
||||||
import { fetchMySubmissions, setContestStatus } from '../../../redux/slices/contests';
|
import {
|
||||||
|
Contest,
|
||||||
|
fetchMySubmissions,
|
||||||
|
setContestStatus,
|
||||||
|
} from "../../../redux/slices/contests";
|
||||||
|
import { arrowLeft } from "../../../assets/icons/header";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export interface Mission {
|
export interface Mission {
|
||||||
id: number;
|
id: number;
|
||||||
authorId: number;
|
authorId: number;
|
||||||
name: string;
|
name: string;
|
||||||
difficulty: 'Easy' | 'Medium' | 'Hard';
|
difficulty: "Easy" | "Medium" | "Hard";
|
||||||
tags: string[];
|
tags: string[];
|
||||||
timeLimit: number;
|
timeLimit: number;
|
||||||
memoryLimit: number;
|
memoryLimit: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SubmissionsProps {
|
interface SubmissionsProps {
|
||||||
contestId: number;
|
contest: Contest;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Submissions: FC<SubmissionsProps> = ({ contestId }) => {
|
const Submissions: FC<SubmissionsProps> = ({ contest }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
const {submissions, status} = useAppSelector(
|
|
||||||
(state) => state.contests.fetchMySubmissions
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const { submissions, status } = useAppSelector(
|
||||||
dispatch(fetchMySubmissions(contestId));
|
(state) => state.contests.fetchMySubmissions
|
||||||
}, [contestId]);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status == "successful"){
|
if (contest && contest.id) dispatch(fetchMySubmissions(contest.id));
|
||||||
dispatch(setContestStatus({key:"fetchMySubmissions", status: "idle"}));
|
}, [contest]);
|
||||||
}
|
|
||||||
}, [status])
|
|
||||||
|
|
||||||
const checkStatus = (status: string) => {
|
useEffect(() => {
|
||||||
if (status == 'IncorrectAnswer') return 'wronganswer';
|
if (status == "successful") {
|
||||||
if (status == 'TimeLimitError') return 'timelimit';
|
dispatch(setContestStatus({ key: "fetchMySubmissions", status: "idle" }));
|
||||||
return undefined;
|
}
|
||||||
};
|
}, [status]);
|
||||||
|
|
||||||
return (
|
const checkStatus = (status: string) => {
|
||||||
<div className="h-full w-full box-border overflow-y-scroll overflow-x-hidden thin-scrollbar pr-[10px]">
|
if (status == "IncorrectAnswer") return "wronganswer";
|
||||||
{submissions &&
|
if (status == "TimeLimitError") return "timelimit";
|
||||||
submissions.map((v, i) => (
|
return undefined;
|
||||||
<SubmissionItem
|
};
|
||||||
key={i}
|
|
||||||
id={v.id??0}
|
const solvedCount = (contest.missions ?? []).filter((mission) =>
|
||||||
language={v.solution.language}
|
submissions.some(
|
||||||
time={v.solution.time}
|
(s) =>
|
||||||
verdict={
|
s.solution.missionId === mission.id &&
|
||||||
v.solution.testerMessage?.includes(
|
s.solution.status === "Accepted: All tests passed"
|
||||||
'Compilation failed',
|
)
|
||||||
)
|
).length;
|
||||||
? 'Compilation failed'
|
|
||||||
: v.solution.testerMessage
|
const totalCount = contest.missions?.length ?? 0;
|
||||||
}
|
|
||||||
type={i % 2 ? 'second' : 'first'}
|
return (
|
||||||
status={
|
<div className="h-full w-[calc(100%+250px)] box-border overflow-y-scroll overflow-x-hidden thin-scrollbar p-[20px] flex flex-col gap-[20px]">
|
||||||
v.solution.testerMessage == 'All tests passed'
|
<div className="">
|
||||||
? 'success'
|
<div className="h-[50px] text-[40px] text-liquid-white font-bold">
|
||||||
: checkStatus(v.solution.testerErrorCode)
|
{contest.name}
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
<div className="flex justify-between h-[24px] items-center gap-[10px]">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<img
|
||||||
|
src={arrowLeft}
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/contest/${contest.id}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="text-liquid-light font-bold text-[18px]">
|
||||||
|
Контест #{contest.id}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-liquid-white text-[16px] font-bold">{`${solvedCount}/${totalCount} Решено`}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="grid grid-cols-7 text-center items-center h-[43px] mb-[10px] text-[16px] font-bold text-liquid-white">
|
||||||
|
<div>Посылка</div>
|
||||||
|
<div>Когда</div>
|
||||||
|
<div>Задача</div>
|
||||||
|
<div>Язык</div>
|
||||||
|
<div>Вердикт</div>
|
||||||
|
<div>Время</div>
|
||||||
|
<div>Память</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!submissions || submissions.length == 0 ? (
|
||||||
|
<div className="text-liquid-brightmain text-[16px] font-medium text-center mt-[50px]">Вы еще ничего не отсылали</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{submissions.map((v, i) => (
|
||||||
|
<SubmissionItem
|
||||||
|
key={i}
|
||||||
|
id={v.id ?? 0}
|
||||||
|
datetime={v.solution.time}
|
||||||
|
missionId={v.solution.missionId}
|
||||||
|
language={v.solution.language}
|
||||||
|
verdict={
|
||||||
|
v.solution.testerMessage?.includes("Compilation failed")
|
||||||
|
? "Compilation failed"
|
||||||
|
: v.solution.testerMessage
|
||||||
|
}
|
||||||
|
duration={1000}
|
||||||
|
memory={256 * 1024 * 1024}
|
||||||
|
type={i % 2 ? "second" : "first"}
|
||||||
|
status={
|
||||||
|
v.solution.testerMessage == "All tests passed"
|
||||||
|
? "success"
|
||||||
|
: checkStatus(v.solution.testerErrorCode)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Submissions;
|
export default Submissions;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import SubmissionItem from './SubmissionItem';
|
import SubmissionItem from './SubmissionItem';
|
||||||
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
|
import { useAppSelector } from '../../../redux/hooks';
|
||||||
import { FC, useEffect } from 'react';
|
import { FC } from 'react';
|
||||||
import { fetchMySubmissions } from '../../../redux/slices/contests';
|
|
||||||
|
|
||||||
export interface Mission {
|
export interface Mission {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -21,78 +20,42 @@ interface MissionSubmissionsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MissionSubmissions: FC<MissionSubmissionsProps> = ({ missionId, contestId }) => {
|
const MissionSubmissions: FC<MissionSubmissionsProps> = ({ missionId, contestId }) => {
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const submissions = useAppSelector(
|
const submissions = useAppSelector(
|
||||||
(state) => state.submin.submitsById[missionId],
|
(state) => state.submin.submitsById[missionId] || []
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const {submissions: contestSubmission, status: contestStatus} = useAppSelector((state) => state.contests.fetchMySubmissions);
|
|
||||||
|
|
||||||
useEffect(() => {}, []);
|
|
||||||
|
|
||||||
const checkStatus = (status: string) => {
|
const checkStatus = (status: string) => {
|
||||||
if (status == 'IncorrectAnswer') return 'wronganswer';
|
if (status === 'IncorrectAnswer') return 'wronganswer';
|
||||||
if (status == 'TimeLimitError') return 'timelimit';
|
if (status === 'TimeLimitError') return 'timelimit';
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Если contestId передан, фильтруем по нему, иначе показываем все
|
||||||
|
const filteredSubmissions = contestId
|
||||||
|
? submissions.filter((v) => v.contestId === contestId)
|
||||||
|
: submissions;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (contestId){
|
|
||||||
dispatch(fetchMySubmissions(contestId));
|
|
||||||
}
|
|
||||||
}, [contestId, missionId])
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full box-border overflow-y-scroll overflow-x-hidden thin-scrollbar pr-[10px]">
|
<div className="h-full w-full box-border overflow-y-scroll overflow-x-hidden thin-scrollbar pr-[10px]">
|
||||||
|
{filteredSubmissions.map((v, i) => (
|
||||||
{contestId ?
|
<SubmissionItem
|
||||||
contestSubmission &&
|
key={v.id}
|
||||||
contestSubmission.filter(v => v.solution.missionId == missionId).map((v, i) => (
|
id={v.id}
|
||||||
<SubmissionItem
|
language={v.solution.language}
|
||||||
key={i}
|
time={v.solution.time}
|
||||||
id={v.id}
|
verdict={
|
||||||
language={v.solution.language}
|
v.solution.testerMessage?.includes('Compilation failed')
|
||||||
time={v.solution.time}
|
? 'Compilation failed'
|
||||||
verdict={
|
: v.solution.testerMessage
|
||||||
v.solution.testerMessage?.includes(
|
}
|
||||||
'Compilation failed',
|
type={i % 2 ? 'second' : 'first'}
|
||||||
)
|
status={
|
||||||
? 'Compilation failed'
|
v.solution.testerMessage === 'All tests passed'
|
||||||
: v.solution.testerMessage
|
? 'success'
|
||||||
}
|
: checkStatus(v.solution.testerErrorCode)
|
||||||
type={i % 2 ? 'second' : 'first'}
|
}
|
||||||
status={
|
/>
|
||||||
v.solution.testerMessage == 'All tests passed'
|
))}
|
||||||
? 'success'
|
|
||||||
: checkStatus(v.solution.testerErrorCode)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
:
|
|
||||||
submissions &&
|
|
||||||
submissions.map((v, i) => (
|
|
||||||
<SubmissionItem
|
|
||||||
key={i}
|
|
||||||
id={v.id}
|
|
||||||
language={v.solution.language}
|
|
||||||
time={v.solution.time}
|
|
||||||
verdict={
|
|
||||||
v.solution.testerMessage?.includes(
|
|
||||||
'Compilation failed',
|
|
||||||
)
|
|
||||||
? 'Compilation failed'
|
|
||||||
: v.solution.testerMessage
|
|
||||||
}
|
|
||||||
type={i % 2 ? 'second' : 'first'}
|
|
||||||
status={
|
|
||||||
v.solution.testerMessage == 'All tests passed'
|
|
||||||
? 'success'
|
|
||||||
: checkStatus(v.solution.testerErrorCode)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user