mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 10:56:23 +00:00
실시간 성과 분석 화면 개발
- /analytics 경로에 실시간 대시보드 페이지 추가 - 실시간 업데이트 로직 구현 (30초마다 시간 갱신, 5분마다 데이터 갱신) - KPI 요약 카드 4개 구현 (참여자수, 총비용, 예상수익, ROI) - 채널별 성과 차트 섹션 (파이차트 플레이스홀더 + 범례) - 시간대별 참여 추이 차트 섹션 (라인차트 플레이스홀더 + 통계) - ROI 상세 분석 테이블 (비용/수익 분해 + 계산식 시각화) - 참여자 프로필 분석 (연령별/성별 막대 그래프)
This commit is contained in:
parent
3f8658f9f3
commit
526bf06182
511
src/app/(main)/analytics/page.tsx
Normal file
511
src/app/(main)/analytics/page.tsx
Normal file
@ -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<Date>(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 (
|
||||||
|
<Box sx={{ minHeight: '100vh', bgcolor: 'background.default', pb: 10 }}>
|
||||||
|
<Container maxWidth="lg" sx={{ pt: 4, pb: 4, px: { xs: 3, sm: 3, md: 4 } }}>
|
||||||
|
{/* Title with Real-time Indicator */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
mb: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h5" sx={{ fontWeight: 700 }}>
|
||||||
|
📊 요약 (실시간)
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
borderRadius: '50%',
|
||||||
|
bgcolor: 'success.main',
|
||||||
|
animation: 'pulse 2s infinite',
|
||||||
|
'@keyframes pulse': {
|
||||||
|
'0%, 100%': { opacity: 1 },
|
||||||
|
'50%': { opacity: 0.3 },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
{updateText}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Summary KPI Cards */}
|
||||||
|
<Grid container spacing={3} sx={{ mb: 5 }}>
|
||||||
|
<Grid item xs={6} md={3}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
||||||
|
참여자 수
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h4" sx={{ fontWeight: 700, my: 1 }}>
|
||||||
|
{summary.participants}명
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="success.main" sx={{ fontWeight: 600 }}>
|
||||||
|
↑ {summary.participantsDelta}명 (오늘)
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} md={3}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
||||||
|
총 비용
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h4" sx={{ fontWeight: 700, my: 1 }}>
|
||||||
|
{Math.floor(summary.totalCost / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
||||||
|
경품 {Math.floor(roiDetail.prizeCost / 10000)}만 + 채널{' '}
|
||||||
|
{Math.floor(roiDetail.channelCost / 10000)}만
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} md={3}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
||||||
|
예상 수익
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h4" sx={{ fontWeight: 700, my: 1, color: 'success.main' }}>
|
||||||
|
{Math.floor(summary.expectedRevenue / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="success.main" sx={{ fontWeight: 600 }}>
|
||||||
|
매출증가 {Math.floor(roiDetail.salesIncrease / 10000)}만 + LTV{' '}
|
||||||
|
{Math.floor(roiDetail.newCustomerLTV / 10000)}만
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} md={3}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
||||||
|
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
||||||
|
투자대비수익률
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h4" sx={{ fontWeight: 700, my: 1, color: 'success.main' }}>
|
||||||
|
{summary.roi}%
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="success.main" sx={{ fontWeight: 600 }}>
|
||||||
|
목표 {summary.targetRoi}% 달성
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Charts Grid */}
|
||||||
|
<Grid container spacing={4} sx={{ mb: 5 }}>
|
||||||
|
{/* Channel Performance */}
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ p: 4 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
||||||
|
<PieChart color="error" />
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||||
|
채널별 성과
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Chart Placeholder */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
aspectRatio: '1 / 1',
|
||||||
|
bgcolor: 'grey.100',
|
||||||
|
borderRadius: 3,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexDirection: 'column',
|
||||||
|
p: 4,
|
||||||
|
mb: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-icons" style={{ fontSize: 64, color: '#bdbdbd' }}>
|
||||||
|
donut_large
|
||||||
|
</span>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
||||||
|
파이 차트
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Legend */}
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
|
{channelPerformance.map((item) => (
|
||||||
|
<Box
|
||||||
|
key={item.channel}
|
||||||
|
sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: '50%',
|
||||||
|
bgcolor: item.color,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography variant="body2">{item.channel}</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||||
|
{item.percentage}% ({item.participants}명)
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Time Trend */}
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ p: 4 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
||||||
|
<ShowChart color="error" />
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||||
|
시간대별 참여 추이
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Chart Placeholder */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: '100%',
|
||||||
|
aspectRatio: '16 / 9',
|
||||||
|
bgcolor: 'grey.100',
|
||||||
|
borderRadius: 3,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexDirection: 'column',
|
||||||
|
p: 4,
|
||||||
|
mb: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="material-icons" style={{ fontSize: 64, color: '#bdbdbd' }}>
|
||||||
|
trending_up
|
||||||
|
</span>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
||||||
|
라인 차트
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 0.5 }}>
|
||||||
|
피크 시간: {timePerformance.peakTime} ({timePerformance.peakParticipants}명)
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
평균 시간당: {timePerformance.avgPerHour}명
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* ROI Detail & Participant Profile */}
|
||||||
|
<Grid container spacing={4} sx={{ mb: 5 }}>
|
||||||
|
{/* ROI Detail */}
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ p: 4 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
||||||
|
<Payments color="error" />
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||||
|
투자대비수익률 상세
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1 }}>
|
||||||
|
총 비용: {Math.floor(roiDetail.totalCost / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ pl: 2, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
• 경품 비용
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||||
|
{Math.floor(roiDetail.prizeCost / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
• 채널 비용
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||||
|
{Math.floor(roiDetail.channelCost / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1 }}>
|
||||||
|
예상 수익: {Math.floor(roiDetail.expectedRevenue / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ pl: 2, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
• 매출 증가
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 600, color: 'success.main' }}>
|
||||||
|
{Math.floor(roiDetail.salesIncrease / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
• 신규 고객 LTV
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ fontWeight: 600, color: 'success.main' }}>
|
||||||
|
{Math.floor(roiDetail.newCustomerLTV / 10000)}만원
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1, color: 'success.main' }}>
|
||||||
|
투자대비수익률
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
bgcolor: 'grey.100',
|
||||||
|
borderRadius: 2,
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" sx={{ mb: 0.5 }}>
|
||||||
|
(수익 - 비용) ÷ 비용 × 100
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
||||||
|
({Math.floor(roiDetail.expectedRevenue / 10000)}만 -{' '}
|
||||||
|
{Math.floor(roiDetail.totalCost / 10000)}만) ÷{' '}
|
||||||
|
{Math.floor(roiDetail.totalCost / 10000)}만 × 100
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h5" sx={{ fontWeight: 700, color: 'success.main' }}>
|
||||||
|
= {summary.roi}%
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Participant Profile */}
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
||||||
|
<CardContent sx={{ p: 4 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
||||||
|
<People color="error" />
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||||
|
참여자 프로필
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Age Distribution */}
|
||||||
|
<Box sx={{ mb: 4 }}>
|
||||||
|
<Typography variant="body1" sx={{ fontWeight: 600, mb: 2 }}>
|
||||||
|
연령별
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||||
|
{participantProfile.age.map((item) => (
|
||||||
|
<Box key={item.label}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||||
|
<Typography variant="body2" sx={{ minWidth: 60 }}>
|
||||||
|
{item.label}
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
flex: 1,
|
||||||
|
height: 24,
|
||||||
|
bgcolor: 'grey.200',
|
||||||
|
borderRadius: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: `${item.percentage}%`,
|
||||||
|
height: '100%',
|
||||||
|
bgcolor: 'info.main',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
pr: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{ color: 'white', fontWeight: 600, fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{item.percentage}%
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Gender Distribution */}
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body1" sx={{ fontWeight: 600, mb: 2 }}>
|
||||||
|
성별
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||||
|
{participantProfile.gender.map((item) => (
|
||||||
|
<Box key={item.label}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||||
|
<Typography variant="body2" sx={{ minWidth: 60 }}>
|
||||||
|
{item.label}
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
flex: 1,
|
||||||
|
height: 24,
|
||||||
|
bgcolor: 'grey.200',
|
||||||
|
borderRadius: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: `${item.percentage}%`,
|
||||||
|
height: '100%',
|
||||||
|
bgcolor: 'error.main',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
pr: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{ color: 'white', fontWeight: 600, fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{item.percentage}%
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user