mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-17 13:17:20 +08:00
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:
@@ -1,12 +1,3 @@
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchAgentTemplates, useSetAgent } from '@/hooks/use-agent-request';
|
||||
@@ -15,14 +6,11 @@ import { CardContainer } from '@/components/card-container';
|
||||
import { AgentCategory } from '@/constants/agent';
|
||||
import { IFlowTemplate } from '@/interfaces/database/agent';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CreateAgentDialog } from './create-agent-dialog';
|
||||
import { TemplateCard } from './template-card';
|
||||
import { MenuItemKey, SideBar } from './template-sidebar';
|
||||
|
||||
export default function AgentTemplates() {
|
||||
const { navigateToAgents } = useNavigatePage();
|
||||
const { t } = useTranslation();
|
||||
const list = useFetchAgentTemplates();
|
||||
const { loading, setAgent } = useSetAgent();
|
||||
const [templateList, setTemplateList] = useState<IFlowTemplate[]>([]);
|
||||
@@ -99,21 +87,6 @@ export default function AgentTemplates() {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToAgents}>
|
||||
{t('flow.agent')}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{t('flow.createGraph')}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<div className="flex flex-1 h-dvh">
|
||||
<SideBar
|
||||
change={handleSiderBarChange}
|
||||
|
||||
@@ -23,14 +23,7 @@ import DocumentPreview from '@/components/document-preview';
|
||||
import DocumentHeader from '@/components/document-preview/document-header';
|
||||
import { useGetDocumentUrl } from '@/components/document-preview/hooks';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import message from '@/components/ui/message';
|
||||
import {
|
||||
RAGFlowPagination,
|
||||
@@ -42,6 +35,7 @@ import {
|
||||
useNavigatePage,
|
||||
} from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||
import { LucideArrowBigLeft } from 'lucide-react';
|
||||
import styles from './index.module.less';
|
||||
|
||||
const Chunk = () => {
|
||||
@@ -182,29 +176,15 @@ const Chunk = () => {
|
||||
return (
|
||||
<>
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToDatasetList}>
|
||||
{t('knowledgeDetails.dataset')}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
onClick={navigateToDataFile(
|
||||
getQueryString(QueryStringMap.id) as string,
|
||||
)}
|
||||
>
|
||||
{dataset.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{documentInfo?.name}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={navigateToDataFile(
|
||||
getQueryString(QueryStringMap.id) as string,
|
||||
)}
|
||||
>
|
||||
<LucideArrowBigLeft />
|
||||
{t('common.back')}
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<div className={styles.chunkPage}>
|
||||
<div className="flex flex-1 gap-8">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ export default function Datasets() {
|
||||
searchUrl.delete('isCreate');
|
||||
setSearchUrl(searchUrl);
|
||||
}
|
||||
}, [isCreate, showModal, searchUrl, setSearchUrl]);
|
||||
}, [isCreate, showModal, searchUrl, setSearchUrl, queryClient]);
|
||||
return (
|
||||
<>
|
||||
<section className="py-4 flex-1 flex flex-col">
|
||||
<section className="py-4 pt-8 flex-1 flex flex-col">
|
||||
{(!kbs?.length || kbs?.length <= 0) && !searchString && (
|
||||
<div className="flex w-full items-center justify-center h-[calc(100vh-164px)]">
|
||||
<EmptyAppCard
|
||||
|
||||
@@ -42,11 +42,11 @@ export function Banner() {
|
||||
export function NextBanner() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<section className="text-5xl pt-10 pb-14 font-bold px-10">
|
||||
<span className="text-text-primary">{t('header.welcome')}</span>
|
||||
<span className="pl-3 text-transparent bg-clip-text bg-gradient-to-l from-[#40EBE3] to-[#4A51FF]">
|
||||
<h1 className="text-5xl font-bold leading-normal">
|
||||
<span className="text-text-primary">{t('header.welcome')} </span>
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-l from-[#40EBE3] to-[#4A51FF]">
|
||||
RAGFlow
|
||||
</span>
|
||||
</section>
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { PageContainer } from '@/layouts/page-container';
|
||||
import { Applications } from './applications';
|
||||
import { NextBanner } from './banner';
|
||||
import { Datasets } from './datasets';
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<section>
|
||||
<NextBanner></NextBanner>
|
||||
<section className="h-[calc(100dvh-260px)] overflow-auto px-10">
|
||||
<Datasets></Datasets>
|
||||
<Applications></Applications>
|
||||
</section>
|
||||
</section>
|
||||
<PageContainer>
|
||||
<header className="mb-8">
|
||||
<NextBanner />
|
||||
</header>
|
||||
|
||||
<Datasets />
|
||||
<Applications />
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -71,43 +71,24 @@ function LoginFormContent({
|
||||
</div>
|
||||
<div className=" w-full max-w-[540px] bg-bg-component backdrop-blur-sm rounded-2xl shadow-xl pt-14 pl-10 pr-10 pb-2 border border-border-button ">
|
||||
{!disablePasswordLogin && (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="flex flex-col gap-8 text-text-primary "
|
||||
data-testid="auth-form"
|
||||
data-active={isActiveFace ? 'true' : undefined}
|
||||
onSubmit={form.handleSubmit(onCheck)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel required>{t('emailLabel')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
data-testid="auth-email"
|
||||
placeholder={t('emailPlaceholder')}
|
||||
autoComplete="email"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{title === 'register' && (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="flex flex-col gap-8 text-text-primary "
|
||||
data-testid="auth-form"
|
||||
data-active={isActiveFace ? 'true' : undefined}
|
||||
onSubmit={form.handleSubmit(onCheck)}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="nickname"
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel required>{t('nicknameLabel')}</FormLabel>
|
||||
<FormLabel required>{t('emailLabel')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
data-testid="auth-nickname"
|
||||
placeholder={t('nicknamePlaceholder')}
|
||||
autoComplete="username"
|
||||
data-testid="auth-email"
|
||||
placeholder={t('emailPlaceholder')}
|
||||
autoComplete="email"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -115,73 +96,92 @@ function LoginFormContent({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel required>{t('passwordLabel')}</FormLabel>
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
data-testid="auth-password"
|
||||
type={'password'}
|
||||
placeholder={t('passwordPlaceholder')}
|
||||
autoComplete={
|
||||
title === 'login'
|
||||
? 'current-password'
|
||||
: 'new-password'
|
||||
}
|
||||
{...field}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
{title === 'register' && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="nickname"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel required>{t('nicknameLabel')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
data-testid="auth-nickname"
|
||||
placeholder={t('nicknamePlaceholder')}
|
||||
autoComplete="username"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{title === 'login' && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="remember"
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel required>{t('passwordLabel')}</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex gap-2">
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked);
|
||||
}}
|
||||
<div className="relative">
|
||||
<Input
|
||||
data-testid="auth-password"
|
||||
type={'password'}
|
||||
placeholder={t('passwordPlaceholder')}
|
||||
autoComplete={
|
||||
title === 'login'
|
||||
? 'current-password'
|
||||
: 'new-password'
|
||||
}
|
||||
{...field}
|
||||
/>
|
||||
<FormLabel
|
||||
className={cn(' hover:text-text-primary', {
|
||||
'text-text-disabled': !field.value,
|
||||
'text-text-primary': field.value,
|
||||
})}
|
||||
>
|
||||
{t('rememberMe')}
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<ButtonLoading
|
||||
data-testid="auth-submit"
|
||||
type="submit"
|
||||
loading={loading}
|
||||
className="bg-metallic-gradient border-b-[#00BEB4] border-b-2 hover:bg-metallic-gradient hover:border-b-[#02bcdd] w-full my-8"
|
||||
>
|
||||
{title === 'login' ? t('login') : t('continue')}
|
||||
</ButtonLoading>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
{title === 'login' && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="remember"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div className="flex gap-2">
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked);
|
||||
}}
|
||||
/>
|
||||
<FormLabel
|
||||
className={cn(' hover:text-text-primary', {
|
||||
'text-text-disabled': !field.value,
|
||||
'text-text-primary': field.value,
|
||||
})}
|
||||
>
|
||||
{t('rememberMe')}
|
||||
</FormLabel>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<ButtonLoading
|
||||
data-testid="auth-submit"
|
||||
type="submit"
|
||||
loading={loading}
|
||||
className="bg-metallic-gradient border-b-[#00BEB4] border-b-2 hover:bg-metallic-gradient hover:border-b-[#02bcdd] w-full my-8"
|
||||
>
|
||||
{title === 'login' ? t('login') : t('continue')}
|
||||
</ButtonLoading>
|
||||
</form>
|
||||
</Form>
|
||||
)}
|
||||
|
||||
{title === 'login' && channels && channels.length > 0 && (
|
||||
@@ -361,7 +361,7 @@ const Login = () => {
|
||||
<div className=" h-[inherit] relative overflow-auto">
|
||||
<BgSvg isPaused />
|
||||
|
||||
<div className="absolute top-3 flex flex-col items-center mb-12 w-full text-text-primary">
|
||||
<div className="z-20 absolute top-3 flex flex-col items-center mb-12 w-full text-text-primary">
|
||||
<div className="flex items-center mb-4 w-full pl-10 pt-10 ">
|
||||
<div className="w-12 h-12 p-2 rounded-lg flex items-center justify-center mr-3">
|
||||
<img
|
||||
@@ -399,4 +399,4 @@ const Login = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
export default Login;
|
||||
|
||||
@@ -181,7 +181,7 @@ const ChatCard = forwardRef(function ChatCard(
|
||||
</div>
|
||||
<div className="space-x-2">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
|
||||
@@ -1,36 +1,18 @@
|
||||
import EmbedDialog from '@/components/embed-dialog';
|
||||
import { useShowEmbedModal } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import {
|
||||
useFetchConversationList,
|
||||
useFetchConversationManually,
|
||||
useFetchDialog,
|
||||
useGetChatSearchParams,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { IClientConversation } from '@/interfaces/database/chat';
|
||||
import { NextLayoutContainer } from '@/layouts/next';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Routes } from '@/routes';
|
||||
import { useMount } from 'ahooks';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
LucideArrowBigLeft,
|
||||
LucideArrowUpRight,
|
||||
LucideSend,
|
||||
} from 'lucide-react';
|
||||
import { LucideArrowBigLeft, LucideArrowUpRight } from 'lucide-react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router';
|
||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||
import { ChatSettings } from './app-settings/chat-settings';
|
||||
import { MultipleChatBox } from './chat-box/next-multiple-chat-box';
|
||||
@@ -40,8 +22,6 @@ import { useAddChatBox } from './use-add-box';
|
||||
import { useSwitchDebugMode } from './use-switch-debug-mode';
|
||||
|
||||
export default function Chat() {
|
||||
const { id } = useParams();
|
||||
const { data } = useFetchDialog();
|
||||
const { t } = useTranslation();
|
||||
const [currentConversation, setCurrentConversation] =
|
||||
useState<IClientConversation>({} as IClientConversation);
|
||||
@@ -55,9 +35,6 @@ export default function Chat() {
|
||||
const { removeChatBox, addChatBox, chatBoxIds, hasSingleChatBox } =
|
||||
useAddChatBox(isDebugMode);
|
||||
|
||||
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
||||
useShowEmbedModal();
|
||||
|
||||
const { conversationId, isNew } = useGetChatSearchParams();
|
||||
|
||||
const { data: dialogList } = useFetchConversationList();
|
||||
@@ -93,7 +70,7 @@ export default function Chat() {
|
||||
if (isDebugMode) {
|
||||
return (
|
||||
<section
|
||||
className="pt-5 pb-16 h-[100vh] flex flex-col"
|
||||
className="pt-5 pb-14 h-[100vh] flex flex-col"
|
||||
data-testid="chat-detail-multimodel-root"
|
||||
>
|
||||
<header className="px-10 pb-5">
|
||||
@@ -126,77 +103,46 @@ export default function Chat() {
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="h-full flex flex-col" data-testid="chat-detail">
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
// Not friendly for keyboard navigation
|
||||
// onClick={navigateToChatList}
|
||||
href={Routes.Chats}
|
||||
>
|
||||
{t('chat.chat')}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{data.name}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<Button onClick={showEmbedModal} data-testid="chat-detail-embed-open">
|
||||
<LucideSend />
|
||||
{t('common.embedIntoSite')}
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<NextLayoutContainer>
|
||||
<section className="h-full flex flex-col" data-testid="chat-detail">
|
||||
<article className="flex flex-1 min-h-0 pb-9">
|
||||
<Sessions handleConversationCardClick={handleSessionClick}></Sessions>
|
||||
|
||||
<article className="flex flex-1 min-h-0 pb-9">
|
||||
<Sessions handleConversationCardClick={handleSessionClick}></Sessions>
|
||||
<Card className="flex-1 min-w-0 bg-transparent border-none shadow-none h-full">
|
||||
<CardContent className="flex p-0 h-full">
|
||||
<Card className="flex flex-col flex-1 bg-transparent min-w-0">
|
||||
<CardHeader
|
||||
className={cn('p-5', {
|
||||
'border-b-0.5 border-border-button': hasSingleChatBox,
|
||||
})}
|
||||
>
|
||||
<CardTitle className="flex justify-between items-center text-base gap-2">
|
||||
<div className="truncate">{currentConversationName}</div>
|
||||
|
||||
<Card className="flex-1 min-w-0 bg-transparent border-none shadow-none h-full">
|
||||
<CardContent className="flex p-0 h-full">
|
||||
<Card className="flex flex-col flex-1 bg-transparent min-w-0">
|
||||
<CardHeader
|
||||
className={cn('p-5', {
|
||||
'border-b-0.5 border-border-button': hasSingleChatBox,
|
||||
})}
|
||||
>
|
||||
<CardTitle className="flex justify-between items-center text-base gap-2">
|
||||
<div className="truncate">{currentConversationName}</div>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
onClick={switchDebugMode}
|
||||
data-testid="chat-detail-multimodel-toggle"
|
||||
>
|
||||
<LucideArrowUpRight /> {t('chat.multipleModels')}
|
||||
</Button>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 p-0 min-h-0">
|
||||
<SingleChatBox
|
||||
controller={controller}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
conversation={currentConversation}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={switchDebugMode}
|
||||
data-testid="chat-detail-multimodel-toggle"
|
||||
>
|
||||
<LucideArrowUpRight />
|
||||
{t('chat.multipleModels')}
|
||||
</Button>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 p-0 min-h-0">
|
||||
<SingleChatBox
|
||||
controller={controller}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
conversation={currentConversation}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<ChatSettings hasSingleChatBox={hasSingleChatBox}></ChatSettings>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</article>
|
||||
|
||||
{embedVisible && (
|
||||
<EmbedDialog
|
||||
visible={embedVisible}
|
||||
hideModal={hideEmbedModal}
|
||||
token={id!}
|
||||
from={SharedFrom.Chat}
|
||||
beta={beta}
|
||||
isAgent={false}
|
||||
></EmbedDialog>
|
||||
)}
|
||||
</section>
|
||||
<ChatSettings hasSingleChatBox={hasSingleChatBox}></ChatSettings>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</article>
|
||||
</section>
|
||||
</NextLayoutContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||
import EmbedDialog from '@/components/embed-dialog';
|
||||
import { useShowEmbedModal } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { SearchInput } from '@/components/ui/input';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import {
|
||||
useFetchDialog,
|
||||
@@ -15,11 +23,13 @@ import {
|
||||
LucideListChecks,
|
||||
LucidePanelLeftClose,
|
||||
LucidePlus,
|
||||
LucideSend,
|
||||
LucideTrash2,
|
||||
LucideUndo2,
|
||||
} from 'lucide-react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router';
|
||||
import { useChatUrlParams } from '../hooks/use-chat-url';
|
||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||
import { useSelectDerivedConversationList } from '../hooks/use-select-conversation-list';
|
||||
@@ -132,6 +142,10 @@ export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||
|
||||
const selectedCount = useMemo(() => selectedIds.size, [selectedIds]);
|
||||
|
||||
const { id } = useParams();
|
||||
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
|
||||
useShowEmbedModal();
|
||||
|
||||
if (!visible) {
|
||||
return (
|
||||
<div className="p-5">
|
||||
@@ -158,7 +172,7 @@ export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||
role="complementary"
|
||||
data-testid="chat-detail-sessions"
|
||||
>
|
||||
<header className="flex items-center text-base justify-between gap-2">
|
||||
<header className="flex items-center text-base justify-between gap-4">
|
||||
<div className="flex gap-3 items-center min-w-0">
|
||||
<RAGFlowAvatar
|
||||
avatar={data.icon}
|
||||
@@ -169,10 +183,32 @@ export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||
<span className="flex-1 truncate">{data.name}</span>
|
||||
</div>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={showEmbedModal}
|
||||
size="icon-xs"
|
||||
data-testid="chat-detail-embed-open"
|
||||
>
|
||||
<LucideSend />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{t('common.embedIntoSite')}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<EmbedDialog
|
||||
visible={embedVisible}
|
||||
hideModal={hideEmbedModal}
|
||||
token={id!}
|
||||
from={SharedFrom.Chat}
|
||||
beta={beta}
|
||||
isAgent={false}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="transparent"
|
||||
size="icon-sm"
|
||||
className="border-0"
|
||||
className="border-0 ml-auto"
|
||||
onClick={switchVisible}
|
||||
data-testid="chat-detail-sessions-close"
|
||||
>
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import { useFetchTokenListBeforeOtherStep } from '@/components/embed-dialog/use-show-embed-dialog';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import {
|
||||
useFetchTenantInfo,
|
||||
useFetchUserInfo,
|
||||
@@ -31,7 +22,6 @@ import { SearchSetting } from './search-setting';
|
||||
import SearchingPage from './searching';
|
||||
|
||||
export default function SearchPage() {
|
||||
const { navigateToSearchList } = useNavigatePage();
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const { data: SearchData } = useFetchSearchDetail();
|
||||
const { beta, handleOperate } = useFetchTokenListBeforeOtherStep();
|
||||
@@ -57,22 +47,7 @@ export default function SearchPage() {
|
||||
}, [isSearching]);
|
||||
|
||||
return (
|
||||
<section data-testid="search-detail">
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToSearchList}>
|
||||
{t('header.search')}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{SearchData?.name}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<section className="size-full relative" data-testid="search-detail">
|
||||
<div className="flex gap-3 w-full bg-bg-base">
|
||||
<div className="flex-1">
|
||||
{!isSearching && (
|
||||
@@ -128,9 +103,8 @@ export default function SearchPage() {
|
||||
// ></EmbedDialog>
|
||||
}
|
||||
</div>
|
||||
<div className="absolute end-5 top-4 ">
|
||||
<div className="absolute end-5 top-4">
|
||||
<Button
|
||||
className="bg-text-primary text-bg-base border-b-accent-primary border-b-2"
|
||||
onClick={() => {
|
||||
handleOperate().then((res) => {
|
||||
console.log(res, 'res');
|
||||
|
||||
@@ -1,52 +1,15 @@
|
||||
import { Outlet } from 'react-router';
|
||||
import { SideBar } from './sidebar';
|
||||
|
||||
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 { cn } from '@/lib/utils';
|
||||
import { House } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './index.module.less';
|
||||
|
||||
const UserSetting = () => {
|
||||
const { t } = useTranslation();
|
||||
const { navigateToHome } = useNavigatePage();
|
||||
|
||||
return (
|
||||
<section className="flex flex-col h-full">
|
||||
<PageHeader>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink onClick={navigateToHome}>
|
||||
<House className="size-4" />
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{t('setting.profile')}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<div
|
||||
className={cn(
|
||||
styles.settingWrapper,
|
||||
'overflow-auto flex flex-1 pt-4 pr-4 pb-4',
|
||||
)}
|
||||
>
|
||||
<SideBar></SideBar>
|
||||
<div className={cn(styles.outletWrapper, 'flex flex-1 rounded-lg')}>
|
||||
<Outlet></Outlet>
|
||||
</div>
|
||||
<section className="pt-8 size-full grid grid-cols-[auto_1fr] grid-rows-1">
|
||||
<SideBar></SideBar>
|
||||
|
||||
<div className={cn('pr-6 pb-6 flex flex-1 rounded-lg overflow-hidden')}>
|
||||
<Outlet></Outlet>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user