Files
tgex-frontend/components/project-selector.tsx
2026-02-10 17:15:38 +03:00

158 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
// ============================================================================
// Project Selector (Multi-select)
// ============================================================================
import * as React from "react";
import { Check, ChevronsUpDown, Tv, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu";
import { Checkbox } from "@/components/ui/checkbox";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { useWorkspace } from "@/components/providers/workspace-provider";
import { cn } from "@/lib/utils";
export const ProjectSelector: React.FC = () => {
const {
projects,
selectedProjects,
toggleProject,
selectAllProjects,
clearProjectSelection,
isLoadingProjects,
} = useWorkspace();
if (isLoadingProjects) {
return (
<div className="w-[240px] h-10 rounded-lg border border-input bg-background animate-pulse" />
);
}
if (projects.length === 0) {
return (
<div className="text-sm text-muted-foreground px-3 py-2">
Нет проектов
</div>
);
}
const allSelected = selectedProjects.length === projects.length;
const getAvatarUrl = (project: typeof projects[0]) => {
return project.username
? `https://t.me/i/userpic/320/${project.username}.jpg`
: null;
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-[240px] h-10 px-3 rounded-lg justify-start gap-2"
>
{selectedProjects.length === 0 ? (
<>
<span className="text-muted-foreground">Все проекты</span>
</>
) : selectedProjects.length === 1 ? (
<>
<Avatar className="h-6 w-6 shrink-0">
<AvatarImage src={getAvatarUrl(selectedProjects[0]) || undefined} />
<AvatarFallback className="text-xs">
{selectedProjects[0].title.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
<span className="truncate max-w-[180px]">
{selectedProjects[0].title}
</span>
</>
) : allSelected ? (
<>
<span className="font-medium">Все проекты</span>
</>
) : (
<>
<Avatar className="h-6 w-6 shrink-0">
<AvatarFallback className="text-xs">
{selectedProjects.length}
</AvatarFallback>
</Avatar>
<span className="truncate max-w-[180px]">
{selectedProjects.length} проекта
</span>
</>
)}
<ChevronsUpDown className="h-4 w-4 shrink-0 opacity-50 ml-auto" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[240px] p-2" align="start">
{/* Quick actions */}
<div className="flex gap-2 mb-2">
<Button
variant="ghost"
size="sm"
className="flex-1 text-xs"
onClick={selectAllProjects}
disabled={allSelected}
>
Выбрать все
</Button>
<Button
variant="ghost"
size="sm"
className="flex-1 text-xs"
onClick={clearProjectSelection}
disabled={selectedProjects.length <= 1}
>
Сбросить
</Button>
</div>
<DropdownMenuSeparator />
{/* Projects list */}
<div className="max-h-[300px] overflow-y-auto">
{projects.map((project) => {
const isSelected = selectedProjects.some((p) => p.id === project.id);
const isOnlyOne = selectedProjects.length === 1 && isSelected;
return (
<div
key={project.id}
className={cn(
"flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer hover:bg-accent",
isSelected && "bg-accent/50"
)}
onClick={() => !isOnlyOne && toggleProject(project)}
>
<Checkbox
checked={isSelected}
disabled={isOnlyOne}
onCheckedChange={() => !isOnlyOne && toggleProject(project)}
className="shrink-0"
/>
<Avatar className="h-5 w-5 shrink-0">
<AvatarImage src={getAvatarUrl(project) || undefined} />
<AvatarFallback className="text-[10px]">
{project.title.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
<span className="truncate text-sm">{project.title}</span>
</div>
);
})}
</div>
</DropdownMenuContent>
</DropdownMenu>
);
};