From 9a2c2a9589d8fcf57df51d7e0d1eafc564374c2c 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: Mon, 3 Nov 2025 20:36:29 +0300 Subject: [PATCH] dont work --- src/components/modal/Modal.tsx | 5 +- src/redux/slices/groups.ts | 91 +++++++++++++-------- src/views/home/groups/GroupItem.tsx | 15 +++- src/views/home/groups/Groups.tsx | 18 +++- src/views/home/groups/ModalCreate.tsx | 43 ++++++++++ src/views/home/groups/ModalUpdate.tsx | 45 ++++++++++ src/views/mission/codeeditor/CodeEditor.tsx | 2 +- 7 files changed, 175 insertions(+), 44 deletions(-) create mode 100644 src/views/home/groups/ModalCreate.tsx create mode 100644 src/views/home/groups/ModalUpdate.tsx diff --git a/src/components/modal/Modal.tsx b/src/components/modal/Modal.tsx index 98e850f..8c25d1e 100644 --- a/src/components/modal/Modal.tsx +++ b/src/components/modal/Modal.tsx @@ -48,9 +48,8 @@ export const Modal: React.FC = ({ transition={{ duration: 0.15 }} className={cn( " fixed top-0 left-0 h-svh w-svw backdrop-filter transition-all z-50", - // " pointer-events-none", - backdrop == "blur" && open ? "backdrop-blur-sm" : "", - backdrop == "opaque" && open ? "bg-[#00000055]" : "" + backdrop == "blur" && open && "backdrop-blur-sm", + backdrop == "opaque" && open && "bg-[#00000055] pointer-events-none", )} > )} diff --git a/src/redux/slices/groups.ts b/src/redux/slices/groups.ts index 8af10d2..c42a9e5 100644 --- a/src/redux/slices/groups.ts +++ b/src/redux/slices/groups.ts @@ -3,6 +3,8 @@ import axios from "../../axios"; // ─── Типы ──────────────────────────────────────────── +type Status = "idle" | "loading" | "successful" | "failed"; + export interface GroupMember { userId: number; username: string; @@ -20,17 +22,34 @@ export interface Group { interface GroupsState { groups: Group[]; currentGroup: Group | null; - status: "idle" | "loading" | "successful" | "failed"; + statuses: { + create: Status; + update: Status; + delete: Status; + fetchMy: Status; + fetchById: Status; + addMember: Status; + removeMember: Status; + }; error: string | null; } const initialState: GroupsState = { groups: [], currentGroup: null, - status: "idle", + statuses: { + create: "idle", + update: "idle", + delete: "idle", + fetchMy: "idle", + fetchById: "idle", + addMember: "idle", + removeMember: "idle", + }, error: null, }; + // ─── Async Thunks ───────────────────────────────────── // POST /groups @@ -146,111 +165,117 @@ const groupsSlice = createSlice({ extraReducers: (builder) => { // ─── CREATE GROUP ─── builder.addCase(createGroup.pending, (state) => { - state.status = "loading"; + state.statuses.create = "loading"; state.error = null; }); builder.addCase(createGroup.fulfilled, (state, action: PayloadAction) => { - state.status = "successful"; + state.statuses.create = "successful"; state.groups.push(action.payload); }); builder.addCase(createGroup.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.create = "failed"; state.error = action.payload; }); + // ─── UPDATE GROUP ─── builder.addCase(updateGroup.pending, (state) => { - state.status = "loading"; + state.statuses.update = "loading"; state.error = null; }); builder.addCase(updateGroup.fulfilled, (state, action: PayloadAction) => { - state.status = "successful"; + state.statuses.update = "successful"; const index = state.groups.findIndex((g) => g.id === action.payload.id); if (index !== -1) state.groups[index] = action.payload; - if (state.currentGroup?.id === action.payload.id) + if (state.currentGroup?.id === action.payload.id) { state.currentGroup = action.payload; + } }); builder.addCase(updateGroup.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.update = "failed"; state.error = action.payload; }); + + // ─── DELETE GROUP ─── builder.addCase(deleteGroup.pending, (state) => { - state.status = "loading"; + state.statuses.delete = "loading"; state.error = null; }); builder.addCase(deleteGroup.fulfilled, (state, action: PayloadAction) => { - state.status = "successful"; + state.statuses.delete = "successful"; state.groups = state.groups.filter((g) => g.id !== action.payload); if (state.currentGroup?.id === action.payload) state.currentGroup = null; }); builder.addCase(deleteGroup.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.delete = "failed"; state.error = action.payload; }); + // ─── FETCH MY GROUPS ─── builder.addCase(fetchMyGroups.pending, (state) => { - state.status = "loading"; + state.statuses.fetchMy = "loading"; state.error = null; }); builder.addCase(fetchMyGroups.fulfilled, (state, action: PayloadAction) => { - state.status = "successful"; + state.statuses.fetchMy = "successful"; state.groups = action.payload; }); builder.addCase(fetchMyGroups.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.fetchMy = "failed"; state.error = action.payload; }); + // ─── FETCH GROUP BY ID ─── builder.addCase(fetchGroupById.pending, (state) => { - state.status = "loading"; + state.statuses.fetchById = "loading"; state.error = null; }); builder.addCase(fetchGroupById.fulfilled, (state, action: PayloadAction) => { - state.status = "successful"; + state.statuses.fetchById = "successful"; state.currentGroup = action.payload; }); builder.addCase(fetchGroupById.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.fetchById = "failed"; state.error = action.payload; }); + // ─── ADD MEMBER ─── builder.addCase(addGroupMember.pending, (state) => { - state.status = "loading"; + state.statuses.addMember = "loading"; state.error = null; }); builder.addCase(addGroupMember.fulfilled, (state) => { - state.status = "successful"; + state.statuses.addMember = "successful"; }); builder.addCase(addGroupMember.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.addMember = "failed"; state.error = action.payload; }); + // ─── REMOVE MEMBER ─── builder.addCase(removeGroupMember.pending, (state) => { - state.status = "loading"; + state.statuses.removeMember = "loading"; state.error = null; }); - builder.addCase( - removeGroupMember.fulfilled, - (state, action: PayloadAction<{ groupId: number; memberId: number }>) => { - state.status = "successful"; - if (state.currentGroup && state.currentGroup.id === action.payload.groupId) { - state.currentGroup.members = state.currentGroup.members.filter( - (m) => m.userId !== action.payload.memberId - ); - } + builder.addCase(removeGroupMember.fulfilled, (state, action: PayloadAction<{ groupId: number; memberId: number }>) => { + state.statuses.removeMember = "successful"; + if (state.currentGroup && state.currentGroup.id === action.payload.groupId) { + state.currentGroup.members = state.currentGroup.members.filter( + (m) => m.userId !== action.payload.memberId + ); } - ); + }); builder.addCase(removeGroupMember.rejected, (state, action: PayloadAction) => { - state.status = "failed"; + state.statuses.removeMember = "failed"; state.error = action.payload; }); + }, }); diff --git a/src/views/home/groups/GroupItem.tsx b/src/views/home/groups/GroupItem.tsx index 1f12097..0bab197 100644 --- a/src/views/home/groups/GroupItem.tsx +++ b/src/views/home/groups/GroupItem.tsx @@ -1,31 +1,37 @@ import { cn } from "../../../lib/cn"; import { Book, UserAdd, Edit, EyeClosed, EyeOpen } from "../../../assets/icons/groups"; import { useNavigate } from "react-router-dom"; +import { GroupUpdate } from "./Groups"; export interface GroupItemProps { id: number; role: "menager" | "member" | "owner" | "viewer"; visible: boolean; name: string; + description: string; + setUpdateActive: (value: any) => void; + setUpdateGroup: (value: GroupUpdate) => void; } interface IconComponentProps { src: string; + onClick?: () => void; } const IconComponent: React.FC = ({ - src + src, onClick = () => void }) => { return onClick()} className="hover:bg-liquid-light rounded-[5px] cursor-pointer transition-all duration-300" /> } const GroupItem: React.FC = ({ - id, name, visible, role + id, name, visible, role, description, setUpdateGroup, setUpdateActive }) => { const navigate = useNavigate(); @@ -45,7 +51,10 @@ const GroupItem: React.FC = ({ (role == "menager" || role == "owner") && } { - (role == "menager" || role == "owner") && + (role == "menager" || role == "owner") && { + setUpdateGroup({id, }); + setUpdateActive(true); + }} /> } { visible == false && diff --git a/src/views/home/groups/Groups.tsx b/src/views/home/groups/Groups.tsx index a4664dc..79f1f09 100644 --- a/src/views/home/groups/Groups.tsx +++ b/src/views/home/groups/Groups.tsx @@ -5,11 +5,22 @@ import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; import GroupsBlock from "./GroupsBlock"; import { setMenuActivePage } from "../../../redux/slices/store"; import { fetchMyGroups } from "../../../redux/slices/groups"; -import { Modal } from "../../../components/modal/Modal"; +import ModalCreate from "./ModalCreate"; +import ModalUpdate from "./ModalUpdate"; + +export interface GroupUpdate { + id: number; + name: string; + description: string; +} const Groups = () => { const [modalActive, setModalActive] = useState(false); + const [modelUpdateActive, setModalUpdateActive] = useState(false); + const [updateGroup, setUpdateGroup] = useState({id: 0, name: "", description: ""}); + const dispatch = useAppDispatch(); + // Берём группы из стора const groups = useAppSelector((store) => store.groups.groups); @@ -84,9 +95,8 @@ const Groups = () => { - -
modal
-
+ + ); }; diff --git a/src/views/home/groups/ModalCreate.tsx b/src/views/home/groups/ModalCreate.tsx new file mode 100644 index 0000000..c77c82d --- /dev/null +++ b/src/views/home/groups/ModalCreate.tsx @@ -0,0 +1,43 @@ +import { FC, useEffect, useState } from "react"; +import { Modal } from "../../../components/modal/Modal"; +import { PrimaryButton } from "../../../components/button/PrimaryButton"; +import { SecondaryButton } from "../../../components/button/SecondaryButton"; +import { Input } from "../../../components/input/Input"; +import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; +import { createGroup } from "../../../redux/slices/groups"; + +interface ModalCreateProps { + active: boolean; + setActive: (value: boolean) => void; +} + +const ModalCreate: FC = ({ active, setActive }) => { + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const status = useAppSelector((state) => state.groups.statuses.create); + const dispatch = useAppDispatch(); + + useEffect(() => { + if (status == "successful"){ + setActive(false); + } + }, [status]); + + return ( + +
+
Создать группу
+ { setName(v)}} placeholder="login" /> + { setDescription(v)}} placeholder="login" /> + +
+ {dispatch(createGroup({name, description}))}} text="Создать" disabled={status=="loading"}/> + {setActive(false);}} text="Отмена" /> +
+
+
+ ); +}; + +export default ModalCreate; + diff --git a/src/views/home/groups/ModalUpdate.tsx b/src/views/home/groups/ModalUpdate.tsx new file mode 100644 index 0000000..f2f3548 --- /dev/null +++ b/src/views/home/groups/ModalUpdate.tsx @@ -0,0 +1,45 @@ +import { FC, useEffect, useState } from "react"; +import { Modal } from "../../../components/modal/Modal"; +import { PrimaryButton } from "../../../components/button/PrimaryButton"; +import { SecondaryButton } from "../../../components/button/SecondaryButton"; +import { Input } from "../../../components/input/Input"; +import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; +import { createGroup } from "../../../redux/slices/groups"; + +interface ModalUpdateProps { + active: boolean; + setActive: (value: boolean) => void; + groupId: number; + groupName: string; +} + +const ModalUpdate: FC = ({ active, setActive, groupName, groupId }) => { + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const status = useAppSelector((state) => state.groups.statuses.create); + const dispatch = useAppDispatch(); + + useEffect(() => { + if (status == "successful"){ + setActive(false); + } + }, [status]); + + return ( + +
+
Изменить группу {groupName} #{groupId}
+ { setName(v)}} placeholder="login" /> + { setDescription(v)}} placeholder="login" /> + +
+ {dispatch(createGroup({name, description}))}} text="Обновить" disabled={status=="loading"}/> + {setActive(false);}} text="Отмена" /> +
+
+
+ ); +}; + +export default ModalUpdate; + diff --git a/src/views/mission/codeeditor/CodeEditor.tsx b/src/views/mission/codeeditor/CodeEditor.tsx index a9133d9..2e520c6 100644 --- a/src/views/mission/codeeditor/CodeEditor.tsx +++ b/src/views/mission/codeeditor/CodeEditor.tsx @@ -6,7 +6,7 @@ import { DropDownList } from "../../../components/drop-down-list/DropDownList"; const languageMap: Record = { c: "cpp", - cpp: "cpp", + "C++": "cpp", java: "java", python: "python", pascal: "pascal",