diff --git a/surfsense_web/components/layout/ui/sidebar/NavSection.tsx b/surfsense_web/components/layout/ui/sidebar/NavSection.tsx index 18ff24e4d..3eb66f3cd 100644 --- a/surfsense_web/components/layout/ui/sidebar/NavSection.tsx +++ b/surfsense_web/components/layout/ui/sidebar/NavSection.tsx @@ -2,9 +2,9 @@ import { CheckCircle2, CircleAlert } from "lucide-react"; import { Spinner } from "@/components/ui/spinner"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import type { NavItem } from "../../types/layout.types"; +import { SidebarButton } from "./SidebarButton"; interface NavSectionProps { items: NavItem[]; @@ -66,71 +66,48 @@ function StatusIcon({ return ; } +function CollapsedOverlay({ item }: { item: NavItem }) { + const indicator = item.statusIndicator; + if (indicator && indicator !== "idle") { + return ; + } + if (item.badge) { + return ( + + {item.badge} + + ); + } + return null; +} + export function NavSection({ items, onItemClick, isCollapsed = false }: NavSectionProps) { return (
{items.map((item) => { - const Icon = item.icon; - const indicator = item.statusIndicator; - const joyrideAttr = item.title === "Inbox" || item.title.toLowerCase().includes("inbox") - ? { "data-joyride": "inbox-sidebar" } + ? { "data-joyride": "inbox-sidebar" as const } : {}; - if (isCollapsed) { - return ( - - - - - - {item.title} - {item.badge && ` (${item.badge})`} - - - ); - } - return ( - + isCollapsed={isCollapsed} + badge={item.badge} + collapsedOverlay={} + expandedIconNode={ + + } + buttonProps={joyrideAttr} + /> ); })}
diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx index 1db8f706b..cbc7bbbee 100644 --- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx +++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx @@ -3,15 +3,14 @@ import { PenSquare } from "lucide-react"; import { useTranslations } from "next-intl"; import { useState } from "react"; -import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { SIDEBAR_MIN_WIDTH } from "../../hooks/useSidebarResize"; import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types"; import { ChatListItem } from "./ChatListItem"; import { NavSection } from "./NavSection"; import { PageUsageDisplay } from "./PageUsageDisplay"; +import { SidebarButton } from "./SidebarButton"; import { SidebarCollapseButton } from "./SidebarCollapseButton"; import { SidebarHeader } from "./SidebarHeader"; import { SidebarSection } from "./SidebarSection"; @@ -132,23 +131,13 @@ export function Sidebar({ )} {/* New chat button */} -
- {isCollapsed ? ( - - - - - {t("new_chat")} - - ) : ( - - )} +
+
{/* Chat sections - fills available space */} diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarButton.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarButton.tsx new file mode 100644 index 000000000..b40919eae --- /dev/null +++ b/surfsense_web/components/layout/ui/sidebar/SidebarButton.tsx @@ -0,0 +1,86 @@ +"use client"; + +import type { LucideIcon } from "lucide-react"; +import type React from "react"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; + +interface SidebarButtonProps { + icon: LucideIcon; + label: string; + onClick?: () => void; + isCollapsed?: boolean; + badge?: React.ReactNode; + /** Overlay in the top-right corner of the collapsed icon (e.g. status badge) */ + collapsedOverlay?: React.ReactNode; + /** Custom icon node for expanded mode — overrides the default rendering */ + expandedIconNode?: React.ReactNode; + className?: string; + /** Extra attributes spread onto the inner + + + {label} + {typeof badge === "string" && ` (${badge})`} + + + ); + } + + return ( + + ); +}