- DashboardPage: 미사용 변수 i 제거 (TS6133) - PlatformStatusIndicator: CredentialStatus 타입 내로잉 적용 (TS7053) Closes #17
137 lines
4.7 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|