diff --git a/services/frontend/src/pages/CompetitionSession/components/CompetitionHeader/index.tsx b/services/frontend/src/pages/CompetitionSession/components/CompetitionHeader/index.tsx index 849b020..98e6c63 100644 --- a/services/frontend/src/pages/CompetitionSession/components/CompetitionHeader/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/components/CompetitionHeader/index.tsx @@ -2,13 +2,14 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { Task } from '@/shared/types/task'; import { ArrowLeft } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; interface CompetitionHeaderProps { title: string; tasks: Task[]; competitionId: string; setAnswer: (value: string) => void; - setSelectedFile: (file: File | null) => void; // заглушка + setSelectedFile: (file: File | null) => void; } const CompetitionHeader: React.FC = ({ @@ -18,11 +19,15 @@ const CompetitionHeader: React.FC = ({ setAnswer, setSelectedFile }) => { + const navigate = useNavigate(); - const handleTaskSelect = () => { - setAnswer("") - setSelectedFile(null) - console.log("STOP IT") + const handleTaskSelect = (event: React.MouseEvent, taskId: string) => { + event.preventDefault(); + setAnswer(""); + setSelectedFile(null); + console.log("link check"); + + navigate(`/competition/${competitionId}/tasks/${taskId}`); } return ( @@ -32,7 +37,6 @@ const CompetitionHeader: React.FC = ({ @@ -53,6 +57,7 @@ const CompetitionHeader: React.FC = ({ rounded-lg px-3 py-1.5 font-medium text-sm font-hse-sans cursor-pointer transition-all hover:brightness-95 flex-shrink-0 `} + onClick={(e) => handleTaskSelect(e, task.id)} > {task.in_competition_position} diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx index 9deb45a..3eee14c 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx @@ -1,168 +1,163 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { useParams } from 'react-router-dom'; -import { Task, TaskType, Solution } from '@/shared/types/task'; -import { useQuery } from '@tanstack/react-query'; -import { getTaskSolutionHistory } from '@/shared/api/session'; -import SolutionStatus from './components/SolutionStatus'; -import InputSolution from './components/InputSolution'; -import FileSolution from './components/FileSolution'; -import CodeSolution from './components/CodeSolution'; -import ActionButtons from './components/ActionButtons'; -import SolutionHistorySheet from './components/SolutionHistorySheet'; + import React, { useState, useRef, useEffect } from 'react'; + import { useParams } from 'react-router-dom'; + import { Task, TaskType, Solution } from '@/shared/types/task'; + import { useQuery } from '@tanstack/react-query'; + import { getTaskSolutionHistory } from '@/shared/api/session'; + import SolutionStatus from './components/SolutionStatus'; + import InputSolution from './components/InputSolution'; + import FileSolution from './components/FileSolution'; + import CodeSolution from './components/CodeSolution'; + import ActionButtons from './components/ActionButtons'; + import SolutionHistorySheet from './components/SolutionHistorySheet'; -interface TaskSolutionProps { - task: Task; - answer: string; - setAnswer: (value: string) => void; - selectedFile: File | null; - setSelectedFile: (file: File | null) => void; - onSubmit: () => void; -} + interface TaskSolutionProps { + task: Task; + answer: string; + setAnswer: (value: string) => void; + selectedFile: File | null; + setSelectedFile: (file: File | null) => void; + onSubmit: () => void; + } -const TaskSolution: React.FC = ({ - task, - answer, - setAnswer, - selectedFile, - setSelectedFile, - onSubmit, -}) => { - const fileInputRef = useRef(null); - const [isHistoryOpen, setIsHistoryOpen] = useState(false); - const [selectedSolutionUrl, setSelectedSolutionUrl] = useState(null); - const [displayedSolution, setDisplayedSolution] = useState(null); - const { id: competitionId } = useParams<{ id: string }>(); - const prevTaskIdRef = useRef(null); + const TaskSolution: React.FC = ({ + task, + answer, + setAnswer, + selectedFile, + setSelectedFile, + onSubmit, + }) => { + const fileInputRef = useRef(null); + const [isHistoryOpen, setIsHistoryOpen] = useState(false); + const [selectedSolutionUrl, setSelectedSolutionUrl] = useState(null); + const [displayedSolution, setDisplayedSolution] = useState(null); + const { id: competitionId } = useParams<{ id: string }>(); + const prevTaskIdRef = useRef(null); - const solutionsQuery = useQuery({ - queryKey: ['solutionHistory', competitionId, task.id], - queryFn: () => getTaskSolutionHistory(competitionId || '', task.id), - enabled: !!(competitionId && task.id), - }); + const solutionsQuery = useQuery({ + queryKey: ['solutionHistory', competitionId, task.id], + queryFn: () => getTaskSolutionHistory(competitionId || '', task.id), + enabled: !!(competitionId && task.id), + }); - const solutionHistory = solutionsQuery.data || []; + const solutionHistory = solutionsQuery.data || []; - useEffect(() => { - if (solutionHistory.length > 0 && !displayedSolution) { - const latestSolution = solutionHistory[solutionHistory.length - 1]; - setDisplayedSolution(latestSolution); - } - }, [solutionHistory, displayedSolution]); - - useEffect(() => { - if (prevTaskIdRef.current !== task.id) { - setDisplayedSolution(null); - setSelectedSolutionUrl(null); - - // If solutions are already loaded for the new task, set the latest one - if (solutionHistory.length > 0) { + useEffect(() => { + if (solutionHistory.length > 0 && !displayedSolution) { const latestSolution = solutionHistory[solutionHistory.length - 1]; setDisplayedSolution(latestSolution); } - - prevTaskIdRef.current = task.id; - } - }, [task.id, solutionHistory]); + }, [solutionHistory, displayedSolution]); - // Check if a new solution was submitted (latest solution ID changed) - useEffect(() => { - if (solutionHistory.length > 0 && displayedSolution) { - const latestSolution = solutionHistory[solutionHistory.length - 1]; - - // If the latest solution ID is different from the displayed one, - // a new solution was submitted - update to show the latest - if (latestSolution.id !== displayedSolution.id) { - setDisplayedSolution(latestSolution); - } - } - }, [solutionHistory, displayedSolution]); - - // Load solution content when the displayed solution changes - useEffect(() => { - const loadSolutionContent = async () => { - if (!displayedSolution || !displayedSolution.content) return; - - try { - if (task.type === TaskType.FILE) { - setSelectedFile(null); - setSelectedSolutionUrl(displayedSolution.content); - } else { - const response = await fetch(displayedSolution.content); - if (!response.ok) { - throw new Error(`Failed to fetch solution content: ${response.status}`); - } - const text = await response.text(); - setAnswer(text); + useEffect(() => { + if (prevTaskIdRef.current !== task.id) { + setDisplayedSolution(null); + setSelectedSolutionUrl(null); + + if (solutionHistory.length > 0) { + const latestSolution = solutionHistory[solutionHistory.length - 1]; + setDisplayedSolution(latestSolution); } - } catch (error) { - console.error('Error loading solution content:', error); + + prevTaskIdRef.current = task.id; } + }, [task.id, solutionHistory]); + + useEffect(() => { + if (solutionHistory.length > 0 && displayedSolution) { + const latestSolution = solutionHistory[solutionHistory.length - 1]; + + if (latestSolution.id !== displayedSolution.id) { + setDisplayedSolution(latestSolution); + } + } + }, [solutionHistory, displayedSolution]); + + // Load solution content when the displayed solution changes + useEffect(() => { + const loadSolutionContent = async () => { + if (!displayedSolution || !displayedSolution.content) return; + + try { + if (task.type === TaskType.FILE) { + setSelectedFile(null); + setSelectedSolutionUrl(displayedSolution.content); + } else { + const response = await fetch(displayedSolution.content); + if (!response.ok) { + throw new Error(`Failed to fetch solution content: ${response.status}`); + } + const text = await response.text(); + setAnswer(text); + } + } catch (error) { + console.error('Error loading solution content:', error); + } + }; + + loadSolutionContent(); + }, [displayedSolution, task.type, setAnswer, setSelectedFile]); + + const handleOpenHistory = () => { + setIsHistoryOpen(true); }; - loadSolutionContent(); - }, [displayedSolution, task.type, setAnswer, setSelectedFile]); + const handleSolutionSelect = (solution: Solution) => { + setDisplayedSolution(solution); + }; - const handleOpenHistory = () => { - setIsHistoryOpen(true); + const handleClearExistingFile = () => { + setSelectedSolutionUrl(null); + }; + + return ( +
+ {displayedSolution ? ( + + ) : ( +
+ Решение еще не отправлено +
+ )} + + {task.type === TaskType.INPUT && ( + + )} + + {task.type === TaskType.FILE && ( + + )} + + {task.type === TaskType.CODE && ( + + )} + + + + +
+ ); }; - const handleSolutionSelect = (solution: Solution) => { - setDisplayedSolution(solution); - console.log(displayedSolution) - }; - - const handleClearExistingFile = () => { - setSelectedSolutionUrl(null); - }; - - return ( -
- {displayedSolution ? ( - - ) : ( -
- Решение еще не отправлено -
- )} - - {task.type === TaskType.INPUT && ( - - )} - - {task.type === TaskType.FILE && ( - - )} - - {task.type === TaskType.CODE && ( - - )} - - - - -
- ); -}; - -export default TaskSolution; \ No newline at end of file + export default TaskSolution; \ No newline at end of file