From e5745fc0268bbb5770bc14a55fd58a0ec30b466e Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Wed, 17 Sep 2025 20:17:11 +0900
Subject: (김준회) 기능추가: 벤더 공급품목 확정정보 테이블에 선택&삭제 기능
 추가
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../vendor-material/simple-vendor-materials.tsx    | 138 ++++++++++++++++++---
 1 file changed, 118 insertions(+), 20 deletions(-)

(limited to 'lib/material/vendor-material/simple-vendor-materials.tsx')

diff --git a/lib/material/vendor-material/simple-vendor-materials.tsx b/lib/material/vendor-material/simple-vendor-materials.tsx
index 73fdfd18..5b5128c4 100644
--- a/lib/material/vendor-material/simple-vendor-materials.tsx
+++ b/lib/material/vendor-material/simple-vendor-materials.tsx
@@ -6,9 +6,12 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
 import { Input } from "@/components/ui/input";
 import { Button } from "@/components/ui/button";
 import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
-import { ArrowUpDown, Search } from "lucide-react";
-import { VendorPossibleMaterial } from "../vendor-possible-material-service";
+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 {
@@ -37,22 +40,16 @@ export function SimpleVendorMaterials({
     if (!date) return "-";
     return date.toISOString().split('T')[0];
   };
+
+  // 선택 상태 관리
+  const [selectedConfirmedIds, setSelectedConfirmedIds] = useState<number[]>([]);
+  const [isDeletePending, startDeleteTransition] = useTransition();
+
   const [confirmedSearch, setConfirmedSearch] = useState("");
   const [vendorInputSearch, setVendorInputSearch] = useState("");
   const [confirmedSort, setConfirmedSort] = useState<SortState>({ field: null, direction: "asc" });
   const [vendorInputSort, setVendorInputSort] = useState<SortState>({ field: null, direction: "asc" });
 
-  // 검색 필터링 함수
-  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 sortMaterials = (materials: VendorPossibleMaterial[], sortState: SortState) => {
     if (!sortState.field) return materials;
@@ -104,7 +101,18 @@ export function SimpleVendorMaterials({
     });
   };
 
-  // 필터링 및 정렬된 데이터
+
+  // 검색 필터링 함수
+  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);
@@ -115,6 +123,63 @@ export function SimpleVendorMaterials({
     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, 
@@ -160,11 +225,25 @@ export function SimpleVendorMaterials({
                 구매담당자가 확정한 공급 품목 정보입니다.
               </CardDescription>
             </div>
-            <AddConfirmedMaterial
-              vendorId={vendorId}
-              existingConfirmedMaterials={confirmedMaterials}
-              onMaterialAdded={onDataRefresh}
-            />
+            <div className="flex items-center gap-2">
+              {selectedConfirmedIds.length > 0 && (
+                <Button
+                  variant="destructive"
+                  size="sm"
+                  onClick={handleDeleteSelected}
+                  disabled={isDeletePending}
+                  className="flex items-center gap-2"
+                >
+                  <Trash2 className="h-4 w-4" />
+                  {isDeletePending ? "삭제 중..." : `삭제 (${selectedConfirmedIds.length})`}
+                </Button>
+              )}
+              <AddConfirmedMaterial
+                vendorId={vendorId}
+                existingConfirmedMaterials={confirmedMaterials}
+                onMaterialAdded={onDataRefresh}
+              />
+            </div>
           </div>
           <div className="flex items-center space-x-2">
             <Search className="h-4 w-4 text-muted-foreground" />
@@ -181,6 +260,18 @@ export function SimpleVendorMaterials({
             <Table>
               <TableHeader className="sticky top-0 bg-background z-10">
                 <TableRow>
+                  <TableHead className="w-12">
+                    <Checkbox
+                      checked={isAllSelected}
+                      ref={(el) => {
+                        if (el && 'indeterminate' in el) {
+                          el.indeterminate = isIndeterminate;
+                        }
+                      }}
+                      onCheckedChange={handleSelectAllConfirmed}
+                      aria-label="전체 선택"
+                    />
+                  </TableHead>
                   <TableHead>
                     <SortButton
                       field="vendorTypeNameEn"
@@ -250,13 +341,20 @@ export function SimpleVendorMaterials({
               <TableBody>
                 {filteredAndSortedConfirmed.length === 0 ? (
                   <TableRow>
-                    <TableCell colSpan={8} className="text-center text-muted-foreground">
+                    <TableCell colSpan={9} className="text-center text-muted-foreground">
                       {confirmedSearch ? "검색 결과가 없습니다." : "확정된 공급품목이 없습니다."}
                     </TableCell>
                   </TableRow>
                 ) : (
                   filteredAndSortedConfirmed.map((material) => (
                     <TableRow key={material.id}>
+                      <TableCell>
+                        <Checkbox
+                          checked={selectedConfirmedIds.includes(material.id)}
+                          onCheckedChange={(checked) => handleSelectConfirmed(material.id, checked as boolean)}
+                          aria-label={`자재 ${material.itemCode} 선택`}
+                        />
+                      </TableCell>
                       <TableCell>{material.vendorTypeNameEn || "-"}</TableCell>
                       <TableCell className="font-mono">{material.itemCode || "-"}</TableCell>
                       <TableCell>{material.itemName || "-"}</TableCell>
-- 
cgit v1.2.3

