From 3bdd648ad4cb863043db181291ddaebbc025965b Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Thu, 29 May 2025 10:25:22 +0000
Subject: (김준회) 기술영업 조선 RFQ 변경 (셀 클릭 상세보기 및 Unread Message,
 Action Column 관련 변경사항 적용)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/detail-table/rfq-detail-table.tsx        | 128 ++++++++++++++-------
 1 file changed, 89 insertions(+), 39 deletions(-)

(limited to 'lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx')

diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
index a2f012ad..dbaeae0c 100644
--- a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
+++ b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
@@ -15,7 +15,7 @@ import { Button } from "@/components/ui/button"
 import { Loader2, UserPlus, BarChart2, Send, Trash2 } from "lucide-react"
 import { ClientDataTable } from "@/components/client-data-table/data-table"
 import { AddVendorDialog } from "./add-vendor-dialog"
-import { DeleteVendorDialog } from "./delete-vendor-dialog"
+import { DeleteVendorsDialog } from "./delete-vendors-dialog"
 import { VendorCommunicationDrawer } from "./vendor-communication-drawer"
 import { VendorQuotationComparisonDialog } from "./vendor-quotation-comparison-dialog"
 
@@ -48,8 +48,6 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
   const [isLoading, setIsLoading] = useState(false)
   const [details, setDetails] = useState<RfqDetailView[]>([])
   const [vendorDialogOpen, setVendorDialogOpen] = React.useState(false)
-  const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false)
-  const [selectedDetail, setSelectedDetail] = React.useState<RfqDetailView | null>(null)
 
   const [isAdddialogLoading, setIsAdddialogLoading] = useState(false)
 
@@ -70,6 +68,9 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
   const [isSendingRfq, setIsSendingRfq] = useState(false)
   const [isDeletingVendors, setIsDeletingVendors] = useState(false)
 
+  // 벤더 삭제 확인 다이얼로그 상태 추가
+  const [deleteConfirmDialogOpen, setDeleteConfirmDialogOpen] = useState(false)
+
   // selectedRfq ID 메모이제이션 (객체 참조 변경 방지)
   const selectedRfqId = useMemo(() => selectedRfq?.id, [selectedRfq?.id])
 
@@ -83,12 +84,13 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
     if (!selectedRfqId) return;
 
     try {
-      // TODO: 기술영업용 읽지 않은 메시지 수 가져오기 함수 구현 필요
-      // const unreadData = await fetchUnreadMessages(selectedRfqId);
-      // setUnreadMessages(unreadData);
-      setUnreadMessages({});
+      // 기술영업용 읽지 않은 메시지 수 가져오기 함수 구현
+      const { getTechSalesUnreadMessageCounts } = await import("@/lib/techsales-rfq/service");
+      const unreadData = await getTechSalesUnreadMessageCounts(selectedRfqId);
+      setUnreadMessages(unreadData);
     } catch (error) {
       console.error("읽지 않은 메시지 로드 오류:", error);
+      setUnreadMessages({});
     }
   }, [selectedRfqId]);
 
@@ -236,6 +238,21 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
     }
   }, [selectedRows, selectedRfqId, handleRefreshData]);
 
+  // 벤더 삭제 확인 핸들러
+  const handleDeleteVendorsConfirm = useCallback(() => {
+    if (selectedRows.length === 0) {
+      toast.warning("삭제할 벤더를 선택해주세요.");
+      return;
+    }
+    setDeleteConfirmDialogOpen(true);
+  }, [selectedRows]);
+
+  // 벤더 삭제 확정 실행
+  const executeDeleteVendors = useCallback(async () => {
+    setDeleteConfirmDialogOpen(false);
+    await handleDeleteVendors();
+  }, [handleDeleteVendors]);
+
   // 견적 비교 다이얼로그 열기 핸들러 메모이제이션
   const handleOpenComparisonDialog = useCallback(() => {
     // 제출된 견적이 있는 벤더가 최소 1개 이상 있는지 확인
@@ -281,11 +298,6 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
   )
 
   // 계산된 값들 메모이제이션
-  const totalUnreadMessages = useMemo(() => 
-    Object.values(unreadMessages).reduce((sum, count) => sum + count, 0),
-    [unreadMessages]
-  );
-
   const vendorsWithQuotations = useMemo(() => 
     details.filter(detail => detail.status === "Submitted").length,
     [details]
@@ -360,24 +372,45 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
           setSelectedVendor(rowAction.row.original);
           setCommunicationDrawerOpen(true);
 
-          // 해당 벤더의 읽지 않은 메시지를 0으로 설정 (메시지를 읽은 것으로 간주)
-          const vendorId = rowAction.row.original.vendorId;
-          if (vendorId) {
-            setUnreadMessages(prev => ({
-              ...prev,
-              [vendorId]: 0
-            }));
-          }
-
           // rowAction 초기화
           setRowAction(null);
           return;
         }
 
-        // 삭제 액션인 경우
+        // 삭제 액션인 경우 개별 벤더 삭제
         if (rowAction.type === "delete") {
-          setSelectedDetail(rowAction.row.original);
-          setDeleteDialogOpen(true);
+          const vendor = rowAction.row.original;
+          
+          if (!vendor.vendorId || !selectedRfqId) {
+            toast.error("벤더 정보가 없습니다.");
+            setRowAction(null);
+            return;
+          }
+
+          // Draft 상태 체크
+          if (vendor.status !== "Draft") {
+            toast.error("Draft 상태의 벤더만 삭제할 수 있습니다.");
+            setRowAction(null);
+            return;
+          }
+
+          // 개별 벤더 삭제
+          const { removeVendorFromTechSalesRfq } = await import("@/lib/techsales-rfq/service");
+          
+          const result = await removeVendorFromTechSalesRfq({
+            rfqId: selectedRfqId,
+            vendorId: vendor.vendorId
+          });
+          
+          if (result.error) {
+            toast.error(result.error);
+          } else {
+            toast.success(`${vendor.vendorName || '벤더'}가 성공적으로 삭제되었습니다.`);
+            // 데이터 새로고침
+            await handleRefreshData();
+          }
+
+          // rowAction 초기화
           setRowAction(null);
           return;
         }
@@ -388,7 +421,7 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
     };
 
     handleRowAction();
-  }, [rowAction])
+  }, [rowAction, selectedRfqId, handleRefreshData])
 
   // 선택된 행 변경 핸들러 메모이제이션
   const handleSelectedRowsChange = useCallback((selectedRowsData: RfqDetailView[]) => {
@@ -398,9 +431,25 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
   // 커뮤니케이션 드로어 변경 핸들러 메모이제이션
   const handleCommunicationDrawerChange = useCallback((open: boolean) => {
     setCommunicationDrawerOpen(open);
-    // 드로어가 닫힐 때 읽지 않은 메시지 개수 갱신
-    if (!open) loadUnreadMessages();
-  }, [loadUnreadMessages]);
+    // 드로어가 닫힐 때 해당 벤더의 메시지를 읽음 처리하고 읽지 않은 메시지 개수 갱신
+    if (!open && selectedVendor?.vendorId && selectedRfqId) {
+      // 메시지를 읽음으로 처리
+      import("@/lib/techsales-rfq/service").then(({ markTechSalesMessagesAsRead }) => {
+        markTechSalesMessagesAsRead(selectedRfqId, selectedVendor.vendorId || undefined).catch(error => {
+          console.error("메시지 읽음 처리 오류:", error);
+        });
+      });
+      
+      // 해당 벤더의 읽지 않은 메시지를 0으로 즉시 업데이트
+      setUnreadMessages(prev => ({
+        ...prev,
+        [selectedVendor.vendorId!]: 0
+      }));
+      
+      // 전체 읽지 않은 메시지 개수 갱신
+      loadUnreadMessages();
+    }
+  }, [selectedVendor, selectedRfqId, loadUnreadMessages]);
 
   if (!selectedRfq) {
     return (
@@ -439,11 +488,11 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
                   {selectedRows.length}개 선택됨
                 </Badge>
               )}
-              {totalUnreadMessages > 0 && (
+              {/* {totalUnreadMessages > 0 && (
                 <Badge variant="destructive" className="h-6">
                   읽지 않은 메시지: {totalUnreadMessages}건
                 </Badge>
-              )}
+              )} */}
               {vendorsWithQuotations > 0 && (
                 <Badge variant="outline" className="h-6">
                   견적 제출: {vendorsWithQuotations}개 벤더
@@ -471,7 +520,7 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
               <Button
                 variant="outline"
                 size="sm"
-                onClick={handleDeleteVendors}
+                onClick={handleDeleteVendorsConfirm}
                 disabled={selectedRows.length === 0 || isDeletingVendors}
                 className="gap-2"
               >
@@ -549,14 +598,6 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
         onSuccess={handleRefreshData}
       />
 
-      <DeleteVendorDialog
-        open={deleteDialogOpen}
-        onOpenChange={setDeleteDialogOpen}
-        detail={selectedDetail}
-        showTrigger={false}
-        onSuccess={handleRefreshData}
-      />
-
       {/* 벤더 커뮤니케이션 드로어 */}
       <VendorCommunicationDrawer
         open={communicationDrawerOpen}
@@ -572,6 +613,15 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
         onOpenChange={setComparisonDialogOpen}
         selectedRfq={selectedRfq}
       />
+
+      {/* 다중 벤더 삭제 확인 다이얼로그 */}
+      <DeleteVendorsDialog
+        open={deleteConfirmDialogOpen}
+        onOpenChange={setDeleteConfirmDialogOpen}
+        vendors={selectedRows}
+        onConfirm={executeDeleteVendors}
+        isLoading={isDeletingVendors}
+      />
     </div>
   )
 }
\ No newline at end of file
-- 
cgit v1.2.3

