"use client" import * as React from "react" import { type Table } from "@tanstack/react-table" import { Download, ClipboardCheck, X, Send } from "lucide-react" import { toast } from "sonner" import { exportTableToExcel } from "@/lib/export" import { Button } from "@/components/ui/button" import { PQSubmission } from "./vendors-table-columns" import { requestInvestigationAction, cancelInvestigationAction, sendInvestigationResultsAction, getFactoryLocationAnswer } from "@/lib/pq/service" import { RequestInvestigationDialog } from "./request-investigation-dialog" import { CancelInvestigationDialog } from "./cancel-investigation-dialog" import { SendResultsDialog } from "./send-results-dialog" interface VendorsTableToolbarActionsProps { table: Table } interface InvestigationInitialData { evaluationType?: "SITE_AUDIT" | "QM_SELF_AUDIT"; qmManagerId?: number; forecastedAt?: Date; createdAt?: Date; investigationAddress?: string; investigationNotes?: string; } export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActionsProps) { const selectedRows = table.getFilteredSelectedRowModel().rows const [isLoading, setIsLoading] = React.useState(false) // Dialog 상태 관리 const [isRequestDialogOpen, setIsRequestDialogOpen] = React.useState(false) const [isCancelDialogOpen, setIsCancelDialogOpen] = React.useState(false) const [isSendResultsDialogOpen, setIsSendResultsDialogOpen] = React.useState(false) // 초기 데이터 상태 const [dialogInitialData, setDialogInitialData] = React.useState(undefined) // 실사 의뢰 대화상자 열기 핸들러 // 실사 의뢰 대화상자 열기 핸들러 const handleOpenRequestDialog = async () => { setIsLoading(true); const initialData: InvestigationInitialData = {}; try { // 선택된 행이 정확히 1개인 경우에만 초기값 설정 if (selectedRows.length === 1) { const row = selectedRows[0].original; // 승인된 PQ이고 아직 실사가 없는 경우 if (row.status === "APPROVED" && !row.investigation) { // Factory Location 정보 가져오기 const locationResponse = await getFactoryLocationAnswer( row.vendorId, row.projectId ); // 기본 주소 설정 - Factory Location 응답 또는 fallback let defaultAddress = ""; if (locationResponse.success && locationResponse.factoryLocation) { defaultAddress = locationResponse.factoryLocation; } else { // Factory Location을 찾지 못한 경우 fallback defaultAddress = row.taxId ? `${row.vendorName} 사업장 (${row.taxId})` : `${row.vendorName} 사업장`; } // 이미 같은 회사에 대한 다른 실사가 있는지 확인 const existingInvestigations = table.getFilteredRowModel().rows .map(r => r.original) .filter(r => r.vendorId === row.vendorId && r.investigation !== null ); // 같은 업체의 이전 실사 기록이 있다면 참고하되, 주소는 Factory Location 사용 if (existingInvestigations.length > 0) { // 날짜 기준으로 정렬하여 가장 최근 것을 가져옴 const latestInvestigation = existingInvestigations.sort((a, b) => { const dateA = a.investigation?.createdAt || new Date(0); const dateB = b.investigation?.createdAt || new Date(0); return (dateB as Date).getTime() - (dateA as Date).getTime(); })[0].investigation; if (latestInvestigation) { initialData.evaluationType = latestInvestigation.evaluationType || "SITE_AUDIT"; initialData.qmManagerId = latestInvestigation.qmManagerId || undefined; initialData.investigationAddress = defaultAddress; // Factory Location 사용 // 날짜는 미래로 설정 const futureDate = new Date(); futureDate.setDate(futureDate.getDate() + 14); // 기본값으로 2주 후 initialData.forecastedAt = futureDate; } } else { // 기본값 설정 initialData.evaluationType = "SITE_AUDIT"; const futureDate = new Date(); futureDate.setDate(futureDate.getDate() + 14); // 기본값으로 2주 후 initialData.forecastedAt = futureDate; initialData.investigationAddress = defaultAddress; // Factory Location 사용 } } // 실사가 이미 있고 수정하는 경우 else if (row.investigation) { initialData.evaluationType = row.investigation.evaluationType || "SITE_AUDIT"; initialData.qmManagerId = row.investigation.qmManagerId !== null ? row.investigation.qmManagerId : undefined; initialData.forecastedAt = row.investigation.forecastedAt || new Date(); initialData.investigationAddress = row.investigation.investigationAddress || ""; initialData.investigationNotes = row.investigation.investigationNotes || ""; } } } catch (error) { console.error("초기 데이터 로드 중 오류:", error); toast.error("초기 데이터 로드 중 오류가 발생했습니다."); } finally { setIsLoading(false); // 초기 데이터 설정 및 대화상자 열기 setDialogInitialData(Object.keys(initialData).length > 0 ? initialData : undefined); setIsRequestDialogOpen(true); } }; // 실사 의뢰 요청 처리 const handleRequestInvestigation = async (formData: { evaluationType: "SITE_AUDIT" | "QM_SELF_AUDIT", qmManagerId: number, forecastedAt: Date, investigationAddress: string, investigationNotes?: string }) => { setIsLoading(true) try { // 승인된 PQ 제출만 필터링 const approvedPQs = selectedRows.filter(row => row.original.status === "APPROVED" && !row.original.investigation ) if (approvedPQs.length === 0) { toast.error("실사를 의뢰할 수 있는 업체가 없습니다. 승인된 PQ 제출만 실사 의뢰가 가능합니다.") return } // 서버 액션 호출 const result = await requestInvestigationAction( approvedPQs.map(row => row.original.id), formData ) if (result.success) { toast.success(`${result.count}개 업체에 대한 ${formData.evaluationType === "SITE_AUDIT" ? "실사의뢰평가" : "QM자체평가"}가 의뢰되었습니다.`) window.location.reload() } else { toast.error(result.error || "실사 의뢰 처리 중 오류가 발생했습니다.") } } catch (error) { console.error("실사 의뢰 중 오류 발생:", error) toast.error("실사 의뢰 중 오류가 발생했습니다.") } finally { setIsLoading(false) setIsRequestDialogOpen(false) setDialogInitialData(undefined); // 초기 데이터 초기화 } } const handleCloseRequestDialog = () => { setIsRequestDialogOpen(false); setDialogInitialData(undefined); }; // 실사 의뢰 취소 처리 const handleCancelInvestigation = async () => { setIsLoading(true) try { // 실사가 계획됨 상태인 PQ만 필터링 const plannedInvestigations = selectedRows.filter(row => row.original.investigation && row.original.investigation.investigationStatus === "PLANNED" ) if (plannedInvestigations.length === 0) { toast.error("취소할 수 있는 실사 의뢰가 없습니다. 계획 상태의 실사만 취소할 수 있습니다.") return } // 서버 액션 호출 const result = await cancelInvestigationAction( plannedInvestigations.map(row => row.original.investigation!.id) ) if (result.success) { toast.success(`${result.count}개 업체에 대한 실사 의뢰가 취소되었습니다.`) window.location.reload() } else { toast.error(result.error || "실사 취소 처리 중 오류가 발생했습니다.") } } catch (error) { console.error("실사 의뢰 취소 중 오류 발생:", error) toast.error("실사 의뢰 취소 중 오류가 발생했습니다.") } finally { setIsLoading(false) setIsCancelDialogOpen(false) } } // 실사 결과 발송 처리 const handleSendInvestigationResults = async () => { setIsLoading(true) try { // 완료된 실사만 필터링 const completedInvestigations = selectedRows.filter(row => row.original.investigation && row.original.investigation.investigationStatus === "COMPLETED" ) if (completedInvestigations.length === 0) { toast.error("발송할 실사 결과가 없습니다. 완료된 실사만 결과를 발송할 수 있습니다.") return } // 서버 액션 호출 const result = await sendInvestigationResultsAction( completedInvestigations.map(row => row.original.investigation!.id) ) if (result.success) { toast.success(`${result.count}개 업체에 대한 실사 결과가 발송되었습니다.`) window.location.reload() } else { toast.error(result.error || "실사 결과 발송 처리 중 오류가 발생했습니다.") } } catch (error) { console.error("실사 결과 발송 중 오류 발생:", error) toast.error("실사 결과 발송 중 오류가 발생했습니다.") } finally { setIsLoading(false) setIsSendResultsDialogOpen(false) } } // 승인된 업체 수 확인 const approvedPQsCount = selectedRows.filter(row => row.original.status === "APPROVED" && !row.original.investigation ).length // 계획 상태 실사 수 확인 const plannedInvestigationsCount = selectedRows.filter(row => row.original.investigation && row.original.investigation.investigationStatus === "PLANNED" ).length // 완료된 실사 수 확인 const completedInvestigationsCount = selectedRows.filter(row => row.original.investigation && row.original.investigation.investigationStatus === "COMPLETED" ).length return ( <>
{/* 실사 의뢰 버튼 */} {/* 실사 의뢰 취소 버튼 */} {/* 실사 결과 발송 버튼 */} {/** Export 버튼 */}
{/* 실사 의뢰 Dialog */} {/* 실사 취소 Dialog */} setIsCancelDialogOpen(false)} onConfirm={handleCancelInvestigation} selectedCount={plannedInvestigationsCount} /> {/* 결과 발송 Dialog */} setIsSendResultsDialogOpen(false)} onConfirm={handleSendInvestigationResults} selectedCount={completedInvestigationsCount} /> ) }