user role controller
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import Cup from './cup.svg';
|
import Cup from './cup.svg';
|
||||||
import Home from './home.svg';
|
import Home from './home.svg';
|
||||||
import MessageChat from './message-chat.svg';
|
import MessageChat from './message-chat.svg';
|
||||||
|
import Logout from './logout.svg';
|
||||||
|
|
||||||
export { Cup, MessageChat, Home };
|
export { Cup, MessageChat, Home, Logout };
|
||||||
|
|||||||
3
src/assets/icons/group/logout.svg
Normal file
3
src/assets/icons/group/logout.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.79562 4H5.1075C4.54856 4 4.01251 4.21071 3.61727 4.58579C3.22204 4.96086 3 5.46957 3 6V18C3 18.5304 3.22204 19.0391 3.61727 19.4142C4.01251 19.7893 4.54856 20 5.1075 20H8.79562M9.0575 12.0007L21 12.0007M21 12.0007L16.4368 7.42931M21 12.0007L16.4368 16.5722" stroke="#EDF6F7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 456 B |
Binary file not shown.
|
Before Width: | Height: | Size: 8.9 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { cn } from '../../lib/cn';
|
import { cn } from '../../lib/cn';
|
||||||
import { checkMark, chevroneDropDownList } from '../../assets/icons/input';
|
import { checkMark, chevroneDropDownList } from '../../assets/icons/input';
|
||||||
import { useClickOutside } from '../../hooks/useClickOutside';
|
import { useClickOutside } from '../../hooks/useClickOutside';
|
||||||
@@ -14,6 +14,7 @@ interface DropDownListProps {
|
|||||||
onChange: (state: string) => void;
|
onChange: (state: string) => void;
|
||||||
defaultState?: DropDownListItem;
|
defaultState?: DropDownListItem;
|
||||||
items: DropDownListItem[];
|
items: DropDownListItem[];
|
||||||
|
weight?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DropDownList: React.FC<DropDownListProps> = ({
|
export const DropDownList: React.FC<DropDownListProps> = ({
|
||||||
@@ -22,6 +23,7 @@ export const DropDownList: React.FC<DropDownListProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
defaultState,
|
defaultState,
|
||||||
items = [{ text: '', value: '' }],
|
items = [{ text: '', value: '' }],
|
||||||
|
weight = 'w-[180px]',
|
||||||
}) => {
|
}) => {
|
||||||
if (items.length == 0) items.push({ text: '', value: '' });
|
if (items.length == 0) items.push({ text: '', value: '' });
|
||||||
|
|
||||||
@@ -38,13 +40,19 @@ export const DropDownList: React.FC<DropDownListProps> = ({
|
|||||||
setActive(false);
|
setActive(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(defaultState != undefined ? defaultState : items[0]);
|
||||||
|
}, [defaultState]);
|
||||||
|
|
||||||
|
console.log(defaultState, items);
|
||||||
return (
|
return (
|
||||||
<div className={cn('relative', className)} ref={ref}>
|
<div className={cn('relative', className)} ref={ref}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
' flex items-center h-[40px] rounded-[10px] bg-liquid-lighter px-[16px] w-[180px]',
|
' flex items-center h-[40px] rounded-[10px] bg-liquid-lighter px-[16px]',
|
||||||
'text-[18px] font-bold cursor-pointer select-none',
|
'text-[18px] font-bold cursor-pointer select-none',
|
||||||
'transitin-all active:scale-95 duration-300',
|
'transitin-all active:scale-95 duration-300',
|
||||||
|
weight,
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActive(!active);
|
setActive(!active);
|
||||||
@@ -63,8 +71,9 @@ export const DropDownList: React.FC<DropDownListProps> = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
' absolute rounded-[10px] bg-liquid-lighter w-[180px] left-0 top-[48px] z-50 transition-all duration-300',
|
' absolute rounded-[10px] bg-liquid-lighter left-0 top-[48px] z-50 transition-all duration-300',
|
||||||
'grid overflow-hidden',
|
'grid overflow-hidden',
|
||||||
|
weight,
|
||||||
active
|
active
|
||||||
? 'grid-rows-[1fr] opacity-100'
|
? 'grid-rows-[1fr] opacity-100'
|
||||||
: 'grid-rows-[0fr] opacity-0',
|
: 'grid-rows-[0fr] opacity-0',
|
||||||
|
|||||||
56
src/components/modal/ConfirmModal.tsx
Normal file
56
src/components/modal/ConfirmModal.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { FC, useEffect } from 'react';
|
||||||
|
import { Modal } from './Modal';
|
||||||
|
import { PrimaryButton } from '../../components/button/PrimaryButton';
|
||||||
|
import { SecondaryButton } from '../../components/button/SecondaryButton';
|
||||||
|
|
||||||
|
interface ConfirmModalProps {
|
||||||
|
active: boolean;
|
||||||
|
setActive: (value: boolean) => void;
|
||||||
|
onConfirmClick: () => void;
|
||||||
|
title?: string;
|
||||||
|
message?: string;
|
||||||
|
confirmColor?: 'primary' | 'secondary' | 'error' | 'warning' | 'success';
|
||||||
|
confirmText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfirmModal: FC<ConfirmModalProps> = ({
|
||||||
|
active,
|
||||||
|
setActive,
|
||||||
|
onConfirmClick,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
confirmColor = 'secondary',
|
||||||
|
confirmText = 'Ок',
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="bg-liquid-background border-liquid-lighter border-[2px] p-[25px] rounded-[20px] text-liquid-white fixed top-0 left-0"
|
||||||
|
onOpenChange={setActive}
|
||||||
|
open={active}
|
||||||
|
backdrop="blur"
|
||||||
|
>
|
||||||
|
<div className="w-[500px]">
|
||||||
|
<div className="font-bold text-[30px]">{title}</div>
|
||||||
|
<div className="font-bold text-[20px] mt-[20px]">{message}</div>
|
||||||
|
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => {
|
||||||
|
onConfirmClick();
|
||||||
|
setActive(false);
|
||||||
|
}}
|
||||||
|
text={confirmText}
|
||||||
|
color={confirmColor}
|
||||||
|
/>
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setActive(false);
|
||||||
|
}}
|
||||||
|
text="Отмена"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmModal;
|
||||||
@@ -47,7 +47,7 @@ export const Modal: React.FC<ModalProps> = ({
|
|||||||
exit={modalbgVariants.closed}
|
exit={modalbgVariants.closed}
|
||||||
transition={{ duration: 0.15 }}
|
transition={{ duration: 0.15 }}
|
||||||
className={cn(
|
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 == 'blur' && open && 'backdrop-blur-sm',
|
||||||
backdrop == 'opaque' &&
|
backdrop == 'opaque' &&
|
||||||
open &&
|
open &&
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import Account from '../views/home/account/Account';
|
|||||||
import ProtectedRoute from '../components/router/ProtectedRoute';
|
import ProtectedRoute from '../components/router/ProtectedRoute';
|
||||||
import { MissionsRightPanel } from '../views/home/rightpanel/Missions';
|
import { MissionsRightPanel } from '../views/home/rightpanel/Missions';
|
||||||
import { ArticlesRightPanel } from '../views/home/rightpanel/Articles';
|
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 GroupInvite from '../views/home/groupinviter/GroupInvite';
|
||||||
import {
|
import {
|
||||||
toastError,
|
toastError,
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ const Group: FC<GroupsBlockProps> = () => {
|
|||||||
dispatch(fetchGroupById(groupId));
|
dispatch(fetchGroupById(groupId));
|
||||||
}, [groupId]);
|
}, [groupId]);
|
||||||
|
|
||||||
console.log(group);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -8,5 +8,10 @@ export const Chat = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setMenuActiveGroupPage('chat'));
|
dispatch(setMenuActiveGroupPage('chat'));
|
||||||
}, []);
|
}, []);
|
||||||
return <></>;
|
return (
|
||||||
|
<div className="h-full overflow-y-scroll thin-dark-scrollbar flex items-center justify-center font-bold text-liquid-white text-[50px]">
|
||||||
|
{' '}
|
||||||
|
Пока пусто :(
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,5 +8,10 @@ export const Contests = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setMenuActiveGroupPage('contests'));
|
dispatch(setMenuActiveGroupPage('contests'));
|
||||||
}, []);
|
}, []);
|
||||||
return <></>;
|
return (
|
||||||
|
<div className="h-full overflow-y-scroll thin-dark-scrollbar flex items-center justify-center font-bold text-liquid-white text-[50px]">
|
||||||
|
{' '}
|
||||||
|
Пока пусто :(
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Modal } from '../../../components/modal/Modal';
|
|||||||
import { PrimaryButton } from '../../../components/button/PrimaryButton';
|
import { PrimaryButton } from '../../../components/button/PrimaryButton';
|
||||||
import { SecondaryButton } from '../../../components/button/SecondaryButton';
|
import { SecondaryButton } from '../../../components/button/SecondaryButton';
|
||||||
import { Input } from '../../../components/input/Input';
|
import { Input } from '../../../components/input/Input';
|
||||||
|
import { toastSuccess } from '../../../lib/toastNotification';
|
||||||
|
|
||||||
interface ModalInviteProps {
|
interface ModalInviteProps {
|
||||||
active: boolean;
|
active: boolean;
|
||||||
@@ -51,6 +52,7 @@ const ModalInvite: FC<ModalInviteProps> = ({
|
|||||||
if (!inviteLink) return;
|
if (!inviteLink) return;
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(inviteLink);
|
await navigator.clipboard.writeText(inviteLink);
|
||||||
|
toastSuccess('Приглашение скопировано в буфер обмена!');
|
||||||
setActive(false);
|
setActive(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Не удалось скопировать ссылку:', err);
|
console.error('Не удалось скопировать ссылку:', err);
|
||||||
|
|||||||
@@ -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 (
|
|
||||||
<div className="h-screen w-full overflow-y-scroll thin-dark-scrollbar p-[20px] gap-[5px] flex flex-col">
|
|
||||||
<div className="text-liquid-white font-bold text-[18px]">
|
|
||||||
Пользователи
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{items.map((v, i) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{
|
|
||||||
<div className="text-liquid-light text-[16px] grid grid-cols-[40px,1fr] gap-[10px] items-center cursor-pointer hover:bg-liquid-lighter transition-all duration-300 rounded-[10px] p-[5px]">
|
|
||||||
<div className="h-[40px] w-[40px] rounded-[10px] bg-[#D9D9D9]"></div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="text-liquid-white font-bold text-[16px] leading-5">
|
|
||||||
{v.name}
|
|
||||||
</div>
|
|
||||||
<div className="text-liquid-light font-normal text-[16px] leading-5">
|
|
||||||
{v.role}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{i + 1 != items.length && (
|
|
||||||
<div className="h-[1px] w-full bg-liquid-lighter"></div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
126
src/views/home/rightpanel/group/Group.tsx
Normal file
126
src/views/home/rightpanel/group/Group.tsx
Normal file
@@ -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 <Navigate to="/home/groups" replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const [user, setUser] = useState<GroupMember | undefined>();
|
||||||
|
const [myUser, setMyUser] = useState<GroupMember | undefined>();
|
||||||
|
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||||
|
const [modalLeaveActive, setModalLeaveActive] = useState<boolean>(false);
|
||||||
|
const [modalUpdateActive, setModalUpdateActive] = useState<boolean>(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 (
|
||||||
|
<div className="h-screen w-full overflow-y-scroll thin-dark-scrollbar p-[20px] gap-[5px] flex flex-col">
|
||||||
|
<div className="text-liquid-white font-bold text-[18px]">
|
||||||
|
Пользователи
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{group?.members.map((v, i) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
<div className="text-liquid-light text-[16px] grid grid-cols-[40px,1fr] gap-[10px] items-center cursor-pointer hover:bg-liquid-lighter transition-all duration-300 rounded-[10px] p-[5px] group">
|
||||||
|
<div className="h-[40px] w-[40px] rounded-[10px] bg-[#D9D9D9]"></div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="text-liquid-white font-bold text-[16px] leading-5">
|
||||||
|
{v.username}
|
||||||
|
</div>
|
||||||
|
<div className="text-liquid-light font-normal text-[16px] leading-5">
|
||||||
|
{v.role +
|
||||||
|
(Number(userId) == v.userId
|
||||||
|
? ' (Вы)'
|
||||||
|
: '')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(isAdmin || Number(userId) == v.userId) &&
|
||||||
|
!v.role.includes('Creator') && (
|
||||||
|
<div
|
||||||
|
className="h-[34px] w-[34px] absolute right-[34px] opacity-0 group-hover:opacity-100 transition-all duration-300 hover:bg-liquid-light rounded-[10px] p-[5px] active:scale-90"
|
||||||
|
onClick={() => {
|
||||||
|
if (
|
||||||
|
Number(userId) == v.userId
|
||||||
|
) {
|
||||||
|
setModalLeaveActive(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isAdmin) {
|
||||||
|
setUser(v);
|
||||||
|
setModalUpdateActive(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Number(userId) == v.userId ? (
|
||||||
|
<img src={Logout} />
|
||||||
|
) : isAdmin ? (
|
||||||
|
<img src={Edit} />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{i + 1 != group?.members.length && (
|
||||||
|
<div className="h-[1px] w-full bg-liquid-lighter"></div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<ModalLeave
|
||||||
|
groupId={groupId}
|
||||||
|
groupName={group?.name}
|
||||||
|
userId={Number(userId)}
|
||||||
|
active={modalLeaveActive}
|
||||||
|
setActive={setModalLeaveActive}
|
||||||
|
/>
|
||||||
|
<ModalUpdate
|
||||||
|
groupId={groupId}
|
||||||
|
groupName={group?.name}
|
||||||
|
userId={Number(userId)}
|
||||||
|
user={user}
|
||||||
|
adminUser={myUser}
|
||||||
|
active={modalUpdateActive}
|
||||||
|
setActive={setModalUpdateActive}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
107
src/views/home/rightpanel/group/ModalLeave.tsx
Normal file
107
src/views/home/rightpanel/group/ModalLeave.tsx
Normal file
@@ -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<ModalLeaveProps> = ({
|
||||||
|
active,
|
||||||
|
setActive,
|
||||||
|
groupName,
|
||||||
|
groupId,
|
||||||
|
userId,
|
||||||
|
}) => {
|
||||||
|
const statusLeave = useAppSelector(
|
||||||
|
(state) => state.groups.removeGroupMember.status,
|
||||||
|
);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [modalConfirmActive, setModalConfirmActive] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (statusLeave == 'successful') {
|
||||||
|
dispatch(
|
||||||
|
setGroupsStatus({ key: 'removeGroupMember', status: 'idle' }),
|
||||||
|
);
|
||||||
|
setActive(false);
|
||||||
|
navigate('/home/groups');
|
||||||
|
}
|
||||||
|
}, [statusLeave]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
className="bg-liquid-background border-liquid-lighter border-[2px] p-[25px] rounded-[20px] text-liquid-white"
|
||||||
|
onOpenChange={setActive}
|
||||||
|
open={active}
|
||||||
|
backdrop="blur"
|
||||||
|
>
|
||||||
|
<div className="w-[500px]">
|
||||||
|
<div className="font-bold text-[30px]">
|
||||||
|
Вы действительно хотите покинуть группу:
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-[20px] mt-[20px]">
|
||||||
|
"{groupName}" #{groupId}?
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setModalConfirmActive(true);
|
||||||
|
}}
|
||||||
|
text={
|
||||||
|
statusLeave == 'loading'
|
||||||
|
? 'Покинуть...'
|
||||||
|
: 'Покинуть'
|
||||||
|
}
|
||||||
|
disabled={statusLeave == 'loading'}
|
||||||
|
color="error"
|
||||||
|
/>
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setActive(false);
|
||||||
|
}}
|
||||||
|
text="Отмена"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ConfirmModal
|
||||||
|
active={modalConfirmActive}
|
||||||
|
setActive={setModalConfirmActive}
|
||||||
|
title="Подтвердите действия"
|
||||||
|
message="Вы действительно хотите покинуть группу?"
|
||||||
|
confirmColor="error"
|
||||||
|
confirmText="Покинуть"
|
||||||
|
onConfirmClick={() => {
|
||||||
|
dispatch(
|
||||||
|
removeGroupMember({
|
||||||
|
groupId,
|
||||||
|
memberId: userId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalLeave;
|
||||||
208
src/views/home/rightpanel/group/ModalUpdate.tsx
Normal file
208
src/views/home/rightpanel/group/ModalUpdate.tsx
Normal file
@@ -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<ModalUpdateProps> = ({
|
||||||
|
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<boolean>(false);
|
||||||
|
const [modalConfirmRoleActive, setModalConfirmRoleActive] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
const [userRole, setUserRole] = useState<string>('');
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Modal
|
||||||
|
className="bg-liquid-background border-liquid-lighter border-[2px] p-[25px] rounded-[20px] text-liquid-white"
|
||||||
|
onOpenChange={setActive}
|
||||||
|
open={active}
|
||||||
|
backdrop="blur"
|
||||||
|
>
|
||||||
|
<div className="w-[500px]">
|
||||||
|
<div className="font-bold text-[30px]">
|
||||||
|
Управление участниками группы:
|
||||||
|
</div>
|
||||||
|
<div className="font-bold text-[20px]">
|
||||||
|
"{groupName}" #{groupId}
|
||||||
|
</div>
|
||||||
|
<div className="my-[5px]">Пользователь: {user?.username}</div>
|
||||||
|
<div>Текущая роль: {user?.role}</div>
|
||||||
|
<div className="flex flex-row w-full items-center justify-between mt-[20px] gap-[20px]">
|
||||||
|
<div>
|
||||||
|
<DropDownList
|
||||||
|
defaultState={{ value: userRole, text: userRole }}
|
||||||
|
weight="w-[230px]"
|
||||||
|
items={roles.map((v) => {
|
||||||
|
return { text: v, value: v };
|
||||||
|
})}
|
||||||
|
onChange={(v) => {
|
||||||
|
setUserRole(v);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setModalConfirmRoleActive(true);
|
||||||
|
}}
|
||||||
|
text={
|
||||||
|
statusUpdate == 'loading'
|
||||||
|
? 'Назначить...'
|
||||||
|
: 'Назначить'
|
||||||
|
}
|
||||||
|
disabled={statusUpdate == 'loading'}
|
||||||
|
color="secondary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row w-full items-center justify-between mt-[20px] gap-[20px]">
|
||||||
|
<div className="font-bold text-[24px]">
|
||||||
|
Исключить пользователя?
|
||||||
|
</div>
|
||||||
|
<ReverseButton
|
||||||
|
onClick={() => {
|
||||||
|
setModalConfirmDeleteUser(true);
|
||||||
|
}}
|
||||||
|
text={
|
||||||
|
statusLeave == 'loading'
|
||||||
|
? 'Исключить...'
|
||||||
|
: 'Исключить'
|
||||||
|
}
|
||||||
|
disabled={statusLeave == 'loading'}
|
||||||
|
color="error"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setActive(false);
|
||||||
|
}}
|
||||||
|
text="Отмена"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
active={modalConfirmDeleteUser}
|
||||||
|
setActive={setModalConfirmDeleteUser}
|
||||||
|
title="Подтвердите действия"
|
||||||
|
message={`Вы действительно хотите исключить пользователя ${user?.username}?`}
|
||||||
|
confirmColor="error"
|
||||||
|
confirmText="Исключить"
|
||||||
|
onConfirmClick={() => {
|
||||||
|
if (user) {
|
||||||
|
dispatch(
|
||||||
|
removeGroupMember({
|
||||||
|
groupId,
|
||||||
|
memberId: user.userId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
active={modalConfirmRoleActive}
|
||||||
|
setActive={setModalConfirmRoleActive}
|
||||||
|
title="Подтвердите действия"
|
||||||
|
message={`Вы действительно хотите назначить пользователя ${user?.username} в качестве ${userRole}?`}
|
||||||
|
confirmText="Назначить"
|
||||||
|
onConfirmClick={() => {
|
||||||
|
if (user) {
|
||||||
|
dispatch(
|
||||||
|
addGroupMember({
|
||||||
|
groupId,
|
||||||
|
userId: user.userId,
|
||||||
|
role:
|
||||||
|
userRole == 'Creator'
|
||||||
|
? 'Administrator, Creator'
|
||||||
|
: userRole,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalUpdate;
|
||||||
@@ -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"}
|
{"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"}
|
||||||
Reference in New Issue
Block a user