feat: sync org from wecom, pref: member list pagination (#3549)
* feat: sync org * chore: fe * chore: loading * chore: type * pref: team member list change to pagination. Edit a sort of list apis. * feat: member update avatar * chore: user avatar move to tmb * chore: init scripts move user avatar * chore: sourceMember * fix: list api sourceMember * fix: member sync * fix: pagination * chore: adjust code * chore: move changeOwner to pro * chore: init v4819 script * chore: adjust code * chore: UserBox
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useCallback, useMemo, useEffect } from 'react';
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
billStatusMap,
|
||||
billTypeMap
|
||||
} from '@fastgpt/global/support/wallet/bill/constants';
|
||||
// import { usePagination } from '@/web/common/hooks/usePagination';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
@@ -33,25 +32,23 @@ import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
const BillTable = () => {
|
||||
const { t } = useTranslation();
|
||||
const { commonT } = useI18n();
|
||||
const { toast } = useToast();
|
||||
const [billType, setBillType] = useState<BillTypeEnum | ''>('');
|
||||
const [billType, setBillType] = useState<BillTypeEnum | undefined>(undefined);
|
||||
const [billDetail, setBillDetail] = useState<BillSchemaType>();
|
||||
|
||||
const billTypeList = useMemo(
|
||||
() =>
|
||||
[
|
||||
{ label: t('account_bill:all'), value: '' },
|
||||
{ label: t('account_bill:all'), value: undefined },
|
||||
...Object.entries(billTypeMap).map(([key, value]) => ({
|
||||
label: t(value.label as any),
|
||||
value: key
|
||||
}))
|
||||
] as {
|
||||
label: string;
|
||||
value: BillTypeEnum | '';
|
||||
value: BillTypeEnum | undefined;
|
||||
}[],
|
||||
[t]
|
||||
);
|
||||
@@ -62,8 +59,7 @@ const BillTable = () => {
|
||||
Pagination,
|
||||
getData,
|
||||
total
|
||||
} = usePagination({
|
||||
api: getBills,
|
||||
} = usePagination(getBills, {
|
||||
pageSize: 20,
|
||||
params: {
|
||||
type: billType
|
||||
@@ -110,7 +106,7 @@ const BillTable = () => {
|
||||
<Tr>
|
||||
<Th>#</Th>
|
||||
<Th>
|
||||
<MySelect<BillTypeEnum | ''>
|
||||
<MySelect
|
||||
list={billTypeList}
|
||||
value={billType}
|
||||
size={'sm'}
|
||||
@@ -181,7 +177,6 @@ export default BillTable;
|
||||
|
||||
function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) {
|
||||
const { t } = useTranslation();
|
||||
const { commonT } = useI18n();
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getInvoiceRecords } from '@/web/support/wallet/bill/invoice/api';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
@@ -30,8 +30,7 @@ const InvoiceTable = () => {
|
||||
isLoading,
|
||||
Pagination,
|
||||
total
|
||||
} = usePagination({
|
||||
api: getInvoiceRecords,
|
||||
} = usePagination(getInvoiceRecords, {
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -160,6 +160,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
color: 'myGray.900'
|
||||
};
|
||||
|
||||
const isSyncMember = feConfigs.register_method?.includes('sync');
|
||||
return (
|
||||
<Box>
|
||||
{/* user info */}
|
||||
@@ -224,6 +225,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
<Box {...labelStyles}>{t('account_info:member_name')}: </Box>
|
||||
<Input
|
||||
flex={'1 0 0'}
|
||||
disabled={isSyncMember}
|
||||
defaultValue={userInfo?.team?.memberName || 'Member'}
|
||||
title={t('account_info:click_modify_nickname')}
|
||||
borderColor={'transparent'}
|
||||
@@ -590,11 +592,6 @@ const Other = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { t } = useTranslation();
|
||||
const { isPc } = useSystem();
|
||||
const { userInfo, updateUserInfo } = useUserStore();
|
||||
|
||||
const { reset } = useForm<UserUpdateParams>({
|
||||
defaultValues: userInfo as UserType
|
||||
});
|
||||
|
||||
return (
|
||||
<Box>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Flex, useTheme } from '@chakra-ui/react';
|
||||
import { getInforms, readInform } from '@/web/support/user/inform/api';
|
||||
import type { UserInformSchema } from '@fastgpt/global/support/user/inform/type';
|
||||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
||||
import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import AccountContainer, { TabEnum } from './components/AccountContainer';
|
||||
import AccountContainer from './components/AccountContainer';
|
||||
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
|
||||
|
||||
const InformTable = () => {
|
||||
@@ -23,8 +22,7 @@ const InformTable = () => {
|
||||
Pagination,
|
||||
getData,
|
||||
pageNum
|
||||
} = usePagination<UserInformSchema>({
|
||||
api: getInforms,
|
||||
} = usePagination(getInforms, {
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { usePagination } from '@fastgpt/web/hooks/usePagination';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import AccountContainer, { TabEnum } from './components/AccountContainer';
|
||||
import AccountContainer from './components/AccountContainer';
|
||||
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
|
||||
|
||||
const Promotion = () => {
|
||||
@@ -41,8 +41,7 @@ const Promotion = () => {
|
||||
total,
|
||||
pageSize,
|
||||
Pagination
|
||||
} = usePagination({
|
||||
api: getPromotionRecords,
|
||||
} = usePagination(getPromotionRecords, {
|
||||
pageSize: 20
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
Tr,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
@@ -30,6 +29,8 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { delLeaveTeam } from '@/web/support/user/team/api';
|
||||
import { postSyncMembers } from '@/web/support/user/api';
|
||||
import MyLoading from '@fastgpt/web/components/common/MyLoading';
|
||||
|
||||
const InviteModal = dynamic(() => import('./InviteModal'));
|
||||
const TeamTagModal = dynamic(() => import('@/components/support/user/team/TeamTagModal'));
|
||||
@@ -40,8 +41,16 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
const { userInfo, teamPlanStatus } = useUserStore();
|
||||
const { feConfigs, setNotSufficientModalType } = useSystemStore();
|
||||
|
||||
const { groups, refetchGroups, myTeams, refetchTeams, members, refetchMembers, onSwitchTeam } =
|
||||
useContextSelector(TeamContext, (v) => v);
|
||||
const {
|
||||
groups,
|
||||
refetchGroups,
|
||||
myTeams,
|
||||
refetchTeams,
|
||||
members,
|
||||
refetchMembers,
|
||||
onSwitchTeam,
|
||||
MemberScrollData
|
||||
} = useContextSelector(TeamContext, (v) => v);
|
||||
|
||||
const {
|
||||
isOpen: isOpenTeamTagsAsync,
|
||||
@@ -54,6 +63,8 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
type: 'delete'
|
||||
});
|
||||
|
||||
const isSyncMember = feConfigs.register_method?.includes('sync');
|
||||
|
||||
const { runAsync: onLeaveTeam } = useRequest2(
|
||||
async () => {
|
||||
const defaultTeam = myTeams.find((item) => item.defaultTeam) || myTeams[0];
|
||||
@@ -72,8 +83,17 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
content: t('account_team:confirm_leave_team')
|
||||
});
|
||||
|
||||
const { runAsync: onSyncMember, loading: isSyncing } = useRequest2(postSyncMembers, {
|
||||
onSuccess() {
|
||||
refetchMembers();
|
||||
},
|
||||
successToast: t('account_team:sync_member_success'),
|
||||
errorToast: t('account_team:sync_member_failed')
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{isSyncing && <MyLoading />}
|
||||
<Flex justify={'space-between'} align={'center'} pb={'1rem'}>
|
||||
{Tabs}
|
||||
<HStack>
|
||||
@@ -91,7 +111,21 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
{t('account_team:label_sync')}
|
||||
</Button>
|
||||
)}
|
||||
{userInfo?.team.permission.hasManagePer && (
|
||||
{userInfo?.team.permission.hasManagePer && isSyncMember && (
|
||||
<Button
|
||||
variant={'primary'}
|
||||
size="md"
|
||||
borderRadius={'md'}
|
||||
ml={3}
|
||||
leftIcon={<MyIcon name="common/retryLight" w={'16px'} color={'white'} />}
|
||||
onClick={() => {
|
||||
onSyncMember();
|
||||
}}
|
||||
>
|
||||
{t('account_team:sync_immediately')}
|
||||
</Button>
|
||||
)}
|
||||
{userInfo?.team.permission.hasManagePer && !isSyncMember && (
|
||||
<Button
|
||||
variant={'primary'}
|
||||
size="md"
|
||||
@@ -135,76 +169,84 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
|
||||
<Box flex={'1 0 0'} overflow={'auto'}>
|
||||
<TableContainer overflow={'unset'} fontSize={'sm'}>
|
||||
<Table overflow={'unset'}>
|
||||
<Thead>
|
||||
<Tr bgColor={'white !important'}>
|
||||
<Th borderLeftRadius="6px" bgColor="myGray.100">
|
||||
{t('account_team:user_name')}
|
||||
</Th>
|
||||
<Th bgColor="myGray.100">{t('account_team:member_group')}</Th>
|
||||
<Th borderRightRadius="6px" bgColor="myGray.100">
|
||||
{t('common:common.Action')}
|
||||
</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{members?.map((item) => (
|
||||
<Tr key={item.userId} overflow={'unset'}>
|
||||
<Td>
|
||||
<HStack>
|
||||
<Avatar src={item.avatar} w={['18px', '22px']} borderRadius={'50%'} />
|
||||
<Box className={'textEllipsis'}>
|
||||
{item.memberName}
|
||||
{item.status === 'waiting' && (
|
||||
<Tag ml="2" colorSchema="yellow">
|
||||
{t('account_team:waiting')}
|
||||
</Tag>
|
||||
)}
|
||||
</Box>
|
||||
</HStack>
|
||||
</Td>
|
||||
<Td maxW={'300px'}>
|
||||
<GroupTags
|
||||
names={groups
|
||||
?.filter((group) => group.members.map((m) => m.tmbId).includes(item.tmbId))
|
||||
.map((g) => g.name)}
|
||||
max={3}
|
||||
/>
|
||||
</Td>
|
||||
<Td>
|
||||
{userInfo?.team.permission.hasManagePer &&
|
||||
item.role !== TeamMemberRoleEnum.owner &&
|
||||
item.tmbId !== userInfo?.team.tmbId && (
|
||||
<Icon
|
||||
name={'common/trash'}
|
||||
cursor={'pointer'}
|
||||
w="1rem"
|
||||
p="1"
|
||||
borderRadius="sm"
|
||||
_hover={{
|
||||
color: 'red.600',
|
||||
bgColor: 'myGray.100'
|
||||
}}
|
||||
onClick={() => {
|
||||
openRemoveMember(
|
||||
() =>
|
||||
delRemoveMember(item.tmbId).then(() =>
|
||||
Promise.all([refetchGroups(), refetchMembers()])
|
||||
),
|
||||
undefined,
|
||||
t('account_team:remove_tip', {
|
||||
username: item.memberName
|
||||
})
|
||||
)();
|
||||
}}
|
||||
{MemberScrollData && (
|
||||
<MemberScrollData>
|
||||
<Table overflow={'unset'}>
|
||||
<Thead>
|
||||
<Tr bgColor={'white !important'}>
|
||||
<Th borderLeftRadius="6px" bgColor="myGray.100">
|
||||
{t('account_team:user_name')}
|
||||
</Th>
|
||||
<Th bgColor="myGray.100">{t('account_team:member_group')}</Th>
|
||||
{!isSyncMember && (
|
||||
<Th borderRightRadius="6px" bgColor="myGray.100">
|
||||
{t('common:common.Action')}
|
||||
</Th>
|
||||
)}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{members?.map((item) => (
|
||||
<Tr key={item.userId} overflow={'unset'}>
|
||||
<Td>
|
||||
<HStack>
|
||||
<Avatar src={item.avatar} w={['18px', '22px']} borderRadius={'50%'} />
|
||||
<Box className={'textEllipsis'}>
|
||||
{item.memberName}
|
||||
{item.status === 'waiting' && (
|
||||
<Tag ml="2" colorSchema="yellow">
|
||||
{t('account_team:waiting')}
|
||||
</Tag>
|
||||
)}
|
||||
</Box>
|
||||
</HStack>
|
||||
</Td>
|
||||
<Td maxW={'300px'}>
|
||||
<GroupTags
|
||||
names={groups
|
||||
?.filter((group) =>
|
||||
group.members.map((m) => m.tmbId).includes(item.tmbId)
|
||||
)
|
||||
.map((g) => g.name)}
|
||||
max={3}
|
||||
/>
|
||||
</Td>
|
||||
{!isSyncMember && (
|
||||
<Td>
|
||||
userInfo?.team.permission.hasManagePer && item.role !==
|
||||
TeamMemberRoleEnum.owner && item.tmbId !== userInfo?.team.tmbId && (
|
||||
<Icon
|
||||
name={'common/trash'}
|
||||
cursor={'pointer'}
|
||||
w="1rem"
|
||||
p="1"
|
||||
borderRadius="sm"
|
||||
_hover={{
|
||||
color: 'red.600',
|
||||
bgColor: 'myGray.100'
|
||||
}}
|
||||
onClick={() => {
|
||||
openRemoveMember(
|
||||
() =>
|
||||
delRemoveMember(item.tmbId).then(() =>
|
||||
Promise.all([refetchGroups(), refetchMembers()])
|
||||
),
|
||||
undefined,
|
||||
t('account_team:remove_tip', {
|
||||
username: item.memberName
|
||||
})
|
||||
)();
|
||||
}}
|
||||
/>
|
||||
)
|
||||
</Td>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</MemberScrollData>
|
||||
)}
|
||||
<ConfirmRemoveMemberModal />
|
||||
</TableContainer>
|
||||
</Box>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { deleteOrg, deleteOrgMember } from '@/web/support/user/team/org/api';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import {
|
||||
Box,
|
||||
@@ -27,7 +26,7 @@ import { useMemo, useState } from 'react';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import MemberTag from '@/components/support/user/team/Info/MemberTag';
|
||||
import { TeamContext } from '../context';
|
||||
import { getOrgList } from '@/web/support/user/team/org/api';
|
||||
import { deleteOrg, deleteOrgMember, getOrgList } from '@/web/support/user/team/org/api';
|
||||
|
||||
import IconButton from './IconButton';
|
||||
import { defaultOrgForm, type OrgFormType } from './OrgInfoModal';
|
||||
@@ -37,6 +36,7 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import Path from '@/components/common/folder/Path';
|
||||
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
const OrgInfoModal = dynamic(() => import('./OrgInfoModal'));
|
||||
const OrgMemberManageModal = dynamic(() => import('./OrgMemberManageModal'));
|
||||
@@ -76,7 +76,9 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
const { userInfo, isTeamAdmin } = useUserStore();
|
||||
|
||||
const { members } = useContextSelector(TeamContext, (v) => v);
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
const isSyncMember = feConfigs.register_method?.includes('sync');
|
||||
const [parentPath, setParentPath] = useState('');
|
||||
const {
|
||||
data: orgs = [],
|
||||
@@ -174,9 +176,11 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
<Th bg="myGray.100" borderLeftRadius="6px">
|
||||
{t('common:Name')}
|
||||
</Th>
|
||||
<Th bg="myGray.100" borderRightRadius="6px">
|
||||
{t('common:common.Action')}
|
||||
</Th>
|
||||
{!isSyncMember && (
|
||||
<Th bg="myGray.100" borderRightRadius="6px">
|
||||
{t('common:common.Action')}
|
||||
</Th>
|
||||
)}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
@@ -197,8 +201,8 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
/>
|
||||
</HStack>
|
||||
</Td>
|
||||
<Td w={'6rem'}>
|
||||
{isTeamAdmin && (
|
||||
{isTeamAdmin && !isSyncMember && (
|
||||
<Td w={'6rem'}>
|
||||
<MyMenu
|
||||
trigger="hover"
|
||||
Button={<IconButton name="more" />}
|
||||
@@ -225,8 +229,8 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Td>
|
||||
</Td>
|
||||
)}
|
||||
</Tr>
|
||||
))}
|
||||
{currentOrg?.members.map((member) => {
|
||||
@@ -239,7 +243,7 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
<MemberTag name={memberInfo.memberName} avatar={memberInfo.avatar} />
|
||||
</Td>
|
||||
<Td w={'6rem'}>
|
||||
{isTeamAdmin && (
|
||||
{isTeamAdmin && !isSyncMember && (
|
||||
<MyMenu
|
||||
trigger={'hover'}
|
||||
Button={<IconButton name="more" />}
|
||||
@@ -268,57 +272,59 @@ function OrgTable({ Tabs }: { Tabs: React.ReactNode }) {
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{/* Slider */}
|
||||
<VStack w={'180px'} alignItems={'start'}>
|
||||
<HStack gap={'6px'}>
|
||||
<Avatar src={currentOrg?.avatar} w={'1rem'} h={'1rem'} rounded={'xs'} />
|
||||
<Box fontWeight={500} color={'myGray.900'}>
|
||||
{currentOrg?.name}
|
||||
</Box>
|
||||
{currentOrg?.path !== '' && (
|
||||
<IconButton name="edit" onClick={() => setEditOrg(currentOrg)} />
|
||||
)}
|
||||
</HStack>
|
||||
<Box fontSize={'xs'}>{currentOrg?.description || t('common:common.no_intro')}</Box>
|
||||
|
||||
<Divider my={'20px'} />
|
||||
|
||||
<Box fontWeight={500} fontSize="sm" color="myGray.900">
|
||||
{t('common:common.Action')}
|
||||
</Box>
|
||||
{currentOrg && isTeamAdmin && (
|
||||
<VStack gap="13px" w="100%">
|
||||
<ActionButton
|
||||
icon="common/add2"
|
||||
text={t('account_team:create_sub_org')}
|
||||
onClick={() => {
|
||||
setEditOrg({
|
||||
...defaultOrgForm,
|
||||
parentId: currentOrg?._id
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ActionButton
|
||||
icon="common/administrator"
|
||||
text={t('account_team:manage_member')}
|
||||
onClick={() => setManageMemberOrg(currentOrg)}
|
||||
/>
|
||||
{!isSyncMember && (
|
||||
<VStack w={'180px'} alignItems={'start'}>
|
||||
<HStack gap={'6px'}>
|
||||
<Avatar src={currentOrg?.avatar} w={'1rem'} h={'1rem'} rounded={'xs'} />
|
||||
<Box fontWeight={500} color={'myGray.900'}>
|
||||
{currentOrg?.name}
|
||||
</Box>
|
||||
{currentOrg?.path !== '' && (
|
||||
<>
|
||||
<ActionButton
|
||||
icon="common/file/move"
|
||||
text={t('account_team:move_org')}
|
||||
onClick={() => setMovingOrg(currentOrg)}
|
||||
/>
|
||||
<ActionButton
|
||||
icon="delete"
|
||||
text={t('account_team:delete_org')}
|
||||
onClick={() => deleteOrgHandler(currentOrg._id)}
|
||||
/>
|
||||
</>
|
||||
<IconButton name="edit" onClick={() => setEditOrg(currentOrg)} />
|
||||
)}
|
||||
</VStack>
|
||||
)}
|
||||
</VStack>
|
||||
</HStack>
|
||||
<Box fontSize={'xs'}>{currentOrg?.description || t('common:common.no_intro')}</Box>
|
||||
|
||||
<Divider my={'20px'} />
|
||||
|
||||
<Box fontWeight={500} fontSize="sm" color="myGray.900">
|
||||
{t('common:common.Action')}
|
||||
</Box>
|
||||
{currentOrg && isTeamAdmin && (
|
||||
<VStack gap="13px" w="100%">
|
||||
<ActionButton
|
||||
icon="common/add2"
|
||||
text={t('account_team:create_sub_org')}
|
||||
onClick={() => {
|
||||
setEditOrg({
|
||||
...defaultOrgForm,
|
||||
parentId: currentOrg?._id
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ActionButton
|
||||
icon="common/administrator"
|
||||
text={t('account_team:manage_member')}
|
||||
onClick={() => setManageMemberOrg(currentOrg)}
|
||||
/>
|
||||
{currentOrg?.path !== '' && (
|
||||
<>
|
||||
<ActionButton
|
||||
icon="common/file/move"
|
||||
text={t('account_team:move_org')}
|
||||
onClick={() => setMovingOrg(currentOrg)}
|
||||
/>
|
||||
<ActionButton
|
||||
icon="delete"
|
||||
text={t('account_team:delete_org')}
|
||||
onClick={() => deleteOrgHandler(currentOrg._id)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</VStack>
|
||||
)}
|
||||
</VStack>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{!!editOrg && (
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { ReactNode, useState } from 'react';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import type { EditTeamFormDataType } from './EditInfoModal';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { getTeamList, putSwitchTeam } from '@/web/support/user/team/api';
|
||||
import { getTeamList, getTeamMembers, putSwitchTeam } from '@/web/support/user/team/api';
|
||||
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import type { TeamTmbItemType, TeamMemberItemType } from '@fastgpt/global/support/user/team/type';
|
||||
@@ -10,7 +10,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getGroupList } from '@/web/support/user/team/group/api';
|
||||
import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||
import { OrgType } from '@fastgpt/global/support/user/team/org/type';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
|
||||
const EditInfoModal = dynamic(() => import('./EditInfoModal'));
|
||||
|
||||
@@ -26,6 +26,7 @@ type TeamModalContextType = {
|
||||
refetchTeams: () => void;
|
||||
refetchGroups: () => void;
|
||||
teamSize: number;
|
||||
MemberScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
|
||||
};
|
||||
|
||||
export const TeamContext = createContext<TeamModalContextType>({
|
||||
@@ -49,13 +50,14 @@ export const TeamContext = createContext<TeamModalContextType>({
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
|
||||
teamSize: 0
|
||||
teamSize: 0,
|
||||
MemberScrollData: undefined
|
||||
});
|
||||
|
||||
export const TeamModalContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const { t } = useTranslation();
|
||||
const [editTeamData, setEditTeamData] = useState<EditTeamFormDataType>();
|
||||
const { userInfo, initUserInfo, loadAndGetTeamMembers } = useUserStore();
|
||||
const { userInfo, initUserInfo } = useUserStore();
|
||||
|
||||
const {
|
||||
data: myTeams = [],
|
||||
@@ -69,18 +71,11 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode })
|
||||
// member action
|
||||
const {
|
||||
data: members = [],
|
||||
runAsync: refetchMembers,
|
||||
loading: loadingMembers
|
||||
} = useRequest2(
|
||||
() => {
|
||||
if (!userInfo?.team?.teamId) return Promise.resolve([]);
|
||||
return loadAndGetTeamMembers(true);
|
||||
},
|
||||
{
|
||||
manual: false,
|
||||
refreshDeps: [userInfo?.team?.teamId]
|
||||
}
|
||||
);
|
||||
isLoading: loadingMembers,
|
||||
refreshList: refetchMembers,
|
||||
total: memberTotal,
|
||||
ScrollData: MemberScrollData
|
||||
} = useScrollPagination(getTeamMembers, {});
|
||||
|
||||
const { runAsync: onSwitchTeam, loading: isSwitchingTeam } = useRequest2(
|
||||
async (teamId: string) => {
|
||||
@@ -115,7 +110,8 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode })
|
||||
refetchMembers,
|
||||
groups,
|
||||
refetchGroups,
|
||||
teamSize: members.length
|
||||
teamSize: memberTotal,
|
||||
MemberScrollData
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
|
||||
import AccountContainer from '../components/AccountContainer';
|
||||
import { Box, Flex, useDisclosure } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import Icon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import TeamSelector from '../components/TeamSelector';
|
||||
@@ -13,11 +13,10 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { TeamContext, TeamModalContextProvider } from './components/context';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MemberTable from './components/MemberTable';
|
||||
|
||||
const MemberTable = dynamic(() => import('./components/MemberTable'));
|
||||
const PermissionManage = dynamic(() => import('./components/PermissionManage/index'));
|
||||
const GroupManage = dynamic(() => import('./components/GroupManage/index'));
|
||||
|
||||
const OrgManage = dynamic(() => import('./components/OrgManage/index'));
|
||||
|
||||
export enum TeamTabEnum {
|
||||
@@ -34,7 +33,7 @@ const Team = () => {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
const { setEditTeamData, teamSize, isLoading } = useContextSelector(TeamContext, (v) => v);
|
||||
const { setEditTeamData, isLoading, teamSize } = useContextSelector(TeamContext, (v) => v);
|
||||
|
||||
const Tabs = useMemo(
|
||||
() => (
|
||||
|
||||
@@ -23,15 +23,16 @@ import DateRangePicker, {
|
||||
import { addDays } from 'date-fns';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import { formatNumber } from '@fastgpt/global/common/math/tools';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import AccountContainer, { TabEnum } from '../components/AccountContainer';
|
||||
import AccountContainer from '../components/AccountContainer';
|
||||
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import { getTeamMembers } from '@/web/support/user/team/api';
|
||||
|
||||
const UsageDetail = dynamic(() => import('./UsageDetail'));
|
||||
|
||||
@@ -44,7 +45,7 @@ const UsageTable = () => {
|
||||
});
|
||||
const [usageSource, setUsageSource] = useState<UsageSourceEnum | ''>('');
|
||||
const { isPc } = useSystem();
|
||||
const { userInfo, loadAndGetTeamMembers } = useUserStore();
|
||||
const { userInfo } = useUserStore();
|
||||
const [usageDetail, setUsageDetail] = useState<UsageItemType>();
|
||||
|
||||
const sourceList = useMemo(
|
||||
@@ -63,10 +64,7 @@ const UsageTable = () => {
|
||||
);
|
||||
|
||||
const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId);
|
||||
const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => {
|
||||
if (!userInfo?.team?.teamId) return [];
|
||||
return loadAndGetTeamMembers();
|
||||
});
|
||||
const { data: members, ScrollData } = useScrollPagination(getTeamMembers, {});
|
||||
const tmbList = useMemo(
|
||||
() =>
|
||||
members.map((item) => ({
|
||||
@@ -86,14 +84,13 @@ const UsageTable = () => {
|
||||
isLoading,
|
||||
Pagination,
|
||||
getData
|
||||
} = usePagination<UsageItemType>({
|
||||
api: getUserUsages,
|
||||
} = usePagination(getUserUsages, {
|
||||
pageSize: isPc ? 20 : 10,
|
||||
params: {
|
||||
dateStart: dateRange.from || new Date(),
|
||||
dateEnd: addDays(dateRange.to || new Date(), 1),
|
||||
source: usageSource,
|
||||
teamMemberId: selectTmbId
|
||||
source: usageSource as UsageSourceEnum,
|
||||
teamMemberId: selectTmbId ?? ''
|
||||
},
|
||||
defaultRequest: false
|
||||
});
|
||||
@@ -120,6 +117,7 @@ const UsageTable = () => {
|
||||
<MySelect
|
||||
size={'sm'}
|
||||
minW={'100px'}
|
||||
ScrollData={ScrollData}
|
||||
list={tmbList}
|
||||
value={selectTmbId}
|
||||
onchange={setSelectTmbId}
|
||||
|
||||
Reference in New Issue
Block a user