pmtool / src /components /custom /team-selector.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
5.7 kB
import React, { useState, useRef, useEffect } from "react";
import { Check, ChevronsUpDown, Search, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { Team } from "@/types";
interface TeamSelectorProps {
teams: Team[];
selectedTeam: string;
onSelect: (teamId: string) => void;
placeholder?: string;
disabled?: boolean;
className?: string;
}
const TeamSelector = ({
teams,
selectedTeam,
onSelect,
placeholder = "Select a team",
disabled = false,
className
}: TeamSelectorProps) => {
const [isOpen, setIsOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const dropdownRef = useRef<HTMLDivElement>(null);
// Find the currently selected team
const selected = teams.find(team => team.id.toString() === selectedTeam);
// Filter teams based on search query
const filteredTeams = teams.filter(team =>
team.name.toLowerCase().includes(searchQuery.toLowerCase())
);
// Handle click outside to close dropdown
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
// Handle selecting a team
const handleSelectTeam = (teamId: string) => {
onSelect(teamId);
setIsOpen(false);
setSearchQuery("");
};
// Handle clearing selection
const handleClearSelection = (e: React.MouseEvent) => {
e.stopPropagation();
onSelect("");
};
return (
<div className={cn("relative w-full", className)} ref={dropdownRef}>
<Button
type="button"
variant="outline"
role="combobox"
aria-expanded={isOpen}
className={cn(
"w-full justify-between font-normal",
!selectedTeam && "text-muted-foreground"
)}
onClick={() => setIsOpen(!isOpen)}
disabled={disabled}
>
{selected ? (
<div className="flex items-center gap-2">
<Avatar className="h-6 w-6">
<AvatarFallback>
{selected.name.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
<span>{selected.name}</span>
</div>
) : (
<span>{placeholder}</span>
)}
<div className="flex items-center">
{selectedTeam && (
<Button
variant="ghost"
size="sm"
className="h-4 w-4 p-0 mr-1 opacity-70 hover:opacity-100"
onClick={handleClearSelection}
>
<X className="h-3 w-3" />
<span className="sr-only">Clear selection</span>
</Button>
)}
<ChevronsUpDown className="h-4 w-4 opacity-50" />
</div>
</Button>
{isOpen && (
<div className="absolute top-full z-[9999] mt-1 w-full rounded-md border border-input bg-popover shadow-md animate-in fade-in-0 zoom-in-95">
<div className="flex items-center border-b px-3 py-2">
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<Input
placeholder="Search teams..."
className="flex h-8 w-full rounded-md bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 border-0 focus-visible:ring-0 focus-visible:ring-offset-0"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="max-h-[15rem] overflow-y-auto py-1">
{filteredTeams.length === 0 ? (
<div className="px-2 py-2 text-sm text-center text-muted-foreground">
No teams found
</div>
) : (
filteredTeams.map(team => (
<div
key={team.id}
className={cn(
"flex items-center justify-between px-3 py-2 text-sm cursor-pointer hover:bg-accent",
team.id.toString() === selectedTeam && "bg-accent/50"
)}
onClick={() => handleSelectTeam(team.id.toString())}
>
<div className="flex items-center gap-2">
<Avatar className="h-6 w-6">
<AvatarFallback>
{team.name.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
<div>
<p className="font-medium">{team.name}</p>
{team.description && (
<p className="text-xs text-muted-foreground truncate max-w-[200px]">
{team.description}
</p>
)}
</div>
</div>
{team.id.toString() === selectedTeam && (
<Check className="h-4 w-4 text-primary" />
)}
</div>
))
)}
</div>
</div>
)}
</div>
);
};
export default TeamSelector;