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(''); 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([]); const [selectedGroupIdList, setSelectedGroupIdList] = useState([]); const [selectedOrgIdList, setSelectedOrgIdList] = useState([]); 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 ( setSearchText(e.target.value)} /> {!searchText && (filterClass === undefined ? ( <> setFilterClass('member')} > {t('user:team.group.members')} setFilterClass('org')} > {t('user:team.org.org')} setFilterClass('group')} > {t('user:team.group.group')} ) : ( { 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 ( {org.name} {org.count && ( <> {org.count} )} {!!collaborator && ( )} {org.count && ( { setParentPath(getOrgChildrenPath(org)); }} /> )} ); })} {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 ( {group.name === DefaultGroupName ? userInfo?.team.teamName : group.name} {!!collaborator && ( )} ); })} {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 ( {member.memberName} {!!collaborator && ( )} ); })} {`${t('user:has_chosen')}: `} {selectedMemberIdList.length + selectedGroupIdList.length + selectedOrgIdList.length} {selectedOrgIdList.map((orgId) => { const org = orgs.find((v) => String(v._id) === orgId); return ( setSelectedOrgIdList(selectedOrgIdList.filter((v) => v !== orgId)) } > {org?.name} ); })} {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 ( {group?.name === DefaultGroupName ? userInfo?.team.teamName : group?.name} ); })} {selectedMemberIdList.map((tmbId) => { const member = members.find((v) => v.tmbId === tmbId); return member ? ( setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId)) } > {member.memberName} ) : null; })} {selectedPermission && ( {t(perLabel as any)} } onChange={(v) => setSelectedPermission(v)} /> )} ); } export default MemberModal;