diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-29 10:25:22 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-29 10:25:22 +0000 |
| commit | 3bdd648ad4cb863043db181291ddaebbc025965b (patch) | |
| tree | 7197902b7eb6e442b2c62e1cb4e8e9e2553d4def /lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx | |
| parent | 0257350f55c00735cadbd5b507ef5cc9cd3adb10 (diff) | |
(김준회) 기술영업 조선 RFQ 변경 (셀 클릭 상세보기 및 Unread Message, Action Column 관련 변경사항 적용)
Diffstat (limited to 'lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx')
| -rw-r--r-- | lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx | 128 |
1 files changed, 89 insertions, 39 deletions
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 |
