Feat: Modify the style of the release confirmation box. (#13542)

### What problem does this PR solve?

Feat: Modify the style of the release confirmation box.

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
Co-authored-by: balibabu <assassin_cike@163.com>
Co-authored-by: 6ba3i <isbaaoui09@gmail.com>
This commit is contained in:
balibabu
2026-03-13 16:31:17 +08:00
committed by GitHub
parent 02070bab2a
commit 717f1f1362
12 changed files with 196 additions and 97 deletions

View File

@ -17,12 +17,11 @@ interface IProps {
sharedBadge?: ReactNode;
icon?: React.ReactNode;
testId?: string;
showReleaseTime?: boolean;
}
function Time({ time }: { time: string | number | undefined }) {
return (
<p className="text-sm opacity-80 whitespace-nowrap">{formatDate(time)}</p>
);
return <p className="text-sm whitespace-nowrap">{formatDate(time)}</p>;
}
export function HomeCard({
data,
@ -31,6 +30,7 @@ export function HomeCard({
sharedBadge,
icon,
testId,
showReleaseTime = false,
}: IProps) {
const { t } = useTranslation();
@ -82,16 +82,18 @@ export function HomeCard({
{data.description}
</div>
<div className="flex justify-between items-center">
{data.release_time ? (
<section>
<div className="flex items-center gap-2 text-sm opacity-80">
{showReleaseTime ? (
<section className="text-sm text-text-secondary space-y-1">
<div className="flex items-center gap-2">
{`${t('flow.lastSavedAt')}:`}
<Time time={data.update_time}></Time>
</div>
<div className="flex items-center gap-2 text-sm opacity-80">
{`${t('flow.publishedAt')}:`}
<Time time={data.release_time}></Time>
</div>
{data.release_time && (
<div className="flex items-center gap-2">
{`${t('flow.publishedAt')}:`}
<Time time={data.release_time}></Time>
</div>
)}
</section>
) : (
<Time time={data.update_time}></Time>

View File

@ -33,6 +33,7 @@ export interface ISwitchForm {
import { AgentCategory } from '@/constants/agent';
import { Edge, Node } from '@xyflow/react';
import { IReference, Message } from './chat';
import { IKnowledge } from './knowledge';
export type DSLComponents = Record<string, IOperator>;
@ -80,6 +81,7 @@ export declare interface IFlow {
release?: boolean;
release_time?: number;
last_publish_time?: number;
datasets?: Pick<IKnowledge, 'id' | 'name' | 'avatar'>[];
}
export interface IFlowTemplate {

View File

@ -2196,9 +2196,11 @@ This process aggregates variables from multiple branches into a single variable
production: 'Production',
productionTooltip:
'This version is published to production. Access it via the API or the embedded page.',
confirmPublish: 'Confirm Publish',
publishDescription: 'You are about to publish this data pipeline.',
linkedDataset: 'Linked dataset',
confirmPublish: 'Confirm publish',
publishIngestionPipeline:
'You are about to publish this Ingestion pipeline.',
publishAgent: 'You are about to publish this agent',
linkedDataset: 'Linked dataset:',
lastPublished: 'Last published',
createFromBlank: 'Create from blank',
createFromTemplate: 'Create from template',

View File

@ -202,7 +202,7 @@ export default {
// editMetadataForDataset: '查看和编辑元数据 ',
restrictDefinedValues: '限制为已定义的值',
metadataGenerationSettings: '元数据生成设置',
// manageMetadataForDataset: '管理此数据集的元数据',
// manageMetadataForDataset: '管理此知识库的元数据',
manageMetadata: '管理元数据',
metadata: '元数据',
values: '值',
@ -247,7 +247,7 @@ export default {
startDate: '开始时间',
source: '来源',
fileName: '文件名',
datasetLogs: '数据集',
datasetLogs: '知识库',
fileLogs: '文件',
overview: '日志',
success: '成功',
@ -390,7 +390,7 @@ export default {
knowledgeConfiguration: {
randomSeedTip:
'种子是伪随机算法的起点,它确保在不同运行中产生相同的输出,从而保证可重复性。',
datasetDescription: '你的数据集描述。',
datasetDescription: '你的知识库描述。',
overlappedPercentTip: '相邻两个块之间的重叠百分比',
settings: '设置',
autoMetadataTip:
@ -430,13 +430,13 @@ export default {
baseInfo: '基础信息',
globalIndex: '全局索引',
dataSource: '数据源',
linkSourceSetTip: '管理与此数据集的数据源链接',
linkSourceSetTip: '管理与此知识库的数据源链接',
linkDataSource: '链接数据源',
tocExtractionTip:
'对于已有的chunk生成层级结构的目录信息每个文件一个目录。在查询时激活`Page Index`后系统会用大模型去判断用户问题和哪些目录项相关从而找到相关的chunk。',
deleteGenerateModalContent: `
<p>删除生成的 <strong class='text-text-primary'>{{type}}</strong> 结果
将从此数据集中移除所有派生实体和关系。
将从此知识库中移除所有派生实体和关系。
您的原始文件将保持不变。<p>
<br/>
是否要继续?
@ -449,7 +449,7 @@ export default {
setDefaultTip: '',
setDefault: '设置默认',
editLinkDataPipeline: '编辑pipeline',
linkPipelineSetTip: '管理与此数据集的数据管道链接',
linkPipelineSetTip: '管理与此知识库的数据管道链接',
default: '默认',
dataPipeline: '切换或配置 ingestion pipeline。',
linkDataPipeline: '关联pipeline',
@ -630,7 +630,7 @@ export default {
tagSetTip: `
<p> 请选择一个或多个标签集或标签知识库,用于对知识库中的每个文本块进行标记。</p>
<p>对这些文本块的查询也将自动关联相应标签。 </p>
<p>此功能基于文本相似度,能够为数据集的文本块批量添加更多领域知识,从而显著提高检索准确性。该功能还能提升大量文本块的操作效率。</p>
<p>此功能基于文本相似度,能够为知识库的文本块批量添加更多领域知识,从而显著提高检索准确性。该功能还能提升大量文本块的操作效率。</p>
<p>为了更好地理解标签集的作用,以下是标签集和关键词之间的主要区别:</p>
<ul>
<li>标签集是一个由用户定义和管理的封闭集,而自动生成的关键词属于开放集合。 </li>
@ -704,7 +704,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
assistantAvatar: '助理头像',
language: '语言',
emptyResponse: '空回复',
emptyResponsePlaceholder: '在数据集中未找到您要寻找的答案!',
emptyResponsePlaceholder: '在知识库中未找到您要寻找的答案!',
emptyResponseTip: `如果在知识库中没有检索到用户的问题,它将使用它作为答案。 如果您希望 LLM 在未检索到任何内容时提出自己的意见,请将此留空。`,
emptyResponseMessage: `当知识库中未检索到任何相关信息时,将触发空响应。由于未选择任何知识库,因此请清除“空响应”。`,
setAnOpener: '设置开场白',
@ -719,7 +719,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
systemPlaceholder: `你是一个智能助手,主要功能是基于提供的知识库严格回答问题。
**重要规则:**
- 你的回答必须**仅**来自此数据集{knowledge}。
- 你的回答必须**仅**来自此知识库{knowledge}。
- **当信息可用时**: 总结内容以给出详细答案。
- **当信息不可用时**: 你的回答必须包含这句确切的话:"在知识库中未找到您要的答案!"
- **始终考虑**整个对话历史。`,
@ -1893,6 +1893,11 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
release: '发布',
production: '正式版',
productionTooltip: '此版本已发布到生产环境。可通过 API 或嵌入页面访问。',
confirmPublish: '确认发布',
publishIngestionPipeline: '您即将发布此 Ingestion pipeline。',
publishAgent: '您即将发布此智能体',
linkedDataset: '已关联的知识库:',
lastPublished: '上次发布时间',
createFromBlank: '从空白创建',
createFromTemplate: '从模板创建',
importJsonFile: '导入 JSON 文件',
@ -1920,7 +1925,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
regularExpressions: '正则表达式',
overlappedPercent: '重叠百分比(%',
searchMethod: '搜索方法',
searchMethodTip: `决定该数据集启用的搜索方式,可选择全文、向量,或两者兼有。
searchMethodTip: `决定该知识库启用的搜索方式,可选择全文、向量,或两者兼有。
Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
filenameEmbdWeight: '文件名嵌入权重',
parserMethod: '解析方法',
@ -2178,7 +2183,7 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
changeStepModalCancelText: '取消',
unlinkPipelineModalTitle: '解绑pipeline',
unlinkPipelineModalContent: `
<p>一旦取消链接,该数据集将不再连接到当前数据管道。</p>
<p>一旦取消链接,该知识库将不再连接到当前数据管道。</p>
<p>正在解析的文件将继续解析,直到完成。</p>
<p>尚未解析的文件将不再被处理。</p> <br/>
<p>你确定要继续吗?</p> `,
@ -2217,8 +2222,8 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
noMCP: '暂无 MCP 服务器可用',
agentTitle: '尚未创建智能体',
notFoundAgent: '未查询到智能体',
datasetTitle: '尚未创建数据集',
notFoundDataset: '未查询到数据集',
datasetTitle: '尚未创建知识库',
notFoundDataset: '未查询到知识库',
chatTitle: '尚未创建聊天应用',
notFoundChat: '未查询到聊天应用',
searchTitle: '尚未创建搜索应用',

View File

@ -1,3 +1,4 @@
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { Button, ButtonLoading } from '@/components/ui/button';
import {
Dialog,
@ -9,12 +10,12 @@ import {
DialogTrigger,
} from '@/components/ui/dialog';
import { IFlow } from '@/interfaces/database/agent';
import { Operator } from '@/pages/agent/constant';
import useGraphStore from '@/pages/agent/store';
import { IKnowledge } from '@/interfaces/database/knowledge';
import { formatDate } from '@/utils/date';
import { BookPlus } from 'lucide-react';
import { useMemo, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useIsPipeline } from '../hooks/use-is-pipeline';
interface PublishConfirmDialogProps {
agentDetail: IFlow;
@ -22,6 +23,43 @@ interface PublishConfirmDialogProps {
onPublish: () => void;
}
function AssociatedDataset({
associatedDatasets,
}: {
associatedDatasets: Pick<IKnowledge, 'id' | 'name' | 'avatar'>[];
}) {
const { t } = useTranslation();
return (
<div className="space-y-2 pl-10 pt-3">
<div className="text-sm font-medium text-text-secondary">
{t('flow.linkedDataset')}
</div>
{associatedDatasets.length > 0 ? (
<div className="space-y-2 max-h-32 overflow-y-auto">
{associatedDatasets.map((dataset) => (
<div
key={dataset.id}
className="flex items-center gap-2 px-2 py-2 bg-bg-card rounded text-sm text-text-primary"
>
<RAGFlowAvatar
avatar={dataset.avatar}
name={dataset.name}
className="size-4 text-xs"
/>
<span className="truncate text-text-secondary">
{dataset.name}
</span>
</div>
))}
</div>
) : (
<div className="text-sm text-text-disabled">{t('common.noData')}</div>
)}
</div>
);
}
export function PublishConfirmDialog({
agentDetail,
loading,
@ -29,30 +67,28 @@ export function PublishConfirmDialog({
}: PublishConfirmDialogProps) {
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const nodes = useGraphStore((state) => state.nodes);
const linkedDatasets = useMemo(() => {
const datasets: string[] = [];
nodes.forEach((node) => {
if (node.data.label === Operator.Retrieval) {
const kbIds = node.data.form?.kb_ids || [];
datasets.push(...kbIds);
}
});
return [...new Set(datasets)];
}, [nodes]);
const isPipeline = useIsPipeline();
const lastPublished = useMemo(() => {
if (agentDetail?.last_publish_time) {
return formatDate(agentDetail.last_publish_time);
}
return '-';
return '';
}, [agentDetail?.update_time]);
const handleConfirmPublish = () => {
// Get datasets associated with this pipeline from API response
const associatedDatasets = useMemo(() => {
return agentDetail?.datasets || [];
}, [agentDetail?.datasets]);
const handleConfirmPublish = useCallback(() => {
onPublish();
setOpen(false);
};
}, []);
if (isPipeline) {
return null;
}
return (
<Dialog open={open} onOpenChange={setOpen}>
@ -66,29 +102,39 @@ export function PublishConfirmDialog({
<DialogTitle>{t('flow.confirmPublish')}</DialogTitle>
</DialogHeader>
<DialogDescription>
<div className="space-y-4">
<div className="flex flex-col gap-1">
<span className="text-sm font-medium text-text-primary">
{agentDetail.title}
</span>
<div className="space-y-3">
<div className="text-sm text-text-secondary">
{t(
`flow.${isPipeline ? 'publishIngestionPipeline' : 'publishAgent'}`,
)}
</div>
<section className="bg-bg-input px-2.5 py-4 rounded border border-border-default">
<div className="flex gap-2.5 items-center">
<RAGFlowAvatar
avatar={agentDetail.avatar}
name={agentDetail.title}
className="size-8"
/>
<span className="text-text-primary text-lg">
{agentDetail.title}
</span>
</div>
{isPipeline && (
<AssociatedDataset
associatedDatasets={associatedDatasets}
></AssociatedDataset>
)}
</section>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between text-sm">
<span className="text-text-secondary">
{t('flow.linkedDataset')}
</span>
<span className="text-text-primary">
{linkedDatasets.length > 0
? linkedDatasets.join(', ')
: t('common.none')}
</span>
</div>
<div className="flex items-center justify-between text-sm">
<span className="text-text-secondary">
{t('flow.lastPublished')}
</span>
<span className="text-text-primary">{lastPublished}</span>
</div>
{lastPublished && (
<div className="flex items-center text-sm text-text-secondary gap-2">
<span>{t('flow.lastPublished')}:</span>
<span>{lastPublished}</span>
</div>
)}
</div>
</div>
</DialogDescription>

View File

@ -44,6 +44,7 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) {
</Button>
)
}
showReleaseTime
/>
);
}