From 7db6cb8ca3c7effc8301c8e8e420842cc3848f57 Mon Sep 17 00:00:00 2001 From: chanx <1243304602@qq.com> Date: Tue, 11 Nov 2025 11:18:07 +0800 Subject: [PATCH] Fixes: Bugs fixed #10703 (#11154) ### What problem does this PR solve? Fixes: Bugs fixed - Removed invalid code, - Modified the user center style, - Added an automatic data source parsing switch. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- web/src/interfaces/database/knowledge.ts | 1 + web/src/locales/en.ts | 1 + web/src/locales/zh.ts | 1 + .../agent/gobal-variable-sheet/index.tsx | 22 +- .../components/link-data-source.tsx | 34 ++- .../dataset/dataset-setting/form-schema.ts | 1 + .../pages/dataset/dataset-setting/index.tsx | 30 ++- web/src/pages/home/datasets.tsx | 12 +- web/src/pages/login-next/index.tsx | 5 +- .../components/user-setting-header/index.tsx | 44 ++++ .../data-source-detail-page/log-table.tsx | 4 +- .../pages/user-setting/data-source/index.tsx | 102 ++++----- web/src/pages/user-setting/index.tsx | 7 +- .../user-setting/mcp/edit-mcp-dialog.tsx | 178 +++++++++++++++ .../pages/user-setting/mcp/edit-mcp-form.tsx | 171 ++++++++++++++ .../mcp/import-mcp-dialog/import-mcp-form.tsx | 72 ++++++ .../mcp/import-mcp-dialog/index.tsx | 36 +++ web/src/pages/user-setting/mcp/index.tsx | 157 +++++++++++++ web/src/pages/user-setting/mcp/mcp-card.tsx | 74 +++++++ .../pages/user-setting/mcp/mcp-operation.tsx | 43 ++++ web/src/pages/user-setting/mcp/tool-card.tsx | 16 ++ .../user-setting/mcp/use-bulk-operate-mcp.tsx | 56 +++++ .../pages/user-setting/mcp/use-edit-mcp.ts | 53 +++++ .../pages/user-setting/mcp/use-export-mcp.ts | 21 ++ .../pages/user-setting/mcp/use-import-mcp.ts | 73 ++++++ web/src/pages/user-setting/profile/index.tsx | 29 +-- .../setting-model/components/modal-card.tsx | 2 +- .../components/system-setting.tsx | 5 +- .../setting-model/components/un-add-model.tsx | 18 +- .../setting-model/components/used-model.tsx | 2 +- .../user-setting/setting-model/index.tsx | 4 +- .../user-setting/setting-password/index.less | 3 - .../user-setting/setting-password/index.tsx | 137 ------------ .../user-setting/setting-profile/index.less | 7 - .../user-setting/setting-profile/index.tsx | 208 ------------------ .../user-setting/setting-system/index.less | 33 --- .../user-setting/setting-system/index.tsx | 127 ----------- .../setting-system/task-bar-chat.tsx | 108 --------- .../pages/user-setting/setting-team/index.tsx | 38 ++-- .../setting-team/tenant-table.tsx | 4 +- .../user-setting/setting-team/user-table.tsx | 4 +- web/src/pages/user-setting/sidebar/index.tsx | 4 +- web/src/routes.ts | 11 +- 43 files changed, 1201 insertions(+), 757 deletions(-) create mode 100644 web/src/pages/user-setting/components/user-setting-header/index.tsx create mode 100644 web/src/pages/user-setting/mcp/edit-mcp-dialog.tsx create mode 100644 web/src/pages/user-setting/mcp/edit-mcp-form.tsx create mode 100644 web/src/pages/user-setting/mcp/import-mcp-dialog/import-mcp-form.tsx create mode 100644 web/src/pages/user-setting/mcp/import-mcp-dialog/index.tsx create mode 100644 web/src/pages/user-setting/mcp/index.tsx create mode 100644 web/src/pages/user-setting/mcp/mcp-card.tsx create mode 100644 web/src/pages/user-setting/mcp/mcp-operation.tsx create mode 100644 web/src/pages/user-setting/mcp/tool-card.tsx create mode 100644 web/src/pages/user-setting/mcp/use-bulk-operate-mcp.tsx create mode 100644 web/src/pages/user-setting/mcp/use-edit-mcp.ts create mode 100644 web/src/pages/user-setting/mcp/use-export-mcp.ts create mode 100644 web/src/pages/user-setting/mcp/use-import-mcp.ts delete mode 100644 web/src/pages/user-setting/setting-password/index.less delete mode 100644 web/src/pages/user-setting/setting-password/index.tsx delete mode 100644 web/src/pages/user-setting/setting-profile/index.less delete mode 100644 web/src/pages/user-setting/setting-profile/index.tsx delete mode 100644 web/src/pages/user-setting/setting-system/index.less delete mode 100644 web/src/pages/user-setting/setting-system/index.tsx delete mode 100644 web/src/pages/user-setting/setting-system/task-bar-chat.tsx diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index a88505f10..602748669 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -6,6 +6,7 @@ export interface IConnector { name: string; status: RunningStatus; source: DataSourceKey; + auto_parse?: '0' | '1'; } // knowledge base export interface IKnowledge { diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 3740f5583..e89e6823d 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -274,6 +274,7 @@ export default { reRankModelWaring: 'Re-rank model is very time consuming.', }, knowledgeConfiguration: { + autoParse: 'Auto Parse', rebuildTip: 'Re-downloads files from the linked data source and parses them again.', baseInfo: 'Basic Info', diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts index 79fa16c41..c7d21477d 100644 --- a/web/src/locales/zh.ts +++ b/web/src/locales/zh.ts @@ -260,6 +260,7 @@ export default { theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除', }, knowledgeConfiguration: { + autoParse: '自动解析', rebuildTip: '从所有已关联的数据源重新下载文件并再次解析。', baseInfo: '基础信息', gobalIndex: '全局索引', diff --git a/web/src/pages/agent/gobal-variable-sheet/index.tsx b/web/src/pages/agent/gobal-variable-sheet/index.tsx index f56a75d1b..8fe2b5be2 100644 --- a/web/src/pages/agent/gobal-variable-sheet/index.tsx +++ b/web/src/pages/agent/gobal-variable-sheet/index.tsx @@ -3,6 +3,7 @@ import { DynamicForm, DynamicFormRef, FormFieldConfig, + FormFieldType, } from '@/components/dynamic-form'; import { Button } from '@/components/ui/button'; import { Modal } from '@/components/ui/modal/modal'; @@ -112,6 +113,23 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => { }; const handleEditGobalVariable = (item: FieldValues) => { + fields.forEach((field) => { + if (field.name === 'value') { + switch (item.type) { + // [TypesWithArray.String]: FormFieldType.Textarea, + // [TypesWithArray.Number]: FormFieldType.Number, + // [TypesWithArray.Boolean]: FormFieldType.Checkbox, + case TypesWithArray.Boolean: + field.type = FormFieldType.Checkbox; + break; + case TypesWithArray.Number: + field.type = FormFieldType.Number; + break; + default: + field.type = FormFieldType.Textarea; + } + } + }); setDefaultValues(item); showModal(); }; @@ -124,7 +142,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => { > - {t('flow.conversationVariable')} + {t('flow.gobalVariable')} @@ -185,7 +203,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => { void; unbindFunc?: (item: DataSourceItemProps) => void; + handleAutoParse?: (option: { + source_id: string; + isAutoParse: boolean; + }) => void; } interface DataSourceItemProps extends IDataSourceNodeProps { openLinkModalFunc?: (open: boolean, data?: IDataSourceNodeProps) => void; unbindFunc?: (item: DataSourceItemProps) => void; + handleAutoParse?: (option: { + source_id: string; + isAutoParse: boolean; + }) => void; } const DataSourceItem = (props: DataSourceItemProps) => { const { t } = useTranslation(); - const { id, name, icon, source, unbindFunc } = props; + const { id, name, icon, source, auto_parse, unbindFunc, handleAutoParse } = + props; const { navigateToDataSourceDetail } = useNavigatePage(); const { handleRebuild } = useDataSourceRebuild(); @@ -50,7 +60,19 @@ const DataSourceItem = (props: DataSourceItemProps) => {
{name}
-
+
+
+
+ {t('knowledgeConfiguration.autoParse')} +
+ { + handleAutoParse?.({ source_id: id, isAutoParse }); + }} + className="w-8 h-4" + /> +
diff --git a/web/src/pages/home/datasets.tsx b/web/src/pages/home/datasets.tsx index b8156c7ef..4d78d34d0 100644 --- a/web/src/pages/home/datasets.tsx +++ b/web/src/pages/home/datasets.tsx @@ -2,10 +2,12 @@ import { CardSineLineContainer } from '@/components/card-singleline-container'; import { RenameDialog } from '@/components/rename-dialog'; import { HomeIcon } from '@/components/svg-icon'; import { CardSkeleton } from '@/components/ui/skeleton'; +import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request'; import { useTranslation } from 'react-i18next'; -import { DatasetCard, SeeAllCard } from '../datasets/dataset-card'; +import { DatasetCard } from '../datasets/dataset-card'; import { useRenameDataset } from '../datasets/use-rename-dataset'; +import { SeeAllAppCard } from './application-card'; export function Datasets() { const { t } = useTranslation(); @@ -18,6 +20,7 @@ export function Datasets() { hideDatasetRenameModal, showDatasetRenameModal, } = useRenameDataset(); + const { navigateToDatasetList } = useNavigatePage(); return (
@@ -26,13 +29,12 @@ export function Datasets() { {t('header.dataset')} -
+
{loading ? (
) : ( - //
{kbs ?.slice(0, 6) @@ -43,9 +45,7 @@ export function Datasets() { showDatasetRenameModal={showDatasetRenameModal} > ))} -
- -
+ {}
//
)} diff --git a/web/src/pages/login-next/index.tsx b/web/src/pages/login-next/index.tsx index 23a8f1b83..64bf2f165 100644 --- a/web/src/pages/login-next/index.tsx +++ b/web/src/pages/login-next/index.tsx @@ -26,7 +26,6 @@ import { import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Eye, EyeOff } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { BgSvg } from './bg'; @@ -247,7 +246,7 @@ const Login = () => { } {...field} /> - + */}
diff --git a/web/src/pages/user-setting/components/user-setting-header/index.tsx b/web/src/pages/user-setting/components/user-setting-header/index.tsx new file mode 100644 index 000000000..931e2681d --- /dev/null +++ b/web/src/pages/user-setting/components/user-setting-header/index.tsx @@ -0,0 +1,44 @@ +import { Card, CardContent, CardHeader } from '@/components/ui/card'; +import { PropsWithChildren } from 'react'; + +export const UserSettingHeader = ({ + name, + description, +}: { + name: string; + description?: string; +}) => { + return ( + <> +
+
{name}
+ {description && ( +
{description}
+ )} +
+ {/* */} + + ); +}; + +export function Title({ children }: PropsWithChildren) { + return {children}; +} + +type ProfileSettingWrapperCardProps = { + header: React.ReactNode; +} & PropsWithChildren; + +export function ProfileSettingWrapperCard({ + header, + children, +}: ProfileSettingWrapperCardProps) { + return ( + + + {header} + + {children} + + ); +} diff --git a/web/src/pages/user-setting/data-source/data-source-detail-page/log-table.tsx b/web/src/pages/user-setting/data-source/data-source-detail-page/log-table.tsx index 82a4333aa..29527dd9a 100644 --- a/web/src/pages/user-setting/data-source/data-source-detail-page/log-table.tsx +++ b/web/src/pages/user-setting/data-source/data-source-detail-page/log-table.tsx @@ -217,8 +217,8 @@ export const DataSourceLogsTable = ({ )) ) : ( - - No results. + + {t('common.noData')} )} diff --git a/web/src/pages/user-setting/data-source/index.tsx b/web/src/pages/user-setting/data-source/index.tsx index 1e27dc6a3..80ceea1d7 100644 --- a/web/src/pages/user-setting/data-source/index.tsx +++ b/web/src/pages/user-setting/data-source/index.tsx @@ -3,8 +3,11 @@ import { useTranslation } from 'react-i18next'; import Spotlight from '@/components/spotlight'; import { Button } from '@/components/ui/button'; -import { Separator } from '@/components/ui/separator'; import { Plus } from 'lucide-react'; +import { + ProfileSettingWrapperCard, + UserSettingHeader, +} from '../components/user-setting-header'; import AddDataSourceModal from './add-datasource-modal'; import { AddedSourceCard } from './component/added-source-card'; import { DataSourceInfo, DataSourceKey } from './contant'; @@ -93,60 +96,57 @@ const DataSource = () => { }; return ( -
+ + } + > - - {/* */} -
-
- {t('setting.dataSources')} -
- {t('setting.datasourceDescription')} +
+
+
+ {categorizedList.map((item, index) => ( + + ))}
-
-
- {/*
*/} - -
-
- {categorizedList.map((item, index) => ( - - ))} -
-
-
- {/* */} - - {t('setting.availableSources')} -
- {t('setting.availableSourcesDescription')} +
+
+ {/* */} + + {t('setting.availableSources')} +
+ {t('setting.availableSourcesDescription')} +
+
+
+
+ {/* */} +
+ {dataSourceTemplates.map((item, index) => ( + + ))}
- -
-
- {/* */} -
- {dataSourceTemplates.map((item, index) => ( - - ))} -
-
-
-
+ +
+
- {addingModalVisible && ( - { - console.log(data); - handleAddOk(data); - }} - sourceData={addSource} - > - )} - + {addingModalVisible && ( + { + console.log(data); + handleAddOk(data); + }} + sourceData={addSource} + > + )} + + ); }; diff --git a/web/src/pages/user-setting/index.tsx b/web/src/pages/user-setting/index.tsx index 3f5644234..ec6bf015a 100644 --- a/web/src/pages/user-setting/index.tsx +++ b/web/src/pages/user-setting/index.tsx @@ -44,12 +44,7 @@ const UserSetting = () => { )} > -
+
diff --git a/web/src/pages/user-setting/mcp/edit-mcp-dialog.tsx b/web/src/pages/user-setting/mcp/edit-mcp-dialog.tsx new file mode 100644 index 000000000..3029d176a --- /dev/null +++ b/web/src/pages/user-setting/mcp/edit-mcp-dialog.tsx @@ -0,0 +1,178 @@ +import { Collapse } from '@/components/collapse'; +import { Button, ButtonLoading } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { useGetMcpServer, useTestMcpServer } from '@/hooks/use-mcp-request'; +import { IModalProps } from '@/interfaces/common'; +import { IMCPTool, IMCPToolObject } from '@/interfaces/database/mcp'; +import { cn } from '@/lib/utils'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { isEmpty, omit, pick } from 'lodash'; +import { RefreshCw } from 'lucide-react'; +import { + MouseEventHandler, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; +import { + EditMcpForm, + FormId, + ServerType, + useBuildFormSchema, +} from './edit-mcp-form'; +import { McpToolCard } from './tool-card'; + +function transferToolToArray(tools: IMCPToolObject) { + return Object.entries(tools).reduce((pre, [name, tool]) => { + pre.push({ ...tool, name }); + return pre; + }, []); +} + +const DefaultValues = { + name: '', + server_type: ServerType.SSE, + url: '', +}; + +export function EditMcpDialog({ + hideModal, + loading, + onOk, + id, +}: IModalProps & { id: string }) { + const { t } = useTranslation(); + const { + testMcpServer, + data: testData, + loading: testLoading, + } = useTestMcpServer(); + const [isTriggeredBySaving, setIsTriggeredBySaving] = useState(false); + const FormSchema = useBuildFormSchema(); + const [collapseOpen, setCollapseOpen] = useState(true); + const { data } = useGetMcpServer(id); + const [fieldChanged, setFieldChanged] = useState(false); + + const tools = useMemo(() => { + return testData?.data || []; + }, [testData?.data]); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: DefaultValues, + }); + + const handleTest: MouseEventHandler = useCallback((e) => { + e.stopPropagation(); + setIsTriggeredBySaving(false); + }, []); + + const handleSave: MouseEventHandler = useCallback(() => { + setIsTriggeredBySaving(true); + }, []); + + const handleOk = async (values: z.infer) => { + const nextValues = { + ...omit(values, 'authorization_token'), + variables: { authorization_token: values.authorization_token }, + headers: { Authorization: 'Bearer ${authorization_token}' }, + }; + if (isTriggeredBySaving) { + onOk?.(nextValues); + } else { + const ret = await testMcpServer(nextValues); + if (ret.code === 0) { + setFieldChanged(false); + } + } + }; + + useEffect(() => { + if (!isEmpty(data)) { + form.reset(pick(data, ['name', 'server_type', 'url'])); + } + }, [data, form]); + + const nextTools = useMemo(() => { + return isEmpty(tools) + ? transferToolToArray(data.variables?.tools || {}) + : tools; + }, [data.variables?.tools, tools]); + + const disabled = !!!tools?.length || testLoading || fieldChanged; + + return ( + + + + {id ? t('mcp.editMCP') : t('mcp.addMCP')} + + + + + + {nextTools?.length || 0} {t('mcp.toolsAvailable')} + + } + open={collapseOpen} + onOpenChange={setCollapseOpen} + rightContent={ + + } + > +
+ {nextTools?.map((x) => ( + + ))} +
+
+
+
+ + + + + + {t('common.save')} + + +
+
+ ); +} diff --git a/web/src/pages/user-setting/mcp/edit-mcp-form.tsx b/web/src/pages/user-setting/mcp/edit-mcp-form.tsx new file mode 100644 index 000000000..d6b49db11 --- /dev/null +++ b/web/src/pages/user-setting/mcp/edit-mcp-form.tsx @@ -0,0 +1,171 @@ +'use client'; + +import { UseFormReturn } from 'react-hook-form'; +import { z } from 'zod'; + +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { IModalProps } from '@/interfaces/common'; +import { buildOptions } from '@/utils/form'; +import { loader } from '@monaco-editor/react'; +import { Dispatch, SetStateAction } from 'react'; +import { useTranslation } from 'react-i18next'; + +loader.config({ paths: { vs: '/vs' } }); + +export const FormId = 'EditMcpForm'; + +export enum ServerType { + SSE = 'sse', + StreamableHttp = 'streamable-http', +} + +const ServerTypeOptions = buildOptions(ServerType); + +export function useBuildFormSchema() { + const { t } = useTranslation(); + + const FormSchema = z.object({ + name: z + .string() + .min(1, { + message: t('common.mcp.namePlaceholder'), + }) + .regex(/^[a-zA-Z0-9_-]{1,64}$/, { + message: t('common.mcp.nameRequired'), + }) + .trim(), + url: z + .string() + .url() + .min(1, { + message: t('common.mcp.urlPlaceholder'), + }) + .trim(), + server_type: z + .string() + .min(1, { + message: t('common.pleaseSelect'), + }) + .trim(), + authorization_token: z.string().optional(), + }); + + return FormSchema; +} + +export function EditMcpForm({ + form, + onOk, + setFieldChanged, +}: IModalProps & { + form: UseFormReturn; + setFieldChanged: Dispatch>; +}) { + const { t } = useTranslation(); + const FormSchema = useBuildFormSchema(); + + function onSubmit(data: z.infer) { + onOk?.(data); + } + + return ( +
+ + ( + + {t('common.name')} + + + + + + )} + /> + ( + + {t('mcp.url')} + + { + field.onChange(e.target.value.trim()); + setFieldChanged(true); + }} + /> + + + + )} + /> + ( + + {t('mcp.serverType')} + + { + field.onChange(value); + setFieldChanged(true); + }} + /> + + + + )} + /> + ( + + Authorization Token + + { + field.onChange(e.target.value.trim()); + setFieldChanged(true); + }} + /> + + + + )} + /> + + + ); +} diff --git a/web/src/pages/user-setting/mcp/import-mcp-dialog/import-mcp-form.tsx b/web/src/pages/user-setting/mcp/import-mcp-dialog/import-mcp-form.tsx new file mode 100644 index 000000000..d34507308 --- /dev/null +++ b/web/src/pages/user-setting/mcp/import-mcp-dialog/import-mcp-form.tsx @@ -0,0 +1,72 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; + +import { FileUploader } from '@/components/file-uploader'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { FileMimeType, Platform } from '@/constants/common'; +import { IModalProps } from '@/interfaces/common'; +import { TagRenameId } from '@/pages/add-knowledge/constant'; +import { useTranslation } from 'react-i18next'; + +export function ImportMcpForm({ hideModal, onOk }: IModalProps) { + const { t } = useTranslation(); + const FormSchema = z.object({ + platform: z + .string() + .min(1, { + message: t('common.namePlaceholder'), + }) + .trim(), + fileList: z.array(z.instanceof(File)), + }); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { platform: Platform.RAGFlow }, + }); + + async function onSubmit(data: z.infer) { + const ret = await onOk?.(data); + if (ret) { + hideModal?.(); + } + } + + return ( +
+ + ( + + {t('common.name')} + + + + + + )} + /> + + + ); +} diff --git a/web/src/pages/user-setting/mcp/import-mcp-dialog/index.tsx b/web/src/pages/user-setting/mcp/import-mcp-dialog/index.tsx new file mode 100644 index 000000000..76b35387b --- /dev/null +++ b/web/src/pages/user-setting/mcp/import-mcp-dialog/index.tsx @@ -0,0 +1,36 @@ +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { LoadingButton } from '@/components/ui/loading-button'; +import { IModalProps } from '@/interfaces/common'; +import { TagRenameId } from '@/pages/add-knowledge/constant'; +import { useTranslation } from 'react-i18next'; +import { ImportMcpForm } from './import-mcp-form'; + +export function ImportMcpDialog({ + hideModal, + onOk, + loading, +}: IModalProps) { + const { t } = useTranslation(); + + return ( + + + + {t('mcp.import')} + + + + + {t('common.save')} + + + + + ); +} diff --git a/web/src/pages/user-setting/mcp/index.tsx b/web/src/pages/user-setting/mcp/index.tsx new file mode 100644 index 000000000..b403f1bc5 --- /dev/null +++ b/web/src/pages/user-setting/mcp/index.tsx @@ -0,0 +1,157 @@ +import { CardContainer } from '@/components/card-container'; +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import Spotlight from '@/components/spotlight'; +import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; +import { SearchInput } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { RAGFlowPagination } from '@/components/ui/ragflow-pagination'; +import { useListMcpServer } from '@/hooks/use-mcp-request'; +import { pick } from 'lodash'; +import { + Download, + LayoutList, + ListChecks, + Plus, + Trash2, + Upload, +} from 'lucide-react'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ProfileSettingWrapperCard } from '../components/user-setting-header'; +import { EditMcpDialog } from './edit-mcp-dialog'; +import { ImportMcpDialog } from './import-mcp-dialog'; +import { McpCard } from './mcp-card'; +import { useBulkOperateMCP } from './use-bulk-operate-mcp'; +import { useEditMcp } from './use-edit-mcp'; +import { useImportMcp } from './use-import-mcp'; + +export default function McpServer() { + const { data, setPagination, searchString, handleInputChange, pagination } = + useListMcpServer(); + const { editVisible, showEditModal, hideEditModal, handleOk, id, loading } = + useEditMcp(); + const { + selectedList, + handleSelectChange, + handleDelete, + handleExportMcp, + handleSelectAll, + } = useBulkOperateMCP(data.mcp_servers); + const { t } = useTranslation(); + const { importVisible, showImportModal, hideImportModal, onImportOk } = + useImportMcp(); + + const [isSelectionMode, setSelectionMode] = useState(false); + + const handlePageChange = useCallback( + (page: number, pageSize?: number) => { + setPagination({ page, pageSize }); + }, + [setPagination], + ); + + const switchSelectionMode = useCallback(() => { + setSelectionMode((prev) => !prev); + }, []); + + return ( + +
+ {t('mcp.mcpServers')} +
+
+
+ {t('mcp.customizeTheListOfMcpServers')} +
+
+ + + + +
+
+ + } + > + {isSelectionMode && ( +
+ + + + {t('mcp.selected')} {selectedList.length} + +
+ + + + +
+
+ )} + + {data.mcp_servers.map((item) => ( + + ))} + +
+ +
+ {editVisible && ( + + )} + {importVisible && ( + + )} + +
+ ); +} diff --git a/web/src/pages/user-setting/mcp/mcp-card.tsx b/web/src/pages/user-setting/mcp/mcp-card.tsx new file mode 100644 index 000000000..b0cfcd162 --- /dev/null +++ b/web/src/pages/user-setting/mcp/mcp-card.tsx @@ -0,0 +1,74 @@ +import { Card, CardContent } from '@/components/ui/card'; +import { Checkbox } from '@/components/ui/checkbox'; +import { IMcpServer } from '@/interfaces/database/mcp'; +import { formatDate } from '@/utils/date'; +import { isPlainObject } from 'lodash'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { McpOperation } from './mcp-operation'; +import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp'; +import { UseEditMcpReturnType } from './use-edit-mcp'; + +export type DatasetCardProps = { + data: IMcpServer; + isSelectionMode: boolean; +} & Pick & + Pick; + +export function McpCard({ + data, + selectedList, + handleSelectChange, + showEditModal, + isSelectionMode, +}: DatasetCardProps) { + const { t } = useTranslation(); + const toolLength = useMemo(() => { + const tools = data.variables?.tools; + if (isPlainObject(tools)) { + return Object.keys(tools || {}).length; + } + return 0; + }, [data.variables?.tools]); + const onCheckedChange = (checked: boolean) => { + if (typeof checked === 'boolean') { + handleSelectChange(data.id, checked); + } + }; + + return ( + + +
+

+ {data.name} +

+
+ {isSelectionMode ? ( + { + e.stopPropagation(); + }} + /> + ) : ( + + )} +
+
+
+
+
+ {toolLength} {t('mcp.cachedTools')} +
+

{formatDate(data.update_date)}

+
+
+
+
+ ); +} diff --git a/web/src/pages/user-setting/mcp/mcp-operation.tsx b/web/src/pages/user-setting/mcp/mcp-operation.tsx new file mode 100644 index 000000000..9bbd9e5c8 --- /dev/null +++ b/web/src/pages/user-setting/mcp/mcp-operation.tsx @@ -0,0 +1,43 @@ +import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog'; +import { RAGFlowTooltip } from '@/components/ui/tooltip'; +import { useDeleteMcpServer } from '@/hooks/use-mcp-request'; +import { PenLine, Trash2, Upload } from 'lucide-react'; +import { MouseEventHandler, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { UseEditMcpReturnType } from './use-edit-mcp'; +import { useExportMcp } from './use-export-mcp'; + +export function McpOperation({ + mcpId, + showEditModal, +}: { mcpId: string } & Pick) { + const { t } = useTranslation(); + const { deleteMcpServer } = useDeleteMcpServer(); + const { handleExportMcpJson } = useExportMcp(); + + const handleDelete: MouseEventHandler = useCallback(() => { + deleteMcpServer([mcpId]); + }, [deleteMcpServer, mcpId]); + + return ( +
+ + + + + + + + + + + +
+ ); +} diff --git a/web/src/pages/user-setting/mcp/tool-card.tsx b/web/src/pages/user-setting/mcp/tool-card.tsx new file mode 100644 index 000000000..123b4a611 --- /dev/null +++ b/web/src/pages/user-setting/mcp/tool-card.tsx @@ -0,0 +1,16 @@ +import { IMCPTool } from '@/interfaces/database/mcp'; + +export type McpToolCardProps = { + data: IMCPTool; +}; + +export function McpToolCard({ data }: McpToolCardProps) { + return ( +
+

{data.name}

+
+ {data.description} +
+
+ ); +} diff --git a/web/src/pages/user-setting/mcp/use-bulk-operate-mcp.tsx b/web/src/pages/user-setting/mcp/use-bulk-operate-mcp.tsx new file mode 100644 index 000000000..2afef8347 --- /dev/null +++ b/web/src/pages/user-setting/mcp/use-bulk-operate-mcp.tsx @@ -0,0 +1,56 @@ +import { useDeleteMcpServer } from '@/hooks/use-mcp-request'; +import { IMcpServer } from '@/interfaces/database/mcp'; +import { Trash2, Upload } from 'lucide-react'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useExportMcp } from './use-export-mcp'; + +export function useBulkOperateMCP(mcpList: IMcpServer[]) { + const { t } = useTranslation(); + const [selectedList, setSelectedList] = useState>([]); + const { deleteMcpServer } = useDeleteMcpServer(); + const { handleExportMcpJson } = useExportMcp(); + + const handleDelete = useCallback(() => { + deleteMcpServer(selectedList); + }, [deleteMcpServer, selectedList]); + + const handleSelectChange = useCallback((id: string, checked: boolean) => { + setSelectedList((list) => { + return checked ? [...list, id] : list.filter((item) => item !== id); + }); + }, []); + + const handleSelectAll = useCallback( + (checked: boolean) => { + setSelectedList(() => (checked ? mcpList.map((item) => item.id) : [])); + }, + [mcpList], + ); + + const list = [ + { + id: 'export', + label: t('mcp.export'), + icon: , + onClick: handleExportMcpJson(selectedList), + }, + { + id: 'delete', + label: t('common.delete'), + icon: , + onClick: handleDelete, + }, + ]; + + return { + list, + selectedList, + handleSelectChange, + handleDelete, + handleExportMcp: handleExportMcpJson(selectedList), + handleSelectAll, + }; +} + +export type UseBulkOperateMCPReturnType = ReturnType; diff --git a/web/src/pages/user-setting/mcp/use-edit-mcp.ts b/web/src/pages/user-setting/mcp/use-edit-mcp.ts new file mode 100644 index 000000000..09fc83be5 --- /dev/null +++ b/web/src/pages/user-setting/mcp/use-edit-mcp.ts @@ -0,0 +1,53 @@ +import { useSetModalState } from '@/hooks/common-hooks'; +import { + useCreateMcpServer, + useUpdateMcpServer, +} from '@/hooks/use-mcp-request'; +import { useCallback, useState } from 'react'; + +export const useEditMcp = () => { + const { + visible: editVisible, + hideModal: hideEditModal, + showModal: showEditModal, + } = useSetModalState(); + const { createMcpServer, loading } = useCreateMcpServer(); + const [id, setId] = useState(''); + + const { updateMcpServer, loading: updateLoading } = useUpdateMcpServer(); + + const handleShowModal = useCallback( + (id: string) => () => { + setId(id); + showEditModal(); + }, + [setId, showEditModal], + ); + + const handleOk = useCallback( + async (values: any) => { + let code; + if (id) { + code = await updateMcpServer({ ...values, mcp_id: id }); + } else { + code = await createMcpServer(values); + } + if (code === 0) { + hideEditModal(); + } + }, + [createMcpServer, hideEditModal, id, updateMcpServer], + ); + + return { + editVisible, + hideEditModal, + showEditModal: handleShowModal, + loading: loading || updateLoading, + createMcpServer, + handleOk, + id, + }; +}; + +export type UseEditMcpReturnType = ReturnType; diff --git a/web/src/pages/user-setting/mcp/use-export-mcp.ts b/web/src/pages/user-setting/mcp/use-export-mcp.ts new file mode 100644 index 000000000..636290c91 --- /dev/null +++ b/web/src/pages/user-setting/mcp/use-export-mcp.ts @@ -0,0 +1,21 @@ +import { useExportMcpServer } from '@/hooks/use-mcp-request'; +import { downloadJsonFile } from '@/utils/file-util'; +import { useCallback } from 'react'; + +export function useExportMcp() { + const { exportMcpServer } = useExportMcpServer(); + + const handleExportMcpJson = useCallback( + (ids: string[]) => async () => { + const data = await exportMcpServer(ids); + if (data.code === 0) { + downloadJsonFile(data.data, `mcp.json`); + } + }, + [exportMcpServer], + ); + + return { + handleExportMcpJson, + }; +} diff --git a/web/src/pages/user-setting/mcp/use-import-mcp.ts b/web/src/pages/user-setting/mcp/use-import-mcp.ts new file mode 100644 index 000000000..33e844191 --- /dev/null +++ b/web/src/pages/user-setting/mcp/use-import-mcp.ts @@ -0,0 +1,73 @@ +import message from '@/components/ui/message'; +import { FileMimeType } from '@/constants/common'; +import { useSetModalState } from '@/hooks/common-hooks'; +import { useImportMcpServer } from '@/hooks/use-mcp-request'; +import { isEmpty } from 'lodash'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +const ServerEntrySchema = z.object({ + authorization_token: z.string().optional(), + name: z.string().optional(), + tool_configuration: z.object({}).passthrough().optional(), + type: z.string(), + url: z.string().url(), +}); + +const McpConfigSchema = z.object({ + mcpServers: z.record(ServerEntrySchema), +}); + +export const useImportMcp = () => { + const { + visible: importVisible, + hideModal: hideImportModal, + showModal: showImportModal, + } = useSetModalState(); + const { t } = useTranslation(); + const { importMcpServer, loading } = useImportMcpServer(); + + const onImportOk = useCallback( + async ({ fileList }: { fileList: File[] }) => { + if (fileList.length > 0) { + const file = fileList[0]; + if (file.type !== FileMimeType.Json) { + message.error(t('flow.jsonUploadTypeErrorMessage')); + return; + } + + const mcpStr = await file.text(); + const errorMessage = t('flow.jsonUploadContentErrorMessage'); + try { + const mcp = JSON.parse(mcpStr); + try { + McpConfigSchema.parse(mcp); + } catch (error) { + message.error('Incorrect data format'); + return; + } + if (mcpStr && !isEmpty(mcp)) { + const ret = await importMcpServer(mcp); + if (ret.code === 0) { + hideImportModal(); + } + } else { + message.error(errorMessage); + } + } catch (error) { + message.error(errorMessage); + } + } + }, + [hideImportModal, importMcpServer, t], + ); + + return { + importVisible, + showImportModal, + hideImportModal, + onImportOk, + loading, + }; +}; diff --git a/web/src/pages/user-setting/profile/index.tsx b/web/src/pages/user-setting/profile/index.tsx index da19afa0d..dceb2cdf3 100644 --- a/web/src/pages/user-setting/profile/index.tsx +++ b/web/src/pages/user-setting/profile/index.tsx @@ -20,7 +20,6 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Separator } from '@/components/ui/separator'; import { useTranslate } from '@/hooks/common-hooks'; import { TimezoneList } from '@/pages/user-setting/constants'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -29,6 +28,10 @@ import { Loader2Icon, PenLine } from 'lucide-react'; import { FC, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { + ProfileSettingWrapperCard, + UserSettingHeader, +} from '../components/user-setting-header'; import { EditType, modalTitle, useProfile } from './hooks/use-profile'; const baseSchema = z.object({ @@ -123,18 +126,17 @@ const ProfilePage: FC = () => { // }; return ( -
+ //
+ + } + > - {/* Header */} -
-
- {t('profile')} -
-
- {t('profileDescription')} -
-
- + {/* Main Content */}
{/* Name */} @@ -411,7 +413,8 @@ const ProfilePage: FC = () => { )} -
+
+ //
); }; diff --git a/web/src/pages/user-setting/setting-model/components/modal-card.tsx b/web/src/pages/user-setting/setting-model/components/modal-card.tsx index 90950e64e..270f9b4b5 100644 --- a/web/src/pages/user-setting/setting-model/components/modal-card.tsx +++ b/web/src/pages/user-setting/setting-model/components/modal-card.tsx @@ -97,7 +97,7 @@ export const ModelProviderCard: FC = ({ className="px-3 py-1 text-sm bg-bg-input hover:bg-bg-input text-text-primary rounded-md transition-colors flex items-center space-x-1" > {visible ? t('hideModels') : t('showMoreModels')} - {visible ? : } + {!visible ? : } - - - - - - ); -}; - -export default UserSettingPassword; diff --git a/web/src/pages/user-setting/setting-profile/index.less b/web/src/pages/user-setting/setting-profile/index.less deleted file mode 100644 index 1950f8c2b..000000000 --- a/web/src/pages/user-setting/setting-profile/index.less +++ /dev/null @@ -1,7 +0,0 @@ -.profileWrapper { - width: 100%; - .emailDescription { - padding: 10px 0; - margin: 0; - } -} diff --git a/web/src/pages/user-setting/setting-profile/index.tsx b/web/src/pages/user-setting/setting-profile/index.tsx deleted file mode 100644 index 9f11ed8c8..000000000 --- a/web/src/pages/user-setting/setting-profile/index.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import { LanguageList, LanguageMap } from '@/constants/common'; -import { useTranslate } from '@/hooks/common-hooks'; -import { useChangeLanguage } from '@/hooks/logic-hooks'; -import { useFetchUserInfo, useSaveSetting } from '@/hooks/user-setting-hooks'; -import { - getBase64FromUploadFileList, - getUploadFileListFromBase64, - normFile, -} from '@/utils/file-util'; -import { PlusOutlined } from '@ant-design/icons'; -import { - Button, - Divider, - Form, - Input, - Select, - Space, - Spin, - Upload, - UploadFile, -} from 'antd'; -import { useEffect } from 'react'; -import SettingTitle from '../components/setting-title'; -import { TimezoneList } from '../constants'; -import { useValidateSubmittable } from '../hooks'; -import parentStyles from '../index.less'; -import styles from './index.less'; - -const { Option } = Select; - -type FieldType = { - nickname?: string; - language?: string; - email?: string; - color_schema?: string; - timezone?: string; - avatar?: string; -}; - -const tailLayout = { - wrapperCol: { offset: 20, span: 4 }, -}; - -const UserSettingProfile = () => { - const { data: userInfo, loading } = useFetchUserInfo(); - const { saveSetting, loading: submitLoading } = useSaveSetting(); - const { form, submittable } = useValidateSubmittable(); - const { t } = useTranslate('setting'); - const changeLanguage = useChangeLanguage(); - - const onFinish = async (values: any) => { - const avatar = await getBase64FromUploadFileList(values.avatar); - saveSetting({ ...values, avatar }); - }; - - const onFinishFailed = (errorInfo: any) => { - console.log('Failed:', errorInfo); - }; - - useEffect(() => { - const fileList: UploadFile[] = getUploadFileListFromBase64(userInfo.avatar); - form.setFieldsValue({ ...userInfo, avatar: fileList }); - }, [form, userInfo]); - - return ( -
- - - -
- - label={t('username')} - name="nickname" - rules={[ - { - required: true, - message: t('usernameMessage'), - whitespace: true, - }, - ]} - > - - - - - label={ -
- {t('photo')} -
{t('photoDescription')}
-
- } - name="avatar" - valuePropName="fileList" - getValueFromEvent={normFile} - > - { - return false; - }} - showUploadList={{ showPreviewIcon: false, showRemoveIcon: false }} - > - - - - - - label={t('colorSchema')} - name="color_schema" - rules={[{ required: true, message: t('colorSchemaMessage') }]} - > - - - - - label={t('language', { keyPrefix: 'common' })} - name="language" - rules={[ - { - required: true, - message: t('languageMessage', { keyPrefix: 'common' }), - }, - ]} - > - - - - - label={t('timezone')} - name="timezone" - rules={[{ required: true, message: t('timezoneMessage') }]} - > - - - - - name="email" noStyle> - - -

- {t('emailDescription')} -

- - - prevValues.additional !== curValues.additional - } - > - - - - - - -
-
- ); -}; - -export default UserSettingProfile; diff --git a/web/src/pages/user-setting/setting-system/index.less b/web/src/pages/user-setting/setting-system/index.less deleted file mode 100644 index 77757dfba..000000000 --- a/web/src/pages/user-setting/setting-system/index.less +++ /dev/null @@ -1,33 +0,0 @@ -.systemInfo { - width: 100%; - .title { - font-size: 20px; - font-weight: 600; - } - .text { - height: 26px; - line-height: 26px; - } - .badge { - :global(.ant-badge-status-dot) { - width: 10px; - height: 10px; - } - } - .error { - color: red; - } -} - -.taskBarTooltip { - font-size: 16px; -} - -.taskBar { - width: '100%'; - height: 200px; -} - -.taskBarTitle { - font-size: 16px; -} diff --git a/web/src/pages/user-setting/setting-system/index.tsx b/web/src/pages/user-setting/setting-system/index.tsx deleted file mode 100644 index 9c41154a3..000000000 --- a/web/src/pages/user-setting/setting-system/index.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import SvgIcon from '@/components/svg-icon'; -import { useFetchSystemStatus } from '@/hooks/user-setting-hooks'; -import { - ISystemStatus, - TaskExecutorHeartbeatItem, -} from '@/interfaces/database/user-setting'; -import { Badge, Card, Flex, Spin, Typography } from 'antd'; -import classNames from 'classnames'; -import lowerCase from 'lodash/lowerCase'; -import upperFirst from 'lodash/upperFirst'; -import { useEffect } from 'react'; - -import { toFixed } from '@/utils/common-util'; -import { isObject } from 'lodash'; -import styles from './index.less'; -import TaskBarChat from './task-bar-chat'; - -const { Text } = Typography; - -enum Status { - 'green' = 'success', - 'red' = 'error', - 'yellow' = 'warning', -} - -const TitleMap = { - doc_engine: 'Doc Engine', - storage: 'Object Storage', - redis: 'Redis', - database: 'Database', - task_executor_heartbeats: 'Task Executor', -}; - -const IconMap = { - es: 'es', - doc_engine: 'storage', - redis: 'redis', - storage: 'minio', - database: 'database', -}; - -const SystemInfo = () => { - const { - systemStatus, - fetchSystemStatus, - loading: statusLoading, - } = useFetchSystemStatus(); - - useEffect(() => { - fetchSystemStatus(); - }, [fetchSystemStatus]); - - return ( -
- - - {Object.keys(systemStatus).map((key) => { - const info = systemStatus[key as keyof ISystemStatus]; - - return ( - - {key === 'task_executor_heartbeats' ? ( - - ) : ( - - )} - - {TitleMap[key as keyof typeof TitleMap]} - - - - } - key={key} - > - {key === 'task_executor_heartbeats' ? ( - isObject(info) ? ( - } - > - ) : ( - - {typeof info.error === 'string' ? info.error : ''} - - ) - ) : ( - Object.keys(info) - .filter((x) => x !== 'status') - .map((x) => { - return ( - - {upperFirst(lowerCase(x))}: - - {toFixed((info as Record)[x]) as any} - {x === 'elapsed' && ' ms'} - - - ); - }) - )} - - ); - })} - - -
- ); -}; - -export default SystemInfo; diff --git a/web/src/pages/user-setting/setting-system/task-bar-chat.tsx b/web/src/pages/user-setting/setting-system/task-bar-chat.tsx deleted file mode 100644 index f0b62a47e..000000000 --- a/web/src/pages/user-setting/setting-system/task-bar-chat.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { TaskExecutorHeartbeatItem } from '@/interfaces/database/user-setting'; -import { Divider, Flex } from 'antd'; -import { - Bar, - BarChart, - CartesianGrid, - Legend, - Rectangle, - ResponsiveContainer, - Tooltip, - XAxis, -} from 'recharts'; - -import { formatDate, formatTime } from '@/utils/date'; -import dayjs from 'dayjs'; -import { get } from 'lodash'; -import JsonView from 'react18-json-view'; -import 'react18-json-view/src/style.css'; - -import styles from './index.less'; - -interface IProps { - data: Record; -} - -const CustomTooltip = ({ active, payload, ...restProps }: any) => { - if (active && payload && payload.length) { - const taskExecutorHeartbeatItem: TaskExecutorHeartbeatItem = get( - payload, - '0.payload', - {}, - ); - return ( -
-
-
- {formatDate(restProps.label)} -
- - -
-
- ); - } - - return null; -}; - -const TaskBarChat = ({ data }: IProps) => { - return Object.entries(data).map(([key, val]) => { - const data = val.map((x) => ({ - ...x, - now: dayjs(x.now).valueOf(), - })); - const firstItem = data[0]; - const lastItem = data[data.length - 1]; - - const domain = [firstItem?.now, lastItem?.now]; - return ( - -
- ID: {key} - Lag: {lastItem?.lag} - Pending: {lastItem?.pending} -
- - - formatTime(x)} - allowDataOverflow - angle={60} - padding={{ left: 20, right: 20 }} - tickMargin={20} - /> - - } - trigger="click" - /> - - } - /> - } - /> - - - -
- ); - }); -}; - -export default TaskBarChat; diff --git a/web/src/pages/user-setting/setting-team/index.tsx b/web/src/pages/user-setting/setting-team/index.tsx index 0481cee4f..28d21f8ae 100644 --- a/web/src/pages/user-setting/setting-team/index.tsx +++ b/web/src/pages/user-setting/setting-team/index.tsx @@ -8,9 +8,12 @@ import { useTranslation } from 'react-i18next'; import Spotlight from '@/components/spotlight'; import { SearchInput } from '@/components/ui/input'; -import { Separator } from '@/components/ui/separator'; import { UserPlus } from 'lucide-react'; import { useState } from 'react'; +import { + ProfileSettingWrapperCard, + UserSettingHeader, +} from '../components/user-setting-header'; import AddingUserModal from './add-user-modal'; import { useAddUser } from './hooks'; import TenantTable from './tenant-table'; @@ -30,18 +33,21 @@ const UserSettingTeam = () => { } = useAddUser(); return ( -
+ //
+ // + // + + } + > - - - - {userInfo?.nickname} {t('setting.workspace')} - - - - - + {/* */} {t('setting.teamMembers')} @@ -59,15 +65,15 @@ const UserSettingTeam = () => { - + - + {/* */} - + {t('setting.joinedTeams')} { placeholder={t('common.search')} /> - + @@ -89,7 +95,7 @@ const UserSettingTeam = () => { onOk={handleAddUserOk} > )} -
+ ); }; diff --git a/web/src/pages/user-setting/setting-team/tenant-table.tsx b/web/src/pages/user-setting/setting-team/tenant-table.tsx index be95ec9af..833defb1b 100644 --- a/web/src/pages/user-setting/setting-team/tenant-table.tsx +++ b/web/src/pages/user-setting/setting-team/tenant-table.tsx @@ -71,7 +71,7 @@ const TenantTable = ({ searchTerm }: { searchTerm: string }) => { return (
- + {t('common.name')} { {t('common.action')} - + {loading ? ( diff --git a/web/src/pages/user-setting/setting-team/user-table.tsx b/web/src/pages/user-setting/setting-team/user-table.tsx index 95d40e95c..3f3fceae3 100644 --- a/web/src/pages/user-setting/setting-team/user-table.tsx +++ b/web/src/pages/user-setting/setting-team/user-table.tsx @@ -77,7 +77,7 @@ const UserTable = ({ searchUser }: { searchUser: string }) => { return (
- + {t('common.name')} { {t('common.action')} - + {loading ? ( diff --git a/web/src/pages/user-setting/sidebar/index.tsx b/web/src/pages/user-setting/sidebar/index.tsx index 6acd2edc1..c1c1c1da1 100644 --- a/web/src/pages/user-setting/sidebar/index.tsx +++ b/web/src/pages/user-setting/sidebar/index.tsx @@ -12,7 +12,7 @@ import { import { cn } from '@/lib/utils'; import { Routes } from '@/routes'; import { t } from 'i18next'; -import { Banknote, Box, Cog, Server, Unplug, User, Users } from 'lucide-react'; +import { Banknote, Box, Server, Unplug, User, Users } from 'lucide-react'; import { useEffect } from 'react'; import { useHandleMenuClick } from './hooks'; @@ -28,7 +28,7 @@ const menuItems = [ // }, // { icon: TextSearch, label: 'Retrieval Templates', key: Routes.Profile }, { icon: Server, label: t('setting.dataSources'), key: Routes.DataSource }, - { icon: Cog, label: t('setting.system'), key: Routes.System }, + // { icon: Cog, label: t('setting.system'), key: Routes.System }, // { icon: Banknote, label: 'Plan', key: Routes.Plan }, { icon: Banknote, label: 'MCP', key: Routes.Mcp }, ]; diff --git a/web/src/routes.ts b/web/src/routes.ts index c828e5a91..2e9692120 100644 --- a/web/src/routes.ts +++ b/web/src/routes.ts @@ -24,7 +24,6 @@ export enum Routes { Mcp = '/mcp', Team = '/team', Plan = '/plan', - System = '/system', Model = '/model', Prompt = '/prompt', DataSource = '/data-source', @@ -378,10 +377,6 @@ const routes = [ path: '/user-setting/locale', component: '@/pages/user-setting/setting-locale', }, - { - path: '/user-setting/password', - component: '@/pages/user-setting/setting-password', - }, { path: '/user-setting/model', component: '@/pages/user-setting/setting-model', @@ -390,17 +385,13 @@ const routes = [ path: '/user-setting/team', component: '@/pages/user-setting/setting-team', }, - { - path: `/user-setting${Routes.System}`, - component: '@/pages/user-setting/setting-system', - }, { path: `/user-setting${Routes.Api}`, component: '@/pages/user-setting/setting-api', }, { path: `/user-setting${Routes.Mcp}`, - component: `@/pages${Routes.ProfileMcp}`, + component: `@/pages/user-setting/${Routes.Mcp}`, }, { path: `/user-setting${Routes.DataSource}`,