feat: global platform v2 update

This commit is contained in:
ivannoskov
2025-12-29 10:12:13 +03:00
parent 8e6a1fa83f
commit f64cf02100
84 changed files with 11023 additions and 11486 deletions

View File

@@ -0,0 +1,147 @@
"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 { 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-11 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;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="w-[280px] h-auto min-h-[44px] py-2 px-3 rounded-lg justify-between"
>
<div className="flex flex-col items-start text-left overflow-hidden">
{selectedProjects.length === 0 ? (
<span className="text-muted-foreground">Выберите проекты</span>
) : selectedProjects.length === 1 ? (
<>
<span className="font-semibold text-base truncate max-w-[220px]">
{selectedProjects[0].title}
</span>
{selectedProjects[0].username && (
<span className="text-xs text-muted-foreground -mt-0.5">
@{selectedProjects[0].username}
</span>
)}
</>
) : allSelected ? (
<span className="font-semibold text-base">Все проекты</span>
) : (
<>
<span className="font-semibold text-base">
{selectedProjects.length} проекта
</span>
<span className="text-xs text-muted-foreground -mt-0.5 truncate max-w-[220px]">
{selectedProjects.map((p) => p.title).join(", ")}
</span>
</>
)}
</div>
<ChevronsUpDown className="h-4 w-4 shrink-0 opacity-50" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[280px] 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-3 px-2 py-2 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"
/>
<Tv className="h-4 w-4 text-muted-foreground shrink-0" />
<div className="flex flex-col min-w-0 flex-1">
<span className="font-medium truncate">{project.title}</span>
{project.username && (
<span className="text-xs text-muted-foreground">
@{project.username}
</span>
)}
</div>
</div>
);
})}
</div>
</DropdownMenuContent>
</DropdownMenu>
);
};