"use client"; import * as React from "react"; import { useState, useMemo } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { ArrowUpDown, Search, Trash2 } from "lucide-react"; import { VendorPossibleMaterial, removeConfirmedMaterials } from "../vendor-possible-material-service"; import { AddConfirmedMaterial } from "./add-confirmed-material"; import { toast } from "sonner"; import { useTransition } from "react"; // formatDate 함수 제거 - YYYY-MM-DD 형식으로 직접 포맷 interface SimpleVendorMaterialsProps { vendorId: number; confirmedMaterials: VendorPossibleMaterial[]; vendorInputMaterials: VendorPossibleMaterial[]; onDataRefresh?: () => void; } type SortField = "itemCode" | "itemName" | "createdAt" | "recentPoDate" | "recentDeliveryDate" | "vendorTypeNameEn" | "recentPoNo" | "registerUserName"; type SortDirection = "asc" | "desc"; interface SortState { field: SortField | null; direction: SortDirection; } export function SimpleVendorMaterials({ vendorId, confirmedMaterials, vendorInputMaterials, onDataRefresh, }: SimpleVendorMaterialsProps) { // 날짜를 YYYY-MM-DD 형식으로 포맷하는 함수 const formatDateToYMD = (date: Date | null | undefined): string => { if (!date) return "-"; return date.toISOString().split('T')[0]; }; // 선택 상태 관리 const [selectedConfirmedIds, setSelectedConfirmedIds] = useState([]); const [isDeletePending, startDeleteTransition] = useTransition(); const [confirmedSearch, setConfirmedSearch] = useState(""); const [vendorInputSearch, setVendorInputSearch] = useState(""); const [confirmedSort, setConfirmedSort] = useState({ field: null, direction: "asc" }); const [vendorInputSort, setVendorInputSort] = useState({ field: null, direction: "asc" }); // 정렬 함수 const sortMaterials = (materials: VendorPossibleMaterial[], sortState: SortState) => { if (!sortState.field) return materials; return [...materials].sort((a, b) => { let aValue: string | Date; let bValue: string | Date; switch (sortState.field) { case "itemCode": aValue = a.itemCode || ""; bValue = b.itemCode || ""; break; case "itemName": aValue = a.itemName || ""; bValue = b.itemName || ""; break; case "createdAt": aValue = a.createdAt; bValue = b.createdAt; break; case "recentPoDate": aValue = a.recentPoDate || new Date(0); bValue = b.recentPoDate || new Date(0); break; case "recentDeliveryDate": aValue = a.recentDeliveryDate || new Date(0); bValue = b.recentDeliveryDate || new Date(0); break; case "vendorTypeNameEn": aValue = a.vendorTypeNameEn || ""; bValue = b.vendorTypeNameEn || ""; break; case "recentPoNo": aValue = a.recentPoNo || ""; bValue = b.recentPoNo || ""; break; case "registerUserName": aValue = a.registerUserName || ""; bValue = b.registerUserName || ""; break; default: return 0; } if (aValue < bValue) return sortState.direction === "asc" ? -1 : 1; if (aValue > bValue) return sortState.direction === "asc" ? 1 : -1; return 0; }); }; // 검색 필터링 함수 const filterMaterials = (materials: VendorPossibleMaterial[], searchTerm: string) => { if (!searchTerm.trim()) return materials; const lowercaseSearch = searchTerm.toLowerCase(); return materials.filter(material => (material.itemCode?.toLowerCase().includes(lowercaseSearch)) || (material.itemName?.toLowerCase().includes(lowercaseSearch)) ); }; const filteredAndSortedConfirmed = useMemo(() => { const filtered = filterMaterials(confirmedMaterials, confirmedSearch); return sortMaterials(filtered, confirmedSort); }, [confirmedMaterials, confirmedSearch, confirmedSort]); const filteredAndSortedVendorInput = useMemo(() => { const filtered = filterMaterials(vendorInputMaterials, vendorInputSearch); return sortMaterials(filtered, vendorInputSort); }, [vendorInputMaterials, vendorInputSearch, vendorInputSort]); // 선택 상태 관리 함수들 (필터링된 데이터 이후에 선언) const handleSelectAllConfirmed = (checked: boolean) => { if (checked) { const allIds = filteredAndSortedConfirmed.map(material => material.id); setSelectedConfirmedIds(allIds); } else { setSelectedConfirmedIds([]); } }; // 데이터 변경 시 선택 상태 정리 (유효하지 않은 ID들 제거) React.useEffect(() => { const validIds = confirmedMaterials.map(material => material.id); setSelectedConfirmedIds(prev => prev.filter(id => validIds.includes(id))); }, [confirmedMaterials]); // 전체 선택 체크박스의 상태 계산 const isAllSelected = filteredAndSortedConfirmed.length > 0 && selectedConfirmedIds.length === filteredAndSortedConfirmed.length && filteredAndSortedConfirmed.every(material => selectedConfirmedIds.includes(material.id)); const isIndeterminate = selectedConfirmedIds.length > 0 && selectedConfirmedIds.length < filteredAndSortedConfirmed.length; const handleSelectConfirmed = (id: number, checked: boolean) => { if (checked) { setSelectedConfirmedIds(prev => [...prev, id]); } else { setSelectedConfirmedIds(prev => prev.filter(selectedId => selectedId !== id)); } }; // 삭제 함수 const handleDeleteSelected = () => { if (selectedConfirmedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; } startDeleteTransition(async () => { const result = await removeConfirmedMaterials(vendorId, selectedConfirmedIds); if (result.success) { toast.success(result.message); setSelectedConfirmedIds([]); // 선택 상태 초기화 onDataRefresh?.(); // 데이터 새로고침 } else { toast.error(result.message); } }); }; // 정렬 핸들러 const handleSort = ( field: SortField, currentSort: SortState, setSort: React.Dispatch> ) => { setSort(prev => ({ field, direction: prev.field === field && prev.direction === "asc" ? "desc" : "asc" })); }; // 정렬 버튼 컴포넌트 const SortButton = ({ field, children, onSort }: { field: SortField; children: React.ReactNode; onSort: (field: SortField) => void; }) => ( ); return (
{/* 확정정보 테이블 */}
확정정보 구매담당자가 확정한 공급 품목 정보입니다.
{selectedConfirmedIds.length > 0 && ( )}
setConfirmedSearch(e.target.value)} className="max-w-sm" />
{ if (el && 'indeterminate' in el) { el.indeterminate = isIndeterminate; } }} onCheckedChange={handleSelectAllConfirmed} aria-label="전체 선택" /> handleSort(field, confirmedSort, setConfirmedSort)} > 업체유형 handleSort(field, confirmedSort, setConfirmedSort)} > 자재그룹코드 handleSort(field, confirmedSort, setConfirmedSort)} > 자재그룹명 handleSort(field, confirmedSort, setConfirmedSort)} > 최근 Po No. handleSort(field, confirmedSort, setConfirmedSort)} > 최근 Po일 handleSort(field, confirmedSort, setConfirmedSort)} > 최근 납품일 handleSort(field, confirmedSort, setConfirmedSort)} > 등록일 handleSort(field, confirmedSort, setConfirmedSort)} > 등록자명 {filteredAndSortedConfirmed.length === 0 ? ( {confirmedSearch ? "검색 결과가 없습니다." : "확정된 공급품목이 없습니다."} ) : ( filteredAndSortedConfirmed.map((material) => ( handleSelectConfirmed(material.id, checked as boolean)} aria-label={`자재 ${material.itemCode} 선택`} /> {material.vendorTypeNameEn || "-"} {material.itemCode || "-"} {material.itemName || "-"} {material.recentPoNo || "-"} {formatDateToYMD(material.recentPoDate)} {formatDateToYMD(material.recentDeliveryDate)} {formatDateToYMD(material.createdAt)} {material.registerUserName || "-"} )) )}
총 {filteredAndSortedConfirmed.length}건
{/* 업체입력정보 테이블 */} 업체입력정보 업체에서 입력한 공급 가능 품목 정보입니다.
setVendorInputSearch(e.target.value)} className="max-w-sm" />
handleSort(field, vendorInputSort, setVendorInputSort)} > 업체유형 handleSort(field, vendorInputSort, setVendorInputSort)} > 자재그룹코드 handleSort(field, vendorInputSort, setVendorInputSort)} > 자재그룹명 handleSort(field, vendorInputSort, setVendorInputSort)} > 등록일 handleSort(field, vendorInputSort, setVendorInputSort)} > 등록자명 {filteredAndSortedVendorInput.length === 0 ? ( {vendorInputSearch ? "검색 결과가 없습니다." : "업체입력 정보가 없습니다."} ) : ( filteredAndSortedVendorInput.map((material) => ( {material.vendorTypeNameEn || "-"} {material.itemCode || "-"} {material.itemName || "-"} {formatDateToYMD(material.createdAt)} {material.registerUserName || "-"} )) )}
총 {filteredAndSortedVendorInput.length}건
); }