add group chat

This commit is contained in:
Виталий Лавшонок
2025-11-23 10:30:31 +03:00
parent abb7301c16
commit 390f1f52c8
28 changed files with 414 additions and 217 deletions

View File

@@ -1,124 +1,126 @@
import { FC, useEffect } from "react";
import MissionItem from "./MissionItem";
import { FC, useEffect } from 'react';
import MissionItem from './MissionItem';
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";
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 {
id: number;
name: string;
tags: string[];
id: number;
name: string;
tags: string[];
}
interface ContestMissionsProps {
contest?: Contest;
contest?: Contest;
}
const ContestMissions: FC<ContestMissionsProps> = ({ contest }) => {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const { submissions, status } = useAppSelector(
(state) => state.contests.fetchMySubmissions
);
const navigate = useNavigate();
const dispatch = useAppDispatch();
const { submissions, status } = useAppSelector(
(state) => state.contests.fetchMySubmissions,
);
useEffect(() => {
if (contest) dispatch(fetchMySubmissions(contest.id));
}, [contest]);
useEffect(() => {
if (contest) dispatch(fetchMySubmissions(contest.id));
}, [contest]);
useEffect(() => {
if (status == "successful") {
dispatch(setContestStatus({ key: "fetchMySubmissions", status: "idle" }));
useEffect(() => {
if (status == 'successful') {
dispatch(
setContestStatus({ key: 'fetchMySubmissions', status: 'idle' }),
);
}
}, [status]);
if (!contest) {
return <></>;
}
}, [status]);
if (!contest) {
return <></>;
}
const solvedCount = (contest.missions ?? []).filter((mission) =>
submissions.some(
(s) =>
s.solution.missionId === mission.id &&
s.solution.status === 'Accepted: All tests passed',
),
).length;
const solvedCount = (contest.missions ?? []).filter((mission) =>
submissions.some(
(s) =>
s.solution.missionId === mission.id &&
s.solution.status === "Accepted: All tests passed"
)
).length;
const totalCount = contest.missions?.length ?? 0;
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 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>
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 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',
);
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>
<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;