"use client" import * as React from "react" import { format } from "date-fns" import { Package, ExternalLink, Download, FileText } from "lucide-react" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { ScrollArea } from "@/components/ui/scroll-area" import { Skeleton } from "@/components/ui/skeleton" import { Separator } from "@/components/ui/separator" import { toast } from "sonner" import { RfqsLastView } from "@/db/schema" import { getRfqItemsAction } from "../service" import { getDesignDocumentsForRfqItemsAction } from "@/lib/pos" import { downloadFile } from "@/lib/file-download" // 품목 타입 interface RfqItem { id: number rfqsLastId: number | null rfqItem: string | null prItem: string | null prNo: string | null materialCode: string | null materialCategory: string | null acc: string | null materialDescription: string | null size: string | null deliveryDate: Date | null quantity: number | null uom: string | null grossWeight: number | null gwUom: string | null specNo: string | null specUrl: string | null trackingNo: string | null majorYn: boolean | null remark: string | null projectDef: string | null projectSc: string | null projectKl: string | null projectLc: string | null projectDl: string | null // RFQ 관련 정보 rfqCode: string | null rfqType: string | null rfqTitle: string | null itemCode: string | null itemName: string | null projectCode: string | null projectName: string | null } interface ItemStatistics { total: number major: number regular: number totalQuantity: number totalWeight: number } interface RfqItemsDialogProps { isOpen: boolean onClose: () => void rfqData: RfqsLastView } export function RfqItemsDialog({ isOpen, onClose, rfqData }: RfqItemsDialogProps) { const [items, setItems] = React.useState([]) const [statistics, setStatistics] = React.useState(null) const [isLoading, setIsLoading] = React.useState(false) // 자재코드별 설계 문서 매핑 const [designDocuments, setDesignDocuments] = React.useState>({}) const [isLoadingDocs, setIsLoadingDocs] = React.useState(false) // 품목 목록 및 설계 문서 로드 React.useEffect(() => { if (!isOpen || !rfqData.id) return const loadData = async () => { setIsLoading(true) setIsLoadingDocs(true) try { // 1. 품목 목록 로드 const itemsResult = await getRfqItemsAction(rfqData.id) if (itemsResult.success) { setItems(itemsResult.data) setStatistics(itemsResult.statistics || null) } else { toast.error(itemsResult.error || "품목을 불러오는데 실패했습니다") setItems([]) setStatistics(null) } // 2. 설계 문서 매핑 로드 const docsResult = await getDesignDocumentsForRfqItemsAction(rfqData.id) if (docsResult.success && docsResult.documents) { setDesignDocuments(docsResult.documents) } else { console.warn("설계 문서 매핑 로드 실패:", docsResult.error) setDesignDocuments({}) } } catch (error) { console.error("데이터 로드 오류:", error) toast.error("데이터를 불러오는데 실패했습니다") setItems([]) setStatistics(null) setDesignDocuments({}) } finally { setIsLoading(false) setIsLoadingDocs(false) } } loadData() }, [isOpen, rfqData.id]) // 사양서 링크 열기 const handleOpenSpec = (specUrl: string) => { window.open(specUrl, '_blank', 'noopener,noreferrer') } // 설계 문서 다운로드 const handleDownloadDesignDoc = async (materialCode: string, fileName: string, filePath: string) => { try { await downloadFile(filePath, fileName, { action: 'download', showToast: true }) } catch (error) { console.error("설계 문서 다운로드 오류:", error) toast.error("설계 문서 다운로드에 실패했습니다") } } // 파일 크기 포맷팅 const formatFileSize = (bytes: number | null) => { if (!bytes) return "" const sizes = ['B', 'KB', 'MB', 'GB'] if (bytes === 0) return '0 B' const i = Math.floor(Math.log(bytes) / Math.log(1024)) return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i] } return ( 견적 품목 목록 {rfqData.rfqCode} - {rfqData.rfqTitle || rfqData.itemName || "품목 정보"} {/* 통계 정보 */} {statistics && !isLoading && ( <>
{statistics.total}
전체 품목
{/*
{statistics.major}
주요 품목
*/}
{statistics.regular}
일반 품목
{statistics.totalQuantity.toLocaleString()}
총 수량
{statistics.totalWeight.toLocaleString()}
총 중량 (KG)
)} {isLoading ? ( 아이템 자재코드 자재명 수량 수량단위 중량 중량단위 납기일 PR번호 사양/설계문서 비고 {[...Array(3)].map((_, i) => ( ))}
) : items.length === 0 ? (

품목이 없습니다.

) : ( 아이템 자재코드 자재명 수량 수량단위 중량 중량단위 납기일 PR번호 PR 아이템 번호 사양/설계문서 프로젝트 비고 {items.map((item, index) => (
#{index + 1} {item.majorYn && ( 주요 )}
{item.materialCode || "-"} {item.acc && ( ACC: {item.acc} )}
{item.materialDescription || "-"} {item.materialCategory && ( {item.materialCategory} )} {item.size && ( 크기: {item.size} )}
{item.quantity ? item.quantity.toLocaleString() : "-"} {item.uom || "-"} {item.grossWeight ? item.grossWeight.toLocaleString() : "-"} {item.gwUom || "-"} {item.deliveryDate ? format(new Date(item.deliveryDate), "yyyy-MM-dd") : "-"} {item.prNo || "-"} {item.prItem || "-"}
{/* 기존 스펙 정보 */}
{item.specNo && ( {item.specNo} )} {item.specUrl && ( )}
{/* 설계 문서 다운로드 */} {item.materialCode && designDocuments[item.materialCode] && (
)} {/* 트래킹 번호 */} {item.trackingNo && (
TRK: {item.trackingNo}
)} {/* 설계 문서 로딩 상태 */} {isLoadingDocs && item.materialCode && (
설계문서 확인 중...
)}
{[ item.projectDef && `${item.projectDef}`, item.projectSc && `SC: ${item.projectSc}`, item.projectKl && `KL: ${item.projectKl}`, item.projectLc && `LC: ${item.projectLc}`, item.projectDl && `DL: ${item.projectDl}` ].filter(Boolean).join(" | ") || "-"}
{item.remark ? (item.remark.length > 30 ? `${item.remark.slice(0, 30)}...` : item.remark) : "-"}
))}
)}
{/* 하단 통계 정보 */} {statistics && !isLoading && (
총 {statistics.total}개 품목 {/* (주요: {statistics.major}개, 일반: {statistics.regular}개) */} 전체 수량: {statistics.totalQuantity.toLocaleString()} | 전체 중량: {statistics.totalWeight.toLocaleString()} KG
)}
) }