pref: member list (#4344)
* chore: search member new api * chore: permission * fix: ts error * fix: member modal
This commit is contained in:
@@ -29,12 +29,14 @@ function OrgInfoModal({
|
||||
editOrg,
|
||||
onClose,
|
||||
onSuccess,
|
||||
updateCurrentOrg
|
||||
updateCurrentOrg,
|
||||
parentId
|
||||
}: {
|
||||
editOrg: OrgFormType;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
updateCurrentOrg: (data: { name?: string; avatar?: string; description?: string }) => void;
|
||||
parentId?: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -51,10 +53,11 @@ function OrgInfoModal({
|
||||
|
||||
const { run: onCreate, loading: isLoadingCreate } = useRequest2(
|
||||
async (data: OrgFormType) => {
|
||||
if (parentId === undefined) return;
|
||||
return postCreateOrg({
|
||||
name: data.name,
|
||||
avatar: data.avatar,
|
||||
path: editOrg.path,
|
||||
orgId: parentId,
|
||||
description: data.description
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import { getOrgMembers, putUpdateOrgMembers } from '@/web/support/user/team/org/api';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Grid,
|
||||
HStack,
|
||||
ModalBody,
|
||||
ModalFooter
|
||||
} from '@chakra-ui/react';
|
||||
import { putUpdateOrgMembers } from '@/web/support/user/team/org/api';
|
||||
import { Box, Button, Flex, Grid, HStack, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import type { GroupMemberRole } from '@fastgpt/global/support/permission/memberGroup/constant';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
@@ -17,11 +8,11 @@ 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 { useEffect, useMemo, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { OrgListItemType } from '@fastgpt/global/support/user/team/org/type';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
|
||||
import { getTeamMembers } from '@/web/support/user/team/api';
|
||||
import MemberItemCard from '@/components/support/permission/MemberManager/MemberItemCard';
|
||||
|
||||
export type GroupFormType = {
|
||||
members: {
|
||||
@@ -30,16 +21,6 @@ export type GroupFormType = {
|
||||
}[];
|
||||
};
|
||||
|
||||
function CheckboxIcon({
|
||||
name
|
||||
}: {
|
||||
isChecked?: boolean;
|
||||
isIndeterminate?: boolean;
|
||||
name: IconNameType;
|
||||
}) {
|
||||
return <MyIcon name={name} w="12px" />;
|
||||
}
|
||||
|
||||
function OrgMemberManageModal({
|
||||
currentOrg,
|
||||
refetchOrgs,
|
||||
@@ -56,17 +37,24 @@ function OrgMemberManageModal({
|
||||
ScrollData: MemberScrollData,
|
||||
isLoading: isLoadingMembers
|
||||
} = useScrollPagination(getTeamMembers, {
|
||||
pageSize: 20
|
||||
pageSize: 20,
|
||||
params: {
|
||||
withOrgs: true,
|
||||
withPermission: false,
|
||||
status: 'active'
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
data: orgMembers,
|
||||
ScrollData: OrgMemberScrollData,
|
||||
isLoading: isLoadingOrgMembers
|
||||
} = useScrollPagination(getOrgMembers, {
|
||||
pageSize: 20,
|
||||
} = useScrollPagination(getTeamMembers, {
|
||||
pageSize: 100000,
|
||||
params: {
|
||||
orgPath: getOrgChildrenPath(currentOrg)
|
||||
orgId: currentOrg._id,
|
||||
withOrgs: false,
|
||||
withPermission: false
|
||||
}
|
||||
});
|
||||
|
||||
@@ -83,11 +71,6 @@ function OrgMemberManageModal({
|
||||
}, [orgMembers]);
|
||||
|
||||
const [searchKey, setSearchKey] = useState('');
|
||||
const filterMembers = useMemo(() => {
|
||||
if (!searchKey) return allMembers;
|
||||
const regx = new RegExp(searchKey, 'i');
|
||||
return allMembers.filter((member) => regx.test(member.memberName));
|
||||
}, [searchKey, allMembers]);
|
||||
|
||||
const { run: onUpdate, loading: isLoadingUpdate } = useRequest2(
|
||||
() => {
|
||||
@@ -165,35 +148,20 @@ function OrgMemberManageModal({
|
||||
}}
|
||||
/>
|
||||
<MemberScrollData mt={3} flexGrow="1" overflow={'auto'} isLoading={isLoadingMembers}>
|
||||
{filterMembers.map((member) => {
|
||||
{allMembers.map((member) => {
|
||||
return (
|
||||
<HStack
|
||||
py="2"
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
alignItems="center"
|
||||
<MemberItemCard
|
||||
avatar={member.avatar}
|
||||
key={member.tmbId}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
bg: 'myGray.50',
|
||||
...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {})
|
||||
}}
|
||||
_notLast={{ mb: 2 }}
|
||||
onClick={() => handleToggleSelect(member.tmbId)}
|
||||
>
|
||||
<Checkbox
|
||||
isChecked={!!isSelected(member.tmbId)}
|
||||
icon={<CheckboxIcon name={'common/check'} />}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||
<Box>{member.memberName}</Box>
|
||||
</HStack>
|
||||
name={member.memberName}
|
||||
onChange={() => handleToggleSelect(member.tmbId)}
|
||||
isChecked={!!isSelected(member.tmbId)}
|
||||
orgs={member.orgs}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</MemberScrollData>
|
||||
</Flex>
|
||||
{/* <Flex mt={3} flexDirection="column" flexGrow="1" overflow={'auto'} maxH={'100%'}> */}
|
||||
<Flex flexDirection="column" p="4" overflowY="auto" overflowX="hidden">
|
||||
<OrgMemberScrollData
|
||||
mt={3}
|
||||
|
||||
@@ -20,8 +20,6 @@ function OrgMoveModal({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedOrg, setSelectedOrg] = useState<OrgListItemType>();
|
||||
const { userInfo } = useUserStore();
|
||||
const team = userInfo?.team!;
|
||||
|
||||
const { runAsync: onMoveOrg, loading } = useRequest2(putMoveOrg, {
|
||||
onSuccess: () => {
|
||||
@@ -39,7 +37,7 @@ function OrgMoveModal({
|
||||
iconColor="primary.600"
|
||||
>
|
||||
<ModalBody>
|
||||
<OrgTree selectedOrg={selectedOrg} setSelectedOrg={setSelectedOrg} />
|
||||
<OrgTree selectedOrg={selectedOrg} setSelectedOrg={setSelectedOrg} movingOrg={movingOrg} />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
|
||||
@@ -14,17 +14,19 @@ function OrgTreeNode({
|
||||
org,
|
||||
selectedOrg,
|
||||
setSelectedOrg,
|
||||
index = 0
|
||||
index = 0,
|
||||
movingOrg
|
||||
}: {
|
||||
org: OrgListItemType;
|
||||
selectedOrg?: OrgListItemType;
|
||||
setSelectedOrg: (org?: OrgListItemType) => void;
|
||||
index?: number;
|
||||
movingOrg: OrgListItemType;
|
||||
}) {
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(index === 0);
|
||||
const [canBeExpanded, setCanBeExpanded] = useState(true);
|
||||
const { data: orgs = [], runAsync: getOrgs } = useRequest2(() =>
|
||||
getOrgList({ orgPath: getOrgChildrenPath(org) })
|
||||
getOrgList({ orgId: org._id, withPermission: false })
|
||||
);
|
||||
const onClickExpand = async () => {
|
||||
const data = await getOrgs();
|
||||
@@ -34,6 +36,9 @@ function OrgTreeNode({
|
||||
toggleIsExpanded.toggle();
|
||||
};
|
||||
|
||||
if (org._id === movingOrg._id) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Box userSelect={'none'}>
|
||||
<HStack
|
||||
@@ -78,6 +83,7 @@ function OrgTreeNode({
|
||||
orgs.map((child) => (
|
||||
<Box key={child._id} mt={0.5}>
|
||||
<OrgTreeNode
|
||||
movingOrg={movingOrg}
|
||||
org={child}
|
||||
index={index + 1}
|
||||
selectedOrg={selectedOrg}
|
||||
@@ -91,10 +97,12 @@ function OrgTreeNode({
|
||||
|
||||
function OrgTree({
|
||||
selectedOrg,
|
||||
setSelectedOrg
|
||||
setSelectedOrg,
|
||||
movingOrg
|
||||
}: {
|
||||
selectedOrg?: OrgListItemType;
|
||||
setSelectedOrg: (org?: OrgListItemType) => void;
|
||||
movingOrg: OrgListItemType;
|
||||
}) {
|
||||
const { userInfo } = useUserStore();
|
||||
const root: OrgListItemType = {
|
||||
@@ -107,6 +115,7 @@ function OrgTree({
|
||||
|
||||
return (
|
||||
<OrgTreeNode
|
||||
movingOrg={movingOrg}
|
||||
key={'root'}
|
||||
org={root}
|
||||
selectedOrg={selectedOrg}
|
||||
|
||||
@@ -78,9 +78,6 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
const [editOrg, setEditOrg] = useState<OrgFormType>();
|
||||
const [manageMemberOrg, setManageMemberOrg] = useState<OrgListItemType>();
|
||||
const [movingOrg, setMovingOrg] = useState<OrgListItemType>();
|
||||
|
||||
const [searchOrg, setSearchOrg] = useState('');
|
||||
|
||||
const {
|
||||
currentOrg,
|
||||
orgs,
|
||||
@@ -91,7 +88,9 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
MemberScrollData,
|
||||
onPathClick,
|
||||
refresh,
|
||||
updateCurrentOrg
|
||||
updateCurrentOrg,
|
||||
setSearchKey,
|
||||
searchKey
|
||||
} = useOrg();
|
||||
|
||||
// Delete org
|
||||
@@ -123,12 +122,6 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
onSuccess: refresh
|
||||
});
|
||||
|
||||
const searchedOrgs = useMemo(() => {
|
||||
if (!searchOrg) return [];
|
||||
|
||||
return orgs.filter((org) => org.name.includes(searchOrg));
|
||||
}, [orgs, searchOrg]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
|
||||
@@ -136,8 +129,8 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
<Box w="200px">
|
||||
<SearchInput
|
||||
placeholder={t('account_team:search_org')}
|
||||
value={searchOrg}
|
||||
onChange={(e) => setSearchOrg(e.target.value)}
|
||||
value={searchKey}
|
||||
onChange={(e) => setSearchKey(e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
@@ -166,102 +159,55 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{searchedOrgs.map((org) => (
|
||||
<Tr key={org._id} overflow={'unset'} onClick={() => onClickOrg(org)}>
|
||||
<Td>
|
||||
<HStack cursor={'pointer'} onClick={() => onClickOrg(org)}>
|
||||
<MemberTag name={org.name} avatar={org.avatar!} />
|
||||
<Tag size="sm">{org.total}</Tag>
|
||||
<MyIcon
|
||||
name="core/chat/chevronRight"
|
||||
w={'1rem'}
|
||||
h={'1rem'}
|
||||
color={'myGray.500'}
|
||||
/>
|
||||
</HStack>
|
||||
</Td>
|
||||
{isTeamAdmin && !isSyncMember && (
|
||||
<Td w={'6rem'}>
|
||||
<MyMenu
|
||||
trigger="hover"
|
||||
Button={<IconButton name="more" />}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: 'edit',
|
||||
label: t('account_team:edit_info'),
|
||||
onClick: () => setEditOrg(org)
|
||||
},
|
||||
{
|
||||
icon: 'common/file/move',
|
||||
label: t('common:Move'),
|
||||
onClick: () => setMovingOrg(org)
|
||||
},
|
||||
{
|
||||
icon: 'delete',
|
||||
label: t('account_team:delete'),
|
||||
type: 'danger',
|
||||
onClick: () => deleteOrgHandler(org._id)
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{orgs
|
||||
.filter((org) => org.path !== '')
|
||||
.map((org) => (
|
||||
<Tr key={org._id} overflow={'unset'}>
|
||||
<Td>
|
||||
<HStack cursor={'pointer'} onClick={() => onClickOrg(org)}>
|
||||
<MemberTag name={org.name} avatar={org.avatar} />
|
||||
<Tag size="sm">{org.total}</Tag>
|
||||
<MyIcon
|
||||
name="core/chat/chevronRight"
|
||||
w={'1rem'}
|
||||
h={'1rem'}
|
||||
color={'myGray.500'}
|
||||
/>
|
||||
</HStack>
|
||||
</Td>
|
||||
)}
|
||||
</Tr>
|
||||
))}
|
||||
{!searchOrg &&
|
||||
orgs
|
||||
.filter((org) => org.path !== '')
|
||||
.map((org) => (
|
||||
<Tr key={org._id} overflow={'unset'}>
|
||||
<Td>
|
||||
<HStack cursor={'pointer'} onClick={() => onClickOrg(org)}>
|
||||
<MemberTag name={org.name} avatar={org.avatar} />
|
||||
<Tag size="sm">{org.total}</Tag>
|
||||
<MyIcon
|
||||
name="core/chat/chevronRight"
|
||||
w={'1rem'}
|
||||
h={'1rem'}
|
||||
color={'myGray.500'}
|
||||
/>
|
||||
</HStack>
|
||||
{isTeamAdmin && !isSyncMember && (
|
||||
<Td w={'6rem'}>
|
||||
<MyMenu
|
||||
trigger="hover"
|
||||
Button={<IconButton name="more" />}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: 'edit',
|
||||
label: t('account_team:edit_info'),
|
||||
onClick: () => setEditOrg(org)
|
||||
},
|
||||
{
|
||||
icon: 'common/file/move',
|
||||
label: t('common:Move'),
|
||||
onClick: () => setMovingOrg(org)
|
||||
},
|
||||
{
|
||||
icon: 'delete',
|
||||
label: t('account_team:delete'),
|
||||
type: 'danger',
|
||||
onClick: () => deleteOrgHandler(org._id)
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Td>
|
||||
{isTeamAdmin && !isSyncMember && (
|
||||
<Td w={'6rem'}>
|
||||
<MyMenu
|
||||
trigger="hover"
|
||||
Button={<IconButton name="more" />}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
icon: 'edit',
|
||||
label: t('account_team:edit_info'),
|
||||
onClick: () => setEditOrg(org)
|
||||
},
|
||||
{
|
||||
icon: 'common/file/move',
|
||||
label: t('common:Move'),
|
||||
onClick: () => setMovingOrg(org)
|
||||
},
|
||||
{
|
||||
icon: 'delete',
|
||||
label: t('account_team:delete'),
|
||||
type: 'danger',
|
||||
onClick: () => deleteOrgHandler(org._id)
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Td>
|
||||
)}
|
||||
</Tr>
|
||||
))}
|
||||
{!searchOrg &&
|
||||
)}
|
||||
</Tr>
|
||||
))}
|
||||
{!searchKey &&
|
||||
members.map((member) => {
|
||||
return (
|
||||
<Tr key={member.tmbId}>
|
||||
@@ -403,6 +349,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
onClose={() => setEditOrg(undefined)}
|
||||
onSuccess={refresh}
|
||||
updateCurrentOrg={updateCurrentOrg}
|
||||
parentId={currentOrg._id}
|
||||
/>
|
||||
)}
|
||||
{!!movingOrg && (
|
||||
|
||||
Reference in New Issue
Block a user