Fix: Refactoring and enhancing the functionality of the delete confirmation dialog component #10703 (#11542)

### What problem does this PR solve?

Fix: Refactoring and enhancing the functionality of the delete
confirmation dialog component

- Refactoring and enhancing the functionality of the delete confirmation
dialog component
- Modifying the style of the user center

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-11-26 19:49:21 +08:00
committed by GitHub
parent 89ba7abe30
commit 376eb15c63
25 changed files with 393 additions and 112 deletions

View File

@ -1,9 +1,13 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { BrushCleaning } from 'lucide-react';
import { ReactNode, useCallback } from 'react';
import { ConfirmDeleteDialog } from './confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from './confirm-delete-dialog';
import { Separator } from './ui/separator';
export type BulkOperateItemType = {
@ -45,6 +49,15 @@ export function BulkOperateBar({
<ConfirmDeleteDialog
hidden={!isDeleteItem(x.id)}
onOk={x.onClick}
title={t('deleteModal.delFiles')}
content={{
title: t('common.deleteThem'),
node: (
<ConfirmDeleteDialogNode
name={`${t('deleteModal.delFilesContent', { count })}`}
></ConfirmDeleteDialogNode>
),
}}
>
<Button
variant={'ghost'}

View File

@ -3,19 +3,30 @@ import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { AlertDialogOverlay } from '@radix-ui/react-alert-dialog';
import { DialogProps } from '@radix-ui/react-dialog';
import { X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { RAGFlowAvatar } from './ragflow-avatar';
import { Separator } from './ui/separator';
interface IProps {
title?: string;
onOk?: (...args: any[]) => any;
onCancel?: (...args: any[]) => any;
hidden?: boolean;
content?: {
title?: string;
node?: React.ReactNode;
};
okButtonText?: string;
cancelButtonText?: string;
}
export function ConfirmDeleteDialog({
@ -27,6 +38,9 @@ export function ConfirmDeleteDialog({
onOpenChange,
open,
defaultOpen,
content,
okButtonText,
cancelButtonText,
}: IProps & DialogProps) {
const { t } = useTranslation();
@ -41,32 +55,78 @@ export function ConfirmDeleteDialog({
defaultOpen={defaultOpen}
>
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
<AlertDialogContent
onSelect={(e) => e.preventDefault()}
onClick={(e) => e.stopPropagation()}
className="bg-bg-base"
<AlertDialogOverlay
onClick={(e) => {
e.stopPropagation();
}}
>
<AlertDialogHeader>
<AlertDialogTitle>
{title ?? t('common.deleteModalTitle')}
</AlertDialogTitle>
{/* <AlertDialogDescription>
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
</AlertDialogDescription> */}
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={onCancel}>
{t('common.no')}
</AlertDialogCancel>
<AlertDialogAction
className="bg-state-error text-text-primary hover:text-text-primary hover:bg-state-error"
onClick={onOk}
>
{t('common.yes')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
<AlertDialogContent
onSelect={(e) => e.preventDefault()}
onClick={(e) => e.stopPropagation()}
className="bg-bg-base "
>
<AlertDialogHeader className="space-y-5">
<AlertDialogTitle>
{title ?? t('common.deleteModalTitle')}
<AlertDialogCancel
onClick={onCancel}
className="border-none bg-transparent hover:border-none hover:bg-transparent absolute right-3 top-3 hover:text-text-primary"
>
<X size={16} />
</AlertDialogCancel>
</AlertDialogTitle>
{content && (
<>
<Separator className="w-[calc(100%+48px)] -translate-x-6"></Separator>
<AlertDialogDescription className="mt-5">
<div className="flex flex-col gap-5 text-base mb-10 px-5">
<div className="text-text-primary">
{content.title || t('common.deleteModalTitle')}
</div>
{content.node}
</div>
</AlertDialogDescription>
</>
)}
</AlertDialogHeader>
<AlertDialogFooter className="px-5 flex items-center gap-2">
<AlertDialogCancel onClick={onCancel}>
{okButtonText || t('common.cancel')}
</AlertDialogCancel>
<AlertDialogAction
className="bg-state-error text-text-primary hover:text-text-primary hover:bg-state-error"
onClick={onOk}
>
{cancelButtonText || t('common.delete')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
);
}
export const ConfirmDeleteDialogNode = ({
avatar,
name,
children,
}: {
avatar?: { avatar?: string; name?: string; isPerson?: boolean };
name?: string;
children?: React.ReactNode;
}) => {
return (
<div className="flex items-center border-0.5 text-text-secondary border-border-button rounded-lg px-3 py-4">
{avatar && (
<RAGFlowAvatar
className="w-8 h-8"
avatar={avatar.avatar}
isPerson={avatar.isPerson}
name={avatar.name}
/>
)}
{name && <div className="ml-3">{name}</div>}
{children}
</div>
);
};

View File

@ -274,7 +274,7 @@ export function FileUploader(props: FileUploaderProps) {
className={cn(
'group relative grid h-72 w-full cursor-pointer place-items-center rounded-lg border border-dashed border-border-default px-5 py-2.5 text-center transition hover:bg-border-button bg-bg-card',
'ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
isDragActive && 'border-muted-foreground/50',
isDragActive && 'border-border-button',
isDisabled && 'pointer-events-none opacity-60',
className,
)}
@ -285,7 +285,7 @@ export function FileUploader(props: FileUploaderProps) {
<div className="flex flex-col items-center justify-center gap-4 sm:px-5">
<div className="rounded-full border border-dashed p-3">
<Upload
className="size-7 text-text-secondary"
className="size-7 text-text-secondary hover:text-text-primary"
aria-hidden="true"
/>
</div>
@ -297,7 +297,7 @@ export function FileUploader(props: FileUploaderProps) {
<div className="flex flex-col items-center justify-center gap-4 sm:px-5">
<div className="rounded-full border border-dashed p-3">
<Upload
className="size-7 text-text-secondary"
className="size-7 text-text-secondary hover:text-text-primary"
aria-hidden="true"
/>
</div>

View File

@ -186,7 +186,9 @@ export const SelectWithSearch = forwardRef<
/>
)}
<CommandList className="mt-2 outline-none">
<CommandEmpty>{emptyData}</CommandEmpty>
<CommandEmpty>
<div dangerouslySetInnerHTML={{ __html: emptyData }}></div>
</CommandEmpty>
{options.map((group, idx) => {
if (group.options) {
return (

View File

@ -53,6 +53,11 @@ const buttonVariants = cva(
bg-text-primary text-bg-base border-b-4 border-b-accent-primary
hover:bg-text-primary/90 focus-visible:bg-text-primary/90
`,
delete: `
text-text-secondary
hover:bg-state-error-5 hover:text-state-error
focus-visible:text-state-error focus-visible:bg-state-error-5
`,
},
size: {
default: 'h-8 px-2.5 py-1.5 ',

View File

@ -76,6 +76,7 @@ export function useDynamicSVGImport(
}
interface IProps {
header?: string | ReactNode;
title?: string;
content?: ReactNode;
onOk?: (...args: any[]) => any;
@ -85,19 +86,19 @@ interface IProps {
export const useShowDeleteConfirm = () => {
const { t } = useTranslation();
const showDeleteConfirm = useCallback(
({ title, content, onOk, onCancel }: IProps): Promise<number> => {
({ title, content, onOk, onCancel, header }: IProps): Promise<number> => {
return new Promise((resolve, reject) => {
Modal.show({
// title: title ?? t('common.deleteModalTitle'),
closable: false,
title: header,
closable: !!header,
visible: true,
onVisibleChange: () => {
Modal.hide();
Modal.destroy();
},
footer: null,
maskClosable: false,
okText: t('common.yes'),
cancelText: t('common.no'),
okText: t('common.delete'),
cancelText: t('common.cancel'),
style: {
width: '450px',
},
@ -114,7 +115,7 @@ export const useShowDeleteConfirm = () => {
},
onCancel: () => {
onCancel?.();
Modal.hide();
Modal.destroy();
},
children: (
<div className="flex flex-col justify-start items-start mt-3">

View File

@ -7,7 +7,8 @@ export default {
selectPlaceholder: 'select value',
selectAll: 'Select all',
delete: 'Delete',
deleteModalTitle: 'Are you sure to delete this item?',
deleteModalTitle: 'Are you sure to delete it ?',
deleteThem: 'Are you sure to delete them ?',
ok: 'Ok',
cancel: 'Cancel',
yes: 'Yes',
@ -59,6 +60,7 @@ export default {
urlPlaceholder: 'https://api.example.com/v1/mcp',
tokenPlaceholder: 'e.g. eyJhbGciOiJIUzI1Ni...',
},
selected: 'Selected',
},
login: {
loginTitle: 'Sign in to Your Account',
@ -92,7 +94,7 @@ export default {
home: 'Home',
setting: 'User settings',
logout: 'Log out',
fileManager: 'File Management',
fileManager: 'File',
flow: 'Agent',
search: 'Search',
welcome: 'Welcome to',
@ -696,8 +698,9 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
tocEnhanceTip: ` During the parsing of the document, table of contents information was generated (see the 'Enable Table of Contents Extraction' option in the General method). This allows the large model to return table of contents items relevant to the user's query, thereby using these items to retrieve related chunks and apply weighting to these chunks during the sorting process. This approach is derived from mimicking the behavioral logic of how humans search for knowledge in books.`,
},
setting: {
deleteModel: 'Delete model',
modelEmptyTip:
'No models available. Please add models from the panel on the right.',
'No models available. <br>Please add models from the panel on the right.',
sourceEmptyTip: 'No data sources added yet. Select one below to connect.',
seconds: 'seconds',
minutes: 'minutes',
@ -1917,6 +1920,7 @@ Important structured information may include: names, dates, locations, events, k
editMCP: 'Edit MCP',
toolsAvailable: 'tools available',
mcpServers: 'MCP servers',
mcpServer: 'MCP server',
customizeTheListOfMcpServers: 'Customize the list of MCP servers',
cachedTools: 'cached tools',
bulkManage: 'Bulk manage',
@ -2014,6 +2018,18 @@ Important structured information may include: names, dates, locations, events, k
processingFailedTip: 'Total failed processes',
processing: 'Processing',
},
deleteModal: {
delAgent: 'Delete agent',
delDataset: 'Delete dataset',
delSearch: 'Delete search',
delFile: 'Delete file',
delFiles: 'Delete files',
delFilesContent: 'Selected {{count}} files',
delChat: 'Delete chat',
delMember: 'Delete member',
},
admin: {
loginTitle: 'Admin Console',
title: 'RAGFlow',

View File

@ -8,6 +8,7 @@ export default {
selectAll: '全选',
delete: '删除',
deleteModalTitle: '确定删除吗?',
deleteThem: '确定要删除吗?',
ok: '确认',
cancel: '取消',
yes: '是',
@ -51,6 +52,7 @@ export default {
noDataFound: '没有找到数据。',
noData: '暂无数据',
promptPlaceholder: '请输入或使用 / 快速插入变量。',
selected: '已选择',
},
login: {
loginTitle: '登录账户',
@ -685,7 +687,8 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
tocEnhanceTip: `解析文档时生成了目录信息见General方法的启用目录抽取让大模型返回和用户问题相关的目录项从而利用目录项拿到相关chunk对这些chunk在排序中进行加权。这种方法来源于模仿人类查询书本中知识的行为逻辑`,
},
setting: {
modelEmptyTip: '暂无可用模型,请先在右侧面板添加模型。',
deleteModel: '删除模型',
modelEmptyTip: '暂无可用模型,<br>请先在右侧面板添加模型。',
sourceEmptyTip: '暂未添加任何数据源,请从下方选择一个进行连接。',
seconds: '秒',
minutes: '分',
@ -1770,6 +1773,7 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
editMCP: '编辑 MCP',
toolsAvailable: '可用的工具',
mcpServers: 'MCP 服务器',
mcpServer: 'MCP 服务器',
customizeTheListOfMcpServers: '自定义 MCP 服务器列表',
cachedTools: '缓存工具',
selected: '已选择',
@ -1866,5 +1870,16 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
processingSuccessTip: '处理成功的文件总数',
processingFailedTip: '处理失败的文件总数',
},
deleteModal: {
delAgent: '删除智能体',
delDataset: '删除知识库',
delSearch: '删除搜索',
delFile: '删除文件',
delFiles: '删除文件',
delFilesContent: '已选择 {{count}} 个文件',
delChat: '删除聊天',
delMember: '删除成员',
},
},
};

View File

@ -1,4 +1,7 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import {
DropdownMenu,
DropdownMenuContent,
@ -45,7 +48,18 @@ export function AgentDropdown({
{t('common.rename')} <PenLine />
</DropdownMenuItem>
<DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('deleteModal.delAgent')}
content={{
node: (
<ConfirmDeleteDialogNode
avatar={{ avatar: agent.avatar, name: agent.title }}
name={agent.title}
/>
),
}}
>
<DropdownMenuItem
className="text-state-error"
onSelect={(e) => {

View File

@ -3,6 +3,7 @@ import isEmpty from 'lodash/isEmpty';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { buildNodesAndCombos } from './util';
import { useIsDarkTheme } from '@/components/theme-provider';
import styles from './index.less';
const TooltipColorMap = {
@ -19,7 +20,7 @@ interface IProps {
const ForceGraph = ({ data, show }: IProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const graphRef = useRef<Graph | null>(null);
const isDark = useIsDarkTheme();
const nextData = useMemo(() => {
if (!isEmpty(data)) {
const graphData = data;
@ -80,8 +81,13 @@ const ForceGraph = ({ data, show }: IProps) => {
},
node: {
style: {
size: 150,
size: (d) => {
let size = 100 + ((d.rank as number) || 0) * 5;
size = size > 300 ? 300 : size;
return size;
},
labelText: (d) => d.id,
labelFill: isDark ? 'rgba(255,255,255,1)' : 'rgba(0,0,0,1)',
// labelPadding: 30,
labelFontSize: 40,
// labelOffsetX: 20,
@ -101,8 +107,9 @@ const ForceGraph = ({ data, show }: IProps) => {
const weight: number = Number(model?.weight) || 2;
const lineWeight = weight * 4;
return {
stroke: '#99ADD1',
lineWidth: lineWeight > 10 ? 10 : lineWeight,
stroke: isDark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)',
lineDash: [10, 10],
lineWidth: lineWeight > 8 ? 8 : lineWeight,
};
},
},

View File

@ -1,4 +1,7 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import {
DropdownMenu,
DropdownMenuContent,
@ -45,7 +48,18 @@ export function DatasetDropdown({
{t('common.rename')} <PenLine />
</DropdownMenuItem>
<DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('deleteModal.delDataset')}
content={{
node: (
<ConfirmDeleteDialogNode
avatar={{ avatar: dataset.avatar, name: dataset.name }}
name={dataset.name}
/>
),
}}
>
<DropdownMenuItem
className="text-state-error"
onSelect={(e) => {

View File

@ -1,13 +1,19 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import { FileIcon } from '@/components/icon-font';
import NewDocumentLink from '@/components/new-document-link';
import { Button } from '@/components/ui/button';
import { useDownloadFile } from '@/hooks/file-manager-hooks';
import { IFile } from '@/interfaces/database/file-manager';
import { cn } from '@/lib/utils';
import {
getExtension,
isSupportedPreviewDocumentType,
} from '@/utils/document-util';
import { CellContext } from '@tanstack/react-table';
import { t } from 'i18next';
import {
ArrowDownToLine,
Eye,
@ -38,6 +44,9 @@ export function ActionCell({
}: IProps) {
const record = row.original;
const documentId = record.id;
const name: string = row.getValue('name');
const type = record.type;
const { downloadFile } = useDownloadFile();
const isFolder = isFolderType(record.type);
const extension = getExtension(record.name);
@ -151,7 +160,28 @@ export function ActionCell({
</DropdownMenuContent>
</DropdownMenu> */}
{isKnowledgeBase || (
<ConfirmDeleteDialog onOk={onRemoveFile}>
<ConfirmDeleteDialog
onOk={onRemoveFile}
title={t('deleteModal.delFile')}
content={{
node: (
<ConfirmDeleteDialogNode>
<div className="flex items-center gap-2 text-text-secondary">
<span className="size-4">
<FileIcon name={name} type={type}></FileIcon>
</span>
<span
className={cn('truncate text-xs', {
['cursor-pointer']: isFolder,
})}
>
{name}
</span>
</div>
</ConfirmDeleteDialogNode>
),
}}
>
<Button
variant="transparent"
className="border-none hover:bg-bg-card text-text-primary"

View File

@ -1,4 +1,7 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import {
DropdownMenu,
DropdownMenuContent,
@ -45,7 +48,18 @@ export function ChatDropdown({
{t('common.rename')} <PenLine />
</DropdownMenuItem>
<DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('deleteModal.delChat')}
content={{
node: (
<ConfirmDeleteDialogNode
avatar={{ avatar: chat.icon, name: chat.name }}
name={chat.name}
/>
),
}}
>
<DropdownMenuItem
className="text-state-error"
onSelect={(e) => {

View File

@ -1,4 +1,7 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import {
DropdownMenu,
DropdownMenuContent,
@ -41,9 +44,20 @@ export function SearchDropdown({
{t('common.rename')} <PenLine />
</DropdownMenuItem>
<DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('deleteModal.delSearch')}
content={{
node: (
<ConfirmDeleteDialogNode
avatar={{ avatar: dataset.avatar, name: dataset.name }}
name={dataset.name}
/>
),
}}
>
<DropdownMenuItem
className="text-text-delete-red"
className="text-state-error"
onSelect={(e) => {
e.preventDefault();
}}

View File

@ -31,7 +31,7 @@ export const AddedSourceCard = (props: IAddedSourceCardProps) => {
key={item.id}
className="flex flex-row items-center justify-between rounded-md bg-bg-card px-[10px] py-4"
>
<div className="text-sm text-text-secondary ">{item.name}</div>
<div className="text-sm text-text-primary ">{item.name}</div>
<div className="text-sm text-text-secondary flex gap-2">
<Button
variant={'ghost'}

View File

@ -21,28 +21,33 @@ export const delSourceModal = <T extends IDataSourceBase>(
? t('setting.deleteSourceModalTitle')
: t('dataflowParser.unlinkSourceModalTitle'),
content: (
<div className="px-2 py-6">
<div className="flex items-center gap-1 p-2 border border-border-button rounded-md mb-3">
<div className="w-6 h-6 flex-shrink-0">
{data?.source ? DataSourceInfo[data?.source].icon : ''}
</div>
<div>{data?.name}</div>
</div>
<div className="px-2 space-y-6 pt-5 pb-3">
{type === 'delete' ? (
<div
className="text-sm text-text-secondary"
className="text-base text-text-primary"
dangerouslySetInnerHTML={{
__html: t('setting.deleteSourceModalContent'),
}}
></div>
) : (
<div
className="text-sm text-text-secondary"
className="text-base text-text-primary"
dangerouslySetInnerHTML={{
__html: t('dataflowParser.unlinkSourceModalContent'),
}}
/>
)}
<div className="flex items-center gap-1 p-2 border border-border-button rounded-md mb-3">
<div className="w-6 h-6 flex-shrink-0">
{data?.source ? DataSourceInfo[data?.source].icon : ''}
</div>
<div className="flex items-center gap-2 text-text-secondary text-xs">
{/* <div className="h-6 flex-shrink-0 text-text-primary text-base">
{data?.source ? DataSourceInfo[data?.source].name : ''}
</div> */}
{data?.name}
</div>
</div>
</div>
),
confirmText:
@ -56,12 +61,13 @@ export const delSourceModal = <T extends IDataSourceBase>(
...otherProps,
title: config.title,
children: config.content,
titleClassName: 'border-b border-border-button',
onVisibleChange: () => {
Modal.hide();
Modal.destroy();
},
footer: (
<div className="flex justify-end gap-2">
<Button variant={'outline'} onClick={() => Modal.hide()}>
<Button variant={'outline'} onClick={() => Modal.destroy()}>
{t('dataflowParser.changeStepModalCancelText')}
</Button>
<Button
@ -69,7 +75,7 @@ export const delSourceModal = <T extends IDataSourceBase>(
className="!bg-state-error text-text-base"
onClick={() => {
onOk?.(data);
Modal.hide();
Modal.destroy();
}}
>
{config.confirmText}

View File

@ -84,7 +84,7 @@ const DataSource = () => {
>
<Spotlight />
<div className="relative">
<div className=" flex flex-col gap-4 max-h-[calc(100vh-230px)] overflow-y-auto overflow-x-hidden scrollbar-auto">
<div className=" flex flex-col gap-4 max-h-[calc(100vh-235px)] overflow-y-auto overflow-x-hidden scrollbar-auto">
<div className="flex flex-col gap-3">
{categorizedList?.length <= 0 && (
<div className="text-text-secondary w-full flex justify-center items-center h-20">

View File

@ -1,5 +1,8 @@
import { CardContainer } from '@/components/card-container';
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import Spotlight from '@/components/spotlight';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
@ -110,7 +113,18 @@ export default function McpServer() {
<Upload className="size-3.5"></Upload>
{t('mcp.export')}
</Button>
<ConfirmDeleteDialog onOk={handleDelete}>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('common.delete') + ' ' + t('mcp.mcpServers')}
content={{
title: t('common.deleteThem'),
node: (
<ConfirmDeleteDialogNode
name={`${t('mcp.selected')} ${selectedList.length} ${t('mcp.mcpServers')}`}
></ConfirmDeleteDialogNode>
),
}}
>
<Button variant={'danger'}>
<Trash2 className="size-3.5 cursor-pointer" />
{t('common.delete')}

View File

@ -54,7 +54,7 @@ export function McpCard({
/>
) : (
<McpOperation
mcpId={data.id}
mcp={data}
showEditModal={showEditModal}
></McpOperation>
)}

View File

@ -1,5 +1,9 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import { useDeleteMcpServer } from '@/hooks/use-mcp-request';
import { IMcpServer } from '@/interfaces/database/mcp';
import { PenLine, Trash2, Upload } from 'lucide-react';
import { MouseEventHandler, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@ -7,33 +11,41 @@ import { UseEditMcpReturnType } from './use-edit-mcp';
import { useExportMcp } from './use-export-mcp';
export function McpOperation({
mcpId,
mcp,
showEditModal,
}: { mcpId: string } & Pick<UseEditMcpReturnType, 'showEditModal'>) {
}: { mcp: IMcpServer } & Pick<UseEditMcpReturnType, 'showEditModal'>) {
const { t } = useTranslation();
const { deleteMcpServer } = useDeleteMcpServer();
const { handleExportMcpJson } = useExportMcp();
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
deleteMcpServer([mcpId]);
}, [deleteMcpServer, mcpId]);
deleteMcpServer([mcp.id]);
}, [deleteMcpServer, mcp.id]);
return (
<div className="hidden gap-1 group-hover:flex text-text-secondary">
{/* <RAGFlowTooltip tooltip={t('mcp.export')}> */}
<Upload
className="size-5 cursor-pointer p-1 rounded-sm hover:text-text-primary hover:bg-bg-card"
onClick={handleExportMcpJson([mcpId])}
onClick={handleExportMcpJson([mcp.id])}
/>
{/* </RAGFlowTooltip>
<RAGFlowTooltip tooltip={t('common.edit')}> */}
<PenLine
className="size-5 cursor-pointer p-1 rounded-sm hover:text-text-primary hover:bg-bg-card"
onClick={showEditModal(mcpId)}
onClick={showEditModal(mcp.id)}
/>
{/* </RAGFlowTooltip>
<RAGFlowTooltip tooltip={t('common.delete')}> */}
<ConfirmDeleteDialog onOk={handleDelete}>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('common.delete') + ' ' + t('mcp.mcpServer')}
content={{
node: (
<ConfirmDeleteDialogNode name={mcp.name}></ConfirmDeleteDialogNode>
),
}}
>
<Trash2 className="size-5 cursor-pointer p-1 rounded-sm hover:text-state-error hover:bg-state-error-5" />
</ConfirmDeleteDialog>
{/* </RAGFlowTooltip> */}

View File

@ -1,4 +1,8 @@
// src/components/ModelProviderCard.tsx
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import { LlmIcon } from '@/components/svg-icon';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
@ -54,7 +58,7 @@ export const ModelProviderCard: FC<IModelCardProps> = ({
const { visible, switchVisible } = useSetModalState();
const { t } = useTranslate('setting');
const { handleEnableLlm } = useHandleEnableLlm(item.name);
const { handleDeleteFactory } = useHandleDeleteFactory(item.name);
const { deleteFactory } = useHandleDeleteFactory(item.name);
const handleApiKeyClick = () => {
clickApiKey(item.name);
@ -102,16 +106,31 @@ export const ModelProviderCard: FC<IModelCardProps> = ({
{!visible ? <ChevronsDown /> : <ChevronsUp />}
</Button>
<Button
variant={'ghost'}
onClick={(e) => {
e.stopPropagation();
handleDeleteFactory();
<ConfirmDeleteDialog
onOk={() => deleteFactory({ llm_factory: item.name })}
title={t('deleteModel')}
content={{
node: (
<ConfirmDeleteDialogNode>
<div className="flex items-center gap-2">
<LlmIcon name={item.name} />
{item.name}
</div>
</ConfirmDeleteDialogNode>
),
}}
className=" hover:text-state-error hover:bg-state-error-5 transition-colors border border-border-default"
>
<Trash2 />
</Button>
<Button
variant={'ghost'}
// onClick={(e) => {
// e.stopPropagation();
// handleDeleteFactory(item);
// }}
className=" hover:text-state-error hover:bg-state-error-5 transition-colors border border-border-default"
>
<Trash2 />
</Button>
</ConfirmDeleteDialog>
</div>
</div>

View File

@ -1,10 +1,10 @@
// src/components/AvailableModels.tsx
import { LlmIcon } from '@/components/svg-icon';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { SearchInput } from '@/components/ui/input';
import { useTranslate } from '@/hooks/common-hooks';
import { useSelectLlmList } from '@/hooks/llm-hooks';
import { Plus, Search } from 'lucide-react';
import { Plus } from 'lucide-react';
import { FC, useMemo, useState } from 'react';
type TagType =
@ -77,16 +77,16 @@ export const AvailableModels: FC<{
</div>
{/* Search Bar */}
<div className="mb-6">
<div className="relative">
<Input
type="text"
placeholder={t('search')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full px-4 py-2 pl-10 bg-bg-input border border-border-default rounded-lg focus:outline-none focus:ring-1 focus:ring-border-button transition-colors"
/>
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-text-secondary" />
</div>
{/* <div className="relative"> */}
<SearchInput
type="text"
placeholder={t('search')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full px-4 py-2 pl-10 bg-bg-input border border-border-default rounded-lg focus:outline-none focus:ring-1 focus:ring-border-button transition-colors"
/>
{/* <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-text-secondary" /> */}
{/* </div> */}
</div>
{/* Tags Filter */}

View File

@ -457,5 +457,5 @@ export const useHandleDeleteFactory = (llmFactory: string) => {
});
};
return { handleDeleteFactory };
return { handleDeleteFactory, deleteFactory };
};

View File

@ -51,7 +51,7 @@ export const useHandleDeleteUser = () => {
});
};
return { handleDeleteTenantUser, loading };
return { handleDeleteTenantUser, deleteTenantUser, loading };
};
export const useHandleAgreeTenant = () => {

View File

@ -1,3 +1,7 @@
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@ -26,7 +30,7 @@ const ColorMap: Record<string, string> = {
const UserTable = ({ searchUser }: { searchUser: string }) => {
const { data, loading } = useListTenantUser();
const { handleDeleteTenantUser } = useHandleDeleteUser();
const { deleteTenantUser } = useHandleDeleteUser();
const [sortOrder, setSortOrder] = useState<'asc' | 'desc' | null>(null);
const { t } = useTranslation();
const sortedData = useMemo(() => {
@ -134,14 +138,35 @@ const UserTable = ({ searchUser }: { searchUser: string }) => {
)}
</TableCell>
<TableCell className="p-4">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 p-0 hover:bg-state-error-5 hover:text-state-error"
onClick={handleDeleteTenantUser(record.user_id)}
<ConfirmDeleteDialog
title={t('deleteModal.delMember')}
onOk={async () => {
await deleteTenantUser({
userId: record.user_id,
});
return;
}}
content={{
node: (
<ConfirmDeleteDialogNode
avatar={{
avatar: record.avatar,
name: record.nickname,
isPerson: true,
}}
name={record.email}
></ConfirmDeleteDialogNode>
),
}}
>
<Trash2 className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 p-0 hover:bg-state-error-5 hover:text-state-error"
>
<Trash2 className="h-4 w-4" />
</Button>
</ConfirmDeleteDialog>
</TableCell>
</TableRow>
))