From 526bf06182846ad7804a32e9e23b6df3105c4dab Mon Sep 17 00:00:00 2001 From: cherry2250 Date: Fri, 24 Oct 2025 15:34:44 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8B=A4=EC=8B=9C=EA=B0=84=20=EC=84=B1?= =?UTF-8?q?=EA=B3=BC=20=EB=B6=84=EC=84=9D=20=ED=99=94=EB=A9=B4=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /analytics 경로에 실시간 대시보드 페이지 추가 - 실시간 업데이트 로직 구현 (30초마다 시간 갱신, 5분마다 데이터 갱신) - KPI 요약 카드 4개 구현 (참여자수, 총비용, 예상수익, ROI) - 채널별 성과 차트 섹션 (파이차트 플레이스홀더 + 범례) - 시간대별 참여 추이 차트 섹션 (라인차트 플레이스홀더 + 통계) - ROI 상세 분석 테이블 (비용/수익 분해 + 계산식 시각화) - 참여자 프로필 분석 (연령별/성별 막대 그래프) --- src/app/(main)/analytics/page.tsx | 511 ++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100644 src/app/(main)/analytics/page.tsx diff --git a/src/app/(main)/analytics/page.tsx b/src/app/(main)/analytics/page.tsx new file mode 100644 index 0000000..c62f806 --- /dev/null +++ b/src/app/(main)/analytics/page.tsx @@ -0,0 +1,511 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + Box, + Container, + Typography, + Card, + CardContent, + Grid, +} from '@mui/material'; +import { + PieChart, + ShowChart, + Payments, + People, +} from '@mui/icons-material'; + +// Mock 데이터 +const mockAnalyticsData = { + summary: { + participants: 128, + participantsDelta: 12, + totalCost: 300000, + expectedRevenue: 1350000, + roi: 450, + targetRoi: 300, + }, + channelPerformance: [ + { channel: '우리동네TV', participants: 58, percentage: 45, color: '#E31E24' }, + { channel: '링고비즈', participants: 38, percentage: 30, color: '#0066FF' }, + { channel: 'SNS', participants: 32, percentage: 25, color: '#FFB800' }, + ], + timePerformance: { + peakTime: '오후 2-4시', + peakParticipants: 35, + avgPerHour: 8, + }, + roiDetail: { + totalCost: 300000, + prizeCost: 250000, + channelCost: 50000, + expectedRevenue: 1350000, + salesIncrease: 1000000, + newCustomerLTV: 350000, + }, + participantProfile: { + age: [ + { label: '20대', percentage: 35 }, + { label: '30대', percentage: 40 }, + { label: '40대', percentage: 25 }, + ], + gender: [ + { label: '여성', percentage: 60 }, + { label: '남성', percentage: 40 }, + ], + }, +}; + +export default function AnalyticsPage() { + const [lastUpdate, setLastUpdate] = useState(new Date()); + const [updateText, setUpdateText] = useState('방금 전'); + + useEffect(() => { + // 업데이트 시간 표시 갱신 + const updateInterval = setInterval(() => { + const now = new Date(); + const diff = Math.floor((now.getTime() - lastUpdate.getTime()) / 1000); + + let text; + if (diff < 60) { + text = '방금 전'; + } else if (diff < 3600) { + text = `${Math.floor(diff / 60)}분 전`; + } else { + text = `${Math.floor(diff / 3600)}시간 전`; + } + + setUpdateText(text); + }, 30000); // 30초마다 갱신 + + // 5분마다 데이터 업데이트 시뮬레이션 + const dataUpdateInterval = setInterval(() => { + setLastUpdate(new Date()); + setUpdateText('방금 전'); + }, 300000); // 5분 + + return () => { + clearInterval(updateInterval); + clearInterval(dataUpdateInterval); + }; + }, [lastUpdate]); + + const { summary, channelPerformance, timePerformance, roiDetail, participantProfile } = + mockAnalyticsData; + + return ( + + + {/* Title with Real-time Indicator */} + + + 📊 요약 (실시간) + + + + + {updateText} + + + + + {/* Summary KPI Cards */} + + + + + + 참여자 수 + + + {summary.participants}명 + + + ↑ {summary.participantsDelta}명 (오늘) + + + + + + + + + 총 비용 + + + {Math.floor(summary.totalCost / 10000)}만원 + + + 경품 {Math.floor(roiDetail.prizeCost / 10000)}만 + 채널{' '} + {Math.floor(roiDetail.channelCost / 10000)}만 + + + + + + + + + 예상 수익 + + + {Math.floor(summary.expectedRevenue / 10000)}만원 + + + 매출증가 {Math.floor(roiDetail.salesIncrease / 10000)}만 + LTV{' '} + {Math.floor(roiDetail.newCustomerLTV / 10000)}만 + + + + + + + + + 투자대비수익률 + + + {summary.roi}% + + + 목표 {summary.targetRoi}% 달성 + + + + + + + {/* Charts Grid */} + + {/* Channel Performance */} + + + + + + + 채널별 성과 + + + + {/* Chart Placeholder */} + + + donut_large + + + 파이 차트 + + + + {/* Legend */} + + {channelPerformance.map((item) => ( + + + + {item.channel} + + + {item.percentage}% ({item.participants}명) + + + ))} + + + + + + {/* Time Trend */} + + + + + + + 시간대별 참여 추이 + + + + {/* Chart Placeholder */} + + + trending_up + + + 라인 차트 + + + + {/* Stats */} + + + 피크 시간: {timePerformance.peakTime} ({timePerformance.peakParticipants}명) + + + 평균 시간당: {timePerformance.avgPerHour}명 + + + + + + + + {/* ROI Detail & Participant Profile */} + + {/* ROI Detail */} + + + + + + + 투자대비수익률 상세 + + + + + + + 총 비용: {Math.floor(roiDetail.totalCost / 10000)}만원 + + + + + • 경품 비용 + + + {Math.floor(roiDetail.prizeCost / 10000)}만원 + + + + + • 채널 비용 + + + {Math.floor(roiDetail.channelCost / 10000)}만원 + + + + + + + + 예상 수익: {Math.floor(roiDetail.expectedRevenue / 10000)}만원 + + + + + • 매출 증가 + + + {Math.floor(roiDetail.salesIncrease / 10000)}만원 + + + + + • 신규 고객 LTV + + + {Math.floor(roiDetail.newCustomerLTV / 10000)}만원 + + + + + + + + 투자대비수익률 + + + + (수익 - 비용) ÷ 비용 × 100 + + + ({Math.floor(roiDetail.expectedRevenue / 10000)}만 -{' '} + {Math.floor(roiDetail.totalCost / 10000)}만) ÷{' '} + {Math.floor(roiDetail.totalCost / 10000)}만 × 100 + + + = {summary.roi}% + + + + + + + + + {/* Participant Profile */} + + + + + + + 참여자 프로필 + + + + {/* Age Distribution */} + + + 연령별 + + + {participantProfile.age.map((item) => ( + + + + {item.label} + + + + + {item.percentage}% + + + + + + ))} + + + + {/* Gender Distribution */} + + + 성별 + + + {participantProfile.gender.map((item) => ( + + + + {item.label} + + + + + {item.percentage}% + + + + + + ))} + + + + + + + + + ); +}