mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 07:36:23 +00:00
추첨 페이지 디자인 개선 및 통일
- 페이지 헤더 및 요약 카드 추가 (이벤트명, 참여자 수) - 추첨 설정 카드 간격 2배 확대 및 버튼 크기 증가 - 추첨 이력 섹션 간격 확대 및 카드 디자인 개선 - 당첨자 목록 카드 디자인 개선 (순위 배지 크기 증가, 간격 확대) - 모든 액션 버튼에 그라데이션 배경 적용 - 모든 다이얼로그 컴포넌트 간격 및 디자인 통일 - 애니메이션 다이얼로그 크기 증가 - 미사용 import 제거 (EmojiEvents) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
de7726ffad
commit
28f0ebde33
@ -29,8 +29,27 @@ import {
|
||||
Add,
|
||||
Remove,
|
||||
Info,
|
||||
People,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
// 디자인 시스템 색상
|
||||
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 = {
|
||||
name: '신규고객 유치 이벤트',
|
||||
@ -163,55 +182,77 @@ export default function DrawPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ minHeight: '100vh', bgcolor: 'background.default', pb: 4 }}>
|
||||
<Container maxWidth="lg" sx={{ pt: 4, pb: 4, px: { xs: 3, sm: 3, md: 4 } }}>
|
||||
<Box sx={{ minHeight: '100vh', bgcolor: 'background.default', pb: 10 }}>
|
||||
<Container maxWidth="lg" sx={{ pt: 8, pb: 8, px: { xs: 6, sm: 8, md: 10 } }}>
|
||||
{/* Setup View (Before Drawing) */}
|
||||
{!showResults && (
|
||||
<>
|
||||
{/* Event Info */}
|
||||
<Card sx={{ mb: 3, borderRadius: 3 }}>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
|
||||
<EventNote color="error" />
|
||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||
이벤트 정보
|
||||
{/* Page Header */}
|
||||
<Box sx={{ mb: 8 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, fontSize: '2rem', mb: 2 }}>
|
||||
🎲 당첨자 추첨
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
참여자 중에서 공정하게 당첨자를 선정하세요
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
|
||||
{/* Event Info Summary Cards */}
|
||||
<Grid container spacing={6} sx={{ mb: 10 }}>
|
||||
<Grid item xs={6} md={6}>
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||
<EventNote sx={{ fontSize: 40, mb: 2, color: 'white' }} />
|
||||
<Typography variant="caption" display="block" sx={{ mb: 2, color: 'rgba(255,255,255,0.9)' }}>
|
||||
이벤트명
|
||||
</Typography>
|
||||
<Typography variant="body1">{mockEventData.name}</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
총 참여자
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, color: 'white' }}>
|
||||
{mockEventData.name}
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||
{mockEventData.totalParticipants}명
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
추첨 상태
|
||||
</Typography>
|
||||
<Typography variant="body1">추첨 전</Typography>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6} md={6}>
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
background: `linear-gradient(135deg, ${colors.blue} 0%, #93C5FD 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||
<People sx={{ fontSize: 40, mb: 2, color: 'white' }} />
|
||||
<Typography variant="caption" display="block" sx={{ mb: 2, color: 'rgba(255,255,255,0.9)' }}>
|
||||
총 참여자
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem' }}>
|
||||
{mockEventData.totalParticipants}명
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Drawing Settings */}
|
||||
<Card sx={{ mb: 3, borderRadius: 3 }}>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
|
||||
<Tune color="error" />
|
||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||
<Card elevation={0} sx={{ mb: 10, borderRadius: 4, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)' }}>
|
||||
<CardContent sx={{ p: 6 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 6 }}>
|
||||
<Tune sx={{ fontSize: 32, color: colors.pink }} />
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, fontSize: '1.5rem' }}>
|
||||
추첨 설정
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="body2" sx={{ mb: 2 }}>
|
||||
<Box sx={{ mb: 6 }}>
|
||||
<Typography variant="h6" sx={{ mb: 4, fontWeight: 600 }}>
|
||||
당첨 인원
|
||||
</Typography>
|
||||
<Box
|
||||
@ -219,33 +260,44 @@ export default function DrawPage() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 2,
|
||||
gap: 4,
|
||||
p: 4,
|
||||
bgcolor: colors.gray[100],
|
||||
borderRadius: 3,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={handleDecrease}
|
||||
sx={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
width: 60,
|
||||
height: 60,
|
||||
border: '2px solid',
|
||||
borderColor: colors.purple,
|
||||
color: colors.purple,
|
||||
'&:hover': {
|
||||
bgcolor: colors.purpleLight,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Remove />
|
||||
<Remove sx={{ fontSize: 28 }} />
|
||||
</IconButton>
|
||||
<Typography variant="h3" sx={{ fontWeight: 600, width: 80, textAlign: 'center' }}>
|
||||
<Typography variant="h2" sx={{ fontWeight: 700, width: 120, textAlign: 'center', color: colors.purple }}>
|
||||
{winnerCount}
|
||||
</Typography>
|
||||
<IconButton
|
||||
onClick={handleIncrease}
|
||||
sx={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
width: 60,
|
||||
height: 60,
|
||||
border: '2px solid',
|
||||
borderColor: colors.purple,
|
||||
color: colors.purple,
|
||||
'&:hover': {
|
||||
bgcolor: colors.purpleLight,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Add />
|
||||
<Add sx={{ fontSize: 28 }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
@ -255,21 +307,42 @@ export default function DrawPage() {
|
||||
<Checkbox
|
||||
checked={storeBonus}
|
||||
onChange={(e) => setStoreBonus(e.target.checked)}
|
||||
sx={{
|
||||
color: colors.purple,
|
||||
'&.Mui-checked': {
|
||||
color: colors.purple,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label="매장 방문 고객 가산점 (가중치: 1.5배)"
|
||||
sx={{ mb: 3 }}
|
||||
label={
|
||||
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
||||
매장 방문 고객 가산점 (가중치: 1.5배)
|
||||
</Typography>
|
||||
}
|
||||
sx={{ mb: 6 }}
|
||||
/>
|
||||
|
||||
<Box sx={{ bgcolor: 'background.default', p: 2, borderRadius: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 1 }}>
|
||||
<Info sx={{ fontSize: 16 }} color="action" />
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: colors.purpleLight,
|
||||
p: 4,
|
||||
borderRadius: 3,
|
||||
border: `1px solid ${colors.purple}40`,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, mb: 3 }}>
|
||||
<Info sx={{ fontSize: 24, color: colors.purple }} />
|
||||
<Typography variant="h6" sx={{ fontWeight: 600, color: colors.purple }}>
|
||||
추첨 방식
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="body2">• 난수 기반 무작위 추첨</Typography>
|
||||
<Typography variant="body2">• 모든 추첨 과정은 자동 기록됩니다</Typography>
|
||||
<Typography variant="body1" sx={{ mb: 2, color: colors.gray[700] }}>
|
||||
• 난수 기반 무작위 추첨
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: colors.gray[700] }}>
|
||||
• 모든 추첨 과정은 자동 기록됩니다
|
||||
</Typography>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -279,45 +352,75 @@ export default function DrawPage() {
|
||||
fullWidth
|
||||
variant="contained"
|
||||
size="large"
|
||||
startIcon={<Casino />}
|
||||
startIcon={<Casino sx={{ fontSize: 28 }} />}
|
||||
onClick={handleStartDrawing}
|
||||
sx={{ mb: 3, py: 1.5, borderRadius: 2, fontWeight: 700, fontSize: '1rem' }}
|
||||
sx={{
|
||||
mb: 10,
|
||||
py: 3,
|
||||
borderRadius: 4,
|
||||
fontWeight: 700,
|
||||
fontSize: '1.25rem',
|
||||
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.purple} 100%)`,
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.purple} 100%)`,
|
||||
boxShadow: '0 6px 16px rgba(0, 0, 0, 0.2)',
|
||||
transform: 'translateY(-2px)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
추첨 시작
|
||||
</Button>
|
||||
|
||||
{/* Drawing History */}
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 2 }}>
|
||||
📜 추첨 이력 (최근 3건)
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, mb: 6, fontSize: '1.5rem' }}>
|
||||
📜 추첨 이력
|
||||
</Typography>
|
||||
{mockDrawingHistory.length === 0 ? (
|
||||
<Card sx={{ borderRadius: 3 }}>
|
||||
<CardContent sx={{ textAlign: 'center', py: 4 }}>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
<Card elevation={0} sx={{ borderRadius: 4, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)' }}>
|
||||
<CardContent sx={{ textAlign: 'center', py: 8 }}>
|
||||
<ListIcon sx={{ fontSize: 64, color: colors.gray[300], mb: 2 }} />
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
추첨 이력이 없습니다
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
{mockDrawingHistory.slice(0, 3).map((history, index) => (
|
||||
<Card key={index} sx={{ borderRadius: 2 }}>
|
||||
<CardContent>
|
||||
<Card
|
||||
key={index}
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 5 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Box>
|
||||
<Typography variant="body1" sx={{ mb: 0.5 }}>
|
||||
<Typography variant="h6" sx={{ mb: 2, fontWeight: 700 }}>
|
||||
{history.date} {history.isRedraw && '(재추첨)'}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
당첨자 {history.winnerCount}명
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button
|
||||
variant="text"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
size="medium"
|
||||
onClick={() => handleHistoryDetail(history)}
|
||||
startIcon={<ListIcon />}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
borderColor: colors.purple,
|
||||
color: colors.purple,
|
||||
'&:hover': {
|
||||
borderColor: colors.purple,
|
||||
bgcolor: colors.purpleLight,
|
||||
},
|
||||
}}
|
||||
>
|
||||
상세보기
|
||||
</Button>
|
||||
@ -335,51 +438,59 @@ export default function DrawPage() {
|
||||
{showResults && (
|
||||
<>
|
||||
{/* Results Header */}
|
||||
<Box sx={{ textAlign: 'center', mb: 4 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, mb: 1 }}>
|
||||
<Box sx={{ textAlign: 'center', mb: 10 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, mb: 4, fontSize: '2rem' }}>
|
||||
🎉 추첨 완료!
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
<Typography variant="h6" sx={{ fontSize: '1.25rem' }}>
|
||||
총 {mockEventData.totalParticipants}명 중 {winnerCount}명 당첨
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Winner List */}
|
||||
<Box sx={{ mb: 4 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 2 }}>
|
||||
<Box sx={{ mb: 10 }}>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, mb: 6, fontSize: '1.5rem' }}>
|
||||
🏆 당첨자 목록
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
{winners.map((winner, index) => {
|
||||
const rank = index + 1;
|
||||
return (
|
||||
<Card key={winner.id} sx={{ borderRadius: 3 }}>
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Card
|
||||
key={winner.id}
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: '50%',
|
||||
background: getRankClass(rank),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'white',
|
||||
fontWeight: 600,
|
||||
fontSize: 18,
|
||||
fontWeight: 700,
|
||||
fontSize: 20,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{rank}위
|
||||
</Box>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
응모번호: #{winner.id}
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 0.5 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 2, fontSize: '1.25rem' }}>
|
||||
{winner.name} ({winner.phone})
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '1rem' }}>
|
||||
참여: {winner.channel}{' '}
|
||||
{winner.hasBonus && storeBonus && '🌟'}
|
||||
</Typography>
|
||||
@ -391,14 +502,18 @@ export default function DrawPage() {
|
||||
})}
|
||||
</Box>
|
||||
{storeBonus && (
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', textAlign: 'center', mt: 2 }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
sx={{ display: 'block', textAlign: 'center', mt: 6, fontSize: '0.875rem' }}
|
||||
>
|
||||
🌟 매장 방문 고객 가산점 적용
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<Grid container spacing={2} sx={{ mb: 2 }}>
|
||||
<Grid container spacing={4} sx={{ mb: 4 }}>
|
||||
<Grid item xs={6}>
|
||||
<Button
|
||||
fullWidth
|
||||
@ -406,7 +521,16 @@ export default function DrawPage() {
|
||||
size="large"
|
||||
startIcon={<Download />}
|
||||
onClick={handleDownload}
|
||||
sx={{ borderRadius: 2 }}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
py: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
borderWidth: 2,
|
||||
'&:hover': {
|
||||
borderWidth: 2,
|
||||
},
|
||||
}}
|
||||
>
|
||||
엑셀다운로드
|
||||
</Button>
|
||||
@ -418,7 +542,16 @@ export default function DrawPage() {
|
||||
size="large"
|
||||
startIcon={<Refresh />}
|
||||
onClick={() => setRedrawDialogOpen(true)}
|
||||
sx={{ borderRadius: 2 }}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
py: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
borderWidth: 2,
|
||||
'&:hover': {
|
||||
borderWidth: 2,
|
||||
},
|
||||
}}
|
||||
>
|
||||
재추첨
|
||||
</Button>
|
||||
@ -431,7 +564,18 @@ export default function DrawPage() {
|
||||
size="large"
|
||||
startIcon={<Notifications />}
|
||||
onClick={() => setNotifyDialogOpen(true)}
|
||||
sx={{ mb: 2, py: 1.5, borderRadius: 2, fontWeight: 700, fontSize: '1rem' }}
|
||||
sx={{
|
||||
mb: 4,
|
||||
py: 3,
|
||||
borderRadius: 3,
|
||||
fontWeight: 700,
|
||||
fontSize: '1rem',
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}}
|
||||
>
|
||||
당첨자에게 알림 전송
|
||||
</Button>
|
||||
@ -441,7 +585,12 @@ export default function DrawPage() {
|
||||
variant="text"
|
||||
size="large"
|
||||
onClick={handleBackToEvents}
|
||||
sx={{ borderRadius: 2 }}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
py: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
이벤트 목록으로
|
||||
</Button>
|
||||
@ -457,14 +606,15 @@ export default function DrawPage() {
|
||||
sx: {
|
||||
bgcolor: 'rgba(0, 0, 0, 0.9)',
|
||||
color: 'white',
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogContent sx={{ textAlign: 'center', py: 8 }}>
|
||||
<DialogContent sx={{ textAlign: 'center', py: 16 }}>
|
||||
<Casino
|
||||
sx={{
|
||||
fontSize: 80,
|
||||
mb: 3,
|
||||
fontSize: 100,
|
||||
mb: 6,
|
||||
animation: 'spin 0.5s infinite',
|
||||
'@keyframes spin': {
|
||||
'0%, 100%': { transform: 'rotate(0deg)' },
|
||||
@ -472,95 +622,228 @@ export default function DrawPage() {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, mb: 1 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, mb: 2, fontSize: '2rem' }}>
|
||||
{animationText}
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: 'rgba(255,255,255,0.7)' }}>
|
||||
<Typography variant="body1" sx={{ color: 'rgba(255,255,255,0.7)', fontSize: '1.125rem' }}>
|
||||
{animationSubtext}
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Confirm Dialog */}
|
||||
<Dialog open={confirmDialogOpen} onClose={() => setConfirmDialogOpen(false)} maxWidth="xs" fullWidth>
|
||||
<DialogTitle>추첨 확인</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body1" sx={{ textAlign: 'center' }}>
|
||||
<Dialog
|
||||
open={confirmDialogOpen}
|
||||
onClose={() => setConfirmDialogOpen(false)}
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
PaperProps={{ sx: { borderRadius: 4 } }}
|
||||
>
|
||||
<DialogTitle sx={{ pt: 6, px: 6, pb: 4, fontSize: '1.5rem', fontWeight: 700 }}>
|
||||
추첨 확인
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ px: 6, pb: 4 }}>
|
||||
<Typography variant="body1" sx={{ textAlign: 'center', fontSize: '1.125rem' }}>
|
||||
총 {mockEventData.totalParticipants}명 중 {winnerCount}명을 추첨하시겠습니까?
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setConfirmDialogOpen(false)}>취소</Button>
|
||||
<Button onClick={executeDrawing} variant="contained">
|
||||
<DialogActions sx={{ px: 6, pb: 6, pt: 4, gap: 2 }}>
|
||||
<Button
|
||||
onClick={() => setConfirmDialogOpen(false)}
|
||||
sx={{
|
||||
flex: 1,
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
<Button
|
||||
onClick={executeDrawing}
|
||||
variant="contained"
|
||||
sx={{
|
||||
flex: 1,
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}}
|
||||
>
|
||||
확인
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* Redraw Dialog */}
|
||||
<Dialog open={redrawDialogOpen} onClose={() => setRedrawDialogOpen(false)} maxWidth="xs" fullWidth>
|
||||
<DialogTitle>재추첨 확인</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body1" sx={{ mb: 2, textAlign: 'center' }}>
|
||||
<Dialog
|
||||
open={redrawDialogOpen}
|
||||
onClose={() => setRedrawDialogOpen(false)}
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
PaperProps={{ sx: { borderRadius: 4 } }}
|
||||
>
|
||||
<DialogTitle sx={{ pt: 6, px: 6, pb: 4, fontSize: '1.5rem', fontWeight: 700 }}>
|
||||
재추첨 확인
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ px: 6, pb: 4 }}>
|
||||
<Typography variant="body1" sx={{ mb: 4, textAlign: 'center', fontSize: '1.125rem' }}>
|
||||
재추첨 시 현재 당첨자 정보가 변경됩니다.
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ mb: 2, textAlign: 'center' }}>
|
||||
<Typography variant="body1" sx={{ mb: 4, textAlign: 'center', fontSize: '1.125rem' }}>
|
||||
계속하시겠습니까?
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', textAlign: 'center' }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
sx={{ display: 'block', textAlign: 'center', fontSize: '0.875rem' }}
|
||||
>
|
||||
이전 추첨 이력은 보관됩니다
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setRedrawDialogOpen(false)}>취소</Button>
|
||||
<Button onClick={handleRedraw} variant="contained">
|
||||
<DialogActions sx={{ px: 6, pb: 6, pt: 4, gap: 2 }}>
|
||||
<Button
|
||||
onClick={() => setRedrawDialogOpen(false)}
|
||||
sx={{
|
||||
flex: 1,
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleRedraw}
|
||||
variant="contained"
|
||||
sx={{
|
||||
flex: 1,
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}}
|
||||
>
|
||||
재추첨
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* Notify Dialog */}
|
||||
<Dialog open={notifyDialogOpen} onClose={() => setNotifyDialogOpen(false)} maxWidth="xs" fullWidth>
|
||||
<DialogTitle>알림 전송</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body1" sx={{ mb: 2, textAlign: 'center' }}>
|
||||
<Dialog
|
||||
open={notifyDialogOpen}
|
||||
onClose={() => setNotifyDialogOpen(false)}
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
PaperProps={{ sx: { borderRadius: 4 } }}
|
||||
>
|
||||
<DialogTitle sx={{ pt: 6, px: 6, pb: 4, fontSize: '1.5rem', fontWeight: 700 }}>
|
||||
알림 전송
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ px: 6, pb: 4 }}>
|
||||
<Typography variant="body1" sx={{ mb: 4, textAlign: 'center', fontSize: '1.125rem' }}>
|
||||
{winnerCount}명의 당첨자에게 SMS 알림을 전송하시겠습니까?
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', textAlign: 'center' }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
sx={{ display: 'block', textAlign: 'center', fontSize: '0.875rem' }}
|
||||
>
|
||||
예상 비용: {winnerCount * 100}원 (100원/건)
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setNotifyDialogOpen(false)}>취소</Button>
|
||||
<Button onClick={handleNotify} variant="contained">
|
||||
<DialogActions sx={{ px: 6, pb: 6, pt: 4, gap: 2 }}>
|
||||
<Button
|
||||
onClick={() => setNotifyDialogOpen(false)}
|
||||
sx={{
|
||||
flex: 1,
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleNotify}
|
||||
variant="contained"
|
||||
sx={{
|
||||
flex: 1,
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}}
|
||||
>
|
||||
전송
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* History Detail Dialog */}
|
||||
<Dialog open={historyDetailOpen} onClose={() => setHistoryDetailOpen(false)} maxWidth="xs" fullWidth>
|
||||
<DialogTitle>추첨 이력 상세</DialogTitle>
|
||||
<DialogContent>
|
||||
<Dialog
|
||||
open={historyDetailOpen}
|
||||
onClose={() => setHistoryDetailOpen(false)}
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
PaperProps={{ sx: { borderRadius: 4 } }}
|
||||
>
|
||||
<DialogTitle sx={{ pt: 6, px: 6, pb: 4, fontSize: '1.5rem', fontWeight: 700 }}>
|
||||
추첨 이력 상세
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ px: 6, pb: 4 }}>
|
||||
{selectedHistory && (
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography variant="body1" sx={{ mb: 1 }}>
|
||||
<Box sx={{ p: 4 }}>
|
||||
<Typography variant="body1" sx={{ mb: 3, fontSize: '1.125rem' }}>
|
||||
추첨 일시: {selectedHistory.date}
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ mb: 1 }}>
|
||||
<Typography variant="body1" sx={{ mb: 3, fontSize: '1.125rem' }}>
|
||||
당첨 인원: {selectedHistory.winnerCount}명
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ mb: 2 }}>
|
||||
<Typography variant="body1" sx={{ mb: 4, fontSize: '1.125rem' }}>
|
||||
재추첨 여부: {selectedHistory.isRedraw ? '예' : '아니오'}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.875rem' }}>
|
||||
※ 당첨자 정보는 개인정보 보호를 위해 마스킹 처리됩니다
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setHistoryDetailOpen(false)} variant="contained">
|
||||
<DialogActions sx={{ px: 6, pb: 6, pt: 4 }}>
|
||||
<Button
|
||||
onClick={() => setHistoryDetailOpen(false)}
|
||||
variant="contained"
|
||||
fullWidth
|
||||
sx={{
|
||||
py: 2,
|
||||
borderRadius: 3,
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}}
|
||||
>
|
||||
확인
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
@ -21,14 +21,37 @@ import {
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Grid,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Search,
|
||||
FilterList,
|
||||
Casino,
|
||||
Download,
|
||||
People,
|
||||
TrendingUp,
|
||||
Person,
|
||||
AccessTime,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
// 디자인 시스템 색상
|
||||
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 mockParticipants = [
|
||||
{
|
||||
@ -150,11 +173,117 @@ export default function ParticipantsPage() {
|
||||
alert('엑셀 다운로드 기능은 추후 구현됩니다');
|
||||
};
|
||||
|
||||
// 통계 계산
|
||||
const stats = {
|
||||
total: mockParticipants.length,
|
||||
waiting: mockParticipants.filter((p) => p.status === 'waiting').length,
|
||||
winner: mockParticipants.filter((p) => p.status === 'winner').length,
|
||||
channelDistribution: {
|
||||
uriTV: mockParticipants.filter((p) => p.channelType === 'uriTV').length,
|
||||
ringoBiz: mockParticipants.filter((p) => p.channelType === 'ringoBiz').length,
|
||||
sns: mockParticipants.filter((p) => p.channelType === 'sns').length,
|
||||
},
|
||||
};
|
||||
|
||||
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 } }}>
|
||||
<Container maxWidth="lg" sx={{ pt: 8, pb: 8, px: { xs: 6, sm: 8, md: 10 } }}>
|
||||
{/* Page Header */}
|
||||
<Box sx={{ mb: 8 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, fontSize: '2rem', mb: 2 }}>
|
||||
👥 참여자 목록
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
이벤트에 참여한 사용자들의 정보를 확인하고 관리하세요
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Statistics Cards */}
|
||||
<Grid container spacing={6} sx={{ mb: 10 }}>
|
||||
<Grid item xs={6} md={3}>
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||
<People sx={{ fontSize: 40, mb: 2, color: 'white' }} />
|
||||
<Typography variant="caption" display="block" sx={{ mb: 2, color: 'rgba(255,255,255,0.9)' }}>
|
||||
전체 참여자
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem' }}>
|
||||
{stats.total}명
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6} md={3}>
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
background: `linear-gradient(135deg, ${colors.yellow} 0%, #FCD34D 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||
<AccessTime sx={{ fontSize: 40, mb: 2, color: 'white' }} />
|
||||
<Typography variant="caption" display="block" sx={{ mb: 2, color: 'rgba(255,255,255,0.9)' }}>
|
||||
대기중
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem' }}>
|
||||
{stats.waiting}명
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6} md={3}>
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
background: `linear-gradient(135deg, ${colors.mint} 0%, #6EE7B7 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||
<TrendingUp sx={{ fontSize: 40, mb: 2, color: 'white' }} />
|
||||
<Typography variant="caption" display="block" sx={{ mb: 2, color: 'rgba(255,255,255,0.9)' }}>
|
||||
당첨자
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem' }}>
|
||||
{stats.winner}명
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={6} md={3}>
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
background: `linear-gradient(135deg, ${colors.blue} 0%, #93C5FD 100%)`,
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
||||
<Casino sx={{ fontSize: 40, mb: 2, color: 'white' }} />
|
||||
<Typography variant="caption" display="block" sx={{ mb: 2, color: 'rgba(255,255,255,0.9)' }}>
|
||||
당첨률
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, color: 'white', fontSize: '1.75rem' }}>
|
||||
{stats.total > 0 ? Math.round((stats.winner / stats.total) * 100) : 0}%
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Search Section */}
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Box sx={{ mb: 6 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
placeholder="이름 또는 전화번호 검색..."
|
||||
@ -169,22 +298,24 @@ export default function ParticipantsPage() {
|
||||
}}
|
||||
sx={{
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: 2,
|
||||
borderRadius: 3,
|
||||
bgcolor: 'white',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Filters */}
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'wrap' }}>
|
||||
<FilterList color="error" />
|
||||
<FormControl sx={{ flex: 1, minWidth: 140 }}>
|
||||
<Box sx={{ mb: 8 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, flexWrap: 'wrap' }}>
|
||||
<FilterList sx={{ fontSize: 28, color: colors.pink }} />
|
||||
<FormControl sx={{ flex: 1, minWidth: 160 }}>
|
||||
<InputLabel>참여 경로</InputLabel>
|
||||
<Select
|
||||
value={channelFilter}
|
||||
label="참여 경로"
|
||||
onChange={(e) => setChannelFilter(e.target.value as ChannelType)}
|
||||
sx={{ borderRadius: 2 }}
|
||||
>
|
||||
<MenuItem value="all">전체 경로</MenuItem>
|
||||
<MenuItem value="uriTV">우리동네TV</MenuItem>
|
||||
@ -192,12 +323,13 @@ export default function ParticipantsPage() {
|
||||
<MenuItem value="sns">SNS</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl sx={{ flex: 1, minWidth: 120 }}>
|
||||
<FormControl sx={{ flex: 1, minWidth: 140 }}>
|
||||
<InputLabel>상태</InputLabel>
|
||||
<Select
|
||||
value={statusFilter}
|
||||
label="상태"
|
||||
onChange={(e) => setStatusFilter(e.target.value as StatusType)}
|
||||
sx={{ borderRadius: 2 }}
|
||||
>
|
||||
<MenuItem value="all">전체</MenuItem>
|
||||
<MenuItem value="waiting">당첨 대기</MenuItem>
|
||||
@ -209,77 +341,122 @@ export default function ParticipantsPage() {
|
||||
</Box>
|
||||
|
||||
{/* Total Count & Drawing Button */}
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 2 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||
총 <span style={{ color: '#e91e63' }}>{filteredParticipants.length}</span>명 참여
|
||||
<Box sx={{ mb: 6 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 4 }}>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, fontSize: '1.5rem' }}>
|
||||
총 <span style={{ color: colors.pink }}>{filteredParticipants.length}</span>명 참여
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 3 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Download />}
|
||||
onClick={handleDownloadClick}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
px: 4,
|
||||
py: 1.5,
|
||||
borderColor: colors.blue,
|
||||
color: colors.blue,
|
||||
'&:hover': {
|
||||
borderColor: colors.blue,
|
||||
bgcolor: `${colors.blue}10`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
엑셀 다운로드
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<Casino />}
|
||||
onClick={handleDrawClick}
|
||||
sx={{ borderRadius: 2 }}
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
px: 4,
|
||||
py: 1.5,
|
||||
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.purple} 100%)`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.purple} 100%)`,
|
||||
opacity: 0.9,
|
||||
},
|
||||
}}
|
||||
>
|
||||
당첨자 추첨
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Participant List */}
|
||||
<Box sx={{ mb: 5 }}>
|
||||
<Box sx={{ mb: 10 }}>
|
||||
{pageParticipants.length === 0 ? (
|
||||
<Box sx={{ textAlign: 'center', py: 8 }}>
|
||||
<span className="material-icons" style={{ fontSize: 64, color: '#bdbdbd' }}>
|
||||
people_outline
|
||||
</span>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mt: 2 }}>
|
||||
<Box sx={{ textAlign: 'center', py: 16 }}>
|
||||
<Person sx={{ fontSize: 80, color: colors.gray[300], mb: 3 }} />
|
||||
<Typography variant="h6" color="text.secondary" sx={{ fontWeight: 600 }}>
|
||||
검색 결과가 없습니다
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
|
||||
다른 검색어나 필터를 사용해보세요
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
{pageParticipants.map((participant) => (
|
||||
<Card
|
||||
key={participant.id}
|
||||
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 ease',
|
||||
'&:hover': {
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
||||
transform: 'translateY(-2px)',
|
||||
transform: 'translateY(-4px)',
|
||||
},
|
||||
}}
|
||||
onClick={() => handleParticipantClick(participant)}
|
||||
>
|
||||
<CardContent sx={{ p: 3 }}>
|
||||
<CardContent sx={{ p: 5 }}>
|
||||
{/* Header */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-between',
|
||||
mb: 2,
|
||||
mb: 4,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 0.5 }}>
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center', gap: 3 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: '50%',
|
||||
bgcolor: colors.purpleLight,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Person sx={{ fontSize: 32, color: colors.purple }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
#{participant.id}
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 0.5 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 1, fontSize: '1.25rem' }}>
|
||||
{participant.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
{participant.phone}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Chip
|
||||
label={getStatusText(participant.status)}
|
||||
color={getStatusColor(participant.status) as any}
|
||||
size="small"
|
||||
sx={{ fontWeight: 600 }}
|
||||
size="medium"
|
||||
sx={{ fontWeight: 600, px: 2, py: 2.5 }}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -287,26 +464,34 @@ export default function ParticipantsPage() {
|
||||
<Box
|
||||
sx={{
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider',
|
||||
pt: 2,
|
||||
borderColor: colors.gray[100],
|
||||
pt: 4,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 0.5,
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
참여 경로
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||
{participant.channel}
|
||||
</Typography>
|
||||
<Chip
|
||||
label={participant.channel}
|
||||
size="small"
|
||||
sx={{
|
||||
bgcolor: colors.purpleLight,
|
||||
color: colors.purple,
|
||||
fontWeight: 600,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
참여 일시
|
||||
</Typography>
|
||||
<Typography variant="body2">{participant.date}</Typography>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
||||
{participant.date}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</CardContent>
|
||||
@ -318,87 +503,145 @@ export default function ParticipantsPage() {
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 5 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 10 }}>
|
||||
<Pagination
|
||||
count={totalPages}
|
||||
page={currentPage}
|
||||
onChange={(_, page) => setCurrentPage(page)}
|
||||
color="primary"
|
||||
size="large"
|
||||
sx={{
|
||||
'& .MuiPaginationItem-root': {
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Excel Download Button (Desktop only) */}
|
||||
<Box sx={{ display: { xs: 'none', md: 'block' } }}>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
size="large"
|
||||
startIcon={<Download />}
|
||||
onClick={handleDownloadClick}
|
||||
sx={{ borderRadius: 2 }}
|
||||
>
|
||||
엑셀 다운로드
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Participant Detail Dialog */}
|
||||
<Dialog
|
||||
open={detailDialogOpen}
|
||||
onClose={() => setDetailDialogOpen(false)}
|
||||
maxWidth="sm"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>참여자 상세 정보</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
{selectedParticipant && (
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
응모번호
|
||||
<DialogTitle sx={{ p: 5, pb: 3 }}>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700 }}>
|
||||
참여자 상세 정보
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
||||
</DialogTitle>
|
||||
<DialogContent dividers sx={{ p: 5 }}>
|
||||
{selectedParticipant && (
|
||||
<Box>
|
||||
<Box sx={{ mb: 4, textAlign: 'center' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: '50%',
|
||||
bgcolor: colors.purpleLight,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
margin: '0 auto',
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Person sx={{ fontSize: 40, color: colors.purple }} />
|
||||
</Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 1 }}>
|
||||
{selectedParticipant.name}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
#{selectedParticipant.id}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
이름
|
||||
</Typography>
|
||||
<Typography variant="body1">{selectedParticipant.name}</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 3,
|
||||
bgcolor: colors.gray[100],
|
||||
borderRadius: 3,
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
전화번호
|
||||
</Typography>
|
||||
<Typography variant="body1">{selectedParticipant.phone}</Typography>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
||||
{selectedParticipant.phone}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
p: 3,
|
||||
bgcolor: colors.gray[100],
|
||||
borderRadius: 3,
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
참여 경로
|
||||
</Typography>
|
||||
<Typography variant="body1">{selectedParticipant.channel}</Typography>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
||||
{selectedParticipant.channel}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
p: 3,
|
||||
bgcolor: colors.gray[100],
|
||||
borderRadius: 3,
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
참여 일시
|
||||
</Typography>
|
||||
<Typography variant="body1">{selectedParticipant.date}</Typography>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
||||
{selectedParticipant.date}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
p: 3,
|
||||
bgcolor: colors.gray[100],
|
||||
borderRadius: 3,
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
당첨 여부
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
{getStatusText(selectedParticipant.status)}
|
||||
</Typography>
|
||||
<Chip
|
||||
label={getStatusText(selectedParticipant.status)}
|
||||
color={getStatusColor(selectedParticipant.status) as any}
|
||||
size="medium"
|
||||
sx={{ fontWeight: 600 }}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setDetailDialogOpen(false)} variant="contained">
|
||||
<DialogActions sx={{ p: 5, pt: 3 }}>
|
||||
<Button
|
||||
onClick={() => setDetailDialogOpen(false)}
|
||||
variant="contained"
|
||||
fullWidth
|
||||
sx={{
|
||||
borderRadius: 3,
|
||||
py: 1.5,
|
||||
background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.purple} 100%)`,
|
||||
}}
|
||||
>
|
||||
확인
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user