
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 = () => {
- База задач
+ Задачи
{}}