From 01e775605e2131e0dbae29efed9fcd1a8528e9fd Mon Sep 17 00:00:00 2001 From: rngsurrounded Date: Sat, 1 Mar 2025 23:48:12 +0900 Subject: [PATCH] feat: history sheet --- .../frontend/src/components/ui/drawer.tsx | 130 +++++++++++++++++ services/frontend/src/components/ui/sheet.tsx | 134 ++++++++++++++++++ .../src/pages/CompetitionSession/index.tsx | 3 +- .../components/ActionButtons/index.tsx | 57 +++++--- .../components/SolutionHistorySheet/index.tsx | 51 +++++++ .../components/SolutionStatus/index.tsx | 37 +++-- .../modules/TaskSolution/index.tsx | 6 +- .../frontend/src/pages/Competitions/index.tsx | 2 +- services/frontend/src/shared/mocks/mocks.ts | 33 ++++- services/frontend/src/shared/types.ts | 9 +- 10 files changed, 428 insertions(+), 34 deletions(-) create mode 100644 services/frontend/src/components/ui/drawer.tsx create mode 100644 services/frontend/src/components/ui/sheet.tsx create mode 100644 services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx diff --git a/services/frontend/src/components/ui/drawer.tsx b/services/frontend/src/components/ui/drawer.tsx new file mode 100644 index 0000000..bec5a19 --- /dev/null +++ b/services/frontend/src/components/ui/drawer.tsx @@ -0,0 +1,130 @@ +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/shared/lib/utils" + +function Drawer({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerClose({ + ...props +}: React.ComponentProps) { + return +} + +function DrawerOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DrawerContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + +
+ {children} + + + ) +} + +function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DrawerTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DrawerDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} diff --git a/services/frontend/src/components/ui/sheet.tsx b/services/frontend/src/components/ui/sheet.tsx new file mode 100644 index 0000000..9adc293 --- /dev/null +++ b/services/frontend/src/components/ui/sheet.tsx @@ -0,0 +1,134 @@ +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { XIcon } from "lucide-react" + +import { cn } from "@/shared/lib/utils" + +function Sheet({ ...props }: React.ComponentProps) { + return +} + +function SheetTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function SheetClose({ + ...props +}: React.ComponentProps) { + return +} + +function SheetPortal({ + ...props +}: React.ComponentProps) { + return +} + +function SheetOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SheetContent({ + className, + children, + side = "right", + ...props +}: React.ComponentProps & { + side?: "top" | "right" | "bottom" | "left" +}) { + return ( + + + + {children} + {/* Removed the default close button that was causing duplication */} + + + ) +} + +function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function SheetTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SheetDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Sheet, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} \ No newline at end of file diff --git a/services/frontend/src/pages/CompetitionSession/index.tsx b/services/frontend/src/pages/CompetitionSession/index.tsx index 7fdafa0..f068751 100644 --- a/services/frontend/src/pages/CompetitionSession/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/index.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { useParams, Navigate } from "react-router-dom"; import { Task } from "@/shared/types"; -import { mockTasks } from "@/shared/mocks/mocks"; +import { mockSolutions, mockTasks } from "@/shared/mocks/mocks"; import CompetitionHeader from "./components/CompetitionHeader"; import TaskContent from "./components/TaskContent"; import TaskSolution from "./modules/TaskSolution"; @@ -40,6 +40,7 @@ const CompetitionSessionPage = () => { void; onSubmit: () => void; + solutionHistory?: Solution[]; } -const ActionButtons: React.FC = ({ onHistoryClick, onSubmit }) => { +const ActionButtons: React.FC = ({ + onHistoryClick, + onSubmit, + solutionHistory = mockSolutions +}) => { + const [isHistoryOpen, setIsHistoryOpen] = useState(false); + + const handleHistoryClick = () => { + setIsHistoryOpen(true); + onHistoryClick(); + }; + return ( -
- - -
+ <> +
+ + +
+ {/* чуть-чуть рак */} + + ); }; diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx new file mode 100644 index 0000000..dcaaa82 --- /dev/null +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionHistorySheet/index.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Sheet, SheetClose, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; +import { Button } from "@/components/ui/button"; +import { X } from "lucide-react"; +import SolutionStatus from '../SolutionStatus'; +import { Solution } from "@/shared/types"; + +interface SolutionHistorySheetProps { + isOpen: boolean; + onOpenChange: (open: boolean) => void; + solutions: Solution[]; +} + +const SolutionHistorySheet: React.FC = ({ + isOpen, + onOpenChange, + solutions +}) => { + return ( + + + +
+ История решений + + + +
+
+ +
+ {solutions.length > 0 ? ( + solutions.map((solution, index) => ( +
+ +
+ )) + ) : ( +
+ У вас пока нет истории решений для этой задачи +
+ )} +
+
+
+ ); +}; + +export default SolutionHistorySheet; \ No newline at end of file diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionStatus/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionStatus/index.tsx index bbde29c..02b5c8d 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionStatus/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/components/SolutionStatus/index.tsx @@ -1,24 +1,41 @@ import React from 'react'; -import { Task } from "@/shared/types"; +import { Solution, TaskStatus } from "@/shared/types"; import { getTaskBgColor, getTaskTextColor } from '@/pages/CompetitionSession/utils/utils'; interface SolutionStatusProps { - task: Task; + solution: Solution; } -const SolutionStatus: React.FC = ({ task }) => { +const SolutionStatus: React.FC = ({ solution }) => { + const getStatusText = (status: TaskStatus, score?: number, maxScore?: number) => { + switch (status) { + case 'checking': + return 'На проверке'; + case 'wrong': + return 'Неверный ответ'; + case 'correct': + return `Зачтено ${maxScore}/${maxScore} баллов`; + case 'partial': + return `Зачтено ${score}/${maxScore} баллов`; + case 'uncleared': + return 'Не решено'; + default: + return ''; + } + }; + return ( -
+
- - Решение 12345 + + Решение {solution.id} - - Зачтено 5/10 баллов + + {getStatusText(solution.status, solution.score, solution.maxScore)}
-
- 1 марта, 08:41 +
+ {solution.date}
); diff --git a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx index c2e9fc3..58db705 100644 --- a/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx +++ b/services/frontend/src/pages/CompetitionSession/modules/TaskSolution/index.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef } from 'react'; -import { Task } from "@/shared/types"; +import { Solution, Task } from "@/shared/types"; import SolutionStatus from './components/SolutionStatus'; import InputSolution from './components/InputSolution'; import FileSolution from './components/FileSolution'; @@ -8,6 +8,7 @@ import ActionButtons from './components/ActionButtons'; interface TaskSolutionProps { task: Task; + solutions: Solution[]; answer: string; setAnswer: (value: string) => void; onSubmit: () => void; @@ -16,6 +17,7 @@ interface TaskSolutionProps { const TaskSolution: React.FC = ({ task, + solutions, answer, setAnswer, onSubmit, @@ -26,7 +28,7 @@ const TaskSolution: React.FC = ({ return (
- + {task.solutionType === 'input' && ( diff --git a/services/frontend/src/pages/Competitions/index.tsx b/services/frontend/src/pages/Competitions/index.tsx index 08c213d..0af28c2 100644 --- a/services/frontend/src/pages/Competitions/index.tsx +++ b/services/frontend/src/pages/Competitions/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; import { Competition, CompetitionStatus } from "@/shared/types"; import { CompetitionGrid } from "./modules/CompetitionGrid"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; diff --git a/services/frontend/src/shared/mocks/mocks.ts b/services/frontend/src/shared/mocks/mocks.ts index 58099e7..d7a543e 100644 --- a/services/frontend/src/shared/mocks/mocks.ts +++ b/services/frontend/src/shared/mocks/mocks.ts @@ -1,4 +1,4 @@ -import { Competition, CompetitionStatus, Task } from "../types"; +import { Competition, CompetitionStatus, Solution, Task } from "../types"; const mockCompetitions: Competition[] = [ { @@ -104,4 +104,33 @@ const mockTasks: Task[] = [ ]; -export { mockCompetitions, mockTasks }; +const mockSolutions: Solution[] = [ + { + id: '1', + status: 'wrong', + date: '1 марта, 08:41', + }, + { + id: '2', + status: 'partial', + score: 5, + maxScore: 10, + date: '28 февраля, 15:22', + }, + { + id: '3', + status: 'correct', + score: 0, + maxScore: 10, + date: '27 февраля, 12:10', + }, + { + id: '4', + status: 'checking', + date: '1 марта, 08:41', + }, + +]; + + +export { mockCompetitions, mockTasks, mockSolutions }; diff --git a/services/frontend/src/shared/types.ts b/services/frontend/src/shared/types.ts index cb4089c..16741c4 100644 --- a/services/frontend/src/shared/types.ts +++ b/services/frontend/src/shared/types.ts @@ -16,6 +16,13 @@ interface Competition { type TaskStatus = "uncleared" | "checking" | "correct" | "partial" | "wrong"; type SolutionType = "input" | "file" | "code"; +interface Solution { + id: string, + status: TaskStatus, + date: string, + score?: number, + maxScore?: number, +} interface Task { id: string; number: string; @@ -24,4 +31,4 @@ interface Task { } export { CompetitionStatus }; -export type { Competition, TaskStatus, Task }; +export type { Solution, Competition, TaskStatus, Task };