gitdataai/src/components/layout/sidebar-system.tsx
2026-04-15 09:08:09 +08:00

175 lines
8.7 KiB
TypeScript

import {useTheme} from '@/contexts/theme-context';
import {tryUseWorkspace} from '@/contexts/workspace-context';
import {cn} from '@/lib/utils';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {BookOpen, Box, ChevronDown, Compass, Home, LayoutGrid, Monitor, Moon, Plus, Sun, Users} from 'lucide-react';
import {useNavigate} from 'react-router-dom';
import {Avatar, AvatarFallback, AvatarImage} from '@/components/ui/avatar';
const btnClass = 'flex w-full h-9 justify-start items-center rounded-md font-medium hover:bg-muted cursor-pointer bg-transparent border-0 text-left text-sm';
export function SidebarSystem({collapsed}: {collapsed: boolean}) {
const {theme, setTheme} = useTheme();
const navigate = useNavigate();
const workspaceCtx = tryUseWorkspace();
const workspaces = workspaceCtx?.workspaces;
const currentWorkspace = workspaceCtx?.currentWorkspace;
return (
<div className="w-full">
{/* Workspace switcher — only shown when inside WorkspaceProvider */}
{workspaceCtx && (
<DropdownMenu>
<DropdownMenuTrigger
render={
<button
type="button"
className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')}
/>
}
>
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
{currentWorkspace ? (
<Avatar className="h-4 w-4">
<AvatarImage src={currentWorkspace.avatar_url || ''}/>
<AvatarFallback className="text-[8px]">
{currentWorkspace.name.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
) : (
<LayoutGrid className="h-4 w-4"/>
)}
</span>
{!collapsed && (
<span className="flex-1 truncate text-sm leading-none">
{currentWorkspace?.name || 'Workspaces'}
</span>
)}
{!collapsed && <ChevronDown className="h-3 w-3 ml-auto shrink-0"/>}
</DropdownMenuTrigger>
<DropdownMenuContent side="right" align="start" className="w-56">
<DropdownMenuGroup>
<DropdownMenuLabel>Workspaces</DropdownMenuLabel>
{workspaces?.workspaces.map((ws) => (
<DropdownMenuItem
key={ws.id}
onClick={() => navigate(`/w/${ws.slug}`)}
className="gap-2"
>
<Avatar className="h-5 w-5">
<AvatarImage src={ws.avatar_url || ''}/>
<AvatarFallback className="text-[9px]">
{ws.name.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
<span className="flex-1 truncate">{ws.name}</span>
<span className="text-xs text-muted-foreground">@{ws.slug}</span>
</DropdownMenuItem>
))}
<DropdownMenuSeparator/>
<DropdownMenuItem onClick={() => navigate('/w/me')} className="gap-2">
<Users className="h-4 w-4"/>
<span>All Workspaces</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => navigate('/init/workspace')} className="gap-2">
<Plus className="h-4 w-4"/>
<span>Create Workspace</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
)}
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => navigate('/')}>
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
<Home className="h-4 w-4"/>
</span>
{!collapsed && <span className="text-sm leading-none">Home</span>}
</button>
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => navigate('/explore')}>
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
<Compass className="h-4 w-4" />
</span>
{!collapsed && <span className="text-sm leading-none">Explore</span>}
</button>
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => navigate('/market')}>
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
<Box className="h-4 w-4" />
</span>
{!collapsed && <span className="text-sm leading-none">Marketplace</span>}
</button>
<button type="button" className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')} onClick={() => window.open('/docs', '_blank')}>
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
<BookOpen className="h-4 w-4" />
</span>
{!collapsed && <span className="text-sm leading-none">Docs</span>}
</button>
<DropdownMenu>
<DropdownMenuTrigger
render={
<button
type="button"
className={cn(btnClass, collapsed ? 'justify-center px-0' : 'px-2')}
/>
}
>
<span className={cn('flex h-6 items-center shrink-0', collapsed ? 'w-6 justify-center' : 'w-6')}>
{theme === 'dark' ? (
<Moon className="h-4 w-4" />
) : theme === 'light' ? (
<Sun className="h-4 w-4" />
) : (
<Monitor className="h-4 w-4" />
)}
</span>
{!collapsed && <span className="text-sm leading-none">Theme</span>}
</DropdownMenuTrigger>
<DropdownMenuContent side="right" align="start">
{!collapsed && (
<DropdownMenuGroup>
<DropdownMenuLabel>Theme Settings</DropdownMenuLabel>
<DropdownMenuItem onClick={() => setTheme('light')}>
<Sun className="mr-2 h-4 w-4" />
<span>Light</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
<Moon className="mr-2 h-4 w-4" />
<span>Dark</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
<Monitor className="mr-2 h-4 w-4" />
<span>System</span>
</DropdownMenuItem>
</DropdownMenuGroup>
)}
{collapsed && (
<>
<DropdownMenuItem onClick={() => setTheme('light')}>
<Sun className="mr-2 h-4 w-4" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>
<Moon className="mr-2 h-4 w-4" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>
<Monitor className="mr-2 h-4 w-4" />
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}