"use client"; import * as React from "react"; import { type Table } from "@tanstack/react-table"; import { Download, RefreshCw, Plus, Lock, LockOpen } from "lucide-react"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { toast } from "sonner"; import { RfqsLastView } from "@/db/schema"; import { CreateGeneralRfqDialog } from "./create-general-rfq-dialog"; import { sealMultipleRfqs, unsealMultipleRfqs } from "../service"; interface RfqTableToolbarActionsProps { table: Table; onRefresh?: () => void; rfqCategory?: "general" | "itb" | "rfq"; } export function RfqTableToolbarActions({ table, onRefresh, rfqCategory = "itb", }: RfqTableToolbarActionsProps) { const [isExporting, setIsExporting] = React.useState(false); const [isSealing, setIsSealing] = React.useState(false); const [sealDialogOpen, setSealDialogOpen] = React.useState(false); const [sealAction, setSealAction] = React.useState<"seal" | "unseal">("seal"); const selectedRows = table.getFilteredSelectedRowModel().rows; const selectedRfqIds = selectedRows.map(row => row.original.id); // 선택된 항목들의 밀봉 상태 확인 const sealedCount = selectedRows.filter(row => row.original.rfqSealedYn).length; const unsealedCount = selectedRows.filter(row => !row.original.rfqSealedYn).length; const handleSealAction = React.useCallback(async (action: "seal" | "unseal") => { setSealAction(action); setSealDialogOpen(true); }, []); const confirmSealAction = React.useCallback(async () => { setIsSealing(true); try { const result = sealAction === "seal" ? await sealMultipleRfqs(selectedRfqIds) : await unsealMultipleRfqs(selectedRfqIds); if (result.success) { toast.success(result.message); table.toggleAllRowsSelected(false); // 선택 해제 onRefresh?.(); // 데이터 새로고침 } else { toast.error(result.error); } } catch (error) { toast.error("작업 중 오류가 발생했습니다."); } finally { setIsSealing(false); setSealDialogOpen(false); } }, [sealAction, selectedRfqIds, table, onRefresh]); const handleExportCSV = React.useCallback(async () => { setIsExporting(true); try { const data = table.getFilteredRowModel().rows.map((row) => { const original = row.original; return { "RFQ 코드": original.rfqCode || "", "상태": original.status || "", "밀봉여부": original.rfqSealedYn ? "밀봉" : "미밀봉", "프로젝트 코드": original.projectCode || "", "프로젝트명": original.projectName || "", "자재코드": original.itemCode || "", "자재명": original.itemName || "", "패키지 번호": original.packageNo || "", "패키지명": original.packageName || "", "구매담당자": original.picUserName || original.picName || "", "엔지니어링 담당": original.engPicName || "", "발송일": original.rfqSendDate ? new Date(original.rfqSendDate).toLocaleDateString("ko-KR") : "", "마감일": original.dueDate ? new Date(original.dueDate).toLocaleDateString("ko-KR") : "", "업체수": original.vendorCount || 0, "Short List": original.shortListedVendorCount || 0, "견적접수": original.quotationReceivedCount || 0, "PR Items": original.prItemsCount || 0, "주요 Items": original.majorItemsCount || 0, "시리즈": original.series || "", "견적 유형": original.rfqType || "", "견적 제목": original.rfqTitle || "", "프로젝트 회사": original.projectCompany || "", "프로젝트 사이트": original.projectSite || "", "SM 코드": original.smCode || "", "PR 번호": original.prNumber || "", "PR 발행일": original.prIssueDate ? new Date(original.prIssueDate).toLocaleDateString("ko-KR") : "", "생성자": original.createdByUserName || "", "생성일": original.createdAt ? new Date(original.createdAt).toLocaleDateString("ko-KR") : "", "수정자": original.updatedByUserName || "", "수정일": original.updatedAt ? new Date(original.updatedAt).toLocaleDateString("ko-KR") : "", }; }); const fileName = `RFQ_목록_${new Date().toISOString().split("T")[0]}.csv`; exportTableToCSV({ data, filename: fileName }); } catch (error) { console.error("Export failed:", error); } finally { setIsExporting(false); } }, [table]); const handleExportSelected = React.useCallback(async () => { setIsExporting(true); try { const selectedRows = table.getFilteredSelectedRowModel().rows; if (selectedRows.length === 0) { alert("선택된 항목이 없습니다."); return; } const data = selectedRows.map((row) => { const original = row.original; return { "RFQ 코드": original.rfqCode || "", "상태": original.status || "", "밀봉여부": original.rfqSealedYn ? "밀봉" : "미밀봉", "프로젝트 코드": original.projectCode || "", "프로젝트명": original.projectName || "", "자재코드": original.itemCode || "", "자재명": original.itemName || "", "패키지 번호": original.packageNo || "", "패키지명": original.packageName || "", "구매담당자": original.picUserName || original.picName || "", "엔지니어링 담당": original.engPicName || "", "발송일": original.rfqSendDate ? new Date(original.rfqSendDate).toLocaleDateString("ko-KR") : "", "마감일": original.dueDate ? new Date(original.dueDate).toLocaleDateString("ko-KR") : "", "업체수": original.vendorCount || 0, "Short List": original.shortListedVendorCount || 0, "견적접수": original.quotationReceivedCount || 0, }; }); const fileName = `RFQ_선택항목_${new Date().toISOString().split("T")[0]}.csv`; exportTableToCSV({ data, filename: fileName }); } catch (error) { console.error("Export failed:", error); } finally { setIsExporting(false); } }, [table]); return ( <>
{onRefresh && ( )} {/* 견적 밀봉/해제 버튼 */} {selectedRfqIds.length > 0 && ( handleSealAction("seal")} disabled={unsealedCount === 0} > 선택 항목 밀봉 ({unsealedCount}개) handleSealAction("unseal")} disabled={sealedCount === 0} > 선택 항목 밀봉 해제 ({sealedCount}개)
전체 {selectedRfqIds.length}개 선택됨
)} 전체 데이터 내보내기 선택한 항목 내보내기 ({table.getFilteredSelectedRowModel().rows.length}개) {/* rfqCategory가 'general'일 때만 일반견적 생성 다이얼로그 표시 */} {rfqCategory === "general" && ( )}
{/* 밀봉 확인 다이얼로그 */} {sealAction === "seal" ? "견적 밀봉 확인" : "견적 밀봉 해제 확인"} {sealAction === "seal" ? `선택한 ${unsealedCount}개의 견적을 밀봉하시겠습니까? 밀봉된 견적은 업체에서 수정할 수 없습니다.` : `선택한 ${sealedCount}개의 견적 밀봉을 해제하시겠습니까? 밀봉이 해제되면 업체에서 견적을 수정할 수 있습니다.`} 취소 {isSealing ? "처리 중..." : "확인"} ); } // CSV 내보내기 유틸리티 함수 function exportTableToCSV({ data, filename }: { data: any[]; filename: string }) { if (!data || data.length === 0) { console.warn("No data to export"); return; } const headers = Object.keys(data[0]); const csvContent = [ headers.join(","), ...data.map(row => headers.map(header => { const value = row[header]; // 값에 쉼표, 줄바꿈, 따옴표가 있으면 따옴표로 감싸기 if (typeof value === "string" && (value.includes(",") || value.includes("\n") || value.includes('"'))) { return `"${value.replace(/"/g, '""')}"`; } return value; }).join(",") ) ].join("\n"); const blob = new Blob(["\uFEFF" + csvContent], { type: "text/csv;charset=utf-8;" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); URL.revokeObjectURL(link.href); }