175 lines
6.2 KiB
TypeScript
175 lines
6.2 KiB
TypeScript
"use client";
|
||
|
||
import { Building2, Check, LogOut, Plus, Sparkles } from "lucide-react";
|
||
|
||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||
import {
|
||
DropdownMenu,
|
||
DropdownMenuContent,
|
||
DropdownMenuItem,
|
||
DropdownMenuLabel,
|
||
DropdownMenuSeparator,
|
||
DropdownMenuTrigger,
|
||
} from "@/components/ui/dropdown-menu";
|
||
import {
|
||
SidebarMenu,
|
||
SidebarMenuButton,
|
||
SidebarMenuItem,
|
||
useSidebar,
|
||
} from "@/components/ui/sidebar";
|
||
import { useAuth } from "@/components/auth/auth-provider";
|
||
import type { Workspace } from "@/lib/types/api";
|
||
import { DEMO_WORKSPACE_ID } from "@/lib/demo";
|
||
|
||
export function NavUser({
|
||
user,
|
||
workspaceSwitcher,
|
||
}: {
|
||
user: {
|
||
name: string;
|
||
email: string;
|
||
avatar: string;
|
||
};
|
||
workspaceSwitcher?: {
|
||
workspaces: Workspace[];
|
||
currentWorkspaceId?: string;
|
||
onSelect: (workspace: Workspace) => void;
|
||
onCreate?: () => void;
|
||
};
|
||
}) {
|
||
const { isMobile } = useSidebar();
|
||
const { logout } = useAuth();
|
||
|
||
const handleLogout = () => {
|
||
logout();
|
||
};
|
||
|
||
const getInitials = (name: string) => {
|
||
return name
|
||
.split(" ")
|
||
.map((n) => n[0])
|
||
.join("")
|
||
.toUpperCase()
|
||
.slice(0, 2);
|
||
};
|
||
|
||
return (
|
||
<SidebarMenu>
|
||
<SidebarMenuItem>
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<SidebarMenuButton
|
||
size="lg"
|
||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||
>
|
||
<Avatar className="h-8 w-8 rounded-lg">
|
||
<AvatarImage src={user.avatar} alt={user.name} />
|
||
<AvatarFallback className="rounded-lg">
|
||
{getInitials(user.name)}
|
||
</AvatarFallback>
|
||
</Avatar>
|
||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||
<span className="truncate font-medium">{user.name}</span>
|
||
<span className="text-muted-foreground truncate text-xs">
|
||
{user.email}
|
||
</span>
|
||
</div>
|
||
</SidebarMenuButton>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
className="w-56 rounded-lg"
|
||
side={isMobile ? "bottom" : "right"}
|
||
align="end"
|
||
sideOffset={4}
|
||
>
|
||
<DropdownMenuLabel className="p-0 font-normal">
|
||
<div className="flex items-center gap-2 px-2 py-1.5 text-left text-sm">
|
||
<Avatar className="h-8 w-8 rounded-lg">
|
||
<AvatarImage src={user.avatar} alt={user.name} />
|
||
<AvatarFallback className="rounded-lg">
|
||
{getInitials(user.name)}
|
||
</AvatarFallback>
|
||
</Avatar>
|
||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||
<span className="truncate font-medium">{user.name}</span>
|
||
<span className="text-muted-foreground truncate text-xs">
|
||
{user.email}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</DropdownMenuLabel>
|
||
{workspaceSwitcher && (
|
||
<>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
||
Воркспейсы
|
||
</DropdownMenuLabel>
|
||
{workspaceSwitcher.workspaces.length > 0 ? (
|
||
workspaceSwitcher.workspaces.map((workspace) => {
|
||
const isDemo = workspace.id === DEMO_WORKSPACE_ID;
|
||
const isSelected = workspaceSwitcher.currentWorkspaceId === workspace.id;
|
||
return (
|
||
<DropdownMenuItem
|
||
key={workspace.id}
|
||
onClick={() => workspaceSwitcher.onSelect(workspace)}
|
||
className="gap-2 p-2 text-sm"
|
||
>
|
||
<Avatar className="h-6 w-6 rounded-sm">
|
||
<AvatarImage src={workspace.avatar_url || undefined} alt={workspace.name} />
|
||
<AvatarFallback className="rounded-sm text-xs">
|
||
{isDemo ? (
|
||
<Sparkles className="h-3 w-3 text-purple-500" />
|
||
) : (
|
||
workspace.name.charAt(0).toUpperCase()
|
||
)}
|
||
</AvatarFallback>
|
||
</Avatar>
|
||
<span className="flex-1 truncate">
|
||
{workspace.name}
|
||
{isDemo && (
|
||
<span className="ml-2 text-xs text-muted-foreground">
|
||
(Демо)
|
||
</span>
|
||
)}
|
||
</span>
|
||
{isSelected && (
|
||
<Check className="size-4 ml-auto" />
|
||
)}
|
||
</DropdownMenuItem>
|
||
);
|
||
})
|
||
) : (
|
||
<div className="px-2 py-1.5 text-sm text-muted-foreground">
|
||
Нет доступных воркспейсов
|
||
</div>
|
||
)}
|
||
{workspaceSwitcher.onCreate && (
|
||
<>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuItem
|
||
onClick={workspaceSwitcher.onCreate}
|
||
className="gap-2 p-2"
|
||
>
|
||
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
|
||
<Plus className="size-4" />
|
||
</div>
|
||
<span className="text-muted-foreground">
|
||
Создать воркспейс
|
||
</span>
|
||
</DropdownMenuItem>
|
||
</>
|
||
)}
|
||
</>
|
||
)}
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuItem onClick={handleLogout}>
|
||
<LogOut className="mr-2 h-4 w-4" />
|
||
Выйти
|
||
</DropdownMenuItem>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</SidebarMenuItem>
|
||
</SidebarMenu>
|
||
);
|
||
}
|