From fb20768fa881841d3f80d12a276a9445feb6f514 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 30 Sep 2025 05:04:33 +0000 Subject: (고건) 이메일 템플릿 정보 수정/복제 기능 에러 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/email-template/editor/template-settings.tsx | 205 ++++++++++++------------ 1 file changed, 100 insertions(+), 105 deletions(-) (limited to 'lib/email-template/editor/template-settings.tsx') diff --git a/lib/email-template/editor/template-settings.tsx b/lib/email-template/editor/template-settings.tsx index f253f87d..99ef5443 100644 --- a/lib/email-template/editor/template-settings.tsx +++ b/lib/email-template/editor/template-settings.tsx @@ -1,24 +1,6 @@ -"use client" +'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 { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card" +/* IMPORT */ import { AlertDialog, AlertDialogAction, @@ -29,68 +11,92 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog" -import { Badge } from "@/components/ui/badge" -import { Separator } from "@/components/ui/separator" +} from '@/components/ui/alert-dialog'; import { - Save, - Trash2, AlertTriangle, - Info, Calendar, - User, + Copy, Hash, - Copy -} from "lucide-react" -import { toast } from "sonner" -import { useRouter } from "next/navigation" -import { type TemplateWithVariables } from "@/db/schema" -import { deleteTemplate, duplicateTemplate, updateTemplateAction } from "../service" -import { TEMPLATE_CATEGORY_OPTIONS, getCategoryDisplayName } from "../validations" + Info, + Save, + Trash2, + User, +} from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { deleteTemplate, duplicateTemplate, getCurrentUserId, updateTemplateAction } from '../service'; +import { getCategoryDisplayName, TEMPLATE_CATEGORY_OPTIONS } from '../validations'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Separator } from '@/components/ui/separator'; +import { Textarea } from '@/components/ui/textarea'; +import { toast } from 'sonner'; +import { type TemplateWithVariables } from '@/db/schema'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +// ---------------------------------------------------------------------------------------------------- +/* TYPES */ interface TemplateSettingsProps { - template: TemplateWithVariables - onUpdate: (template: TemplateWithVariables) => void + template: TemplateWithVariables; + onUpdate: (template: TemplateWithVariables) => void; } +// ---------------------------------------------------------------------------------------------------- + export function TemplateSettings({ template, onUpdate }: TemplateSettingsProps) { - const router = useRouter() - const [isLoading, setIsLoading] = React.useState(false) - const [formData, setFormData] = React.useState({ + const router = useRouter(); + const [isLoading, setIsLoading] = useState(false); + const [formData, setFormData] = useState({ name: template.name, description: template.description || '', category: template.category || '', sampleData: JSON.stringify(template.sampleData || {}, null, 2) - }) + }); // 폼 데이터 업데이트 const updateFormData = (field: keyof typeof formData, value: string) => { setFormData(prev => ({ ...prev, [field]: value })) - } + }; // 기본 정보 저장 const handleSaveBasicInfo = async () => { - setIsLoading(true) + setIsLoading(true); try { // 샘플 데이터 JSON 파싱 검증 - let parsedSampleData = {} + let parsedSampleData = {}; if (formData.sampleData.trim()) { try { - parsedSampleData = JSON.parse(formData.sampleData) + parsedSampleData = JSON.parse(formData.sampleData); } catch (error) { - toast.error('샘플 데이터 JSON 형식이 올바르지 않습니다.') - setIsLoading(false) - return + toast.error('샘플 데이터 JSON 형식이 올바르지 않습니다.'); + setIsLoading(false); + return; } } const result = await updateTemplateAction(template.slug, { name: formData.name, description: formData.description || undefined, + category: formData.category, sampleData: parsedSampleData, - updatedBy: 'current-user-id' // TODO: 실제 사용자 ID - }) + updatedBy: await getCurrentUserId(), + }); if (result.success) { toast.success('템플릿 설정이 저장되었습니다.') @@ -98,62 +104,63 @@ export function TemplateSettings({ template, onUpdate }: TemplateSettingsProps) ...template, name: formData.name, description: formData.description, + category: formData.category, sampleData: parsedSampleData, - version: template.version + 1 - }) + version: template.version ? template.version + 1 : 1, + }); } else { - toast.error(result.error || '저장에 실패했습니다.') + toast.error(result.error || '저장에 실패했습니다.'); } } catch (error) { - toast.error('저장 중 오류가 발생했습니다.') + toast.error('저장 중 오류가 발생했습니다.'); } finally { - setIsLoading(false) + setIsLoading(false); } } // 템플릿 복제 const handleDuplicate = async () => { - setIsLoading(true) + setIsLoading(true); try { - const copyName = `${template.name} (복사본)` - const copySlug = `${template.slug}-copy-${Date.now()}` + const copyName = `${template.name} (복사본)`; + const copySlug = `${template.slug}-copy-${Date.now()}`; const result = await duplicateTemplate( template.id, copyName, copySlug, - 'current-user-id' // TODO: 실제 사용자 ID - ) + await getCurrentUserId(), + ); if (result.success && result.data) { - toast.success('템플릿이 복제되었습니다.') - router.push(`/evcp/templates/${result.data.slug}`) + toast.success('템플릿이 복제되었습니다.'); + router.push(`/evcp/email-template/${result.data.slug}`); } else { - toast.error(result.error || '복제에 실패했습니다.') + toast.error(result.error || '복제에 실패했습니다.'); } } catch (error) { - toast.error('복제 중 오류가 발생했습니다.') + toast.error('복제 중 오류가 발생했습니다.'); } finally { - setIsLoading(false) + setIsLoading(false); } } // 템플릿 삭제 const handleDelete = async () => { - setIsLoading(true) + setIsLoading(true); try { - const result = await deleteTemplate(template.id) + const result = await deleteTemplate(template.id); if (result.success) { - toast.success('템플릿이 삭제되었습니다.') - router.push('/evcp/templates') + toast.success('템플릿이 삭제되었습니다.'); + router.push('/evcp/email-template'); } else { - toast.error(result.error || '삭제에 실패했습니다.') + toast.error(result.error || '삭제에 실패했습니다.'); } } catch (error) { - toast.error('삭제 중 오류가 발생했습니다.') + toast.error('삭제 중 오류가 발생했습니다.'); } finally { - setIsLoading(false) + setIsLoading(false); } } @@ -215,7 +222,6 @@ export function TemplateSettings({ template, onUpdate }: TemplateSettingsProps) placeholder="템플릿 이름을 입력하세요" /> -