import { useState, useCallback, useEffect } from "react"; import PageHeader from "@/components/common/PageHeader"; import DashboardFilter from "../components/DashboardFilter"; import type { DashboardFilterValues } from "../components/DashboardFilter"; import StatsCards from "../components/StatsCards"; import WeeklyChart from "../components/WeeklyChart"; import RecentMessages from "../components/RecentMessages"; import PlatformDonut from "../components/PlatformDonut"; import { fetchDashboard } from "@/api/dashboard.api"; import { formatNumber } from "@/utils/format"; import type { DashboardData } from "../types"; /** KPI → StatsCards props 변환 */ function mapCards(data: DashboardData) { const { kpi } = data; return [ { label: "오늘 발송 수", value: formatNumber(kpi.total_sent), badge: { type: "trend" as const, value: `${Math.abs(kpi.sent_change_rate)}%` }, link: "/statistics", }, { label: "성공률", value: kpi.success_rate.toFixed(1), unit: "%", badge: { type: "icon" as const, icon: "check_circle", color: "bg-green-100 text-green-600" }, link: "/statistics", }, { label: "등록 기기 수", value: formatNumber(kpi.device_count), badge: { type: "icon" as const, icon: "devices", color: "bg-blue-50 text-primary" }, link: "/devices", }, { label: "활성 서비스", value: String(kpi.service_count), badge: { type: "icon" as const, icon: "grid_view", color: "bg-purple-50 text-purple-600" }, link: "/services", }, ]; } /** daily_trend → WeeklyChart props 변환 */ function mapChart(data: DashboardData) { const trends = data.daily_trend; if (trends.length === 0) return []; const maxVal = Math.max(...trends.map((t) => Math.max(t.sent, t.success)), 1); const todayStr = new Date().toISOString().slice(0, 10); return trends.map((t) => { const isToday = t.date === todayStr; const mm = t.date.slice(5, 7); const dd = t.date.slice(8, 10); return { label: isToday ? "Today" : `${mm}.${dd}`, blue: 1 - t.sent / maxVal, // Y축 비율 (0=최상단) green: 1 - t.success / maxVal, sent: formatNumber(t.sent), reach: formatNumber(t.success), }; }); } /** top_messages → RecentMessages props 변환 */ function mapMessages(data: DashboardData) { return data.top_messages.map((m) => ({ template: m.message_name, targetCount: formatNumber(m.target_count), status: m.status as "완료" | "실패" | "진행" | "예약", sentAt: m.sent_at, })); } /** platform_ratio → PlatformDonut props 변환 */ function mapPlatform(data: DashboardData) { return { ios: data.platform_ratio.ios, android: data.platform_ratio.android, }; } export default function DashboardPage() { const [loading, setLoading] = useState(false); const [error, setError] = useState(false); const [empty, setEmpty] = useState(false); // API 성공이나 데이터 없음 const [cards, setCards] = useState | undefined>(); const [chart, setChart] = useState | undefined>(); const [messages, setMessages] = useState | undefined>(); const [platform, setPlatform] = useState | undefined>(); // 필터 상태 보관 (초기 로드 + 조회 버튼) const [filter, setFilter] = useState({ dateStart: (() => { const d = new Date(); d.setDate(d.getDate() - 30); return d.toISOString().slice(0, 10); })(), dateEnd: new Date().toISOString().slice(0, 10), }); const loadDashboard = useCallback(async (f: DashboardFilterValues) => { setLoading(true); setError(false); setEmpty(false); try { const res = await fetchDashboard( { start_date: f.dateStart, end_date: f.dateEnd }, f.serviceCode, ); const d = res.data.data; // 데이터 비어있는지 판단 const hasData = d.kpi.total_sent > 0 || d.daily_trend.length > 0 || d.top_messages.length > 0; if (!hasData) { setEmpty(true); return; } setCards(mapCards(d)); setChart(mapChart(d)); setMessages(mapMessages(d)); setPlatform(mapPlatform(d)); } catch { setError(true); } finally { setLoading(false); } }, []); // 초기 로드 useEffect(() => { loadDashboard(filter); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 조회 버튼 핸들러 const handleSearch = useCallback( (f: DashboardFilterValues) => { setFilter(f); loadDashboard(f); }, [loadDashboard], ); // 스켈레톤 표시 조건 const showSkeleton = loading || error || empty || !cards; return ( <> {/* 페이지 헤더 */} {/* 필터 */} {/* 데이터 영역 */}
{/* API 에러 오버레이 */} {error && !loading && (
cloud_off

데이터를 불러올 수 없습니다

네트워크 상태를 확인하거나 다시 조회해 주세요

)} {/* 조회 결과 없음 오버레이 */} {empty && !loading && (
inbox

조회된 데이터가 없습니다

기간이나 서비스를 변경하여 다시 조회해 주세요

)}
); }