"use client"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Printer, FileDown, FileText } from "lucide-react"; import { useReportDesigner } from "@/contexts/ReportDesignerContext"; import { useState } from "react"; import { useToast } from "@/hooks/use-toast"; import { Document, Packer, Paragraph, TextRun, Table, TableCell, TableRow, WidthType } from "docx"; import { getFullImageUrl } from "@/lib/api/client"; interface ReportPreviewModalProps { isOpen: boolean; onClose: () => void; } export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) { const { components, canvasWidth, canvasHeight, getQueryResult, reportDetail } = useReportDesigner(); const [isExporting, setIsExporting] = useState(false); const { toast } = useToast(); // 컴포넌트의 실제 표시 값 가져오기 const getComponentValue = (component: any): string => { if (component.queryId && component.fieldName) { const queryResult = getQueryResult(component.queryId); if (queryResult && queryResult.rows.length > 0) { const value = queryResult.rows[0][component.fieldName]; if (value !== null && value !== undefined) { return String(value); } } return `{${component.fieldName}}`; } return component.defaultValue || "텍스트"; }; const handlePrint = () => { // 현재 미리보기 영역만 인쇄 const printContent = document.getElementById("preview-content"); if (!printContent) return; const printWindow = window.open("", "_blank"); if (!printWindow) return; printWindow.document.write(` 리포트 인쇄 ${printContent.innerHTML} `); printWindow.document.close(); printWindow.print(); }; // PDF 다운로드 (브라우저 인쇄 기능 이용) const handleDownloadPDF = () => { const printContent = document.getElementById("preview-content"); if (!printContent) return; const printWindow = window.open("", "_blank"); if (!printWindow) return; printWindow.document.write(` 리포트 인쇄 ${printContent.innerHTML} `); printWindow.document.close(); toast({ title: "안내", description: "인쇄 대화상자에서 'PDF로 저장'을 선택하세요.", }); }; // WORD 다운로드 const handleDownloadWord = async () => { setIsExporting(true); try { // 컴포넌트를 Paragraph로 변환 const paragraphs: (Paragraph | Table)[] = []; // Y 좌표로 정렬 const sortedComponents = [...components].sort((a, b) => a.y - b.y); for (const component of sortedComponents) { if (component.type === "text" || component.type === "label") { const value = getComponentValue(component); paragraphs.push( new Paragraph({ children: [ new TextRun({ text: value, size: (component.fontSize || 13) * 2, // pt to half-pt color: component.fontColor?.replace("#", "") || "000000", bold: component.fontWeight === "bold", }), ], spacing: { after: 200, }, }), ); } else if (component.type === "table" && component.queryId) { const queryResult = getQueryResult(component.queryId); if (queryResult && queryResult.rows.length > 0) { // 테이블 헤더 const headerCells = queryResult.fields.map( (field) => new TableCell({ children: [new Paragraph({ text: field })], width: { size: 100 / queryResult.fields.length, type: WidthType.PERCENTAGE }, }), ); // 테이블 행 const dataRows = queryResult.rows.map( (row) => new TableRow({ children: queryResult.fields.map( (field) => new TableCell({ children: [new Paragraph({ text: String(row[field] ?? "") })], }), ), }), ); const table = new Table({ rows: [new TableRow({ children: headerCells }), ...dataRows], width: { size: 100, type: WidthType.PERCENTAGE }, }); paragraphs.push(table); } } } // 문서 생성 const doc = new Document({ sections: [ { properties: {}, children: paragraphs, }, ], }); // Blob 생성 및 다운로드 const blob = await Packer.toBlob(doc); const fileName = reportDetail?.report?.report_name_kor || "리포트"; const timestamp = new Date().toISOString().slice(0, 10); const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = `${fileName}_${timestamp}.docx`; link.click(); window.URL.revokeObjectURL(url); toast({ title: "성공", description: "WORD 파일이 다운로드되었습니다.", }); } catch (error) { const errorMessage = error instanceof Error ? error.message : "WORD 생성에 실패했습니다."; toast({ title: "오류", description: errorMessage, variant: "destructive", }); } finally { setIsExporting(false); } }; return ( 미리보기 현재 레이아웃의 미리보기입니다. 인쇄하거나 파일로 다운로드할 수 있습니다. {/* 미리보기 영역 */}
{components.map((component) => { const displayValue = getComponentValue(component); const queryResult = component.queryId ? getQueryResult(component.queryId) : null; return (
{component.type === "text" && (
{displayValue}
)} {component.type === "label" && (
{displayValue}
)} {component.type === "table" && queryResult && queryResult.rows.length > 0 ? ( {queryResult.fields.map((field) => ( ))} {queryResult.rows.map((row, idx) => ( {queryResult.fields.map((field) => ( ))} ))}
{field}
{String(row[field] ?? "")}
) : component.type === "table" ? (
쿼리를 실행해주세요
) : null} {component.type === "image" && component.imageUrl && ( 이미지 )} {component.type === "divider" && (
)}
); })}
); }