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 (
);
};