Group role (#2993)

* feat: app/dataset support group (#2898)

* pref: member-group (#2862)

* feat: group list ordered by updateTime

* fix: transfer ownership of group when deleting member

* fix: i18n fix

* feat: can not set member as admin/owner when user is not active

* fix: GroupInfoModal hover input do not change color

* fix(fe): searchinput do not scroll

* feat: app collaborator with group, remove default permission

* feat: dataset collaborator with group, remove default permission

* chore(test): pref mock

* chore: remove useless code

* chore: adjust

* fix: add self as collaborator when creating folder

* fix(fe): folder manage menu do not show when user has write permission
only

* fix: dataset folder create

* feat: Add code comment

* Pref: app move (#2952)

* perf: app schema

* doc

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
This commit is contained in:
Archer
2024-10-25 19:39:11 +08:00
committed by shilin66
parent 8df886452e
commit c6d053e050
60 changed files with 1142 additions and 1094 deletions

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import React, { useState } from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { Box, Button, Flex, ModalBody, ModalFooter } from '@chakra-ui/react';
@@ -11,6 +11,7 @@ import { useMemoizedFn, useMount } from 'ahooks';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import LightTip from '@fastgpt/web/components/common/LightTip';
type FolderItemType = {
id: string;
@@ -27,9 +28,10 @@ type Props = {
server: (e: GetResourceFolderListProps) => Promise<GetResourceFolderListItemResponse[]>;
onConfirm: (id: ParentIdType) => Promise<any>;
onClose: () => void;
moveHint?: string;
};
const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose }: Props) => {
const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose, moveHint }: Props) => {
const { t } = useTranslation();
const [selectedId, setSelectedId] = React.useState<string>();
const [requestingIdList, setRequestingIdList] = useState<ParentIdType[]>([]);
@@ -170,6 +172,7 @@ const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose }: Props)
onClose={onClose}
>
<ModalBody flex={'1 0 0'} overflow={'auto'} minH={'400px'}>
{moveHint && <LightTip text={moveHint} />}
<RenderList list={folderList} />
</ModalBody>
<ModalFooter>

View File

@@ -1,5 +1,4 @@
import { Box, Button, Flex, HStack } from '@chakra-ui/react';
import { useToast } from '@fastgpt/web/hooks/useToast';
import React from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
@@ -40,7 +39,7 @@ const FolderSlideCard = ({
deleteTip: string;
onDelete: () => void;
defaultPer: {
defaultPer?: {
value: PermissionValueType;
defaultValue: PermissionValueType;
onChange: (v: PermissionValueType) => Promise<any>;
@@ -54,7 +53,6 @@ const FolderSlideCard = ({
}) => {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const { toast } = useToast();
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
@@ -136,7 +134,7 @@ const FolderSlideCard = ({
</Box>
)}
{managePer.permission.hasManagePer && (
{managePer.permission.hasManagePer && !!defaultPer && (
<Box mt={5}>
<Box fontSize={'sm'} color={'myGray.500'}>
{t('common:permission.Default permission')}

View File

@@ -1,11 +1,9 @@
import React from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import CollaboratorContextProvider, { MemberManagerInputPropsType } from '../MemberManager/context';
import { Box, Button, Flex, HStack, ModalBody, useDisclosure } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import DefaultPermissionList from '../DefaultPerList';
import MyIcon from '@fastgpt/web/components/common/Icon';
import ResumeInherit from '../ResumeInheritText';
import { ChangeOwnerModal } from '../ChangeOwnerModal';
@@ -14,11 +12,6 @@ export type ConfigPerModalProps = {
avatar?: string;
name: string;
defaultPer: {
value: PermissionValueType;
defaultValue: PermissionValueType;
onChange: (v: PermissionValueType) => Promise<any>;
};
managePer: MemberManagerInputPropsType;
isInheritPermission?: boolean;
resumeInheritPermission?: () => void;
@@ -30,7 +23,6 @@ export type ConfigPerModalProps = {
const ConfigPerModal = ({
avatar,
name,
defaultPer,
managePer,
isInheritPermission,
resumeInheritPermission,
@@ -66,17 +58,6 @@ const ConfigPerModal = ({
<ResumeInherit onResume={resumeInheritPermission} />
</Box>
)}
<Box mt={5}>
<Box fontSize={'sm'}>{t('common:permission.Default permission')}</Box>
<DefaultPermissionList
mt="1"
per={defaultPer.value}
defaultPer={defaultPer.defaultValue}
isInheritPermission={isInheritPermission}
onChange={(v) => defaultPer.onChange(v)}
hasParent={hasParent}
/>
</Box>
<Box mt={4}>
<CollaboratorContextProvider
{...managePer}

View File

@@ -1,35 +1,22 @@
import React, { useMemo } from 'react';
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
import React from 'react';
import { PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
import { Box, StackProps, HStack } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { Permission } from '@fastgpt/global/support/permission/controller';
const PermissionIconText = ({
permission,
defaultPermission,
w = '1rem',
fontSize = 'mini',
iconColor = 'myGray.500',
private: Private = false,
...props
}: {
permission?: `${PermissionTypeEnum}`;
defaultPermission?: PermissionValueType;
private?: boolean;
iconColor?: string;
} & StackProps) => {
const { t } = useTranslation();
const per = useMemo(() => {
if (permission) return permission;
if (defaultPermission !== undefined) {
const Per = new Permission({ per: defaultPermission });
if (Per.hasWritePer) return PermissionTypeEnum.publicWrite;
if (Per.hasReadPer) return PermissionTypeEnum.publicRead;
return PermissionTypeEnum.clbPrivate;
}
return 'private';
}, [defaultPermission, permission]);
const per = Private ? 'private' : 'public';
return PermissionTypeMap[per] ? (
<HStack spacing={1} fontSize={fontSize} {...props}>

View File

@@ -2,12 +2,11 @@ import {
Flex,
Box,
ModalBody,
InputGroup,
InputLeftElement,
Input,
Checkbox,
ModalFooter,
Button
Button,
Grid,
HStack
} from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -18,63 +17,76 @@ import PermissionSelect from './PermissionSelect';
import PermissionTags from './PermissionTags';
import { CollaboratorContext } from './context';
import { useUserStore } from '@/web/support/user/useUserStore';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { ChevronDownIcon } from '@chakra-ui/icons';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
export type AddModalPropsType = {
onClose: () => void;
mode?: 'member' | 'all';
};
function AddMemberModal({ onClose }: AddModalPropsType) {
function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
const { t } = useTranslation();
const { userInfo, loadAndGetTeamMembers } = useUserStore();
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups } = useUserStore();
const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList } =
const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList, permission } =
useContextSelector(CollaboratorContext, (v) => v);
const [searchText, setSearchText] = useState<string>('');
const { data: members = [], loading: loadingMembers } = useRequest2(
const { data: [members = [], groups = []] = [], loading: loadingMembersAndGroups } = useRequest2(
async () => {
if (!userInfo?.team?.teamId) return [];
const members = await loadAndGetTeamMembers(true);
return members;
if (!userInfo?.team?.teamId) return [[], []];
return await Promise.all([loadAndGetTeamMembers(true), loadAndGetGroups(true)]);
},
{
manual: false,
refreshDeps: [userInfo?.team?.teamId]
}
);
const filterMembers = useMemo(() => {
return members.filter((item) => {
// if (item.permission.isOwner) return false;
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 [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
const [selectedGroupIdList, setSelectedGroupIdList] = useState<string[]>([]);
const [selectedPermission, setSelectedPermission] = useState(permissionList['read'].value);
const perLabel = useMemo(() => {
return getPerLabelList(selectedPermission).join('、');
}, [getPerLabelList, selectedPermission]);
const { mutate: onConfirm, isLoading: isUpdating } = useRequest({
mutationFn: () => {
return onUpdateCollaborators({
const { runAsync: onConfirm, loading: isUpdating } = useRequest2(
() =>
onUpdateCollaborators({
members: selectedMemberIdList,
groups: selectedGroupIdList,
permission: selectedPermission
});
},
successToast: t('common:common.Add Success'),
errorToast: 'Error',
onSuccess() {
onClose();
}),
{
successToast: t('common:common.Add Success'),
errorToast: 'Error',
onSuccess() {
onClose();
}
}
});
);
return (
<MyModal
@@ -83,17 +95,15 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
iconSrc="modal/AddClb"
title={t('user:team.add_collaborator')}
minW="800px"
isCentered
isLoading={loadingMembersAndGroups}
>
<ModalBody>
<MyBox
isLoading={loadingMembers}
display={'grid'}
minH="400px"
<Grid
border="1px solid"
borderColor="myGray.200"
borderRadius="0.5rem"
gridTemplateColumns="55% 45%"
fontSize={'sm'}
gridTemplateColumns="1fr 1fr"
>
<Flex
flexDirection="column"
@@ -102,17 +112,53 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
p="4"
minH="200px"
>
<InputGroup alignItems="center" size="sm">
<InputLeftElement>
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
</InputLeftElement>
<Input
placeholder={t('user:search_user')}
bgColor="myGray.50"
onChange={(e) => setSearchText(e.target.value)}
/>
</InputGroup>
<Flex flexDirection="column" mt="2">
<SearchInput
placeholder={t('user:search_user')}
bgColor="myGray.50"
onChange={(e) => setSearchText(e.target.value)}
/>
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
{filterGroups.map((group) => {
const onChange = () => {
if (selectedGroupIdList.includes(group._id)) {
setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== group._id));
} else {
setSelectedGroupIdList([...selectedGroupIdList, 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)}
icon={<MyIcon name={'common/check'} w={'12px'} />}
/>
<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 = () => {
if (selectedMemberIdList.includes(member.tmbId)) {
@@ -123,10 +169,10 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
};
const collaborator = collaboratorList.find((v) => v.tmbId === member.tmbId);
return (
<Flex
<HStack
justifyContent="space-between"
key={member.tmbId}
mt="1"
py="1"
py="2"
px="3"
borderRadius="sm"
alignItems="center"
@@ -137,51 +183,87 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
? { svg: { color: 'myGray.50' } }
: {})
}}
onClick={onChange}
>
<Checkbox
mr="3"
isChecked={selectedMemberIdList.includes(member.tmbId)}
icon={<MyIcon name={'common/check'} w={'12px'} />}
onChange={onChange}
/>
<Flex
flexDirection="row"
onClick={onChange}
w="full"
justifyContent="space-between"
>
<Flex flexDirection="row" alignItems="center">
<MyAvatar src={member.avatar} w="32px" />
<Box ml="2">{member.memberName}</Box>
</Flex>
{!!collaborator && (
<PermissionTags permission={collaborator.permission.value} />
)}
</Flex>
</Flex>
<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}
{t('user:has_chosen') + ': '}{' '}
{selectedMemberIdList.length + selectedGroupIdList.length}
</Box>
<Flex flexDirection="column" mt="2">
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
{selectedGroupIdList.map((groupId) => {
const onChange = () => {
if (selectedGroupIdList.includes(groupId)) {
setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== groupId));
} else {
setSelectedGroupIdList([...selectedGroupIdList, 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 ? (
<Flex
<HStack
justifyContent="space-between"
key={tmbId}
alignItems="center"
justifyContent="space-between"
py="2"
px={3}
borderRadius={'md'}
_hover={{ bg: 'myGray.50' }}
_notLast={{ mb: 2 }}
onClick={() =>
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
}
>
<Avatar src={member.avatar} w="24px" />
<MyAvatar src={member.avatar} w="1.5rem" borderRadius="50%" />
<Box w="full" ml={2}>
{member.memberName}
</Box>
@@ -192,16 +274,13 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
_hover={{
color: 'red.600'
}}
onClick={() =>
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
}
/>
</Flex>
</HStack>
) : null;
})}
</Flex>
</Flex>
</MyBox>
</Grid>
</ModalBody>
<ModalFooter>
<PermissionSelect

View File

@@ -8,11 +8,12 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
import { CollaboratorContext } from './context';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { useUserStore } from '@/web/support/user/useUserStore';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import Loading from '@fastgpt/web/components/common/MyLoading';
import { useTranslation } from 'next-i18next';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
export type ManageModalProps = {
onClose: () => void;
};
@@ -23,21 +24,12 @@ function ManageModal({ onClose }: ManageModalProps) {
const { permission, collaboratorList, onUpdateCollaborators, onDelOneCollaborator } =
useContextSelector(CollaboratorContext, (v) => v);
const { runAsync: onDelete, loading: isDeleting } = useRequest2((tmbId: string) =>
onDelOneCollaborator(tmbId)
);
const { runAsync: onDelete, loading: isDeleting } = useRequest2(onDelOneCollaborator);
const { runAsync: onUpdate, loading: isUpdating } = useRequest2(
({ tmbId, per }: { tmbId: string; per: PermissionValueType }) =>
onUpdateCollaborators({
members: [tmbId],
permission: per
}),
{
successToast: t('common.Update Success'),
errorToast: 'Error'
}
);
const { runAsync: onUpdate, loading: isUpdating } = useRequest2(onUpdateCollaborators, {
successToast: t('common.Update Success'),
errorToast: 'Error'
});
const loading = isDeleting || isUpdating;
@@ -74,7 +66,7 @@ function ManageModal({ onClose }: ManageModalProps) {
<Td border="none">
<Flex alignItems="center">
<Avatar src={item.avatar} w="24px" mr={2} />
{item.name}
{item.name === DefaultGroupName ? userInfo?.team.teamName : item.name}
</Flex>
</Td>
<Td border="none">
@@ -89,14 +81,18 @@ function ManageModal({ onClose }: ManageModalProps) {
<MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} />
}
value={item.permission.value}
onChange={(per) => {
onChange={(permission) => {
onUpdate({
tmbId: item.tmbId,
per
members: item.tmbId ? [item.tmbId] : undefined,
groups: item.groupId ? [item.groupId] : undefined,
permission
});
}}
onDelete={() => {
onDelete(item.tmbId);
onDelete({
tmbId: item.tmbId,
groupId: item.groupId
} as RequireOnlyOne<{ tmbId: string; groupId: string }>);
}}
/>
)}

View File

@@ -6,11 +6,14 @@ import { CollaboratorContext } from './context';
import Tag, { TagProps } from '@fastgpt/web/components/common/Tag';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { useTranslation } from 'next-i18next';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { useUserStore } from '@/web/support/user/useUserStore';
export type MemberListCardProps = BoxProps & { tagStyle?: Omit<TagProps, 'children'> };
const MemberListCard = ({ tagStyle, ...props }: MemberListCardProps) => {
const { t } = useTranslation();
const { userInfo } = useUserStore();
const { collaboratorList, isFetchingCollaborator } = useContextSelector(
CollaboratorContext,
@@ -27,10 +30,15 @@ const MemberListCard = ({ tagStyle, ...props }: MemberListCardProps) => {
<Flex gap="2" flexWrap={'wrap'}>
{collaboratorList?.map((member) => {
return (
<Tag key={member.tmbId} type={'fill'} colorSchema="white" {...tagStyle}>
<Tag
key={member.tmbId || member.groupId}
type={'fill'}
colorSchema="white"
{...tagStyle}
>
<Avatar src={member.avatar} w="1.25rem" />
<Box fontSize={'sm'} ml={1}>
{member.name}
{member.name === DefaultGroupName ? userInfo?.team.teamName : member.name}
</Box>
</Tag>
);

View File

@@ -15,6 +15,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useI18n } from '@/web/context/I18n';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
const AddMemberModal = dynamic(() => import('./AddMemberModal'));
const ManageModal = dynamic(() => import('./ManageModal'));
@@ -22,10 +23,12 @@ export type MemberManagerInputPropsType = {
permission: Permission;
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
permissionList: PermissionListType;
onUpdateCollaborators: (props: any) => any; // TODO: type. should be UpdatePermissionBody after app and dataset permission refactored
onDelOneCollaborator: (tmbId: string) => any;
onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>;
onDelOneCollaborator: (props: RequireOnlyOne<{ tmbId: string; groupId: string }>) => Promise<any>;
refreshDeps?: any[];
mode?: 'member' | 'all';
};
export type MemberManagerPropsType = MemberManagerInputPropsType & {
collaboratorList: CollaboratorItemType[];
refetchCollaboratorList: () => void;
@@ -72,7 +75,8 @@ const CollaboratorContextProvider = ({
refetchResource,
refreshDeps = [],
isInheritPermission,
hasParent
hasParent,
mode = 'member'
}: MemberManagerInputPropsType & {
children: (props: ChildrenProps) => ReactNode;
refetchResource?: () => void;
@@ -83,8 +87,10 @@ const CollaboratorContextProvider = ({
await onUpdateCollaborators(props);
refetchCollaboratorList();
};
const onDelOneCollaboratorThen = async (tmbId: string) => {
await onDelOneCollaborator(tmbId);
const onDelOneCollaboratorThen = async (
props: RequireOnlyOne<{ tmbId: string; groupId: string }>
) => {
await onDelOneCollaborator(props);
refetchCollaboratorList();
};
@@ -197,6 +203,7 @@ const CollaboratorContextProvider = ({
onCloseAddMember();
refetchResource?.();
}}
mode={mode}
/>
)}
{isOpenManageModal && (

View File

@@ -1,14 +1,5 @@
import React, { useMemo, useState } from 'react';
import {
Box,
Checkbox,
Flex,
Grid,
HStack,
Input,
InputGroup,
InputLeftElement
} from '@chakra-ui/react';
import { Box, Checkbox, Flex, Grid, HStack } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { useTranslation } from 'next-i18next';
@@ -16,6 +7,7 @@ import { Control, Controller } from 'react-hook-form';
import { RequireAtLeastOne } from '@fastgpt/global/common/type/utils';
import { useUserStore } from '@/web/support/user/useUserStore';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
type memberType = {
type: 'member';
@@ -120,19 +112,14 @@ function SelectMember({
h={'100%'}
>
<Flex flexDirection="column" p="4" h={'100%'} overflow={'auto'}>
<InputGroup alignItems="center" size={'sm'}>
<InputLeftElement>
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
</InputLeftElement>
<Input
placeholder={t('user:search_user')}
fontSize="sm"
bg={'myGray.50'}
onChange={(e) => {
setSearchKey(e.target.value);
}}
/>
</InputGroup>
<SearchInput
placeholder={t('user:search_user')}
fontSize="sm"
bg={'myGray.50'}
onChange={(e) => {
setSearchKey(e.target.value);
}}
/>
<Flex flexDirection="column" mt={3}>
{filtered.map((member) => {
return (