mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-08 12:30:14 +00:00
refactor: Add frontend code to DB-GPT (#912)
This commit is contained in:
152
web/components/agent/market-plugins.tsx
Normal file
152
web/components/agent/market-plugins.tsx
Normal 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;
|
124
web/components/agent/my-plugins.tsx
Normal file
124
web/components/agent/my-plugins.tsx
Normal 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;
|
Reference in New Issue
Block a user