2025-10-01 16:15:53 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import { Mail, Edit2, Trash2, Eye, Copy, Calendar } from 'lucide-react';
|
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
import { MailTemplate } from '@/lib/api/mail';
|
|
|
|
|
|
|
|
|
|
interface MailTemplateCardProps {
|
|
|
|
|
template: MailTemplate;
|
|
|
|
|
onEdit: (template: MailTemplate) => void;
|
|
|
|
|
onDelete: (template: MailTemplate) => void;
|
|
|
|
|
onPreview: (template: MailTemplate) => void;
|
|
|
|
|
onDuplicate?: (template: MailTemplate) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function MailTemplateCard({
|
|
|
|
|
template,
|
|
|
|
|
onEdit,
|
|
|
|
|
onDelete,
|
|
|
|
|
onPreview,
|
|
|
|
|
onDuplicate,
|
|
|
|
|
}: MailTemplateCardProps) {
|
|
|
|
|
const formatDate = (dateString: string) => {
|
|
|
|
|
return new Date(dateString).toLocaleDateString('ko-KR', {
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
month: '2-digit',
|
|
|
|
|
day: '2-digit',
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getCategoryColor = (category?: string) => {
|
|
|
|
|
const colors: Record<string, string> = {
|
2025-10-02 14:34:15 +09:00
|
|
|
welcome: 'bg-primary/20 text-blue-700 border-blue-300',
|
2025-10-01 16:15:53 +09:00
|
|
|
promotion: 'bg-purple-100 text-purple-700 border-purple-300',
|
|
|
|
|
notification: 'bg-green-100 text-green-700 border-green-300',
|
|
|
|
|
newsletter: 'bg-orange-100 text-orange-700 border-orange-300',
|
2025-10-13 15:17:34 +09:00
|
|
|
system: 'bg-muted text-foreground border',
|
2025-10-01 16:15:53 +09:00
|
|
|
};
|
2025-10-13 15:17:34 +09:00
|
|
|
return colors[category || ''] || 'bg-muted text-foreground border';
|
2025-10-01 16:15:53 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
2025-10-13 15:17:34 +09:00
|
|
|
<div className="group bg-white rounded-xl border border shadow-sm hover:shadow-lg transition-all duration-300 overflow-hidden">
|
2025-10-01 16:15:53 +09:00
|
|
|
{/* 헤더 */}
|
2025-10-13 15:17:34 +09:00
|
|
|
<div className="bg-gradient-to-r from-muted to-muted p-4 border-b border">
|
2025-10-01 16:15:53 +09:00
|
|
|
<div className="flex items-start justify-between">
|
|
|
|
|
<div className="flex items-start gap-3 flex-1">
|
|
|
|
|
<div className="p-2 bg-white rounded-lg shadow-sm">
|
2025-10-13 15:17:34 +09:00
|
|
|
<Mail className="w-5 h-5 text-primary" />
|
2025-10-01 16:15:53 +09:00
|
|
|
</div>
|
|
|
|
|
<div className="flex-1 min-w-0">
|
2025-10-13 15:17:34 +09:00
|
|
|
<h3 className="font-semibold text-foreground truncate">
|
2025-10-01 16:15:53 +09:00
|
|
|
{template.name}
|
|
|
|
|
</h3>
|
2025-10-02 14:34:15 +09:00
|
|
|
<p className="text-sm text-muted-foreground truncate mt-1">
|
2025-10-01 16:15:53 +09:00
|
|
|
{template.subject}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{template.category && (
|
|
|
|
|
<span
|
|
|
|
|
className={`text-xs px-2 py-1 rounded-full border ${getCategoryColor(
|
|
|
|
|
template.category
|
|
|
|
|
)}`}
|
|
|
|
|
>
|
|
|
|
|
{template.category}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 본문 미리보기 */}
|
|
|
|
|
<div className="p-4 space-y-3">
|
2025-10-13 15:17:34 +09:00
|
|
|
<div className="bg-muted/30 rounded-lg p-3 border border min-h-[100px]">
|
|
|
|
|
<p className="text-xs text-muted-foreground mb-2">컴포넌트 {template.components.length}개</p>
|
2025-10-01 16:15:53 +09:00
|
|
|
<div className="space-y-1">
|
|
|
|
|
{template.components.slice(0, 3).map((component, idx) => (
|
2025-10-02 14:34:15 +09:00
|
|
|
<div key={idx} className="flex items-center gap-2 text-xs text-muted-foreground">
|
2025-10-01 16:15:53 +09:00
|
|
|
<div className="w-1.5 h-1.5 rounded-full bg-orange-400" />
|
|
|
|
|
<span className="capitalize">{component.type}</span>
|
|
|
|
|
{component.type === 'text' && component.content && (
|
|
|
|
|
<span className="text-gray-400 truncate flex-1">
|
|
|
|
|
{component.content.replace(/<[^>]*>/g, '').substring(0, 30)}...
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
{template.components.length > 3 && (
|
|
|
|
|
<p className="text-xs text-gray-400 pl-3.5">
|
|
|
|
|
+{template.components.length - 3}개 더보기
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 메타 정보 */}
|
2025-10-13 15:17:34 +09:00
|
|
|
<div className="flex items-center gap-3 text-xs text-muted-foreground pt-2 border-t">
|
2025-10-01 16:15:53 +09:00
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
<Calendar className="w-3.5 h-3.5" />
|
|
|
|
|
<span>{formatDate(template.createdAt)}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{template.updatedAt !== template.createdAt && (
|
|
|
|
|
<span className="text-gray-400">수정됨</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 액션 버튼 */}
|
|
|
|
|
<div className="p-4 pt-0 flex gap-2">
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
2025-10-02 14:34:15 +09:00
|
|
|
className="flex-1 hover:bg-accent hover:text-primary hover:border-blue-300"
|
2025-10-01 16:15:53 +09:00
|
|
|
onClick={() => onPreview(template)}
|
|
|
|
|
>
|
|
|
|
|
<Eye className="w-4 h-4 mr-1" />
|
|
|
|
|
미리보기
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="flex-1 hover:bg-green-50 hover:text-green-600 hover:border-green-300"
|
|
|
|
|
onClick={() => onEdit(template)}
|
|
|
|
|
>
|
|
|
|
|
<Edit2 className="w-4 h-4 mr-1" />
|
|
|
|
|
수정
|
|
|
|
|
</Button>
|
|
|
|
|
{onDuplicate && (
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="hover:bg-purple-50 hover:text-purple-600 hover:border-purple-300"
|
|
|
|
|
onClick={() => onDuplicate(template)}
|
|
|
|
|
>
|
|
|
|
|
<Copy className="w-4 h-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
2025-10-02 14:34:15 +09:00
|
|
|
className="hover:bg-destructive/10 hover:text-destructive hover:border-red-300"
|
2025-10-01 16:15:53 +09:00
|
|
|
onClick={() => onDelete(template)}
|
|
|
|
|
>
|
|
|
|
|
<Trash2 className="w-4 h-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|