ERP-node/frontend/app/(main)/admin/mail/dashboard/page.tsx

357 lines
12 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import Link from "next/link";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import {
Mail,
Send,
Inbox,
FileText,
RefreshCw,
TrendingUp,
Users,
Calendar,
ArrowRight,
Trash2,
Edit
} from "lucide-react";
import { getMailAccounts, getMailTemplates, getMailStatistics, getTodayReceivedCount } from "@/lib/api/mail";
import MailNotifications from "@/components/mail/MailNotifications";
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 accounts = await getMailAccounts();
const templates = await getMailTemplates();
// 메일 통계 조회 (실패 시 기본값 사용)
let mailStats = {
todayCount: 0,
thisMonthCount: 0,
successRate: 0,
};
try {
const stats = await getMailStatistics();
if (stats && typeof stats === 'object') {
mailStats = {
todayCount: stats.todayCount || 0,
thisMonthCount: stats.thisMonthCount || 0,
successRate: stats.successRate || 0,
};
}
} catch (error) {
// console.error('메일 통계 조회 실패:', error);
// 기본값 사용
}
// 오늘 수신 메일 수 조회 (IMAP 실시간 조회)
let receivedTodayCount = 0;
try {
receivedTodayCount = await getTodayReceivedCount();
} catch (error) {
// console.error('수신 메일 수 조회 실패:', error);
// 실패 시 0으로 표시
}
setStats({
totalAccounts: accounts.length,
totalTemplates: templates.length,
sentToday: mailStats.todayCount,
receivedToday: receivedTodayCount,
sentThisMonth: mailStats.thisMonthCount,
successRate: mailStats.successRate,
});
} 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-600",
href: "/admin/mail/accounts",
},
{
title: "템플릿 수",
value: stats.totalTemplates,
icon: FileText,
color: "green",
bgColor: "bg-green-100",
iconColor: "text-green-600",
href: "/admin/mail/templates",
},
{
title: "오늘 발송",
value: stats.sentToday,
icon: Send,
color: "orange",
bgColor: "bg-orange-100",
iconColor: "text-orange-600",
href: "/admin/mail/sent",
},
{
title: "오늘 수신",
value: stats.receivedToday,
icon: Inbox,
color: "purple",
bgColor: "bg-purple-100",
iconColor: "text-purple-600",
href: "/admin/mail/receive",
},
];
const quickLinks = [
{
title: "계정 관리",
description: "메일 계정 설정",
href: "/admin/mail/accounts",
icon: Users,
color: "blue",
},
{
title: "템플릿 관리",
description: "템플릿 편집",
href: "/admin/mail/templates",
icon: FileText,
color: "green",
},
{
title: "메일 발송",
description: "메일 보내기",
href: "/admin/mail/send",
icon: Send,
color: "orange",
},
{
title: "대량 발송",
description: "CSV로 대량 발송",
href: "/admin/mail/bulk-send",
icon: Users,
color: "teal",
},
{
title: "보낸메일함",
description: "발송 이력 확인",
href: "/admin/mail/sent",
icon: Mail,
color: "indigo",
},
{
title: "수신함",
description: "받은 메일 확인",
href: "/admin/mail/receive",
icon: Inbox,
color: "purple",
},
{
title: "임시 저장",
description: "작성 중인 메일",
href: "/admin/mail/drafts",
icon: Edit,
color: "amber",
},
{
title: "휴지통",
description: "삭제된 메일",
href: "/admin/mail/trash",
icon: Trash2,
color: "red",
},
];
return (
<div className="min-h-screen bg-background">
<div className="w-full px-3 py-3 space-y-3">
{/* 페이지 제목 */}
<div className="flex items-center justify-between bg-card rounded-lg border p-8">
<div className="flex items-center gap-4">
<div className="p-4 bg-primary/10 rounded-lg">
<Mail className="w-8 h-8 text-primary" />
</div>
<div>
<h1 className="text-3xl font-bold text-foreground mb-1"> </h1>
<p className="text-muted-foreground"> </p>
</div>
</div>
<div className="flex gap-3">
<MailNotifications />
<Button
variant="outline"
size="lg"
onClick={loadStats}
disabled={loading}
>
<RefreshCw className={`w-5 h-5 mr-2 ${loading ? 'animate-spin' : ''}`} />
</Button>
</div>
</div>
{/* 통계 카드 */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
{statCards.map((stat, index) => (
<Link key={index} href={stat.href}>
<Card className="hover:shadow-md transition-all hover:scale-105 cursor-pointer">
<CardContent className="p-6">
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<p className="text-sm font-medium text-muted-foreground mb-3">
{stat.title}
</p>
<p className="text-4xl font-bold text-foreground">
{stat.value}
</p>
</div>
<div className="p-4 bg-muted rounded-lg">
<stat.icon className="w-7 h-7 text-muted-foreground" />
</div>
</div>
{/* 진행 바 */}
<div className="h-2 bg-muted rounded-full overflow-hidden">
<div
className="h-full bg-primary transition-all duration-1000"
style={{ width: `${Math.min((stat.value / 10) * 100, 100)}%` }}
></div>
</div>
</CardContent>
</Card>
</Link>
))}
</div>
{/* 이번 달 통계 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
<Card>
<CardHeader className="border-b">
<CardTitle className="text-lg flex items-center">
<div className="p-2 bg-muted rounded-lg mr-3">
<Calendar className="w-5 h-5 text-foreground" />
</div>
<span> </span>
</CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="space-y-4">
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
<span className="text-sm font-medium text-muted-foreground"> </span>
<span className="text-2xl font-bold text-foreground">{stats.sentThisMonth} </span>
</div>
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
<span className="text-sm font-medium text-muted-foreground"></span>
<span className="text-2xl font-bold text-foreground">{stats.successRate}%</span>
</div>
{/* 전월 대비 통계는 현재 불필요하여 주석처리
<div className="flex items-center justify-between pt-3 border-t">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
<TrendingUp className="w-4 h-4" />
전월 대비
</div>
<span className="text-lg font-bold text-foreground">+12%</span>
</div>
*/}
</div>
</CardContent>
</Card>
<Card>
<CardHeader className="border-b">
<CardTitle className="text-lg flex items-center">
<div className="p-2 bg-muted rounded-lg mr-3">
<Mail className="w-5 h-5 text-foreground" />
</div>
<span> </span>
</CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="space-y-4">
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
<div className="flex items-center gap-3">
<div className="w-3 h-3 bg-primary rounded-full animate-pulse"></div>
<span className="text-sm font-medium text-muted-foreground"> </span>
</div>
<span className="text-sm font-bold text-foreground"> </span>
</div>
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
<div className="flex items-center gap-3">
<div className="w-3 h-3 bg-primary rounded-full"></div>
<span className="text-sm font-medium text-muted-foreground"> </span>
</div>
<span className="text-lg font-bold text-foreground">{stats.totalAccounts} </span>
</div>
<div className="flex items-center justify-between p-4 bg-muted rounded-lg">
<div className="flex items-center gap-3">
<div className="w-3 h-3 bg-primary rounded-full"></div>
<span className="text-sm font-medium text-muted-foreground"> 릿</span>
</div>
<span className="text-lg font-bold text-foreground">{stats.totalTemplates} </span>
</div>
</div>
</CardContent>
</Card>
</div>
{/* 빠른 액세스 */}
<Card>
<CardHeader className="border-b">
<CardTitle className="text-lg"> </CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{quickLinks.map((link, index) => (
<a
key={index}
href={link.href}
className="group flex items-center gap-4 p-5 rounded-lg border hover:border-primary/50 hover:shadow-md transition-all bg-card hover:bg-muted/50"
>
<div className="p-3 bg-muted rounded-lg group-hover:scale-105 transition-transform">
<link.icon className="w-6 h-6 text-muted-foreground" />
</div>
<div className="flex-1 min-w-0">
<p className="font-semibold text-foreground text-base mb-1">{link.title}</p>
<p className="text-sm text-muted-foreground truncate">{link.description}</p>
</div>
<ArrowRight className="w-5 h-5 text-muted-foreground group-hover:text-foreground group-hover:translate-x-1 transition-all" />
</a>
))}
</div>
</CardContent>
</Card>
</div>
</div>
);
}