From 71985fa04fa91c1261d369c119d833589a6a3e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D1=82=D0=B0=D0=BB=D0=B8=D0=B9=20=D0=9B=D0=B0?= =?UTF-8?q?=D0=B2=D1=88=D0=BE=D0=BD=D0=BE=D0=BA?= <114582703+valavshonok@users.noreply.github.com> Date: Tue, 28 Oct 2025 23:15:29 +0300 Subject: [PATCH] add problems --- src/App.tsx | 17 +- src/assets/icons/problems/icon-error.svg | 3 + src/assets/icons/problems/icon-success.svg | 3 + src/assets/icons/problems/index.ts | 4 + src/pages/Home.tsx | 8 +- src/styles/index.css | 22 + src/views/home/menu/Menu.tsx | 6 +- src/views/home/problems/ProblemItem.tsx | 72 +++ src/views/home/problems/Problems.tsx | 503 +++++++++++++++++++++ 9 files changed, 625 insertions(+), 13 deletions(-) create mode 100644 src/assets/icons/problems/icon-error.svg create mode 100644 src/assets/icons/problems/icon-success.svg create mode 100644 src/assets/icons/problems/index.ts create mode 100644 src/views/home/problems/ProblemItem.tsx create mode 100644 src/views/home/problems/Problems.tsx diff --git a/src/App.tsx b/src/App.tsx index fd375fb..55f9dea 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,13 +11,16 @@ import ProblemStatement from "./views/problem/statement/Proble"; function App() { return ( -
- - }/> -
}/> - }/> - }/> - +
+
+ + } /> +
} /> +
} /> + } /> + + + {/* + + diff --git a/src/assets/icons/problems/icon-success.svg b/src/assets/icons/problems/icon-success.svg new file mode 100644 index 0000000..343b6c1 --- /dev/null +++ b/src/assets/icons/problems/icon-success.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/problems/index.ts b/src/assets/icons/problems/index.ts new file mode 100644 index 0000000..30948a7 --- /dev/null +++ b/src/assets/icons/problems/index.ts @@ -0,0 +1,4 @@ +import IconSuccess from "./icon-success.svg" +import IconError from "./icon-error.svg" + +export {IconError, IconSuccess} \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index cb7eb27..004a00a 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -6,6 +6,7 @@ import Menu from "../views/home/menu/Menu"; import { useAppDispatch, useAppSelector } from "../redux/hooks"; import { useEffect } from "react"; import { fetchWhoAmI } from "../redux/slices/auth"; +import Problems from "../views/home/problems/Problems"; const Home = () => { const name = useAppSelector((state) => state.auth.username); @@ -18,15 +19,16 @@ const Home = () => { return ( -
-
+
+
-
+
} /> } /> } /> + } /> diff --git a/src/styles/index.css b/src/styles/index.css index 6356eee..ecfc61b 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -7,6 +7,7 @@ /* outline: 1px solid green; */ } + :root { color-scheme: light dark; width: 100%; @@ -19,6 +20,7 @@ /* font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; */ font-weight: 400; line-height: 1.5; + background-color: var(--color-liquid-background); color: rgba(255, 255, 255, 0.87); } @@ -73,3 +75,23 @@ body { cursor: pointer; } + + + +html { + scrollbar-gutter: stable; + padding-left: 8px; +} +html::-webkit-scrollbar { + width: 8px; /* ширина вертикального */ +} +/* Трек (фон) */ +html::-webkit-scrollbar-track { + background: transparent; +} +/* Ползунок (thumb) */ +html::-webkit-scrollbar-thumb { + background-color: var(--color-liquid-lighter); + border-radius: 1000px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/views/home/menu/Menu.tsx b/src/views/home/menu/Menu.tsx index 0e505a1..5616e2a 100644 --- a/src/views/home/menu/Menu.tsx +++ b/src/views/home/menu/Menu.tsx @@ -6,7 +6,7 @@ import { useAppSelector } from "../../../redux/hooks"; const Menu = () => { const menuItems = [ {text: "Главная", href: "/home", icon: Home, page: "home" }, - {text: "Задачи", href: "/home", icon: Clipboard, page: "clipboard" }, + {text: "Задачи", href: "/home/problems", icon: Clipboard, page: "clipboard" }, {text: "Статьи", href: "/home", icon: Openbook, page: "openbool" }, {text: "Группы", href: "/home", icon: Users, page: "users" }, {text: "Контесты", href: "/home", icon: Cup, page: "cup" }, @@ -15,8 +15,8 @@ const Menu = () => { const activePage = useAppSelector((state) => state.store.menu.activePage); return ( -
- +
+
{menuItems.map((v, i) => ( diff --git a/src/views/home/problems/ProblemItem.tsx b/src/views/home/problems/ProblemItem.tsx new file mode 100644 index 0000000..55ab2c5 --- /dev/null +++ b/src/views/home/problems/ProblemItem.tsx @@ -0,0 +1,72 @@ +import { Logo } from "../../../assets/logos"; +import { Account, Clipboard, Cup, Home, Openbook, Users } from "../../../assets/icons/menu"; +// import MenuItem from "./MenuItem"; +import { cn } from "../../../lib/cn"; +import { IconError, IconSuccess } from "../../../assets/icons/problems"; + +export interface ProblemItemProps { + id: number; + authorId: number; + name: string; + difficulty: "Easy" | "Medium" | "Hard"; + tags: string[]; + timeLimit: number; + memoryLimit: number; + createdAt: string; + updatedAt: string; + type: "first" | "second"; + status: "empty" | "success" | "error"; +} + +export function formatMilliseconds(ms: number): string { + const rounded = Math.round(ms) / 1000; + const formatted = rounded.toString().replace(/\.?0+$/, ''); + return `${formatted} c`; +} + +export function formatBytesToMB(bytes: number): string { + const megabytes = Math.floor(bytes / (1024 * 1024)); + return `${megabytes} МБ`; +} + +const ProblemItem: React.FC = ({ + id, authorId, name, difficulty, tags, timeLimit, memoryLimit, createdAt, updatedAt, type, status +}) => { + console.log(id); + return ( +
+
+ #{id} +
+
+ {name} +
+
+ стандартный ввод/вывод {formatMilliseconds(timeLimit)}, {formatBytesToMB(memoryLimit)} +
+
+ {difficulty} +
+
+ { + status == "error" && + } + { + status == "success" && + } +
+
+ ); +}; + +export default ProblemItem; diff --git a/src/views/home/problems/Problems.tsx b/src/views/home/problems/Problems.tsx new file mode 100644 index 0000000..1523049 --- /dev/null +++ b/src/views/home/problems/Problems.tsx @@ -0,0 +1,503 @@ +import { Logo } from "../../../assets/logos"; +import { Account, Clipboard, Cup, Home, Openbook, Users } from "../../../assets/icons/menu"; +// import MenuItem from "./MenuItem"; +import { useAppSelector } from "../../../redux/hooks"; +import ProblemItem from "./ProblemItem"; +import { SecondaryButton } from "../../../components/button/SecondaryButton"; + + +export interface Problem { + id: number; + authorId: number; + name: string; + difficulty: "Easy" | "Medium" | "Hard"; + tags: string[]; + timeLimit: number; + memoryLimit: number; + createdAt: string; + updatedAt: string; +} + + +const Problems = () => { + + const problems: Problem[] = [ + { + "id": 1, + "authorId": 1, + "name": "Todo List App", + "difficulty": "Easy", + "tags": ["react", "state", "list"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:13.000Z", + "updatedAt": "2025-10-28T13:23:13.000Z" + }, + { + "id": 2, + "authorId": 1, + "name": "Search Filter Component", + "difficulty": "Medium", + "tags": ["filter", "props", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:14.000Z", + "updatedAt": "2025-10-28T13:23:14.000Z" + }, + { + "id": 3, + "authorId": 1, + "name": "User Card List", + "difficulty": "Easy", + "tags": ["components", "props", "array"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:15.000Z", + "updatedAt": "2025-10-28T13:23:15.000Z" + }, + { + "id": 4, + "authorId": 1, + "name": "Theme Switcher", + "difficulty": "Medium", + "tags": ["context", "theme", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:16.000Z", + "updatedAt": "2025-10-28T13:23:16.000Z" + }, + { + "id": 5, + "authorId": 1, + "name": "Debounced Input", + "difficulty": "Hard", + "tags": ["debounce", "hooks", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:17.000Z", + "updatedAt": "2025-10-28T13:23:17.000Z" + }, + { + "id": 6, + "authorId": 1, + "name": "Pagination Component", + "difficulty": "Medium", + "tags": ["pagination", "array", "state"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:18.000Z", + "updatedAt": "2025-10-28T13:23:18.000Z" + }, + { + "id": 7, + "authorId": 1, + "name": "Modal Window", + "difficulty": "Easy", + "tags": ["ui", "portal", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:19.000Z", + "updatedAt": "2025-10-28T13:23:19.000Z" + }, + { + "id": 8, + "authorId": 1, + "name": "Form Validation", + "difficulty": "Hard", + "tags": ["form", "validation", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:20.000Z", + "updatedAt": "2025-10-28T13:23:20.000Z" + }, + { + "id": 9, + "authorId": 1, + "name": "Countdown Timer", + "difficulty": "Medium", + "tags": ["timer", "hooks", "state"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:21.000Z", + "updatedAt": "2025-10-28T13:23:21.000Z" + }, + { + "id": 10, + "authorId": 1, + "name": "Drag And Drop List", + "difficulty": "Hard", + "tags": ["dragdrop", "array", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:22.000Z", + "updatedAt": "2025-10-28T13:23:22.000Z" + }, + { + "id": 11, + "authorId": 1, + "name": "Custom Hook Use Fetch", + "difficulty": "Medium", + "tags": ["hook", "fetch", "async"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:23.000Z", + "updatedAt": "2025-10-28T13:23:23.000Z" + }, + { + "id": 12, + "authorId": 1, + "name": "Infinite Scroll", + "difficulty": "Hard", + "tags": ["scroll", "pagination", "api"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:24.000Z", + "updatedAt": "2025-10-28T13:23:24.000Z" + }, + { + "id": 13, + "authorId": 1, + "name": "Responsive Navbar", + "difficulty": "Easy", + "tags": ["css", "layout", "responsive"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:25.000Z", + "updatedAt": "2025-10-28T13:23:25.000Z" + }, + { + "id": 14, + "authorId": 1, + "name": "Accordion Component", + "difficulty": "Easy", + "tags": ["ui", "state", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:26.000Z", + "updatedAt": "2025-10-28T13:23:26.000Z" + }, + { + "id": 15, + "authorId": 1, + "name": "File Upload Preview", + "difficulty": "Hard", + "tags": ["file", "events", "preview"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:27.000Z", + "updatedAt": "2025-10-28T13:23:27.000Z" + }, + { + "id": 16, + "authorId": 1, + "name": "Dark Mode Toggle", + "difficulty": "Easy", + "tags": ["theme", "context", "localStorage"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:28.000Z", + "updatedAt": "2025-10-28T13:23:28.000Z" + }, + { + "id": 17, + "authorId": 1, + "name": "Realtime Clock", + "difficulty": "Easy", + "tags": ["date", "state", "interval"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:29.000Z", + "updatedAt": "2025-10-28T13:23:29.000Z" + }, + { + "id": 18, + "authorId": 1, + "name": "Chart With Recharts", + "difficulty": "Medium", + "tags": ["chart", "data", "props"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:30.000Z", + "updatedAt": "2025-10-28T13:23:30.000Z" + }, + { + "id": 19, + "authorId": 1, + "name": "Router Navigation", + "difficulty": "Medium", + "tags": ["router", "navigation", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:31.000Z", + "updatedAt": "2025-10-28T13:23:31.000Z" + }, + { + "id": 20, + "authorId": 1, + "name": "Data Table Sortable", + "difficulty": "Hard", + "tags": ["table", "sort", "filter"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:32.000Z", + "updatedAt": "2025-10-28T13:23:32.000Z" + }, + { + "id": 1, + "authorId": 1, + "name": "Todo List App", + "difficulty": "Easy", + "tags": ["react", "state", "list"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:13.000Z", + "updatedAt": "2025-10-28T13:23:13.000Z" + }, + { + "id": 2, + "authorId": 1, + "name": "Search Filter Component", + "difficulty": "Medium", + "tags": ["filter", "props", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:14.000Z", + "updatedAt": "2025-10-28T13:23:14.000Z" + }, + { + "id": 3, + "authorId": 1, + "name": "User Card List", + "difficulty": "Easy", + "tags": ["components", "props", "array"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:15.000Z", + "updatedAt": "2025-10-28T13:23:15.000Z" + }, + { + "id": 4, + "authorId": 1, + "name": "Theme Switcher", + "difficulty": "Medium", + "tags": ["context", "theme", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:16.000Z", + "updatedAt": "2025-10-28T13:23:16.000Z" + }, + { + "id": 5, + "authorId": 1, + "name": "Debounced Input", + "difficulty": "Hard", + "tags": ["debounce", "hooks", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:17.000Z", + "updatedAt": "2025-10-28T13:23:17.000Z" + }, + { + "id": 6, + "authorId": 1, + "name": "Pagination Component", + "difficulty": "Medium", + "tags": ["pagination", "array", "state"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:18.000Z", + "updatedAt": "2025-10-28T13:23:18.000Z" + }, + { + "id": 7, + "authorId": 1, + "name": "Modal Window", + "difficulty": "Easy", + "tags": ["ui", "portal", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:19.000Z", + "updatedAt": "2025-10-28T13:23:19.000Z" + }, + { + "id": 8, + "authorId": 1, + "name": "Form Validation", + "difficulty": "Hard", + "tags": ["form", "validation", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:20.000Z", + "updatedAt": "2025-10-28T13:23:20.000Z" + }, + { + "id": 9, + "authorId": 1, + "name": "Countdown Timer", + "difficulty": "Medium", + "tags": ["timer", "hooks", "state"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:21.000Z", + "updatedAt": "2025-10-28T13:23:21.000Z" + }, + { + "id": 10, + "authorId": 1, + "name": "Drag And Drop List", + "difficulty": "Hard", + "tags": ["dragdrop", "array", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:22.000Z", + "updatedAt": "2025-10-28T13:23:22.000Z" + }, + { + "id": 11, + "authorId": 1, + "name": "Custom Hook Use Fetch", + "difficulty": "Medium", + "tags": ["hook", "fetch", "async"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:23.000Z", + "updatedAt": "2025-10-28T13:23:23.000Z" + }, + { + "id": 12, + "authorId": 1, + "name": "Infinite Scroll", + "difficulty": "Hard", + "tags": ["scroll", "pagination", "api"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:24.000Z", + "updatedAt": "2025-10-28T13:23:24.000Z" + }, + { + "id": 13, + "authorId": 1, + "name": "Responsive Navbar", + "difficulty": "Easy", + "tags": ["css", "layout", "responsive"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:25.000Z", + "updatedAt": "2025-10-28T13:23:25.000Z" + }, + { + "id": 14, + "authorId": 1, + "name": "Accordion Component", + "difficulty": "Easy", + "tags": ["ui", "state", "events"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:26.000Z", + "updatedAt": "2025-10-28T13:23:26.000Z" + }, + { + "id": 15, + "authorId": 1, + "name": "File Upload Preview", + "difficulty": "Hard", + "tags": ["file", "events", "preview"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:27.000Z", + "updatedAt": "2025-10-28T13:23:27.000Z" + }, + { + "id": 16, + "authorId": 1, + "name": "Dark Mode Toggle", + "difficulty": "Easy", + "tags": ["theme", "context", "localStorage"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:28.000Z", + "updatedAt": "2025-10-28T13:23:28.000Z" + }, + { + "id": 17, + "authorId": 1, + "name": "Realtime Clock", + "difficulty": "Easy", + "tags": ["date", "state", "interval"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:29.000Z", + "updatedAt": "2025-10-28T13:23:29.000Z" + }, + { + "id": 18, + "authorId": 1, + "name": "Chart With Recharts", + "difficulty": "Medium", + "tags": ["chart", "data", "props"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:30.000Z", + "updatedAt": "2025-10-28T13:23:30.000Z" + }, + { + "id": 19, + "authorId": 1, + "name": "Router Navigation", + "difficulty": "Medium", + "tags": ["router", "navigation", "hooks"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:31.000Z", + "updatedAt": "2025-10-28T13:23:31.000Z" + }, + { + "id": 20, + "authorId": 1, + "name": "Data Table Sortable", + "difficulty": "Hard", + "tags": ["table", "sort", "filter"], + "timeLimit": 1000, + "memoryLimit": 268435456, + "createdAt": "2025-10-28T13:23:32.000Z", + "updatedAt": "2025-10-28T13:23:32.000Z" + } + ]; + + + return ( +
+
+ +
+
+ База задач +
+ {}} + text="Создать задачу" + className="absolute right-0" + /> +
+ +
+ +
+ +
+ + {problems.map((v, i) => ( + + ))} +
+ + +
+ pages +
+
+
+ ); +}; + +export default Problems;