mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 10:56:23 +00:00
UI 개선: 간격 확대, 차트 색상 변경, 이벤트 목록 기능 추가
- 전체 간격(spacing) 2배 증가 및 컨테이너 패딩 확대 - 기본 폰트 크기 14px→16px 증가 - 채널별 성과 차트 색상을 조화로운 색상으로 변경 (#F472B6, #60A5FA, #FB923C) - 이벤트 목록 페이지에 통계 요약 카드 추가 (전체/진행중/참여자/평균ROI) - 진행중인 이벤트에 진행률 바 추가 - 이벤트 상태 배지 추가 (마감임박/인기/높은ROI/신규) - 상품 및 참여방법에 아이콘 추가 - 이벤트 카드 레이아웃 개선: 참여자/ROI 정보를 오른쪽 하단으로 재배치 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e1fefc2dad
commit
e8ea659c0b
25
package-lock.json
generated
25
package-lock.json
generated
@ -18,11 +18,11 @@
|
|||||||
"@use-funnel/browser": "^0.0.12",
|
"@use-funnel/browser": "^0.0.12",
|
||||||
"@use-funnel/next": "^0.0.12",
|
"@use-funnel/next": "^0.0.12",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"chart.js": "^4.4.6",
|
"chart.js": "^4.5.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"next": "^14.2.15",
|
"next": "^14.2.15",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-chartjs-2": "^5.2.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.53.0",
|
"react-hook-form": "^7.53.0",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
@ -30,6 +30,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.48.0",
|
"@playwright/test": "^1.48.0",
|
||||||
|
"@types/chart.js": "^2.9.41",
|
||||||
"@types/node": "^22.7.5",
|
"@types/node": "^22.7.5",
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
@ -1215,6 +1216,16 @@
|
|||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/chart.js": {
|
||||||
|
"version": "2.9.41",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.41.tgz",
|
||||||
|
"integrity": "sha512-3dvkDvueckY83UyUXtJMalYoH6faOLkWQoaTlJgB4Djde3oORmNP0Jw85HtzTuXyliUHcdp704s0mZFQKio/KQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/json5": {
|
"node_modules/@types/json5": {
|
||||||
"version": "0.0.29",
|
"version": "0.0.29",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||||
@ -4684,6 +4695,16 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
@ -20,11 +20,11 @@
|
|||||||
"@use-funnel/browser": "^0.0.12",
|
"@use-funnel/browser": "^0.0.12",
|
||||||
"@use-funnel/next": "^0.0.12",
|
"@use-funnel/next": "^0.0.12",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"chart.js": "^4.4.6",
|
"chart.js": "^4.5.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"next": "^14.2.15",
|
"next": "^14.2.15",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-chartjs-2": "^5.2.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.53.0",
|
"react-hook-form": "^7.53.0",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.48.0",
|
"@playwright/test": "^1.48.0",
|
||||||
|
"@types/chart.js": "^2.9.41",
|
||||||
"@types/node": "^22.7.5",
|
"@types/node": "^22.7.5",
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
|||||||
@ -10,12 +10,41 @@ import {
|
|||||||
Grid,
|
Grid,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
PieChart,
|
PieChart as PieChartIcon,
|
||||||
ShowChart,
|
ShowChart as ShowChartIcon,
|
||||||
Payments,
|
Payments,
|
||||||
People,
|
People,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
ArcElement,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
} from 'chart.js';
|
||||||
|
import { Pie, Line } from 'react-chartjs-2';
|
||||||
import Header from '@/shared/ui/Header';
|
import Header from '@/shared/ui/Header';
|
||||||
|
import {
|
||||||
|
cardStyles,
|
||||||
|
colors,
|
||||||
|
responsiveText,
|
||||||
|
} from '@/shared/lib/button-styles';
|
||||||
|
|
||||||
|
// Chart.js 등록
|
||||||
|
ChartJS.register(
|
||||||
|
ArcElement,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend
|
||||||
|
);
|
||||||
|
|
||||||
// Mock 데이터
|
// Mock 데이터
|
||||||
const mockAnalyticsData = {
|
const mockAnalyticsData = {
|
||||||
@ -28,9 +57,9 @@ const mockAnalyticsData = {
|
|||||||
targetRoi: 300,
|
targetRoi: 300,
|
||||||
},
|
},
|
||||||
channelPerformance: [
|
channelPerformance: [
|
||||||
{ channel: '우리동네TV', participants: 58, percentage: 45, color: '#E31E24' },
|
{ channel: '우리동네TV', participants: 58, percentage: 45, color: '#F472B6' },
|
||||||
{ channel: '링고비즈', participants: 38, percentage: 30, color: '#0066FF' },
|
{ channel: '링고비즈', participants: 38, percentage: 30, color: '#60A5FA' },
|
||||||
{ channel: 'SNS', participants: 32, percentage: 25, color: '#FFB800' },
|
{ channel: 'SNS', participants: 32, percentage: 25, color: '#FB923C' },
|
||||||
],
|
],
|
||||||
timePerformance: {
|
timePerformance: {
|
||||||
peakTime: '오후 2-4시',
|
peakTime: '오후 2-4시',
|
||||||
@ -98,18 +127,25 @@ export default function AnalyticsPage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header title="성과 분석" showBack={true} showMenu={false} showProfile={true} />
|
<Header title="성과 분석" showBack={true} showMenu={false} showProfile={true} />
|
||||||
<Box sx={{ pt: { xs: 7, sm: 8 }, minHeight: '100vh', bgcolor: 'background.default', pb: 10 }}>
|
<Box
|
||||||
<Container maxWidth="lg" sx={{ pt: 4, pb: 4, px: { xs: 3, sm: 3, md: 4 } }}>
|
sx={{
|
||||||
|
pt: { xs: 7, sm: 8 },
|
||||||
|
pb: 10,
|
||||||
|
bgcolor: colors.gray[50],
|
||||||
|
minHeight: '100vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="lg" sx={{ pt: 8, pb: 6, px: { xs: 6, sm: 8, md: 10 } }}>
|
||||||
{/* Title with Real-time Indicator */}
|
{/* Title with Real-time Indicator */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
mb: 4,
|
mb: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h5" sx={{ fontWeight: 700 }}>
|
<Typography variant="h5" sx={{ ...responsiveText.h3 }}>
|
||||||
📊 요약 (실시간)
|
📊 요약 (실시간)
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
@ -118,7 +154,7 @@ export default function AnalyticsPage() {
|
|||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
bgcolor: 'success.main',
|
bgcolor: colors.mint,
|
||||||
animation: 'pulse 2s infinite',
|
animation: 'pulse 2s infinite',
|
||||||
'@keyframes pulse': {
|
'@keyframes pulse': {
|
||||||
'0%, 100%': { opacity: 1 },
|
'0%, 100%': { opacity: 1 },
|
||||||
@ -126,39 +162,75 @@ export default function AnalyticsPage() {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" sx={{ ...responsiveText.body2 }}>
|
||||||
{updateText}
|
{updateText}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Summary KPI Cards */}
|
{/* Summary KPI Cards */}
|
||||||
<Grid container spacing={3} sx={{ mb: 5 }}>
|
<Grid container spacing={6} sx={{ mb: 10 }}>
|
||||||
<Grid item xs={6} md={3}>
|
<Grid item xs={6} md={3}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card
|
||||||
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
elevation={0}
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
mb: 2,
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
참여자 수
|
참여자 수
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h4" sx={{ fontWeight: 700, my: 1 }}>
|
<Typography variant="h3" sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}>
|
||||||
{summary.participants}명
|
{summary.participants}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="success.main" sx={{ fontWeight: 600 }}>
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{ color: 'rgba(255, 255, 255, 0.8)', fontWeight: 600, fontSize: '0.875rem' }}
|
||||||
|
>
|
||||||
↑ {summary.participantsDelta}명 (오늘)
|
↑ {summary.participantsDelta}명 (오늘)
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6} md={3}>
|
<Grid item xs={6} md={3}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card
|
||||||
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
elevation={0}
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.orange} 0%, ${colors.orangeLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
mb: 2,
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
총 비용
|
총 비용
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h4" sx={{ fontWeight: 700, my: 1 }}>
|
<Typography variant="h3" sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}>
|
||||||
{Math.floor(summary.totalCost / 10000)}만원
|
{Math.floor(summary.totalCost / 10000)}만
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{ color: 'rgba(255, 255, 255, 0.8)', fontWeight: 600, fontSize: '0.875rem' }}
|
||||||
|
>
|
||||||
경품 {Math.floor(roiDetail.prizeCost / 10000)}만 + 채널{' '}
|
경품 {Math.floor(roiDetail.prizeCost / 10000)}만 + 채널{' '}
|
||||||
{Math.floor(roiDetail.channelCost / 10000)}만
|
{Math.floor(roiDetail.channelCost / 10000)}만
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -166,31 +238,67 @@ export default function AnalyticsPage() {
|
|||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6} md={3}>
|
<Grid item xs={6} md={3}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card
|
||||||
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
elevation={0}
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.mint} 0%, ${colors.mintLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
mb: 2,
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
예상 수익
|
예상 수익
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h4" sx={{ fontWeight: 700, my: 1, color: 'success.main' }}>
|
<Typography variant="h3" sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}>
|
||||||
{Math.floor(summary.expectedRevenue / 10000)}만원
|
{Math.floor(summary.expectedRevenue / 10000)}만
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="success.main" sx={{ fontWeight: 600 }}>
|
<Typography
|
||||||
매출증가 {Math.floor(roiDetail.salesIncrease / 10000)}만 + LTV{' '}
|
variant="caption"
|
||||||
|
sx={{ color: 'rgba(255, 255, 255, 0.8)', fontWeight: 600, fontSize: '0.875rem' }}
|
||||||
|
>
|
||||||
|
매출 {Math.floor(roiDetail.salesIncrease / 10000)}만 + LTV{' '}
|
||||||
{Math.floor(roiDetail.newCustomerLTV / 10000)}만
|
{Math.floor(roiDetail.newCustomerLTV / 10000)}만
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6} md={3}>
|
<Grid item xs={6} md={3}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card
|
||||||
<CardContent sx={{ textAlign: 'center', py: 3, px: 2 }}>
|
elevation={0}
|
||||||
<Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}>
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.blueLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
mb: 2,
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
fontWeight: 500,
|
||||||
|
fontSize: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
투자대비수익률
|
투자대비수익률
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h4" sx={{ fontWeight: 700, my: 1, color: 'success.main' }}>
|
<Typography variant="h3" sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}>
|
||||||
{summary.roi}%
|
{summary.roi}%
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption" color="success.main" sx={{ fontWeight: 600 }}>
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={{ color: 'rgba(255, 255, 255, 0.8)', fontWeight: 600, fontSize: '0.875rem' }}
|
||||||
|
>
|
||||||
목표 {summary.targetRoi}% 달성
|
목표 {summary.targetRoi}% 달성
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@ -199,39 +307,76 @@ export default function AnalyticsPage() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Charts Grid */}
|
{/* Charts Grid */}
|
||||||
<Grid container spacing={4} sx={{ mb: 5 }}>
|
<Grid container spacing={6} sx={{ mb: 10 }}>
|
||||||
{/* Channel Performance */}
|
{/* Channel Performance */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card elevation={0} sx={{ ...cardStyles.default, height: '100%' }}>
|
||||||
<CardContent sx={{ p: 4 }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 }, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 6 }}>
|
||||||
<PieChart color="error" />
|
<Box
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
sx={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: '12px',
|
||||||
|
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.pinkLight} 100%)`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PieChartIcon sx={{ fontSize: 24, color: 'white' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900] }}>
|
||||||
채널별 성과
|
채널별 성과
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Chart Placeholder */}
|
{/* Pie Chart */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
aspectRatio: '1 / 1',
|
maxWidth: 300,
|
||||||
bgcolor: 'grey.100',
|
mx: 'auto',
|
||||||
borderRadius: 3,
|
mb: 3,
|
||||||
|
flex: 1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexDirection: 'column',
|
|
||||||
p: 4,
|
|
||||||
mb: 3,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="material-icons" style={{ fontSize: 64, color: '#bdbdbd' }}>
|
<Pie
|
||||||
donut_large
|
data={{
|
||||||
</span>
|
labels: channelPerformance.map((item) => item.channel),
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
datasets: [
|
||||||
파이 차트
|
{
|
||||||
</Typography>
|
data: channelPerformance.map((item) => item.participants),
|
||||||
|
backgroundColor: channelPerformance.map((item) => item.color),
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function (context) {
|
||||||
|
const label = context.label || '';
|
||||||
|
const value = context.parsed || 0;
|
||||||
|
const total = context.dataset.data.reduce((a: number, b: number) => a + b, 0);
|
||||||
|
const percentage = ((value / total) * 100).toFixed(1);
|
||||||
|
return `${label}: ${value}명 (${percentage}%)`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Legend */}
|
{/* Legend */}
|
||||||
@ -250,9 +395,11 @@ export default function AnalyticsPage() {
|
|||||||
bgcolor: item.color,
|
bgcolor: item.color,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography variant="body2">{item.channel}</Typography>
|
<Typography variant="body2" sx={{ color: colors.gray[700] }}>
|
||||||
|
{item.channel}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
<Typography variant="body2" sx={{ fontWeight: 600, color: colors.gray[900] }}>
|
||||||
{item.percentage}% ({item.participants}명)
|
{item.percentage}% ({item.participants}명)
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -264,44 +411,113 @@ export default function AnalyticsPage() {
|
|||||||
|
|
||||||
{/* Time Trend */}
|
{/* Time Trend */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card elevation={0} sx={{ ...cardStyles.default, height: '100%' }}>
|
||||||
<CardContent sx={{ p: 4 }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 }, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 6 }}>
|
||||||
<ShowChart color="error" />
|
<Box
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
sx={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: '12px',
|
||||||
|
background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.blueLight} 100%)`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ShowChartIcon sx={{ fontSize: 24, color: 'white' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900] }}>
|
||||||
시간대별 참여 추이
|
시간대별 참여 추이
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Chart Placeholder */}
|
{/* Line Chart */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
aspectRatio: '16 / 9',
|
mb: 3,
|
||||||
bgcolor: 'grey.100',
|
flex: 1,
|
||||||
borderRadius: 3,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
minHeight: 200,
|
||||||
flexDirection: 'column',
|
|
||||||
p: 4,
|
|
||||||
mb: 3,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="material-icons" style={{ fontSize: 64, color: '#bdbdbd' }}>
|
<Line
|
||||||
trending_up
|
data={{
|
||||||
</span>
|
labels: [
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
'00시',
|
||||||
라인 차트
|
'03시',
|
||||||
</Typography>
|
'06시',
|
||||||
|
'09시',
|
||||||
|
'12시',
|
||||||
|
'15시',
|
||||||
|
'18시',
|
||||||
|
'21시',
|
||||||
|
],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: '참여자 수',
|
||||||
|
data: [3, 2, 5, 12, 28, 35, 22, 15],
|
||||||
|
borderColor: colors.blue,
|
||||||
|
backgroundColor: `${colors.blue}33`,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4,
|
||||||
|
pointBackgroundColor: colors.blue,
|
||||||
|
pointBorderColor: '#fff',
|
||||||
|
pointBorderWidth: 2,
|
||||||
|
pointRadius: 4,
|
||||||
|
pointHoverRadius: 6,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
options={{
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: colors.gray[900],
|
||||||
|
padding: 12,
|
||||||
|
displayColors: false,
|
||||||
|
callbacks: {
|
||||||
|
label: function (context) {
|
||||||
|
return `${context.parsed.y}명`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
color: colors.gray[600],
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: colors.gray[200],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
color: colors.gray[600],
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 0.5 }}>
|
<Typography variant="body2" sx={{ mb: 0.5, color: colors.gray[600] }}>
|
||||||
피크 시간: {timePerformance.peakTime} ({timePerformance.peakParticipants}명)
|
피크 시간: {timePerformance.peakTime} ({timePerformance.peakParticipants}명)
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" sx={{ color: colors.gray[600] }}>
|
||||||
평균 시간당: {timePerformance.avgPerHour}명
|
평균 시간당: {timePerformance.avgPerHour}명
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -311,37 +527,49 @@ export default function AnalyticsPage() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* ROI Detail & Participant Profile */}
|
{/* ROI Detail & Participant Profile */}
|
||||||
<Grid container spacing={4} sx={{ mb: 5 }}>
|
<Grid container spacing={6}>
|
||||||
{/* ROI Detail */}
|
{/* ROI Detail */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card elevation={0} sx={{ ...cardStyles.default, height: '100%' }}>
|
||||||
<CardContent sx={{ p: 4 }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 }, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 6 }}>
|
||||||
<Payments color="error" />
|
<Box
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
sx={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: '12px',
|
||||||
|
background: `linear-gradient(135deg, ${colors.orange} 0%, ${colors.orangeLight} 100%)`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Payments sx={{ fontSize: 24, color: 'white' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900] }}>
|
||||||
투자대비수익률 상세
|
투자대비수익률 상세
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1.5, color: colors.gray[900] }}>
|
||||||
총 비용: {Math.floor(roiDetail.totalCost / 10000)}만원
|
총 비용: {Math.floor(roiDetail.totalCost / 10000)}만원
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ pl: 2, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
<Box sx={{ pl: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" sx={{ color: colors.gray[600] }}>
|
||||||
• 경품 비용
|
• 경품 비용
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
<Typography variant="body2" sx={{ fontWeight: 600, color: colors.gray[900] }}>
|
||||||
{Math.floor(roiDetail.prizeCost / 10000)}만원
|
{Math.floor(roiDetail.prizeCost / 10000)}만원
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" sx={{ color: colors.gray[600] }}>
|
||||||
• 채널 비용
|
• 채널 비용
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
<Typography variant="body2" sx={{ fontWeight: 600, color: colors.gray[900] }}>
|
||||||
{Math.floor(roiDetail.channelCost / 10000)}만원
|
{Math.floor(roiDetail.channelCost / 10000)}만원
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -349,23 +577,23 @@ export default function AnalyticsPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1.5, color: colors.gray[900] }}>
|
||||||
예상 수익: {Math.floor(roiDetail.expectedRevenue / 10000)}만원
|
예상 수익: {Math.floor(roiDetail.expectedRevenue / 10000)}만원
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ pl: 2, display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
<Box sx={{ pl: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" sx={{ color: colors.gray[600] }}>
|
||||||
• 매출 증가
|
• 매출 증가
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600, color: 'success.main' }}>
|
<Typography variant="body2" sx={{ fontWeight: 600, color: colors.mint }}>
|
||||||
{Math.floor(roiDetail.salesIncrease / 10000)}만원
|
{Math.floor(roiDetail.salesIncrease / 10000)}만원
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" sx={{ color: colors.gray[600] }}>
|
||||||
• 신규 고객 LTV
|
• 신규 고객 LTV
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600, color: 'success.main' }}>
|
<Typography variant="body2" sx={{ fontWeight: 600, color: colors.mint }}>
|
||||||
{Math.floor(roiDetail.newCustomerLTV / 10000)}만원
|
{Math.floor(roiDetail.newCustomerLTV / 10000)}만원
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -373,26 +601,26 @@ export default function AnalyticsPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1, color: 'success.main' }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 700, mb: 1.5, color: colors.mint }}>
|
||||||
투자대비수익률
|
투자대비수익률
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
p: 2,
|
p: 2.5,
|
||||||
bgcolor: 'grey.100',
|
bgcolor: colors.gray[100],
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="body2" sx={{ mb: 0.5 }}>
|
<Typography variant="body2" sx={{ mb: 0.5, color: colors.gray[700] }}>
|
||||||
(수익 - 비용) ÷ 비용 × 100
|
(수익 - 비용) ÷ 비용 × 100
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ mb: 1 }}>
|
<Typography variant="body2" sx={{ mb: 1.5, color: colors.gray[600] }}>
|
||||||
({Math.floor(roiDetail.expectedRevenue / 10000)}만 -{' '}
|
({Math.floor(roiDetail.expectedRevenue / 10000)}만 -{' '}
|
||||||
{Math.floor(roiDetail.totalCost / 10000)}만) ÷{' '}
|
{Math.floor(roiDetail.totalCost / 10000)}만) ÷{' '}
|
||||||
{Math.floor(roiDetail.totalCost / 10000)}만 × 100
|
{Math.floor(roiDetail.totalCost / 10000)}만 × 100
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h5" sx={{ fontWeight: 700, color: 'success.main' }}>
|
<Typography variant="h5" sx={{ fontWeight: 700, color: colors.mint }}>
|
||||||
= {summary.roi}%
|
= {summary.roi}%
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -404,33 +632,45 @@ export default function AnalyticsPage() {
|
|||||||
|
|
||||||
{/* Participant Profile */}
|
{/* Participant Profile */}
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<Card elevation={0} sx={{ borderRadius: 3, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }}>
|
<Card elevation={0} sx={{ ...cardStyles.default, height: '100%' }}>
|
||||||
<CardContent sx={{ p: 4 }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 }, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 6 }}>
|
||||||
<People color="error" />
|
<Box
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
sx={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: '12px',
|
||||||
|
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<People sx={{ fontSize: 24, color: 'white' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900] }}>
|
||||||
참여자 프로필
|
참여자 프로필
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Age Distribution */}
|
{/* Age Distribution */}
|
||||||
<Box sx={{ mb: 4 }}>
|
<Box sx={{ mb: 4 }}>
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600, mb: 2 }}>
|
<Typography variant="body1" sx={{ fontWeight: 600, mb: 2, color: colors.gray[900] }}>
|
||||||
연령별
|
연령별
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||||
{participantProfile.age.map((item) => (
|
{participantProfile.age.map((item) => (
|
||||||
<Box key={item.label}>
|
<Box key={item.label}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||||
<Typography variant="body2" sx={{ minWidth: 60 }}>
|
<Typography variant="body2" sx={{ minWidth: 60, color: colors.gray[700] }}>
|
||||||
{item.label}
|
{item.label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: 24,
|
height: 28,
|
||||||
bgcolor: 'grey.200',
|
bgcolor: colors.gray[200],
|
||||||
borderRadius: 1,
|
borderRadius: 1.5,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -438,11 +678,11 @@ export default function AnalyticsPage() {
|
|||||||
sx={{
|
sx={{
|
||||||
width: `${item.percentage}%`,
|
width: `${item.percentage}%`,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
bgcolor: 'info.main',
|
background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.blueLight} 100%)`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
pr: 1,
|
pr: 1.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@ -461,22 +701,22 @@ export default function AnalyticsPage() {
|
|||||||
|
|
||||||
{/* Gender Distribution */}
|
{/* Gender Distribution */}
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600, mb: 2 }}>
|
<Typography variant="body1" sx={{ fontWeight: 600, mb: 2, color: colors.gray[900] }}>
|
||||||
성별
|
성별
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||||
{participantProfile.gender.map((item) => (
|
{participantProfile.gender.map((item) => (
|
||||||
<Box key={item.label}>
|
<Box key={item.label}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||||
<Typography variant="body2" sx={{ minWidth: 60 }}>
|
<Typography variant="body2" sx={{ minWidth: 60, color: colors.gray[700] }}>
|
||||||
{item.label}
|
{item.label}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: 24,
|
height: 28,
|
||||||
bgcolor: 'grey.200',
|
bgcolor: colors.gray[200],
|
||||||
borderRadius: 1,
|
borderRadius: 1.5,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -484,11 +724,11 @@ export default function AnalyticsPage() {
|
|||||||
sx={{
|
sx={{
|
||||||
width: `${item.percentage}%`,
|
width: `${item.percentage}%`,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
bgcolor: 'error.main',
|
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.pinkLight} 100%)`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
pr: 1,
|
pr: 1.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
|
|||||||
@ -13,13 +13,34 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
Typography,
|
Typography,
|
||||||
Chip,
|
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
Pagination,
|
Pagination,
|
||||||
Grid,
|
Grid,
|
||||||
|
LinearProgress,
|
||||||
|
Chip,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Search, FilterList } from '@mui/icons-material';
|
import {
|
||||||
|
Search,
|
||||||
|
FilterList,
|
||||||
|
Event,
|
||||||
|
TrendingUp,
|
||||||
|
People,
|
||||||
|
CardGiftcard,
|
||||||
|
Phone,
|
||||||
|
Share,
|
||||||
|
ShoppingCart,
|
||||||
|
Email,
|
||||||
|
LocalFireDepartment,
|
||||||
|
NewReleases,
|
||||||
|
Warning,
|
||||||
|
Star,
|
||||||
|
} from '@mui/icons-material';
|
||||||
import Header from '@/shared/ui/Header';
|
import Header from '@/shared/ui/Header';
|
||||||
|
import {
|
||||||
|
cardStyles,
|
||||||
|
colors,
|
||||||
|
responsiveText,
|
||||||
|
} from '@/shared/lib/button-styles';
|
||||||
|
|
||||||
// Mock 데이터
|
// Mock 데이터
|
||||||
const mockEvents = [
|
const mockEvents = [
|
||||||
@ -29,11 +50,16 @@ const mockEvents = [
|
|||||||
status: 'active' as const,
|
status: 'active' as const,
|
||||||
daysLeft: 5,
|
daysLeft: 5,
|
||||||
participants: 128,
|
participants: 128,
|
||||||
|
targetParticipants: 200,
|
||||||
roi: 450,
|
roi: 450,
|
||||||
startDate: '2025-11-01',
|
startDate: '2025-11-01',
|
||||||
endDate: '2025-11-15',
|
endDate: '2025-11-15',
|
||||||
prize: '커피 쿠폰',
|
prize: '커피 쿠폰',
|
||||||
method: '전화번호 입력',
|
method: '전화번호 입력',
|
||||||
|
isUrgent: true,
|
||||||
|
isPopular: false,
|
||||||
|
isHighROI: true,
|
||||||
|
isNew: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
@ -41,11 +67,16 @@ const mockEvents = [
|
|||||||
status: 'active' as const,
|
status: 'active' as const,
|
||||||
daysLeft: 12,
|
daysLeft: 12,
|
||||||
participants: 56,
|
participants: 56,
|
||||||
|
targetParticipants: 100,
|
||||||
roi: 320,
|
roi: 320,
|
||||||
startDate: '2025-11-05',
|
startDate: '2025-11-05',
|
||||||
endDate: '2025-11-20',
|
endDate: '2025-11-20',
|
||||||
prize: '할인 쿠폰',
|
prize: '할인 쿠폰',
|
||||||
method: 'SNS 팔로우',
|
method: 'SNS 팔로우',
|
||||||
|
isUrgent: false,
|
||||||
|
isPopular: false,
|
||||||
|
isHighROI: false,
|
||||||
|
isNew: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
@ -53,11 +84,16 @@ const mockEvents = [
|
|||||||
status: 'ended' as const,
|
status: 'ended' as const,
|
||||||
daysLeft: 0,
|
daysLeft: 0,
|
||||||
participants: 234,
|
participants: 234,
|
||||||
|
targetParticipants: 150,
|
||||||
roi: 580,
|
roi: 580,
|
||||||
startDate: '2025-10-15',
|
startDate: '2025-10-15',
|
||||||
endDate: '2025-10-31',
|
endDate: '2025-10-31',
|
||||||
prize: '상품권',
|
prize: '상품권',
|
||||||
method: '구매 인증',
|
method: '구매 인증',
|
||||||
|
isUrgent: false,
|
||||||
|
isPopular: true,
|
||||||
|
isHighROI: true,
|
||||||
|
isNew: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
@ -65,11 +101,16 @@ const mockEvents = [
|
|||||||
status: 'scheduled' as const,
|
status: 'scheduled' as const,
|
||||||
daysLeft: 30,
|
daysLeft: 30,
|
||||||
participants: 0,
|
participants: 0,
|
||||||
|
targetParticipants: 300,
|
||||||
roi: 0,
|
roi: 0,
|
||||||
startDate: '2025-12-01',
|
startDate: '2025-12-01',
|
||||||
endDate: '2025-12-15',
|
endDate: '2025-12-15',
|
||||||
prize: '체험권',
|
prize: '체험권',
|
||||||
method: '이메일 등록',
|
method: '이메일 등록',
|
||||||
|
isUrgent: false,
|
||||||
|
isPopular: false,
|
||||||
|
isHighROI: false,
|
||||||
|
isNew: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -114,16 +155,28 @@ export default function EventsPage() {
|
|||||||
router.push(`/events/${eventId}`);
|
router.push(`/events/${eventId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusStyle = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'active':
|
case 'active':
|
||||||
return 'success';
|
return {
|
||||||
|
bgcolor: colors.mint,
|
||||||
|
color: 'white',
|
||||||
|
};
|
||||||
case 'scheduled':
|
case 'scheduled':
|
||||||
return 'info';
|
return {
|
||||||
|
bgcolor: colors.blue,
|
||||||
|
color: 'white',
|
||||||
|
};
|
||||||
case 'ended':
|
case 'ended':
|
||||||
return 'default';
|
return {
|
||||||
|
bgcolor: colors.gray[300],
|
||||||
|
color: colors.gray[700],
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return 'default';
|
return {
|
||||||
|
bgcolor: colors.gray[200],
|
||||||
|
color: colors.gray[600],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -140,13 +193,149 @@ export default function EventsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getMethodIcon = (method: string) => {
|
||||||
|
switch (method) {
|
||||||
|
case '전화번호 입력':
|
||||||
|
return <Phone sx={{ fontSize: 18 }} />;
|
||||||
|
case 'SNS 팔로우':
|
||||||
|
return <Share sx={{ fontSize: 18 }} />;
|
||||||
|
case '구매 인증':
|
||||||
|
return <ShoppingCart sx={{ fontSize: 18 }} />;
|
||||||
|
case '이메일 등록':
|
||||||
|
return <Email sx={{ fontSize: 18 }} />;
|
||||||
|
default:
|
||||||
|
return <Event sx={{ fontSize: 18 }} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateProgress = (event: typeof mockEvents[0]) => {
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 통계 계산
|
||||||
|
const stats = {
|
||||||
|
total: mockEvents.length,
|
||||||
|
active: mockEvents.filter((e) => e.status === 'active').length,
|
||||||
|
totalParticipants: mockEvents.reduce((sum, e) => sum + e.participants, 0),
|
||||||
|
avgROI: Math.round(
|
||||||
|
mockEvents.filter((e) => e.roi > 0).reduce((sum, e) => sum + e.roi, 0) /
|
||||||
|
mockEvents.filter((e) => e.roi > 0).length
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header title="이벤트 목록" showBack={true} showMenu={false} showProfile={true} />
|
<Header title="이벤트 목록" showBack={true} showMenu={false} showProfile={true} />
|
||||||
<Box sx={{ pt: { xs: 7, sm: 8 }, minHeight: '100vh', bgcolor: 'background.default', pb: 10 }}>
|
<Box
|
||||||
<Container maxWidth="lg" sx={{ pt: 4, pb: 4, px: { xs: 3, sm: 3, md: 4 } }}>
|
sx={{
|
||||||
|
pt: { xs: 7, sm: 8 },
|
||||||
|
pb: 10,
|
||||||
|
bgcolor: colors.gray[50],
|
||||||
|
minHeight: '100vh',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="lg" sx={{ pt: 8, pb: 6, px: { xs: 6, sm: 8, md: 10 } }}>
|
||||||
|
{/* Summary Statistics */}
|
||||||
|
<Grid container spacing={4} sx={{ mb: 8 }}>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Card
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
||||||
|
<Event sx={{ fontSize: 32, color: 'white', mb: 1 }} />
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem', mb: 0.5 }}
|
||||||
|
>
|
||||||
|
{stats.total}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', fontSize: '0.875rem' }}>
|
||||||
|
전체 이벤트
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Card
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.mint} 0%, ${colors.mintLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
||||||
|
<LocalFireDepartment sx={{ fontSize: 32, color: 'white', mb: 1 }} />
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem', mb: 0.5 }}
|
||||||
|
>
|
||||||
|
{stats.active}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', fontSize: '0.875rem' }}>
|
||||||
|
진행중
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Card
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.blueLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
||||||
|
<People sx={{ fontSize: 32, color: 'white', mb: 1 }} />
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem', mb: 0.5 }}
|
||||||
|
>
|
||||||
|
{stats.totalParticipants}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', fontSize: '0.875rem' }}>
|
||||||
|
총 참여자
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} sm={3}>
|
||||||
|
<Card
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...cardStyles.default,
|
||||||
|
background: `linear-gradient(135deg, ${colors.orange} 0%, ${colors.orangeLight} 100%)`,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
||||||
|
<TrendingUp sx={{ fontSize: 32, color: 'white', mb: 1 }} />
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem', mb: 0.5 }}
|
||||||
|
>
|
||||||
|
{stats.avgROI}%
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', fontSize: '0.875rem' }}>
|
||||||
|
평균 ROI
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
{/* Search Section */}
|
{/* Search Section */}
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 8 }}>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
placeholder="이벤트명 검색..."
|
placeholder="이벤트명 검색..."
|
||||||
@ -155,28 +344,51 @@ export default function EventsPage() {
|
|||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment position="start">
|
<InputAdornment position="start">
|
||||||
<Search />
|
<Search sx={{ color: colors.gray[400] }} />
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
'& .MuiOutlinedInput-root': {
|
'& .MuiOutlinedInput-root': {
|
||||||
borderRadius: 2,
|
borderRadius: 3,
|
||||||
|
bgcolor: 'white',
|
||||||
|
'& fieldset': {
|
||||||
|
borderColor: colors.gray[200],
|
||||||
|
},
|
||||||
|
'&:hover fieldset': {
|
||||||
|
borderColor: colors.gray[300],
|
||||||
|
},
|
||||||
|
'&.Mui-focused fieldset': {
|
||||||
|
borderColor: colors.purple,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Filters */}
|
{/* Filters */}
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 8 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'wrap' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' }}>
|
||||||
<FilterList color="error" />
|
<FilterList sx={{ color: colors.purple }} />
|
||||||
<FormControl sx={{ flex: 1, minWidth: 120 }}>
|
<FormControl sx={{ flex: 1, minWidth: 120 }}>
|
||||||
<InputLabel>상태</InputLabel>
|
<InputLabel>상태</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={statusFilter}
|
value={statusFilter}
|
||||||
label="상태"
|
label="상태"
|
||||||
onChange={(e) => setStatusFilter(e.target.value as EventStatus)}
|
onChange={(e) => setStatusFilter(e.target.value as EventStatus)}
|
||||||
|
sx={{
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: 'white',
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.gray[200],
|
||||||
|
},
|
||||||
|
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.gray[300],
|
||||||
|
},
|
||||||
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.purple,
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem value="all">전체</MenuItem>
|
<MenuItem value="all">전체</MenuItem>
|
||||||
<MenuItem value="active">진행중</MenuItem>
|
<MenuItem value="active">진행중</MenuItem>
|
||||||
@ -190,6 +402,19 @@ export default function EventsPage() {
|
|||||||
value={periodFilter}
|
value={periodFilter}
|
||||||
label="기간"
|
label="기간"
|
||||||
onChange={(e) => setPeriodFilter(e.target.value as Period)}
|
onChange={(e) => setPeriodFilter(e.target.value as Period)}
|
||||||
|
sx={{
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: 'white',
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.gray[200],
|
||||||
|
},
|
||||||
|
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.gray[300],
|
||||||
|
},
|
||||||
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.purple,
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem value="1month">최근 1개월</MenuItem>
|
<MenuItem value="1month">최근 1개월</MenuItem>
|
||||||
<MenuItem value="3months">최근 3개월</MenuItem>
|
<MenuItem value="3months">최근 3개월</MenuItem>
|
||||||
@ -202,16 +427,29 @@ export default function EventsPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Sorting */}
|
{/* Sorting */}
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 8 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" sx={{ ...responsiveText.body2, fontWeight: 600 }}>
|
||||||
정렬:
|
정렬
|
||||||
</Typography>
|
</Typography>
|
||||||
<FormControl sx={{ width: 160 }}>
|
<FormControl sx={{ width: 160 }}>
|
||||||
<Select
|
<Select
|
||||||
value={sortBy}
|
value={sortBy}
|
||||||
onChange={(e) => setSortBy(e.target.value as SortBy)}
|
onChange={(e) => setSortBy(e.target.value as SortBy)}
|
||||||
size="small"
|
size="small"
|
||||||
|
sx={{
|
||||||
|
borderRadius: 2,
|
||||||
|
bgcolor: 'white',
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.gray[200],
|
||||||
|
},
|
||||||
|
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.gray[300],
|
||||||
|
},
|
||||||
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: colors.purple,
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem value="latest">최신순</MenuItem>
|
<MenuItem value="latest">최신순</MenuItem>
|
||||||
<MenuItem value="participants">참여자순</MenuItem>
|
<MenuItem value="participants">참여자순</MenuItem>
|
||||||
@ -222,89 +460,234 @@ export default function EventsPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Event List */}
|
{/* Event List */}
|
||||||
<Box sx={{ mb: 5 }}>
|
<Box sx={{ mb: 10 }}>
|
||||||
{pageEvents.length === 0 ? (
|
{pageEvents.length === 0 ? (
|
||||||
<Box sx={{ textAlign: 'center', py: 8 }}>
|
<Card
|
||||||
<span className="material-icons" style={{ fontSize: 64, color: '#bdbdbd' }}>
|
elevation={0}
|
||||||
event_busy
|
sx={{
|
||||||
</span>
|
...cardStyles.default,
|
||||||
<Typography variant="body1" color="text.secondary" sx={{ mt: 2 }}>
|
}}
|
||||||
검색 결과가 없습니다
|
>
|
||||||
</Typography>
|
<CardContent sx={{ textAlign: 'center', py: 20 }}>
|
||||||
</Box>
|
<Box sx={{ color: colors.gray[300], mb: 3 }}>
|
||||||
|
<span className="material-icons" style={{ fontSize: 72 }}>
|
||||||
|
event_busy
|
||||||
|
</span>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ mb: 2, color: colors.gray[700] }}>
|
||||||
|
검색 결과가 없습니다
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: colors.gray[500] }}>
|
||||||
|
다른 검색 조건으로 다시 시도해보세요
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
{pageEvents.map((event) => (
|
{pageEvents.map((event) => (
|
||||||
<Card
|
<Card
|
||||||
key={event.id}
|
key={event.id}
|
||||||
elevation={0}
|
elevation={0}
|
||||||
sx={{
|
sx={{
|
||||||
cursor: 'pointer',
|
...cardStyles.clickable,
|
||||||
borderRadius: 3,
|
|
||||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
|
||||||
transition: 'all 0.2s ease',
|
|
||||||
'&:hover': {
|
|
||||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
||||||
transform: 'translateY(-2px)',
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
onClick={() => handleEventClick(event.id)}
|
onClick={() => handleEventClick(event.id)}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
||||||
{/* Header */}
|
{/* Header with Badges */}
|
||||||
<Box
|
<Box sx={{ mb: 4 }}>
|
||||||
sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
alignItems: 'flex-start',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
mb: 2,
|
alignItems: 'start',
|
||||||
}}
|
mb: 3,
|
||||||
>
|
}}
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700, flex: 1 }}>
|
>
|
||||||
{event.title}
|
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900], flex: 1 }}>
|
||||||
</Typography>
|
{event.title}
|
||||||
<Chip
|
</Typography>
|
||||||
label={`${getStatusText(event.status)}${
|
<Box
|
||||||
event.status === 'active'
|
sx={{
|
||||||
|
px: 2.5,
|
||||||
|
py: 0.75,
|
||||||
|
borderRadius: 2,
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
fontWeight: 600,
|
||||||
|
...getStatusStyle(event.status),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getStatusText(event.status)}
|
||||||
|
{event.status === 'active'
|
||||||
? ` | D-${event.daysLeft}`
|
? ` | D-${event.daysLeft}`
|
||||||
: event.status === 'scheduled'
|
: event.status === 'scheduled'
|
||||||
? ` | D+${event.daysLeft}`
|
? ` | D+${event.daysLeft}`
|
||||||
: ''
|
: ''}
|
||||||
}`}
|
</Box>
|
||||||
color={getStatusColor(event.status) as any}
|
</Box>
|
||||||
size="small"
|
|
||||||
sx={{ fontWeight: 600 }}
|
{/* Status Badges */}
|
||||||
/>
|
<Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap' }}>
|
||||||
|
{event.isUrgent && (
|
||||||
|
<Chip
|
||||||
|
icon={<Warning sx={{ fontSize: 16 }} />}
|
||||||
|
label="마감임박"
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
bgcolor: '#FEF3C7',
|
||||||
|
color: '#92400E',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
'& .MuiChip-icon': { color: '#92400E' },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{event.isPopular && (
|
||||||
|
<Chip
|
||||||
|
icon={<LocalFireDepartment sx={{ fontSize: 16 }} />}
|
||||||
|
label="인기"
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
bgcolor: '#FEE2E2',
|
||||||
|
color: '#991B1B',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
'& .MuiChip-icon': { color: '#991B1B' },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{event.isHighROI && (
|
||||||
|
<Chip
|
||||||
|
icon={<Star sx={{ fontSize: 16 }} />}
|
||||||
|
label="높은 ROI"
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
bgcolor: '#DCFCE7',
|
||||||
|
color: '#166534',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
'& .MuiChip-icon': { color: '#166534' },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{event.isNew && (
|
||||||
|
<Chip
|
||||||
|
icon={<NewReleases sx={{ fontSize: 16 }} />}
|
||||||
|
label="신규"
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
bgcolor: '#DBEAFE',
|
||||||
|
color: '#1E40AF',
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
'& .MuiChip-icon': { color: '#1E40AF' },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Progress Bar for Active Events */}
|
||||||
<Grid container spacing={2} sx={{ mb: 2 }}>
|
{event.status === 'active' && (
|
||||||
<Grid item xs={6}>
|
<Box sx={{ mb: 4 }}>
|
||||||
<Box>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" sx={{ color: colors.gray[600], fontSize: '0.75rem' }}>
|
||||||
참여
|
이벤트 진행률
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
<Typography variant="caption" sx={{ color: colors.gray[700], fontWeight: 600, fontSize: '0.75rem' }}>
|
||||||
{event.participants}명
|
{Math.round(calculateProgress(event))}%
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
<LinearProgress
|
||||||
<Grid item xs={6}>
|
variant="determinate"
|
||||||
|
value={calculateProgress(event)}
|
||||||
|
sx={{
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
bgcolor: colors.gray[200],
|
||||||
|
'& .MuiLinearProgress-bar': {
|
||||||
|
borderRadius: 4,
|
||||||
|
background: `linear-gradient(90deg, ${colors.mint} 0%, ${colors.blue} 100%)`,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Event Info and Stats Container */}
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
|
||||||
|
{/* Left: Event Info */}
|
||||||
|
<Box>
|
||||||
|
<Box sx={{ mb: 4, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<CardGiftcard sx={{ fontSize: 18, color: colors.pink }} />
|
||||||
|
<Typography variant="body2" sx={{ color: colors.gray[700], fontSize: '0.875rem' }}>
|
||||||
|
{event.prize}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
{getMethodIcon(event.method)}
|
||||||
|
<Typography variant="body2" sx={{ color: colors.gray[700], fontSize: '0.875rem' }}>
|
||||||
|
{event.method}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Date */}
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
color: colors.gray[600],
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>📅</span>
|
||||||
|
<span>
|
||||||
|
{event.startDate} ~ {event.endDate}
|
||||||
|
</span>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Right: Stats */}
|
||||||
|
<Box sx={{ display: 'flex', gap: 8, textAlign: 'right' }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography
|
||||||
투자대비수익률
|
variant="body2"
|
||||||
|
sx={{ mb: 1, color: colors.gray[600], fontWeight: 500 }}
|
||||||
|
>
|
||||||
|
참여자
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600, color: 'error.main' }}>
|
<Typography variant="h5" sx={{ fontWeight: 700, color: colors.gray[900], mb: 0.5 }}>
|
||||||
|
{event.participants.toLocaleString()}
|
||||||
|
<Typography
|
||||||
|
component="span"
|
||||||
|
variant="body2"
|
||||||
|
sx={{ ml: 0.5, color: colors.gray[600] }}
|
||||||
|
>
|
||||||
|
명
|
||||||
|
</Typography>
|
||||||
|
</Typography>
|
||||||
|
{event.targetParticipants > 0 && (
|
||||||
|
<Typography variant="caption" sx={{ color: colors.gray[500], fontSize: '0.75rem' }}>
|
||||||
|
목표: {event.targetParticipants}명 ({Math.round((event.participants / event.targetParticipants) * 100)}%)
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{ mb: 1, color: colors.gray[600], fontWeight: 500 }}
|
||||||
|
>
|
||||||
|
ROI
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h5" sx={{ fontWeight: 700, color: event.roi >= 400 ? colors.mint : event.roi >= 200 ? colors.orange : colors.gray[500] }}>
|
||||||
{event.roi}%
|
{event.roi}%
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
</Box>
|
||||||
|
|
||||||
{/* Date */}
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
{event.startDate} ~ {event.endDate}
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
@ -314,13 +697,28 @@ export default function EventsPage() {
|
|||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
{totalPages > 1 && (
|
{totalPages > 1 && (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 5 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||||
<Pagination
|
<Pagination
|
||||||
count={totalPages}
|
count={totalPages}
|
||||||
page={currentPage}
|
page={currentPage}
|
||||||
onChange={(_, page) => setCurrentPage(page)}
|
onChange={(_, page) => setCurrentPage(page)}
|
||||||
color="primary"
|
|
||||||
size="large"
|
size="large"
|
||||||
|
sx={{
|
||||||
|
'& .MuiPaginationItem-root': {
|
||||||
|
color: colors.gray[700],
|
||||||
|
fontWeight: 600,
|
||||||
|
'&.Mui-selected': {
|
||||||
|
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.blue} 100%)`,
|
||||||
|
color: 'white',
|
||||||
|
'&:hover': {
|
||||||
|
background: `linear-gradient(135deg, ${colors.purpleLight} 0%, ${colors.blueLight} 100%)`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&:hover': {
|
||||||
|
bgcolor: colors.gray[100],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -88,14 +88,14 @@ export default function HomePage() {
|
|||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container maxWidth="lg" sx={{ pt: 5, pb: 4, px: { xs: 3, sm: 4, md: 5 } }}>
|
<Container maxWidth="lg" sx={{ pt: 8, pb: 6, px: { xs: 6, sm: 8, md: 10 } }}>
|
||||||
{/* Welcome Section */}
|
{/* Welcome Section */}
|
||||||
<Box sx={{ mb: 6 }}>
|
<Box sx={{ mb: 10 }}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h3"
|
variant="h3"
|
||||||
sx={{
|
sx={{
|
||||||
...responsiveText.h2,
|
...responsiveText.h2,
|
||||||
mb: 2,
|
mb: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
안녕하세요, {mockUser.name}님! 👋
|
안녕하세요, {mockUser.name}님! 👋
|
||||||
@ -106,7 +106,7 @@ export default function HomePage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* KPI Cards */}
|
{/* KPI Cards */}
|
||||||
<Grid container spacing={4} sx={{ mb: 6 }}>
|
<Grid container spacing={6} sx={{ mb: 10 }}>
|
||||||
<Grid item xs={12} sm={4}>
|
<Grid item xs={12} sm={4}>
|
||||||
<Card
|
<Card
|
||||||
elevation={0}
|
elevation={0}
|
||||||
@ -116,7 +116,7 @@ export default function HomePage() {
|
|||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 64,
|
width: 64,
|
||||||
@ -127,7 +127,7 @@ export default function HomePage() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
mb: 2,
|
mb: 3,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Celebration sx={{ fontSize: 32, color: 'white' }} />
|
<Celebration sx={{ fontSize: 32, color: 'white' }} />
|
||||||
@ -145,7 +145,7 @@ export default function HomePage() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h3"
|
variant="h3"
|
||||||
sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}
|
sx={{ fontWeight: 700, color: 'white', fontSize: '2.25rem' }}
|
||||||
>
|
>
|
||||||
{activeEvents.length}
|
{activeEvents.length}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -161,7 +161,7 @@ export default function HomePage() {
|
|||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 64,
|
width: 64,
|
||||||
@ -172,7 +172,7 @@ export default function HomePage() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
mb: 2,
|
mb: 3,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Group sx={{ fontSize: 32, color: 'white' }} />
|
<Group sx={{ fontSize: 32, color: 'white' }} />
|
||||||
@ -190,7 +190,7 @@ export default function HomePage() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h3"
|
variant="h3"
|
||||||
sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}
|
sx={{ fontWeight: 700, color: 'white', fontSize: '2.25rem' }}
|
||||||
>
|
>
|
||||||
{totalParticipants.toLocaleString()}
|
{totalParticipants.toLocaleString()}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -206,7 +206,7 @@ export default function HomePage() {
|
|||||||
borderColor: 'transparent',
|
borderColor: 'transparent',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 4, px: 3 }}>
|
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 64,
|
width: 64,
|
||||||
@ -217,7 +217,7 @@ export default function HomePage() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
mb: 2,
|
mb: 3,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TrendingUp sx={{ fontSize: 32, color: 'white' }} />
|
<TrendingUp sx={{ fontSize: 32, color: 'white' }} />
|
||||||
@ -235,7 +235,7 @@ export default function HomePage() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h3"
|
variant="h3"
|
||||||
sx={{ fontWeight: 700, color: 'white', fontSize: '2.5rem' }}
|
sx={{ fontWeight: 700, color: 'white', fontSize: '2.25rem' }}
|
||||||
>
|
>
|
||||||
{avgROI}%
|
{avgROI}%
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -245,11 +245,11 @@ export default function HomePage() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{/* Quick Actions */}
|
{/* Quick Actions */}
|
||||||
<Box sx={{ mb: 6 }}>
|
<Box sx={{ mb: 10 }}>
|
||||||
<Typography variant="h5" sx={{ ...responsiveText.h3, mb: 4 }}>
|
<Typography variant="h5" sx={{ ...responsiveText.h3, mb: 6 }}>
|
||||||
빠른 시작
|
빠른 시작
|
||||||
</Typography>
|
</Typography>
|
||||||
<Grid container spacing={4}>
|
<Grid container spacing={6}>
|
||||||
<Grid item xs={6} sm={6}>
|
<Grid item xs={6} sm={6}>
|
||||||
<Card
|
<Card
|
||||||
elevation={0}
|
elevation={0}
|
||||||
@ -258,7 +258,7 @@ export default function HomePage() {
|
|||||||
}}
|
}}
|
||||||
onClick={handleCreateEvent}
|
onClick={handleCreateEvent}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 5 }}>
|
<CardContent sx={{ textAlign: 'center', py: 6 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 72,
|
width: 72,
|
||||||
@ -269,13 +269,13 @@ export default function HomePage() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
mb: 2.5,
|
mb: 3,
|
||||||
boxShadow: '0 4px 14px 0 rgba(167, 139, 250, 0.39)',
|
boxShadow: '0 4px 14px 0 rgba(167, 139, 250, 0.39)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Add sx={{ fontSize: 36, color: 'white' }} />
|
<Add sx={{ fontSize: 36, color: 'white' }} />
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600, color: colors.gray[900] }}>
|
<Typography variant="body1" sx={{ fontWeight: 600, color: colors.gray[900], fontSize: '1.125rem' }}>
|
||||||
새 이벤트
|
새 이벤트
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@ -289,7 +289,7 @@ export default function HomePage() {
|
|||||||
}}
|
}}
|
||||||
onClick={handleViewAnalytics}
|
onClick={handleViewAnalytics}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ textAlign: 'center', py: 5 }}>
|
<CardContent sx={{ textAlign: 'center', py: 6 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: 72,
|
width: 72,
|
||||||
@ -300,13 +300,13 @@ export default function HomePage() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
mb: 2.5,
|
mb: 3,
|
||||||
boxShadow: '0 4px 14px 0 rgba(96, 165, 250, 0.39)',
|
boxShadow: '0 4px 14px 0 rgba(96, 165, 250, 0.39)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Analytics sx={{ fontSize: 36, color: 'white' }} />
|
<Analytics sx={{ fontSize: 36, color: 'white' }} />
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="body1" sx={{ fontWeight: 600, color: colors.gray[900] }}>
|
<Typography variant="body1" sx={{ fontWeight: 600, color: colors.gray[900], fontSize: '1.125rem' }}>
|
||||||
성과분석
|
성과분석
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@ -316,9 +316,9 @@ export default function HomePage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Active Events */}
|
{/* Active Events */}
|
||||||
<Box sx={{ mb: 6 }}>
|
<Box sx={{ mb: 10 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 4 }}
|
sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 6 }}
|
||||||
>
|
>
|
||||||
<Typography variant="h5" sx={{ ...responsiveText.h3 }}>
|
<Typography variant="h5" sx={{ ...responsiveText.h3 }}>
|
||||||
진행 중인 이벤트
|
진행 중인 이벤트
|
||||||
@ -372,7 +372,7 @@ export default function HomePage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
{activeEvents.map((event) => (
|
{activeEvents.map((event) => (
|
||||||
<Card
|
<Card
|
||||||
key={event.id}
|
key={event.id}
|
||||||
@ -382,13 +382,13 @@ export default function HomePage() {
|
|||||||
}}
|
}}
|
||||||
onClick={() => handleEventClick(event.id)}
|
onClick={() => handleEventClick(event.id)}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ p: { xs: 3, sm: 4 } }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'start',
|
alignItems: 'start',
|
||||||
mb: 3,
|
mb: 6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900] }}>
|
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900] }}>
|
||||||
@ -411,11 +411,11 @@ export default function HomePage() {
|
|||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{
|
sx={{
|
||||||
mb: 3,
|
mb: 6,
|
||||||
color: colors.gray[600],
|
color: colors.gray[600],
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 1,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>📅</span>
|
<span>📅</span>
|
||||||
@ -423,7 +423,7 @@ export default function HomePage() {
|
|||||||
{event.startDate} ~ {event.endDate}
|
{event.startDate} ~ {event.endDate}
|
||||||
</span>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', gap: 6 }}>
|
<Box sx={{ display: 'flex', gap: 12 }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
@ -465,8 +465,8 @@ export default function HomePage() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Recent Activity */}
|
{/* Recent Activity */}
|
||||||
<Box sx={{ mb: 6 }}>
|
<Box sx={{ mb: 10 }}>
|
||||||
<Typography variant="h5" sx={{ ...responsiveText.h3, mb: 4 }}>
|
<Typography variant="h5" sx={{ ...responsiveText.h3, mb: 6 }}>
|
||||||
최근 활동
|
최근 활동
|
||||||
</Typography>
|
</Typography>
|
||||||
<Card
|
<Card
|
||||||
@ -475,15 +475,15 @@ export default function HomePage() {
|
|||||||
...cardStyles.default,
|
...cardStyles.default,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContent sx={{ p: { xs: 3, sm: 4 } }}>
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
||||||
{mockActivities.map((activity, index) => (
|
{mockActivities.map((activity, index) => (
|
||||||
<Box
|
<Box
|
||||||
key={index}
|
key={index}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: 3,
|
gap: 4,
|
||||||
pt: index > 0 ? 3.5 : 0,
|
pt: index > 0 ? 6 : 0,
|
||||||
mt: index > 0 ? 3.5 : 0,
|
mt: index > 0 ? 6 : 0,
|
||||||
borderTop: index > 0 ? 1 : 0,
|
borderTop: index > 0 ? 1 : 0,
|
||||||
borderColor: colors.gray[200],
|
borderColor: colors.gray[200],
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -24,13 +24,13 @@
|
|||||||
--color-error: #D32F2F;
|
--color-error: #D32F2F;
|
||||||
--color-info: #0288D1;
|
--color-info: #0288D1;
|
||||||
|
|
||||||
/* Spacing (4px Grid) */
|
/* Spacing (4px Grid) - Doubled values */
|
||||||
--spacing-xs: 4px;
|
--spacing-xs: 8px;
|
||||||
--spacing-s: 8px;
|
--spacing-s: 16px;
|
||||||
--spacing-m: 16px;
|
--spacing-m: 32px;
|
||||||
--spacing-l: 24px;
|
--spacing-l: 48px;
|
||||||
--spacing-xl: 32px;
|
--spacing-xl: 64px;
|
||||||
--spacing-2xl: 48px;
|
--spacing-2xl: 96px;
|
||||||
|
|
||||||
/* Border Radius */
|
/* Border Radius */
|
||||||
--border-radius-sm: 4px;
|
--border-radius-sm: 4px;
|
||||||
@ -73,8 +73,8 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
font-family: "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||||
"Roboto", "Helvetica Neue", system-ui, sans-serif;
|
"Roboto", "Helvetica Neue", system-ui, sans-serif;
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
line-height: 1.5;
|
line-height: 1.6;
|
||||||
color: var(--color-gray-900);
|
color: var(--color-gray-900);
|
||||||
background-color: var(--color-gray-50);
|
background-color: var(--color-gray-50);
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
@ -149,18 +149,18 @@ button {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 20px;
|
padding: 0 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.container {
|
.container {
|
||||||
padding: 0 40px;
|
padding: 0 80px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.container {
|
.container {
|
||||||
padding: 0 80px;
|
padding: 0 120px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user