Feat: Adjust the style of the mcp dialog #10703 (#10719)

### What problem does this PR solve?

Feat: Adjust the style of the mcp dialog #10703

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-10-22 12:20:19 +08:00
committed by GitHub
parent 307cdc62ea
commit 02a452993e
14 changed files with 134 additions and 86 deletions

View File

@ -51,8 +51,8 @@ export function Collapse({
onOpenChange={handleOpenChange} onOpenChange={handleOpenChange}
disabled={disabled} disabled={disabled}
> >
<CollapsibleTrigger className="w-full"> <CollapsibleTrigger className={'w-full'}>
<section className="flex justify-between items-center pb-2"> <section className="flex justify-between items-center">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<IconFontFill <IconFontFill
name={`more`} name={`more`}
@ -60,12 +60,18 @@ export function Collapse({
'rotate-90': !currentOpen, 'rotate-90': !currentOpen,
})} })}
></IconFontFill> ></IconFontFill>
{title} <div
className={cn('text-text-secondary', {
'text-text-primary': open,
})}
>
{title}
</div>
</div> </div>
<div>{rightContent}</div> <div>{rightContent}</div>
</section> </section>
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent>{children}</CollapsibleContent> <CollapsibleContent className="pt-2">{children}</CollapsibleContent>
</Collapsible> </Collapsible>
); );
} }

View File

@ -56,13 +56,13 @@ export function ConfirmDeleteDialog({
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel onClick={onCancel}> <AlertDialogCancel onClick={onCancel}>
{t('common.cancel')} {t('common.no')}
</AlertDialogCancel> </AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
className="bg-state-error text-text-primary" className="bg-state-error text-text-primary"
onClick={onOk} onClick={onOk}
> >
{t('common.ok')} {t('common.yes')}
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>

View File

@ -73,7 +73,7 @@ const DialogFooter = ({
}: React.HTMLAttributes<HTMLDivElement>) => ( }: React.HTMLAttributes<HTMLDivElement>) => (
<div <div
className={cn( className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-4',
className, className,
)} )}
{...props} {...props}

View File

@ -7,7 +7,7 @@ import * as React from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
const labelVariants = cva( const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-text-secondary',
); );
const Label = React.forwardRef< const Label = React.forwardRef<

View File

@ -94,9 +94,9 @@ export const useShowDeleteConfirm = () => {
title: title ?? t('common.deleteModalTitle'), title: title ?? t('common.deleteModalTitle'),
icon: <ExclamationCircleFilled />, icon: <ExclamationCircleFilled />,
content, content,
okText: t('common.ok'), okText: t('common.yes'),
okType: 'danger', okType: 'danger',
cancelText: t('common.cancel'), cancelText: t('common.no'),
async onOk() { async onOk() {
try { try {
const ret = await onOk?.(); const ret = await onOk?.();

View File

@ -6,8 +6,9 @@ export default {
selectAll: 'Select All', selectAll: 'Select All',
delete: 'Delete', delete: 'Delete',
deleteModalTitle: 'Are you sure to delete this item?', deleteModalTitle: 'Are you sure to delete this item?',
ok: 'Yes', ok: 'Ok',
cancel: 'No', cancel: 'Cancel',
yes: 'Yes',
no: 'No', no: 'No',
total: 'Total', total: 'Total',
rename: 'Rename', rename: 'Rename',
@ -1744,6 +1745,7 @@ Important structured information may include: names, dates, locations, events, k
toolsAvailable: 'tools available', toolsAvailable: 'tools available',
mcpServers: 'MCP Servers', mcpServers: 'MCP Servers',
customizeTheListOfMcpServers: 'Customize the list of MCP servers', customizeTheListOfMcpServers: 'Customize the list of MCP servers',
cachedTools: 'cached tools',
}, },
search: { search: {
searchApps: 'Search Apps', searchApps: 'Search Apps',

View File

@ -6,8 +6,10 @@ export default {
selectAll: '全选', selectAll: '全选',
delete: '删除', delete: '删除',
deleteModalTitle: '确定删除吗?', deleteModalTitle: '确定删除吗?',
ok: '', ok: '确认',
cancel: '', cancel: '取消',
yes: '是',
no: '否',
total: '总共', total: '总共',
rename: '重命名', rename: '重命名',
name: '名称', name: '名称',
@ -1631,6 +1633,7 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
toolsAvailable: '可用的工具', toolsAvailable: '可用的工具',
mcpServers: 'MCP 服务器', mcpServers: 'MCP 服务器',
customizeTheListOfMcpServers: '自定义 MCP 服务器列表', customizeTheListOfMcpServers: '自定义 MCP 服务器列表',
cachedTools: '缓存工具',
}, },
search: { search: {
searchApps: '搜索', searchApps: '搜索',

View File

@ -124,8 +124,8 @@ export const ParsingStatusCell = ({ record }: IProps) => {
onConfirm={handleOperationIconClick(true)} onConfirm={handleOperationIconClick(true)}
onCancel={handleOperationIconClick(false)} onCancel={handleOperationIconClick(false)}
disabled={record.chunk_num === 0} disabled={record.chunk_num === 0}
okText={t('common.ok')} okText={t('common.yes')}
cancelText={t('common.cancel')} cancelText={t('common.no')}
> >
<div <div
className={classNames(styles.operationIcon)} className={classNames(styles.operationIcon)}

View File

@ -1,5 +1,24 @@
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
export function Title({ children }: PropsWithChildren) { export function Title({ children }: PropsWithChildren) {
return <span className="font-bold text-xl">{children}</span>; return <span className="font-bold text-xl">{children}</span>;
} }
type ProfileSettingWrapperCardProps = {
header: React.ReactNode;
} & PropsWithChildren;
export function ProfileSettingWrapperCard({
header,
children,
}: ProfileSettingWrapperCardProps) {
return (
<Card className="w-full mb-5 border-border-button bg-transparent">
<CardHeader className="border-b border-border-button p-5">
{header}
</CardHeader>
<CardContent className="p-5">{children}</CardContent>
</Card>
);
}

View File

@ -1,7 +1,9 @@
import { Collapse } from '@/components/collapse'; import { Collapse } from '@/components/collapse';
import { Button, ButtonLoading } from '@/components/ui/button'; import { Button, ButtonLoading } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { import {
Dialog, Dialog,
DialogClose,
DialogContent, DialogContent,
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
@ -121,36 +123,44 @@ export function EditMcpDialog({
form={form} form={form}
setFieldChanged={setFieldChanged} setFieldChanged={setFieldChanged}
></EditMcpForm> ></EditMcpForm>
<Collapse <Card>
title={ <CardContent className="p-3">
<div> <Collapse
{nextTools?.length || 0} {t('mcp.toolsAvailable')} title={
</div> <div>
} {nextTools?.length || 0} {t('mcp.toolsAvailable')}
open={collapseOpen} </div>
onOpenChange={setCollapseOpen} }
rightContent={ open={collapseOpen}
<Button onOpenChange={setCollapseOpen}
variant={'ghost'} rightContent={
form={FormId} <Button
type="submit" variant={'transparent'}
onClick={handleTest} form={FormId}
type="submit"
onClick={handleTest}
className="border-none p-0 hover:bg-transparent"
>
<RefreshCw
className={cn('text-text-secondary', {
'animate-spin': testLoading,
})}
/>
</Button>
}
> >
<RefreshCw <div className="overflow-auto max-h-80 divide-y bg-bg-card rounded-md px-2.5">
className={cn('text-accent-primary', { {nextTools?.map((x) => (
'animate-spin': testLoading, <McpToolCard key={x.name} data={x}></McpToolCard>
})} ))}
/> </div>
</Button> </Collapse>
} </CardContent>
> </Card>
<div className="space-y-2.5 overflow-auto max-h-80">
{nextTools?.map((x) => (
<McpToolCard key={x.name} data={x}></McpToolCard>
))}
</div>
</Collapse>
<DialogFooter> <DialogFooter>
<DialogClose asChild>
<Button variant="outline">{t('common.cancel')}</Button>
</DialogClose>
<ButtonLoading <ButtonLoading
type="submit" type="submit"
form={FormId} form={FormId}

View File

@ -89,7 +89,7 @@ export function EditMcpForm({
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('common.name')}</FormLabel> <FormLabel required>{t('common.name')}</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder={t('common.mcp.namePlaceholder')} placeholder={t('common.mcp.namePlaceholder')}
@ -106,7 +106,7 @@ export function EditMcpForm({
name="url" name="url"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('mcp.url')}</FormLabel> <FormLabel required>{t('mcp.url')}</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder={t('common.mcp.urlPlaceholder')} placeholder={t('common.mcp.urlPlaceholder')}
@ -127,7 +127,7 @@ export function EditMcpForm({
name="server_type" name="server_type"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('mcp.serverType')}</FormLabel> <FormLabel required>{t('mcp.serverType')}</FormLabel>
<FormControl> <FormControl>
<RAGFlowSelect <RAGFlowSelect
{...field} {...field}

View File

@ -1,13 +1,15 @@
import { BulkOperateBar } from '@/components/bulk-operate-bar'; import { BulkOperateBar } from '@/components/bulk-operate-bar';
import { CardContainer } from '@/components/card-container'; import { CardContainer } from '@/components/card-container';
import Spotlight from '@/components/spotlight';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { SearchInput } from '@/components/ui/input'; import { SearchInput } from '@/components/ui/input';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination'; import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useListMcpServer } from '@/hooks/use-mcp-request'; import { useListMcpServer } from '@/hooks/use-mcp-request';
import { pick } from 'lodash'; import { pick } from 'lodash';
import { Import, Plus } from 'lucide-react'; import { Plus, SquareArrowOutDownLeft } from 'lucide-react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ProfileSettingWrapperCard } from '../components';
import { EditMcpDialog } from './edit-mcp-dialog'; import { EditMcpDialog } from './edit-mcp-dialog';
import { ImportMcpDialog } from './import-mcp-dialog'; import { ImportMcpDialog } from './import-mcp-dialog';
import { McpCard } from './mcp-card'; import { McpCard } from './mcp-card';
@ -33,27 +35,33 @@ export default function McpServer() {
); );
return ( return (
<section className="p-4 w-full"> <ProfileSettingWrapperCard
<div className="text-text-primary text-2xl">{t('mcp.mcpServers')}</div> header={
<section className="flex items-center justify-between pb-5"> <>
<div className="text-text-secondary"> <div className="text-text-primary text-2xl font-semibold">
{t('mcp.customizeTheListOfMcpServers')} {t('mcp.mcpServers')}
</div> </div>
<div className="flex gap-5"> <section className="flex items-center justify-between">
<SearchInput <div className="text-text-secondary">
className="w-40" {t('mcp.customizeTheListOfMcpServers')}
value={searchString} </div>
onChange={handleInputChange} <div className="flex gap-5">
></SearchInput> <SearchInput
<Button variant={'secondary'} onClick={showImportModal}> className="w-40"
<Import /> {t('mcp.import')} value={searchString}
</Button> onChange={handleInputChange}
<Button onClick={showEditModal('')}> ></SearchInput>
<Plus /> {t('mcp.addMCP')} <Button onClick={showEditModal('')}>
</Button> <Plus /> {t('mcp.addMCP')}
</div> </Button>
</section> <Button variant={'secondary'} onClick={showImportModal}>
<SquareArrowOutDownLeft /> {t('mcp.import')}
</Button>
</div>
</section>
</>
}
>
{selectedList.length > 0 && ( {selectedList.length > 0 && (
<BulkOperateBar <BulkOperateBar
list={list} list={list}
@ -93,6 +101,7 @@ export default function McpServer() {
onOk={onImportOk} onOk={onImportOk}
></ImportMcpDialog> ></ImportMcpDialog>
)} )}
</section> <Spotlight className="z-0" opcity={0.7} coverage={70} />
</ProfileSettingWrapperCard>
); );
} }

View File

@ -5,6 +5,7 @@ import { IMcpServer } from '@/interfaces/database/mcp';
import { formatDate } from '@/utils/date'; import { formatDate } from '@/utils/date';
import { isPlainObject } from 'lodash'; import { isPlainObject } from 'lodash';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { McpDropdown } from './mcp-dropdown'; import { McpDropdown } from './mcp-dropdown';
import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp'; import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp';
import { UseEditMcpReturnType } from './use-edit-mcp'; import { UseEditMcpReturnType } from './use-edit-mcp';
@ -20,6 +21,7 @@ export function McpCard({
handleSelectChange, handleSelectChange,
showEditModal, showEditModal,
}: DatasetCardProps) { }: DatasetCardProps) {
const { t } = useTranslation();
const toolLength = useMemo(() => { const toolLength = useMemo(() => {
const tools = data.variables?.tools; const tools = data.variables?.tools;
if (isPlainObject(tools)) { if (isPlainObject(tools)) {
@ -36,7 +38,9 @@ export function McpCard({
<Card key={data.id}> <Card key={data.id}>
<CardContent className="p-2.5 pt-2 group"> <CardContent className="p-2.5 pt-2 group">
<section className="flex justify-between pb-2"> <section className="flex justify-between pb-2">
<h3 className="text-lg font-semibold truncate flex-1">{data.name}</h3> <h3 className="text-base font-normal truncate flex-1 text-text-primary">
{data.name}
</h3>
<div className="space-x-4"> <div className="space-x-4">
<McpDropdown mcpId={data.id} showEditModal={showEditModal}> <McpDropdown mcpId={data.id} showEditModal={showEditModal}>
<MoreButton></MoreButton> <MoreButton></MoreButton>
@ -50,14 +54,12 @@ export function McpCard({
/> />
</div> </div>
</section> </section>
<div className="flex justify-between items-end"> <div className="flex justify-between items-end text-xs text-text-secondary">
<div className="w-full"> <div className="w-full">
<div className="text-base font-semibold mb-3 line-clamp-1 text-text-secondary"> <div className="line-clamp-1 pb-1">
{toolLength} cached tools {toolLength} {t('mcp.cachedTools')}
</div> </div>
<p className="text-sm text-text-secondary"> <p>{formatDate(data.update_date)}</p>
{formatDate(data.update_date)}
</p>
</div> </div>
</div> </div>
</CardContent> </CardContent>

View File

@ -1,4 +1,3 @@
import { Card, CardContent } from '@/components/ui/card';
import { IMCPTool } from '@/interfaces/database/mcp'; import { IMCPTool } from '@/interfaces/database/mcp';
export type McpToolCardProps = { export type McpToolCardProps = {
@ -7,13 +6,11 @@ export type McpToolCardProps = {
export function McpToolCard({ data }: McpToolCardProps) { export function McpToolCard({ data }: McpToolCardProps) {
return ( return (
<Card> <section className="group py-2.5">
<CardContent className="p-2.5 pt-2 group"> <h3 className="text-sm font-semibold line-clamp-1 pb-2">{data.name}</h3>
<h3 className="text-sm font-semibold line-clamp-1 pb-2">{data.name}</h3> <div className="text-xs font-normal text-text-secondary">
<div className="text-xs font-normal mb-3 text-text-secondary"> {data.description}
{data.description} </div>
</div> </section>
</CardContent>
</Card>
); );
} }