import { cn } from '../../../lib/cn'; import { useNavigate } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '../../../redux/hooks'; import { useQuery } from '../../../hooks/useQuery'; import { ReverseButton } from '../../../components/button/ReverseButton'; import { PrimaryButton } from '../../../components/button/PrimaryButton'; import { toastWarning } from '../../../lib/toastNotification'; import { useEffect, useState } from 'react'; import { addOrUpdateContestMember, fetchParticipatingContests, } from '../../../redux/slices/contests'; export interface PastContestItemProps { name: string; contestId: number; scheduleType: 'AlwaysOpen' | 'FixedWindow' | 'RollingWindow'; startsAt: string; endsAt: string; attemptDurationMinutes: number; type: 'first' | 'second'; } 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} мин`; } } type Role = 'None' | 'Participant' | 'Organizer'; const PastContestItem: React.FC = ({ name, contestId, scheduleType, startsAt, endsAt, attemptDurationMinutes, type, }) => { const navigate = useNavigate(); const dispatch = useAppDispatch(); const [role, setRole] = useState('None'); const myname = useAppSelector((state) => state.auth.username); const userId = useAppSelector((state) => state.auth.id); const query = useQuery(); const username = query.get('username') ?? myname ?? ''; const { contests: myContests } = useAppSelector( (state) => state.contests.fetchMyContests, ); const { contests: participantContests } = useAppSelector( (state) => state.contests.fetchParticipating, ); const nameFilter = useAppSelector( (state) => state.store.contests.filterName, ); const highlightZ = (name: string, filter: string) => { if (!filter) return name; const s = filter.toLowerCase(); const t = name.toLowerCase(); const n = t.length; const m = s.length; const mark = Array(n).fill(false); // Проходимся с конца и ставим отметки for (let i = n - 1; i >= 0; i--) { if (i + m <= n && t.slice(i, i + m) === s) { for (let j = i; j < i + m; j++) { if (mark[j]) break; mark[j] = true; } } } // === Формируем единые жёлтые блоки === const result: any[] = []; let i = 0; while (i < n) { if (!mark[i]) { // обычный символ result.push(name[i]); i++; } else { // начинаем жёлтый блок let j = i; while (j < n && mark[j]) j++; const chunk = name.slice(i, j); result.push( {chunk} , ); i = j; } } return result; }; 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 (role == 'None') { toastWarning('Нужно зарегистрироваться на контест'); return; } const params = new URLSearchParams({ back: '/home/contests', }); navigate(`/contest/${contestId}?${params}`); }} >
{highlightZ(name, nameFilter)}
{username}
{scheduleType == 'AlwaysOpen' ? (
Всегда открыт
) : (
{formatDate(startsAt)}
-
{formatDate(endsAt)}
)}
{formatDurationTime(attemptDurationMinutes)}
{scheduleType == 'AlwaysOpen' ? 'Открыт' : 'Завершен'}
{userId && (
{role == 'Organizer' || role == 'Participant' ? ( { const params = new URLSearchParams({ back: '/home/contests', }); navigate(`/contest/${contestId}?${params}`); }} text="Войти" /> ) : ( { dispatch( addOrUpdateContestMember({ contestId: contestId, member: { userId: Number(userId), role: 'Participant', }, }), ) .unwrap() .then(() => dispatch( fetchParticipatingContests({}), ), ); }} text="Регистрация" /> )}
)}
); }; export default PastContestItem;