메일 관리 작업 저장용 커밋
This commit is contained in:
163
frontend/components/mail/MailTemplatePreviewModal.tsx
Normal file
163
frontend/components/mail/MailTemplatePreviewModal.tsx
Normal file
@@ -0,0 +1,163 @@
|
||||
"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-blue-50 border border-blue-200 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-gray-600 w-20">제목:</span>
|
||||
<span className="text-gray-900">{template.subject}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-semibold text-gray-600 w-20">발신:</span>
|
||||
<span className="text-gray-700">your-email@company.com</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="font-semibold text-gray-600 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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user