|
|
|
@@ -1,39 +1,87 @@ |
|
|
|
import { useTheme } from '@/components/theme-provider'; |
|
|
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; |
|
|
|
import { Badge } from '@/components/ui/badge'; |
|
|
|
import { Button } from '@/components/ui/button'; |
|
|
|
import { Container } from '@/components/ui/container'; |
|
|
|
import { |
|
|
|
DropdownMenu, |
|
|
|
DropdownMenuContent, |
|
|
|
DropdownMenuItem, |
|
|
|
DropdownMenuTrigger, |
|
|
|
} from '@/components/ui/dropdown-menu'; |
|
|
|
import { Segmented, SegmentedValue } from '@/components/ui/segmented'; |
|
|
|
import { useTranslate } from '@/hooks/common-hooks'; |
|
|
|
import { LanguageList, LanguageMap } from '@/constants/common'; |
|
|
|
import { useChangeLanguage } from '@/hooks/logic-hooks'; |
|
|
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; |
|
|
|
import { useNavigateWithFromState } from '@/hooks/route-hook'; |
|
|
|
import { useFetchUserInfo, useListTenant } from '@/hooks/user-setting-hooks'; |
|
|
|
import { TenantRole } from '@/pages/user-setting/constants'; |
|
|
|
import { Routes } from '@/routes'; |
|
|
|
import { camelCase } from 'lodash'; |
|
|
|
import { |
|
|
|
ChevronDown, |
|
|
|
CircleHelp, |
|
|
|
Cpu, |
|
|
|
File, |
|
|
|
Github, |
|
|
|
House, |
|
|
|
Library, |
|
|
|
MessageSquareText, |
|
|
|
Moon, |
|
|
|
Search, |
|
|
|
Zap, |
|
|
|
Sun, |
|
|
|
} from 'lucide-react'; |
|
|
|
import { useCallback, useMemo } from 'react'; |
|
|
|
import React, { useCallback, useMemo } from 'react'; |
|
|
|
import { useTranslation } from 'react-i18next'; |
|
|
|
import { useLocation } from 'umi'; |
|
|
|
|
|
|
|
const handleDocHelpCLick = () => { |
|
|
|
window.open('https://ragflow.io/docs/dev/category/guides', 'target'); |
|
|
|
}; |
|
|
|
|
|
|
|
export function Header() { |
|
|
|
const { t } = useTranslate('header'); |
|
|
|
const { t } = useTranslation(); |
|
|
|
const { pathname } = useLocation(); |
|
|
|
const navigate = useNavigateWithFromState(); |
|
|
|
const { navigateToProfile } = useNavigatePage(); |
|
|
|
|
|
|
|
const changeLanguage = useChangeLanguage(); |
|
|
|
const { setTheme, theme } = useTheme(); |
|
|
|
|
|
|
|
const { |
|
|
|
data: { language = 'English' }, |
|
|
|
} = useFetchUserInfo(); |
|
|
|
|
|
|
|
const handleItemClick = (key: string) => () => { |
|
|
|
changeLanguage(key); |
|
|
|
}; |
|
|
|
|
|
|
|
const { data } = useListTenant(); |
|
|
|
|
|
|
|
const showBell = useMemo(() => { |
|
|
|
return data.some((x) => x.role === TenantRole.Invite); |
|
|
|
}, [data]); |
|
|
|
|
|
|
|
const items = LanguageList.map((x) => ({ |
|
|
|
key: x, |
|
|
|
label: <span>{LanguageMap[x as keyof typeof LanguageMap]}</span>, |
|
|
|
})); |
|
|
|
|
|
|
|
const onThemeClick = React.useCallback(() => { |
|
|
|
setTheme(theme === 'dark' ? 'light' : 'dark'); |
|
|
|
}, [setTheme, theme]); |
|
|
|
|
|
|
|
const handleBellClick = useCallback(() => { |
|
|
|
navigate('/user-setting/team'); |
|
|
|
}, [navigate]); |
|
|
|
|
|
|
|
const tagsData = useMemo( |
|
|
|
() => [ |
|
|
|
{ path: Routes.Home, name: t('home'), icon: House }, |
|
|
|
{ path: Routes.Datasets, name: t('knowledgeBase'), icon: Library }, |
|
|
|
{ path: Routes.Chats, name: t('chat'), icon: MessageSquareText }, |
|
|
|
{ path: Routes.Searches, name: t('search'), icon: Search }, |
|
|
|
{ path: Routes.Agents, name: t('flow'), icon: Cpu }, |
|
|
|
{ path: Routes.Files, name: t('fileManager'), icon: File }, |
|
|
|
{ path: Routes.Home, name: t('header.home'), icon: House }, |
|
|
|
{ path: Routes.Datasets, name: t('header.knowledgeBase'), icon: Library }, |
|
|
|
{ path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText }, |
|
|
|
{ path: Routes.Searches, name: t('header.search'), icon: Search }, |
|
|
|
{ path: Routes.Agents, name: t('header.flow'), icon: Cpu }, |
|
|
|
{ path: Routes.Files, name: t('header.fileManager'), icon: File }, |
|
|
|
], |
|
|
|
[t], |
|
|
|
); |
|
|
|
@@ -87,30 +135,37 @@ export function Header() { |
|
|
|
value={currentPath} |
|
|
|
onChange={handleChange} |
|
|
|
></Segmented> |
|
|
|
<div className="flex items-center gap-4"> |
|
|
|
<Container className="bg-colors-background-inverse-standard hidden xl:flex"> |
|
|
|
V 0.13.0 |
|
|
|
<Button variant="secondary" className="size-8"> |
|
|
|
<ChevronDown /> |
|
|
|
</Button> |
|
|
|
</Container> |
|
|
|
<Container className="px-3 py-2 bg-colors-background-inverse-standard"> |
|
|
|
<Avatar |
|
|
|
className="w-[30px] h-[30px] cursor-pointer" |
|
|
|
onClick={navigateToProfile} |
|
|
|
> |
|
|
|
<div className="flex items-center gap-5 text-text-badge"> |
|
|
|
<DropdownMenu> |
|
|
|
<DropdownMenuTrigger> |
|
|
|
<div className="flex items-center gap-1"> |
|
|
|
{t(`common.${camelCase(language)}`)} |
|
|
|
<ChevronDown className="size-4" /> |
|
|
|
</div> |
|
|
|
</DropdownMenuTrigger> |
|
|
|
<DropdownMenuContent> |
|
|
|
{items.map((x) => ( |
|
|
|
<DropdownMenuItem key={x.key} onClick={handleItemClick(x.key)}> |
|
|
|
{x.label} |
|
|
|
</DropdownMenuItem> |
|
|
|
))} |
|
|
|
</DropdownMenuContent> |
|
|
|
</DropdownMenu> |
|
|
|
<Button variant={'ghost'} onClick={handleDocHelpCLick}> |
|
|
|
<CircleHelp /> |
|
|
|
</Button> |
|
|
|
<Button variant={'ghost'} onClick={onThemeClick}> |
|
|
|
{theme === 'light' ? <Sun /> : <Moon />} |
|
|
|
</Button> |
|
|
|
<div className="relative"> |
|
|
|
<Avatar className="size-8 cursor-pointer" onClick={navigateToProfile}> |
|
|
|
<AvatarImage src="https://github.com/shadcn.png" /> |
|
|
|
<AvatarFallback>CN</AvatarFallback> |
|
|
|
</Avatar> |
|
|
|
<span className="max-w-14 truncate"> yifanwu92@gmail.com</span> |
|
|
|
<Button |
|
|
|
variant="destructive" |
|
|
|
className="py-[2px] px-[8px] h-[23px] rounded-[4px]" |
|
|
|
> |
|
|
|
<Zap /> |
|
|
|
<Badge className="h-5 w-8 absolute font-normal p-0 justify-center -right-8 -top-2 text-text-title-invert bg-gradient-to-l from-[#42D7E7] to-[#478AF5]"> |
|
|
|
Pro |
|
|
|
</Button> |
|
|
|
</Container> |
|
|
|
</Badge> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
); |