LibreChat/client/src/components/SidePanel/Nav.tsx
Danny Avila 33b4a97b42
🔒 fix: Agents Config/Permission Checks after Streamline Change (#8089)
* refactor: access control logic to TypeScript

* chore: Change EndpointURLs to a constant object for improved type safety

* 🐛 fix: Enhance agent access control by adding skipAgentCheck functionality

* 🐛 fix: Add endpointFileConfig prop to AttachFileMenu and update file handling logic

* 🐛 fix: Update tool handling logic to support optional groupedTools and improve null checks, add dedicated tool dialog for Assistants

* chore: Export Accordion component from UI index for improved modularity

* feat: Add ActivePanelContext for managing active panel state across components

* chore: Replace string IDs with EModelEndpoint constants for assistants and agents in useSideNavLinks

* fix: Integrate access checks for agent creation and deletion routes in actions.js
2025-06-26 18:53:05 -04:00

109 lines
4.8 KiB
TypeScript

import * as AccordionPrimitive from '@radix-ui/react-accordion';
import type { NavLink, NavProps } from '~/common';
import { AccordionContent, AccordionItem, TooltipAnchor, Accordion, Button } from '~/components/ui';
import { ActivePanelProvider, useActivePanel } from '~/Providers';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
function NavContent({ links, isCollapsed, resize }: Omit<NavProps, 'defaultActive'>) {
const localize = useLocalize();
const { active, setActive } = useActivePanel();
const getVariant = (link: NavLink) => (link.id === active ? 'default' : 'ghost');
return (
<div
data-collapsed={isCollapsed}
className="bg-token-sidebar-surface-primary hide-scrollbar group flex-shrink-0 overflow-x-hidden"
>
<div className="h-full">
<div className="flex h-full min-h-0 flex-col">
<div className="flex h-full min-h-0 flex-col opacity-100 transition-opacity">
<div className="scrollbar-trigger relative h-full w-full flex-1 items-start border-white/20">
<div className="flex h-full w-full flex-col gap-1 px-3 py-2.5 group-[[data-collapsed=true]]:items-center group-[[data-collapsed=true]]:justify-center group-[[data-collapsed=true]]:px-2">
{links.map((link, index) => {
const variant = getVariant(link);
return isCollapsed ? (
<TooltipAnchor
description={localize(link.title)}
side="left"
key={`nav-link-${index}`}
render={
<Button
variant="ghost"
size="icon"
onClick={(e) => {
if (link.onClick) {
link.onClick(e);
setActive('');
return;
}
setActive(link.id);
resize && resize(25);
}}
>
<link.icon className="h-4 w-4 text-text-secondary" />
<span className="sr-only">{localize(link.title)}</span>
</Button>
}
/>
) : (
<Accordion
key={index}
type="single"
value={active}
onValueChange={setActive}
collapsible
>
<AccordionItem value={link.id} className="w-full border-none">
<AccordionPrimitive.Header asChild>
<AccordionPrimitive.Trigger asChild>
<Button
variant="outline"
size="sm"
className="w-full justify-start bg-transparent text-text-secondary data-[state=open]:bg-surface-secondary data-[state=open]:text-text-primary"
onClick={(e) => {
if (link.onClick) {
link.onClick(e);
setActive('');
}
}}
>
<link.icon className="mr-2 h-4 w-4" />
{localize(link.title)}
{link.label != null && link.label && (
<span
className={cn(
'ml-auto opacity-100 transition-all duration-300 ease-in-out',
variant === 'default' ? 'text-text-primary' : '',
)}
>
{link.label}
</span>
)}
</Button>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
<AccordionContent className="w-full text-text-primary">
{link.Component && <link.Component />}
</AccordionContent>
</AccordionItem>
</Accordion>
);
})}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default function Nav({ links, isCollapsed, resize, defaultActive }: NavProps) {
return (
<ActivePanelProvider defaultActive={defaultActive}>
<NavContent links={links} isCollapsed={isCollapsed} resize={resize} />
</ActivePanelProvider>
);
}