refactor: Add frontend code to DB-GPT (#912)

This commit is contained in:
katakuri
2023-12-11 00:05:42 +08:00
committed by GitHub
parent b8dc9cf11e
commit 43190ca333
189 changed files with 19179 additions and 16 deletions

View File

@@ -0,0 +1,152 @@
import { apiInterceptors, postAgentHubUpdate, postAgentInstall, postAgentQuery, postAgentUninstall } from '@/client/api';
import { IAgentPlugin, PostAgentQueryParams } from '@/types/agent';
import { useRequest } from 'ahooks';
import { Button, Card, Form, Input, Spin, Tag, Tooltip, message } from 'antd';
import { useCallback, useMemo, useState } from 'react';
import MyEmpty from '../common/MyEmpty';
import { ClearOutlined, DownloadOutlined, GithubOutlined, LoadingOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
function MarketPlugins() {
const { t } = useTranslation();
const [uploading, setUploading] = useState(false);
const [isError, setIsError] = useState(false);
const [actionIndex, setActionIndex] = useState<number | undefined>();
const [form] = Form.useForm<PostAgentQueryParams['filter']>();
const pagination = useMemo<{ pageNo: number; pageSize: number }>(
() => ({
pageNo: 1,
pageSize: 20,
}),
[],
);
const {
data: agents = [],
loading,
refresh,
} = useRequest(async () => {
const queryParams: PostAgentQueryParams = {
page_index: pagination.pageNo,
page_size: pagination.pageSize,
filter: form.getFieldsValue(),
};
const [err, res] = await apiInterceptors(postAgentQuery(queryParams));
setIsError(!!err);
return res?.datas ?? [];
});
const updateFromGithub = async () => {
try {
setUploading(true);
const [err] = await apiInterceptors(postAgentHubUpdate());
if (err) return;
message.success('success');
refresh();
} finally {
setUploading(false);
}
};
const pluginAction = useCallback(
async (name: string, index: number, isInstall: boolean) => {
if (actionIndex) return;
setActionIndex(index);
const [err] = await apiInterceptors((isInstall ? postAgentInstall : postAgentUninstall)(name));
if (!err) {
message.success('success');
refresh();
}
setActionIndex(undefined);
},
[actionIndex, refresh],
);
const renderAction = useCallback(
(agent: IAgentPlugin, index: number) => {
if (index === actionIndex) {
return <LoadingOutlined />;
}
return agent.installed ? (
<Tooltip title="Uninstall">
<div
className="w-full h-full"
onClick={() => {
pluginAction(agent.name, index, false);
}}
>
<ClearOutlined />
</div>
</Tooltip>
) : (
<Tooltip title="Install">
<div
className="w-full h-full"
onClick={() => {
pluginAction(agent.name, index, true);
}}
>
<DownloadOutlined />
</div>
</Tooltip>
);
},
[actionIndex, pluginAction],
);
return (
<Spin spinning={loading}>
<Form form={form} layout="inline" onFinish={refresh} className="mb-2">
<Form.Item className="!mb-2" name="name" label={'Name'}>
<Input allowClear className="w-48" />
</Form.Item>
<Form.Item>
<Button className="mr-2" type="primary" htmlType="submit" icon={<SearchOutlined />}>
{t('Search')}
</Button>
<Button loading={uploading} type="primary" icon={<SyncOutlined />} onClick={updateFromGithub}>
{t('Update_From_Github')}
</Button>
</Form.Item>
</Form>
{!agents.length && !loading && <MyEmpty error={isError} refresh={refresh} />}
<div className="flex flex-wrap gap-2 md:gap-4">
{agents.map((agent, index) => (
<Card
className="w-full md:w-1/2 lg:w-1/3 xl:w-1/4"
key={agent.id}
actions={[
renderAction(agent, index),
<Tooltip key="github" title="Github">
<div
className="w-full h-full"
onClick={() => {
window.open(agent.storage_url, '_blank');
}}
>
<GithubOutlined />
</div>
</Tooltip>,
]}
>
<Tooltip title={agent.name}>
<h2 className="mb-2 text-base font-semibold line-clamp-1">{agent.name}</h2>
</Tooltip>
{agent.author && <Tag>{agent.author}</Tag>}
{agent.version && <Tag>v{agent.version}</Tag>}
{agent.type && <Tag>Type {agent.type}</Tag>}
{agent.storage_channel && <Tag>{agent.storage_channel}</Tag>}
<Tooltip title={agent.description}>
<p className="mt-2 line-clamp-2 text-gray-400 text-sm">{agent.description}</p>
</Tooltip>
</Card>
))}
</div>
</Spin>
);
}
export default MarketPlugins;

View File

@@ -0,0 +1,124 @@
import { apiInterceptors, postAgentMy, postAgentUninstall, postAgentUpload } from '@/client/api';
import { IMyPlugin } from '@/types/agent';
import { useRequest } from 'ahooks';
import { Button, Card, Spin, Tag, Tooltip, Upload, UploadProps, message } from 'antd';
import { useCallback, useState } from 'react';
import MyEmpty from '../common/MyEmpty';
import { ClearOutlined, LoadingOutlined, UploadOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
function MyPlugins() {
const { t } = useTranslation();
const [messageApi, contextHolder] = message.useMessage();
const [uploading, setUploading] = useState(false);
const [isError, setIsError] = useState(false);
const [actionIndex, setActionIndex] = useState<number | undefined>();
const {
data = [],
loading,
refresh,
} = useRequest(async () => {
const [err, res] = await apiInterceptors(postAgentMy());
setIsError(!!err);
return res ?? [];
});
const uninstall = async (name: string, index: number) => {
if (actionIndex) return;
setActionIndex(index);
const [err] = await apiInterceptors(postAgentUninstall(name));
message[err ? 'error' : 'success'](err ? 'failed' : 'success');
!err && refresh();
setActionIndex(undefined);
};
const renderAction = useCallback(
(item: IMyPlugin, index: number) => {
if (index === actionIndex) {
return <LoadingOutlined />;
}
return (
<Tooltip title="Uninstall">
<div
className="w-full h-full"
onClick={() => {
uninstall(item.name, index);
}}
>
<ClearOutlined />
</div>
</Tooltip>
);
},
[actionIndex],
);
const onChange: UploadProps['onChange'] = async (info) => {
if (!info) {
message.error('Please select the *.zip,*.rar file');
return;
}
try {
const file = info.file;
setUploading(true);
const formData = new FormData();
formData.append('doc_file', file as any);
messageApi.open({ content: `Uploading ${file.name}`, type: 'loading', duration: 0 });
const [err] = await apiInterceptors(postAgentUpload(undefined, formData, { timeout: 60000 }));
if (err) return;
message.success('success');
refresh();
} catch (e: any) {
message.error(e?.message || 'Upload Error');
} finally {
setUploading(false);
messageApi.destroy();
}
};
return (
<Spin spinning={loading}>
{contextHolder}
<div>
<Upload
disabled={loading}
className="mr-1"
beforeUpload={() => false}
name="file"
accept=".zip,.rar"
multiple={false}
onChange={onChange}
showUploadList={{
showDownloadIcon: false,
showPreviewIcon: false,
showRemoveIcon: false,
}}
itemRender={() => <></>}
>
<Button loading={uploading} type="primary" icon={<UploadOutlined />}>
{t('Upload')}
</Button>
</Upload>
</div>
{!data.length && !loading && <MyEmpty error={isError} refresh={refresh} />}
<div className="flex gap-2 md:gap-4">
{data.map((item, index) => (
<Card className="w-full md:w-1/2 lg:w-1/3 xl:w-1/4" key={item.id} actions={[renderAction(item, index)]}>
<Tooltip title={item.name}>
<h2 className="mb-2 text-base font-semibold line-clamp-1">{item.name}</h2>
</Tooltip>
{item.version && <Tag>v{item.version}</Tag>}
{item.type && <Tag>Type {item.type}</Tag>}
<Tooltip title={item.description}>
<p className="mt-2 line-clamp-2 text-gray-400 text-sm">{item.description}</p>
</Tooltip>
</Card>
))}
</div>
</Spin>
);
}
export default MyPlugins;