// components/purchase-requests/view-purchase-request-sheet.tsx "use client"; import * as React from "react"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetFooter, } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/components/ui/tabs"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, TableFooter, } from "@/components/ui/table"; import { Separator } from "@/components/ui/separator"; import { FileList, FileListDescription, FileListHeader, FileListIcon, FileListInfo, FileListItem, FileListName, FileListSize, } from "@/components/ui/file-list"; import { FileText, Package, Edit, Download, Calendar, User, Building, MapPin, Hash, DollarSign, Clock, CheckCircle, XCircle, AlertCircle, Layers, Tag, Paperclip, FileIcon, Eye } from "lucide-react"; import { format } from "date-fns"; import { ko } from "date-fns/locale"; import type { PurchaseRequestView } from "@/db/schema"; import { useRouter } from "next/navigation"; import { getPurchaseRequestAttachments } from "../service"; import { downloadFile, quickPreview, formatFileSize, getFileInfo } from "@/lib/file-download"; interface ViewPurchaseRequestSheetProps { request: PurchaseRequestView; open: boolean; onOpenChange: (open: boolean) => void; onEditClick?: () => void; } const statusConfig = { "작성중": { variant: "secondary" as const, color: "text-gray-500", icon: Edit, bgColor: "bg-gray-100" }, "요청완료": { variant: "default" as const, color: "text-blue-500", icon: CheckCircle, bgColor: "bg-blue-50" }, "검토중": { variant: "warning" as const, color: "text-yellow-500", icon: Clock, bgColor: "bg-yellow-50" }, "승인": { variant: "success" as const, color: "text-green-500", icon: CheckCircle, bgColor: "bg-green-50" }, "반려": { variant: "destructive" as const, color: "text-red-500", icon: XCircle, bgColor: "bg-red-50" }, "RFQ생성완료": { variant: "outline" as const, color: "text-purple-500", icon: Package, bgColor: "bg-purple-50" }, }; export function ViewPurchaseRequestSheet({ request, open, onOpenChange, onEditClick, }: ViewPurchaseRequestSheetProps) { const router = useRouter(); const [activeTab, setActiveTab] = React.useState("overview"); const [attachments, setAttachments] = React.useState([]); const [isLoadingFiles, setIsLoadingFiles] = React.useState(false); // 첨부파일 로드 React.useEffect(() => { async function loadAttachments() { if (open && request.id) { setIsLoadingFiles(true); try { const result = await getPurchaseRequestAttachments(request.id); if (result.success && result.data) { setAttachments(result.data); } else { console.error("Failed to load attachments:", result.error); setAttachments([]); } } catch (error) { console.error("Error loading attachments:", error); setAttachments([]); } finally { setIsLoadingFiles(false); } } } loadAttachments(); }, [open, request.id]); // 파일 다운로드 핸들러 const handleFileDownload = async (file: any) => { const result = await downloadFile(file.filePath, file.originalFileName, { action: 'download', showToast: true, onError: (error) => { console.error("Download failed:", error); }, onSuccess: (fileName, fileSize) => { console.log(`Successfully downloaded: ${fileName} (${fileSize} bytes)`); } }); return result; }; // 파일 미리보기 핸들러 const handleFilePreview = async (file: any) => { const fileInfo = getFileInfo(file.originalFileName); if (!fileInfo.canPreview) { // 미리보기가 지원되지 않는 파일은 다운로드 return handleFileDownload(file); } const result = await quickPreview(file.filePath, file.originalFileName); return result; }; // 전체 다운로드 핸들러 const handleDownloadAll = async () => { for (const file of attachments) { await handleFileDownload(file); // 여러 파일 다운로드 시 간격 두기 await new Promise(resolve => setTimeout(resolve, 500)); } }; // 아이템 총액 계산 const totalAmount = React.useMemo(() => { if (!request.items || !Array.isArray(request.items)) return 0; return request.items.reduce((sum, item) => { const subtotal = (item.quantity || 0) * (item.estimatedUnitPrice || 0); return sum + subtotal; }, 0); }, [request.items]); const handleEdit = () => { if (onEditClick) { onEditClick(); } else { onOpenChange(false); } }; const handleExport = () => { // Export to Excel 기능 console.log("Export to Excel"); }; const StatusIcon = statusConfig[request.status]?.icon || AlertCircle; const statusBgColor = statusConfig[request.status]?.bgColor || "bg-gray-50"; return (
구매요청 상세
{request.status}
요청번호: {request.requestCode} | 작성일: {request.createdAt && format(new Date(request.createdAt), "yyyy-MM-dd")}
개요 품목 정보 {request.itemCount > 0 && ( {request.itemCount} )} 첨부파일 {attachments.length > 0 && ( {attachments.length} )} 처리 이력
{/* 기본 정보 */} 기본 정보

요청 제목

{request.requestTitle}

{request.requestDescription && (

요청 설명

{request.requestDescription}

)}

희망 납기일

{request.requestedDeliveryDate ? format(new Date(request.requestedDeliveryDate), "yyyy-MM-dd") : "-"}

예상 예산

{request.estimatedBudget || "-"}

{/* 프로젝트 & 패키지 정보 */} 프로젝트 정보

프로젝트 코드

{request.projectCode || "-"}

프로젝트명

{request.projectName || "-"}

발주처

{request.projectCompany || "-"}

현장

{request.projectSite || "-"}

패키지

{request.packageNo} - {request.packageName || "-"}

SM 코드

{request.smCode || "-"}

{/* 자재 정보 */} {(request.majorItemMaterialCategory || request.majorItemMaterialDescription) && ( 자재 정보

자재 그룹

{request.majorItemMaterialCategory || "-"}

자재 설명

{request.majorItemMaterialDescription || "-"}

)} {/* 담당자 정보 */} 담당자 정보

설계 담당자

{request.engPicName || "-"}

{request.engPicEmail && (

{request.engPicEmail}

)}

구매 담당자

{request.purchasePicName || "미배정"}

{request.purchasePicEmail && (

{request.purchasePicEmail}

)}
{/* 반려 사유 */} {request.status === "반려" && request.rejectReason && ( 반려 사유

{request.rejectReason}

)}
품목 목록 총 {request.itemCount || 0}개 품목 | 총 수량 {request.totalQuantity || 0}개
{(!request.items || request.items.length === 0) ? (

등록된 품목이 없습니다

) : (
번호 아이템 코드 아이템명 사양 수량 단위 예상 단가 예상 금액 {request.items[0]?.remarks && 비고} {request.items.map((item: any, index: number) => { const subtotal = (item.quantity || 0) * (item.estimatedUnitPrice || 0); return ( {index + 1} {item.itemCode || "-"} {item.itemName} {item.specification || "-"} {item.quantity?.toLocaleString('ko-KR')} {item.unit} {item.estimatedUnitPrice ? item.estimatedUnitPrice.toLocaleString('ko-KR') + "원" : "-"} {subtotal > 0 ? subtotal.toLocaleString('ko-KR') + "원" : "-"} {request.items[0]?.remarks && ( {item.remarks || "-"} )} ); })} 총 합계
{new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' }).format(totalAmount)}
{request.items[0]?.remarks && }
)}
첨부파일 구매 요청 관련 문서 및 파일
{isLoadingFiles ? (

파일 목록을 불러오는 중...

) : attachments.length === 0 ? (

첨부된 파일이 없습니다

) : (

총 {attachments.length}개의 파일이 첨부되어 있습니다

{attachments.map((file, index) => { const fileInfo = getFileInfo(file.originalFileName || file.fileName); return ( {file.originalFileName || file.fileName} {file.fileSize} {file.createdAt && ( {format(new Date(file.createdAt), "yyyy-MM-dd HH:mm")} )} {file.category && ( {file.category} )}
{fileInfo.canPreview && ( )}
); })}
{/* 파일 종류별 요약 */} {attachments.length > 0 && (

파일 요약

총 파일 수

{attachments.length}개

총 용량

{formatFileSize( attachments.reduce((sum, file) => sum + (file.fileSize || 0), 0) )}

최근 업로드

{attachments[0]?.createdAt ? format(new Date(attachments[0].createdAt), "yyyy-MM-dd") : "-"}

)}
)}
처리 이력 요청서의 처리 현황을 시간순으로 확인할 수 있습니다
{/* 생성 */}

요청서 작성

{request.createdByName} ({request.createdByEmail})

{request.createdAt && format(new Date(request.createdAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}

{/* 확정 */} {request.confirmedAt && (

요청 확정

{request.confirmedByName}

{format(new Date(request.confirmedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}

)} {/* 반려 */} {request.status === "반려" && request.rejectReason && (

반려됨

{request.rejectReason}

{request.updatedAt && format(new Date(request.updatedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}

)} {/* RFQ 생성 */} {request.rfqCreatedAt && (

RFQ 생성 완료

RFQ 번호: {request.rfqCode}

{format(new Date(request.rfqCreatedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}

)} {/* 최종 수정 */} {request.updatedAt && request.updatedAt !== request.createdAt && !request.rfqCreatedAt && request.status !== "반려" && (

최종 수정

{request.updatedByName} ({request.updatedByEmail})

{format(new Date(request.updatedAt), "yyyy-MM-dd HH:mm:ss", { locale: ko })}

)}
{request.status === "작성중" && ( )}
); }