import { useState, useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { ChevronsUpDown, Folder, Plus, FolderOpen } from 'lucide-react'; import * as LucideIcons from 'lucide-react'; import type { LucideIcon } from 'lucide-react'; import { cn, isMac } from '@/lib/utils'; import { formatShortcut } from '@/store/app-store'; import { isElectron, type Project } from '@/lib/electron'; import { getAuthenticatedImageUrl } from '@/lib/api-fetch'; import { useAppStore } from '@/store/app-store'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; interface SidebarHeaderProps { sidebarOpen: boolean; currentProject: Project | null; onNewProject: () => void; onOpenFolder: () => void; onProjectContextMenu: (project: Project, event: React.MouseEvent) => void; } export function SidebarHeader({ sidebarOpen, currentProject, onNewProject, onOpenFolder, onProjectContextMenu, }: SidebarHeaderProps) { const navigate = useNavigate(); const { projects, setCurrentProject } = useAppStore(); const [dropdownOpen, setDropdownOpen] = useState(false); const handleLogoClick = useCallback(() => { navigate({ to: '/overview' }); }, [navigate]); const handleProjectSelect = useCallback( (project: Project) => { setCurrentProject(project); setDropdownOpen(false); navigate({ to: '/board' }); }, [setCurrentProject, navigate] ); const getIconComponent = (project: Project): LucideIcon => { if (project.icon && project.icon in LucideIcons) { return (LucideIcons as unknown as Record)[project.icon]; } return Folder; }; const renderProjectIcon = (project: Project, size: 'sm' | 'md' = 'md') => { const IconComponent = getIconComponent(project); const sizeClasses = size === 'sm' ? 'w-6 h-6' : 'w-8 h-8'; const iconSizeClasses = size === 'sm' ? 'w-4 h-4' : 'w-5 h-5'; if (project.customIconPath) { return ( {project.name} ); } return (
); }; // Collapsed state - show logo only if (!sidebarOpen) { return (
Go to Dashboard {/* Collapsed project icon with dropdown */} {currentProject && ( <>
{currentProject.name}
Projects
{projects.map((project, index) => { const isActive = currentProject?.id === project.id; const hotkeyLabel = index < 9 ? `${index + 1}` : index === 9 ? '0' : undefined; return ( handleProjectSelect(project)} onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); setDropdownOpen(false); onProjectContextMenu(project, e); }} className="flex items-center gap-3 cursor-pointer" data-testid={`collapsed-project-item-${project.id}`} > {renderProjectIcon(project, 'sm')} {project.name} {hotkeyLabel && ( {formatShortcut(`Cmd+${hotkeyLabel}`, true)} )} ); })} { setDropdownOpen(false); onNewProject(); }} className="cursor-pointer" data-testid="collapsed-new-project-dropdown-item" > New Project { setDropdownOpen(false); onOpenFolder(); }} className="cursor-pointer" data-testid="collapsed-open-project-dropdown-item" > Open Project
)}
); } // Expanded state - show logo + project dropdown return (
{/* Header with logo and project dropdown */}
{/* Logo */} {/* Project Dropdown */} {currentProject ? (
Projects
{projects.map((project, index) => { const isActive = currentProject?.id === project.id; const hotkeyLabel = index < 9 ? `${index + 1}` : index === 9 ? '0' : undefined; return ( handleProjectSelect(project)} onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); setDropdownOpen(false); onProjectContextMenu(project, e); }} className="flex items-center gap-3 cursor-pointer" data-testid={`project-item-${project.id}`} > {renderProjectIcon(project, 'sm')} {project.name} {hotkeyLabel && ( {formatShortcut(`Cmd+${hotkeyLabel}`, true)} )} ); })} { setDropdownOpen(false); onNewProject(); }} className="cursor-pointer" data-testid="new-project-dropdown-item" > New Project { setDropdownOpen(false); onOpenFolder(); }} className="cursor-pointer" data-testid="open-project-dropdown-item" > Open Project
) : (
)}
); }