84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState } from 'react';
|
||
|
|
import { useDroppable } from '@dnd-kit/core';
|
||
|
|
import { Search, UserX } from 'lucide-react';
|
||
|
|
import { Input } from '@/components/ui/input';
|
||
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||
|
|
import { cn } from '@/lib/utils';
|
||
|
|
import { EmployeeCard } from './EmployeeCard';
|
||
|
|
import type { OrgEmployee } from './types';
|
||
|
|
|
||
|
|
interface UnassignedPanelProps {
|
||
|
|
employees: OrgEmployee[];
|
||
|
|
onAssignClick: (employee: OrgEmployee) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function UnassignedPanel({ employees, onAssignClick }: UnassignedPanelProps) {
|
||
|
|
const [searchQuery, setSearchQuery] = useState('');
|
||
|
|
|
||
|
|
const { setNodeRef, isOver } = useDroppable({
|
||
|
|
id: 'unassigned-zone',
|
||
|
|
data: { type: 'unassigned-zone' },
|
||
|
|
});
|
||
|
|
|
||
|
|
const filtered = searchQuery.trim()
|
||
|
|
? employees.filter((e) => {
|
||
|
|
const q = searchQuery.toLowerCase();
|
||
|
|
return (
|
||
|
|
e.displayName.toLowerCase().includes(q) ||
|
||
|
|
(e.positionLabel && e.positionLabel.toLowerCase().includes(q))
|
||
|
|
);
|
||
|
|
})
|
||
|
|
: employees;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
ref={setNodeRef}
|
||
|
|
className={cn(
|
||
|
|
'w-full md:w-72 shrink-0 flex flex-col border rounded-lg bg-white transition-colors h-full',
|
||
|
|
isOver && 'bg-orange-50 ring-2 ring-orange-300',
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
<div className="p-2 md:p-3 border-b">
|
||
|
|
<div className="flex items-center gap-2 mb-1.5 md:mb-2">
|
||
|
|
<UserX className="h-4 w-4 text-orange-500 shrink-0" />
|
||
|
|
<span className="font-semibold text-xs md:text-sm">미배치 직원</span>
|
||
|
|
<span className="text-[10px] md:text-xs text-muted-foreground ml-auto">{employees.length}명</span>
|
||
|
|
</div>
|
||
|
|
<div className="relative">
|
||
|
|
<Search className="absolute left-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
|
||
|
|
<Input
|
||
|
|
placeholder="이름, 직급 검색..."
|
||
|
|
value={searchQuery}
|
||
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||
|
|
className="h-7 md:h-8 pl-7 md:pl-8 text-xs md:text-sm"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex-1 min-h-0">
|
||
|
|
<ScrollArea className="h-full">
|
||
|
|
<div className="p-1.5 md:p-2 space-y-1">
|
||
|
|
{filtered.length > 0 ? (
|
||
|
|
filtered.map((emp) => (
|
||
|
|
<EmployeeCard
|
||
|
|
key={emp.id}
|
||
|
|
employee={emp}
|
||
|
|
sourceDeptId={null}
|
||
|
|
showAssignButton
|
||
|
|
onAssignClick={onAssignClick}
|
||
|
|
/>
|
||
|
|
))
|
||
|
|
) : (
|
||
|
|
<div className="text-center text-xs md:text-sm text-muted-foreground py-6 md:py-8">
|
||
|
|
{searchQuery ? '검색 결과가 없습니다' : '미배치 직원이 없습니다'}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</ScrollArea>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|