"use client"; import React, { useState, useEffect } from "react"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Send, Search, RefreshCw, CheckCircle2, XCircle, Calendar, ChevronRight, Loader2, Mail, User, Clock, Paperclip, Trash2, Eye, Reply, Forward, } from "lucide-react"; import { useRouter, useSearchParams } from "next/navigation"; import Link from "next/link"; import { SentMailHistory, getSentMailList, deleteSentMail, getMailAccounts, MailAccount, getMailStatistics, } from "@/lib/api/mail"; import { useToast } from "@/hooks/use-toast"; import DOMPurify from "isomorphic-dompurify"; export default function SentMailPage() { const router = useRouter(); const searchParams = useSearchParams(); const { toast } = useToast(); // 상태 const [mails, setMails] = useState([]); const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(false); const [loadingDetail, setLoadingDetail] = useState(false); const [deleting, setDeleting] = useState(false); // 선택된 메일 const [selectedMailId, setSelectedMailId] = useState(""); const [selectedMailDetail, setSelectedMailDetail] = useState(null); // 필터 const [searchTerm, setSearchTerm] = useState(""); const [filterStatus, setFilterStatus] = useState<"all" | "success" | "failed">("all"); const [filterAccountId, setFilterAccountId] = useState("all"); // 페이지네이션 const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage] = useState(10); const [totalPages, setTotalPages] = useState(1); // 통계 const [stats, setStats] = useState({ totalSent: 0, successCount: 0, failedCount: 0, todayCount: 0, }); useEffect(() => { loadAccounts(); loadStats(); loadMails(); }, []); useEffect(() => { setCurrentPage(1); // 필터 변경 시 첫 페이지로 loadMails(); }, [filterStatus, filterAccountId]); useEffect(() => { loadMails(); }, [currentPage]); // URL에서 mailId 파라미터 확인하여 자동 선택 useEffect(() => { const mailId = searchParams.get("mailId"); if (mailId && mails.length > 0) { const mail = mails.find((m) => m.id === mailId); if (mail) { // console.log("🎯 URL에서 지정된 메일 자동 선택:", mailId); handleMailClick(mail); } } }, [searchParams, mails]); const loadAccounts = async () => { try { const data = await getMailAccounts(); setAccounts(data.filter((acc) => acc.status === "active")); } catch (error: unknown) { const err = error as Error; console.error("계정 로드 실패:", err); } }; const loadStats = async () => { try { const data = await getMailStatistics(); setStats({ totalSent: data.totalSent || 0, successCount: data.successCount || 0, failedCount: data.failedCount || 0, todayCount: data.todaySent || 0, }); } catch (error: unknown) { const err = error as Error; console.error("통계 로드 실패:", err); } }; const loadMails = async () => { try { setLoading(true); const filters: any = { sortBy: "sentAt", sortOrder: "desc", limit: 1000, // 전체 메일 가져오기 (프론트에서 페이지네이션) }; // 상태 필터: 'all'이면 status 필터 없음 (draft 제외하려면 success/failed만) if (filterStatus === "all") { // draft를 제외하고 발송된 메일만 (success + failed) // status 필터를 안 넣으면 draft도 포함되므로, 클라이언트에서 필터링 } else { filters.status = filterStatus; // 'success' 또는 'failed' } if (filterAccountId !== "all") { filters.accountId = filterAccountId; } // console.log("📤 발신메일 로드 시작:", filters); const response = await getSentMailList(filters); // console.log("📤 발신메일 응답:", response); let mailList = response.items || []; // draft 제외 (발송된 메일만 표시) mailList = mailList.filter((mail) => mail.status !== "draft"); // console.log("📤 발신메일 개수 (draft 제외):", mailList.length); // 검색어 필터링 (클라이언트 사이드) if (searchTerm.trim()) { const term = searchTerm.toLowerCase(); mailList = mailList.filter( (mail) => mail.subject?.toLowerCase().includes(term) || mail.to?.some((email) => email.toLowerCase().includes(term)) || mail.accountEmail?.toLowerCase().includes(term) ); } // 페이지네이션 적용 const totalItems = mailList.length; const totalPagesCalc = Math.ceil(totalItems / itemsPerPage); setTotalPages(totalPagesCalc); const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const paginatedMails = mailList.slice(startIndex, endIndex); setMails(paginatedMails); } catch (error: unknown) { const err = error as Error; console.error("❌ 메일 로드 실패:", err); toast({ title: "메일 로드 실패", description: err.message, variant: "destructive", }); } finally { setLoading(false); } }; const handleMailClick = (mail: SentMailHistory) => { setSelectedMailId(mail.id); setSelectedMailDetail(mail); // console.log("📧 메일 클릭:", mail.id); }; const handleDeleteMail = async () => { if (!selectedMailId || !confirm("이 메일을 삭제하시겠습니까?")) return; try { setDeleting(true); await deleteSentMail(selectedMailId); // 메일 목록에서 제거 setMails(mails.filter((m) => m.id !== selectedMailId)); // 상세 패널 닫기 setSelectedMailId(""); setSelectedMailDetail(null); toast({ title: "메일 삭제 완료", description: "메일이 휴지통으로 이동되었습니다.", }); // 통계 새로고침 loadStats(); } catch (error: any) { console.error("메일 삭제 실패:", error); toast({ title: "메일 삭제 실패", description: error.response?.data?.message || error.message, variant: "destructive", }); } finally { setDeleting(false); } }; const handleReply = () => { if (!selectedMailDetail) return; // 원본 수신자를 받는사람으로 설정 const toEmail = selectedMailDetail.to?.[0] || ""; const replyData = { originalFrom: selectedMailDetail.accountEmail, originalTo: toEmail, originalSubject: selectedMailDetail.subject, originalDate: selectedMailDetail.sentAt, originalBody: selectedMailDetail.htmlContent || "", }; router.push( `/admin/mail/send?action=reply&data=${encodeURIComponent(JSON.stringify(replyData))}` ); }; const handleForward = () => { if (!selectedMailDetail) return; const forwardData = { originalFrom: selectedMailDetail.accountEmail, originalSubject: selectedMailDetail.subject, originalDate: selectedMailDetail.sentAt, originalBody: selectedMailDetail.htmlContent || "", }; router.push( `/admin/mail/send?action=forward&data=${encodeURIComponent(JSON.stringify(forwardData))}` ); }; const formatDate = (dateString: string) => { const date = new Date(dateString); const now = new Date(); const diff = now.getTime() - date.getTime(); const hours = Math.floor(diff / (1000 * 60 * 60)); if (hours < 24) { return date.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", }); } else { return date.toLocaleDateString("ko-KR", { month: "short", day: "numeric", }); } }; // 필터링된 메일 개수 const filteredCount = mails.length; if (loading && mails.length === 0) { return (

메일을 불러오는 중...

); } return (
{/* 헤더 */}
{/* 브레드크럼브 */} {/* 제목 및 빠른 액션 */}

보낸메일함

총 {filteredCount}개의 발송 메일

{/* 통계 카드 */}

전체 발송

{stats.totalSent}

발송 성공

{stats.successCount}

발송 실패

{stats.failedCount}

오늘 발송

{stats.todayCount}

{/* 검색 및 필터 */}
{/* 검색 */}
setSearchTerm(e.target.value)} className="pl-10" />
{/* 상태 필터 */} {/* 계정 필터 */}
{/* 메일 목록 + 상세보기 */}
{/* 왼쪽: 메일 목록 */}
메일 목록 {filteredCount}개의 메일
{mails.length === 0 ? (

발송된 메일이 없습니다

) : ( mails.map((mail) => (
handleMailClick(mail)} className={` p-4 border-b cursor-pointer transition-colors hover:bg-accent ${selectedMailId === mail.id ? "bg-accent" : ""} `} >
{mail.to?.[0] || "받는사람 없음"}

{mail.subject || "(제목 없음)"}

{formatDate(mail.sentAt)} {mail.status === "success" ? ( 성공 ) : mail.status === "failed" ? ( 실패 ) : null}
{mail.accountEmail} {mail.attachments && mail.attachments.length > 0 && ( <> {mail.attachments.length} )}
)) )}
{/* 페이지네이션 */} {totalPages > 1 && (
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => { let pageNum; if (totalPages <= 5) { pageNum = i + 1; } else if (currentPage <= 3) { pageNum = i + 1; } else if (currentPage >= totalPages - 2) { pageNum = totalPages - 4 + i; } else { pageNum = currentPage - 2 + i; } return ( ); })}
)}
{/* 오른쪽: 메일 상세보기 */}
{selectedMailDetail ? (
{selectedMailDetail.subject || "(제목 없음)"}
보낸사람: {selectedMailDetail.accountName} ({selectedMailDetail.accountEmail})
받는사람: {selectedMailDetail.to?.join(", ") || "-"}
{selectedMailDetail.cc && selectedMailDetail.cc.length > 0 && (
참조: {selectedMailDetail.cc.join(", ")}
)}
발송일시: {new Date(selectedMailDetail.sentAt).toLocaleString("ko-KR")}
{/* 답장/전달/삭제 버튼 */}
{/* 첨부파일 */} {selectedMailDetail.attachments && selectedMailDetail.attachments.length > 0 && (
첨부파일 ({selectedMailDetail.attachments.length})
{selectedMailDetail.attachments.map((file: any, index: number) => (
{file.filename || file.name} {file.size ? `${(file.size / 1024).toFixed(1)}KB` : ""}
))}
)} {/* 메일 본문 */} {selectedMailDetail.htmlContent ? (
) : (
{selectedMailDetail.htmlContent || "(내용 없음)"}
)} ) : (

메일을 선택하면 내용이 표시됩니다

)}
); }