import "./SkillMultiselect.css"; import { useState, useEffect } from "react"; import { fetchDiscoveredSkills } from "../api"; import type { DiscoveredSkill } from "../api"; export interface SkillMultiselectProps { /** Currently selected skill IDs */ value: string[]; /** Called when the selection changes */ onChange: (skills: string[]) => void; /** Project context for API calls */ projectId?: string; /** Disable the control */ disabled?: boolean; /** HTML id for accessibility */ id?: string; /** Label text shown above the component */ label?: string; } /** * Reusable skill multiselect component. * Shows selected skills as removable chips and a dropdown to add more skills. */ export function SkillMultiselect({ value, onChange, projectId, disabled = false, id, label = "Skills", }: SkillMultiselectProps) { const [skills, setSkills] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { let cancelled = false; setIsLoading(true); fetchDiscoveredSkills(projectId) .then((discovered) => { if (!cancelled) { setSkills(discovered); } }) .catch(() => { if (!cancelled) { setSkills([]); } }) .finally(() => { if (!cancelled) { setIsLoading(false); } }); return () => { cancelled = true; }; }, [projectId]); const handleAddSkill = (skillId: string) => { if (!value.includes(skillId)) { onChange([...value, skillId]); } }; const handleRemoveSkill = (skillId: string) => { onChange(value.filter((id) => id !== skillId)); }; // Get skill names for display const getSkillName = (skillId: string): string => { const skill = skills.find((s) => s.id === skillId); return skill?.name ?? skillId; }; // Skills available to add (not yet selected) const availableSkills = skills.filter((s) => !value.includes(s.id)); return (
{label && ( )} {/* Selected skill chips */} {value.length > 0 && (
{value.map((skillId) => ( {getSkillName(skillId)} ))}
)} {/* Dropdown to add skills */}
{isLoading ? ( Loading skills… ) : availableSkills.length === 0 ? ( {value.length === 0 ? "No skills discovered" : "All skills selected"} ) : ( )}
); }