### What problem does this PR solve? Feat: Adjust the page header to breadcrumbs #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| import { ArrowLeft } from 'lucide-react'; | |||||
| import { PropsWithChildren, ReactNode } from 'react'; | |||||
| import { Button } from './ui/button'; | |||||
| import { PropsWithChildren } from 'react'; | |||||
| interface IPageHeaderProps extends PropsWithChildren { | |||||
| back(): void; | |||||
| title: ReactNode; | |||||
| } | |||||
| export function PageHeader({ back, title, children }: IPageHeaderProps) { | |||||
| export function PageHeader({ children }: PropsWithChildren) { | |||||
| return ( | return ( | ||||
| <header className="flex justify-between items-center border-b pr-9"> | |||||
| <div className="flex items-center "> | |||||
| <div className="flex items-center border-r p-1.5"> | |||||
| <Button variant="ghost" size="icon" onClick={back}> | |||||
| <ArrowLeft className="w-5 h-5" /> | |||||
| </Button> | |||||
| </div> | |||||
| <div className="p-4"> | |||||
| <h1 className="text-2xl font-semibold tracking-tight">{title}</h1> | |||||
| </div> | |||||
| </div> | |||||
| <header className="flex justify-between items-center border-b bg-background-header-bar p-5"> | |||||
| {children} | {children} | ||||
| </header> | </header> | ||||
| ); | ); |
| >(({ className, ...props }, ref) => ( | >(({ className, ...props }, ref) => ( | ||||
| <li | <li | ||||
| ref={ref} | ref={ref} | ||||
| className={cn('inline-flex items-center gap-1.5', className)} | |||||
| className={cn( | |||||
| 'inline-flex items-center gap-1.5 text-text-sub-title', | |||||
| className, | |||||
| )} | |||||
| {...props} | {...props} | ||||
| /> | /> | ||||
| )); | )); |
| tavilySearchDescription: 'Search results via Tavily service.', | tavilySearchDescription: 'Search results via Tavily service.', | ||||
| tavilyExtract: 'Tavily Extract', | tavilyExtract: 'Tavily Extract', | ||||
| tavilyExtractDescription: 'Tavily Extract', | tavilyExtractDescription: 'Tavily Extract', | ||||
| log: 'Log', | |||||
| management: 'Management', | |||||
| import: 'Import', | |||||
| export: 'Export', | |||||
| }, | }, | ||||
| llmTools: { | llmTools: { | ||||
| bad_calculator: { | bad_calculator: { |
| tavilySearchDescription: '通过 Tavily 服务搜索结果', | tavilySearchDescription: '通过 Tavily 服务搜索结果', | ||||
| tavilyExtract: 'Tavily Extract', | tavilyExtract: 'Tavily Extract', | ||||
| tavilyExtractDescription: 'Tavily Extract', | tavilyExtractDescription: 'Tavily Extract', | ||||
| log: '日志', | |||||
| management: '管理', | |||||
| import: '导入', | |||||
| export: '导出', | |||||
| }, | }, | ||||
| footer: { | footer: { | ||||
| profile: 'All rights reserved @ React', | profile: 'All rights reserved @ React', |
| import { PageHeader } from '@/components/page-header'; | import { PageHeader } from '@/components/page-header'; | ||||
| import { | |||||
| Breadcrumb, | |||||
| BreadcrumbItem, | |||||
| BreadcrumbLink, | |||||
| BreadcrumbList, | |||||
| BreadcrumbPage, | |||||
| BreadcrumbSeparator, | |||||
| } from '@/components/ui/breadcrumb'; | |||||
| import { Button, ButtonLoading } from '@/components/ui/button'; | import { Button, ButtonLoading } from '@/components/ui/button'; | ||||
| import { | import { | ||||
| DropdownMenu, | DropdownMenu, | ||||
| return ( | return ( | ||||
| <section className="h-full"> | <section className="h-full"> | ||||
| <PageHeader back={navigateToAgentList} title={flowDetail.title}> | |||||
| <div className="flex items-center gap-2"> | |||||
| <PageHeader> | |||||
| <Breadcrumb> | |||||
| <BreadcrumbList> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink onClick={navigateToAgentList}> | |||||
| Agent | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbPage>{flowDetail.title}</BreadcrumbPage> | |||||
| </BreadcrumbItem> | |||||
| </BreadcrumbList> | |||||
| </Breadcrumb> | |||||
| <div className="flex items-center gap-5"> | |||||
| <ButtonLoading | <ButtonLoading | ||||
| variant={'secondary'} | variant={'secondary'} | ||||
| onClick={() => saveGraph()} | onClick={() => saveGraph()} | ||||
| loading={loading} | loading={loading} | ||||
| > | > | ||||
| <LaptopMinimalCheck /> Save | |||||
| <LaptopMinimalCheck /> {t('flow.save')} | |||||
| </ButtonLoading> | </ButtonLoading> | ||||
| <Button variant={'secondary'} onClick={handleRunAgent}> | <Button variant={'secondary'} onClick={handleRunAgent}> | ||||
| <CirclePlay /> | <CirclePlay /> | ||||
| Run app | |||||
| {t('flow.run')} | |||||
| </Button> | </Button> | ||||
| <Button variant={'secondary'} onClick={showVersionDialog}> | <Button variant={'secondary'} onClick={showVersionDialog}> | ||||
| <History /> | <History /> | ||||
| History version | |||||
| {t('flow.historyversion')} | |||||
| </Button> | </Button> | ||||
| <Button variant={'secondary'}> | <Button variant={'secondary'}> | ||||
| <Logs /> | <Logs /> | ||||
| Log | |||||
| {t('flow.log')} | |||||
| </Button> | </Button> | ||||
| <DropdownMenu> | <DropdownMenu> | ||||
| <DropdownMenuTrigger asChild> | <DropdownMenuTrigger asChild> | ||||
| <Button variant={'secondary'}> | <Button variant={'secondary'}> | ||||
| <ChevronDown /> Management | |||||
| <ChevronDown /> {t('flow.management')} | |||||
| </Button> | </Button> | ||||
| </DropdownMenuTrigger> | </DropdownMenuTrigger> | ||||
| <DropdownMenuContent> | <DropdownMenuContent> | ||||
| <DropdownMenuSeparator /> | <DropdownMenuSeparator /> | ||||
| <AgentDropdownMenuItem onClick={handleImportJson}> | <AgentDropdownMenuItem onClick={handleImportJson}> | ||||
| <Download /> | <Download /> | ||||
| Import | |||||
| {t('flow.import')} | |||||
| </AgentDropdownMenuItem> | </AgentDropdownMenuItem> | ||||
| <DropdownMenuSeparator /> | <DropdownMenuSeparator /> | ||||
| <AgentDropdownMenuItem onClick={handleExportJson}> | <AgentDropdownMenuItem onClick={handleExportJson}> | ||||
| <Upload /> | <Upload /> | ||||
| Export | |||||
| {t('flow.export')} | |||||
| </AgentDropdownMenuItem> | </AgentDropdownMenuItem> | ||||
| <DropdownMenuSeparator /> | <DropdownMenuSeparator /> | ||||
| <AgentDropdownMenuItem onClick={showEmbedModal}> | <AgentDropdownMenuItem onClick={showEmbedModal}> |
| import { PageHeader } from '@/components/page-header'; | |||||
| import { Button } from '@/components/ui/button'; | |||||
| import { Segmented, SegmentedValue } from '@/components/ui/segmented'; | |||||
| import { | |||||
| QueryStringMap, | |||||
| useNavigatePage, | |||||
| } from '@/hooks/logic-hooks/navigate-hooks'; | |||||
| import { Routes } from '@/routes'; | |||||
| import { EllipsisVertical, Save } from 'lucide-react'; | |||||
| import { useMemo } from 'react'; | |||||
| import { Outlet, useLocation } from 'umi'; | |||||
| export default function ChunkPage() { | |||||
| const { navigateToDataset, getQueryString, navigateToChunk } = | |||||
| useNavigatePage(); | |||||
| const location = useLocation(); | |||||
| const options = useMemo(() => { | |||||
| return [ | |||||
| { | |||||
| label: 'Parsed results', | |||||
| value: Routes.ParsedResult, | |||||
| }, | |||||
| { | |||||
| label: 'Chunk result', | |||||
| value: Routes.ChunkResult, | |||||
| }, | |||||
| { | |||||
| label: 'Result view', | |||||
| value: Routes.ResultView, | |||||
| }, | |||||
| ]; | |||||
| }, []); | |||||
| const path = useMemo(() => { | |||||
| return location.pathname.split('/').slice(0, 3).join('/'); | |||||
| }, [location.pathname]); | |||||
| return ( | |||||
| <section> | |||||
| <PageHeader | |||||
| title="Editing block" | |||||
| back={navigateToDataset( | |||||
| getQueryString(QueryStringMap.KnowledgeId) as string, | |||||
| )} | |||||
| > | |||||
| <div> | |||||
| <Segmented | |||||
| options={options} | |||||
| value={path} | |||||
| onChange={navigateToChunk as (val: SegmentedValue) => void} | |||||
| className="bg-colors-background-inverse-standard text-colors-text-neutral-standard" | |||||
| ></Segmented> | |||||
| </div> | |||||
| <div className="flex items-center gap-2"> | |||||
| <Button variant={'icon'} size={'icon'}> | |||||
| <EllipsisVertical /> | |||||
| </Button> | |||||
| <Button variant={'tertiary'} size={'sm'}> | |||||
| <Save /> | |||||
| Save | |||||
| </Button> | |||||
| </div> | |||||
| </PageHeader> | |||||
| <Outlet /> | |||||
| </section> | |||||
| ); | |||||
| } |
| import { PageHeader } from '@/components/page-header'; | import { PageHeader } from '@/components/page-header'; | ||||
| import { | |||||
| Breadcrumb, | |||||
| BreadcrumbItem, | |||||
| BreadcrumbLink, | |||||
| BreadcrumbList, | |||||
| BreadcrumbPage, | |||||
| BreadcrumbSeparator, | |||||
| } from '@/components/ui/breadcrumb'; | |||||
| import { Button } from '@/components/ui/button'; | import { Button } from '@/components/ui/button'; | ||||
| import { Segmented, SegmentedValue } from '@/components/ui/segmented'; | import { Segmented, SegmentedValue } from '@/components/ui/segmented'; | ||||
| import { | import { | ||||
| return ( | return ( | ||||
| <section> | <section> | ||||
| <PageHeader | |||||
| title="Editing block" | |||||
| back={navigateToDataset( | |||||
| getQueryString(QueryStringMap.KnowledgeId) as string, | |||||
| )} | |||||
| > | |||||
| <PageHeader> | |||||
| <Breadcrumb> | |||||
| <BreadcrumbList> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink | |||||
| onClick={navigateToDataset( | |||||
| getQueryString(QueryStringMap.KnowledgeId) as string, | |||||
| )} | |||||
| > | |||||
| Agent | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbPage>xxx</BreadcrumbPage> | |||||
| </BreadcrumbItem> | |||||
| </BreadcrumbList> | |||||
| </Breadcrumb> | |||||
| <div> | <div> | ||||
| <Segmented | <Segmented | ||||
| options={options} | options={options} |
| import DocumentHeader from './components/document-preview/document-header'; | import DocumentHeader from './components/document-preview/document-header'; | ||||
| import { PageHeader } from '@/components/page-header'; | import { PageHeader } from '@/components/page-header'; | ||||
| import { | |||||
| Breadcrumb, | |||||
| BreadcrumbItem, | |||||
| BreadcrumbLink, | |||||
| BreadcrumbList, | |||||
| BreadcrumbPage, | |||||
| BreadcrumbSeparator, | |||||
| } from '@/components/ui/breadcrumb'; | |||||
| import message from '@/components/ui/message'; | import message from '@/components/ui/message'; | ||||
| import { | import { | ||||
| RAGFlowPagination, | RAGFlowPagination, | ||||
| QueryStringMap, | QueryStringMap, | ||||
| useNavigatePage, | useNavigatePage, | ||||
| } from '@/hooks/logic-hooks/navigate-hooks'; | } from '@/hooks/logic-hooks/navigate-hooks'; | ||||
| import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| const Chunk = () => { | const Chunk = () => { | ||||
| } = useFetchNextChunkList(); | } = useFetchNextChunkList(); | ||||
| const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick(); | const { handleChunkCardClick, selectedChunkId } = useHandleChunkCardClick(); | ||||
| const isPdf = documentInfo?.type === 'pdf'; | const isPdf = documentInfo?.type === 'pdf'; | ||||
| const { data: dataset } = useFetchKnowledgeBaseConfiguration(); | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); | const { changeChunkTextMode, textMode } = useChangeChunkTextMode(); | ||||
| chunkUpdatingVisible, | chunkUpdatingVisible, | ||||
| documentId, | documentId, | ||||
| } = useUpdateChunk(); | } = useUpdateChunk(); | ||||
| const { navigateToDataset, getQueryString } = useNavigatePage(); | |||||
| const { navigateToDataset, getQueryString, navigateToDatasetList } = | |||||
| useNavigatePage(); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| setChunkList(data); | setChunkList(data); | ||||
| }, [data]); | }, [data]); | ||||
| return ( | return ( | ||||
| <> | <> | ||||
| <PageHeader | |||||
| title="Back" | |||||
| back={navigateToDataset(getQueryString(QueryStringMap.id) as string)} | |||||
| ></PageHeader> | |||||
| <PageHeader> | |||||
| <Breadcrumb> | |||||
| <BreadcrumbList> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink onClick={navigateToDatasetList}> | |||||
| {t('knowledgeDetails.dataset')} | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink | |||||
| onClick={navigateToDataset( | |||||
| getQueryString(QueryStringMap.id) as string, | |||||
| )} | |||||
| > | |||||
| {dataset.name} | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbPage>{documentInfo.name}</BreadcrumbPage> | |||||
| </BreadcrumbItem> | |||||
| </BreadcrumbList> | |||||
| </Breadcrumb> | |||||
| </PageHeader> | |||||
| <div className={styles.chunkPage}> | <div className={styles.chunkPage}> | ||||
| <div className="flex flex-1 gap-8"> | <div className="flex flex-1 gap-8"> | ||||
| <div className="w-2/5"> | <div className="w-2/5"> |
| import ParsedResultPanel from '../parsed-result-panel'; | |||||
| export default function ParsedResult() { | |||||
| return ( | |||||
| <section className="flex"> | |||||
| <div className="flex-1"></div> | |||||
| <ParsedResultPanel></ParsedResultPanel> | |||||
| </section> | |||||
| ); | |||||
| } |
| import { PageHeader } from '@/components/page-header'; | 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 { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | ||||
| import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { Outlet } from 'umi'; | import { Outlet } from 'umi'; | ||||
| import { SideBar } from './sidebar'; | import { SideBar } from './sidebar'; | ||||
| export default function DatasetWrapper() { | export default function DatasetWrapper() { | ||||
| const { navigateToDatasetList } = useNavigatePage(); | const { navigateToDatasetList } = useNavigatePage(); | ||||
| const { t } = useTranslation(); | |||||
| const { data } = useFetchKnowledgeBaseConfiguration(); | |||||
| return ( | return ( | ||||
| <section> | <section> | ||||
| <PageHeader | |||||
| title="Dataset details" | |||||
| back={navigateToDatasetList} | |||||
| ></PageHeader> | |||||
| <PageHeader> | |||||
| <Breadcrumb> | |||||
| <BreadcrumbList> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbLink onClick={navigateToDatasetList}> | |||||
| {t('knowledgeDetails.dataset')} | |||||
| </BreadcrumbLink> | |||||
| </BreadcrumbItem> | |||||
| <BreadcrumbSeparator /> | |||||
| <BreadcrumbItem> | |||||
| <BreadcrumbPage>{data.name}</BreadcrumbPage> | |||||
| </BreadcrumbItem> | |||||
| </BreadcrumbList> | |||||
| </Breadcrumb> | |||||
| </PageHeader> | |||||
| <div className="flex flex-1"> | <div className="flex flex-1"> | ||||
| <SideBar></SideBar> | <SideBar></SideBar> | ||||
| <div className="flex-1"> | <div className="flex-1"> |
| import { PageHeader } from '@/components/page-header'; | 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 { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | ||||
| import { House } from 'lucide-react'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { Outlet } from 'umi'; | import { Outlet } from 'umi'; | ||||
| import { SideBar } from './sidebar'; | import { SideBar } from './sidebar'; | ||||
| export default function ProfileSetting() { | export default function ProfileSetting() { | ||||
| const { navigateToHome } = useNavigatePage(); | const { navigateToHome } = useNavigatePage(); | ||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <div className="flex flex-col w-full h-screen bg-background text-foreground"> | <div className="flex flex-col w-full h-screen bg-background text-foreground"> | ||||
| {/* <header className="flex items-center border-b"> | |||||
| <div className="flex items-center border-r p-1.5"> | |||||
| <Button variant="ghost" size="icon" onClick={navigateToHome}> | |||||
| <ArrowLeft className="w-5 h-5" /> | |||||
| </Button> | |||||
| </div> | |||||
| <div className="p-4"> | |||||
| <h1 className="text-2xl font-semibold tracking-tight"> | |||||
| Profile & settings | |||||
| </h1> | |||||
| </div> | |||||
| </header> */} | |||||
| <PageHeader title="Profile & settings" back={navigateToHome}></PageHeader> | |||||
| <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="flex flex-1 bg-muted/50"> | <div className="flex flex-1 bg-muted/50"> | ||||
| <SideBar></SideBar> | <SideBar></SideBar> | ||||
| <main className="flex-1 "> | <main className="flex-1 "> | ||||
| <Outlet></Outlet> | <Outlet></Outlet> | ||||
| {/* <h1 className="text-3xl font-bold mb-6"> {title}</h1> */} | |||||
| </main> | </main> | ||||
| </div> | </div> | ||||
| </div> | </div> |