diff --git a/src/app/(main)/events/[eventId]/page.tsx b/src/app/(main)/events/[eventId]/page.tsx index 6d718dc..578ecd4 100644 --- a/src/app/(main)/events/[eventId]/page.tsx +++ b/src/app/(main)/events/[eventId]/page.tsx @@ -15,6 +15,7 @@ import { Menu, MenuItem, Divider, + LinearProgress, } from '@mui/material'; import { MoreVert, @@ -23,13 +24,63 @@ import { TrendingUp, Share, CardGiftcard, - HowToReg, AttachMoney, People, Edit, Download, Person, + Phone, + Email, + ShoppingCart, + Warning, + LocalFireDepartment, + Star, + NewReleases, } from '@mui/icons-material'; +import { Line, Bar } from 'react-chartjs-2'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + BarElement, + Title, + Tooltip, + Legend, + Filler, +} from 'chart.js'; + +// Chart.js 등록 +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + BarElement, + Title, + Tooltip, + Legend, + Filler +); + +// 디자인 시스템 색상 +const colors = { + pink: '#F472B6', + purple: '#C084FC', + purpleLight: '#E9D5FF', + blue: '#60A5FA', + mint: '#34D399', + orange: '#FB923C', + yellow: '#FBBF24', + gray: { + 900: '#1A1A1A', + 700: '#4A4A4A', + 500: '#9E9E9E', + 300: '#D9D9D9', + 100: '#F5F5F5', + }, +}; // Mock 데이터 const mockEventData = { @@ -41,12 +92,17 @@ const mockEventData = { prize: '커피 쿠폰', method: 'SNS 팔로우', cost: 250000, - channels: ['홈페이지', '카카오톡', 'Instagram'], + channels: ['우리동네TV', '링고비즈', 'SNS'], participants: 128, views: 456, roi: 450, conversion: 28, + targetParticipants: 200, isAIRecommended: true, + isUrgent: false, + isPopular: true, + isHighROI: true, + isNew: false, }; const recentParticipants = [ @@ -57,6 +113,86 @@ const recentParticipants = [ { name: '정*희', phone: '010-****-7890', time: '2시간 전' }, ]; +// 차트 데이터 생성 함수 +const generateParticipationTrendData = (period: '7d' | '30d' | 'all') => { + const labels = + period === '7d' + ? ['1/20', '1/21', '1/22', '1/23', '1/24', '1/25', '1/26'] + : period === '30d' + ? Array.from({ length: 30 }, (_, i) => `1/${i + 1}`) + : Array.from({ length: 31 }, (_, i) => `1/${i + 1}`); + + const data = + period === '7d' + ? [12, 19, 15, 25, 22, 30, 28] + : period === '30d' + ? Array.from({ length: 30 }, () => Math.floor(Math.random() * 30) + 10) + : Array.from({ length: 31 }, () => Math.floor(Math.random() * 30) + 10); + + return { + labels, + datasets: [ + { + label: '일별 참여자', + data, + borderColor: colors.blue, + backgroundColor: `${colors.blue}40`, + fill: true, + tension: 0.4, + }, + ], + }; +}; + +const channelPerformanceData = { + labels: ['우리동네TV', '링고비즈', 'SNS'], + datasets: [ + { + label: '참여자 수', + data: [58, 38, 32], + backgroundColor: [colors.pink, colors.blue, colors.orange], + borderRadius: 8, + }, + ], +}; + +const roiTrendData = { + labels: ['1주차', '2주차', '3주차', '4주차'], + datasets: [ + { + label: 'ROI (%)', + data: [150, 280, 380, 450], + borderColor: colors.mint, + backgroundColor: `${colors.mint}40`, + fill: true, + tension: 0.4, + }, + ], +}; + +// 헬퍼 함수 +const getMethodIcon = (method: string) => { + switch (method) { + case '전화번호 입력': + return ; + case 'SNS 팔로우': + return ; + case '구매 인증': + return ; + case '이메일 등록': + return ; + default: + return ; + } +}; + +const calculateProgress = (event: typeof mockEventData) => { + if (event.status !== 'active') return 0; + const total = new Date(event.endDate).getTime() - new Date(event.startDate).getTime(); + const elapsed = Date.now() - new Date(event.startDate).getTime(); + return Math.min(Math.max((elapsed / total) * 100, 0), 100); +}; + export default function EventDetailPage() { const router = useRouter(); const params = useParams(); @@ -119,11 +255,11 @@ export default function EventDetailPage() { return ( - + {/* Event Header */} - - - + + + {event.title} @@ -131,13 +267,13 @@ export default function EventDetailPage() { - 이벤트 수정 + 이벤트 수정 - 공유하기 + 공유하기 - 데이터 다운로드 + 데이터 다운로드 @@ -146,35 +282,99 @@ export default function EventDetailPage() { - - + + {event.isAIRecommended && ( + + )} + {event.isUrgent && ( } + label="마감임박" + size="medium" + sx={{ bgcolor: '#FEF3C7', color: '#92400E' }} + /> + )} + {event.isPopular && ( + } + label="인기" + size="medium" + sx={{ bgcolor: '#FEE2E2', color: '#991B1B' }} + /> + )} + {event.isHighROI && ( + } + label="높은 ROI" + size="medium" + sx={{ bgcolor: '#DCFCE7', color: '#166534' }} + /> + )} + {event.isNew && ( + } + label="신규" + size="medium" + sx={{ bgcolor: '#DBEAFE', color: '#1E40AF' }} /> )} - - {event.startDate} ~ {event.endDate} + + 📅 {event.startDate} ~ {event.endDate} + + {/* 진행률 바 (진행중인 이벤트만) */} + {event.status === 'active' && ( + + + + 이벤트 진행률 + + + {Math.round(calculateProgress(event))}% + + + + + )} {/* Real-time KPIs */} - - - + + + 실시간 현황 - + @@ -183,56 +383,86 @@ export default function EventDetailPage() { - + - - - - + + + + 참여자 - + {event.participants}명 + + 목표: {event.targetParticipants}명 ( + {Math.round((event.participants / event.targetParticipants) * 100)}%) + - - - - + + + + 조회수 - + {event.views} - - - - + + + + ROI - + {event.roi}% - - - - conversion_path - - + + + + 전환율 - + {event.conversion}% @@ -241,112 +471,236 @@ export default function EventDetailPage() { - {/* Chart Section */} - - - 참여 추이 + {/* Chart Section - 참여 추이 */} + + + 📈 참여 추이 - - - + + + - - - show_chart - - - 참여자 추이 차트 - + + + {/* Chart Section - 채널별 성과 & ROI 추이 */} + + + + 📊 채널별 참여자 + + + + + + + + + + + + + 💰 ROI 추이 + + + + + + + + + + + {/* Event Details */} - - - 이벤트 정보 + + + 🎯 이벤트 정보 - - - - - - - 경품 - - {event.prize} - - - - - - - - - - 참여 방법 - - {event.method} - - - - - - - - - - 예상 비용 - - {event.cost.toLocaleString()}원 - - - - - - - + + + + + 경품 + + + {event.prize} + + + + + + + + {getMethodIcon(event.method)} + + + 참여 방법 + + + {event.method} + + + + + + + + + + + 예상 비용 + + + {event.cost.toLocaleString()}원 + + + + + + + + + + 배포 채널 - + {event.channels.map((channel) => ( - + ))} @@ -356,29 +710,31 @@ export default function EventDetailPage() { {/* Quick Actions */} - - - 빠른 작업 + + + ⚡ 빠른 작업 - + router.push(`/events/${eventId}/participants`)} > - - - 참여자 목록 + + + + 참여자 목록 + @@ -387,18 +743,20 @@ export default function EventDetailPage() { elevation={0} sx={{ cursor: 'pointer', - borderRadius: 3, - boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', + borderRadius: 4, + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', transition: 'all 0.2s', '&:hover': { boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)', - transform: 'translateY(-2px)', + transform: 'translateY(-4px)', }, }} > - - - 이벤트 수정 + + + + 이벤트 수정 + @@ -407,18 +765,20 @@ export default function EventDetailPage() { elevation={0} sx={{ cursor: 'pointer', - borderRadius: 3, - boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', + borderRadius: 4, + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', transition: 'all 0.2s', '&:hover': { boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)', - transform: 'translateY(-2px)', + transform: 'translateY(-4px)', }, }} > - - - 공유하기 + + + + 공유하기 + @@ -427,18 +787,20 @@ export default function EventDetailPage() { elevation={0} sx={{ cursor: 'pointer', - borderRadius: 3, - boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', + borderRadius: 4, + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', transition: 'all 0.2s', '&:hover': { boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)', - transform: 'translateY(-2px)', + transform: 'translateY(-4px)', }, }} > - - - 데이터 다운 + + + + 데이터 다운 + @@ -446,51 +808,51 @@ export default function EventDetailPage() { {/* Recent Participants */} - - - - 최근 참여자 + + + + 👥 최근 참여자 - - + + {recentParticipants.map((participant, index) => ( - {index > 0 && } + {index > 0 && } - + - + - + {participant.name} - + {participant.phone} - + {participant.time}