Sifa professional network frontend (Next.js, React, TailwindCSS) sifa.id/
at main 73 lines 2.3 kB view raw
1'use client'; 2 3import { useState, useCallback } from 'react'; 4import { X } from '@phosphor-icons/react'; 5import { SkillCombobox } from '@/components/skill-combobox'; 6import { Badge } from '@/components/ui/badge'; 7import type { ProfileSkill } from '@/lib/types'; 8 9interface PositionSkillEditorProps { 10 linkedSkills: ProfileSkill[]; 11 /** All skills on the user's profile, for combobox suggestions. */ 12 profileSkills?: ProfileSkill[]; 13 onAdd: (skillName: string, category: string) => void; 14 onRemove: (rkey: string) => void; 15} 16 17export function PositionSkillEditor({ 18 linkedSkills, 19 profileSkills, 20 onAdd, 21 onRemove, 22}: PositionSkillEditorProps) { 23 const [query, setQuery] = useState(''); 24 const [category, setCategory] = useState(''); 25 26 const handleChange = useCallback((skillName: string, cat: string) => { 27 setQuery(skillName); 28 setCategory(cat); 29 }, []); 30 31 const handleSelect = useCallback( 32 (skillName: string, cat: string) => { 33 if (skillName.trim().length < 2) return; 34 onAdd(skillName.trim(), cat); 35 setQuery(''); 36 setCategory(''); 37 }, 38 [onAdd], 39 ); 40 41 return ( 42 <div> 43 <label htmlFor="position-skill-search" className="mb-1 block text-sm font-medium"> 44 Skills used 45 </label> 46 {linkedSkills.length > 0 && ( 47 <div className="mb-2 flex flex-wrap gap-1.5" role="list" aria-label="Linked skills"> 48 {linkedSkills.map((skill) => ( 49 <Badge key={skill.rkey} variant="secondary" className="gap-1" role="listitem"> 50 {skill.name} 51 <button 52 type="button" 53 className="inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-destructive/20 hover:text-destructive" 54 onClick={() => onRemove(skill.rkey)} 55 aria-label={`Remove ${skill.name}`} 56 > 57 <X className="h-2.5 w-2.5" weight="bold" aria-hidden="true" /> 58 </button> 59 </Badge> 60 ))} 61 </div> 62 )} 63 <SkillCombobox 64 id="position-skill-search" 65 value={query} 66 category={category} 67 onChange={handleChange} 68 onSelect={handleSelect} 69 profileSkills={profileSkills} 70 /> 71 </div> 72 ); 73}