feat: sync api collection will refresh title;perf: invite link ux (#4237)

* update queue

* feat: sync api collection will refresh title

* sync collection

* remove lock

* perf: invite link ux
This commit is contained in:
Archer
2025-03-19 21:03:21 +08:00
committed by archer
parent 73451dbc64
commit 87e90c37bd
44 changed files with 368 additions and 327 deletions
@@ -22,7 +22,13 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
function CreateInvitationModal({ onClose }: { onClose: (linkId?: string) => void }) {
function CreateInvitationModal({
onSuccess,
onClose
}: {
onSuccess: (linkId: string) => void;
onClose: () => void;
}) {
const { t } = useTranslation();
const expiresOptions: Array<{ label: string; value: InvitationLinkExpiresType }> = [
{ label: t('account_team:30mins'), value: '30m' }, // 30 mins
@@ -43,12 +49,11 @@ function CreateInvitationModal({ onClose }: { onClose: (linkId?: string) => void
const { runAsync: createInvitationLink, loading } = useRequest2(postCreateInvitationLink, {
manual: true,
successToast: t('common:common.Create Success'),
errorToast: t('common:common.Create Failed'),
onSuccess: (data) => {
onClose(data);
},
onFinally: () => onClose()
onSuccess(data);
onClose();
}
});
return (
@@ -35,15 +35,7 @@ import { useCallback } from 'react';
const CreateInvitationModal = dynamic(() => import('./CreateInvitationModal'));
const InviteModal = ({
teamId,
onClose,
onSuccess
}: {
teamId: string;
onClose: () => void;
onSuccess: () => void;
}) => {
const InviteModal = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation();
const {
@@ -57,10 +49,10 @@ const InviteModal = ({
const { isOpen: isOpenCreate, onOpen: onOpenCreate, onClose: onCloseCreate } = useDisclosure();
const isLoading = isLoadingLink;
const { copyData } = useCopyData();
const { userInfo } = useUserStore();
const { feConfigs } = useSystemStore();
const onCopy = useCallback(
(linkId: string) => {
const url = location.origin + `/account/team?invitelinkid=${linkId}`;
@@ -76,7 +68,7 @@ const InviteModal = ({
})
);
},
[copyData]
[copyData, feConfigs?.systemTitle, t, userInfo?.team.memberName, userInfo?.team.teamName]
);
const { runAsync: onForbid, loading: forbiding } = useRequest2(putForbidInvitationLink, {
@@ -131,13 +123,7 @@ const InviteModal = ({
<Td maxW="200px" minW="100px">
{item.description}
</Td>
<Td>
{isForbidden ? (
<Tag colorSchema="gray">{t('account_team:has_forbidden')}</Tag>
) : (
format(new Date(item.expires), 'yyyy-MM-dd HH:mm')
)}
</Td>
<Td>{format(new Date(item.expires), 'yyyy-MM-dd HH:mm')}</Td>
<Td>
{item.usedTimesLimit === -1
? t('account_team:unlimited')
@@ -153,7 +139,6 @@ const InviteModal = ({
cursor="pointer"
_hover={{ bg: 'myGray.100' }}
p="1.5"
w="fit-content"
>
<AvatarGroup max={3} avatars={item.members.map((i) => i.avatar)} />
</Box>
@@ -162,7 +147,7 @@ const InviteModal = ({
closeOnBlur={true}
>
{() => (
<Box py="4" maxH="200px" w="fit-content">
<Box py="4" maxH="200px">
<Flex mx="4" justifyContent="center" alignItems={'center'}>
<Box>{t('account_team:has_invited')}</Box>
<Box
@@ -175,15 +160,16 @@ const InviteModal = ({
{item.members.length}
</Box>
</Flex>
<Divider my="2" mx="4" />
<Divider my="2" />
<Grid
w="fit-content"
mt="2"
gridRowGap="4"
mt="4"
gap={4}
gridTemplateColumns="1fr 1fr"
overflow="auto"
alignItems="center"
mx="4"
maxH={'250px'}
>
{item.members.map((member) => (
<Box key={member.tmbId} justifySelf="start">
@@ -197,7 +183,9 @@ const InviteModal = ({
)}
</Td>
<Td>
{!isForbidden && (
{isForbidden ? (
<Tag colorSchema="red">{t('account_team:has_forbidden')}</Tag>
) : (
<>
<Button
size="sm"
@@ -261,17 +249,11 @@ const InviteModal = ({
</ModalFooter>
{isOpenCreate && (
<CreateInvitationModal
onClose={(linkId?: string) =>
Promise.all([
onCloseCreate(),
refetchInvitationLinkList(),
(() => {
if (linkId) {
onCopy(linkId);
}
})()
])
}
onSuccess={(linkId) => {
refetchInvitationLinkList();
onCopy(linkId);
}}
onClose={onCloseCreate}
/>
)}
</MyModal>
@@ -29,7 +29,6 @@ import {
DatasetCollectionTypeEnum,
DatasetStatusEnum,
DatasetCollectionSyncResultMap,
DatasetTypeEnum,
DatasetCollectionDataProcessModeMap
} from '@fastgpt/global/core/dataset/constants';
import { getCollectionIcon } from '@fastgpt/global/core/dataset/utils';
@@ -45,7 +44,10 @@ import { CollectionPageContext } from './Context';
import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import { checkCollectionIsFolder } from '@fastgpt/global/core/dataset/collection/utils';
import {
checkCollectionIsFolder,
collectionCanSync
} from '@fastgpt/global/core/dataset/collection/utils';
import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import TagsPopOver from './TagsPopOver';
import { useSystemStore } from '@/web/common/system/useSystemStore';
@@ -315,8 +317,7 @@ const CollectionCard = () => {
menuList={[
{
children: [
...(collection.type === DatasetCollectionTypeEnum.link ||
datasetDetail.type === DatasetTypeEnum.apiDataset
...(collectionCanSync(collection.type)
? [
{
label: (
@@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'next-i18next';
import { Box, Flex, IconButton, useTheme, Progress } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
@@ -9,6 +9,8 @@ import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import MyPopover from '@fastgpt/web/components/common/MyPopover';
import ParentPaths from '@/components/common/ParentPaths';
import { getTrainingQueueLen } from '@/web/core/dataset/api';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
export enum TabEnum {
dataCard = 'dataCard',
@@ -24,8 +26,68 @@ const NavBar = ({ currentTab }: { currentTab: TabEnum }) => {
const router = useRouter();
const query = router.query;
const { isPc } = useSystem();
const { datasetDetail, vectorTrainingMap, agentTrainingMap, rebuildingCount, paths } =
useContextSelector(DatasetPageContext, (v) => v);
const { datasetDetail, rebuildingCount, paths } = useContextSelector(
DatasetPageContext,
(v) => v
);
// global queue
const {
data: {
vectorTrainingCount = 0,
qaTrainingCount = 0,
autoTrainingCount = 0,
imageTrainingCount = 0
} = {}
} = useRequest2(getTrainingQueueLen, {
manual: false,
retryInterval: 10000
});
const { vectorTrainingMap, qaTrainingMap, autoTrainingMap, imageTrainingMap } = useMemo(() => {
const vectorTrainingMap = (() => {
if (vectorTrainingCount < 1000)
return {
colorSchema: 'green',
tip: t('common:core.dataset.training.Leisure')
};
if (vectorTrainingCount < 20000)
return {
colorSchema: 'yellow',
tip: t('common:core.dataset.training.Waiting')
};
return {
colorSchema: 'red',
tip: t('common:core.dataset.training.Full')
};
})();
const countLLMMap = (count: number) => {
if (count < 100)
return {
colorSchema: 'green',
tip: t('common:core.dataset.training.Leisure')
};
if (count < 1000)
return {
colorSchema: 'yellow',
tip: t('common:core.dataset.training.Waiting')
};
return {
colorSchema: 'red',
tip: t('common:core.dataset.training.Full')
};
};
const qaTrainingMap = countLLMMap(qaTrainingCount);
const autoTrainingMap = countLLMMap(autoTrainingCount);
const imageTrainingMap = countLLMMap(imageTrainingCount);
return {
vectorTrainingMap,
qaTrainingMap,
autoTrainingMap,
imageTrainingMap
};
}, [qaTrainingCount, autoTrainingCount, imageTrainingCount, vectorTrainingCount, t]);
const tabList = [
{
@@ -172,12 +234,38 @@ const NavBar = ({ currentTab }: { currentTab: TabEnum }) => {
)}
<Box mb={3}>
<Box fontSize={'sm'} pb={1}>
{t('common:core.dataset.training.Agent queue')}({agentTrainingMap.tip})
{t('common:core.dataset.training.Agent queue')}({qaTrainingMap.tip})
</Box>
<Progress
value={100}
size={'xs'}
colorScheme={agentTrainingMap.colorSchema}
colorScheme={qaTrainingMap.colorSchema}
borderRadius={'md'}
isAnimated
hasStripe
/>
</Box>
<Box mb={3}>
<Box fontSize={'sm'} pb={1}>
{t('dataset:auto_training_queue')}({autoTrainingMap.tip})
</Box>
<Progress
value={100}
size={'xs'}
colorScheme={autoTrainingMap.colorSchema}
borderRadius={'md'}
isAnimated
hasStripe
/>
</Box>
<Box mb={3}>
<Box fontSize={'sm'} pb={1}>
{t('dataset:image_training_queue')}({imageTrainingMap.tip})
</Box>
<Progress
value={100}
size={'xs'}
colorScheme={imageTrainingMap.colorSchema}
borderRadius={'md'}
isAnimated
hasStripe