164 lines
6.4 KiB
TypeScript
164 lines
6.4 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from 'react';
|
|
import { X, Eye, Mail, Code, Maximize2, Minimize2 } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { MailTemplate, renderTemplateToHtml, extractTemplateVariables } from '@/lib/api/mail';
|
|
|
|
interface MailTemplatePreviewModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
template: MailTemplate | null;
|
|
}
|
|
|
|
export default function MailTemplatePreviewModal({
|
|
isOpen,
|
|
onClose,
|
|
template,
|
|
}: MailTemplatePreviewModalProps) {
|
|
const [viewMode, setViewMode] = useState<'preview' | 'code'>('preview');
|
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
const [variables, setVariables] = useState<Record<string, string>>({});
|
|
|
|
if (!isOpen || !template) return null;
|
|
|
|
const templateVariables = extractTemplateVariables(template);
|
|
const renderedHtml = renderTemplateToHtml(template, variables);
|
|
|
|
const handleVariableChange = (key: string, value: string) => {
|
|
setVariables((prev) => ({ ...prev, [key]: value }));
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
|
<div
|
|
className={`bg-white rounded-xl shadow-2xl overflow-hidden transition-all ${
|
|
isFullscreen ? 'w-full h-full' : 'max-w-6xl w-full max-h-[90vh]'
|
|
}`}
|
|
>
|
|
{/* 헤더 */}
|
|
<div className="sticky top-0 bg-gradient-to-r from-orange-500 to-orange-600 px-6 py-4 flex items-center justify-between z-10">
|
|
<div className="flex items-center gap-3">
|
|
<Eye className="w-6 h-6 text-white" />
|
|
<div>
|
|
<h2 className="text-xl font-bold text-white">{template.name}</h2>
|
|
<p className="text-sm text-orange-100">{template.subject}</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={() => setViewMode(viewMode === 'preview' ? 'code' : 'preview')}
|
|
className="text-white hover:bg-white/20 rounded-lg px-3 py-2 transition flex items-center gap-2"
|
|
>
|
|
{viewMode === 'preview' ? (
|
|
<>
|
|
<Code className="w-4 h-4" />
|
|
코드
|
|
</>
|
|
) : (
|
|
<>
|
|
<Eye className="w-4 h-4" />
|
|
미리보기
|
|
</>
|
|
)}
|
|
</button>
|
|
<button
|
|
onClick={() => setIsFullscreen(!isFullscreen)}
|
|
className="text-white hover:bg-white/20 rounded-lg p-2 transition"
|
|
>
|
|
{isFullscreen ? (
|
|
<Minimize2 className="w-5 h-5" />
|
|
) : (
|
|
<Maximize2 className="w-5 h-5" />
|
|
)}
|
|
</button>
|
|
<button
|
|
onClick={onClose}
|
|
className="text-white hover:bg-white/20 rounded-lg p-2 transition"
|
|
>
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 본문 */}
|
|
<div className="flex h-full overflow-hidden">
|
|
{/* 왼쪽: 변수 입력 (변수가 있을 때만) */}
|
|
{templateVariables.length > 0 && (
|
|
<div className="w-80 bg-gray-50 border-r border-gray-200 p-6 overflow-y-auto">
|
|
<h3 className="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
|
<Mail className="w-5 h-5 text-orange-500" />
|
|
템플릿 변수
|
|
</h3>
|
|
<div className="space-y-4">
|
|
{templateVariables.map((variable) => (
|
|
<div key={variable}>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
{variable}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={variables[variable] || ''}
|
|
onChange={(e) => handleVariableChange(variable, e.target.value)}
|
|
placeholder={`{${variable}}`}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="mt-6 p-4 bg-accent border border-primary/20 rounded-lg">
|
|
<p className="text-xs text-blue-800">
|
|
💡 변수 값을 입력하면 미리보기에 반영됩니다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 오른쪽: 미리보기 또는 코드 */}
|
|
<div className="flex-1 overflow-y-auto p-6">
|
|
{viewMode === 'preview' ? (
|
|
<div className="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden">
|
|
{/* 이메일 헤더 시뮬레이션 */}
|
|
<div className="bg-gray-100 px-6 py-4 border-b border-gray-200">
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex">
|
|
<span className="font-semibold text-muted-foreground w-20">제목:</span>
|
|
<span className="text-gray-900">{template.subject}</span>
|
|
</div>
|
|
<div className="flex">
|
|
<span className="font-semibold text-muted-foreground w-20">발신:</span>
|
|
<span className="text-gray-700">your-email@company.com</span>
|
|
</div>
|
|
<div className="flex">
|
|
<span className="font-semibold text-muted-foreground w-20">수신:</span>
|
|
<span className="text-gray-700">recipient@example.com</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 이메일 본문 */}
|
|
<div
|
|
className="p-6"
|
|
dangerouslySetInnerHTML={{ __html: renderedHtml }}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="bg-gray-900 text-gray-100 p-6 rounded-lg font-mono text-sm overflow-x-auto">
|
|
<pre className="whitespace-pre-wrap break-words">{renderedHtml}</pre>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 푸터 */}
|
|
<div className="sticky bottom-0 bg-gray-50 border-t border-gray-200 px-6 py-4 flex justify-end gap-3">
|
|
<Button variant="outline" onClick={onClose}>
|
|
닫기
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|