From 99018537c59adba38bb3de261c3c8d7cd4626b07 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: Thu, 30 Oct 2025 20:43:01 +0300 Subject: [PATCH] pages --- src/assets/icons/groups/book.png | Bin 0 -> 5264 bytes src/assets/icons/groups/chevron-down.svg | 3 + src/assets/icons/groups/edit.svg | 3 + src/assets/icons/groups/eye-closed.svg | 3 + src/assets/icons/groups/eye-open.png | Bin 0 -> 662 bytes src/assets/icons/groups/index.ts | 8 ++ src/assets/icons/groups/user-profile-add.svg | 3 + src/pages/Home.tsx | 4 + src/views/home/articles/ArticleItem.tsx | 15 --- src/views/home/articles/Articles.tsx | 22 ++-- src/views/home/auth/Login.tsx | 9 +- src/views/home/auth/Register.tsx | 6 +- src/views/home/contests/ContestItem.tsx | 72 ++++++++++ src/views/home/contests/Contests.tsx | 131 +++++++++++++++++++ src/views/home/contests/ContestsBlock.tsx | 64 +++++++++ src/views/home/groups/GroupItem.tsx | 59 +++++++++ src/views/home/groups/Groups.tsx | 71 ++++++++++ src/views/home/groups/GroupsBlock.tsx | 60 +++++++++ src/views/home/menu/Menu.tsx | 8 +- src/views/home/problems/ProblemItem.tsx | 5 +- src/views/home/problems/Problems.tsx | 14 +- 21 files changed, 518 insertions(+), 42 deletions(-) create mode 100644 src/assets/icons/groups/book.png create mode 100644 src/assets/icons/groups/chevron-down.svg create mode 100644 src/assets/icons/groups/edit.svg create mode 100644 src/assets/icons/groups/eye-closed.svg create mode 100644 src/assets/icons/groups/eye-open.png create mode 100644 src/assets/icons/groups/index.ts create mode 100644 src/assets/icons/groups/user-profile-add.svg create mode 100644 src/views/home/contests/ContestItem.tsx create mode 100644 src/views/home/contests/Contests.tsx create mode 100644 src/views/home/contests/ContestsBlock.tsx create mode 100644 src/views/home/groups/GroupItem.tsx create mode 100644 src/views/home/groups/Groups.tsx create mode 100644 src/views/home/groups/GroupsBlock.tsx diff --git a/src/assets/icons/groups/book.png b/src/assets/icons/groups/book.png new file mode 100644 index 0000000000000000000000000000000000000000..260af3b44ee9a945c3fb47b7bf32eff6f393616f GIT binary patch literal 5264 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 ahgXa7yuZ)%g?$VR3=E#GelF{r5}E)5DDU0? literal 0 HcmV?d00001 diff --git a/src/assets/icons/groups/chevron-down.svg b/src/assets/icons/groups/chevron-down.svg new file mode 100644 index 0000000..cda1cc2 --- /dev/null +++ b/src/assets/icons/groups/chevron-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/groups/edit.svg b/src/assets/icons/groups/edit.svg new file mode 100644 index 0000000..abafac2 --- /dev/null +++ b/src/assets/icons/groups/edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/groups/eye-closed.svg b/src/assets/icons/groups/eye-closed.svg new file mode 100644 index 0000000..a3a57eb --- /dev/null +++ b/src/assets/icons/groups/eye-closed.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/groups/eye-open.png b/src/assets/icons/groups/eye-open.png new file mode 100644 index 0000000000000000000000000000000000000000..eee56902b013ac2b35bc2d8edcddd0e9e148884e GIT binary patch literal 662 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoI14-?iy0WWg+Z8+Vb&aw z3dZ6fcPEB*=VV?oFfg#BJNh~@Fl_AS{*%bcz`&&K>Eakt!T2`hcH!j!fxCOV=Ke5E zc0R;YzldoHp9|{@J|ET#j!x_Wj0>4AIZoj+5RIrY6tRg7H3twa>%=sJl{>yx>GtDpd zI$1CsQOs$d{o={t&2|5utmpE$-h6lU`kW1$^IpoB&Jmhaz4%JVwXzCzEk$n8eT!?t z-@K2BKYaW7;y!NQA1{wTOBFf)L$UwKcBW8``HXWjndAik`vTSITm`?l-arJ-L26lRE8huaKi6vU-c}}!> zw!=z0@aEk$VGj+jJj`n_b#+k<$`E7`o^RGZTlIX>Qo#(T$gG_r>IM8yVy5t?hdzo3 zZ`t|JXG*LnPx$12hbEVD6wZ`5cFAK>q7{4U9=&xUEOV4D&E~zAcsf;GMw`|BXzXPc zNi)W}pu2nfmHz~XMz7Cql#%mOs@fj0;?Q23Ia8MDBp!e8dY9fuWjVe&W^ + + diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 448e0d1..1cc8c5a 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,6 +8,8 @@ import { useEffect } from "react"; import { fetchWhoAmI } from "../redux/slices/auth"; import Problems from "../views/home/problems/Problems"; import Articles from "../views/home/articles/Articles"; +import Groups from "../views/home/groups/Groups"; +import Contests from "../views/home/contests/Contests"; const Home = () => { const name = useAppSelector((state) => state.auth.username); @@ -31,6 +33,8 @@ const Home = () => { } /> } /> } /> + } /> + } /> diff --git a/src/views/home/articles/ArticleItem.tsx b/src/views/home/articles/ArticleItem.tsx index 680b031..a7dfdc5 100644 --- a/src/views/home/articles/ArticleItem.tsx +++ b/src/views/home/articles/ArticleItem.tsx @@ -1,8 +1,4 @@ -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 ArticleItemProps { id: number; @@ -10,17 +6,6 @@ export interface ArticleItemProps { tags: string[]; } -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 ArticleItem: React.FC = ({ id, name, tags }) => { diff --git a/src/views/home/articles/Articles.tsx b/src/views/home/articles/Articles.tsx index b37a8a5..de5f1d4 100644 --- a/src/views/home/articles/Articles.tsx +++ b/src/views/home/articles/Articles.tsx @@ -1,23 +1,22 @@ +import { useEffect } from "react"; import { SecondaryButton } from "../../../components/button/SecondaryButton"; -import ArticleItem, { ArticleItemProps } from "./ArticleItem"; +import { useAppDispatch } from "../../../redux/hooks"; +import ArticleItem from "./ArticleItem"; +import { setMenuActivePage } from "../../../redux/slices/store"; -export interface Problem { +export interface Article { id: number; - authorId: number; name: string; - difficulty: "Easy" | "Medium" | "Hard"; tags: string[]; - timeLimit: number; - memoryLimit: number; - createdAt: string; - updatedAt: string; } const Articles = () => { - const articles: ArticleItemProps[] = [ + const dispatch = useAppDispatch(); + + const articles: Article[] = [ { "id": 1, "name": "Todo List App", @@ -130,6 +129,9 @@ const Articles = () => { } ]; + useEffect(() => { + dispatch(setMenuActivePage("articles")) + }, []); return (
@@ -137,7 +139,7 @@ const Articles = () => {
- База статей + Статьи
{ }} diff --git a/src/views/home/auth/Login.tsx b/src/views/home/auth/Login.tsx index 6013949..fc06b64 100644 --- a/src/views/home/auth/Login.tsx +++ b/src/views/home/auth/Login.tsx @@ -26,6 +26,9 @@ const Login = () => { // После успешного логина useEffect(() => { dispatch(setMenuActivePage("account")) + }, []); + + useEffect(() => { if (jwt) { navigate("/home/offices"); // или другая страница после входа } @@ -58,8 +61,8 @@ const Login = () => {
- {setUsername(v)}} placeholder="login"/> - {setPassword(v)}} placeholder="abCD1234" /> + { setUsername(v) }} placeholder="login" /> + { setPassword(v) }} placeholder="abCD1234" />
{ /> {}} + onClick={() => { }} >
diff --git a/src/views/home/auth/Register.tsx b/src/views/home/auth/Register.tsx index 7aae6c7..e2a6e8f 100644 --- a/src/views/home/auth/Register.tsx +++ b/src/views/home/auth/Register.tsx @@ -26,8 +26,12 @@ const Register = () => { const { status, error, jwt } = useAppSelector((state) => state.auth); // После успешной регистрации — переход в систему + + useEffect(() => { + dispatch(setMenuActivePage("account")) + }, []); + useEffect(() => { - dispatch(setMenuActivePage("account")); if (jwt) { navigate("/home"); } diff --git a/src/views/home/contests/ContestItem.tsx b/src/views/home/contests/ContestItem.tsx new file mode 100644 index 0000000..5451ae7 --- /dev/null +++ b/src/views/home/contests/ContestItem.tsx @@ -0,0 +1,72 @@ +import { cn } from "../../../lib/cn"; + +export interface ContestItemProps { + id: number; + name: string; + authors: string[]; + startAt: string; + registerAt: string; + duration: number; + members: number; + statusRegister: "reg" | "nonreg"; + 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}`; +} + + + +const ContestItem: React.FC = ({ + id, name, authors, startAt, registerAt, duration, members, statusRegister, type +}) => { + const now = new Date(); + + const waitTime = new Date(startAt).getTime() - now.getTime(); + + return ( +
+
+ {name} +
+
+ {authors.map((v, i) =>

{v}

)} +
+
+ {formatDate(startAt)} +
+
+ {duration} +
+ { + waitTime > 0 && +
+ {waitTime} +
+ } +
+ {members} +
+
+ {statusRegister} +
+ +
+ ); +}; + +export default ContestItem; diff --git a/src/views/home/contests/Contests.tsx b/src/views/home/contests/Contests.tsx new file mode 100644 index 0000000..2d68158 --- /dev/null +++ b/src/views/home/contests/Contests.tsx @@ -0,0 +1,131 @@ +import { useEffect } from "react"; +import { SecondaryButton } from "../../../components/button/SecondaryButton"; +import { cn } from "../../../lib/cn"; +import { useAppDispatch } from "../../../redux/hooks"; +import ContestsBlock from "./ContestsBlock"; +import { setMenuActivePage } from "../../../redux/slices/store"; + + +interface Contest { + id: number; + name: string; + authors: string[]; + startAt: string; + registerAt: string; + duration: number; + members: number; + statusRegister: "reg" | "nonreg"; +} + + + +const Contests = () => { + + const dispatch = useAppDispatch(); + const now = new Date(); + const contests: Contest[] = [ + // === Прошедшие контесты === + { + id: 1, + name: "Code Marathon 2025", + authors: ["tourist", "Petr", "Semen", "Rotar"], + startAt: "2025-09-15T10:00:00.000Z", + registerAt: "2025-09-10T10:00:00.000Z", + duration: 180, + members: 4821, + statusRegister: "reg", + }, + { + id: 2, + name: "Autumn Cup 2025", + authors: ["awoo", "Benq"], + startAt: "2025-09-25T17:00:00.000Z", + registerAt: "2025-09-20T17:00:00.000Z", + duration: 150, + members: 3670, + statusRegister: "nonreg", + }, + + // === Контесты, которые сейчас идут === + { + id: 3, + name: "Halloween Challenge", + authors: ["Errichto", "Radewoosh"], + startAt: "2025-10-29T10:00:00.000Z", // начался сегодня + registerAt: "2025-10-25T10:00:00.000Z", + duration: 240, + members: 5123, + statusRegister: "reg", + }, + { + id: 4, + name: "October Blitz", + authors: ["neal", "Um_nik"], + startAt: "2025-10-29T12:00:00.000Z", + registerAt: "2025-10-24T12:00:00.000Z", + duration: 300, + members: 2890, + statusRegister: "nonreg", + }, + + // === Контесты, которые еще не начались === + { + id: 5, + name: "Winter Warmup", + authors: ["tourist", "rng_58"], + startAt: "2025-11-05T18:00:00.000Z", + registerAt: "2025-11-01T18:00:00.000Z", + duration: 180, + members: 2100, + statusRegister: "reg", + }, + { + id: 6, + name: "Global Coding Cup", + authors: ["maroonrk", "kostka"], + startAt: "2025-11-12T15:00:00.000Z", + registerAt: "2025-11-08T15:00:00.000Z", + duration: 240, + members: 1520, + statusRegister: "nonreg", + }, + ]; + + useEffect(() => { + dispatch(setMenuActivePage("contests")) + }, []); + + return ( +
+
+ +
+
+ Контесты +
+ { }} + text="Создать группу" + className="absolute right-0" + /> +
+ +
+ +
+ + + { + const endTime = new Date(contest.startAt).getTime() + contest.duration * 60 * 1000; + return endTime >= now.getTime(); + })} /> + { + const endTime = new Date(contest.startAt).getTime() + contest.duration * 60 * 1000; + return endTime < now.getTime(); + })} /> +
+
+ ); +}; + +export default Contests; diff --git a/src/views/home/contests/ContestsBlock.tsx b/src/views/home/contests/ContestsBlock.tsx new file mode 100644 index 0000000..63a5d32 --- /dev/null +++ b/src/views/home/contests/ContestsBlock.tsx @@ -0,0 +1,64 @@ +import { useState, FC } from "react"; +import { cn } from "../../../lib/cn"; +import { ChevroneDown } from "../../../assets/icons/groups"; +import ContestItem from "./ContestItem"; + + +interface Contest { + id: number; + name: string; + authors: string[]; + startAt: string; + registerAt: string; + duration: number; + members: number; + statusRegister: "reg" | "nonreg"; +} + +interface GroupsBlockProps { + contests: Contest[]; + title: string; + className?: string; +} + + +const GroupsBlock: FC = ({ contests, title, className }) => { + + + const [active, setActive] = useState(title != "Скрытые"); + + + return ( + +
+
{ + console.log(active); + setActive(!active) + }}> + {title} + +
+
+
+
+ { + contests.map((v, i) => ) + } +
+ +
+
+
+ ); +}; + +export default GroupsBlock; diff --git a/src/views/home/groups/GroupItem.tsx b/src/views/home/groups/GroupItem.tsx new file mode 100644 index 0000000..eafd491 --- /dev/null +++ b/src/views/home/groups/GroupItem.tsx @@ -0,0 +1,59 @@ +import { cn } from "../../../lib/cn"; +import { Book, UserAdd, Edit, EyeClosed, EyeOpen } from "../../../assets/icons/groups"; + +export interface GroupItemProps { + id: number; + role: "menager" | "member" | "owner" | "viewer"; + visible: boolean; + name: string; +} + + +interface IconComponentProps { + src: string; +} + +const IconComponent: React.FC = ({ + src +}) => { + + return +} + +const GroupItem: React.FC = ({ + id, name, visible, role +}) => { + console.log(id); + return ( +
+
+ +
+
+ {name} +
+
+ { + (role == "menager" || role == "owner") && + } + { + (role == "menager" || role == "owner") && + } + { + visible == false && + } + { + visible == true && + } +
+
+
+
+ ); +}; + +export default GroupItem; diff --git a/src/views/home/groups/Groups.tsx b/src/views/home/groups/Groups.tsx new file mode 100644 index 0000000..34559f6 --- /dev/null +++ b/src/views/home/groups/Groups.tsx @@ -0,0 +1,71 @@ +import { useEffect } from "react"; +import { SecondaryButton } from "../../../components/button/SecondaryButton"; +import { cn } from "../../../lib/cn"; +import { useAppDispatch } from "../../../redux/hooks"; +import GroupsBlock from "./GroupsBlock"; +import { setMenuActivePage } from "../../../redux/slices/store"; + + +export interface Group { + id: number; + role: "menager" | "member" | "owner" | "viewer"; + visible: boolean; + name: string; +} + + +const Groups = () => { + + const dispatch = useAppDispatch(); + + const groups: Group[] = [ + { id: 1, role: "owner", name: "Main Administration", visible: true }, + { id: 2, role: "menager", name: "Project Managers", visible: true }, + { id: 3, role: "member", name: "Developers", visible: true }, + { id: 4, role: "viewer", name: "QA Viewers", visible: true }, + { id: 5, role: "member", name: "Design Team", visible: true }, + { id: 6, role: "owner", name: "Executive Board", visible: true }, + { id: 7, role: "menager", name: "HR Managers", visible: true }, + { id: 8, role: "viewer", name: "Marketing Reviewers", visible: false }, + { id: 9, role: "member", name: "Content Creators", visible: false }, + { id: 10, role: "menager", name: "Support Managers", visible: true }, + { id: 11, role: "viewer", name: "External Auditors", visible: false }, + { id: 12, role: "member", name: "Frontend Developers", visible: true }, + { id: 13, role: "member", name: "Backend Developers", visible: true }, + { id: 14, role: "viewer", name: "Guest Access", visible: false }, + { id: 15, role: "menager", name: "Operations", visible: true }, + ]; + + useEffect(() => { + dispatch(setMenuActivePage("groups")) + }, []); + + return ( +
+
+ +
+
+ Группы +
+ { }} + text="Создать группу" + className="absolute right-0" + /> +
+ +
+ +
+ + + v.visible && (v.role == "owner" || v.role == "menager"))} /> + v.visible && (v.role == "member" || v.role == "viewer"))} /> + v.visible == false)} /> +
+
+ ); +}; + +export default Groups; diff --git a/src/views/home/groups/GroupsBlock.tsx b/src/views/home/groups/GroupsBlock.tsx new file mode 100644 index 0000000..fd4e2e1 --- /dev/null +++ b/src/views/home/groups/GroupsBlock.tsx @@ -0,0 +1,60 @@ +import { useState, FC } from "react"; +import GroupItem from "./GroupItem"; +import { cn } from "../../../lib/cn"; +import { ChevroneDown } from "../../../assets/icons/groups"; + + +export interface Group { + id: number; + role: "menager" | "member" | "owner" | "viewer"; + visible: boolean; + name: string; +} + +interface GroupsBlockProps { + groups: Group[]; + title: string; + className?: string; +} + + +const GroupsBlock: FC = ({ groups, title, className }) => { + + + const [active, setActive] = useState(title != "Скрытые"); + + + return ( + +
+
{ + console.log(active); + setActive(!active) + }}> + {title} + +
+
+
+ +
+ { + groups.map((v, i) => ) + } +
+
+
+
+ ); +}; + +export default GroupsBlock; diff --git a/src/views/home/menu/Menu.tsx b/src/views/home/menu/Menu.tsx index f2ccfea..63ae749 100644 --- a/src/views/home/menu/Menu.tsx +++ b/src/views/home/menu/Menu.tsx @@ -6,10 +6,10 @@ import { useAppSelector } from "../../../redux/hooks"; const Menu = () => { const menuItems = [ {text: "Главная", href: "/home", icon: Home, page: "home" }, - {text: "Задачи", href: "/home/problems", icon: Clipboard, page: "clipboard" }, - {text: "Статьи", href: "/home/articles", icon: Openbook, page: "openbool" }, - {text: "Группы", href: "/home", icon: Users, page: "users" }, - {text: "Контесты", href: "/home", icon: Cup, page: "cup" }, + {text: "Задачи", href: "/home/problems", icon: Clipboard, page: "problems" }, + {text: "Статьи", href: "/home/articles", icon: Openbook, page: "articles" }, + {text: "Группы", href: "/home/groups", icon: Users, page: "groups" }, + {text: "Контесты", href: "/home/contests", icon: Cup, page: "contests" }, {text: "Аккаунт", href: "/home/account", icon: Account, page: "account" }, ]; const activePage = useAppSelector((state) => state.store.menu.activePage); diff --git a/src/views/home/problems/ProblemItem.tsx b/src/views/home/problems/ProblemItem.tsx index 55ab2c5..5410cd5 100644 --- a/src/views/home/problems/ProblemItem.tsx +++ b/src/views/home/problems/ProblemItem.tsx @@ -1,6 +1,3 @@ -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"; @@ -30,7 +27,7 @@ export function formatBytesToMB(bytes: number): string { } const ProblemItem: React.FC = ({ - id, authorId, name, difficulty, tags, timeLimit, memoryLimit, createdAt, updatedAt, type, status + id, name, difficulty, timeLimit, memoryLimit, type, status }) => { console.log(id); return ( diff --git a/src/views/home/problems/Problems.tsx b/src/views/home/problems/Problems.tsx index 9507a26..5c1ae29 100644 --- a/src/views/home/problems/Problems.tsx +++ b/src/views/home/problems/Problems.tsx @@ -1,9 +1,8 @@ -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"; +import { useAppDispatch } from "../../../redux/hooks"; +import { useEffect } from "react"; +import { setMenuActivePage } from "../../../redux/slices/store"; export interface Problem { @@ -21,6 +20,8 @@ export interface Problem { const Problems = () => { + const dispatch = useAppDispatch(); + const problems: Problem[] = [ { "id": 1, @@ -464,6 +465,9 @@ const Problems = () => { } ]; + useEffect(() => { + dispatch(setMenuActivePage("problems")) + }, []); return (
@@ -471,7 +475,7 @@ const Problems = () => {
- База задач + Задачи
{}}