This commit is contained in:
Archer
2023-11-09 09:46:57 +08:00
committed by GitHub
parent 661ee79943
commit 8bb5588305
402 changed files with 9899 additions and 5967 deletions

View File

@@ -26,7 +26,7 @@ import {
delOpenApiById,
putOpenApiKey
} from '@/web/support/openapi/api';
import type { EditApiKeyProps } from '@/global/support/api/openapiReq';
import type { EditApiKeyProps } from '@/global/support/openapi/api.d';
import { useQuery, useMutation } from '@tanstack/react-query';
import { useLoading } from '@/web/common/hooks/useLoading';
import dayjs from 'dayjs';

View File

@@ -0,0 +1,20 @@
import React from 'react';
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
import { Box, Flex, FlexProps } from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import { useTranslation } from 'react-i18next';
const PermissionIconText = ({
permission,
...props
}: { permission: `${PermissionTypeEnum}` } & FlexProps) => {
const { t } = useTranslation();
return PermissionTypeMap[permission] ? (
<Flex alignItems={'center'} {...props}>
<MyIcon name={PermissionTypeMap[permission]?.iconLight as any} w={'14px'} />
<Box ml={'1px'}>{t(PermissionTypeMap[permission]?.label)}</Box>
</Flex>
) : null;
};
export default PermissionIconText;

View File

@@ -0,0 +1,38 @@
import React from 'react';
import MyRadio from '@/components/Radio';
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
import { useTranslation } from 'react-i18next';
const PermissionRadio = ({
value,
onChange
}: {
value: `${PermissionTypeEnum}`;
onChange: (e: `${PermissionTypeEnum}`) => void;
}) => {
const { t } = useTranslation();
return (
<MyRadio
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)']}
list={[
{
icon: 'support/permission/privateLight',
title: t('permission.Private'),
desc: t('permission.Private Tip'),
value: PermissionTypeEnum.private
},
{
icon: 'support/permission/publicLight',
title: t('permission.Public'),
desc: t('permission.Public Tip'),
value: PermissionTypeEnum.public
}
]}
value={value}
onChange={(e) => onChange(e as `${PermissionTypeEnum}`)}
/>
);
};
export default PermissionRadio;

View File

@@ -0,0 +1,159 @@
import React, { useCallback, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImgAndUpload } from '@/web/common/file/controller';
import { useToast } from '@/web/common/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useRequest } from '@/web/common/hooks/useRequest';
import MyModal from '@/components/MyModal';
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyTooltip from '@/components/MyTooltip';
import Avatar from '@/components/Avatar';
import { postCreateTeam, putUpdateTeam } from '@/web/support/user/team/api';
import { CreateTeamProps } from '@fastgpt/global/support/user/team/controller.d';
export type FormDataType = CreateTeamProps & {
id?: string;
};
export const defaultForm = {
name: '',
avatar: '/icon/logo.svg'
};
function EditModal({
defaultData = defaultForm,
onClose,
onSuccess
}: {
defaultData?: FormDataType;
onClose: () => void;
onSuccess: () => void;
}) {
const { t } = useTranslation();
const [refresh, setRefresh] = useState(false);
const { toast } = useToast();
const { register, setValue, getValues, handleSubmit } = useForm<CreateTeamProps>({
defaultValues: defaultData
});
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png,.svg',
multiple: false
});
const onSelectFile = useCallback(
async (e: File[]) => {
const file = e[0];
if (!file) return;
try {
const src = await compressImgAndUpload({
file,
maxW: 100,
maxH: 100
});
setValue('avatar', src);
setRefresh((state) => !state);
} catch (err: any) {
toast({
title: getErrText(err, t('common.Select File Failed')),
status: 'warning'
});
}
},
[setValue, t, toast]
);
const { mutate: onclickCreate, isLoading: creating } = useRequest({
mutationFn: async (data: CreateTeamProps) => {
return postCreateTeam(data);
},
onSuccess() {
onSuccess();
onClose();
},
successToast: t('common.Create Success'),
errorToast: t('common.Create Failed')
});
const { mutate: onclickUpdate, isLoading: updating } = useRequest({
mutationFn: async (data: FormDataType) => {
if (!data.id) return Promise.resolve('');
return putUpdateTeam({
teamId: data.id,
name: data.name,
avatar: data.avatar
});
},
onSuccess() {
onSuccess();
onClose();
},
successToast: t('common.Update Success'),
errorToast: t('common.Update Failed')
});
return (
<MyModal
isOpen
onClose={onClose}
title={defaultData.id ? t('user.team.Update Team') : t('user.team.Create Team')}
>
<ModalBody>
<Box color={'myGray.800'} fontWeight={'bold'}>
{t('user.team.Set Name')}
</Box>
<Flex mt={3} alignItems={'center'}>
<MyTooltip label={t('common.Set Avatar')}>
<Avatar
flexShrink={0}
src={getValues('avatar')}
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
borderRadius={'md'}
onClick={onOpenSelectFile}
/>
</MyTooltip>
<Input
flex={1}
ml={4}
autoFocus
bg={'myWhite.600'}
maxLength={20}
placeholder={t('user.team.Team Name')}
{...register('name', {
required: t('common.Please Input Name')
})}
/>
</Flex>
</ModalBody>
<ModalFooter>
{!!defaultData.id ? (
<>
<Box flex={1} />
<Button variant={'base'} mr={3} onClick={onClose}>
{t('common.Close')}
</Button>
<Button isLoading={updating} onClick={handleSubmit((data) => onclickUpdate(data))}>
{t('common.Confirm Update')}
</Button>
</>
) : (
<Button
w={'100%'}
isLoading={creating}
onClick={handleSubmit((data) => onclickCreate(data))}
>
{t('common.Confirm Create')}
</Button>
)}
</ModalFooter>
<File onSelect={onSelectFile} />
</MyModal>
);
}
export default React.memo(EditModal);

View File

@@ -0,0 +1,106 @@
import React, { useMemo, useState } from 'react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'react-i18next';
import { ModalCloseButton, ModalBody, Box, ModalFooter, Button } from '@chakra-ui/react';
import TagTextarea from '@/components/common/Textarea/TagTextarea';
import MySelect from '@/components/Select';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useRequest } from '@/web/common/hooks/useRequest';
import { postInviteTeamMember } from '@/web/support/user/team/api';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import type { InviteMemberResponse } from '@fastgpt/global/support/user/team/controller.d';
const InviteModal = ({
teamId,
onClose,
onSuccess
}: {
teamId: string;
onClose: () => void;
onSuccess: () => void;
}) => {
const { t } = useTranslation();
const { ConfirmModal, openConfirm } = useConfirm({
title: t('user.team.Invite Member Result Tip'),
showCancel: false
});
const [inviteUsernames, setInviteUsernames] = useState<string[]>([]);
const inviteTypes = useMemo(
() => [
{
alias: t('user.team.Invite Role Visitor Alias'),
label: t('user.team.Invite Role Visitor Tip'),
value: TeamMemberRoleEnum.visitor
},
{
alias: t('user.team.Invite Role Admin Alias'),
label: t('user.team.Invite Role Admin Tip'),
value: TeamMemberRoleEnum.admin
}
],
[t]
);
const [selectedInviteType, setSelectInviteType] = useState(inviteTypes[0].value);
const { mutate: onInvite, isLoading } = useRequest({
mutationFn: () => {
return postInviteTeamMember({
teamId,
usernames: inviteUsernames,
role: selectedInviteType
});
},
onSuccess(res: InviteMemberResponse) {
onSuccess();
openConfirm(
() => onClose(),
undefined,
t('user.team.Invite Member Success Tip', {
success: res.invite.length,
inValid: res.inValid.join(', '),
inTeam: res.inTeam.join(', ')
})
)();
},
errorToast: t('user.team.Invite Member Failed Tip')
});
return (
<MyModal
isOpen
title={
<>
<Box>{t('user.team.Invite Member')}</Box>
<Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}>
{t('user.team.Invite Member Tips')}
</Box>
</>
}
maxW={['90vw', '400px']}
overflow={'unset'}
>
<ModalCloseButton onClick={onClose} />
<ModalBody>
<Box mb={2}>{t('common.Username')}</Box>
<TagTextarea defaultValues={inviteUsernames} onUpdate={setInviteUsernames} />
<Box mt={4}>
<MySelect list={inviteTypes} value={selectedInviteType} onchange={setSelectInviteType} />
</Box>
</ModalBody>
<ModalFooter>
<Button
w={'100%'}
h={'34px'}
isDisabled={inviteUsernames.length === 0}
isLoading={isLoading}
onClick={onInvite}
>
{t('user.team.Confirm Invite')}
</Button>
</ModalFooter>
<ConfirmModal />
</MyModal>
);
};
export default InviteModal;

View File

@@ -0,0 +1,436 @@
import React, { useMemo, useState } from 'react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import {
getTeamList,
getTeamMembers,
putSwitchTeam,
putUpdateMember,
delRemoveMember,
delLeaveTeam
} from '@/web/support/user/team/api';
import {
Box,
Button,
Flex,
IconButton,
Table,
Thead,
Tbody,
Tr,
Th,
Td,
TableContainer,
useTheme,
useDisclosure,
MenuButton
} from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar';
import { useUserStore } from '@/web/support/user/useUserStore';
import {
TeamMemberRoleEnum,
TeamMemberRoleMap,
TeamMemberStatusEnum,
TeamMemberStatusMap
} from '@fastgpt/global/support/user/team/constant';
import dynamic from 'next/dynamic';
import { useRequest } from '@/web/common/hooks/useRequest';
import { setToken } from '@/web/support/user/auth';
import { useLoading } from '@/web/common/hooks/useLoading';
import { FormDataType, defaultForm } from './EditModal';
import MyMenu from '@/components/MyMenu';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { useToast } from '@/web/common/hooks/useToast';
const EditModal = dynamic(() => import('./EditModal'));
const InviteModal = dynamic(() => import('./InviteModal'));
const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
const theme = useTheme();
const { t } = useTranslation();
const { Loading } = useLoading();
const { toast } = useToast();
const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm();
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
content: t('user.team.member.Confirm Leave')
});
const { userInfo, initUserInfo } = useUserStore();
const [editTeamData, setEditTeamData] = useState<FormDataType>();
const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure();
const {
data: myTeams = [],
isLoading: isLoadingTeams,
refetch: refetchTeam
} = useQuery(['getTeams', userInfo?._id], () => getTeamList(TeamMemberStatusEnum.active));
const defaultTeam = useMemo(
() => myTeams.find((item) => item.defaultTeam) || myTeams[0],
[myTeams]
);
const { mutate: onSwitchTeam, isLoading: isSwitchTeam } = useRequest({
mutationFn: async (teamId: string) => {
const token = await putSwitchTeam(teamId);
setToken(token);
return initUserInfo();
},
errorToast: t('user.team.Switch Team Failed')
});
// member action
const { data: members = [], refetch: refetchMembers } = useQuery(
['getMembers', userInfo?.team?.teamId],
() => {
if (!userInfo?.team?.teamId) return [];
return getTeamMembers(userInfo.team.teamId);
}
);
const { mutate: onUpdateMember, isLoading: isLoadingUpdateMember } = useRequest({
mutationFn: putUpdateMember,
onSuccess() {
refetchMembers();
}
});
const { mutate: onRemoveMember, isLoading: isLoadingRemoveMember } = useRequest({
mutationFn: delRemoveMember,
onSuccess() {
refetchMembers();
}
});
const { mutate: onLeaveTeam, isLoading: isLoadingLeaveTeam } = useRequest({
mutationFn: async (teamId?: string) => {
if (!teamId) return;
// change to personal team
await onSwitchTeam(defaultTeam.teamId);
return delLeaveTeam(teamId);
},
onSuccess() {
refetchTeam();
},
errorToast: t('user.team.Leave Team Failed')
});
return !!userInfo?.team ? (
<>
<MyModal
isOpen
onClose={onClose}
maxW={['90vw', '1000px']}
w={'100%'}
h={'550px'}
isCentered
bg={'myWhite.600'}
overflow={'hidden'}
>
<Box display={['block', 'flex']} flex={1} position={'relative'} overflow={'auto'}>
{/* teams */}
<Flex
flexDirection={'column'}
w={['auto', '270px']}
h={['auto', '100%']}
pt={3}
px={5}
mb={[2, 0]}
>
<Flex
alignItems={'center'}
py={2}
h={'40px'}
borderBottom={'1.5px solid rgba(0, 0, 0, 0.05)'}
>
<Box flex={['0 0 auto', 1]} fontWeight={'bold'} fontSize={['md', 'lg']}>
{t('common.Team')}
</Box>
{myTeams.length < 1 && (
<IconButton
variant={'ghost'}
border={'none'}
icon={
<MyIcon
name={'addCircle'}
w={['16px', '18px']}
color={'myBlue.600'}
cursor={'pointer'}
/>
}
aria-label={''}
onClick={() => setEditTeamData(defaultForm)}
/>
)}
</Flex>
<Box flex={['auto', '1 0 0']} overflow={'auto'}>
{myTeams.map((team) => (
<Flex
key={team.teamId}
alignItems={'center'}
mt={3}
borderRadius={'md'}
p={3}
cursor={'default'}
gap={3}
{...(userInfo?.team?.teamId === team.teamId
? {
bg: 'myBlue.300'
}
: {
_hover: {
bg: 'myGray.100'
}
})}
>
<Avatar src={team.avatar} w={['18px', '22px']} />
<Box
flex={'1 0 0'}
w={0}
{...(team.role === TeamMemberRoleEnum.owner
? {
fontWeight: 'bold'
}
: {})}
>
{team.teamName}
</Box>
{userInfo?.team?.teamId === team.teamId ? (
<MyIcon name={'common/tickFill'} w={'16px'} color={'myBlue.600'} />
) : (
<Button size={'xs'} variant={'base'} onClick={() => onSwitchTeam(team.teamId)}>
{t('user.team.Check Team')}
</Button>
)}
</Flex>
))}
</Box>
</Flex>
{/* team card */}
<Flex
flexDirection={'column'}
flex={'1'}
h={['auto', '100%']}
bg={'white'}
minH={['50vh', 'auto']}
borderRadius={['8px 8px 0 0', '8px 0 0 8px']}
>
<Flex
alignItems={'center'}
px={5}
py={4}
borderBottom={'1.5px solid'}
borderBottomColor={'myGray.100'}
mb={3}
>
<Box fontSize={['lg', 'xl']} fontWeight={'bold'}>
{userInfo.team.teamName}
</Box>
{userInfo.team.role === TeamMemberRoleEnum.owner && (
<MyIcon
name="edit"
w={'14px'}
ml={2}
cursor={'pointer'}
_hover={{
color: 'myBlue.600'
}}
onClick={() => {
if (!userInfo?.team) return;
setEditTeamData({
id: userInfo.team.teamId,
name: userInfo.team.teamName,
avatar: userInfo.team.avatar
});
}}
/>
)}
</Flex>
<Flex px={5} alignItems={'center'}>
<MyIcon name="support/team/memberLight" w={'14px'} />
<Box ml={1}>{t('user.team.Member')}</Box>
<Box ml={2} bg={'myGray.100'} borderRadius={'20px'} px={3} fontSize={'xs'}>
{members.length}
</Box>
{userInfo.team.role === TeamMemberRoleEnum.owner && (
<Button
variant={'base'}
size="sm"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'common/inviteLight'} w={'14px'} color={'myBlue.600'} />}
onClick={() => {
if (userInfo.team.maxSize <= members.length) {
toast({
status: 'warning',
title: t('user.team.Over Max Member Tip', { max: userInfo.team.maxSize })
});
} else {
onOpenInvite();
}
}}
>
{t('user.team.Invite Member')}
</Button>
)}
<Box flex={1} />
{userInfo.team.role !== TeamMemberRoleEnum.owner && (
<Button
variant={'base'}
size="sm"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'loginoutLight'} w={'14px'} color={'myBlue.600'} />}
onClick={() => {
openLeaveConfirm(() => onLeaveTeam(userInfo?.team?.teamId))();
}}
>
{t('user.team.Leave Team')}
</Button>
)}
</Flex>
<Box mt={3} flex={'1 0 0'} overflow={'auto'}>
<TableContainer overflow={'unset'}>
<Table overflow={'unset'}>
<Thead bg={'myWhite.400'}>
<Tr>
<Th>{t('common.Username')}</Th>
<Th>{t('user.team.Role')}</Th>
<Th>{t('common.Status')}</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{members.map((item) => (
<Tr key={item.userId} overflow={'unset'}>
<Td display={'flex'} alignItems={'center'}>
<Avatar src={item.avatar} w={['18px', '22px']} />
<Box flex={'1 0 0'} w={0} ml={1} className={'textEllipsis'}>
{item.memberUsername}
</Box>
</Td>
<Td>{t(TeamMemberRoleMap[item.role]?.label || '')}</Td>
<Td color={TeamMemberStatusMap[item.status].color}>
{t(TeamMemberStatusMap[item.status]?.label || '')}
</Td>
<Td>
{userInfo?.team?.role === TeamMemberRoleEnum.owner &&
item.role !== TeamMemberRoleEnum.owner && (
<MyMenu
width={20}
Button={
<MenuButton
_hover={{
bg: 'myWhite.600'
}}
px={2}
py={1}
lineHeight={1}
>
<MyIcon
name={'edit'}
cursor={'pointer'}
w="14px"
_hover={{ color: 'myBlue.600' }}
/>
</MenuButton>
}
menuList={[
{
isActive: item.role === TeamMemberRoleEnum.visitor,
child: t('user.team.Invite Role Visitor Tip'),
onClick: () => {
onUpdateMember({
teamId: item.teamId,
memberId: item.tmbId,
role: TeamMemberRoleEnum.visitor
});
}
},
{
isActive: item.role === TeamMemberRoleEnum.admin,
child: t('user.team.Invite Role Admin Tip'),
onClick: () => {
onUpdateMember({
teamId: item.teamId,
memberId: item.tmbId,
role: TeamMemberRoleEnum.admin
});
}
},
...(item.status === TeamMemberStatusEnum.reject
? [
{
child: t('user.team.Reinvite'),
onClick: () => {
onUpdateMember({
teamId: item.teamId,
memberId: item.tmbId,
status: TeamMemberStatusEnum.waiting
});
}
}
]
: []),
{
child: t('user.team.Remove Member Tip'),
onClick: () =>
openRemoveMember(
() =>
onRemoveMember({
teamId: item.teamId,
memberId: item.tmbId
}),
undefined,
t('user.team.Remove Member Confirm Tip', {
username: item.memberUsername
})
)()
}
]}
/>
)}
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Box>
</Flex>
<Loading
loading={
isSwitchTeam ||
isLoadingTeams ||
isLoadingUpdateMember ||
isLoadingRemoveMember ||
isLoadingLeaveTeam
}
fixed={false}
/>
</Box>
</MyModal>
{!!editTeamData && (
<EditModal
defaultData={editTeamData}
onClose={() => setEditTeamData(undefined)}
onSuccess={() => {
refetchTeam();
initUserInfo();
}}
/>
)}
{isOpenInvite && userInfo?.team?.teamId && (
<InviteModal
teamId={userInfo.team.teamId}
onClose={onCloseInvite}
onSuccess={refetchMembers}
/>
)}
<ConfirmRemoveMemberModal />
<ConfirmLeaveTeamModal />
</>
) : null;
};
export default React.memo(TeamManageModal);

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { Box, Button, Flex, Image, useDisclosure, useTheme } from '@chakra-ui/react';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useTranslation } from 'react-i18next';
import MyTooltip from '@/components/MyTooltip';
import dynamic from 'next/dynamic';
import { feConfigs } from '@/web/common/system/staticData';
import { useToast } from '@/web/common/hooks/useToast';
const TeamManageModal = dynamic(() => import('../TeamManageModal'));
const TeamMenu = () => {
const theme = useTheme();
const { t } = useTranslation();
const { userInfo } = useUserStore();
const { toast } = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Button
variant={'base'}
userSelect={'none'}
w={'100%'}
display={'block'}
h={'34px'}
px={3}
css={{
'& span': {
display: 'block'
}
}}
transform={'none !important'}
onClick={() => {
if (feConfigs.isPlus) {
onOpen();
} else {
toast({
status: 'warning',
title: t('common.Business edition features')
});
}
}}
>
<MyTooltip label={t('user.team.Select Team')}>
<Flex w={'100%'} alignItems={'center'}>
{userInfo?.team ? (
<>
<Image src={userInfo.team.avatar} alt={''} w={'16px'} />
<Box ml={2}>{userInfo.team.teamName}</Box>
</>
) : (
<>
<Box w={'8px'} h={'8px'} mr={3} borderRadius={'50%'} bg={'#67c13b'} />
{t('user.team.Personal Team')}
</>
)}
</Flex>
</MyTooltip>
{isOpen && <TeamManageModal onClose={onClose} />}
</Button>
);
};
export default TeamMenu;

View File

@@ -0,0 +1,133 @@
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import MyModal from '@/components/MyModal';
import {
Button,
ModalFooter,
useDisclosure,
ModalBody,
Flex,
Box,
useTheme
} from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { getTeamList, updateInviteResult } from '@/web/support/user/team/api';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
import Avatar from '@/components/Avatar';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useToast } from '@/web/common/hooks/useToast';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { feConfigs } from '@/web/common/system/staticData';
const UpdateInviteModal = () => {
const { t } = useTranslation();
const theme = useTheme();
const { toast } = useToast();
const { ConfirmModal, openConfirm } = useConfirm({});
const { data: inviteList = [], refetch } = useQuery(['getInviteList'], () =>
feConfigs.isPlus ? getTeamList(TeamMemberStatusEnum.waiting) : []
);
const { mutate: onAccept, isLoading: isLoadingAccept } = useRequest({
mutationFn: updateInviteResult,
onSuccess() {
toast({
status: 'success',
title: t('user.team.invite.Accepted')
});
refetch();
}
});
const { mutate: onReject, isLoading: isLoadingReject } = useRequest({
mutationFn: updateInviteResult,
onSuccess() {
toast({
status: 'success',
title: t('user.team.invite.Reject')
});
refetch();
}
});
return (
<MyModal
isOpen={inviteList.length > 0}
title={
<>
<Box>{t('user.team.Processing invitations')}</Box>
<Box fontWeight={'normal'} fontSize={'sm'} color={'myGray.500'}>
{t('user.team.Processing invitations Tips', { amount: inviteList.length })}
</Box>
</>
}
maxW={['90vw', '500px']}
>
<ModalBody>
{inviteList.map((item) => (
<Flex
key={item.teamId}
alignItems={'center'}
border={theme.borders.base}
borderRadius={'md'}
px={3}
py={2}
_notFirst={{
mt: 3
}}
>
<Avatar src={item.avatar} w={['16px', '23px']} />
<Box mx={2}>{item.teamName}</Box>
<Box flex={1} />
<Button
size="sm"
variant={'solid'}
colorScheme="green"
isLoading={isLoadingAccept}
onClick={() => {
openConfirm(
() =>
onAccept({
tmbId: item.tmbId,
status: TeamMemberStatusEnum.active
}),
undefined,
t('user.team.invite.Accept Confirm')
)();
}}
>
{t('user.team.invite.accept')}
</Button>
<Button
size="sm"
ml={2}
variant={'solid'}
colorScheme="red"
isLoading={isLoadingReject}
onClick={() => {
openConfirm(
() =>
onReject({
tmbId: item.tmbId,
status: TeamMemberStatusEnum.reject
}),
undefined,
t('user.team.invite.Reject Confirm')
)();
}}
>
{t('user.team.invite.reject')}
</Button>
</Flex>
))}
</ModalBody>
<ModalFooter justifyContent={'center'}>
<Box>{t('user.team.invite.Deal Width Footer Tip')}</Box>
</ModalFooter>
<ConfirmModal />
</MyModal>
);
};
export default React.memo(UpdateInviteModal);