import React, { useState, useMemo } from 'react'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { Flex, Input, Button, ModalBody, ModalFooter, Box } from '@chakra-ui/react'; import type { UseFormReturn } from 'react-hook-form'; import { useTranslation } from 'next-i18next'; import { getApiDatasetPaths, getApiDatasetCatalog } from '@/web/core/dataset/api'; import type { GetResourceFolderListItemResponse, GetResourceFolderListProps, ParentIdType } from '@fastgpt/global/common/parentFolder/type'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import type { GetApiDatasetCataLogProps } from '@/pages/api/core/dataset/apiDataset/getCatalog'; import MyBox from '@fastgpt/web/components/common/MyBox'; import { useBoolean, useMemoizedFn, useMount } from 'ahooks'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import MyModal from '@fastgpt/web/components/common/MyModal'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { FolderIcon } from '@fastgpt/global/common/file/image/constants'; import type { ApiDatasetServerType } from '@fastgpt/global/core/dataset/apiDataset/type'; const ApiDatasetForm = ({ type, datasetId, form }: { type: `${DatasetTypeEnum}`; datasetId?: string; form: UseFormReturn< { apiDatasetServer?: ApiDatasetServerType; }, any >; }) => { const { t } = useTranslation(); const { register, setValue, watch } = form; const apiDatasetServer = watch('apiDatasetServer'); const yuqueServer = apiDatasetServer?.yuqueServer; const feishuServer = apiDatasetServer?.feishuServer; const apiServer = apiDatasetServer?.apiServer; const [pathNames, setPathNames] = useState(t('dataset:rootdirectory')); const [ isOpenBaseurlSeletModal, { setTrue: openBaseurlSeletModal, setFalse: closeBaseurlSelectModal } ] = useBoolean(); const parentId = yuqueServer?.basePath || apiServer?.basePath; const canSelectBaseUrl = useMemo(() => { switch (type) { case DatasetTypeEnum.yuque: return yuqueServer?.userId && yuqueServer?.token; case DatasetTypeEnum.feishu: return feishuServer?.appId && feishuServer?.appSecret; case DatasetTypeEnum.apiDataset: return !!apiServer?.baseUrl; default: return false; } }, [ type, yuqueServer?.userId, yuqueServer?.token, feishuServer?.appId, feishuServer?.appSecret, apiServer?.baseUrl ]); // Unified function to get the current path const { loading: isFetching } = useRequest2( async () => { if ( !datasetId && ((yuqueServer && (!yuqueServer.userId || !yuqueServer?.token)) || (apiServer && !apiServer?.baseUrl)) ) { return setPathNames(t('dataset:input_required_field_to_select_baseurl')); } if (!parentId) { return setPathNames(t('dataset:rootdirectory')); } const path = await getApiDatasetPaths({ datasetId, parentId, apiDatasetServer }); setPathNames(path); }, { manual: false, refreshDeps: [datasetId, parentId] } ); // Unified handling of directory selection const onSelectBaseUrl = async (id: ParentIdType) => { const value = id === 'root' || id === null || id === undefined ? '' : id; switch (type) { case DatasetTypeEnum.yuque: setValue('apiDatasetServer.yuqueServer.basePath', value); break; case DatasetTypeEnum.feishu: setValue('apiDatasetServer.feishuServer.folderToken', value); break; case DatasetTypeEnum.apiDataset: setValue('apiDatasetServer.apiServer.basePath', value); break; } closeBaseurlSelectModal(); }; const renderBaseUrlSelector = () => ( Base URL {pathNames} ); // Render the directory selection modal const renderDirectoryModal = () => isOpenBaseurlSeletModal ? ( { const params: GetApiDatasetCataLogProps = { parentId: e.parentId, apiDatasetServer }; return getApiDatasetCatalog(params); }} onConfirm={onSelectBaseUrl} onClose={closeBaseurlSelectModal} /> ) : null; return ( <> {type === DatasetTypeEnum.apiDataset && ( <> {t('dataset:api_url')} Authorization {renderBaseUrlSelector()} {renderDirectoryModal()} )} {type === DatasetTypeEnum.feishu && ( <> App ID App Secret Folder Token {/* {renderBaseUrlSelector()} {renderDirectoryModal()} */} )} {type === DatasetTypeEnum.yuque && ( <> User ID Token {renderBaseUrlSelector()} {renderDirectoryModal()} )} ); }; export default ApiDatasetForm; type FolderItemType = { id: string; name: string; open: boolean; children?: FolderItemType[]; }; const rootId = 'root'; type Props = { selectId: string; server: (e: GetResourceFolderListProps) => Promise; onConfirm: (id: ParentIdType) => Promise; onClose: () => void; }; const BaseUrlSelector = ({ selectId, server, onConfirm, onClose }: Props) => { const { t } = useTranslation(); const [selectedId, setSelectedId] = React.useState(selectId); const [requestingIdList, setRequestingIdList] = useState([]); const [folderList, setFolderList] = useState([]); const { runAsync: requestServer } = useRequest2(async (e: GetResourceFolderListProps) => { if (requestingIdList.includes(e.parentId)) return Promise.reject(null); setRequestingIdList((state) => [...state, e.parentId]); return server(e).finally(() => setRequestingIdList((state) => state.filter((id) => id !== e.parentId)) ); }, {}); // Initialize the folder list useMount(async () => { const data = await requestServer({ parentId: null }); setFolderList([ { id: rootId, name: t('common:root_folder'), open: true, children: data.map((item) => ({ id: item.id, name: item.name, open: false })) } ]); }); const RenderList = useMemoizedFn( ({ list, index = 0 }: { list: FolderItemType[]; index?: number }) => { return ( <> {list.map((item) => ( setSelectedId('') } : { onClick: () => setSelectedId(item.id) })} > {index !== 0 && ( 0 ? 'visible' : 'hidden'} w={'1.25rem'} h={'1.25rem'} cursor={'pointer'} borderRadius={'xs'} _hover={{ bg: 'rgba(31, 35, 41, 0.08)' }} onClick={async (e) => { e.stopPropagation(); if (requestingIdList.includes(item.id)) return; if (!item.children) { const data = await requestServer({ parentId: item.id }); item.children = data.map((item) => ({ id: item.id, name: item.name, open: false })); } item.open = !item.open; setFolderList([...folderList]); }} > )} {item.name} {item.children && item.open && ( )} ))} ); } ); const { runAsync: onConfirmSelect, loading: confirming } = useRequest2( () => { if (selectedId) { return onConfirm(selectedId === rootId ? null : selectedId); } return Promise.reject(''); }, { onSuccess: () => { onClose(); } } ); return ( ); };