import { useState, useMemo, useCallback, useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { toast } from "sonner"; import PageHeader from "@/components/common/PageHeader"; import { MOCK_NOTIFICATIONS, type Notification, type NotificationType } from "../types"; import NotificationSlidePanel from "../components/NotificationSlidePanel"; // 타입별 배지 스타일 const BADGE_STYLE: Record< NotificationType, { bg: string; text: string; icon: string } > = { "발송": { bg: "bg-green-50", text: "text-green-700", icon: "check_circle" }, "인증서": { bg: "bg-amber-50", text: "text-amber-700", icon: "warning" }, "서비스": { bg: "bg-blue-50", text: "text-blue-700", icon: "add_circle" }, "실패": { bg: "bg-red-50", text: "text-red-600", icon: "error" }, "시스템": { bg: "bg-gray-100", text: "text-gray-600", icon: "settings" }, }; // 날짜 그룹 레이블 → 표시 날짜 const DATE_LABELS: Record = { "오늘": "2026-02-19", "어제": "2026-02-18", "이번 주": "2026.02.16 ~ 02.17", }; const PAGE_SIZE = 5; export default function NotificationsPage() { const location = useLocation(); const navigate = useNavigate(); const [notifications, setNotifications] = useState( () => [...MOCK_NOTIFICATIONS], ); const [currentPage, setCurrentPage] = useState(1); const [selectedNotification, setSelectedNotification] = useState(null); // 헤더 드롭다운에서 넘어온 경우 해당 알림 패널 자동 오픈 useEffect(() => { const state = location.state as { notificationId?: number } | null; if (state?.notificationId) { const target = notifications.find((n) => n.id === state.notificationId); if (target) { setSelectedNotification(target); // 읽음 처리 if (!target.read) { setNotifications((prev) => prev.map((n) => n.id === target.id ? { ...n, read: true } : n, ), ); setSelectedNotification({ ...target, read: true }); } } // state 소비 후 제거 (새로고침 시 재트리거 방지) navigate(location.pathname, { replace: true, state: null }); } }, [location.state]); // eslint-disable-line react-hooks/exhaustive-deps // 페이지네이션 const totalItems = notifications.length; const totalPages = Math.max(1, Math.ceil(totalItems / PAGE_SIZE)); const pagedNotifications = useMemo( () => notifications.slice( (currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE, ), [notifications, currentPage], ); // 페이징된 알림을 날짜별로 그룹핑 const grouped = useMemo(() => { const map = new Map(); for (const n of pagedNotifications) { const arr = map.get(n.date) ?? []; arr.push(n); map.set(n.date, arr); } return map; }, [pagedNotifications]); // 전체 읽음 const handleReadAll = useCallback(() => { setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); toast.success("모든 알림을 읽음 처리했습니다"); }, []); // 개별 읽음 const handleReadOne = useCallback((id: number) => { setNotifications((prev) => prev.map((n) => (n.id === id ? { ...n, read: true } : n)), ); setSelectedNotification((prev) => prev?.id === id ? { ...prev, read: true } : prev, ); }, []); return ( <> done_all 전체 읽음 } /> {/* 날짜별 그룹 */} {Array.from(grouped.entries()).map(([dateLabel, items]) => (
{/* 그룹 헤더 */}

{dateLabel}

{DATE_LABELS[dateLabel] ?? ""}
{/* 알림 카드 목록 */}
{items.map((notification, idx) => { const badge = BADGE_STYLE[notification.type]; const isUnread = !notification.read; return (
{ setSelectedNotification(notification); handleReadOne(notification.id); }} className={`flex items-start gap-4 px-5 py-4 cursor-pointer transition-colors ${ idx < items.length - 1 ? "border-b border-gray-100" : "" } ${ isUnread ? "bg-blue-50/20 hover:bg-blue-50/30" : "hover:bg-gray-50" }`} > {/* 읽음 상태 점 */}
{/* 콘텐츠 */}
{badge.icon} {notification.type} {notification.time}

{notification.title}

{notification.description}

); })}
))} {/* 페이지네이션 */}
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( ))}
{/* 알림 상세 패널 */} setSelectedNotification(null)} notification={selectedNotification} onMarkRead={handleReadOne} /> ); }