add contests
This commit is contained in:
160
src/views/home/account/contests/UpcomingContestItem.tsx
Normal file
160
src/views/home/account/contests/UpcomingContestItem.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
import { cn } from '../../../../lib/cn';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAppSelector } from '../../../../redux/hooks';
|
||||
import { useQuery } from '../../../../hooks/useQuery';
|
||||
import { toastWarning } from '../../../../lib/toastNotification';
|
||||
|
||||
export interface UpcoingContestItemProps {
|
||||
name: string;
|
||||
contestId: number;
|
||||
scheduleType: 'AlwaysOpen' | 'FixedWindow' | 'RollingWindow';
|
||||
visibility: string;
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
attemptDurationMinutes: number;
|
||||
role: string;
|
||||
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} мин`;
|
||||
}
|
||||
}
|
||||
|
||||
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<UpcoingContestItemProps> = ({
|
||||
name,
|
||||
contestId,
|
||||
scheduleType,
|
||||
startsAt,
|
||||
endsAt,
|
||||
attemptDurationMinutes,
|
||||
type,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const myname = useAppSelector((state) => state.auth.username);
|
||||
|
||||
const query = useQuery();
|
||||
const username = query.get('username') ?? myname ?? '';
|
||||
|
||||
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();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'w-full box-border relative rounded-[10px] px-[20px] py-[14px] text-liquid-white text-[16px] leading-[20px] cursor-pointer grid grid-cols-[1fr,150px,190px,110px,110px,130px] items-center font-bold border-transparent hover:border-liquid-darkmain border-solid border-[1px] transition-all duration-300',
|
||||
type == 'first'
|
||||
? ' bg-liquid-lighter'
|
||||
: ' bg-liquid-background',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!started) {
|
||||
toastWarning('Контест еще не начался');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
back: '/home/account/contests',
|
||||
});
|
||||
navigate(`/contest/${contestId}?${params}`);
|
||||
}}
|
||||
>
|
||||
<div className="text-left font-bold text-[18px]">{name}</div>
|
||||
<div className="text-center text-liquid-brightmain font-normal flex items-center justify-center">
|
||||
{username}
|
||||
</div>
|
||||
{scheduleType == 'AlwaysOpen' ? (
|
||||
<div className="text-center text-nowrap whitespace-pre-line text-[14px]">
|
||||
Всегда открыт
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-[5px] text-[14px]">
|
||||
<div className="text-center text-nowrap whitespace-pre-line">
|
||||
{formatDate(startsAt)}
|
||||
</div>
|
||||
<div>-</div>
|
||||
<div className="text-center text-nowrap whitespace-pre-line">
|
||||
{formatDate(endsAt)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-center">
|
||||
{formatDurationTime(attemptDurationMinutes)}
|
||||
</div>
|
||||
|
||||
{!started ? (
|
||||
<div className="text-center whitespace-pre-line ">
|
||||
{'До начала\n' + formatWaitTime(waitTime)}
|
||||
</div>
|
||||
) : (
|
||||
!finished && (
|
||||
<div className="text-center whitespace-pre-line ">
|
||||
{'До конца\n' + formatWaitTime(waitTime)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-center text-liquid-brightmain font-normal">
|
||||
{new Date() < new Date(startsAt) ? (
|
||||
<>{'Не начался'}</>
|
||||
) : (
|
||||
<>{scheduleType == 'AlwaysOpen' ? 'Открыт' : 'Идет'}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpcoingContestItem;
|
||||
Reference in New Issue
Block a user