diff options
Diffstat (limited to 'lib/techsales-rfq/vendor-response/buyer-communication-drawer.tsx')
| -rw-r--r-- | lib/techsales-rfq/vendor-response/buyer-communication-drawer.tsx | 165 |
1 files changed, 128 insertions, 37 deletions
diff --git a/lib/techsales-rfq/vendor-response/buyer-communication-drawer.tsx b/lib/techsales-rfq/vendor-response/buyer-communication-drawer.tsx index 69ba0363..c8a0efc2 100644 --- a/lib/techsales-rfq/vendor-response/buyer-communication-drawer.tsx +++ b/lib/techsales-rfq/vendor-response/buyer-communication-drawer.tsx @@ -38,31 +38,30 @@ import { } from "@/components/ui/dialog" import { formatDateTime, formatFileSize } from "@/lib/utils" import { useSession } from "next-auth/react" -import { fetchBuyerVendorComments } from "../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 허용으로 변경 +export interface TechSalesAttachment { + id: number + fileName: string + fileSize: number + fileType: string | null + filePath: string + uploadedAt: Date } -interface Attachment { - id: number; - fileName: string; - fileSize: number; - fileType: string | null; // null 허용으로 변경 - filePath: string; - uploadedAt: Date; +export interface TechSalesComment { + id: number + rfqId: number + vendorId: number | null + userId?: number | null + content: string + isVendorComment: boolean | null + createdAt: Date + updatedAt: Date + userName?: string | null + vendorName?: string | null + attachments: TechSalesAttachment[] + isRead: boolean | null } // 프롭스 정의 @@ -73,15 +72,61 @@ interface BuyerCommunicationDrawerProps { id: number; rfqId: number; vendorId: number; - quotationCode: string; + quotationCode: string | null; rfq?: { - rfqCode: string; + rfqCode: string | null; }; } | null; onSuccess?: () => void; } - +// 클라이언트에서 API를 통해 코멘트를 가져오는 함수 +export async function fetchTechSalesVendorCommentsClient(rfqId: number, vendorId: number): Promise<TechSalesComment[]> { + const response = await fetch(`/api/tech-sales-rfqs/${rfqId}/vendors/${vendorId}/comments`); + + if (!response.ok) { + throw new Error(`API 요청 실패: ${response.status}`); + } + + const result = await response.json(); + + if (!result.success) { + throw new Error(result.message || '코멘트 조회 중 오류가 발생했습니다'); + } + + // API 응답 타입 정의 + interface ApiComment { + id: number; + rfqId: number; + vendorId: number | null; + userId?: number | null; + content: string; + isVendorComment: boolean | null; + createdAt: string; + updatedAt: string; + userName?: string | null; + vendorName?: string | null; + isRead: boolean | null; + attachments: Array<{ + id: number; + fileName: string; + fileSize: number; + fileType: string | null; + filePath: string; + uploadedAt: string; + }>; + } + + return result.data.map((comment: ApiComment) => ({ + ...comment, + createdAt: new Date(comment.createdAt), + updatedAt: new Date(comment.updatedAt), + attachments: comment.attachments.map((att) => ({ + ...att, + uploadedAt: new Date(att.uploadedAt) + })) + })); +} // 벤더 코멘트 전송 함수 export function sendVendorCommentClient(params: { @@ -89,7 +134,7 @@ export function sendVendorCommentClient(params: { vendorId: number; content: string; attachments?: File[]; -}): Promise<Comment> { +}): Promise<TechSalesComment> { // 폼 데이터 생성 (파일 첨부를 위해) const formData = new FormData(); formData.append('rfqId', params.rfqId.toString()); @@ -104,8 +149,10 @@ export function sendVendorCommentClient(params: { }); } - // API 엔드포인트 구성 (벤더 API 경로) - const url = `/api/procurement-rfqs/${params.rfqId}/vendors/${params.vendorId}/comments`; + // API 엔드포인트 구성 (techsales API 경로) + const url = `/api/tech-sales-rfqs/${params.rfqId}/vendors/${params.vendorId}/comments`; + + console.log("API 요청 시작:", { url, params }); // API 호출 return fetch(url, { @@ -113,22 +160,65 @@ export function sendVendorCommentClient(params: { body: formData, // multipart/form-data 형식 사용 }) .then(response => { + console.log("API 응답 상태:", response.status); + if (!response.ok) { return response.text().then(text => { + console.error("API 에러 응답:", text); throw new Error(`API 요청 실패: ${response.status} ${text}`); }); } return response.json(); }) .then(result => { + console.log("API 응답 데이터:", result); + if (!result.success || !result.data) { throw new Error(result.message || '코멘트 전송 중 오류가 발생했습니다'); } - return result.data.comment; + + // API 응답 타입 정의 + interface ApiAttachment { + id: number; + fileName: string; + fileSize: number; + fileType: string | null; + filePath: string; + uploadedAt: string; + } + + interface ApiCommentResponse { + id: number; + rfqId: number; + vendorId: number | null; + userId?: number | null; + content: string; + isVendorComment: boolean | null; + createdAt: string; + updatedAt: string; + userName?: string | null; + isRead: boolean | null; + attachments: ApiAttachment[]; + } + + const commentData = result.data.comment as ApiCommentResponse; + + return { + ...commentData, + createdAt: new Date(commentData.createdAt), + updatedAt: new Date(commentData.updatedAt), + attachments: commentData.attachments.map((att) => ({ + ...att, + uploadedAt: new Date(att.uploadedAt) + })) + }; + }) + .catch(error => { + console.error("클라이언트 API 호출 에러:", error); + throw error; }); } - export function BuyerCommunicationDrawer({ open, onOpenChange, @@ -139,7 +229,7 @@ export function BuyerCommunicationDrawer({ const { data: session } = useSession(); // 상태 관리 - const [comments, setComments] = useState<Comment[]>([]); + const [comments, setComments] = useState<TechSalesComment[]>([]); const [newComment, setNewComment] = useState(""); const [attachments, setAttachments] = useState<File[]>([]); const [isLoading, setIsLoading] = useState(false); @@ -149,7 +239,7 @@ export function BuyerCommunicationDrawer({ // 첨부파일 관련 상태 const [previewDialogOpen, setPreviewDialogOpen] = useState(false); - const [selectedAttachment, setSelectedAttachment] = useState<Attachment | null>(null); + const [selectedAttachment, setSelectedAttachment] = useState<TechSalesAttachment | null>(null); // 드로어가 열릴 때 데이터 로드 useEffect(() => { @@ -173,7 +263,7 @@ export function BuyerCommunicationDrawer({ setIsLoading(true); // API를 사용하여 코멘트 데이터 가져오기 - const commentsData = await fetchBuyerVendorComments(quotation.rfqId, quotation.vendorId); + const commentsData = await fetchTechSalesVendorCommentsClient(quotation.rfqId, quotation.vendorId); setComments(commentsData); // 읽음 상태 처리는 API 측에서 처리되는 것으로 가정 @@ -239,19 +329,20 @@ export function BuyerCommunicationDrawer({ }; // 첨부파일 미리보기 - const handleAttachmentPreview = (attachment: Attachment) => { + const handleAttachmentPreview = (attachment: TechSalesAttachment) => { setSelectedAttachment(attachment); setPreviewDialogOpen(true); }; // 첨부파일 다운로드 - const handleAttachmentDownload = (attachment: Attachment) => { + const handleAttachmentDownload = (attachment: TechSalesAttachment) => { // 실제 다운로드 구현 window.open(attachment.filePath, '_blank'); }; // 파일 아이콘 선택 - const getFileIcon = (fileType: string) => { + const getFileIcon = (fileType: string | null) => { + if (!fileType) return <File className="h-5 w-5 text-gray-500" />; if (fileType.startsWith("image/")) return <ImageIcon className="h-5 w-5 text-blue-500" />; if (fileType.includes("pdf")) return <FileText className="h-5 w-5 text-red-500" />; if (fileType.includes("spreadsheet") || fileType.includes("excel")) @@ -265,8 +356,8 @@ export function BuyerCommunicationDrawer({ const renderAttachmentPreviewDialog = () => { if (!selectedAttachment) return null; - const isImage = selectedAttachment.fileType.startsWith("image/"); - const isPdf = selectedAttachment.fileType.includes("pdf"); + const isImage = selectedAttachment.fileType?.startsWith("image/") || false; + const isPdf = selectedAttachment.fileType?.includes("pdf") || false; return ( <Dialog open={previewDialogOpen} onOpenChange={setPreviewDialogOpen}> |
