"use client" import * as React 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Badge } from "@/components/ui/badge" import { Eye, Smartphone, Monitor, Tablet, RefreshCw, Copy, Download, Send } from "lucide-react" import { toast } from "sonner" import { type TemplateWithVariables } from "@/db/schema" import { previewTemplateAction } from "../service" import { copyTextToClipboard } from "@/lib/utils" interface TemplatePreviewProps { template: TemplateWithVariables } type ViewMode = 'desktop' | 'tablet' | 'mobile' export function TemplatePreview({ template }: TemplatePreviewProps) { const [previewData, setPreviewData] = React.useState>( template.sampleData || {} ) const [previewHtml, setPreviewHtml] = React.useState("") const [isLoading, setIsLoading] = React.useState(false) const [viewMode, setViewMode] = React.useState('desktop') const [lastUpdated, setLastUpdated] = React.useState(null) // 미리보기 생성 const generatePreview = async () => { setIsLoading(true) try { const result = await previewTemplateAction( template.slug, previewData, template.content ) if (result.success) { setPreviewHtml(result.data.html) setLastUpdated(new Date()) toast.success('미리보기가 생성되었습니다.') } else { toast.error("샘플데이터를 먼저 생성해주세요." || '미리보기 생성에 실패했습니다.') } } catch (error) { toast.error('미리보기 생성 중 오류가 발생했습니다.') } finally { setIsLoading(false) } } // 초기 미리보기 생성 React.useEffect(() => { generatePreview() }, []) // 샘플 데이터 업데이트 const updatePreviewData = (key: string, value: any) => { setPreviewData(prev => ({ ...prev, [key]: value })) } // 샘플 데이터 리셋 const resetToDefaults = () => { const defaultData: Record = {} template.variables.forEach(variable => { if (variable.defaultValue) { switch (variable.variableType) { case 'number': defaultData[variable.variableName] = parseFloat(variable.defaultValue) || 0 break case 'boolean': defaultData[variable.variableName] = variable.defaultValue.toLowerCase() === 'true' break default: defaultData[variable.variableName] = variable.defaultValue } } else { // 기본 샘플 값 switch (variable.variableType) { case 'string': defaultData[variable.variableName] = `샘플 ${variable.variableName}` break case 'number': defaultData[variable.variableName] = 123 break case 'boolean': defaultData[variable.variableName] = true break case 'date': defaultData[variable.variableName] = new Date().toLocaleDateString('ko-KR') break } } }) setPreviewData(defaultData) toast.success('샘플 데이터가 초기화되었습니다.') } // HTML 복사 const copyHtml = async () => { if (!previewHtml) { toast.error("미리보기를 먼저 생성해주세요.") return } const ok = await copyTextToClipboard(previewHtml) ok ? toast.success("HTML이 클립보드에 복사되었습니다.") : toast.error("복사에 실패했습니다.") } // HTML 다운로드 const downloadHtml = () => { if (!previewHtml) { toast.error('미리보기를 먼저 생성해주세요.') return } const blob = new Blob([previewHtml], { type: 'text/html' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = `${template.slug}-preview.html` document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) toast.success('HTML 파일이 다운로드되었습니다.') } // 뷰포트 크기 const getViewportStyle = (): React.CSSProperties => { switch (viewMode) { case 'mobile': return { width: '375px', height: '667px' } case 'tablet': return { width: '768px', height: '1024px' } case 'desktop': default: return { width: '100%', height: '600px' } } } // 입력 컴포넌트 렌더링 const renderInputForVariable = (variable: any) => { const value = previewData[variable.variableName] || '' switch (variable.variableType) { case 'boolean': return ( ) case 'number': return ( updatePreviewData(variable.variableName, parseFloat(e.target.value) || 0)} placeholder="숫자를 입력하세요" /> ) case 'date': return ( updatePreviewData(variable.variableName, e.target.value)} /> ) default: // string 타입이거나 긴 텍스트인 경우 if (variable.variableName.toLowerCase().includes('message') || variable.variableName.toLowerCase().includes('content') || variable.variableName.toLowerCase().includes('description')) { return (