| import { Download } from 'lucide-react'; | |
| import { useRecoilValue } from 'recoil'; | |
| import { Fragment, useState } from 'react'; | |
| import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider'; | |
| import type { TConversation } from 'librechat-data-provider'; | |
| import { Menu, Transition } from '@headlessui/react'; | |
| import { ExportModel } from './ExportConversation'; | |
| import ClearConvos from './ClearConvos'; | |
| import Settings from './Settings'; | |
| import NavLink from './NavLink'; | |
| import Logout from './Logout'; | |
| import { LinkIcon, DotsIcon, GearIcon } from '~/components'; | |
| import { useAuthContext } from '~/hooks/AuthContext'; | |
| import { useLocalize } from '~/hooks'; | |
| import { cn } from '~/utils/'; | |
| import store from '~/store'; | |
| export default function NavLinks() { | |
| const { user, isAuthenticated } = useAuthContext(); | |
| const { data: startupConfig } = useGetStartupConfig(); | |
| const balanceQuery = useGetUserBalance({ | |
| enabled: !!isAuthenticated && startupConfig?.checkBalance, | |
| }); | |
| const [showExports, setShowExports] = useState(false); | |
| const [showClearConvos, setShowClearConvos] = useState(false); | |
| const [showSettings, setShowSettings] = useState(false); | |
| const localize = useLocalize(); | |
| const conversation = useRecoilValue(store.conversation) ?? ({} as TConversation); | |
| const exportable = | |
| conversation?.conversationId && | |
| conversation?.conversationId !== 'new' && | |
| conversation?.conversationId !== 'search'; | |
| const clickHandler = () => { | |
| if (exportable) { | |
| setShowExports(true); | |
| } | |
| }; | |
| return ( | |
| <> | |
| <Menu as="div" className="group relative"> | |
| {({ open }) => ( | |
| <> | |
| {startupConfig?.checkBalance && balanceQuery.data && ( | |
| <div className="m-1 ml-3 whitespace-nowrap text-left text-sm text-gray-100"> | |
| {`Balance: ${balanceQuery.data}`} | |
| </div> | |
| )} | |
| <Menu.Button | |
| className={cn( | |
| 'group-ui-open:bg-gray-800 flex w-full items-center gap-2.5 rounded-md px-3 py-3 text-sm transition-colors duration-200 hover:bg-gray-800', | |
| open ? 'bg-gray-800' : '', | |
| )} | |
| data-testid="nav-user" | |
| > | |
| <div className="-ml-0.9 -mt-0.8 h-9 w-8 flex-shrink-0"> | |
| <div className="relative flex"> | |
| <img | |
| className="rounded-sm" | |
| src={ | |
| user?.avatar || | |
| `https://api.dicebear.com/6.x/initials/svg?seed=${ | |
| user?.name || 'User' | |
| }&fontFamily=Verdana&fontSize=36` | |
| } | |
| alt="" | |
| /> | |
| </div> | |
| </div> | |
| <div className="grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-white"> | |
| {user?.name || localize('com_nav_user')} | |
| </div> | |
| <DotsIcon /> | |
| </Menu.Button> | |
| <Transition | |
| as={Fragment} | |
| enter="transition ease-out duration-100 transform" | |
| enterFrom="translate-y-2 opacity-0" | |
| enterTo="translate-y-0 opacity-100" | |
| leave="transition ease-in duration-75 transform" | |
| leaveFrom="translate-y-0 opacity-100" | |
| leaveTo="translate-y-2 opacity-0" | |
| > | |
| <Menu.Items className="absolute bottom-full left-0 z-20 mb-2 w-full translate-y-0 overflow-hidden rounded-md bg-[#050509] py-1.5 opacity-100 outline-none"> | |
| <Menu.Item as="div"> | |
| <NavLink | |
| className={cn( | |
| 'flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700', | |
| exportable ? 'cursor-pointer text-white' : 'cursor-not-allowed text-white/50', | |
| )} | |
| svg={() => <Download size={16} />} | |
| text={localize('com_nav_export_conversation')} | |
| clickHandler={clickHandler} | |
| /> | |
| </Menu.Item> | |
| <div className="my-1.5 h-px bg-white/20" role="none" /> | |
| <Menu.Item as="div"> | |
| <NavLink | |
| className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700" | |
| svg={() => <LinkIcon />} | |
| text={localize('com_nav_help_faq')} | |
| clickHandler={() => window.open('https://docs.librechat.ai/', '_blank')} | |
| /> | |
| </Menu.Item> | |
| <Menu.Item as="div"> | |
| <NavLink | |
| className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700" | |
| svg={() => <GearIcon />} | |
| text={localize('com_nav_settings')} | |
| clickHandler={() => setShowSettings(true)} | |
| /> | |
| </Menu.Item> | |
| <div className="my-1.5 h-px bg-white/20" role="none" /> | |
| <Menu.Item as="div"> | |
| <Logout /> | |
| </Menu.Item> | |
| </Menu.Items> | |
| </Transition> | |
| </> | |
| )} | |
| </Menu> | |
| {showExports && <ExportModel open={showExports} onOpenChange={setShowExports} />} | |
| {showClearConvos && <ClearConvos open={showClearConvos} onOpenChange={setShowClearConvos} />} | |
| {showSettings && <Settings open={showSettings} onOpenChange={setShowSettings} />} | |
| </> | |
| ); | |
| } | |