From 748bb1720fd81e97a84c3e92f89d606e976b52e3 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 28 May 2025 00:18:16 +0000 Subject: (대표님) 컴포넌트 파트 커밋 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-table/vendor-communication-drawer.tsx | 518 --------------------- 1 file changed, 518 deletions(-) delete mode 100644 components/po-rfq/detail-table/vendor-communication-drawer.tsx (limited to 'components/po-rfq/detail-table/vendor-communication-drawer.tsx') diff --git a/components/po-rfq/detail-table/vendor-communication-drawer.tsx b/components/po-rfq/detail-table/vendor-communication-drawer.tsx deleted file mode 100644 index 34efdfc2..00000000 --- a/components/po-rfq/detail-table/vendor-communication-drawer.tsx +++ /dev/null @@ -1,518 +0,0 @@ -"use client" - -import * as React from "react" -import { useState, useEffect, useRef } from "react" -import { ProcurementRfqsView } from "@/db/schema" -import { RfqDetailView } from "./rfq-detail-column" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -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: ProcurementRfqsView | 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); - setComments(commentsData); - - // Server Action을 사용하여 읽지 않은 메시지를 읽음 상태로 변경 - await markMessagesAsRead(selectedRfq.id, selectedVendor.vendorId); - } 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 ? ( -