"use client" import * as React from "react" import { useState } from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" 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 { Badge } from "@/components/ui/badge" import { ScrollArea } from "@/components/ui/scroll-area" import { CalendarIcon, Send, AlertCircle, X, FileText, Download, History, FileIcon } from "lucide-react" import { Calendar } from "@/components/ui/calendar" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Alert, AlertDescription } from "@/components/ui/alert" import { formatDate, cn } from "@/lib/utils" import { toast } from "sonner" import { useSession } from "next-auth/react" import { QuotationHistoryDialog } from "@/lib/techsales-rfq/table/detail-table/quotation-history-dialog" interface QuotationResponseTabProps { quotation: { id: number status: string totalPrice: string | null currency: string | null validUntil: Date | null remark: string | null quotationAttachments?: Array<{ id: number fileName: string fileSize: number filePath: string description?: string | null }> rfq: { id: number rfqCode: string | null materialCode: string | null dueDate: Date | null status: string | null item?: { itemName: string | null } | null } | null vendor: { vendorName: string } | null } } const CURRENCIES = [ { value: "KRW", label: "KRW (원)" }, { value: "USD", label: "USD (달러)" }, { value: "EUR", label: "EUR (유로)" }, { value: "JPY", label: "JPY (엔)" }, { value: "CNY", label: "CNY (위안)" }, ] export function QuotationResponseTab({ quotation }: QuotationResponseTabProps) { const [totalPrice, setTotalPrice] = useState(quotation.totalPrice?.toString() || "") const [currency, setCurrency] = useState(quotation.currency || "KRW") const [validUntil, setValidUntil] = useState( quotation.validUntil ? new Date(quotation.validUntil) : undefined ) const [remark, setRemark] = useState(quotation.remark || "") const [isLoading, setIsLoading] = useState(false) const [attachments, setAttachments] = useState>([]) const [isUploadingFiles, setIsUploadingFiles] = useState(false) // 견적 히스토리 다이얼로그 상태 관리 const [historyDialogOpen, setHistoryDialogOpen] = useState(false) const session = useSession() // // 초기 첨부파일 데이터 로드 // useEffect(() => { // if (quotation.quotationAttachments) { // setAttachments(quotation.quotationAttachments.map(att => ({ // id: att.id, // fileName: att.fileName, // fileSize: att.fileSize, // filePath: att.filePath, // isNew: false // }))) // } // }, [quotation.quotationAttachments]) const rfq = quotation.rfq const isDueDatePassed = rfq?.dueDate ? new Date(rfq.dueDate) < new Date() : false // const canSubmit = !["Accepted", "Rejected"].includes(quotation.status) && !isDueDatePassed // const canEdit = !["Accepted", "Rejected"].includes(quotation.status) && !isDueDatePassed const canSubmit = !["Accepted", "Rejected"].includes(quotation.status) const canEdit = !["Accepted", "Rejected"].includes(quotation.status) // 파일 업로드 핸들러 const handleFileSelect = (event: React.ChangeEvent) => { const files = event.target.files if (!files) return Array.from(files).forEach(file => { setAttachments(prev => [ ...prev, { fileName: file.name, fileSize: file.size, filePath: '', isNew: true, file } ]) }) } // 첨부파일 제거 const removeAttachment = (index: number) => { setAttachments(prev => prev.filter((_, i) => i !== index)) } // 파일 업로드 함수 const uploadFiles = async () => { const newFiles = attachments.filter(att => att.isNew && att.file) if (newFiles.length === 0) return [] setIsUploadingFiles(true) try { // 서비스 함수를 사용하여 파일 업로드 const { uploadQuotationAttachments } = await import('@/lib/techsales-rfq/service') const files = newFiles.map(att => att.file!).filter(Boolean) const userId = parseInt(session.data?.user.id || "0") const result = await uploadQuotationAttachments(quotation.id, files, userId) if (result.success && result.attachments) { return result.attachments } else { throw new Error(result.error || '파일 저장에 실패했습니다.') } } catch (error) { console.error('파일 업로드 오류:', error) toast.error('파일 업로드 중 오류가 발생했습니다.') return [] } finally { setIsUploadingFiles(false) } } const handleSubmit = async () => { if (!totalPrice || !currency || !validUntil) { toast.error("모든 필수 항목을 입력해주세요.") return } setIsLoading(true) try { // 파일 업로드 먼저 처리 const uploadedFiles = await uploadFiles() const { submitTechSalesVendorQuotation } = await import("@/lib/techsales-rfq/service") const result = await submitTechSalesVendorQuotation({ id: quotation.id, currency, totalPrice, validUntil: validUntil!, remark, attachments: uploadedFiles, updatedBy: parseInt(session.data?.user.id || "0") }) if (result.error) { toast.error(result.error) } else { toast.success("견적서가 제출되었습니다.") // // 페이지 새로고침 대신 router.refresh() 사용 // router.refresh() // 페이지 새로고침 window.location.reload() } } catch { toast.error("제출 중 오류가 발생했습니다.") } finally { setIsLoading(false) } } const getStatusBadgeVariant = (status: string) => { switch (status) { case "Draft": return "secondary" case "Submitted": return "default" case "Revised": return "outline" case "Rejected": return "destructive" case "Accepted": return "success" default: return "secondary" } } const getStatusLabel = (status: string) => { switch (status) { case "Draft": return "초안" case "Submitted": return "제출됨" case "Revised": return "수정됨" case "Rejected": return "반려됨" case "Accepted": return "승인됨" default: return status } } return (
{/* 견적서 상태 정보 */}
견적서 상태 {getStatusLabel(quotation.status)} 현재 견적서 상태 및 마감일 정보
{/* 견적 히스토리 보기 버튼 */}
견적서 상태
{getStatusLabel(quotation.status)}
RFQ 마감일
{rfq?.dueDate ? formatDate(rfq.dueDate) : "N/A"}
남은 시간
{isDueDatePassed ? ( 마감됨 ) : rfq?.dueDate ? ( {Math.ceil((new Date(rfq.dueDate).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24))}일 ) : ( "N/A" )}
{isDueDatePassed && ( RFQ 마감일이 지났습니다. 견적서를 수정하거나 제출할 수 없습니다. )} {!canEdit && !isDueDatePassed && ( 현재 상태에서는 견적서를 수정할 수 없습니다. )}
{/* 견적 응답 폼 */} 견적 응답 견적 정보를 입력하고 제출하세요
setTotalPrice(e.target.value)} placeholder="견적 금액을 입력하세요" disabled={!canEdit} />