"use client" import * as React from "react" import { useState, useEffect, useRef } from "react" import { RfqDetailView } from "./rfq-detail-column" import { Button } from "@/components/ui/button" import { Textarea } from "@/components/ui/textarea" import { Avatar, AvatarFallback } from "@/components/ui/avatar" import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, } from "@/components/ui/drawer" import { ScrollArea } from "@/components/ui/scroll-area" import { Badge } from "@/components/ui/badge" import { toast } from "sonner" import { Send, Paperclip, DownloadCloud, File, FileText, Image as ImageIcon, AlertCircle, X } from "lucide-react" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { formatDateTime } from "@/lib/utils" import { formatFileSize } from "@/lib/utils" // formatFileSize 유틸리티 임포트 import { fetchVendorComments, markMessagesAsRead } from "@/lib/procurement-rfqs/services" // 타입 정의 interface Comment { id: number; rfqId: number; vendorId: number | null // null 허용으로 변경 userId?: number | null // null 허용으로 변경 content: string; isVendorComment: boolean | null; // null 허용으로 변경 createdAt: Date; updatedAt: Date; userName?: string | null // null 허용으로 변경 vendorName?: string | null // null 허용으로 변경 attachments: Attachment[]; isRead: boolean | null // null 허용으로 변경 } interface Attachment { id: number; fileName: string; fileSize: number; fileType: string; filePath: string; uploadedAt: Date; } // 프롭스 정의 interface VendorCommunicationDrawerProps { open: boolean; onOpenChange: (open: boolean) => void; selectedRfq: { id: number; rfqCode: string | null; status: string; [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any } | null; selectedVendor: RfqDetailView | null; onSuccess?: () => void; } async function sendComment(params: { rfqId: number; vendorId: number; content: string; attachments?: File[]; }): Promise { try { // 폼 데이터 생성 (파일 첨부를 위해) const formData = new FormData(); formData.append('rfqId', params.rfqId.toString()); formData.append('vendorId', params.vendorId.toString()); formData.append('content', params.content); formData.append('isVendorComment', 'false'); // 첨부파일 추가 if (params.attachments && params.attachments.length > 0) { params.attachments.forEach((file) => { formData.append(`attachments`, file); }); } // API 엔드포인트 구성 const url = `/api/procurement-rfqs/${params.rfqId}/vendors/${params.vendorId}/comments`; // API 호출 const response = await fetch(url, { method: 'POST', body: formData, // multipart/form-data 형식 사용 }); if (!response.ok) { const errorText = await response.text(); throw new Error(`API 요청 실패: ${response.status} ${errorText}`); } // 응답 데이터 파싱 const result = await response.json(); if (!result.success || !result.data) { throw new Error(result.message || '코멘트 전송 중 오류가 발생했습니다'); } return result.data.comment; } catch (error) { console.error('코멘트 전송 오류:', error); throw error; } } export function VendorCommunicationDrawer({ open, onOpenChange, selectedRfq, selectedVendor, onSuccess }: VendorCommunicationDrawerProps) { // 상태 관리 const [comments, setComments] = useState([]); const [newComment, setNewComment] = useState(""); const [attachments, setAttachments] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const fileInputRef = useRef(null); const messagesEndRef = useRef(null); // 첨부파일 관련 상태 const [previewDialogOpen, setPreviewDialogOpen] = useState(false); const [selectedAttachment, setSelectedAttachment] = useState(null); // 드로어가 열릴 때 데이터 로드 useEffect(() => { if (open && selectedRfq && selectedVendor) { loadComments(); } }, [open, selectedRfq, selectedVendor]); // 스크롤 최하단으로 이동 useEffect(() => { if (messagesEndRef.current) { messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [comments]); // 코멘트 로드 함수 const loadComments = async () => { if (!selectedRfq || !selectedVendor) return; try { setIsLoading(true); // Server Action을 사용하여 코멘트 데이터 가져오기 const commentsData = await fetchVendorComments(selectedRfq.id, selectedVendor.vendorId || 0); setComments(commentsData as Comment[]); // 구체적인 타입으로 캐스팅 // Server Action을 사용하여 읽지 않은 메시지를 읽음 상태로 변경 await markMessagesAsRead(selectedRfq.id, selectedVendor.vendorId || 0); } catch (error) { console.error("코멘트 로드 오류:", error); toast.error("메시지를 불러오는 중 오류가 발생했습니다"); } finally { setIsLoading(false); } }; // 파일 선택 핸들러 const handleFileSelect = () => { fileInputRef.current?.click(); }; // 파일 변경 핸들러 const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { const newFiles = Array.from(e.target.files); setAttachments(prev => [...prev, ...newFiles]); } }; // 파일 제거 핸들러 const handleRemoveFile = (index: number) => { setAttachments(prev => prev.filter((_, i) => i !== index)); }; console.log(newComment) // 코멘트 전송 핸들러 const handleSubmitComment = async () => { console.log("버튼 클릭1", selectedRfq,selectedVendor, selectedVendor?.vendorId ) console.log(!newComment.trim() && attachments.length === 0) if (!newComment.trim() && attachments.length === 0) return; if (!selectedRfq || !selectedVendor || !selectedVendor.vendorId) return; console.log("버튼 클릭") try { setIsSubmitting(true); // API를 사용하여 새 코멘트 전송 (파일 업로드 때문에 FormData 사용) const newCommentObj = await sendComment({ rfqId: selectedRfq.id, vendorId: selectedVendor.vendorId, content: newComment, attachments: attachments }); // 상태 업데이트 setComments(prev => [...prev, newCommentObj]); setNewComment(""); setAttachments([]); toast.success("메시지가 전송되었습니다"); // 데이터 새로고침 if (onSuccess) { onSuccess(); } } catch (error) { console.error("코멘트 전송 오류:", error); toast.error("메시지 전송 중 오류가 발생했습니다"); } finally { setIsSubmitting(false); } }; // 첨부파일 미리보기 const handleAttachmentPreview = (attachment: Attachment) => { setSelectedAttachment(attachment); setPreviewDialogOpen(true); }; // 첨부파일 다운로드 const handleAttachmentDownload = (attachment: Attachment) => { // TODO: 실제 다운로드 구현 window.open(attachment.filePath, '_blank'); }; // 파일 아이콘 선택 const getFileIcon = (fileType: string) => { if (fileType.startsWith("image/")) return ; if (fileType.includes("pdf")) return ; if (fileType.includes("spreadsheet") || fileType.includes("excel")) return ; if (fileType.includes("document") || fileType.includes("word")) return ; return ; }; // 첨부파일 미리보기 다이얼로그 const renderAttachmentPreviewDialog = () => { if (!selectedAttachment) return null; const isImage = selectedAttachment.fileType.startsWith("image/"); const isPdf = selectedAttachment.fileType.includes("pdf"); return ( {getFileIcon(selectedAttachment.fileType)} {selectedAttachment.fileName} {formatFileSize(selectedAttachment.fileSize)} • {formatDateTime(selectedAttachment.uploadedAt)}
{isImage ? ( {selectedAttachment.fileName} ) : isPdf ? (