import { cn } from '../../../../lib/cn'; import { useNavigate } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '../../../../redux/hooks'; import { useQuery } from '../../../../hooks/useQuery'; import { toastWarning } from '../../../../lib/toastNotification'; import { useEffect, useState } from 'react'; import { ReverseButton } from '../../../../components/button/ReverseButton'; import { PrimaryButton } from '../../../../components/button/PrimaryButton'; import { addOrUpdateContestMember, fetchParticipatingContests, } from '../../../../redux/slices/contests'; type Role = 'None' | 'Participant' | 'Organizer'; export interface UpcoingContestItemProps { name: string; contestId: number; scheduleType: 'AlwaysOpen' | 'FixedWindow' | 'RollingWindow'; startsAt: string; endsAt: string; attemptDurationMinutes: number; type: 'first' | 'second'; groupId: number; } function formatDate(dateString: string): string { const date = new Date(dateString); const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${day}/${month}/${year}\n${hours}:${minutes}`; } function formatDurationTime(minutes: number): string { const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) { const remainder = days % 10; let suffix = 'дней'; if (remainder === 1 && days !== 11) suffix = 'день'; else if (remainder >= 2 && remainder <= 4 && (days < 10 || days > 20)) suffix = 'дня'; return `${days} ${suffix}`; } else if (hours > 0) { const mins = minutes % 60; return mins > 0 ? `${hours} ч ${mins} мин` : `${hours} ч`; } else { return `${minutes} мин`; } } function formatWaitTime(ms: number): string { const minutes = Math.floor(ms / 60000); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) { const remainder = days % 10; let suffix = 'дней'; if (remainder === 1 && days !== 11) suffix = 'день'; else if (remainder >= 2 && remainder <= 4 && (days < 10 || days > 20)) suffix = 'дня'; return `${days} ${suffix}`; } else if (hours > 0) { const mins = minutes % 60; return mins > 0 ? `${hours} ч ${mins} мин` : `${hours} ч`; } else { return `${minutes} мин`; } } const UpcoingContestItem: React.FC = ({ name, contestId, scheduleType, startsAt, endsAt, attemptDurationMinutes, type, groupId, }) => { const navigate = useNavigate(); const dispatch = useAppDispatch(); const [role, setRole] = useState('None'); const myname = useAppSelector((state) => state.auth.username); const { contests: myContests } = useAppSelector( (state) => state.contests.fetchMyContests, ); const { contests: participantContests } = useAppSelector( (state) => state.contests.fetchParticipating, ); const query = useQuery(); const username = query.get('username') ?? myname ?? ''; const userId = useAppSelector((state) => state.auth.id); const started = new Date(startsAt) <= new Date(); const finished = new Date(endsAt) <= new Date(); const waitTime = !started ? new Date(startsAt).getTime() - new Date().getTime() : new Date(endsAt).getTime() - new Date().getTime(); useEffect(() => { setRole( (() => { if (myContests?.some((c) => c.id === contestId)) { return 'Organizer'; } if (participantContests?.some((c) => c.id === contestId)) { return 'Participant'; } return 'None'; })(), ); }, [myContests, participantContests]); return (
{ if (!started) { toastWarning('Контест еще не начался'); return; } const params = new URLSearchParams({ back: `/group/${groupId}/contests`, }); navigate(`/contest/${contestId}?${params}`); }} >
{name}
{username}
{scheduleType == 'AlwaysOpen' ? (
Всегда открыт
) : (
{formatDate(startsAt)}
-
{formatDate(endsAt)}
)}
{formatDurationTime(attemptDurationMinutes)}
{!started ? (
{'До начала\n' + formatWaitTime(waitTime)}
) : ( !finished && (
{'До конца\n' + formatWaitTime(waitTime)}
) )} {userId && (
{role == 'Organizer' || role == 'Participant' ? ( { const params = new URLSearchParams({ back: `/group/${groupId}/contests`, }); navigate(`/contest/${contestId}?${params}`); }} text="Войти" /> ) : ( { dispatch( addOrUpdateContestMember({ contestId: contestId, member: { userId: Number(userId), role: 'Participant', }, }), ) .unwrap() .then(() => dispatch( fetchParticipatingContests({}), ), ); }} text="Регистрация" /> )}
)}
); }; export default UpcoingContestItem;