284 lines
10 KiB
TypeScript
284 lines
10 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import React, { useState, useEffect } from "react";
|
||
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
import {
|
||
|
|
Mail,
|
||
|
|
Send,
|
||
|
|
Inbox,
|
||
|
|
FileText,
|
||
|
|
RefreshCw,
|
||
|
|
TrendingUp,
|
||
|
|
Users,
|
||
|
|
Calendar,
|
||
|
|
Clock
|
||
|
|
} from "lucide-react";
|
||
|
|
|
||
|
|
interface DashboardStats {
|
||
|
|
totalAccounts: number;
|
||
|
|
totalTemplates: number;
|
||
|
|
sentToday: number;
|
||
|
|
receivedToday: number;
|
||
|
|
sentThisMonth: number;
|
||
|
|
successRate: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function MailDashboardPage() {
|
||
|
|
const [stats, setStats] = useState<DashboardStats>({
|
||
|
|
totalAccounts: 0,
|
||
|
|
totalTemplates: 0,
|
||
|
|
sentToday: 0,
|
||
|
|
receivedToday: 0,
|
||
|
|
sentThisMonth: 0,
|
||
|
|
successRate: 0,
|
||
|
|
});
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
|
||
|
|
const loadStats = async () => {
|
||
|
|
setLoading(true);
|
||
|
|
try {
|
||
|
|
// 계정 수
|
||
|
|
const accountsRes = await fetch('/api/mail/accounts');
|
||
|
|
const accountsData = await accountsRes.json();
|
||
|
|
|
||
|
|
// 템플릿 수
|
||
|
|
const templatesRes = await fetch('/api/mail/templates-file');
|
||
|
|
const templatesData = await templatesRes.json();
|
||
|
|
|
||
|
|
setStats({
|
||
|
|
totalAccounts: accountsData.success ? accountsData.data.length : 0,
|
||
|
|
totalTemplates: templatesData.success ? templatesData.data.length : 0,
|
||
|
|
sentToday: 0, // TODO: 실제 발송 통계 API 연동
|
||
|
|
receivedToday: 0,
|
||
|
|
sentThisMonth: 0,
|
||
|
|
successRate: 0,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
// console.error('통계 로드 실패:', error);
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
loadStats();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const statCards = [
|
||
|
|
{
|
||
|
|
title: "등록된 계정",
|
||
|
|
value: stats.totalAccounts,
|
||
|
|
icon: Users,
|
||
|
|
color: "blue",
|
||
|
|
bgColor: "bg-blue-100",
|
||
|
|
iconColor: "text-blue-500",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "템플릿 수",
|
||
|
|
value: stats.totalTemplates,
|
||
|
|
icon: FileText,
|
||
|
|
color: "green",
|
||
|
|
bgColor: "bg-green-100",
|
||
|
|
iconColor: "text-green-500",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "오늘 발송",
|
||
|
|
value: stats.sentToday,
|
||
|
|
icon: Send,
|
||
|
|
color: "orange",
|
||
|
|
bgColor: "bg-orange-100",
|
||
|
|
iconColor: "text-orange-500",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: "오늘 수신",
|
||
|
|
value: stats.receivedToday,
|
||
|
|
icon: Inbox,
|
||
|
|
color: "purple",
|
||
|
|
bgColor: "bg-purple-100",
|
||
|
|
iconColor: "text-purple-500",
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gray-50">
|
||
|
|
<div className="w-full max-w-none px-4 py-8 space-y-8">
|
||
|
|
{/* 페이지 제목 */}
|
||
|
|
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||
|
|
<div>
|
||
|
|
<h1 className="text-3xl font-bold text-gray-900">메일 관리 대시보드</h1>
|
||
|
|
<p className="mt-2 text-gray-600">메일 시스템의 전체 현황을 확인합니다</p>
|
||
|
|
</div>
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
onClick={loadStats}
|
||
|
|
disabled={loading}
|
||
|
|
>
|
||
|
|
<RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />
|
||
|
|
새로고침
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 통계 카드 */}
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||
|
|
{statCards.map((stat, index) => (
|
||
|
|
<Card key={index} className="shadow-sm hover:shadow-md transition-shadow">
|
||
|
|
<CardContent className="p-6">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<div>
|
||
|
|
<p className="text-sm text-gray-500 mb-1">{stat.title}</p>
|
||
|
|
<p className="text-3xl font-bold text-gray-900">{stat.value}</p>
|
||
|
|
</div>
|
||
|
|
<div className={`${stat.bgColor} p-3 rounded-lg`}>
|
||
|
|
<stat.icon className={`w-6 h-6 ${stat.iconColor}`} />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 이번 달 통계 */}
|
||
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
|
|
<Card className="shadow-sm">
|
||
|
|
<CardHeader className="border-b bg-gradient-to-r from-slate-50 to-gray-50">
|
||
|
|
<CardTitle className="flex items-center">
|
||
|
|
<Calendar className="w-5 h-5 mr-2 text-orange-500" />
|
||
|
|
이번 달 발송 통계
|
||
|
|
</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="p-6">
|
||
|
|
<div className="space-y-4">
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="text-sm text-gray-600">총 발송 건수</span>
|
||
|
|
<span className="text-lg font-semibold text-gray-900">
|
||
|
|
{stats.sentThisMonth}건
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center justify-between">
|
||
|
|
<span className="text-sm text-gray-600">성공률</span>
|
||
|
|
<span className="text-lg font-semibold text-green-600">
|
||
|
|
{stats.successRate}%
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div className="pt-4 border-t">
|
||
|
|
<div className="flex items-center text-sm text-gray-500">
|
||
|
|
<TrendingUp className="w-4 h-4 mr-2 text-green-500" />
|
||
|
|
전월 대비 12% 증가
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
|
||
|
|
<Card className="shadow-sm">
|
||
|
|
<CardHeader className="border-b bg-gradient-to-r from-slate-50 to-gray-50">
|
||
|
|
<CardTitle className="flex items-center">
|
||
|
|
<Clock className="w-5 h-5 mr-2 text-blue-500" />
|
||
|
|
최근 활동
|
||
|
|
</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="p-6">
|
||
|
|
<div className="space-y-3">
|
||
|
|
<div className="text-center text-gray-500 py-8">
|
||
|
|
<Mail className="w-12 h-12 mx-auto mb-3 text-gray-300" />
|
||
|
|
<p className="text-sm">최근 활동이 없습니다</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 빠른 액세스 */}
|
||
|
|
<Card className="shadow-sm">
|
||
|
|
<CardHeader className="border-b bg-gradient-to-r from-slate-50 to-gray-50">
|
||
|
|
<CardTitle>빠른 액세스</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent className="p-6">
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||
|
|
<a
|
||
|
|
href="/admin/mail/accounts"
|
||
|
|
className="flex items-center p-4 rounded-lg border border-gray-200 hover:border-orange-300 hover:bg-orange-50 transition-all"
|
||
|
|
>
|
||
|
|
<Users className="w-8 h-8 text-blue-500 mr-3" />
|
||
|
|
<div>
|
||
|
|
<p className="font-medium text-gray-900">계정 관리</p>
|
||
|
|
<p className="text-sm text-gray-500">메일 계정 설정</p>
|
||
|
|
</div>
|
||
|
|
</a>
|
||
|
|
|
||
|
|
<a
|
||
|
|
href="/admin/mail/templates"
|
||
|
|
className="flex items-center p-4 rounded-lg border border-gray-200 hover:border-orange-300 hover:bg-orange-50 transition-all"
|
||
|
|
>
|
||
|
|
<FileText className="w-8 h-8 text-green-500 mr-3" />
|
||
|
|
<div>
|
||
|
|
<p className="font-medium text-gray-900">템플릿 관리</p>
|
||
|
|
<p className="text-sm text-gray-500">템플릿 편집</p>
|
||
|
|
</div>
|
||
|
|
</a>
|
||
|
|
|
||
|
|
<a
|
||
|
|
href="/admin/mail/send"
|
||
|
|
className="flex items-center p-4 rounded-lg border border-gray-200 hover:border-orange-300 hover:bg-orange-50 transition-all"
|
||
|
|
>
|
||
|
|
<Send className="w-8 h-8 text-orange-500 mr-3" />
|
||
|
|
<div>
|
||
|
|
<p className="font-medium text-gray-900">메일 발송</p>
|
||
|
|
<p className="text-sm text-gray-500">메일 보내기</p>
|
||
|
|
</div>
|
||
|
|
</a>
|
||
|
|
|
||
|
|
<a
|
||
|
|
href="/admin/mail/receive"
|
||
|
|
className="flex items-center p-4 rounded-lg border border-gray-200 hover:border-orange-300 hover:bg-orange-50 transition-all"
|
||
|
|
>
|
||
|
|
<Inbox className="w-8 h-8 text-purple-500 mr-3" />
|
||
|
|
<div>
|
||
|
|
<p className="font-medium text-gray-900">수신함</p>
|
||
|
|
<p className="text-sm text-gray-500">받은 메일 확인</p>
|
||
|
|
</div>
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
|
||
|
|
{/* 안내 정보 */}
|
||
|
|
<Card className="bg-gradient-to-r from-orange-50 to-amber-50 border-orange-200 shadow-sm">
|
||
|
|
<CardHeader>
|
||
|
|
<CardTitle className="text-lg flex items-center">
|
||
|
|
<Mail className="w-5 h-5 mr-2 text-orange-500" />
|
||
|
|
메일 관리 시스템 안내
|
||
|
|
</CardTitle>
|
||
|
|
</CardHeader>
|
||
|
|
<CardContent>
|
||
|
|
<p className="text-gray-700 mb-4">
|
||
|
|
💡 메일 관리 시스템의 주요 기능을 확인하세요!
|
||
|
|
</p>
|
||
|
|
<ul className="space-y-2 text-sm text-gray-600">
|
||
|
|
<li className="flex items-start">
|
||
|
|
<span className="text-orange-500 mr-2">✓</span>
|
||
|
|
<span>SMTP 계정을 등록하여 메일 발송</span>
|
||
|
|
</li>
|
||
|
|
<li className="flex items-start">
|
||
|
|
<span className="text-orange-500 mr-2">✓</span>
|
||
|
|
<span>드래그 앤 드롭으로 템플릿 디자인</span>
|
||
|
|
</li>
|
||
|
|
<li className="flex items-start">
|
||
|
|
<span className="text-orange-500 mr-2">✓</span>
|
||
|
|
<span>동적 변수와 SQL 쿼리 연동</span>
|
||
|
|
</li>
|
||
|
|
<li className="flex items-start">
|
||
|
|
<span className="text-orange-500 mr-2">✓</span>
|
||
|
|
<span>발송 통계 및 이력 관리</span>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|