"use client"; import * as React from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Checkbox } from "@/components/ui/checkbox"; import { Send, Building2, User, Calendar, Package, FileText, Plus, X, Paperclip, Download, Mail, Users, AlertCircle, Info, File, CheckCircle, RefreshCw } from "lucide-react"; import { format } from "date-fns"; import { ko } from "date-fns/locale"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { Alert, AlertDescription, } from "@/components/ui/alert"; // 타입 정의 interface Vendor { vendorId: number; vendorName: string; vendorCode?: string | null; vendorCountry?: string | null; vendorEmail?: string | null; currency?: string | null; } interface Attachment { id: number; attachmentType: string; serialNo: string; currentRevision: string; description?: string; fileName?: string; fileSize?: number; uploadedAt?: Date; } interface RfqInfo { rfqCode: string; rfqTitle: string; rfqType: string; projectCode?: string; projectName?: string; picName?: string; picCode?: string; picTeam?: string; packageNo?: string; packageName?: string; designPicName?: string; designTeam?: string; materialGroup?: string; materialGroupDesc?: string; dueDate: Date; quotationType?: string; evaluationApply?: boolean; contractType?: string; } interface VendorWithRecipients extends Vendor { additionalRecipients: string[]; } interface SendRfqDialogProps { open: boolean; onOpenChange: (open: boolean) => void; selectedVendors: Vendor[]; rfqInfo: RfqInfo; attachments?: Attachment[]; onSend: (data: { vendors: VendorWithRecipients[]; attachments: number[]; message?: string; }) => Promise; } // 첨부파일 타입별 아이콘 const getAttachmentIcon = (type: string) => { switch (type.toLowerCase()) { case "technical": return ; case "commercial": return ; case "drawing": return ; default: return ; } }; // 파일 크기 포맷 const formatFileSize = (bytes?: number) => { if (!bytes) return "0 KB"; const kb = bytes / 1024; const mb = kb / 1024; if (mb >= 1) return `${mb.toFixed(2)} MB`; return `${kb.toFixed(2)} KB`; }; export function SendRfqDialog({ open, onOpenChange, selectedVendors, rfqInfo, attachments = [], onSend, }: SendRfqDialogProps) { const [isSending, setIsSending] = React.useState(false); const [vendorsWithRecipients, setVendorsWithRecipients] = React.useState([]); const [selectedAttachments, setSelectedAttachments] = React.useState([]); const [additionalMessage, setAdditionalMessage] = React.useState(""); // 초기화 React.useEffect(() => { if (open && selectedVendors.length > 0) { setVendorsWithRecipients( selectedVendors.map(v => ({ ...v, additionalRecipients: [] })) ); // 모든 첨부파일 선택 setSelectedAttachments(attachments.map(a => a.id)); } }, [open, selectedVendors, attachments]); // 추가 수신처 이메일 추가 const handleAddRecipient = (vendorId: number, email: string) => { if (!email) return; // 이메일 유효성 검사 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { toast.error("올바른 이메일 형식이 아닙니다."); return; } setVendorsWithRecipients(prev => prev.map(v => v.vendorId === vendorId ? { ...v, additionalRecipients: [...v.additionalRecipients, email] } : v ) ); }; // 추가 수신처 이메일 제거 const handleRemoveRecipient = (vendorId: number, index: number) => { setVendorsWithRecipients(prev => prev.map(v => v.vendorId === vendorId ? { ...v, additionalRecipients: v.additionalRecipients.filter((_, i) => i !== index) } : v ) ); }; // 첨부파일 선택 토글 const toggleAttachment = (attachmentId: number) => { setSelectedAttachments(prev => prev.includes(attachmentId) ? prev.filter(id => id !== attachmentId) : [...prev, attachmentId] ); }; // 전송 처리 const handleSend = async () => { try { setIsSending(true); // 유효성 검사 if (selectedAttachments.length === 0) { toast.warning("최소 하나 이상의 첨부파일을 선택해주세요."); return; } await onSend({ vendors: vendorsWithRecipients, attachments: selectedAttachments, message: additionalMessage, }); toast.success(`${vendorsWithRecipients.length}개 업체에 RFQ를 발송했습니다.`); onOpenChange(false); } catch (error) { console.error("RFQ 발송 실패:", error); toast.error("RFQ 발송에 실패했습니다."); } finally { setIsSending(false); } }; // 총 수신자 수 계산 const totalRecipientCount = React.useMemo(() => { return vendorsWithRecipients.reduce((acc, v) => acc + 1 + v.additionalRecipients.length, 0 ); }, [vendorsWithRecipients]); return ( RFQ 일괄 발송 선택한 {selectedVendors.length}개 업체에 RFQ를 발송합니다.
{/* RFQ 정보 섹션 */}
RFQ 정보
{/* 프로젝트 정보 */}
프로젝트: {rfqInfo.projectCode || "PN003"} ({rfqInfo.projectName || "PETRONAS ZLNG nearshore project"})
견적번호: {rfqInfo.rfqCode}
{/* 담당자 정보 */}
구매담당: {rfqInfo.picName || "김*종"} ({rfqInfo.picCode || "86D"}) {rfqInfo.picTeam || "해양구매팀(해양구매1)"}
설계담당: {rfqInfo.designPicName || "이*진"} {rfqInfo.designTeam || "전장설계팀 (전장기기시스템)"}
{/* PKG 및 자재 정보 */}
PKG 정보: {rfqInfo.packageNo || "MM03"} ({rfqInfo.packageName || "Deck Machinery"})
자재그룹: {rfqInfo.materialGroup || "BE2101"} ({rfqInfo.materialGroupDesc || "Combined Windlass & Mooring Wi"})
{/* 견적 정보 */}
견적마감일: {format(rfqInfo.dueDate, "yyyy.MM.dd", { locale: ko })}
평가적용: {rfqInfo.evaluationApply ? "Y" : "N"}
{/* 견적명 */}
견적명: {rfqInfo.rfqTitle}
{/* 계약구분 (일반견적일 때만) */} {rfqInfo.rfqType === "일반견적" && (
계약구분: {rfqInfo.contractType || "-"}
)}
{/* 첨부파일 섹션 */}
첨부파일 ({selectedAttachments.length}/{attachments.length})
{attachments.length > 0 ? ( attachments.map((attachment) => (
toggleAttachment(attachment.id)} /> {getAttachmentIcon(attachment.attachmentType)}
{attachment.fileName || `${attachment.attachmentType}_${attachment.serialNo}`} {attachment.currentRevision}
{attachment.description && (

{attachment.description}

)}
{formatFileSize(attachment.fileSize)}
)) ) : (

첨부파일이 없습니다.

)}
{/* 수신 업체 섹션 */}
수신 업체 ({selectedVendors.length})
총 {totalRecipientCount}명
{vendorsWithRecipients.map((vendor, index) => (
{/* 업체 정보 */}
{index + 1}
{vendor.vendorName} {vendor.vendorCountry}
{vendor.vendorCode && ( {vendor.vendorCode} )}
주 수신: {vendor.vendorEmail || "vendor@example.com"}
{/* 추가 수신처 */}

참조로 RFQ를 받을 추가 이메일 주소를 입력하세요.

{/* 추가된 이메일 목록 */}
{vendor.additionalRecipients.map((email, idx) => ( {email} ))}
{/* 이메일 입력 필드 */}
{ if (e.key === "Enter") { e.preventDefault(); const input = e.target as HTMLInputElement; handleAddRecipient(vendor.vendorId, input.value); input.value = ""; } }} />
))}
{/* 추가 메시지 (선택사항) */}