import React, { useState, useRef, useMemo } from 'react'; import { Box, Flex, TableContainer, Table, Thead, Tr, Th, Td, Tbody, MenuButton, Switch } from '@chakra-ui/react'; import { delDatasetCollectionById, putDatasetCollectionById, postLinkCollectionSync } from '@/web/core/dataset/api'; import { useQuery } from '@tanstack/react-query'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRouter } from 'next/router'; import MyMenu from '@fastgpt/web/components/common/MyMenu'; import { useEditTitle } from '@/web/common/hooks/useEditTitle'; import { DatasetCollectionTypeEnum, DatasetStatusEnum, DatasetCollectionSyncResultMap } from '@fastgpt/global/core/dataset/constants'; import { getCollectionIcon } from '@fastgpt/global/core/dataset/utils'; import { TabEnum } from '../../index'; import dynamic from 'next/dynamic'; import SelectCollections from '@/web/core/dataset/components/SelectCollections'; import { useToast } from '@fastgpt/web/hooks/useToast'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { useContextSelector } from 'use-context-selector'; import { CollectionPageContext } from './Context'; import { DatasetPageContext } from '@/web/core/dataset/context/datasetPageContext'; import { useI18n } from '@/web/context/I18n'; import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; import MyTag from '@fastgpt/web/components/common/Tag/index'; import { checkCollectionIsFolder, getTrainingTypeLabel } from '@fastgpt/global/core/dataset/collection/utils'; import { useFolderDrag } from '@/components/common/folder/useFolderDrag'; const Header = dynamic(() => import('./Header')); const EmptyCollectionTip = dynamic(() => import('./EmptyCollectionTip')); const CollectionCard = () => { const BoxRef = useRef(null); const router = useRouter(); const { toast } = useToast(); const { t } = useTranslation(); const { datasetT } = useI18n(); const { datasetDetail, loadDatasetDetail } = useContextSelector(DatasetPageContext, (v) => v); const { openConfirm: openDeleteConfirm, ConfirmModal: ConfirmDeleteModal } = useConfirm({ content: t('dataset.Confirm to delete the file'), type: 'delete' }); const { openConfirm: openSyncConfirm, ConfirmModal: ConfirmSyncModal } = useConfirm({ content: t('core.dataset.collection.Start Sync Tip') }); const { onOpenModal: onOpenEditTitleModal, EditModal: EditTitleModal } = useEditTitle({ title: t('Rename') }); const [moveCollectionData, setMoveCollectionData] = useState<{ collectionId: string }>(); const { collections, Pagination, total, getData, isGetting, pageNum, pageSize } = useContextSelector(CollectionPageContext, (v) => v); // Ad file status icon const formatCollections = useMemo( () => collections.map((collection) => { const icon = getCollectionIcon(collection.type, collection.name); const status = (() => { if (collection.trainingAmount > 0) { return { statusText: t('dataset.collections.Collection Embedding', { total: collection.trainingAmount }), colorSchema: 'gray' }; } return { statusText: t('core.dataset.collection.status.active'), colorSchema: 'green' }; })(); return { ...collection, icon, ...status }; }), [collections, t] ); const { runAsync: onUpdateCollection, loading: isUpdating } = useRequest2( putDatasetCollectionById, { onSuccess() { getData(pageNum); }, successToast: t('common.Update Success') } ); const { mutate: onDelCollection, isLoading: isDeleting } = useRequest({ mutationFn: (collectionId: string) => { return delDatasetCollectionById({ id: collectionId }); }, onSuccess() { getData(pageNum); }, successToast: t('common.Delete Success'), errorToast: t('common.Delete Failed') }); const { mutate: onclickStartSync, isLoading: isSyncing } = useRequest({ mutationFn: (collectionId: string) => { return postLinkCollectionSync(collectionId); }, onSuccess(res: DatasetCollectionSyncResultEnum) { getData(pageNum); toast({ status: 'success', title: t(DatasetCollectionSyncResultMap[res]?.label) }); }, errorToast: t('core.dataset.error.Start Sync Failed') }); const hasTrainingData = useMemo( () => !!formatCollections.find((item) => item.trainingAmount > 0), [formatCollections] ); useQuery( ['refreshCollection'], () => { getData(1); if (datasetDetail.status === DatasetStatusEnum.syncing) { loadDatasetDetail(datasetDetail._id); } return null; }, { refetchInterval: 6000, enabled: hasTrainingData || datasetDetail.status === DatasetStatusEnum.syncing } ); const { getBoxProps, isDropping } = useFolderDrag({ activeStyles: { bg: 'primary.100' }, onDrop: async (dragId: string, targetId: string) => { try { await putDatasetCollectionById({ id: dragId, parentId: targetId }); getData(pageNum); } catch (error) {} } }); const isLoading = isUpdating || isDeleting || isSyncing || (isGetting && collections.length === 0) || isDropping; return ( {/* header */}
{/* collection table */} {formatCollections.map((collection) => ( { if (collection.type === DatasetCollectionTypeEnum.folder) { router.push({ query: { ...router.query, parentId: collection._id } }); } else { router.push({ query: { ...router.query, collectionId: collection._id, currentTab: TabEnum.dataCard } }); } }} > ))}
{t('common.Name')} {datasetT('collection.Training type')} {t('dataset.collections.Data Amount')} {datasetT('collection.Create update time')} {t('common.Status')} {datasetT('Enable')}
{collection.name} {!checkCollectionIsFolder(collection.type) ? ( <>{t(getTrainingTypeLabel(collection.trainingType) || '-')} ) : ( '-' )} {collection.dataAmount || '-'} {formatTime2YMDHM(collection.createTime)} {formatTime2YMDHM(collection.updateTime)} {t(collection.statusText)} e.stopPropagation()}> onUpdateCollection({ id: collection._id, forbid: !e.target.checked }) } /> e.stopPropagation()}> {collection.permission.hasWritePer && ( } menuList={[ { children: [ ...(collection.type === DatasetCollectionTypeEnum.link ? [ { label: ( {t('core.dataset.collection.Sync')} ), onClick: () => openSyncConfirm(() => { onclickStartSync(collection._id); })() } ] : []), { label: ( {t('Move')} ), onClick: () => setMoveCollectionData({ collectionId: collection._id }) }, { label: ( {t('Rename')} ), onClick: () => onOpenEditTitleModal({ defaultVal: collection.name, onSuccess: (newName) => onUpdateCollection({ id: collection._id, name: newName }) }) } ] }, { children: [ { label: ( {t('common.Delete')} ), type: 'danger', onClick: () => openDeleteConfirm( () => { onDelCollection(collection._id); }, undefined, collection.type === DatasetCollectionTypeEnum.folder ? t('dataset.collections.Confirm to delete the folder') : t('dataset.Confirm to delete the file') )() } ] } ]} /> )}
{total > pageSize && ( )} {total === 0 && }
{!!moveCollectionData && ( setMoveCollectionData(undefined)} onSuccess={async ({ parentId }) => { await putDatasetCollectionById({ id: moveCollectionData.collectionId, parentId }); getData(pageNum); setMoveCollectionData(undefined); toast({ status: 'success', title: t('common.folder.Move Success') }); }} /> )} ); }; export default React.memo(CollectionCard);