mirror of
https://gitlab.com/megazordpobeda/DataRush.git
synced 2026-05-22 23:17:09 +00:00
Merge branch 'master' of gitlab.prodcontest.ru:team-15/project
This commit is contained in:
+92
-19
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { Task } from '@/shared/types/task';
|
import { Task } from '@/shared/types/task';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft, Clock } from 'lucide-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { CompetitionType } from '@/shared/types/competition';
|
||||||
|
|
||||||
interface CompetitionHeaderProps {
|
interface CompetitionHeaderProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -10,6 +10,9 @@ interface CompetitionHeaderProps {
|
|||||||
competitionId: string;
|
competitionId: string;
|
||||||
setAnswer: (value: string) => void;
|
setAnswer: (value: string) => void;
|
||||||
setSelectedFile: (file: File | null) => void;
|
setSelectedFile: (file: File | null) => void;
|
||||||
|
competitionType?: CompetitionType;
|
||||||
|
startDate?: Date;
|
||||||
|
endDate?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CompetitionHeader: React.FC<CompetitionHeaderProps> = ({
|
const CompetitionHeader: React.FC<CompetitionHeaderProps> = ({
|
||||||
@@ -17,33 +20,103 @@ const CompetitionHeader: React.FC<CompetitionHeaderProps> = ({
|
|||||||
tasks,
|
tasks,
|
||||||
competitionId,
|
competitionId,
|
||||||
setAnswer,
|
setAnswer,
|
||||||
setSelectedFile
|
setSelectedFile,
|
||||||
|
competitionType,
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [timeLeft, setTimeLeft] = useState<string>('');
|
||||||
|
|
||||||
const handleTaskSelect = (taskId: string) => {
|
const handleTaskSelect = (taskId: string) => {
|
||||||
setAnswer("");
|
setAnswer("");
|
||||||
setSelectedFile(null);
|
setSelectedFile(null);
|
||||||
console.log("SETTER ERROR")
|
|
||||||
navigate(`/competition/${competitionId}/tasks/${taskId}`);
|
navigate(`/competition/${competitionId}/tasks/${taskId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatDate = (date?: Date) => {
|
||||||
|
if (!date) return '';
|
||||||
|
|
||||||
|
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||||
|
return dateObj.toLocaleString('ru-RU', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!endDate || competitionType !== CompetitionType.COMPETITIVE) return;
|
||||||
|
|
||||||
|
const endDateObj = typeof endDate === 'string' ? new Date(endDate) : endDate;
|
||||||
|
|
||||||
|
const updateTimer = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const diff = endDateObj.getTime() - now.getTime();
|
||||||
|
|
||||||
|
if (diff <= 0) {
|
||||||
|
navigate(`/competition/${competitionId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hours = Math.floor(diff / (1000 * 60 * 60));
|
||||||
|
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||||
|
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
||||||
|
|
||||||
|
setTimeLeft(`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTimer();
|
||||||
|
const timerInterval = setInterval(updateTimer, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(timerInterval);
|
||||||
|
}, [endDate, competitionId, navigate, competitionType]);
|
||||||
|
|
||||||
|
const showTimeSection = competitionType === CompetitionType.COMPETITIVE && (startDate || endDate);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-white shadow-sm sticky top-0 z-30 w-full">
|
<header className="bg-white shadow-sm sticky top-0 z-30 w-full">
|
||||||
<div className="mx-auto max-w-6xl px-4">
|
<div className="mx-auto max-w-6xl px-4">
|
||||||
<div className="flex items-center justify-between py-4">
|
<div className="flex items-center justify-between py-4">
|
||||||
<Link
|
<div>
|
||||||
to={`/competition/${competitionId}`}
|
<Link
|
||||||
className="flex items-center text-gray-600 hover:text-gray-900 transition-colors font-hse-sans text-sm"
|
to={`/competition/${competitionId}`}
|
||||||
>
|
className="flex items-center text-gray-600 hover:text-gray-900 transition-colors font-hse-sans text-sm"
|
||||||
<ArrowLeft className="h-4 w-4 mr-1" />
|
>
|
||||||
</Link>
|
<ArrowLeft className="h-4 w-4 mr-1" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<h1 className="font-hse-sans text-xl font-semibold text-center flex-1">
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<h1 className="font-hse-sans text-xl font-semibold text-center flex-1">
|
{showTimeSection ? (
|
||||||
{title}
|
<div className="flex items-center text-gray-600 font-hse-sans text-sm">
|
||||||
</h1>
|
<div className="flex flex-col items-end">
|
||||||
|
{startDate && (
|
||||||
<div className="w-[70px]"></div>
|
<span className="text-xs text-gray-500">
|
||||||
|
Начало: {formatDate(startDate)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{endDate && (
|
||||||
|
<span className="text-xs text-gray-500">
|
||||||
|
Конец: {formatDate(endDate)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{timeLeft && (
|
||||||
|
<span className="font-medium text-red-600">
|
||||||
|
Осталось: {timeLeft}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-[70px]"></div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-center gap-4 pb-4 overflow-x-auto no-scrollbar">
|
<div className="flex items-center justify-center gap-4 pb-4 overflow-x-auto no-scrollbar">
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { getCompetition } from "@/shared/api/competitions";
|
|||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { TaskType } from "@/shared/types/task";
|
import { TaskType } from "@/shared/types/task";
|
||||||
|
import { CompetitionType } from "@/shared/types/task";
|
||||||
|
|
||||||
const CompetitionSession = () => {
|
const CompetitionSession = () => {
|
||||||
const { id, taskId } = useParams<{ id: string; taskId?: string }>();
|
const { id, taskId } = useParams<{ id: string; taskId?: string }>();
|
||||||
@@ -97,6 +98,9 @@ const CompetitionSession = () => {
|
|||||||
competitionId={competitionId}
|
competitionId={competitionId}
|
||||||
setAnswer={setAnswer}
|
setAnswer={setAnswer}
|
||||||
setSelectedFile={setSelectedFile}
|
setSelectedFile={setSelectedFile}
|
||||||
|
competitionType={competition?.type}
|
||||||
|
startDate={competition?.start_date}
|
||||||
|
endDate={competition?.end_date}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<main className="flex-1 bg-[#F8F8F8] pb-8">
|
<main className="flex-1 bg-[#F8F8F8] pb-8">
|
||||||
@@ -120,6 +124,7 @@ const CompetitionSession = () => {
|
|||||||
selectedFile={selectedFile}
|
selectedFile={selectedFile}
|
||||||
setSelectedFile={setSelectedFile}
|
setSelectedFile={setSelectedFile}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
isSubmitting={submitMutation.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -82,26 +82,31 @@ const TaskSolution: React.FC<TaskSolutionProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSolutionContent = async () => {
|
const loadSolutionContent = async () => {
|
||||||
if (!displayedSolution || !displayedSolution.content) return;
|
if (!displayedSolution || !displayedSolution.content) return;
|
||||||
|
console.log(displayedSolution, solutionHistory, "CHECK")
|
||||||
try {
|
try {
|
||||||
if (task.type === TaskType.FILE) {
|
if (task.type === TaskType.FILE) {
|
||||||
|
setAnswer("");
|
||||||
setSelectedFile(null);
|
setSelectedFile(null);
|
||||||
setSelectedSolutionUrl(displayedSolution.content);
|
setSelectedSolutionUrl(displayedSolution.content);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
|
setSelectedFile(null);
|
||||||
|
setSelectedSolutionUrl(null);
|
||||||
const response = await fetch(displayedSolution.content);
|
const response = await fetch(displayedSolution.content);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch solution content: ${response.status}`);
|
throw new Error(`Failed to fetch solution content: ${response.status}`);
|
||||||
}
|
}
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
|
|
||||||
setAnswer(text);
|
setAnswer(text);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading solution content:', error);
|
console.error('Error loading solution content:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadSolutionContent();
|
loadSolutionContent();
|
||||||
}, [displayedSolution, task.type, setAnswer, setSelectedFile]);
|
}, [displayedSolution, setAnswer, setSelectedFile]);
|
||||||
|
|
||||||
const handleOpenHistory = () => {
|
const handleOpenHistory = () => {
|
||||||
setIsHistoryOpen(true);
|
setIsHistoryOpen(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user