'use client' import React, { useState, useEffect } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Download, Trash2, FileText, LoaderIcon, Paperclip, MessageSquare } from 'lucide-react' import { toast } from 'sonner' import { useTransition } from 'react' import { uploadContractAttachment, getContractAttachments, getContractAttachmentForDownload, deleteContractAttachment, saveContractAttachmentComment } from '../service' import { downloadFile } from '@/lib/file-download' interface ContractDocument { id: number contractId: number documentName: string fileName: string filePath: string documentType?: string shiComment?: string | null vendorComment?: string | null uploadedAt: Date uploadedById: number } interface ContractDocumentsProps { contractId: number userId: string readOnly?: boolean } export function ContractDocuments({ contractId, userId, readOnly = false }: ContractDocumentsProps) { const [documents, setDocuments] = useState([]) const [isLoading, setIsLoading] = useState(false) const [isPending, startTransition] = useTransition() const [editingComment, setEditingComment] = useState<{ id: number; type: 'shi' | 'vendor' } | null>(null) const [commentText, setCommentText] = useState('') const [selectedDocumentType, setSelectedDocumentType] = useState('') const loadDocuments = React.useCallback(async () => { setIsLoading(true) try { const documentList = await getContractAttachments(contractId) setDocuments(documentList as ContractDocument[]) } catch (error) { console.error('Error loading documents:', error) toast.error('문서 목록을 불러오는 중 오류가 발생했습니다.') } finally { setIsLoading(false) } }, [contractId]) useEffect(() => { loadDocuments() }, [loadDocuments]) const handleFileUpload = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return if (!selectedDocumentType) { toast.error('문서 유형을 선택해주세요.') return } startTransition(async () => { try { // 본 계약문서 타입인 경우 기존 문서 확인 if (selectedDocumentType === 'main') { const existingMainDoc = documents.find(doc => doc.documentType === 'main') if (existingMainDoc) { toast.info('기존 계약문서가 새롭게 업로드한 문서로 대체됩니다.') // 기존 본 계약문서 삭제 await deleteContractAttachment(existingMainDoc.id, contractId) } } await uploadContractAttachment(contractId, file, userId, selectedDocumentType) toast.success('문서가 업로드되었습니다.') loadDocuments() // 파일 입력 초기화 event.target.value = '' } catch (error) { console.error('Error uploading document:', error) toast.error('문서 업로드 중 오류가 발생했습니다.') } }) } const handleDownload = async (document: ContractDocument) => { try { const fileData = await getContractAttachmentForDownload(document.id, contractId) downloadFile(fileData.attachment?.filePath || '', fileData.attachment?.fileName || '', { showToast: true }) } catch (error) { console.error('Error downloading document:', error) toast.error('문서 다운로드 중 오류가 발생했습니다.') } } const handleDelete = async (documentId: number) => { startTransition(async () => { try { await deleteContractAttachment(documentId, contractId) toast.success('문서가 삭제되었습니다.') loadDocuments() } catch (error) { console.error('Error deleting document:', error) toast.error('문서 삭제 중 오류가 발생했습니다.') } }) } const handleEditComment = (documentId: number, type: 'shi' | 'vendor', currentComment?: string) => { setEditingComment({ id: documentId, type }) setCommentText(currentComment || '') } const handleSaveComment = async () => { if (!editingComment) return try { await saveContractAttachmentComment( editingComment.id, contractId, editingComment.type, commentText, Number(userId) ) toast.success('댓글이 저장되었습니다.') setEditingComment(null) setCommentText('') loadDocuments() } catch (error) { console.error('Error saving comment:', error) toast.error('댓글 저장 중 오류가 발생했습니다.') } } const getDocumentTypeLabel = (documentName: string) => { switch (documentName) { case 'specification': return '사양' case 'pricing': return '단가종류' case 'other': return '기타' default: return documentName } } const getDocumentTypeColor = (documentName: string) => { switch (documentName) { case 'specification': return 'bg-blue-100 text-blue-800' case 'pricing': return 'bg-green-100 text-green-800' case 'other': return 'bg-gray-100 text-gray-800' default: return 'bg-gray-100 text-gray-800' } } const groupedDocuments = documents.reduce((acc, doc) => { const type = doc.documentName if (!acc[type]) { acc[type] = [] } acc[type].push(doc) return acc }, {} as Record) const documentTypes = [ { value: 'specification', label: '사양' }, { value: 'pricing', label: '단가종류' }, { value: 'other', label: '기타' } ] return ( 계약 첨부문서 {/* 파일 업로드 */} {!readOnly && (
)} {/* 문서 목록 */} {isLoading ? (
문서를 불러오는 중...
) : documents.length === 0 ? (

업로드된 문서가 없습니다.

) : (
{Object.entries(groupedDocuments).map(([type, docs]) => (
{getDocumentTypeLabel(type)} {docs.length}개 문서
{docs.map((doc) => (

{doc.fileName}

업로드: {new Date(doc.uploadedAt).toLocaleDateString()}

{!readOnly && (
)}
{/* 댓글 섹션 */}
{/* SHI 댓글 */}
{!readOnly && ( )}
{editingComment?.id === doc.id && editingComment.type === 'shi' ? (