refactor(ui): adjust global navigation bar style (#13419)

### What problem does this PR solve?

Renovate global navigation bar, align styles to the design.
(May causes minor layout issues in sub-pages, will check and fix soon)

### Type of change

- [x] Refactoring
This commit is contained in:
Jimmy Ben Klieve
2026-03-05 20:47:29 +08:00
committed by GitHub
parent 9e0e128ce5
commit ef4cbe72a3
45 changed files with 1334 additions and 1250 deletions

View File

@@ -2,10 +2,17 @@ import FileStatusBadge from '@/components/file-status-badge';
import { FilterCollection } from '@/components/list-filter-bar/interface';
import SvgIcon from '@/components/svg-icon';
import { useIsDarkTheme } from '@/components/theme-provider';
import { AntToolTip } from '@/components/ui/tooltip';
import {
Card,
CardDescription,
CardFooter,
CardHeader,
} from '@/components/ui/card';
import WhatIsThis from '@/components/what-is-this';
import { RunningStatusMap } from '@/constants/knowledge';
import { useFetchDocumentList } from '@/hooks/use-document-request';
import { CircleQuestionMark } from 'lucide-react';
import { FC, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { RunningStatus } from '../dataset/constant';
@@ -37,23 +44,35 @@ const StatCard: FC<StatCardProps> = ({
tooltip,
}) => {
return (
<div className="bg-bg-card p-4 rounded-lg border border-border flex flex-col gap-2">
<div className="flex items-center justify-between">
<h3 className="flex items-center gap-1 text-sm font-medium text-text-secondary">
{title}
{tooltip && (
<AntToolTip title={tooltip} trigger="hover">
<CircleQuestionMark size={12} />
</AntToolTip>
)}
</h3>
{icon}
<Card
className="px-5 py-2.5 rounded-lg border-border-default grid grid-cols-[1fr_auto] grid-rows-[1fr_auto]"
style={{
gridTemplateAreas: '"data icon" "footer footer"',
}}
>
<span style={{ gridArea: 'icon' }}>{icon}</span>
<div style={{ gridArea: 'data' }}>
<CardHeader className="p-0">
<h3 className="flex items-center gap-1 text-sm font-medium text-text-secondary">
{title}
{tooltip && <WhatIsThis>{tooltip}</WhatIsThis>}
</h3>
</CardHeader>
<CardDescription className="text-text-primary text-2xl font-medium leading-9">
<data value={value}>{value}</data>
</CardDescription>
</div>
<div className="text-2xl font-bold text-text-primary">{value}</div>
<div className="h-12 w-full flex items-center">
<CardFooter
className="p-0 mt-1.5 h-8 w-full flex items-end"
style={{ gridArea: 'footer' }}
>
<div className="flex-1">{children}</div>
</div>
</div>
</CardFooter>
</Card>
);
};
@@ -64,38 +83,34 @@ const CardFooterProcess: FC<CardFooterProcessProps> = ({
failedTip,
}) => {
const { t } = useTranslation();
return (
<div className="flex items-center flex-col gap-2">
<div className="w-full flex justify-between gap-4 rounded-lg text-sm font-bold text-text-primary">
<div className="flex items-center justify-between rounded-md w-1/2 p-2 bg-state-success-5">
<div className="flex items-center rounded-lg gap-1">
<div className="w-2 h-2 rounded-full bg-state-success "></div>
<dl className="w-full flex justify-between gap-4 rounded-lg text-sm font-bold text-text-primary">
<div className="flex items-center justify-between rounded-sm w-1/2 p-2 bg-state-success-5">
<dt className="flex items-center rounded-lg gap-1">
<div className="w-1 h-1 rounded-full bg-state-success"></div>
<div className="font-normal text-text-secondary text-xs flex items-center gap-1">
{t('knowledgeDetails.success')}
{successTip && (
<AntToolTip title={successTip} trigger="hover">
<CircleQuestionMark size={12} />
</AntToolTip>
)}
{successTip && <WhatIsThis>{successTip}</WhatIsThis>}
</div>
</div>
<div>{success || 0}</div>
</dt>
<dd className="font-normal">{success || 0}</dd>
</div>
<div className="flex items-center justify-between rounded-md w-1/2 bg-state-error-5 p-2">
<div className="flex items-center rounded-lg gap-1">
<div className="w-2 h-2 rounded-full bg-state-error"></div>
<div className="flex items-center justify-between rounded-sm w-1/2 bg-state-error-5 p-2">
<dt className="flex items-center rounded-lg gap-1">
<div className="w-1 h-1 rounded-full bg-state-error"></div>
<div className="font-normal text-text-secondary text-xs flex items-center gap-1">
{t('knowledgeDetails.failed')}
{failedTip && (
<AntToolTip title={failedTip} trigger="hover">
<CircleQuestionMark size={12} />
</AntToolTip>
)}
{failedTip && <WhatIsThis>{failedTip}</WhatIsThis>}
</div>
</div>
<div>{failed || 0}</div>
</dt>
<dd className="font-normal">{failed || 0}</dd>
</div>
</div>
</dl>
</div>
);
};
@@ -247,9 +262,13 @@ const FileLogsPage: FC = () => {
const isDark = useIsDarkTheme();
return (
<div className="p-5 min-w-[880px] border-border border rounded-lg mr-5">
<Card
className="
p-5 min-w-[880px] mr-5 mb-5 bg-transparent shadow-none
flex flex-col overflow-y-auto scrollbar-auto"
>
{/* Stats Cards */}
<div className="grid grid-cols-3 md:grid-cols-3 gap-4 mb-6">
<div className="grid grid-cols-3 md:grid-cols-3 gap-7 mb-6">
<StatCard
title={t('datasetOverview.totalFiles')}
value={topAllData.totalFiles.value}
@@ -261,12 +280,12 @@ const FileLogsPage: FC = () => {
)
}
>
<div>
<div className="text-xs">
<span className="text-accent-primary">
{topAllData.totalFiles.precent > 0 ? '+' : ''}
{topAllData.totalFiles.precent}%{' '}
</span>
<span className="font-normal text-text-secondary text-xs">
<span className="font-normal text-text-secondary">
{t('knowledgeConfiguration.lastWeek')}
</span>
</div>
@@ -330,7 +349,7 @@ const FileLogsPage: FC = () => {
pageCount={10}
active={active}
/>
</div>
</Card>
);
};

View File

@@ -414,8 +414,8 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
});
return (
<div className="w-full h-[calc(100vh-360px)]">
<Table rootClassName="max-h-[calc(100vh-380px)]">
<div className="size-full flex flex-col">
<Table rootClassName="max-h-full mb-4">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
@@ -460,15 +460,15 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
)}
</TableBody>
</Table>
<div className="flex items-center justify-end absolute bottom-3 right-12">
<div className="space-x-2">
<RAGFlowPagination
{...{ current: pagination.current, pageSize: pagination.pageSize }}
total={pagination.total}
onChange={(page, pageSize) => setPagination({ page, pageSize })}
/>
</div>
<div className="mt-auto flex items-center justify-end">
<RAGFlowPagination
{...{ current: pagination.current, pageSize: pagination.pageSize }}
total={pagination.total}
onChange={(page, pageSize) => setPagination({ page, pageSize })}
/>
</div>
{isModalVisible && (
<ProcessLogModal
title={active === LogTabs.FILE_LOGS ? t('fileLogs') : t('datasetLog')}

View File

@@ -2,6 +2,13 @@ import { DataFlowSelect } from '@/components/data-pipeline-select';
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import Divider from '@/components/ui/divider';
import { Form } from '@/components/ui/form';
import { FormLayout } from '@/constants/form';
@@ -15,7 +22,6 @@ import { createContext, useEffect, useState } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { TopTitle } from '../dataset-title';
import {
GenerateType,
IGenerateLogButtonProps,
@@ -258,98 +264,113 @@ export default function DatasetSettings() {
};
return (
<section className="p-5 h-full flex flex-col">
<TopTitle
title={t('knowledgeDetails.configuration')}
description={t('knowledgeConfiguration.titleDescription')}
></TopTitle>
<div className="flex gap-14 flex-1 min-h-0">
<DataSetContext.Provider
value={{
loading: datasetSettingLoading,
knowledgeDetails: knowledgeDetails,
}}
>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 ">
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
<MainContainer className="text-text-secondary">
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.baseInfo')}
</div>
<GeneralForm></GeneralForm>
<div className="pr-5 pb-5">
<Card className="p-0 h-full flex flex-col bg-transparent shadow-none">
<CardHeader className="p-5 border-b-0.5 border-border-button">
<header>
<CardTitle as="h1">{t('knowledgeDetails.configuration')}</CardTitle>
<Divider />
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.dataPipeline')}
</div>
<ParseTypeItem line={1} />
{parseType === 1 && (
<ChunkMethodItem line={1}></ChunkMethodItem>
)}
{parseType === 2 && (
<DataFlowSelect
isMult={false}
showToDataPipeline={true}
formFieldName="pipeline_id"
layout={FormLayout.Horizontal}
/>
)}
<CardDescription>
{t('knowledgeConfiguration.titleDescription')}
</CardDescription>
{/* <Divider /> */}
{parseType === 1 && <ChunkMethodForm />}
{/* <Button>Save as Preset</Button> */}
</header>
</CardHeader>
{/* <LinkDataPipeline
data={pipelineData}
handleLinkOrEditSubmit={handleLinkOrEditSubmit}
/> */}
<Divider />
<LinkDataSource
data={sourceData}
<CardContent className="p-0 flex-1 h-0 flex divide-x-0.5">
<DataSetContext.Provider
value={{
loading: datasetSettingLoading,
knowledgeDetails: knowledgeDetails,
}}
>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col"
>
<div className="flex-1 h-0 w-[768px] px-5 pt-5 overflow-y-auto scrollbar-auto">
<MainContainer className="text-text-secondary">
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.baseInfo')}
</div>
<GeneralForm></GeneralForm>
<Divider />
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.dataPipeline')}
</div>
<ParseTypeItem line={1} />
{parseType === 1 && (
<ChunkMethodItem line={1}></ChunkMethodItem>
)}
{parseType === 2 && (
<DataFlowSelect
isMult={false}
showToDataPipeline={true}
formFieldName="pipeline_id"
layout={FormLayout.Horizontal}
/>
)}
{/* <Divider /> */}
{parseType === 1 && <ChunkMethodForm />}
{/* <LinkDataPipeline
data={pipelineData}
handleLinkOrEditSubmit={handleLinkOrEditSubmit}
unbindFunc={unbindFunc}
handleAutoParse={handleAutoParse}
/>
<Divider />
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.globalIndex')}
</div>
<GraphRagItems
className="border-none p-0"
data={graphRagGenerateData as IGenerateLogButtonProps}
onDelete={() =>
handleDeletePipelineTask(GenerateType.KnowledgeGraph)
}
></GraphRagItems>
<Divider />
<RaptorFormFields
data={raptorGenerateData as IGenerateLogButtonProps}
onDelete={() =>
handleDeletePipelineTask(GenerateType.Raptor)
}
></RaptorFormFields>
</MainContainer>
</div>
<div className="text-right items-center flex justify-end gap-3 w-[768px]">
<Button
type="reset"
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
data-testid="ds-settings-page-cancel-btn"
onClick={() => {
form.reset();
}}
>
{t('knowledgeConfiguration.cancel')}
</Button>
<SavingButton></SavingButton>
</div>
</form>
</Form>
<div className="flex-1">
/> */}
<Divider />
<LinkDataSource
data={sourceData}
handleLinkOrEditSubmit={handleLinkOrEditSubmit}
unbindFunc={unbindFunc}
handleAutoParse={handleAutoParse}
/>
<Divider />
<div className="text-base font-medium text-text-primary">
{t('knowledgeConfiguration.globalIndex')}
</div>
<GraphRagItems
className="border-none p-0"
data={graphRagGenerateData as IGenerateLogButtonProps}
onDelete={() =>
handleDeletePipelineTask(GenerateType.KnowledgeGraph)
}
></GraphRagItems>
<Divider />
<RaptorFormFields
data={raptorGenerateData as IGenerateLogButtonProps}
onDelete={() =>
handleDeletePipelineTask(GenerateType.Raptor)
}
></RaptorFormFields>
</MainContainer>
</div>
<div className="p-5 text-right items-center flex justify-end gap-3 w-[768px]">
<Button
type="reset"
variant="transparent"
onClick={() => {
form.reset();
}}
>
{t('knowledgeConfiguration.cancel')}
</Button>
<SavingButton />
</div>
</form>
</Form>
</DataSetContext.Provider>
<div className="flex-1 p-5 overflow-auto">
{parseType === 1 && <ChunkMethodLearnMore parserId={selectedTag} />}
</div>
</DataSetContext.Provider>
</div>
</section>
</CardContent>
</Card>
</div>
);
}

View File

@@ -7,6 +7,11 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Modal } from '@/components/ui/modal/modal';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';
import { cn } from '@/lib/utils';
import { toFixed } from '@/utils/common-util';
import { formatDate } from '@/utils/date';
@@ -25,6 +30,7 @@ import {
useTraceGenerate,
useUnBindTask,
} from './hook';
export enum GenerateType {
KnowledgeGraph = 'KnowledgeGraph',
Raptor = 'Raptor',
@@ -191,45 +197,46 @@ const Generate: React.FC<GenerateProps> = (props) => {
};
return (
<div className="generate">
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
<DropdownMenuTrigger asChild disabled={disabled}>
<div className={cn({ 'cursor-not-allowed': disabled })}>
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
<DropdownMenuTrigger asChild disabled={disabled}>
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={disabled}
variant={'transparent'}
className={cn(disabled && '!cursor-not-allowed')}
variant="transparent"
size="icon"
onClick={() => {
if (!disabled) {
handleOpenChange(!open);
}
}}
>
<WandSparkles className="mr-2 size-4" />
{t('knowledgeDetails.generate')}
<WandSparkles />
</Button>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[380px] p-5 flex flex-col gap-2 ">
{Object.values(GenerateType).map((name) => {
const data = (
name === GenerateType.KnowledgeGraph
? graphRunData
: raptorRunData
) as ITraceInfo;
return (
<div key={name}>
<MenuItem
name={name}
runGenerate={runGenerate}
data={data}
pauseGenerate={pauseGenerate}
/>
</div>
);
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
</TooltipTrigger>
<TooltipContent>{t('knowledgeDetails.generate')}</TooltipContent>
</Tooltip>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[380px] p-5 flex flex-col gap-2 ">
{Object.values(GenerateType).map((name) => {
const data = (
name === GenerateType.KnowledgeGraph ? graphRunData : raptorRunData
) as ITraceInfo;
return (
<div key={name}>
<MenuItem
name={name}
runGenerate={runGenerate}
data={data}
pauseGenerate={pauseGenerate}
/>
</div>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
};

View File

@@ -137,9 +137,6 @@ export default function Dataset() {
return (
<>
<div className="absolute top-4 right-5">
<Generate disabled={!(dataSetData.chunk_num > 0)} />
</div>
<section className="p-5 min-w-[880px]">
<ListFilterBar
title={t('header.dataset')}
@@ -158,6 +155,7 @@ export default function Dataset() {
</div>
</div>
}
preChildren={<Generate disabled={!(dataSetData.chunk_num > 0)} />}
// preChildren={
// <Button
// variant={'ghost'}

View File

@@ -1,51 +1,19 @@
import { PageHeader } from '@/components/page-header';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import { KnowledgeBaseProvider } from '@/pages/dataset/contexts/knowledge-base-context';
import { useTranslation } from 'react-i18next';
import { Outlet } from 'react-router';
import { SideBar } from './sidebar';
export default function DatasetWrapper() {
const { navigateToDatasetList } = useNavigatePage();
const { t } = useTranslation();
const { data, loading } = useFetchKnowledgeBaseConfiguration();
return (
<KnowledgeBaseProvider knowledgeBase={data} loading={loading}>
<section className="flex h-full flex-col w-full">
<PageHeader>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink onClick={navigateToDatasetList}>
{t('knowledgeDetails.dataset')}
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage className="w-28 whitespace-nowrap text-ellipsis overflow-hidden">
{data.name}
</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</PageHeader>
<div className="flex flex-1 min-h-0">
<SideBar></SideBar>
<div className="flex-1 overflow-auto">
<Outlet />
</div>
</div>
</section>
<article className="pt-3 size-full grid grid-cols-[auto_1fr] grid-rows-1">
<SideBar />
<Outlet />
</article>
</KnowledgeBaseProvider>
);
}

View File

@@ -1,3 +1,15 @@
import { isEmpty } from 'lodash';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
LucideFolderOpen,
LucideLogs,
LucideSettings,
LucideTextSearch,
} from 'lucide-react';
import { IconFontFill } from '@/components/icon-font';
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { Button } from '@/components/ui/button';
@@ -9,11 +21,8 @@ import {
import { cn, formatBytes } from '@/lib/utils';
import { Routes } from '@/routes';
import { formatPureDate } from '@/utils/date';
import { isEmpty } from 'lodash';
import { Banknote, FileSearch2, FolderOpen, Logs } from 'lucide-react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useHandleMenuClick } from './hooks';
import { useParams } from 'react-router';
type PropType = {
refreshCount?: number;
@@ -21,7 +30,7 @@ type PropType = {
export function SideBar({ refreshCount }: PropType) {
const pathName = useSecondPathName();
const { handleMenuClick } = useHandleMenuClick();
const { id } = useParams();
// refreshCount: be for avatar img sync update on top left
const { data } = useFetchKnowledgeBaseConfiguration({ refreshCount });
const { data: routerData } = useFetchKnowledgeGraph();
@@ -30,75 +39,91 @@ export function SideBar({ refreshCount }: PropType) {
const items = useMemo(() => {
const list = [
{
icon: <FolderOpen className="size-4" />,
icon: <LucideFolderOpen className="size-[1em]" />,
label: t(`knowledgeDetails.subbarFiles`),
key: Routes.DatasetBase,
},
{
icon: <FileSearch2 className="size-4" />,
icon: <LucideTextSearch className="size-[1em]" />,
label: t(`knowledgeDetails.testing`),
key: Routes.DatasetTesting,
},
{
icon: <Logs className="size-4" />,
icon: <LucideLogs className="size-[1em]" />,
label: t(`knowledgeDetails.overview`),
key: Routes.DataSetOverview,
},
{
icon: <Banknote className="size-4" />,
icon: <LucideSettings className="size-[1em]" />,
label: t(`knowledgeDetails.configuration`),
key: Routes.DataSetSetting,
},
];
if (!isEmpty(routerData?.graph)) {
list.push({
icon: <IconFontFill name="knowledgegraph" className="size-4" />,
icon: <IconFontFill name="knowledgegraph" className="size-[1em]" />,
label: t(`knowledgeDetails.knowledgeGraph`),
key: Routes.KnowledgeGraph,
});
}
return list;
}, [t, routerData]);
return (
<aside className="relative p-5 space-y-8">
<div className="flex gap-2.5 max-w-[200px] items-center">
<aside className="w-64 relative px-5 space-y-8">
<header
className="grid grid-cols-[auto_1fr] grid-rows-[auto_auto] gap-x-3"
style={{
gridTemplateAreas: '"avatar title" "avatar stats"',
}}
>
<RAGFlowAvatar
avatar={data.avatar}
name={data.name}
className="size-16"
></RAGFlowAvatar>
<div className=" text-text-secondary text-xs space-y-1 overflow-hidden">
<h3 className="text-lg font-semibold line-clamp-1 text-text-primary text-ellipsis overflow-hidden">
{data.name}
</h3>
style={{ gridArea: 'avatar' }}
/>
<h3
className="text-lg font-semibold line-clamp-1 text-text-primary text-ellipsis overflow-hidden"
style={{ gridArea: 'title' }}
>
{data.name}
</h3>
<div
className="self-end text-text-secondary text-xs overflow-hidden"
style={{ gridArea: 'stats' }}
>
<div className="flex justify-between">
<span>
{data.doc_num} {t('knowledgeDetails.files')}
</span>
<span>{formatBytes(data.size)}</span>
</div>
<div>
<div className="mt-0.5">
{t('knowledgeDetails.created')} {formatPureDate(data.create_time)}
</div>
</div>
</div>
</header>
<div className="w-[200px] flex flex-col gap-5">
<div className="flex flex-col gap-5">
{items.map((item, itemIdx) => {
const active = '/' + pathName === item.key;
return (
<Button
key={itemIdx}
variant={active ? 'secondary' : 'ghost'}
asLink
variant="ghost"
className={cn(
'w-full justify-start gap-2.5 px-3 relative h-10 text-text-secondary',
{
'bg-bg-card': active,
'text-text-primary': active,
},
'w-full justify-start gap-2.5 px-3 relative h-10 text-base',
active && 'bg-bg-card text-text-primary',
)}
onClick={handleMenuClick(item.key)}
to={`${Routes.DatasetBase}${item.key}/${id}`}
>
{item.icon}
<span>{item.label}</span>

View File

@@ -1,7 +1,13 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { useTestRetrieval } from '@/hooks/use-knowledge-request';
import { t } from 'i18next';
import { useState } from 'react';
import { TopTitle } from '../dataset-title';
import TestingForm from './testing-form';
import { TestingResult } from './testing-result';
@@ -21,79 +27,92 @@ export default function RetrievalTesting() {
const [count] = useState(1);
return (
<div className="p-5">
<section className="flex justify-between items-center">
<TopTitle
title={t('knowledgeDetails.retrievalTesting')}
description={t('knowledgeDetails.testingDescription')}
></TopTitle>
{/* <Button>Save as Preset</Button> */}
</section>
{count === 1 ? (
<section className="flex divide-x h-full">
<div className="p-4 flex-1">
<div className="flex justify-between pb-2.5">
<span className="text-text-primary font-semibold text-2xl">
{t('knowledgeDetails.testSetting')}
</span>
{/* <Button variant={'outline'} onClick={addCount}>
<Plus /> Add New Test
</Button> */}
<div className="pr-5 pb-5">
<Card className="size-full bg-transparent shadow-none flex flex-col">
<CardHeader className="p-5 border-b-0.5 border-border-button">
<header>
<CardTitle as="h1">
{t('knowledgeDetails.retrievalTesting')}
</CardTitle>
<CardDescription>
{t('knowledgeDetails.testingDescription')}
</CardDescription>
{/* <Button>Save as Preset</Button> */}
</header>
</CardHeader>
{count === 1 ? (
<CardContent className="flex-1 overflow-hidden p-0 grid grid-rows-1 grid-cols-2 divide-x-0.5">
<article className="size-full flex-1 flex flex-col">
<header className="px-5 py-3">
<h2 className="font-semibold text-base leading-8">
{t('knowledgeDetails.testSetting')}
</h2>
{/* <Button variant={'outline'} onClick={addCount}>
<Plus /> Add New Test
</Button> */}
</header>
<div className="flex-1 h-0">
<TestingForm
loading={loading}
setValues={setValues}
refetch={refetch}
/>
</div>
</article>
<div className="flex-1">
<TestingResult
data={data}
page={page}
loading={loading}
pageSize={pageSize}
filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit}
onPaginationChange={onPaginationChange}
/>
</div>
<div className="h-[calc(100vh-241px)] overflow-auto scrollbar-thin px-1">
</CardContent>
) : (
<CardContent className="p-0 flex gap-2">
<div className="flex-1">
<TestingForm
loading={loading}
setValues={setValues}
refetch={refetch}
></TestingForm>
<TestingResult
data={data}
page={page}
loading={loading}
pageSize={pageSize}
filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit}
onPaginationChange={onPaginationChange}
></TestingResult>
</div>
</div>
<TestingResult
data={data}
page={page}
loading={loading}
pageSize={pageSize}
filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit}
onPaginationChange={onPaginationChange}
></TestingResult>
</section>
) : (
<section className="flex gap-2">
<div className="flex-1">
<TestingForm
loading={loading}
setValues={setValues}
refetch={refetch}
></TestingForm>
<TestingResult
data={data}
page={page}
loading={loading}
pageSize={pageSize}
filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit}
onPaginationChange={onPaginationChange}
></TestingResult>
</div>
<div className="flex-1">
<TestingForm
loading={loading}
setValues={setValues}
refetch={refetch}
></TestingForm>
<TestingResult
data={data}
page={page}
loading={loading}
pageSize={pageSize}
filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit}
onPaginationChange={onPaginationChange}
></TestingResult>
</div>
</section>
)}
<div className="flex-1">
<TestingForm
loading={loading}
setValues={setValues}
refetch={refetch}
></TestingForm>
<TestingResult
data={data}
page={page}
loading={loading}
pageSize={pageSize}
filterValue={filterValue}
handleFilterSubmit={handleFilterSubmit}
onPaginationChange={onPaginationChange}
></TestingResult>
</div>
</CardContent>
)}
</Card>
</div>
);
}

View File

@@ -90,43 +90,52 @@ export default function TestingForm({
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormContainer className="p-10">
<SimilaritySliderFormField
isTooltipShown={true}
></SimilaritySliderFormField>
<RerankFormFields></RerankFormFields>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>
<CrossLanguageFormField
name={'cross_languages'}
></CrossLanguageFormField>
<MetadataFilter prefix=""></MetadataFilter>
</FormContainer>
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
{/* <FormLabel>{t('knowledgeDetails.testText')}</FormLabel> */}
<FormControl>
<Textarea {...field}></Textarea>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end">
<ButtonLoading
type="submit"
disabled={!!!trim(question)}
loading={loading}
>
{/* {!loading && <CirclePlay />} */}
{t('knowledgeDetails.testingLabel')}
<Send />
</ButtonLoading>
<form
className="size-full flex flex-col"
onSubmit={form.handleSubmit(onSubmit)}
>
<div className="px-5 h-0 flex-1">
<FormContainer className="p-5 h-full overflow-auto">
<SimilaritySliderFormField
isTooltipShown={true}
></SimilaritySliderFormField>
<RerankFormFields></RerankFormFields>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>
<CrossLanguageFormField
name={'cross_languages'}
></CrossLanguageFormField>
<MetadataFilter prefix=""></MetadataFilter>
</FormContainer>
</div>
<footer className="flex-0 p-5">
<FormField
control={form.control}
name="question"
render={({ field }) => (
<FormItem>
{/* <FormLabel>{t('knowledgeDetails.testText')}</FormLabel> */}
<FormControl>
<Textarea {...field}></Textarea>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="mt-2.5 text-end">
<ButtonLoading
type="submit"
disabled={!!!trim(question)}
loading={loading}
>
{/* {!loading && <CirclePlay />} */}
{t('knowledgeDetails.testingLabel')}
<Send />
</ButtonLoading>
</div>
</footer>
</form>
</Form>
);

View File

@@ -1,9 +1,9 @@
import { EmptyType } from '@/components/empty/constant';
import Empty from '@/components/empty/empty';
import { FormContainer } from '@/components/form-container';
import { FilterButton } from '@/components/list-filter-bar';
import { FilterPopover } from '@/components/list-filter-bar/filter-popover';
import { FilterCollection } from '@/components/list-filter-bar/interface';
import { Card } from '@/components/ui/card';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useTranslate } from '@/hooks/common-hooks';
import { useTestRetrieval } from '@/hooks/use-knowledge-request';
@@ -21,12 +21,12 @@ const similarityList: Array<{ field: keyof ITestingChunk; label: string }> = [
const ChunkTitle = ({ item }: { item: ITestingChunk }) => {
const { t } = useTranslate('knowledgeDetails');
return (
<div className="flex gap-3 text-xs text-text-sub-title-invert italic">
<div className="text-xs text-text-sub-title-invert italic space-x-4 rtl:space-x-reverse">
{similarityList.map((x) => (
<div key={x.field} className="space-x-1">
<span>{((item[x.field] as number) * 100).toFixed(2)}</span>
<span>{t(camelCase(x.field))}</span>
</div>
<p key={x.field} className="inline">
{((item[x.field] as number) * 100).toFixed(2)}{' '}
<dfn>{t(camelCase(x.field))}</dfn>
</p>
))}
</div>
);
@@ -68,11 +68,12 @@ export function TestingResult({
}, [data.doc_aggs]);
return (
<div className="p-4 flex-1">
<div className="flex justify-between pb-2.5">
<span className="text-text-primary font-semibold text-2xl">
<article className="size-full flex flex-col">
<header className="flex-0 px-5 py-3 flex justify-between">
<h2 className="font-semibold text-base leading-8">
{t('knowledgeDetails.testResults')}
</span>
</h2>
<FilterPopover
filters={filters}
onChange={handleFilterSubmit}
@@ -80,43 +81,48 @@ export function TestingResult({
>
<FilterButton></FilterButton>
</FilterPopover>
</div>
{data.chunks?.length > 0 && !loading && (
<>
<section className="flex flex-col gap-5 overflow-auto h-[calc(100vh-241px)] scrollbar-thin mb-5">
{data.chunks?.map((x) => (
<FormContainer key={x.chunk_id} className="px-5 py-2.5">
<ChunkTitle item={x}></ChunkTitle>
<p className="!mt-2.5"> {x.content_with_weight}</p>
</FormContainer>
))}
</section>
<RAGFlowPagination
total={data.total}
onChange={onPaginationChange}
current={page}
pageSize={pageSize}
></RAGFlowPagination>
</>
)}
{!data.chunks?.length && !loading && (
<div className="flex justify-center items-center w-full h-[calc(100vh-280px)]">
<div>
<Empty type={EmptyType.SearchData} iconWidth={80}>
{data.isRuned && (
<div className="text-text-secondary">
{t('knowledgeDetails.noTestResultsForRuned')}
</header>
<div className="flex-1 h-0">
{data.chunks?.length > 0 && !loading && (
<>
<section className="px-5 pb-5 flex flex-col gap-5 overflow-auto h-full scrollbar-thin">
{data.chunks?.map((x) => (
<article>
<Card
key={x.chunk_id}
className="px-5 py-2.5 bg-transparent shadow-none"
>
<ChunkTitle item={x}></ChunkTitle>
<p className="!mt-2.5"> {x.content_with_weight}</p>
</Card>
</article>
))}
</section>
<RAGFlowPagination
total={data.total}
onChange={onPaginationChange}
current={page}
pageSize={pageSize}
></RAGFlowPagination>
</>
)}
{!data.chunks?.length && !loading && (
<div className="size-full p-5 flex justify-center items-center">
<div>
<Empty type={EmptyType.SearchData} iconWidth={80}>
<div className="text-text-secondary text-sm">
{t(
data.isRuned
? 'knowledgeDetails.noTestResultsForRuned'
: 'knowledgeDetails.noTestResultsForNotRuned',
)}
</div>
)}
{!data.isRuned && (
<div className="text-text-secondary">
{t('knowledgeDetails.noTestResultsForNotRuned')}
</div>
)}
</Empty>
</Empty>
</div>
</div>
</div>
)}
</div>
)}
</div>
</article>
);
}