From e904297bb910a4bd0a3cd4012b679d53036d963e 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: Wed, 19 Nov 2025 22:28:15 +0300 Subject: [PATCH] user role controller --- src/assets/icons/group/index.ts | 3 +- src/assets/icons/group/logout.svg | 3 + src/assets/icons/input/receipt.png | Bin 9119 -> 0 bytes .../drop-down-list/DropDownList.tsx | 15 +- src/components/modal/ConfirmModal.tsx | 56 +++++ src/components/modal/Modal.tsx | 2 +- src/pages/Home.tsx | 2 +- src/views/home/group/Group.tsx | 2 - src/views/home/group/chat/Chat.tsx | 7 +- src/views/home/group/contests/Contests.tsx | 7 +- src/views/home/groups/ModalInvite.tsx | 2 + src/views/home/rightpanel/Group.tsx | 60 ----- src/views/home/rightpanel/group/Group.tsx | 126 +++++++++++ .../home/rightpanel/group/ModalLeave.tsx | 107 +++++++++ .../home/rightpanel/group/ModalUpdate.tsx | 208 ++++++++++++++++++ tsconfig.app.tsbuildinfo | 2 +- 16 files changed, 531 insertions(+), 71 deletions(-) create mode 100644 src/assets/icons/group/logout.svg delete mode 100644 src/assets/icons/input/receipt.png create mode 100644 src/components/modal/ConfirmModal.tsx delete mode 100644 src/views/home/rightpanel/Group.tsx create mode 100644 src/views/home/rightpanel/group/Group.tsx create mode 100644 src/views/home/rightpanel/group/ModalLeave.tsx create mode 100644 src/views/home/rightpanel/group/ModalUpdate.tsx diff --git a/src/assets/icons/group/index.ts b/src/assets/icons/group/index.ts index 1faa753..0ee02d8 100644 --- a/src/assets/icons/group/index.ts +++ b/src/assets/icons/group/index.ts @@ -1,5 +1,6 @@ import Cup from './cup.svg'; import Home from './home.svg'; import MessageChat from './message-chat.svg'; +import Logout from './logout.svg'; -export { Cup, MessageChat, Home }; +export { Cup, MessageChat, Home, Logout }; diff --git a/src/assets/icons/group/logout.svg b/src/assets/icons/group/logout.svg new file mode 100644 index 0000000..1a4fb3a --- /dev/null +++ b/src/assets/icons/group/logout.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/input/receipt.png b/src/assets/icons/input/receipt.png deleted file mode 100644 index 0f7c554986a50bf98d329ac608e13aba63a47ccf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9119 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 void; defaultState?: DropDownListItem; items: DropDownListItem[]; + weight?: string; } export const DropDownList: React.FC = ({ @@ -22,6 +23,7 @@ export const DropDownList: React.FC = ({ onChange, defaultState, items = [{ text: '', value: '' }], + weight = 'w-[180px]', }) => { if (items.length == 0) items.push({ text: '', value: '' }); @@ -38,13 +40,19 @@ export const DropDownList: React.FC = ({ setActive(false); }); + useEffect(() => { + setValue(defaultState != undefined ? defaultState : items[0]); + }, [defaultState]); + + console.log(defaultState, items); return (
{ setActive(!active); @@ -63,8 +71,9 @@ export const DropDownList: React.FC = ({
void; + onConfirmClick: () => void; + title?: string; + message?: string; + confirmColor?: 'primary' | 'secondary' | 'error' | 'warning' | 'success'; + confirmText?: string; +} + +const ConfirmModal: FC = ({ + active, + setActive, + onConfirmClick, + title, + message, + confirmColor = 'secondary', + confirmText = 'Ок', +}) => { + return ( + +
+
{title}
+
{message}
+
+ { + onConfirmClick(); + setActive(false); + }} + text={confirmText} + color={confirmColor} + /> + { + setActive(false); + }} + text="Отмена" + /> +
+
+
+ ); +}; + +export default ConfirmModal; diff --git a/src/components/modal/Modal.tsx b/src/components/modal/Modal.tsx index a70eaf9..58e46d4 100644 --- a/src/components/modal/Modal.tsx +++ b/src/components/modal/Modal.tsx @@ -47,7 +47,7 @@ export const Modal: React.FC = ({ exit={modalbgVariants.closed} transition={{ duration: 0.15 }} className={cn( - ' fixed top-0 left-0 h-svh w-svw backdrop-filter transition-all z-50', + ' fixed top-0 left-0 h-svh w-svw backdrop-filter transition-[background-color,backdrop-filter,opacity] z-50', backdrop == 'blur' && open && 'backdrop-blur-sm', backdrop == 'opaque' && open && diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index aa588ae..364a38c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -17,7 +17,7 @@ import Account from '../views/home/account/Account'; import ProtectedRoute from '../components/router/ProtectedRoute'; import { MissionsRightPanel } from '../views/home/rightpanel/Missions'; import { ArticlesRightPanel } from '../views/home/rightpanel/Articles'; -import { GroupRightPanel } from '../views/home/rightpanel/Group'; +import { GroupRightPanel } from '../views/home/rightpanel/group/Group'; import GroupInvite from '../views/home/groupinviter/GroupInvite'; import { toastError, diff --git a/src/views/home/group/Group.tsx b/src/views/home/group/Group.tsx index ef34d56..db7adcc 100644 --- a/src/views/home/group/Group.tsx +++ b/src/views/home/group/Group.tsx @@ -24,8 +24,6 @@ const Group: FC = () => { dispatch(fetchGroupById(groupId)); }, [groupId]); - console.log(group); - return (
{ useEffect(() => { dispatch(setMenuActiveGroupPage('chat')); }, []); - return <>; + return ( +
+ {' '} + Пока пусто :( +
+ ); }; diff --git a/src/views/home/group/contests/Contests.tsx b/src/views/home/group/contests/Contests.tsx index 39faea4..5df4e25 100644 --- a/src/views/home/group/contests/Contests.tsx +++ b/src/views/home/group/contests/Contests.tsx @@ -8,5 +8,10 @@ export const Contests = () => { useEffect(() => { dispatch(setMenuActiveGroupPage('contests')); }, []); - return <>; + return ( +
+ {' '} + Пока пусто :( +
+ ); }; diff --git a/src/views/home/groups/ModalInvite.tsx b/src/views/home/groups/ModalInvite.tsx index a00cfec..7983671 100644 --- a/src/views/home/groups/ModalInvite.tsx +++ b/src/views/home/groups/ModalInvite.tsx @@ -5,6 +5,7 @@ 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 { toastSuccess } from '../../../lib/toastNotification'; interface ModalInviteProps { active: boolean; @@ -51,6 +52,7 @@ const ModalInvite: FC = ({ if (!inviteLink) return; try { await navigator.clipboard.writeText(inviteLink); + toastSuccess('Приглашение скопировано в буфер обмена!'); setActive(false); } catch (err) { console.error('Не удалось скопировать ссылку:', err); diff --git a/src/views/home/rightpanel/Group.tsx b/src/views/home/rightpanel/Group.tsx deleted file mode 100644 index 28b26fb..0000000 --- a/src/views/home/rightpanel/Group.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { FC } from 'react'; - -export const GroupRightPanel: FC = () => { - const items = [ - { - name: 'Игнат Герасименко', - role: 'Администратор', - }, - { - name: 'Алиса Макаренко', - role: 'Модератор', - }, - { - name: 'Федор Картман', - role: 'Модератор', - }, - { - name: 'Карина Механаджанович', - role: 'Участник', - }, - { - name: 'Михаил Ангрский', - role: 'Участник', - }, - { - name: 'newuser', - role: 'Участник (Вы)', - }, - ]; - return ( -
-
- Пользователи -
- - {items.map((v, i) => { - return ( - <> - { -
-
-
-
- {v.name} -
-
- {v.role} -
-
-
- } - {i + 1 != items.length && ( -
- )} - - ); - })} -
- ); -}; diff --git a/src/views/home/rightpanel/group/Group.tsx b/src/views/home/rightpanel/group/Group.tsx new file mode 100644 index 0000000..f1f9d7f --- /dev/null +++ b/src/views/home/rightpanel/group/Group.tsx @@ -0,0 +1,126 @@ +import { FC, useEffect, useState } from 'react'; +import { Navigate, useParams } from 'react-router-dom'; +import { useAppDispatch, useAppSelector } from '../../../../redux/hooks'; +import { fetchGroupById, GroupMember } from '../../../../redux/slices/groups'; +import { Edit } from '../../../../assets/icons/input'; +import { Logout } from '../../../../assets/icons/group'; +import ModalLeave from './ModalLeave'; +import ModalUpdate from './ModalUpdate'; + +export const GroupRightPanel: FC = () => { + const groupId = Number(useParams<{ groupId: string }>().groupId); + if (!groupId) { + return ; + } + + const dispatch = useAppDispatch(); + + const [user, setUser] = useState(); + const [myUser, setMyUser] = useState(); + const [isAdmin, setIsAdmin] = useState(false); + const [modalLeaveActive, setModalLeaveActive] = useState(false); + const [modalUpdateActive, setModalUpdateActive] = useState(false); + const { id: userId } = useAppSelector((state) => state.auth); + + const { group } = useAppSelector((state) => state.groups.fetchGroupById); + useEffect(() => { + dispatch(fetchGroupById(groupId)); + }, [groupId]); + + useEffect(() => { + if (!group) return; + + const isUserAdmin = + group.members?.some( + (m) => + Number(m.userId) === Number(userId) && + m.role.includes('Administrator'), + ) || false; + + setIsAdmin(isUserAdmin); + + const member = group.members?.find( + (m) => Number(m.userId) === Number(userId), + ); + + setMyUser(member); + }, [group, userId]); + + return ( +
+
+ Пользователи +
+ + {group?.members.map((v, i) => { + return ( + <> + { +
+
+
+
+ {v.username} +
+
+ {v.role + + (Number(userId) == v.userId + ? ' (Вы)' + : '')} +
+
+ {(isAdmin || Number(userId) == v.userId) && + !v.role.includes('Creator') && ( +
{ + if ( + Number(userId) == v.userId + ) { + setModalLeaveActive(true); + return; + } + if (isAdmin) { + setUser(v); + setModalUpdateActive(true); + return; + } + }} + > + {Number(userId) == v.userId ? ( + + ) : isAdmin ? ( + + ) : ( + <> + )} +
+ )} +
+ } + {i + 1 != group?.members.length && ( +
+ )} + + ); + })} + + + +
+ ); +}; diff --git a/src/views/home/rightpanel/group/ModalLeave.tsx b/src/views/home/rightpanel/group/ModalLeave.tsx new file mode 100644 index 0000000..5427b2f --- /dev/null +++ b/src/views/home/rightpanel/group/ModalLeave.tsx @@ -0,0 +1,107 @@ +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 { + deleteGroup, + removeGroupMember, + setGroupsStatus, + updateGroup, +} from '../../../../redux/slices/groups'; +import ConfirmModal from '../../../../components/modal/ConfirmModal'; +import { useNavigate } from 'react-router-dom'; + +interface ModalLeaveProps { + active: boolean; + setActive: (value: boolean) => void; + groupId: number; + groupName?: string; + userId: number; +} + +const ModalLeave: FC = ({ + active, + setActive, + groupName, + groupId, + userId, +}) => { + const statusLeave = useAppSelector( + (state) => state.groups.removeGroupMember.status, + ); + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + + const [modalConfirmActive, setModalConfirmActive] = + useState(false); + + useEffect(() => { + if (statusLeave == 'successful') { + dispatch( + setGroupsStatus({ key: 'removeGroupMember', status: 'idle' }), + ); + setActive(false); + navigate('/home/groups'); + } + }, [statusLeave]); + + return ( + <> + +
+
+ Вы действительно хотите покинуть группу: +
+
+ "{groupName}" #{groupId}? +
+
+ { + setModalConfirmActive(true); + }} + text={ + statusLeave == 'loading' + ? 'Покинуть...' + : 'Покинуть' + } + disabled={statusLeave == 'loading'} + color="error" + /> + { + setActive(false); + }} + text="Отмена" + /> +
+
+ { + dispatch( + removeGroupMember({ + groupId, + memberId: userId, + }), + ); + }} + /> +
+ + ); +}; + +export default ModalLeave; diff --git a/src/views/home/rightpanel/group/ModalUpdate.tsx b/src/views/home/rightpanel/group/ModalUpdate.tsx new file mode 100644 index 0000000..3062cd2 --- /dev/null +++ b/src/views/home/rightpanel/group/ModalUpdate.tsx @@ -0,0 +1,208 @@ +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 { + addGroupMember, + deleteGroup, + fetchGroupById, + GroupMember, + removeGroupMember, + setGroupsStatus, + updateGroup, +} from '../../../../redux/slices/groups'; +import ConfirmModal from '../../../../components/modal/ConfirmModal'; +import { DropDownList } from '../../../../components/drop-down-list/DropDownList'; +import { ReverseButton } from '../../../../components/button/ReverseButton'; + +interface ModalUpdateProps { + active: boolean; + setActive: (value: boolean) => void; + groupId: number; + userId: number; + user?: GroupMember; + adminUser?: GroupMember; + groupName?: string; +} + +const ModalUpdate: FC = ({ + active, + setActive, + groupId, + userId, + user, + adminUser, + groupName, +}) => { + const statusLeave = useAppSelector( + (state) => state.groups.removeGroupMember.status, + ); + const statusUpdate = useAppSelector( + (state) => state.groups.addGroupMember.status, + ); + const dispatch = useAppDispatch(); + + const [modalConfirmDeleteUser, setModalConfirmDeleteUser] = + useState(false); + const [modalConfirmRoleActive, setModalConfirmRoleActive] = + useState(false); + const [userRole, setUserRole] = useState(''); + + useEffect(() => { + if (active) { + } + }, [active]); + + useEffect(() => { + if (statusLeave == 'successful') { + dispatch( + setGroupsStatus({ key: 'removeGroupMember', status: 'idle' }), + ); + dispatch(fetchGroupById(groupId)); + setActive(false); + } + }, [statusLeave]); + + useEffect(() => { + if (statusUpdate == 'successful') { + dispatch( + setGroupsStatus({ key: 'addGroupMember', status: 'idle' }), + ); + dispatch(fetchGroupById(groupId)); + setActive(false); + } + }, [statusUpdate]); + + useEffect(() => { + console.log(user); + if (user) { + setUserRole( + user?.role.includes('Creator') ? 'Creator' : user?.role, + ); + } + }, [user]); + + const roles = [ + 'Member', + 'Administrator', + ...(adminUser?.role.includes('Creator') ? ['Creator'] : []), + ]; + + return ( + +
+
+ Управление участниками группы: +
+
+ "{groupName}" #{groupId} +
+
Пользователь: {user?.username}
+
Текущая роль: {user?.role}
+
+
+ { + return { text: v, value: v }; + })} + onChange={(v) => { + setUserRole(v); + }} + /> +
+ { + setModalConfirmRoleActive(true); + }} + text={ + statusUpdate == 'loading' + ? 'Назначить...' + : 'Назначить' + } + disabled={statusUpdate == 'loading'} + color="secondary" + /> +
+ +
+
+ Исключить пользователя? +
+ { + setModalConfirmDeleteUser(true); + }} + text={ + statusLeave == 'loading' + ? 'Исключить...' + : 'Исключить' + } + disabled={statusLeave == 'loading'} + color="error" + /> +
+ +
+ { + setActive(false); + }} + text="Отмена" + /> +
+
+ + { + if (user) { + dispatch( + removeGroupMember({ + groupId, + memberId: user.userId, + }), + ); + } + }} + /> + + { + if (user) { + dispatch( + addGroupMember({ + groupId, + userId: user.userId, + role: + userRole == 'Creator' + ? 'Administrator, Creator' + : userRole, + }), + ); + } + }} + /> +
+ ); +}; + +export default ModalUpdate; diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo index f725851..22d984f 100644 --- a/tsconfig.app.tsbuildinfo +++ b/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/axios.ts","./src/main.tsx","./src/vite-env.d.ts","./src/assets/icons/account/index.ts","./src/assets/icons/auth/index.ts","./src/assets/icons/filters/index.ts","./src/assets/icons/groups/index.ts","./src/assets/icons/header/index.ts","./src/assets/icons/input/index.ts","./src/assets/icons/menu/index.ts","./src/assets/icons/missions/index.ts","./src/assets/logos/index.ts","./src/components/button/primarybutton.tsx","./src/components/button/reversebutton.tsx","./src/components/button/secondarybutton.tsx","./src/components/checkbox/checkbox.tsx","./src/components/drop-down-list/dropdownlist.tsx","./src/components/drop-down-list/filter.tsx","./src/components/drop-down-list/sorter.tsx","./src/components/input/daterangeinput.tsx","./src/components/input/input.tsx","./src/components/input/searchinput.tsx","./src/components/modal/modal.tsx","./src/components/router/protectedroute.tsx","./src/components/switch/switch.tsx","./src/config/colors.ts","./src/hooks/useclickoutside.ts","./src/hooks/usequery.ts","./src/lib/cn.ts","./src/pages/article.tsx","./src/pages/articleeditor.tsx","./src/pages/contesteditor.tsx","./src/pages/home.tsx","./src/pages/mission.tsx","./src/redux/hooks.ts","./src/redux/store.ts","./src/redux/slices/articles.ts","./src/redux/slices/auth.ts","./src/redux/slices/contests.ts","./src/redux/slices/groups.ts","./src/redux/slices/missions.ts","./src/redux/slices/store.ts","./src/redux/slices/submit.ts","./src/views/article/header.tsx","./src/views/articleeditor/editor.tsx","./src/views/articleeditor/header.tsx","./src/views/articleeditor/marckdownpreview.tsx","./src/views/home/account/account.tsx","./src/views/home/account/accoutmenu.tsx","./src/views/home/account/rightpanel.tsx","./src/views/home/account/articles/articlesblock.tsx","./src/views/home/account/contests/contests.tsx","./src/views/home/account/contests/contestsblock.tsx","./src/views/home/account/contests/mycontestitem.tsx","./src/views/home/account/contests/registercontestitem.tsx","./src/views/home/account/missions/missions.tsx","./src/views/home/account/missions/missionsblock.tsx","./src/views/home/account/missions/mymissionitem.tsx","./src/views/home/articles/articleitem.tsx","./src/views/home/articles/articles.tsx","./src/views/home/articles/filter.tsx","./src/views/home/auth/login.tsx","./src/views/home/auth/register.tsx","./src/views/home/contest/contest.tsx","./src/views/home/contest/missionitem.tsx","./src/views/home/contest/missions.tsx","./src/views/home/contest/submissionitem.tsx","./src/views/home/contest/submissions.tsx","./src/views/home/contests/contestitem.tsx","./src/views/home/contests/contests.tsx","./src/views/home/contests/contestsblock.tsx","./src/views/home/contests/filter.tsx","./src/views/home/contests/modalcreate.tsx","./src/views/home/groups/filter.tsx","./src/views/home/groups/group.tsx","./src/views/home/groups/groupitem.tsx","./src/views/home/groups/groups.tsx","./src/views/home/groups/groupsblock.tsx","./src/views/home/groups/modalcreate.tsx","./src/views/home/groups/modalupdate.tsx","./src/views/home/menu/menu.tsx","./src/views/home/menu/menuitem.tsx","./src/views/home/missions/filter.tsx","./src/views/home/missions/missionitem.tsx","./src/views/home/missions/missions.tsx","./src/views/home/missions/modalcreate.tsx","./src/views/mission/codeeditor/codeeditor.tsx","./src/views/mission/statement/header.tsx","./src/views/mission/statement/latextcontainer.tsx","./src/views/mission/statement/missionsubmissions.tsx","./src/views/mission/statement/statement.tsx","./src/views/mission/statement/submissionitem.tsx","./src/views/mission/submission/submission.tsx"],"version":"5.6.2"} \ No newline at end of file +{"root":["./src/app.tsx","./src/axios.ts","./src/main.tsx","./src/vite-env.d.ts","./src/assets/icons/account/index.ts","./src/assets/icons/auth/index.ts","./src/assets/icons/filters/index.ts","./src/assets/icons/group/index.ts","./src/assets/icons/groups/index.ts","./src/assets/icons/header/index.ts","./src/assets/icons/input/index.ts","./src/assets/icons/menu/index.ts","./src/assets/icons/missions/index.ts","./src/assets/logos/index.ts","./src/components/button/primarybutton.tsx","./src/components/button/reversebutton.tsx","./src/components/button/secondarybutton.tsx","./src/components/checkbox/checkbox.tsx","./src/components/drop-down-list/dropdownlist.tsx","./src/components/drop-down-list/filter.tsx","./src/components/drop-down-list/sorter.tsx","./src/components/input/daterangeinput.tsx","./src/components/input/input.tsx","./src/components/input/searchinput.tsx","./src/components/modal/confirmmodal.tsx","./src/components/modal/modal.tsx","./src/components/router/protectedroute.tsx","./src/components/switch/switch.tsx","./src/config/colors.ts","./src/hooks/useclickoutside.ts","./src/hooks/usequery.ts","./src/lib/cn.ts","./src/lib/toastnotification.ts","./src/pages/article.tsx","./src/pages/articleeditor.tsx","./src/pages/contesteditor.tsx","./src/pages/home.tsx","./src/pages/mission.tsx","./src/redux/hooks.ts","./src/redux/store.ts","./src/redux/slices/articles.ts","./src/redux/slices/auth.ts","./src/redux/slices/contests.ts","./src/redux/slices/groupfeed.ts","./src/redux/slices/groups.ts","./src/redux/slices/missions.ts","./src/redux/slices/store.ts","./src/redux/slices/submit.ts","./src/views/article/header.tsx","./src/views/articleeditor/editor.tsx","./src/views/articleeditor/header.tsx","./src/views/articleeditor/marckdownpreview.tsx","./src/views/home/account/account.tsx","./src/views/home/account/accoutmenu.tsx","./src/views/home/account/rightpanel.tsx","./src/views/home/account/articles/articlesblock.tsx","./src/views/home/account/contests/contests.tsx","./src/views/home/account/contests/contestsblock.tsx","./src/views/home/account/contests/mycontestitem.tsx","./src/views/home/account/contests/registercontestitem.tsx","./src/views/home/account/missions/missions.tsx","./src/views/home/account/missions/missionsblock.tsx","./src/views/home/account/missions/mymissionitem.tsx","./src/views/home/articles/articleitem.tsx","./src/views/home/articles/articles.tsx","./src/views/home/articles/filter.tsx","./src/views/home/auth/login.tsx","./src/views/home/auth/register.tsx","./src/views/home/contest/contest.tsx","./src/views/home/contest/missionitem.tsx","./src/views/home/contest/missions.tsx","./src/views/home/contest/submissionitem.tsx","./src/views/home/contest/submissions.tsx","./src/views/home/contests/contestitem.tsx","./src/views/home/contests/contests.tsx","./src/views/home/contests/contestsblock.tsx","./src/views/home/contests/filter.tsx","./src/views/home/contests/modalcreate.tsx","./src/views/home/group/group.tsx","./src/views/home/group/groupmenu.tsx","./src/views/home/group/chat/chat.tsx","./src/views/home/group/contests/contests.tsx","./src/views/home/group/posts/modalcreate.tsx","./src/views/home/group/posts/modalupdate.tsx","./src/views/home/group/posts/postitem.tsx","./src/views/home/group/posts/posts.tsx","./src/views/home/groupinviter/groupinvite.tsx","./src/views/home/groups/filter.tsx","./src/views/home/groups/groupitem.tsx","./src/views/home/groups/groups.tsx","./src/views/home/groups/groupsblock.tsx","./src/views/home/groups/modalcreate.tsx","./src/views/home/groups/modalinvite.tsx","./src/views/home/groups/modalupdate.tsx","./src/views/home/menu/menu.tsx","./src/views/home/menu/menuitem.tsx","./src/views/home/missions/filter.tsx","./src/views/home/missions/missionitem.tsx","./src/views/home/missions/missions.tsx","./src/views/home/missions/modalcreate.tsx","./src/views/home/rightpanel/articles.tsx","./src/views/home/rightpanel/missions.tsx","./src/views/home/rightpanel/group/group.tsx","./src/views/home/rightpanel/group/modalleave.tsx","./src/views/home/rightpanel/group/modalupdate.tsx","./src/views/mission/codeeditor/codeeditor.tsx","./src/views/mission/statement/header.tsx","./src/views/mission/statement/latextcontainer.tsx","./src/views/mission/statement/missionsubmissions.tsx","./src/views/mission/statement/statement.tsx","./src/views/mission/statement/submissionitem.tsx","./src/views/mission/submission/submission.tsx"],"errors":true,"version":"5.6.2"} \ No newline at end of file