KanbanColumn.tsx
1 'use client'; 2 3 import { Bookmark, Send, Users, Trophy, XCircle, MinusCircle, type LucideIcon } from 'lucide-react'; 4 import { useTranslation } from '@/lib/i18n'; 5 import type { JobApplication, JobStatus } from '@/lib/types'; 6 import JobCard from './JobCard'; 7 8 interface KanbanColumnProps { 9 status: JobStatus; 10 label: string; 11 color: string; 12 icon: string; 13 jobs: JobApplication[]; 14 onCardClick: (job: JobApplication) => void; 15 onDragStart: (e: React.DragEvent, jobId: string) => void; 16 onDragOver: (e: React.DragEvent) => void; 17 onDrop: (e: React.DragEvent) => void; 18 isDragOver: boolean; 19 draggingJobId: string | null; 20 } 21 22 const ICON_MAP: Record<string, LucideIcon> = { 23 Bookmark, 24 Send, 25 Users, 26 Trophy, 27 XCircle, 28 MinusCircle, 29 }; 30 31 export default function KanbanColumn({ 32 status, 33 label, 34 color, 35 icon, 36 jobs, 37 onCardClick, 38 onDragStart, 39 onDragOver, 40 onDrop, 41 isDragOver, 42 draggingJobId, 43 }: KanbanColumnProps) { 44 const { t } = useTranslation(); 45 const IconComponent = ICON_MAP[icon]; 46 47 return ( 48 <div className="min-w-[220px] w-full max-w-[300px] flex-shrink-0"> 49 {/* Column header */} 50 <div className="flex items-center gap-2 mb-3 px-1"> 51 {IconComponent && ( 52 <IconComponent size={14} className="flex-shrink-0" style={{ color }} /> 53 )} 54 <span 55 className="inline-block w-2.5 h-2.5 rounded-full flex-shrink-0" 56 style={{ backgroundColor: color }} 57 /> 58 <span className="text-sm font-semibold text-text-primary truncate"> 59 {label} 60 </span> 61 <span 62 className="text-[11px] font-medium px-2 py-0.5 rounded-full flex-shrink-0" 63 style={{ 64 backgroundColor: `${color}14`, 65 color, 66 }} 67 > 68 {jobs.length} 69 </span> 70 </div> 71 72 {/* Column body */} 73 <div 74 onDragOver={onDragOver} 75 onDrop={onDrop} 76 className={`flex flex-col gap-2 p-2 rounded-xl min-h-[200px] transition-all duration-200 ${ 77 isDragOver 78 ? 'border-2 border-dashed border-orange-400 bg-orange-50/50' 79 : 'border-2 border-dashed border-transparent bg-black/[0.02]' 80 }`} 81 > 82 {jobs.map((job) => ( 83 <JobCard 84 key={job.id} 85 job={job} 86 onClick={() => onCardClick(job)} 87 onDragStart={onDragStart} 88 isDragging={draggingJobId === job.id} 89 /> 90 ))} 91 92 {jobs.length === 0 && !isDragOver && ( 93 <div className="flex items-center justify-center h-24 text-xs text-text-tertiary"> 94 {t('jobs.emptyColumn')} 95 </div> 96 )} 97 </div> 98 </div> 99 ); 100 }