refactor: team permission manager (#3535)
* perf: classify org, group and member * refactor: team per manager * fix: missing functions
This commit is contained in:
@@ -33,3 +33,27 @@ export type UpdatePermissionBody = {
|
|||||||
groupId: string;
|
groupId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export type CreatePermissionBody = {
|
||||||
|
tmbId: string[];
|
||||||
|
groupId: string[];
|
||||||
|
orgId: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeletePermissionQuery = RequireOnlyOne<{
|
||||||
|
tmbId?: string;
|
||||||
|
groupId?: string;
|
||||||
|
orgId?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type TeamClbsListType = {
|
||||||
|
permission: number;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ListPermissionResponse = {
|
||||||
|
tmb: (TeamClbsListType & { tmbId: string })[];
|
||||||
|
group: (TeamClbsListType & { groupId: string })[];
|
||||||
|
org: (TeamClbsListType & { orgId: string })[];
|
||||||
|
};
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ export async function getResourceAllClbs({
|
|||||||
resourceId,
|
resourceId,
|
||||||
groupId: {
|
groupId: {
|
||||||
$exists: false
|
$exists: false
|
||||||
|
},
|
||||||
|
orgId: {
|
||||||
|
$exists: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
"team.group.set_as_admin": "Set as administrator",
|
"team.group.set_as_admin": "Set as administrator",
|
||||||
"team.group.toast.can_not_delete_owner": "Owner cannot be deleted, please transfer first",
|
"team.group.toast.can_not_delete_owner": "Owner cannot be deleted, please transfer first",
|
||||||
"team.group.transfer_owner": "transfer owner",
|
"team.group.transfer_owner": "transfer owner",
|
||||||
|
"team.org.org": "Organization",
|
||||||
"team.manage_collaborators": "Manage Collaborators",
|
"team.manage_collaborators": "Manage Collaborators",
|
||||||
"team.no_collaborators": "No Collaborators",
|
"team.no_collaborators": "No Collaborators",
|
||||||
"team.write_role_member": "",
|
"team.write_role_member": "",
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
"team.group.set_as_admin": "设为管理员",
|
"team.group.set_as_admin": "设为管理员",
|
||||||
"team.group.toast.can_not_delete_owner": "不能删除所有者, 请先转让",
|
"team.group.toast.can_not_delete_owner": "不能删除所有者, 请先转让",
|
||||||
"team.group.transfer_owner": "转让所有者",
|
"team.group.transfer_owner": "转让所有者",
|
||||||
|
"team.org.org": "组织",
|
||||||
"team.manage_collaborators": "管理协作者",
|
"team.manage_collaborators": "管理协作者",
|
||||||
"team.no_collaborators": "暂无协作者",
|
"team.no_collaborators": "暂无协作者",
|
||||||
"team.write_role_member": "可写权限",
|
"team.write_role_member": "可写权限",
|
||||||
|
|||||||
@@ -106,6 +106,7 @@
|
|||||||
"team.group.set_as_admin": "設為管理員",
|
"team.group.set_as_admin": "設為管理員",
|
||||||
"team.group.toast.can_not_delete_owner": "無法刪除擁有者,請先轉移擁有權",
|
"team.group.toast.can_not_delete_owner": "無法刪除擁有者,請先轉移擁有權",
|
||||||
"team.group.transfer_owner": "轉移擁有者",
|
"team.group.transfer_owner": "轉移擁有者",
|
||||||
|
"team.org.org": "組織",
|
||||||
"team.manage_collaborators": "管理協作者",
|
"team.manage_collaborators": "管理協作者",
|
||||||
"team.no_collaborators": "目前沒有協作者",
|
"team.no_collaborators": "目前沒有協作者",
|
||||||
"team.write_role_member": "可寫入權限",
|
"team.write_role_member": "可寫入權限",
|
||||||
|
|||||||
@@ -1,399 +1,11 @@
|
|||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
|
||||||
import { ChevronDownIcon } from '@chakra-ui/icons';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Flex,
|
|
||||||
Grid,
|
|
||||||
HStack,
|
|
||||||
ModalBody,
|
|
||||||
ModalFooter
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
|
||||||
import MyAvatar from '@fastgpt/web/components/common/Avatar';
|
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
|
||||||
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
|
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import { useMemo, useState } from 'react';
|
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import PermissionSelect from './PermissionSelect';
|
|
||||||
import PermissionTags from './PermissionTags';
|
|
||||||
import { CollaboratorContext } from './context';
|
import { CollaboratorContext } from './context';
|
||||||
|
import { AddModalPropsType } from './MemberModal';
|
||||||
export type AddModalPropsType = {
|
import MemberModal from './MemberModal';
|
||||||
onClose: () => void;
|
|
||||||
mode?: 'member' | 'all';
|
|
||||||
};
|
|
||||||
|
|
||||||
function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
||||||
const { t } = useTranslation();
|
const context = useContextSelector(CollaboratorContext, (v) => v);
|
||||||
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups, loadAndGetOrgs, myOrgs } =
|
return <MemberModal onClose={onClose} mode={mode} collaboratorContext={context} />;
|
||||||
useUserStore();
|
|
||||||
|
|
||||||
const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList, permission } =
|
|
||||||
useContextSelector(CollaboratorContext, (v) => v);
|
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
|
||||||
|
|
||||||
const { data: [members = [], groups = [], orgs = []] = [], loading: loadingMembersAndGroups } =
|
|
||||||
useRequest2(
|
|
||||||
async () => {
|
|
||||||
if (!userInfo?.team?.teamId) return [[], []];
|
|
||||||
return Promise.all([
|
|
||||||
loadAndGetTeamMembers(true),
|
|
||||||
loadAndGetGroups(true),
|
|
||||||
loadAndGetOrgs(true)
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
manual: false,
|
|
||||||
refreshDeps: [userInfo?.team?.teamId]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const filterMembers = useMemo(() => {
|
|
||||||
return members.filter((item) => {
|
|
||||||
if (item.tmbId === userInfo?.team?.tmbId) return false;
|
|
||||||
if (!searchText) return true;
|
|
||||||
return item.memberName.includes(searchText);
|
|
||||||
});
|
|
||||||
}, [members, searchText, userInfo?.team?.tmbId]);
|
|
||||||
|
|
||||||
const filterGroups = useMemo(() => {
|
|
||||||
if (mode !== 'all') return [];
|
|
||||||
return groups.filter((item) => {
|
|
||||||
if (permission.isOwner) return true; // owner can see all groups
|
|
||||||
if (myGroups.find((i) => String(i._id) === String(item._id))) return false;
|
|
||||||
if (!searchText) return true;
|
|
||||||
return item.name.includes(searchText);
|
|
||||||
});
|
|
||||||
}, [groups, searchText, myGroups, mode, permission]);
|
|
||||||
|
|
||||||
const filterOrgs = useMemo(() => {
|
|
||||||
if (mode !== 'all') return [];
|
|
||||||
return orgs.filter((item) => {
|
|
||||||
if (item.path === '') return false; // exclude root org
|
|
||||||
if (!permission.isOwner && !myOrgs.find((i) => String(i._id) === String(item._id)))
|
|
||||||
return false;
|
|
||||||
if (!searchText) return true;
|
|
||||||
return item.name.includes(searchText);
|
|
||||||
});
|
|
||||||
}, [orgs, searchText, myOrgs, mode, permission]);
|
|
||||||
|
|
||||||
const [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
|
|
||||||
const [selectedGroupIdList, setSelectedGroupIdList] = useState<string[]>([]);
|
|
||||||
const [selectedOrgIdList, setSelectedOrgIdList] = useState<string[]>([]);
|
|
||||||
const [selectedPermission, setSelectedPermission] = useState(permissionList['read'].value);
|
|
||||||
const perLabel = useMemo(() => {
|
|
||||||
return getPerLabelList(selectedPermission).join('、');
|
|
||||||
}, [getPerLabelList, selectedPermission]);
|
|
||||||
|
|
||||||
const { runAsync: onConfirm, loading: isUpdating } = useRequest2(
|
|
||||||
() =>
|
|
||||||
onUpdateCollaborators({
|
|
||||||
members: selectedMemberIdList,
|
|
||||||
groups: selectedGroupIdList,
|
|
||||||
orgs: selectedOrgIdList,
|
|
||||||
permission: selectedPermission
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
successToast: t('common:common.Add Success'),
|
|
||||||
errorToast: 'Error',
|
|
||||||
onSuccess() {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MyModal
|
|
||||||
isOpen
|
|
||||||
onClose={onClose}
|
|
||||||
iconSrc="modal/AddClb"
|
|
||||||
title={t('user:team.add_collaborator')}
|
|
||||||
minW="800px"
|
|
||||||
h={'100%'}
|
|
||||||
isCentered
|
|
||||||
isLoading={loadingMembersAndGroups}
|
|
||||||
>
|
|
||||||
<ModalBody flex={'1'}>
|
|
||||||
<Grid
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="myGray.200"
|
|
||||||
borderRadius="0.5rem"
|
|
||||||
gridTemplateColumns="1fr 1fr"
|
|
||||||
h={'100%'}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" borderRight="1px solid" borderColor="myGray.200" p="4">
|
|
||||||
<SearchInput
|
|
||||||
placeholder={t('user:search_user')}
|
|
||||||
bgColor="myGray.50"
|
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
|
||||||
{filterOrgs.map((org) => {
|
|
||||||
const onChange = () => {
|
|
||||||
setSelectedOrgIdList((state) => {
|
|
||||||
if (state.includes(org._id)) {
|
|
||||||
return state.filter((v) => v !== org._id);
|
|
||||||
}
|
|
||||||
return [...state, org._id];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const collaborator = collaboratorList.find((v) => v.orgId === org._id);
|
|
||||||
return (
|
|
||||||
<HStack
|
|
||||||
justifyContent="space-between"
|
|
||||||
key={org._id}
|
|
||||||
py="2"
|
|
||||||
px="3"
|
|
||||||
borderRadius="sm"
|
|
||||||
alignItems="center"
|
|
||||||
_hover={{
|
|
||||||
bgColor: 'myGray.50',
|
|
||||||
cursor: 'pointer',
|
|
||||||
...(!selectedOrgIdList.includes(org._id)
|
|
||||||
? { svg: { color: 'myGray.50' } }
|
|
||||||
: {})
|
|
||||||
}}
|
|
||||||
onClick={onChange}
|
|
||||||
>
|
|
||||||
<Checkbox isChecked={selectedOrgIdList.includes(org._id)} />
|
|
||||||
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
|
|
||||||
<Box ml="2" w="full">
|
|
||||||
{org.name}
|
|
||||||
</Box>
|
|
||||||
{!!collaborator && (
|
|
||||||
<PermissionTags permission={collaborator.permission.value} />
|
|
||||||
)}
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{filterGroups.map((group) => {
|
|
||||||
const onChange = () => {
|
|
||||||
setSelectedGroupIdList((state) => {
|
|
||||||
if (state.includes(group._id)) {
|
|
||||||
return state.filter((v) => v !== group._id);
|
|
||||||
}
|
|
||||||
return [...state, group._id];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const collaborator = collaboratorList.find((v) => v.groupId === group._id);
|
|
||||||
return (
|
|
||||||
<HStack
|
|
||||||
justifyContent="space-between"
|
|
||||||
key={group._id}
|
|
||||||
py="2"
|
|
||||||
px="3"
|
|
||||||
borderRadius="sm"
|
|
||||||
alignItems="center"
|
|
||||||
_hover={{
|
|
||||||
bgColor: 'myGray.50',
|
|
||||||
cursor: 'pointer',
|
|
||||||
...(!selectedGroupIdList.includes(group._id)
|
|
||||||
? { svg: { color: 'myGray.50' } }
|
|
||||||
: {})
|
|
||||||
}}
|
|
||||||
onClick={onChange}
|
|
||||||
>
|
|
||||||
<Checkbox isChecked={selectedGroupIdList.includes(group._id)} />
|
|
||||||
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
|
|
||||||
<Box ml="2" w="full">
|
|
||||||
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
|
|
||||||
</Box>
|
|
||||||
{!!collaborator && (
|
|
||||||
<PermissionTags permission={collaborator.permission.value} />
|
|
||||||
)}
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{filterMembers.map((member) => {
|
|
||||||
const onChange = () => {
|
|
||||||
setSelectedMembers((state) => {
|
|
||||||
if (state.includes(member.tmbId)) {
|
|
||||||
return state.filter((v) => v !== member.tmbId);
|
|
||||||
}
|
|
||||||
return [...state, member.tmbId];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const collaborator = collaboratorList.find((v) => v.tmbId === member.tmbId);
|
|
||||||
return (
|
|
||||||
<HStack
|
|
||||||
justifyContent="space-between"
|
|
||||||
key={member.tmbId}
|
|
||||||
py="2"
|
|
||||||
px="3"
|
|
||||||
borderRadius="sm"
|
|
||||||
alignItems="center"
|
|
||||||
_hover={{
|
|
||||||
bgColor: 'myGray.50',
|
|
||||||
cursor: 'pointer',
|
|
||||||
...(!selectedMemberIdList.includes(member.tmbId)
|
|
||||||
? { svg: { color: 'myGray.50' } }
|
|
||||||
: {})
|
|
||||||
}}
|
|
||||||
onClick={onChange}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
isChecked={selectedMemberIdList.includes(member.tmbId)}
|
|
||||||
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
|
||||||
/>
|
|
||||||
<MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
|
|
||||||
<Box w="full" ml="2">
|
|
||||||
{member.memberName}
|
|
||||||
</Box>
|
|
||||||
{!!collaborator && (
|
|
||||||
<PermissionTags permission={collaborator.permission.value} />
|
|
||||||
)}
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex p="4" flexDirection="column">
|
|
||||||
<Box>
|
|
||||||
{`${t('user:has_chosen')}: `}
|
|
||||||
{selectedMemberIdList.length + selectedGroupIdList.length + selectedOrgIdList.length}
|
|
||||||
</Box>
|
|
||||||
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
|
||||||
{selectedOrgIdList.map((orgId) => {
|
|
||||||
const org = orgs.find((v) => String(v._id) === orgId);
|
|
||||||
return (
|
|
||||||
<HStack
|
|
||||||
justifyContent="space-between"
|
|
||||||
key={orgId}
|
|
||||||
py="2"
|
|
||||||
px="3"
|
|
||||||
borderRadius="sm"
|
|
||||||
alignItems="center"
|
|
||||||
_hover={{
|
|
||||||
bgColor: 'myGray.50',
|
|
||||||
cursor: 'pointer',
|
|
||||||
...(!selectedOrgIdList.includes(orgId) ? { svg: { color: 'myGray.50' } } : {})
|
|
||||||
}}
|
|
||||||
onClick={() =>
|
|
||||||
setSelectedOrgIdList(selectedOrgIdList.filter((v) => v !== orgId))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<MyAvatar src={org?.avatar} w="1.5rem" borderRadius={'50%'} />
|
|
||||||
<Box w="full" ml="2">
|
|
||||||
{org?.name}
|
|
||||||
</Box>
|
|
||||||
<MyIcon
|
|
||||||
name="common/closeLight"
|
|
||||||
w="16px"
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{
|
|
||||||
color: 'red.600'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{selectedGroupIdList.map((groupId) => {
|
|
||||||
const onChange = () => {
|
|
||||||
setSelectedGroupIdList((state) => {
|
|
||||||
if (state.includes(groupId)) {
|
|
||||||
return state.filter((v) => v !== groupId);
|
|
||||||
}
|
|
||||||
return [...state, groupId];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const group = groups.find((v) => String(v._id) === groupId);
|
|
||||||
return (
|
|
||||||
<HStack
|
|
||||||
justifyContent="space-between"
|
|
||||||
key={groupId}
|
|
||||||
py="2"
|
|
||||||
px="3"
|
|
||||||
borderRadius="sm"
|
|
||||||
alignItems="center"
|
|
||||||
_hover={{
|
|
||||||
bgColor: 'myGray.50',
|
|
||||||
cursor: 'pointer',
|
|
||||||
...(!selectedGroupIdList.includes(groupId)
|
|
||||||
? { svg: { color: 'myGray.50' } }
|
|
||||||
: {})
|
|
||||||
}}
|
|
||||||
onClick={onChange}
|
|
||||||
>
|
|
||||||
<MyAvatar src={group?.avatar} w="1.5rem" borderRadius={'50%'} />
|
|
||||||
<Box w="full" ml="2">
|
|
||||||
{group?.name === DefaultGroupName ? userInfo?.team.teamName : group?.name}
|
|
||||||
</Box>
|
|
||||||
<MyIcon
|
|
||||||
name="common/closeLight"
|
|
||||||
w="16px"
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{
|
|
||||||
color: 'red.600'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{selectedMemberIdList.map((tmbId) => {
|
|
||||||
const member = filterMembers.find((v) => v.tmbId === tmbId);
|
|
||||||
return member ? (
|
|
||||||
<HStack
|
|
||||||
justifyContent="space-between"
|
|
||||||
key={tmbId}
|
|
||||||
alignItems="center"
|
|
||||||
py="2"
|
|
||||||
px={3}
|
|
||||||
borderRadius={'md'}
|
|
||||||
_hover={{ bg: 'myGray.50' }}
|
|
||||||
onClick={() =>
|
|
||||||
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<MyAvatar src={member.avatar} w="1.5rem" borderRadius="50%" />
|
|
||||||
<Box w="full" ml={2}>
|
|
||||||
{member.memberName}
|
|
||||||
</Box>
|
|
||||||
<MyIcon
|
|
||||||
name="common/closeLight"
|
|
||||||
w="16px"
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{
|
|
||||||
color: 'red.600'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
) : null;
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Grid>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<PermissionSelect
|
|
||||||
value={selectedPermission}
|
|
||||||
Button={
|
|
||||||
<Flex
|
|
||||||
alignItems={'center'}
|
|
||||||
bg={'myGray.50'}
|
|
||||||
border="base"
|
|
||||||
fontSize={'sm'}
|
|
||||||
px={3}
|
|
||||||
borderRadius={'md'}
|
|
||||||
h={'32px'}
|
|
||||||
>
|
|
||||||
{t(perLabel as any)}
|
|
||||||
<ChevronDownIcon fontSize={'md'} />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
onChange={(v) => setSelectedPermission(v)}
|
|
||||||
/>
|
|
||||||
<Button isLoading={isUpdating} ml="4" h={'32px'} onClick={onConfirm}>
|
|
||||||
{t('common:common.Confirm')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</MyModal>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AddMemberModal;
|
export default AddMemberModal;
|
||||||
|
|||||||
@@ -0,0 +1,569 @@
|
|||||||
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
|
import { ChevronDownIcon } from '@chakra-ui/icons';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Flex,
|
||||||
|
Grid,
|
||||||
|
HStack,
|
||||||
|
ModalBody,
|
||||||
|
ModalFooter,
|
||||||
|
Tag,
|
||||||
|
Text
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
|
import MyAvatar from '@fastgpt/web/components/common/Avatar';
|
||||||
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
|
||||||
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
import { useContextSelector } from 'use-context-selector';
|
||||||
|
import PermissionSelect from './PermissionSelect';
|
||||||
|
import PermissionTags from './PermissionTags';
|
||||||
|
import { MemberManagerPropsType } from './context';
|
||||||
|
import { DEFAULT_ORG_AVATAR, DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants';
|
||||||
|
import Path from '@/components/common/folder/Path';
|
||||||
|
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
|
||||||
|
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
import { OrgType } from '@fastgpt/global/support/user/team/org/type';
|
||||||
|
import { createMemberPermission } from '@/web/support/user/team/api';
|
||||||
|
|
||||||
|
export type AddModalPropsType = {
|
||||||
|
onClose: () => void;
|
||||||
|
mode?: 'member' | 'all';
|
||||||
|
};
|
||||||
|
|
||||||
|
function MemberModal({
|
||||||
|
onClose,
|
||||||
|
mode = 'member',
|
||||||
|
collaboratorContext: context
|
||||||
|
}: AddModalPropsType & { collaboratorContext?: MemberManagerPropsType }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups, loadAndGetOrgs } =
|
||||||
|
useUserStore();
|
||||||
|
|
||||||
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
|
const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>();
|
||||||
|
const [parentPath, setParentPath] = useState('');
|
||||||
|
|
||||||
|
const { data: [members = [], groups = [], orgs = []] = [], loading: loadingMembersAndGroups } =
|
||||||
|
useRequest2(
|
||||||
|
async () => {
|
||||||
|
if (!userInfo?.team?.teamId) return [[], []];
|
||||||
|
return Promise.all([
|
||||||
|
loadAndGetTeamMembers(true),
|
||||||
|
loadAndGetGroups(true),
|
||||||
|
loadAndGetOrgs(true)
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
manual: false,
|
||||||
|
refreshDeps: [userInfo?.team?.teamId]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentOrg = useMemo(() => {
|
||||||
|
const splitPath = parentPath.split('/');
|
||||||
|
const currentOrgId = splitPath[splitPath.length - 1];
|
||||||
|
if (!currentOrgId) return;
|
||||||
|
|
||||||
|
return orgs.find((org) => org.pathId === currentOrgId);
|
||||||
|
}, [orgs, parentPath]);
|
||||||
|
const paths = useMemo(() => {
|
||||||
|
const splitPath = parentPath.split('/').filter(Boolean);
|
||||||
|
return splitPath
|
||||||
|
.map((id) => {
|
||||||
|
const org = orgs.find((org) => org.pathId === id)!;
|
||||||
|
|
||||||
|
if (org.path === '') return;
|
||||||
|
|
||||||
|
return {
|
||||||
|
parentId: getOrgChildrenPath(org),
|
||||||
|
parentName: org.name
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean) as ParentTreePathItemType[];
|
||||||
|
}, [parentPath, orgs]);
|
||||||
|
|
||||||
|
const filterMembers = useMemo(() => {
|
||||||
|
if (!searchText && filterClass !== 'member' && filterClass !== 'org') return [];
|
||||||
|
if (searchText) return members.filter((item) => item.memberName.includes(searchText));
|
||||||
|
if (filterClass === 'org') {
|
||||||
|
if (!currentOrg) return [];
|
||||||
|
return members.filter((item) => currentOrg.members.find((v) => v.tmbId === item.tmbId));
|
||||||
|
}
|
||||||
|
return members;
|
||||||
|
}, [members, searchText, filterClass, currentOrg]);
|
||||||
|
|
||||||
|
const filterGroups = useMemo(() => {
|
||||||
|
if (mode !== 'all') return [];
|
||||||
|
if (!searchText && filterClass !== 'group') return [];
|
||||||
|
if (searchText) return groups.filter((item) => item.name.includes(searchText));
|
||||||
|
return groups.filter((item) => {
|
||||||
|
if (context === undefined || context.permission.isOwner) return true; // owner can see all groups
|
||||||
|
return !myGroups.find((i) => String(i._id) === String(item._id));
|
||||||
|
});
|
||||||
|
}, [groups, searchText, filterClass, myGroups, mode, context]);
|
||||||
|
|
||||||
|
const filterOrgs: (OrgType & { count?: number })[] = useMemo(() => {
|
||||||
|
if (mode !== 'all') return [];
|
||||||
|
if (!searchText && filterClass !== 'org') return [];
|
||||||
|
if (searchText) return orgs.filter((item) => item.name.includes(searchText));
|
||||||
|
if (parentPath === '') {
|
||||||
|
setParentPath(`/${orgs[0].pathId}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return orgs
|
||||||
|
.filter((org) => org.path === parentPath)
|
||||||
|
.map((item) => ({
|
||||||
|
...item,
|
||||||
|
count:
|
||||||
|
item.members.length + orgs.filter((org) => org.path === getOrgChildrenPath(item)).length
|
||||||
|
}));
|
||||||
|
}, [orgs, searchText, filterClass, mode, parentPath]);
|
||||||
|
|
||||||
|
const [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
|
||||||
|
const [selectedGroupIdList, setSelectedGroupIdList] = useState<string[]>([]);
|
||||||
|
const [selectedOrgIdList, setSelectedOrgIdList] = useState<string[]>([]);
|
||||||
|
const [selectedPermission, setSelectedPermission] = useState(
|
||||||
|
context?.permissionList['read'].value
|
||||||
|
);
|
||||||
|
const perLabel = useMemo(() => {
|
||||||
|
if (context) return context.getPerLabelList(selectedPermission!).join('、');
|
||||||
|
}, [context, selectedPermission]);
|
||||||
|
|
||||||
|
const { runAsync: onConfirm, loading: isUpdating } = useRequest2(
|
||||||
|
() => {
|
||||||
|
if (context) {
|
||||||
|
return context.onUpdateCollaborators({
|
||||||
|
members: selectedMemberIdList,
|
||||||
|
groups: selectedGroupIdList,
|
||||||
|
orgs: selectedOrgIdList,
|
||||||
|
permission: selectedPermission!
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return createMemberPermission({
|
||||||
|
tmbId: selectedMemberIdList,
|
||||||
|
groupId: selectedGroupIdList,
|
||||||
|
orgId: selectedOrgIdList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
successToast: t('common:common.Add Success'),
|
||||||
|
errorToast: 'Error',
|
||||||
|
onSuccess() {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyModal
|
||||||
|
isOpen
|
||||||
|
onClose={onClose}
|
||||||
|
iconSrc="modal/AddClb"
|
||||||
|
title={t('user:team.add_collaborator')}
|
||||||
|
minW="800px"
|
||||||
|
h={'100%'}
|
||||||
|
maxH={'800px'}
|
||||||
|
isCentered
|
||||||
|
isLoading={loadingMembersAndGroups}
|
||||||
|
>
|
||||||
|
<ModalBody flex={'1'}>
|
||||||
|
<Grid
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="myGray.200"
|
||||||
|
borderRadius="0.5rem"
|
||||||
|
gridTemplateColumns="1fr 1fr"
|
||||||
|
h={'100%'}
|
||||||
|
>
|
||||||
|
<Flex flexDirection="column" borderRight="1px solid" borderColor="myGray.200" p="4">
|
||||||
|
<SearchInput
|
||||||
|
placeholder={t('user:search_user')}
|
||||||
|
bgColor="myGray.50"
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
||||||
|
{!searchText &&
|
||||||
|
(filterClass === undefined ? (
|
||||||
|
<>
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
onClick={() => setFilterClass('member')}
|
||||||
|
>
|
||||||
|
<MyAvatar src="/imgs/avatar/BlueAvatar.svg" w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box ml="2" w="full">
|
||||||
|
{t('user:team.group.members')}
|
||||||
|
</Box>
|
||||||
|
<MyIcon name="core/chat/chevronRight" w="16px" />
|
||||||
|
</HStack>
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
onClick={() => setFilterClass('org')}
|
||||||
|
>
|
||||||
|
<MyAvatar src={DEFAULT_ORG_AVATAR} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box ml="2" w="full">
|
||||||
|
{t('user:team.org.org')}
|
||||||
|
</Box>
|
||||||
|
<MyIcon name="core/chat/chevronRight" w="16px" />
|
||||||
|
</HStack>
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
onClick={() => setFilterClass('group')}
|
||||||
|
>
|
||||||
|
<MyAvatar src={DEFAULT_TEAM_AVATAR} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box ml="2" w="full">
|
||||||
|
{t('user:team.group.group')}
|
||||||
|
</Box>
|
||||||
|
<MyIcon name="core/chat/chevronRight" w="16px" />
|
||||||
|
</HStack>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Path
|
||||||
|
paths={[
|
||||||
|
{
|
||||||
|
parentId: filterClass,
|
||||||
|
parentName:
|
||||||
|
filterClass === 'member'
|
||||||
|
? t('user:team.group.members')
|
||||||
|
: filterClass === 'org'
|
||||||
|
? t('user:team.org.org')
|
||||||
|
: t('user:team.group.group')
|
||||||
|
},
|
||||||
|
...paths
|
||||||
|
]}
|
||||||
|
onClick={(parentId) => {
|
||||||
|
if (parentId === '') {
|
||||||
|
setFilterClass(undefined);
|
||||||
|
setParentPath('');
|
||||||
|
} else if (
|
||||||
|
parentId === 'member' ||
|
||||||
|
parentId === 'org' ||
|
||||||
|
parentId === 'group'
|
||||||
|
) {
|
||||||
|
setFilterClass(parentId);
|
||||||
|
setParentPath('');
|
||||||
|
} else {
|
||||||
|
setParentPath(parentId);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
rootName={t('common:common.Team')}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{filterOrgs.map((org) => {
|
||||||
|
const onChange = () => {
|
||||||
|
setSelectedOrgIdList((state) => {
|
||||||
|
if (state.includes(org._id)) {
|
||||||
|
return state.filter((v) => v !== org._id);
|
||||||
|
}
|
||||||
|
return [...state, org._id];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const collaborator = context?.collaboratorList?.find((v) => v.orgId === org._id);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={org._id}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
onClick={onChange}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
isChecked={selectedOrgIdList.includes(org._id)}
|
||||||
|
pointerEvents="none"
|
||||||
|
/>
|
||||||
|
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<HStack ml="2" w="full" gap="5px">
|
||||||
|
<Text>{org.name}</Text>
|
||||||
|
{org.count && (
|
||||||
|
<>
|
||||||
|
<Tag size="sm" my="auto">
|
||||||
|
{org.count}
|
||||||
|
</Tag>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
{!!collaborator && (
|
||||||
|
<PermissionTags permission={collaborator.permission.value} />
|
||||||
|
)}
|
||||||
|
{org.count && (
|
||||||
|
<MyIcon
|
||||||
|
name="core/chat/chevronRight"
|
||||||
|
w="16px"
|
||||||
|
p="4px"
|
||||||
|
rounded={'6px'}
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.200'
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setParentPath(getOrgChildrenPath(org));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{filterGroups.map((group) => {
|
||||||
|
const onChange = () => {
|
||||||
|
setSelectedGroupIdList((state) => {
|
||||||
|
if (state.includes(group._id)) {
|
||||||
|
return state.filter((v) => v !== group._id);
|
||||||
|
}
|
||||||
|
return [...state, group._id];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const collaborator = context?.collaboratorList?.find(
|
||||||
|
(v) => v.groupId === group._id
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={group._id}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
onClick={onChange}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
isChecked={selectedGroupIdList.includes(group._id)}
|
||||||
|
pointerEvents="none"
|
||||||
|
/>
|
||||||
|
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box ml="2" w="full">
|
||||||
|
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
|
||||||
|
</Box>
|
||||||
|
{!!collaborator && (
|
||||||
|
<PermissionTags permission={collaborator.permission.value} />
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{filterMembers.map((member) => {
|
||||||
|
const onChange = () => {
|
||||||
|
setSelectedMembers((state) => {
|
||||||
|
if (state.includes(member.tmbId)) {
|
||||||
|
return state.filter((v) => v !== member.tmbId);
|
||||||
|
}
|
||||||
|
return [...state, member.tmbId];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const collaborator = context?.collaboratorList?.find(
|
||||||
|
(v) => v.tmbId === member.tmbId
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={member.tmbId}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
onClick={userInfo?.team?.tmbId === member.tmbId ? undefined : onChange}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
isChecked={selectedMemberIdList.includes(member.tmbId)}
|
||||||
|
pointerEvents="none"
|
||||||
|
isDisabled={userInfo?.team?.tmbId === member.tmbId}
|
||||||
|
/>
|
||||||
|
<MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box w="full" ml="2">
|
||||||
|
{member.memberName}
|
||||||
|
</Box>
|
||||||
|
{!!collaborator && (
|
||||||
|
<PermissionTags permission={collaborator.permission.value} />
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Flex p="4" flexDirection="column">
|
||||||
|
<Box>
|
||||||
|
{`${t('user:has_chosen')}: `}
|
||||||
|
{selectedMemberIdList.length + selectedGroupIdList.length + selectedOrgIdList.length}
|
||||||
|
</Box>
|
||||||
|
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
||||||
|
{selectedOrgIdList.map((orgId) => {
|
||||||
|
const org = orgs.find((v) => String(v._id) === orgId);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={orgId}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer',
|
||||||
|
...(!selectedOrgIdList.includes(orgId) ? { svg: { color: 'myGray.50' } } : {})
|
||||||
|
}}
|
||||||
|
onClick={() =>
|
||||||
|
setSelectedOrgIdList(selectedOrgIdList.filter((v) => v !== orgId))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MyAvatar src={org?.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box w="full" ml="2">
|
||||||
|
{org?.name}
|
||||||
|
</Box>
|
||||||
|
<MyIcon
|
||||||
|
name="common/closeLight"
|
||||||
|
w="16px"
|
||||||
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
color: 'red.600'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{selectedGroupIdList.map((groupId) => {
|
||||||
|
const onChange = () => {
|
||||||
|
setSelectedGroupIdList((state) => {
|
||||||
|
if (state.includes(groupId)) {
|
||||||
|
return state.filter((v) => v !== groupId);
|
||||||
|
}
|
||||||
|
return [...state, groupId];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const group = groups.find((v) => String(v._id) === groupId);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={groupId}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer',
|
||||||
|
...(!selectedGroupIdList.includes(groupId)
|
||||||
|
? { svg: { color: 'myGray.50' } }
|
||||||
|
: {})
|
||||||
|
}}
|
||||||
|
onClick={onChange}
|
||||||
|
>
|
||||||
|
<MyAvatar src={group?.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box w="full" ml="2">
|
||||||
|
{group?.name === DefaultGroupName ? userInfo?.team.teamName : group?.name}
|
||||||
|
</Box>
|
||||||
|
<MyIcon
|
||||||
|
name="common/closeLight"
|
||||||
|
w="16px"
|
||||||
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
color: 'red.600'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{selectedMemberIdList.map((tmbId) => {
|
||||||
|
const member = members.find((v) => v.tmbId === tmbId);
|
||||||
|
return member ? (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={tmbId}
|
||||||
|
alignItems="center"
|
||||||
|
py="2"
|
||||||
|
px={3}
|
||||||
|
borderRadius={'md'}
|
||||||
|
_hover={{ bg: 'myGray.50' }}
|
||||||
|
onClick={() =>
|
||||||
|
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MyAvatar src={member.avatar} w="1.5rem" borderRadius="50%" />
|
||||||
|
<Box w="full" ml={2}>
|
||||||
|
{member.memberName}
|
||||||
|
</Box>
|
||||||
|
<MyIcon
|
||||||
|
name="common/closeLight"
|
||||||
|
w="16px"
|
||||||
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
color: 'red.600'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
) : null;
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Grid>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
{selectedPermission && (
|
||||||
|
<PermissionSelect
|
||||||
|
value={selectedPermission}
|
||||||
|
Button={
|
||||||
|
<Flex
|
||||||
|
alignItems={'center'}
|
||||||
|
bg={'myGray.50'}
|
||||||
|
border="base"
|
||||||
|
fontSize={'sm'}
|
||||||
|
px={3}
|
||||||
|
borderRadius={'md'}
|
||||||
|
h={'32px'}
|
||||||
|
>
|
||||||
|
{t(perLabel as any)}
|
||||||
|
<ChevronDownIcon fontSize={'md'} />
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
onChange={(v) => setSelectedPermission(v)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button isLoading={isUpdating} ml="4" h={'32px'} onClick={onConfirm}>
|
||||||
|
{t('common:common.Confirm')}
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</MyModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MemberModal;
|
||||||
@@ -9,15 +9,17 @@ import {
|
|||||||
Td,
|
Td,
|
||||||
Th,
|
Th,
|
||||||
Thead,
|
Thead,
|
||||||
|
Text,
|
||||||
Tr
|
Tr
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { getTeamClbs, updateMemberPermission } from '@/web/support/user/team/api';
|
import {
|
||||||
|
deleteMemberPermission,
|
||||||
|
getTeamClbs,
|
||||||
|
updateMemberPermission
|
||||||
|
} from '@/web/support/user/team/api';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
|
|
||||||
import { TeamContext } from '../context';
|
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import MemberTag from '../../../../../components/support/user/team/Info/MemberTag';
|
import MemberTag from '../../../../../components/support/user/team/Info/MemberTag';
|
||||||
@@ -27,65 +29,62 @@ import {
|
|||||||
TeamWritePermissionVal
|
TeamWritePermissionVal
|
||||||
} from '@fastgpt/global/support/permission/user/constant';
|
} from '@fastgpt/global/support/permission/user/constant';
|
||||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||||
import { useCreation } from 'ahooks';
|
import { useCreation, useToggle } from 'ahooks';
|
||||||
import { getOrgList } from '@/web/support/user/team/org/api';
|
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||||
|
import MemberModal from '@/components/support/permission/MemberManager/MemberModal';
|
||||||
|
|
||||||
function PermissionManage() {
|
function PermissionManage({
|
||||||
|
isOpenAddPermission,
|
||||||
|
onCloseAddPermission
|
||||||
|
}: {
|
||||||
|
isOpenAddPermission: boolean;
|
||||||
|
onCloseAddPermission: () => void;
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo } = useUserStore();
|
const { userInfo } = useUserStore();
|
||||||
const { groups, refetchMembers, refetchGroups, members, searchKey } = useContextSelector(
|
|
||||||
TeamContext,
|
|
||||||
(v) => v
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const { runAsync: refetchClbs, data: clbs = { tmb: [], group: [], org: [] } } = useRequest2(
|
||||||
data: orgs = [],
|
getTeamClbs,
|
||||||
loading: isLoadingOrgs,
|
{
|
||||||
refresh: refetchOrgs
|
|
||||||
} = useRequest2(getOrgList, {
|
|
||||||
manual: false,
|
manual: false,
|
||||||
refreshDeps: [userInfo?.team?.teamId]
|
refreshDeps: [userInfo?.team?.teamId]
|
||||||
});
|
}
|
||||||
|
|
||||||
const filteredOrgs = useCreation(
|
|
||||||
() =>
|
|
||||||
orgs.filter(
|
|
||||||
(org) => org.path !== '' && org.name.toLowerCase().includes(searchKey.toLowerCase())
|
|
||||||
),
|
|
||||||
[orgs, searchKey]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const { runAsync: refetchClbs, data: clbs = [] } = useRequest2(getTeamClbs, {
|
const [isExpandMember, setExpandMember] = useToggle(true);
|
||||||
manual: false,
|
const [isExpandGroup, setExpandGroup] = useToggle(true);
|
||||||
refreshDeps: [userInfo?.team?.teamId]
|
const [isExpandOrg, setExpandOrg] = useToggle(true);
|
||||||
});
|
|
||||||
|
|
||||||
const filteredGroups = useCreation(
|
const members = useCreation(
|
||||||
() => groups?.filter((group) => group.name.toLowerCase().includes(searchKey.toLowerCase())),
|
|
||||||
[groups, searchKey]
|
|
||||||
);
|
|
||||||
const filteredMembers = useCreation(
|
|
||||||
() =>
|
() =>
|
||||||
members
|
clbs.tmb.map((item) => ({
|
||||||
?.filter((member) => member.memberName.toLowerCase().includes(searchKey.toLowerCase()))
|
...item,
|
||||||
.map((member) => {
|
permission: new TeamPermission({ per: item.permission })
|
||||||
const clb = clbs?.find((clb) => String(clb.tmbId) === String(member.tmbId));
|
})),
|
||||||
const permission =
|
[clbs]
|
||||||
member.role === 'owner'
|
);
|
||||||
? new TeamPermission({ isOwner: true })
|
|
||||||
: new TeamPermission({ per: clb?.permission });
|
|
||||||
|
|
||||||
return { ...member, permission };
|
const groups = useCreation(
|
||||||
}),
|
() =>
|
||||||
[clbs, members, searchKey]
|
clbs.group.map((item) => ({
|
||||||
|
...item,
|
||||||
|
permission: new TeamPermission({ per: item.permission })
|
||||||
|
})),
|
||||||
|
[clbs]
|
||||||
|
);
|
||||||
|
|
||||||
|
const orgs = useCreation(
|
||||||
|
() =>
|
||||||
|
clbs.org.map((item) => ({
|
||||||
|
...item,
|
||||||
|
permission: new TeamPermission({ per: item.permission })
|
||||||
|
})),
|
||||||
|
[clbs]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { runAsync: onUpdateMemberPermission } = useRequest2(updateMemberPermission, {
|
const { runAsync: onUpdateMemberPermission } = useRequest2(updateMemberPermission, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
refetchGroups();
|
|
||||||
refetchMembers();
|
|
||||||
refetchClbs();
|
refetchClbs();
|
||||||
refetchOrgs();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -102,60 +101,60 @@ function PermissionManage() {
|
|||||||
per: 'write' | 'manage';
|
per: 'write' | 'manage';
|
||||||
}) => {
|
}) => {
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
const group = groups?.find((group) => group._id === groupId);
|
const group = groups?.find((group) => group.groupId === groupId);
|
||||||
if (group) {
|
if (group) {
|
||||||
const permission = new TeamPermission({ per: group.permission.value });
|
const permission = new TeamPermission({ per: group.permission.value });
|
||||||
switch (per) {
|
switch (per) {
|
||||||
case 'write':
|
case 'write':
|
||||||
permission.addPer(TeamWritePermissionVal);
|
permission.addPer(TeamWritePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
groupId: group._id,
|
groupId: group.groupId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
case 'manage':
|
case 'manage':
|
||||||
permission.addPer(TeamManagePermissionVal);
|
permission.addPer(TeamManagePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
groupId: group._id,
|
groupId: group.groupId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
const org = orgs.find((org) => String(org._id) === orgId);
|
const org = orgs.find((org) => String(org.orgId) === orgId);
|
||||||
if (org) {
|
if (org) {
|
||||||
const permission = new TeamPermission({ per: org.permission.value });
|
const permission = new TeamPermission({ per: org.permission.value });
|
||||||
switch (per) {
|
switch (per) {
|
||||||
case 'write':
|
case 'write':
|
||||||
permission.addPer(TeamWritePermissionVal);
|
permission.addPer(TeamWritePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
orgId: org._id,
|
orgId: org.orgId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
case 'manage':
|
case 'manage':
|
||||||
permission.addPer(TeamManagePermissionVal);
|
permission.addPer(TeamManagePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
orgId: org._id,
|
orgId: org.orgId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (memberId) {
|
if (memberId) {
|
||||||
const member = filteredMembers?.find((member) => String(member.tmbId) === memberId);
|
const member = members?.find((member) => member.tmbId === memberId);
|
||||||
if (member) {
|
if (member) {
|
||||||
const permission = new TeamPermission({ per: member.permission.value });
|
const permission = new TeamPermission({ per: member.permission.value });
|
||||||
switch (per) {
|
switch (per) {
|
||||||
case 'write':
|
case 'write':
|
||||||
permission.addPer(TeamWritePermissionVal);
|
permission.addPer(TeamWritePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
memberId: String(member.tmbId),
|
memberId: member.tmbId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
case 'manage':
|
case 'manage':
|
||||||
permission.addPer(TeamManagePermissionVal);
|
permission.addPer(TeamManagePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
memberId: String(member.tmbId),
|
memberId: member.tmbId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -177,40 +176,40 @@ function PermissionManage() {
|
|||||||
per: 'write' | 'manage';
|
per: 'write' | 'manage';
|
||||||
}) => {
|
}) => {
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
const group = groups?.find((group) => group._id === groupId);
|
const group = groups?.find((group) => group.groupId === groupId);
|
||||||
if (group) {
|
if (group) {
|
||||||
const permission = new TeamPermission({ per: group.permission.value });
|
const permission = new TeamPermission({ per: group.permission.value });
|
||||||
switch (per) {
|
switch (per) {
|
||||||
case 'write':
|
case 'write':
|
||||||
permission.removePer(TeamWritePermissionVal);
|
permission.removePer(TeamWritePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
groupId: group._id,
|
groupId: group.groupId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
case 'manage':
|
case 'manage':
|
||||||
permission.removePer(TeamManagePermissionVal);
|
permission.removePer(TeamManagePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
groupId: group._id,
|
groupId: group.groupId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
const org = orgs.find((org) => String(org._id) === orgId);
|
const org = orgs.find((org) => String(org.orgId) === orgId);
|
||||||
if (org) {
|
if (org) {
|
||||||
const permission = new TeamPermission({ per: org.permission.value });
|
const permission = new TeamPermission({ per: org.permission.value });
|
||||||
switch (per) {
|
switch (per) {
|
||||||
case 'write':
|
case 'write':
|
||||||
permission.removePer(TeamWritePermissionVal);
|
permission.removePer(TeamWritePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
orgId: org._id,
|
orgId: org.orgId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
case 'manage':
|
case 'manage':
|
||||||
permission.removePer(TeamManagePermissionVal);
|
permission.removePer(TeamManagePermissionVal);
|
||||||
return onUpdateMemberPermission({
|
return onUpdateMemberPermission({
|
||||||
orgId: org._id,
|
orgId: org.orgId,
|
||||||
permission: permission.value
|
permission: permission.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -239,6 +238,12 @@ function PermissionManage() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { runAsync: onDeleteMemberPermission } = useRequest2(deleteMemberPermission, {
|
||||||
|
onSuccess: () => {
|
||||||
|
refetchClbs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const userManage = userInfo?.permission.hasManagePer;
|
const userManage = userInfo?.permission.hasManagePer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -247,7 +252,7 @@ function PermissionManage() {
|
|||||||
<Thead>
|
<Thead>
|
||||||
<Tr bg={'white !important'}>
|
<Tr bg={'white !important'}>
|
||||||
<Th bg="myGray.100" borderLeftRadius="md" maxW={'150px'}>
|
<Th bg="myGray.100" borderLeftRadius="md" maxW={'150px'}>
|
||||||
{t('user:team.group.group')} / {t('user:team.group.members')}
|
{`${t('user:team.group.members')} / ${t('user:team.org.org')} / ${t('user:team.group.group')}`}
|
||||||
<QuestionTip ml="1" label={t('user:team.group.permission_tip')} />
|
<QuestionTip ml="1" label={t('user:team.group.permission_tip')} />
|
||||||
</Th>
|
</Th>
|
||||||
<Th bg="myGray.100">
|
<Th bg="myGray.100">
|
||||||
@@ -255,101 +260,36 @@ function PermissionManage() {
|
|||||||
{t('user:team.group.permission.write')}
|
{t('user:team.group.permission.write')}
|
||||||
</Box>
|
</Box>
|
||||||
</Th>
|
</Th>
|
||||||
<Th bg="myGray.100" borderRightRadius="md">
|
<Th bg="myGray.100">
|
||||||
<Box mx="auto" w="fit-content">
|
<Box mx="auto" w="fit-content">
|
||||||
{t('user:team.group.permission.manage')}
|
{t('user:team.group.permission.manage')}
|
||||||
<QuestionTip ml="1" label={t('user:team.group.manage_tip')} />
|
<QuestionTip ml="1" label={t('user:team.group.manage_tip')} />
|
||||||
</Box>
|
</Box>
|
||||||
</Th>
|
</Th>
|
||||||
|
<Th bg="myGray.100" borderRightRadius="md">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
{t('common:common.Action')}
|
||||||
|
</Box>
|
||||||
|
</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{filteredGroups?.map((group) => (
|
<Tr overflow={'unset'} border="none">
|
||||||
<Tr key={group._id} overflow={'unset'} border="none">
|
<HStack paddingX={'8px'} paddingY={'4px'}>
|
||||||
<Td border="none">
|
<MyIconButton
|
||||||
<MemberTag
|
icon={isExpandMember ? 'common/downArrowFill' : 'common/rightArrowFill'}
|
||||||
name={
|
onClick={setExpandMember.toggle}
|
||||||
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
|
|
||||||
}
|
|
||||||
avatar={group.avatar}
|
|
||||||
/>
|
/>
|
||||||
</Td>
|
<Text>{t('user:team.group.members')}</Text>
|
||||||
<Td border="none">
|
</HStack>
|
||||||
<Box mx="auto" w="fit-content">
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={!userManage}
|
|
||||||
isChecked={group.permission.hasWritePer}
|
|
||||||
onChange={(e) =>
|
|
||||||
e.target.checked
|
|
||||||
? onAddPermission({ groupId: group._id, per: 'write' })
|
|
||||||
: onRemovePermission({ groupId: group._id, per: 'write' })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Td>
|
|
||||||
<Td border="none">
|
|
||||||
<Box mx="auto" w="fit-content">
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={!userInfo?.permission.isOwner}
|
|
||||||
isChecked={group.permission.hasManagePer}
|
|
||||||
onChange={(e) =>
|
|
||||||
e.target.checked
|
|
||||||
? onAddPermission({ groupId: group._id, per: 'manage' })
|
|
||||||
: onRemovePermission({ groupId: group._id, per: 'manage' })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
{isExpandMember &&
|
||||||
{filteredGroups?.length > 0 && filteredOrgs?.length > 0 && (
|
members.map((member) => (
|
||||||
<Tr borderBottom={'1px solid'} borderColor={'myGray.300'} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{filteredOrgs?.map((org) => (
|
|
||||||
<Tr key={org._id} overflow={'unset'} border="none">
|
|
||||||
<Td border="none">
|
|
||||||
<MemberTag name={org.name} avatar={org.avatar} />
|
|
||||||
</Td>
|
|
||||||
<Td border="none">
|
|
||||||
<Box mx="auto" w="fit-content">
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={!userManage}
|
|
||||||
isChecked={org.permission.hasWritePer}
|
|
||||||
onChange={(e) =>
|
|
||||||
e.target.checked
|
|
||||||
? onAddPermission({ orgId: org._id, per: 'write' })
|
|
||||||
: onRemovePermission({ orgId: org._id, per: 'write' })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Td>
|
|
||||||
<Td border="none">
|
|
||||||
<Box mx="auto" w="fit-content">
|
|
||||||
<Checkbox
|
|
||||||
isDisabled={!userInfo?.permission.isOwner}
|
|
||||||
isChecked={org.permission.hasManagePer}
|
|
||||||
onChange={(e) =>
|
|
||||||
e.target.checked
|
|
||||||
? onAddPermission({ orgId: org._id, per: 'manage' })
|
|
||||||
: onRemovePermission({ orgId: org._id, per: 'manage' })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{filteredOrgs?.length > 0 && filteredMembers?.length > 0 && (
|
|
||||||
<Tr borderBottom={'1px solid'} borderColor={'myGray.300'} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{filteredMembers?.map((member) => (
|
|
||||||
<Tr key={member.tmbId} overflow={'unset'} border="none">
|
<Tr key={member.tmbId} overflow={'unset'} border="none">
|
||||||
<Td border="none">
|
<Td border="none">
|
||||||
<HStack>
|
<HStack>
|
||||||
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
|
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
<Box>{member.memberName}</Box>
|
<Box>{member.name}</Box>
|
||||||
</HStack>
|
</HStack>
|
||||||
</Td>
|
</Td>
|
||||||
<Td border="none">
|
<Td border="none">
|
||||||
@@ -378,10 +318,148 @@ function PermissionManage() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Td>
|
</Td>
|
||||||
|
{userManage &&
|
||||||
|
!member.permission.isOwner &&
|
||||||
|
userInfo?.team.tmbId !== member.tmbId && (
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<MyIconButton
|
||||||
|
icon="common/trash"
|
||||||
|
onClick={() => onDeleteMemberPermission({ tmbId: String(member.tmbId) })}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
</Tr>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Tr borderBottom={'1px solid'} borderColor={'myGray.200'} />
|
||||||
|
<Tr overflow={'unset'} border="none">
|
||||||
|
<HStack paddingX={'8px'} paddingY={'4px'}>
|
||||||
|
<MyIconButton
|
||||||
|
icon={isExpandOrg ? 'common/downArrowFill' : 'common/rightArrowFill'}
|
||||||
|
onClick={setExpandOrg.toggle}
|
||||||
|
/>
|
||||||
|
<Text>{t('user:team.org.org')}</Text>
|
||||||
|
</HStack>
|
||||||
|
</Tr>
|
||||||
|
|
||||||
|
{isExpandOrg &&
|
||||||
|
orgs.map((org) => (
|
||||||
|
<Tr key={org.orgId} overflow={'unset'} border="none">
|
||||||
|
<Td border="none">
|
||||||
|
<MemberTag name={org.name} avatar={org.avatar} />
|
||||||
|
</Td>
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={!userManage}
|
||||||
|
isChecked={org.permission.hasWritePer}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.checked
|
||||||
|
? onAddPermission({ orgId: org.orgId, per: 'write' })
|
||||||
|
: onRemovePermission({ orgId: org.orgId, per: 'write' })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={!userInfo?.permission.isOwner}
|
||||||
|
isChecked={org.permission.hasManagePer}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.checked
|
||||||
|
? onAddPermission({ orgId: org.orgId, per: 'manage' })
|
||||||
|
: onRemovePermission({ orgId: org.orgId, per: 'manage' })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
{userInfo?.permission.isOwner && (
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<MyIconButton
|
||||||
|
icon="common/trash"
|
||||||
|
onClick={() => onDeleteMemberPermission({ orgId: org.orgId })}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
</Tr>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Tr borderBottom={'1px solid'} borderColor={'myGray.200'} />
|
||||||
|
<Tr overflow={'unset'} border="none">
|
||||||
|
<HStack paddingX={'8px'} paddingY={'4px'}>
|
||||||
|
<MyIconButton
|
||||||
|
icon={isExpandGroup ? 'common/downArrowFill' : 'common/rightArrowFill'}
|
||||||
|
onClick={setExpandGroup.toggle}
|
||||||
|
/>
|
||||||
|
<Text>{t('user:team.group.group')}</Text>
|
||||||
|
</HStack>
|
||||||
|
</Tr>
|
||||||
|
|
||||||
|
{isExpandGroup &&
|
||||||
|
groups.map((group) => (
|
||||||
|
<Tr key={group.groupId} overflow={'unset'} border="none">
|
||||||
|
<Td border="none">
|
||||||
|
<MemberTag
|
||||||
|
name={
|
||||||
|
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
|
||||||
|
}
|
||||||
|
avatar={group.avatar}
|
||||||
|
/>
|
||||||
|
</Td>
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={!userManage}
|
||||||
|
isChecked={group.permission.hasWritePer}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.checked
|
||||||
|
? onAddPermission({ groupId: group.groupId, per: 'write' })
|
||||||
|
: onRemovePermission({ groupId: group.groupId, per: 'write' })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<Checkbox
|
||||||
|
isDisabled={!userInfo?.permission.isOwner}
|
||||||
|
isChecked={group.permission.hasManagePer}
|
||||||
|
onChange={(e) =>
|
||||||
|
e.target.checked
|
||||||
|
? onAddPermission({ groupId: group.groupId, per: 'manage' })
|
||||||
|
: onRemovePermission({ groupId: group.groupId, per: 'manage' })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
{userInfo?.permission.isOwner && (
|
||||||
|
<Td border="none">
|
||||||
|
<Box mx="auto" w="fit-content">
|
||||||
|
<MyIconButton
|
||||||
|
icon="common/trash"
|
||||||
|
onClick={() => onDeleteMemberPermission({ groupId: group.groupId })}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
))}
|
||||||
</Tbody>
|
</Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
|
{isOpenAddPermission && (
|
||||||
|
<MemberModal
|
||||||
|
onClose={() => {
|
||||||
|
refetchClbs();
|
||||||
|
onCloseAddPermission();
|
||||||
|
}}
|
||||||
|
mode="all"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,11 @@ const Team = () => {
|
|||||||
onOpen: onOpenManageGroupMember,
|
onOpen: onOpenManageGroupMember,
|
||||||
onClose: onCloseManageGroupMember
|
onClose: onCloseManageGroupMember
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
const {
|
||||||
|
isOpen: isOpenAddPermission,
|
||||||
|
onOpen: onOpenAddPermission,
|
||||||
|
onClose: onCloseAddPermission
|
||||||
|
} = useDisclosure();
|
||||||
|
|
||||||
const { runAsync: onLeaveTeam } = useRequest2(
|
const { runAsync: onLeaveTeam } = useRequest2(
|
||||||
async () => {
|
async () => {
|
||||||
@@ -268,6 +273,18 @@ const Team = () => {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
{teamTab === TeamTabEnum.permission && userInfo?.team.permission.hasManagePer && (
|
||||||
|
<Button
|
||||||
|
variant={'primary'}
|
||||||
|
size="md"
|
||||||
|
borderRadius={'md'}
|
||||||
|
ml={3}
|
||||||
|
leftIcon={<MyIcon name="common/add2" w={'14px'} />}
|
||||||
|
onClick={onOpenAddPermission}
|
||||||
|
>
|
||||||
|
{t('common:common.Add')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box flex={'1 0 0'} overflow={'auto'}>
|
<Box flex={'1 0 0'} overflow={'auto'}>
|
||||||
@@ -276,7 +293,12 @@ const Team = () => {
|
|||||||
<GroupManage onEditGroup={onEditGroup} onManageMember={onManageMember} />
|
<GroupManage onEditGroup={onEditGroup} onManageMember={onManageMember} />
|
||||||
)}
|
)}
|
||||||
{teamTab === TeamTabEnum.org && <OrgManage />}
|
{teamTab === TeamTabEnum.org && <OrgManage />}
|
||||||
{teamTab === TeamTabEnum.permission && <PermissionManage />}
|
{teamTab === TeamTabEnum.permission && (
|
||||||
|
<PermissionManage
|
||||||
|
isOpenAddPermission={isOpenAddPermission}
|
||||||
|
onCloseAddPermission={onCloseAddPermission}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{isOpenInvite && userInfo?.team?.teamId && (
|
{isOpenInvite && userInfo?.team?.teamId && (
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
|
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
|
||||||
import { UpdatePermissionBody } from '@fastgpt/global/support/permission/collaborator';
|
import {
|
||||||
|
CreatePermissionBody,
|
||||||
|
DeletePermissionQuery,
|
||||||
|
ListPermissionResponse,
|
||||||
|
UpdatePermissionBody
|
||||||
|
} from '@fastgpt/global/support/permission/collaborator';
|
||||||
import {
|
import {
|
||||||
CreateTeamProps,
|
CreateTeamProps,
|
||||||
InviteMemberProps,
|
InviteMemberProps,
|
||||||
@@ -15,7 +20,6 @@ import {
|
|||||||
} from '@fastgpt/global/support/user/team/type.d';
|
} from '@fastgpt/global/support/user/team/type.d';
|
||||||
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
|
||||||
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
|
import { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
|
||||||
import { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
|
|
||||||
|
|
||||||
/* --------------- team ---------------- */
|
/* --------------- team ---------------- */
|
||||||
export const getTeamList = (status: `${TeamMemberSchema['status']}`) =>
|
export const getTeamList = (status: `${TeamMemberSchema['status']}`) =>
|
||||||
@@ -40,12 +44,18 @@ export const updateInviteResult = (data: UpdateInviteProps) =>
|
|||||||
export const delLeaveTeam = () => DELETE('/proApi/support/user/team/member/leave');
|
export const delLeaveTeam = () => DELETE('/proApi/support/user/team/member/leave');
|
||||||
|
|
||||||
export const getTeamClbs = () =>
|
export const getTeamClbs = () =>
|
||||||
GET<ResourcePermissionType[]>(`/proApi/support/user/team/collaborator/list`);
|
GET<ListPermissionResponse>(`/proApi/support/user/team/collaborator/list`);
|
||||||
|
|
||||||
/* -------------- team collaborator -------------------- */
|
/* -------------- team collaborator -------------------- */
|
||||||
export const updateMemberPermission = (data: UpdatePermissionBody) =>
|
export const updateMemberPermission = (data: UpdatePermissionBody) =>
|
||||||
PUT('/proApi/support/user/team/collaborator/updatePermission', data);
|
PUT('/proApi/support/user/team/collaborator/updatePermission', data);
|
||||||
|
|
||||||
|
export const createMemberPermission = (data: CreatePermissionBody) =>
|
||||||
|
POST('/proApi/support/user/team/collaborator/create', data);
|
||||||
|
|
||||||
|
export const deleteMemberPermission = (id: DeletePermissionQuery) =>
|
||||||
|
DELETE('/proApi/support/user/team/collaborator/delete', id);
|
||||||
|
|
||||||
/* --------------- team tags ---------------- */
|
/* --------------- team tags ---------------- */
|
||||||
export const getTeamsTags = () => GET<TeamTagSchema[]>(`/proApi/support/user/team/tag/list`);
|
export const getTeamsTags = () => GET<TeamTagSchema[]>(`/proApi/support/user/team/tag/list`);
|
||||||
export const loadTeamTagsByDomain = (domain: string) =>
|
export const loadTeamTagsByDomain = (domain: string) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user