From e9897d416b3e7327bbd4d4aef887eee37751ae82 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 27 Jun 2025 01:16:20 +0000
Subject: (대표님) 20250627 오전 10시 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/mail/mail-template-editor-client.tsx | 255 ++++++++++++++++++++++++
 1 file changed, 255 insertions(+)
 create mode 100644 components/mail/mail-template-editor-client.tsx

(limited to 'components/mail/mail-template-editor-client.tsx')

diff --git a/components/mail/mail-template-editor-client.tsx b/components/mail/mail-template-editor-client.tsx
new file mode 100644
index 00000000..dfbeb4e0
--- /dev/null
+++ b/components/mail/mail-template-editor-client.tsx
@@ -0,0 +1,255 @@
+'use client';
+
+import { useState, useEffect, useTransition } from 'react';
+import { useRouter, useParams } from 'next/navigation';
+import { Button } from '@/components/ui/button';
+import { Label } from '@/components/ui/label';
+import { Textarea } from '@/components/ui/textarea';
+import { Save, Eye } from 'lucide-react';
+import { toast } from 'sonner';
+import Link from 'next/link';
+import { getTemplateAction, updateTemplateAction, previewTemplateAction, TemplateFile } from '@/lib/mail/service';
+
+type Template = TemplateFile;
+
+interface MailTemplateEditorClientProps {
+  templateName: string;
+  initialTemplate?: Template | null;
+}
+
+export default function MailTemplateEditorClient({ 
+  templateName, 
+  initialTemplate 
+}: MailTemplateEditorClientProps) {
+  const router = useRouter();
+  const params = useParams();
+  
+  const lng = (params?.lng as string) || 'ko';
+
+  const [template, setTemplate] = useState<Template | null>(initialTemplate || null);
+  const [content, setContent] = useState(initialTemplate?.content || '');
+  const [loading, setLoading] = useState(!initialTemplate);
+  const [saving, setSaving] = useState(false);
+  const [previewLoading, setPreviewLoading] = useState(false);
+  const [previewHtml, setPreviewHtml] = useState<string | null>(null);
+  const [, startTransition] = useTransition();
+
+  // 템플릿 조회
+  const fetchTemplate = async () => {
+    if (!templateName) {
+      toast.error('잘못된 접근입니다.');
+      router.push(`/${lng}/evcp/email-template`);
+      return;
+    }
+
+    try {
+      setLoading(true);
+      startTransition(async () => {
+        const result = await getTemplateAction(templateName);
+
+        if (result.success && result.data) {
+          setTemplate(result.data);
+          setContent(result.data.content);
+        } else {
+          toast.error(result.error || '템플릿을 찾을 수 없습니다.');
+          router.push(`/${lng}/evcp/email-template`);
+        }
+        setLoading(false);
+      });
+    } catch (error) {
+      console.error('Error fetching template:', error);
+      toast.error('템플릿을 불러오는데 실패했습니다.');
+      router.push(`/${lng}/evcp/email-template`);
+      setLoading(false);
+    }
+  };
+
+  // 템플릿 저장
+  const handleSave = async () => {
+    if (!content.trim()) {
+      toast.error('템플릿 내용을 입력해주세요.');
+      return;
+    }
+
+    try {
+      setSaving(true);
+      startTransition(async () => {
+        const result = await updateTemplateAction(templateName, content);
+
+        if (result.success && result.data) {
+          toast.success('템플릿이 성공적으로 저장되었습니다.');
+          setTemplate(result.data);
+        } else {
+          toast.error(result.error || '템플릿 저장에 실패했습니다.');
+        }
+        setSaving(false);
+      });
+    } catch (error) {
+      console.error('Error saving template:', error);
+      toast.error('템플릿 저장에 실패했습니다.');
+      setSaving(false);
+    }
+  };
+
+  // 미리보기 생성
+  const handlePreview = async () => {
+    try {
+      setPreviewLoading(true);
+      startTransition(async () => {
+        const result = await previewTemplateAction(
+          templateName,
+          {
+            userName: '홍길동',
+            companyName: 'EVCP',
+            email: 'user@example.com',
+            date: new Date().toLocaleDateString('ko-KR'),
+            projectName: '샘플 프로젝트',
+            message: '이것은 샘플 메시지입니다.',
+            currentYear: new Date().getFullYear(),
+            language: 'ko',
+            name: '홍길동',
+            loginUrl: 'https://example.com/login'
+          }
+        );
+
+        if (result.success && result.data) {
+          setPreviewHtml(result.data.html);
+        } else {
+          toast.error(result.error || '미리보기 생성에 실패했습니다.');
+        }
+        setPreviewLoading(false);
+      });
+    } catch (error) {
+      console.error('Error generating preview:', error);
+      toast.error('미리보기 생성에 실패했습니다.');
+      setPreviewLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    if (!initialTemplate) {
+      fetchTemplate();
+    }
+  }, [templateName, initialTemplate]);
+
+  if (loading) {
+    return (
+      <div className="text-center py-20">
+        <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
+        <p className="mt-4 text-gray-600">템플릿을 불러오는 중...</p>
+      </div>
+    );
+  }
+
+  if (!template) {
+    return (
+      <div className="text-center py-20">
+        <p className="text-gray-600">템플릿을 찾을 수 없습니다.</p>
+        <Link href={`/${lng}/evcp/email-template`}>
+          <Button className="mt-4">목록으로 돌아가기</Button>
+        </Link>
+      </div>
+    );
+  }
+
+  return (
+    <div className="space-y-8">
+      {/* 헤더 */}
+      <div>
+        <div className="flex items-center gap-4 mb-4">
+          <h1 className="text-3xl font-bold text-gray-900">템플릿 편집</h1>
+        </div>
+        <div>
+          <p className="text-gray-600">
+            <span className="font-medium">{template.name}</span> 템플릿을 편집합니다.
+          </p>
+          <p className="text-sm text-gray-500">
+            마지막 수정: {new Date(template.lastModified).toLocaleString('ko-KR')}
+          </p>
+        </div>
+      </div>
+
+      <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
+        {/* 편집 영역 */}
+        <div className="space-y-6">
+          <div className="bg-white p-6 rounded-lg shadow">
+            <h2 className="text-xl font-semibold mb-4">템플릿 내용</h2>
+            
+            <div className="space-y-4">
+              <div>
+                <Label htmlFor="content">Handlebars 템플릿</Label>
+                <Textarea
+                  id="content"
+                  value={content}
+                  onChange={(e) => setContent(e.target.value)}
+                  className="min-h-[500px] font-mono text-sm"
+                />
+              </div>
+
+              <div className="flex gap-2">
+                <Button onClick={handleSave} disabled={saving}>
+                  <Save className="h-4 w-4 mr-2" />
+                  {saving ? '저장 중...' : '저장'}
+                </Button>
+                <Button variant="outline" onClick={handlePreview} disabled={previewLoading}>
+                  <Eye className="h-4 w-4 mr-2" />
+                  {previewLoading ? '생성 중...' : '미리보기'}
+                </Button>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* 미리보기 영역 */}
+        <div className="space-y-6">
+          <div className="bg-white p-6 rounded-lg shadow">
+            <h2 className="text-xl font-semibold mb-4">빠른 미리보기</h2>
+            
+            <div className="border rounded-lg p-4 min-h-[500px] bg-gray-50 overflow-auto">
+              {previewHtml ? (
+                <div 
+                  className="preview-content"
+                  dangerouslySetInnerHTML={{ __html: previewHtml }}
+                />
+              ) : (
+                <div className="text-center text-gray-500 py-20">
+                  미리보기 버튼을 클릭하여 결과를 확인하세요.
+                </div>
+              )}
+            </div>
+          </div>
+
+          {/* 도움말 */}
+          <div className="bg-blue-50 p-4 rounded-lg">
+            <h3 className="font-semibold text-blue-900 mb-2">Handlebars 문법 도움말</h3>
+            <div className="text-sm text-blue-800 space-y-1">
+              <p><code>{`{{variable}}`}</code> - 변수 출력</p>
+              <p><code>{`{{{html}}}`}</code> - HTML 출력 (이스케이프 없음)</p>
+              <p><code>{`{{#if condition}}`}</code> - 조건문</p>
+              <p><code>{`{{#each items}}`}</code> - 반복문</p>
+            </div>
+          </div>
+
+          {/* 샘플 데이터 */}
+          <div className="bg-gray-50 p-4 rounded-lg">
+            <h3 className="font-semibold text-gray-900 mb-2">미리보기 샘플 데이터</h3>
+            <pre className="text-xs text-gray-600 overflow-auto">
+{JSON.stringify({
+  userName: '홍길동',
+  companyName: 'EVCP',
+  email: 'user@example.com',
+  date: new Date().toLocaleDateString('ko-KR'),
+  projectName: '샘플 프로젝트',
+  message: '이것은 샘플 메시지입니다.',
+  currentYear: new Date().getFullYear(),
+  language: 'ko',
+  name: '홍길동',
+  loginUrl: 'https://example.com/login'
+}, null, 2)}
+            </pre>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+} 
\ No newline at end of file
-- 
cgit v1.2.3

