SPMS_WEB/react/src/features/dashboard/pages/DashboardPage.tsx
SEAN 4f8c282572 fix: TypeScript 빌드 에러 수정 (#17)
- DashboardPage: 미사용 변수 i 제거 (TS6133)
- PlatformStatusIndicator: CredentialStatus 타입 내로잉 적용 (TS7053)

Closes #17
2026-02-27 14:15:36 +09:00

137 lines
4.7 KiB
TypeScript

import { useState, useCallback } from "react";
import PageHeader from "@/components/common/PageHeader";
import DashboardFilter from "../components/DashboardFilter";
import StatsCards from "../components/StatsCards";
import WeeklyChart from "../components/WeeklyChart";
import RecentMessages from "../components/RecentMessages";
import PlatformDonut from "../components/PlatformDonut";
/** 랜덤 정수 (min~max) */
function rand(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/** 숫자를 천단위 콤마로 포맷 */
function fmt(n: number) {
return n.toLocaleString();
}
/** 통계 카드 랜덤 데이터 생성 */
function randomCards() {
const sent = rand(5000, 30000);
const rate = (95 + Math.random() * 4.9).toFixed(1);
const devices = rand(20000, 80000);
const services = rand(3, 15);
const trend = rand(1, 30);
return [
{
label: "오늘 발송 수",
value: fmt(sent),
badge: { type: "trend" as const, value: `${trend}%` },
link: "/statistics",
},
{
label: "성공률",
value: rate,
unit: "%",
badge: { type: "icon" as const, icon: "check_circle", color: "bg-green-100 text-green-600" },
link: "/statistics",
},
{
label: "등록 기기 수",
value: fmt(devices),
badge: { type: "icon" as const, icon: "devices", color: "bg-blue-50 text-primary" },
link: "/devices",
},
{
label: "활성 서비스",
value: String(services),
badge: { type: "icon" as const, icon: "grid_view", color: "bg-purple-50 text-purple-600" },
link: "/services",
},
];
}
/** 주간 차트 랜덤 데이터 생성 */
function randomChart() {
const now = new Date();
return Array.from({ length: 7 }, (_, i) => {
const d = new Date(now);
d.setDate(d.getDate() - (6 - i));
const label = i === 6 ? "Today" : `${String(d.getMonth() + 1).padStart(2, "0")}.${String(d.getDate()).padStart(2, "0")}`;
const blue = 0.1 + Math.random() * 0.7;
const green = Math.min(blue + 0.05 + Math.random() * 0.1, 0.95);
const sent = Math.round((1 - blue) * 15000);
const reach = Math.round((1 - green) * 15000);
return { label, blue, green, sent: fmt(sent), reach: fmt(reach) };
});
}
/** 최근 발송 내역 랜덤 데이터 생성 */
function randomMessages() {
const templates = [
"가을맞이 프로모션 알림", "정기 점검 안내", "비밀번호 변경 알림",
"신규 서비스 런칭", "야간 푸시 마케팅", "결제 완료 알림",
"이벤트 당첨 안내", "서비스 업데이트 공지", "보안 알림",
];
const statuses = ["완료", "완료", "완료", "실패", "진행", "예약"] as const;
const hours = ["09:00", "09:15", "10:30", "11:00", "14:00", "15:30", "18:00", "20:00"];
return Array.from({ length: 5 }, () => ({
template: templates[rand(0, templates.length - 1)],
targetCount: fmt(rand(1, 50000)),
status: statuses[rand(0, statuses.length - 1)],
sentAt: `2026-02-${String(rand(10, 27)).padStart(2, "0")} ${hours[rand(0, hours.length - 1)]}`,
})).sort((a, b) => (a.sentAt > b.sentAt ? -1 : 1));
}
/** 플랫폼 비율 랜덤 데이터 생성 */
function randomPlatform() {
const ios = rand(25, 75);
return { ios, android: 100 - ios };
}
export default function DashboardPage() {
const [loading, setLoading] = useState(false);
const [cards, setCards] = useState<ReturnType<typeof randomCards> | undefined>();
const [chart, setChart] = useState<ReturnType<typeof randomChart> | undefined>();
const [messages, setMessages] = useState<ReturnType<typeof randomMessages> | undefined>();
const [platform, setPlatform] = useState<ReturnType<typeof randomPlatform> | undefined>();
const handleSearch = useCallback(() => {
setLoading(true);
setTimeout(() => {
setCards(randomCards());
setChart(randomChart());
setMessages(randomMessages());
setPlatform(randomPlatform());
setLoading(false);
}, 1200);
}, []);
return (
<>
{/* 페이지 헤더 */}
<PageHeader
title="대시보드"
description="서비스 발송 현황과 주요 지표를 한눈에 확인할 수 있습니다."
/>
{/* 필터 */}
<DashboardFilter onSearch={handleSearch} loading={loading} />
{/* 통계 카드 */}
<StatsCards cards={cards} loading={loading} />
{/* 7일 발송 추이 차트 */}
<WeeklyChart data={chart} loading={loading} />
{/* 최근 발송 내역 + 플랫폼 도넛 */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<RecentMessages messages={messages} loading={loading} />
<PlatformDonut data={platform} loading={loading} />
</div>
</>
);
}