Feat: Bind the route to the navigation bar in the head #3221 (#3863)

### What problem does this PR solve?
Feat: Bind the route to the navigation bar in the head #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-12-04 19:10:08 +08:00 committed by GitHub
parent 1b817a5b4c
commit 84afb4259c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 246 additions and 113 deletions

View File

@ -1,12 +1,12 @@
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { UserOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import { Avatar, Form, Select, Space } from 'antd'; import { Avatar, Form, Select, Space } from 'antd';
const KnowledgeBaseItem = () => { const KnowledgeBaseItem = () => {
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const { list: knowledgeList } = useNextFetchKnowledgeList(true); const { list: knowledgeList } = useFetchKnowledgeList(true);
const knowledgeOptions = knowledgeList.map((x) => ({ const knowledgeOptions = knowledgeList.map((x) => ({
label: ( label: (

View File

@ -0,0 +1,39 @@
import { cn } from '@/lib/utils';
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn('animate-pulse rounded-md bg-muted', className)}
{...props}
/>
);
}
function ParagraphSkeleton() {
return (
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
);
}
function CardSkeleton() {
return (
<div className="flex flex-col space-y-3">
<Skeleton className="h-[125px] w-[250px] rounded-xl" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
);
}
export { CardSkeleton, ParagraphSkeleton, Skeleton };

View File

@ -41,7 +41,7 @@ export const useFetchKnowledgeBaseConfiguration = () => {
return { data, loading }; return { data, loading };
}; };
export const useNextFetchKnowledgeList = ( export const useFetchKnowledgeList = (
shouldFilterListWithoutDocument: boolean = false, shouldFilterListWithoutDocument: boolean = false,
): { ): {
list: IKnowledge[]; list: IKnowledge[];

View File

@ -0,0 +1,21 @@
import { Routes } from '@/routes';
import { useCallback } from 'react';
import { useNavigate } from 'umi';
export const useNavigatePage = () => {
const navigate = useNavigate();
const navigateToDatasetList = useCallback(() => {
navigate(Routes.Datasets);
}, [navigate]);
const navigateToDataset = useCallback(() => {
navigate(Routes.Dataset);
}, [navigate]);
const navigateToHome = useCallback(() => {
navigate(Routes.Home);
}, [navigate]);
return { navigateToDatasetList, navigateToDataset, navigateToHome };
};

View File

@ -3,32 +3,37 @@ import { Button } from '@/components/ui/button';
import { Container } from '@/components/ui/container'; import { Container } from '@/components/ui/container';
import { Segmented, SegmentedValue } from '@/components/ui/segmented '; import { Segmented, SegmentedValue } from '@/components/ui/segmented ';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useNavigateWithFromState } from '@/hooks/route-hook'; import { useNavigateWithFromState } from '@/hooks/route-hook';
import { cn } from '@/lib/utils';
import { Routes } from '@/routes';
import { import {
ChevronDown, ChevronDown,
Cpu, Cpu,
Github, Github,
House,
Library, Library,
MessageSquareText, MessageSquareText,
Search, Search,
Star, Star,
Zap, Zap,
} from 'lucide-react'; } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo } from 'react';
import { useLocation } from 'umi'; import { useLocation } from 'umi';
export function Header() { export function Header() {
const { t } = useTranslate('header'); const { t } = useTranslate('header');
const { pathname } = useLocation(); const { pathname } = useLocation();
const navigate = useNavigateWithFromState(); const navigate = useNavigateWithFromState();
const [currentPath, setCurrentPath] = useState('/home'); // const [currentPath, setCurrentPath] = useState(Routes.Home);
const { navigateToHome } = useNavigatePage();
const tagsData = useMemo( const tagsData = useMemo(
() => [ () => [
{ path: '/home', name: t('knowledgeBase'), icon: Library }, { path: Routes.Datasets, name: t('knowledgeBase'), icon: Library },
{ path: '/chat', name: t('chat'), icon: MessageSquareText }, { path: Routes.Chat, name: t('chat'), icon: MessageSquareText },
{ path: '/search', name: t('search'), icon: Search }, { path: Routes.Search, name: t('search'), icon: Search },
{ path: '/flow', name: t('flow'), icon: Cpu }, { path: Routes.Agent, name: t('flow'), icon: Cpu },
// { path: '/file', name: t('fileManager'), icon: FileIcon }, // { path: '/file', name: t('fileManager'), icon: FileIcon },
], ],
[t], [t],
@ -50,17 +55,21 @@ export function Header() {
}); });
}, [tagsData]); }, [tagsData]);
// const currentPath = useMemo(() => { const currentPath = useMemo(() => {
// return tagsData.find((x) => pathname.startsWith(x.path))?.name || 'home'; return (
// }, [pathname, tagsData]); tagsData.find((x) => pathname.startsWith(x.path))?.path || Routes.Home
);
}, [pathname, tagsData]);
const isHome = Routes.Home === currentPath;
const handleChange = (path: SegmentedValue) => { const handleChange = (path: SegmentedValue) => {
// navigate(path as string); navigate(path as Routes);
setCurrentPath(path as string); // setCurrentPath(path as Routes);
}; };
const handleLogoClick = useCallback(() => { const handleLogoClick = useCallback(() => {
navigate('/'); navigate(Routes.Home);
}, [navigate]); }, [navigate]);
return ( return (
@ -81,7 +90,22 @@ export function Header() {
<Star /> <Star />
</Button> </Button>
</div> </div>
<div> <div className="flex gap-2 items-center">
<Button
variant={'icon'}
size={'icon'}
onClick={navigateToHome}
className={cn({
'bg-colors-background-inverse-strong': isHome,
})}
>
<House
className={cn({
'text-colors-text-inverse-strong': isHome,
})}
/>
</Button>
<div className="h-8 w-[1px] bg-colors-outline-neutral-strong"></div>
<Segmented <Segmented
options={options} options={options}
value={currentPath} value={currentPath}
@ -90,7 +114,7 @@ export function Header() {
></Segmented> ></Segmented>
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Container className="bg-colors-background-inverse-standard"> <Container className="bg-colors-background-inverse-standard hidden xl:flex">
V 0.13.0 V 0.13.0
<Button variant="secondary" className="size-8"> <Button variant="secondary" className="size-8">
<ChevronDown /> <ChevronDown />
@ -101,7 +125,7 @@ export function Header() {
<AvatarImage src="https://github.com/shadcn.png" /> <AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback> <AvatarFallback>CN</AvatarFallback>
</Avatar> </Avatar>
yifanwu92@gmail.com <span className="max-w-14 truncate"> yifanwu92@gmail.com</span>
<Button <Button
variant="destructive" variant="destructive"
className="py-[2px] px-[8px] h-[23px] rounded-[4px]" className="py-[2px] px-[8px] h-[23px] rounded-[4px]"

View File

@ -0,0 +1,3 @@
export default function Agent() {
return <div>Agent</div>;
}

View File

@ -1,5 +1,5 @@
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { filterOptionsByInput } from '@/utils/common-util'; import { filterOptionsByInput } from '@/utils/common-util';
import { Form, Modal, Select } from 'antd'; import { Form, Modal, Select } from 'antd';
@ -13,7 +13,7 @@ const ConnectToKnowledgeModal = ({
loading, loading,
}: IModalProps<string[]> & { initialValue: string[] }) => { }: IModalProps<string[]> & { initialValue: string[] }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const { list } = useNextFetchKnowledgeList(); const { list } = useFetchKnowledgeList();
const { t } = useTranslate('fileManager'); const { t } = useTranslate('fileManager');
const options = list?.map((item) => ({ const options = list?.map((item) => ({

View File

@ -1,4 +1,4 @@
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { UserOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import { Avatar, Flex } from 'antd'; import { Avatar, Flex } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
@ -17,7 +17,7 @@ export function RetrievalNode({
selected, selected,
}: NodeProps<NodeData>) { }: NodeProps<NodeData>) {
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []); const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
const { list: knowledgeList } = useNextFetchKnowledgeList(true); const { list: knowledgeList } = useFetchKnowledgeList(true);
const knowledgeBases = useMemo(() => { const knowledgeBases = useMemo(() => {
return knowledgeBaseIds.map((x) => { return knowledgeBaseIds.map((x) => {
const item = knowledgeList.find((y) => x === y.id); const item = knowledgeList.find((y) => x === y.id);

View File

@ -1,82 +1,78 @@
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { ChevronRight, MoreHorizontal } from 'lucide-react'; import { CardSkeleton } from '@/components/ui/skeleton';
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
const datasets = [ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
{ import { formatDate } from '@/utils/date';
id: 1, import { ChevronRight, Trash2 } from 'lucide-react';
title: 'Legal knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: 'https://github.com/shadcn.png',
},
{
id: 2,
title: 'HR knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: 'https://github.com/shadcn.png',
},
{
id: 3,
title: 'IT knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: 'https://github.com/shadcn.png',
},
{
id: 4,
title: 'Legal knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: 'https://github.com/shadcn.png',
},
];
export function Datasets() { export function Datasets() {
const { navigateToDatasetList, navigateToDataset } = useNavigatePage();
const { list, loading } = useFetchKnowledgeList();
return ( return (
<section> <section>
<h2 className="text-2xl font-bold mb-6">Datasets</h2> <h2 className="text-2xl font-bold mb-6">Datasets</h2>
<div className="flex gap-6"> <div className="flex gap-6">
{datasets.map((dataset) => ( {loading ? (
<Card <div className="flex-1">
key={dataset.id} <CardSkeleton />
className="bg-colors-background-inverse-weak flex-1 border-colors-outline-neutral-standard" </div>
> ) : (
<CardContent className="p-4"> <div className="flex gap-4 flex-1">
<div className="flex justify-between mb-4"> {list.slice(0, 3).map((dataset) => (
<div <Card
className="w-[70px] h-[70px] rounded-xl bg-cover" key={dataset.id}
style={{ backgroundImage: `url(${dataset.image})` }} className="bg-colors-background-inverse-weak flex-1 border-colors-outline-neutral-standard"
/> >
<Button variant="ghost" size="icon"> <CardContent className="p-4">
<MoreHorizontal className="h-6 w-6" /> <div className="flex justify-between mb-4">
</Button> {dataset.avatar ? (
</div> <div
<div className="flex justify-between items-end"> className="w-[70px] h-[70px] rounded-xl bg-cover"
<div> style={{ backgroundImage: `url(${dataset.avatar})` }}
<h3 className="text-lg font-semibold mb-2"> />
{dataset.title} ) : (
</h3> <Avatar>
<p className="text-sm opacity-80"> <AvatarImage src="https://github.com/shadcn.png" />
{dataset.files} | {dataset.size} <AvatarFallback>CN</AvatarFallback>
</p> </Avatar>
<p className="text-sm opacity-80"> )}
Created {dataset.created} <Button variant="ghost" size="icon">
</p> <Trash2 />
</div> </Button>
<Button variant="icon" size="icon"> </div>
<ChevronRight className="h-6 w-6" /> <div className="flex justify-between items-end">
</Button> <div>
</div> <h3 className="text-lg font-semibold mb-2">
</CardContent> {dataset.name}
</Card> </h3>
))} <div className="text-sm opacity-80">
<Button className="h-auto " variant={'tertiary'}> {dataset.doc_num} files
</div>
<p className="text-sm opacity-80">
Created {formatDate(dataset.update_time)}
</p>
</div>
<Button
variant="icon"
size="icon"
onClick={navigateToDataset}
>
<ChevronRight className="h-6 w-6" />
</Button>
</div>
</CardContent>
</Card>
))}
</div>
)}
<Button
className="h-auto "
variant={'tertiary'}
onClick={navigateToDatasetList}
>
See all See all
</Button> </Button>
</div> </div>

View File

@ -0,0 +1,3 @@
export default function Chat() {
return <div>chat</div>;
}

View File

@ -0,0 +1,3 @@
export default function Search() {
return <div>Search</div>;
}

View File

@ -6,7 +6,7 @@ import { useClickDrawer } from '@/components/pdf-drawer/hooks';
import RetrievalDocuments from '@/components/retrieval-documents'; import RetrievalDocuments from '@/components/retrieval-documents';
import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { import {
useNextFetchKnowledgeList, useFetchKnowledgeList,
useSelectTestingResult, useSelectTestingResult,
} from '@/hooks/knowledge-hooks'; } from '@/hooks/knowledge-hooks';
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks'; import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
@ -45,7 +45,7 @@ const SearchPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [checkedList, setCheckedList] = useState<string[]>([]); const [checkedList, setCheckedList] = useState<string[]>([]);
const { chunks, total } = useSelectTestingResult(); const { chunks, total } = useSelectTestingResult();
const { list: knowledgeList } = useNextFetchKnowledgeList(); const { list: knowledgeList } = useFetchKnowledgeList();
const checkedWithoutEmbeddingIdList = useMemo(() => { const checkedWithoutEmbeddingIdList = useMemo(() => {
return checkedList.filter((x) => knowledgeList.some((y) => y.id === x)); return checkedList.filter((x) => knowledgeList.some((y) => y.id === x));
}, [checkedList, knowledgeList]); }, [checkedList, knowledgeList]);

View File

@ -1,4 +1,4 @@
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { UserOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import type { TreeDataNode, TreeProps } from 'antd'; import type { TreeDataNode, TreeProps } from 'antd';
import { Avatar, Layout, Space, Spin, Tree, Typography } from 'antd'; import { Avatar, Layout, Space, Spin, Tree, Typography } from 'antd';
@ -27,7 +27,7 @@ const SearchSidebar = ({
checkedList, checkedList,
setCheckedList, setCheckedList,
}: IProps) => { }: IProps) => {
const { list, loading } = useNextFetchKnowledgeList(); const { list, loading } = useFetchKnowledgeList();
const groupedList = useMemo(() => { const groupedList = useMemo(() => {
return list.reduce((pre: TreeDataNode[], cur) => { return list.reduce((pre: TreeDataNode[], cur) => {

View File

@ -1,3 +1,14 @@
export enum Routes {
Login = '/login',
Home = '/home',
Datasets = '/datasets',
DatasetBase = '/dataset',
Dataset = `${Routes.DatasetBase}${Routes.DatasetBase}`,
Agent = '/agent',
Search = '/next-search',
Chat = '/next-chat',
}
const routes = [ const routes = [
{ {
path: '/login', path: '/login',
@ -127,48 +138,81 @@ const routes = [
layout: false, layout: false,
}, },
{ {
path: '/home', path: Routes.Home,
layout: false, layout: false,
component: '@/layouts/next', component: '@/layouts/next',
routes: [ routes: [
{ {
path: '/home', path: Routes.Home,
component: '@/pages/home', component: `@/pages${Routes.Home}`,
}, },
], ],
}, },
{ {
path: '/datasets', path: Routes.Datasets,
layout: false, layout: false,
component: '@/layouts/next', component: '@/layouts/next',
routes: [ routes: [
{ {
path: '/datasets', path: Routes.Datasets,
component: '@/pages/datasets', component: `@/pages${Routes.Datasets}`,
}, },
], ],
}, },
{ {
path: '/dataset', path: Routes.Chat,
layout: false, layout: false,
component: '@/layouts/next', component: '@/layouts/next',
routes: [ routes: [
{ path: '/dataset', redirect: '/dataset/dataset' },
{ {
path: '/dataset', path: Routes.Chat,
component: '@/pages/dataset', component: `@/pages${Routes.Chat}`,
},
],
},
{
path: Routes.Search,
layout: false,
component: '@/layouts/next',
routes: [
{
path: Routes.Search,
component: `@/pages${Routes.Search}`,
},
],
},
{
path: Routes.Agent,
layout: false,
component: '@/layouts/next',
routes: [
{
path: Routes.Agent,
component: `@/pages${Routes.Agent}`,
},
],
},
{
path: Routes.DatasetBase,
layout: false,
component: '@/layouts/next',
routes: [
{ path: Routes.DatasetBase, redirect: Routes.Dataset },
{
path: Routes.DatasetBase,
component: `@/pages${Routes.DatasetBase}`,
routes: [ routes: [
{ {
path: '/dataset/dataset', path: Routes.Dataset,
component: '@/pages/dataset/dataset', component: `@/pages${Routes.Dataset}`,
}, },
{ {
path: '/dataset/configuration', path: `${Routes.DatasetBase}/configuration`,
component: '@/pages/dataset/settings', component: `@/pages${Routes.DatasetBase}/settings`,
}, },
{ {
path: '/dataset/testing', path: `${Routes.DatasetBase}/testing`,
component: '@/pages/dataset/testing', component: `@/pages${Routes.DatasetBase}/testing`,
}, },
], ],
}, },