mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 06:16:24 +00:00
- 배경색이 있는 모든 카드의 흰색 글자를 검은색으로 변경하여 가독성 향상 - 아이콘과 텍스트에 그림자 효과를 추가하여 입체감 부여 - Profile 페이지 디자인 통일성 완료 변경 파일: - src/app/(main)/page.tsx: 대시보드 KPI 카드 (3개) - src/app/(main)/events/page.tsx: 이벤트 통계 카드 (4개) - src/app/(main)/events/create/steps/ApprovalStep.tsx: 승인 단계 요약 카드 (4개) - src/app/(main)/profile/page.tsx: 프로필 페이지 전체 리디자인 적용된 효과: - 아이콘: drop-shadow(0px 2px 4px rgba(0,0,0,0.2)) - 큰 텍스트: text-shadow 0px 2px 4px rgba(0,0,0,0.15) - 작은 텍스트: text-shadow 0px 1px 2px rgba(0,0,0,0.1) - 아이콘 배경: rgba(0,0,0,0.05) (대시보드) - 글자 색상: colors.gray[900] (제목), colors.gray[700] (라벨)
557 lines
19 KiB
TypeScript
557 lines
19 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
Box,
|
|
Container,
|
|
Typography,
|
|
Card,
|
|
CardContent,
|
|
Button,
|
|
Checkbox,
|
|
FormControlLabel,
|
|
Chip,
|
|
Grid,
|
|
IconButton,
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
Link,
|
|
} from '@mui/material';
|
|
import { ArrowBack, CheckCircle, Edit, RocketLaunch, Save, People, AttachMoney, TrendingUp } from '@mui/icons-material';
|
|
import { EventData } from '../page';
|
|
import { cardStyles, colors, responsiveText } from '@/shared/lib/button-styles';
|
|
|
|
interface ApprovalStepProps {
|
|
eventData: EventData;
|
|
onApprove: () => void;
|
|
onBack: () => void;
|
|
}
|
|
|
|
export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalStepProps) {
|
|
const [agreeTerms, setAgreeTerms] = useState(false);
|
|
const [termsDialogOpen, setTermsDialogOpen] = useState(false);
|
|
const [successDialogOpen, setSuccessDialogOpen] = useState(false);
|
|
const [isDeploying, setIsDeploying] = useState(false);
|
|
|
|
const handleApprove = () => {
|
|
if (!agreeTerms) return;
|
|
|
|
setIsDeploying(true);
|
|
|
|
// 배포 시뮬레이션
|
|
setTimeout(() => {
|
|
setIsDeploying(false);
|
|
setSuccessDialogOpen(true);
|
|
}, 2000);
|
|
};
|
|
|
|
const handleSaveDraft = () => {
|
|
// TODO: 임시저장 API 연동
|
|
alert('임시저장되었습니다');
|
|
};
|
|
|
|
const getChannelNames = (channels?: string[]) => {
|
|
const channelMap: Record<string, string> = {
|
|
uriTV: '우리동네TV',
|
|
ringoBiz: '링고비즈',
|
|
genieTV: '지니TV',
|
|
sns: 'SNS',
|
|
};
|
|
|
|
return channels?.map((ch) => channelMap[ch] || ch) || [];
|
|
};
|
|
|
|
return (
|
|
<Box sx={{ minHeight: '100vh', bgcolor: colors.gray[50], pt: { xs: 7, sm: 8 }, pb: 10 }}>
|
|
<Container maxWidth="lg" sx={{ pt: 8, pb: 6, px: { xs: 6, sm: 8, md: 10 } }}>
|
|
{/* Header */}
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 8 }}>
|
|
<IconButton onClick={onBack}>
|
|
<ArrowBack />
|
|
</IconButton>
|
|
<Typography variant="h5" sx={{ ...responsiveText.h3, fontWeight: 700 }}>
|
|
최종 승인
|
|
</Typography>
|
|
</Box>
|
|
|
|
{/* Title Section */}
|
|
<Box sx={{ textAlign: 'center', mb: 10 }}>
|
|
<CheckCircle sx={{ fontSize: 64, color: colors.purple, mb: 2 }} />
|
|
<Typography variant="h5" sx={{ ...responsiveText.h3, fontWeight: 700, mb: 2 }}>
|
|
이벤트를 확인해주세요
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary" sx={{ ...responsiveText.body1 }}>
|
|
모든 정보를 검토한 후 배포하세요
|
|
</Typography>
|
|
</Box>
|
|
|
|
{/* Event Summary Statistics */}
|
|
<Grid container spacing={4} sx={{ mb: 10 }}>
|
|
<Grid item xs={12} sm={6} md={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 }}>
|
|
<CheckCircle sx={{
|
|
fontSize: 32,
|
|
color: colors.gray[900],
|
|
mb: 1,
|
|
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
|
}} />
|
|
<Typography variant="body2" sx={{
|
|
color: colors.gray[700],
|
|
fontSize: '0.875rem',
|
|
mb: 1,
|
|
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
|
}}>
|
|
이벤트 제목
|
|
</Typography>
|
|
<Typography
|
|
variant="h6"
|
|
sx={{
|
|
fontWeight: 700,
|
|
color: colors.gray[900],
|
|
fontSize: '1rem',
|
|
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
|
}}
|
|
>
|
|
{eventData.recommendation?.title || '이벤트 제목'}
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6} md={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: colors.gray[900],
|
|
mb: 1,
|
|
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
|
}} />
|
|
<Typography variant="body2" sx={{
|
|
color: colors.gray[700],
|
|
fontSize: '0.875rem',
|
|
mb: 1,
|
|
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
|
}}>
|
|
목표 참여자
|
|
</Typography>
|
|
<Typography
|
|
variant="h4"
|
|
sx={{
|
|
fontWeight: 700,
|
|
color: colors.gray[900],
|
|
fontSize: '1.75rem',
|
|
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
|
}}
|
|
>
|
|
{eventData.recommendation?.expectedParticipants || 0}
|
|
<Typography component="span" sx={{
|
|
fontSize: '1rem',
|
|
ml: 0.5,
|
|
color: colors.gray[900],
|
|
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
|
}}>
|
|
명
|
|
</Typography>
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6} md={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 }}>
|
|
<AttachMoney sx={{
|
|
fontSize: 32,
|
|
color: colors.gray[900],
|
|
mb: 1,
|
|
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
|
}} />
|
|
<Typography variant="body2" sx={{
|
|
color: colors.gray[700],
|
|
fontSize: '0.875rem',
|
|
mb: 1,
|
|
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
|
}}>
|
|
예상 비용
|
|
</Typography>
|
|
<Typography
|
|
variant="h4"
|
|
sx={{
|
|
fontWeight: 700,
|
|
color: colors.gray[900],
|
|
fontSize: '1.75rem',
|
|
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
|
}}
|
|
>
|
|
{((eventData.recommendation?.estimatedCost || 0) / 10000).toFixed(0)}
|
|
<Typography component="span" sx={{
|
|
fontSize: '1rem',
|
|
ml: 0.5,
|
|
color: colors.gray[900],
|
|
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
|
}}>
|
|
만원
|
|
</Typography>
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6} md={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 }}>
|
|
<TrendingUp sx={{
|
|
fontSize: 32,
|
|
color: colors.gray[900],
|
|
mb: 1,
|
|
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
|
}} />
|
|
<Typography variant="body2" sx={{
|
|
color: colors.gray[700],
|
|
fontSize: '0.875rem',
|
|
mb: 1,
|
|
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
|
}}>
|
|
예상 ROI
|
|
</Typography>
|
|
<Typography
|
|
variant="h4"
|
|
sx={{
|
|
fontWeight: 700,
|
|
color: colors.gray[900],
|
|
fontSize: '1.75rem',
|
|
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
|
}}
|
|
>
|
|
{eventData.recommendation?.roi || 0}%
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
{/* Event Details */}
|
|
<Typography variant="h6" sx={{ ...responsiveText.h4, fontWeight: 700, mb: 6 }}>
|
|
이벤트 상세
|
|
</Typography>
|
|
|
|
<Card elevation={0} sx={{ ...cardStyles.default, mb: 4 }}>
|
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
|
|
<Box sx={{ flex: 1 }}>
|
|
<Typography variant="caption" color="text.secondary" sx={{ ...responsiveText.body2, fontSize: '0.875rem' }}>
|
|
이벤트 제목
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ ...responsiveText.body1, fontWeight: 600, mt: 1 }}>
|
|
{eventData.recommendation?.title}
|
|
</Typography>
|
|
</Box>
|
|
<IconButton size="small">
|
|
<Edit fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card elevation={0} sx={{ ...cardStyles.default, mb: 4 }}>
|
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
|
|
<Box sx={{ flex: 1 }}>
|
|
<Typography variant="caption" color="text.secondary" sx={{ ...responsiveText.body2, fontSize: '0.875rem' }}>
|
|
경품
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ ...responsiveText.body1, fontWeight: 600, mt: 1 }}>
|
|
{eventData.recommendation?.prize}
|
|
</Typography>
|
|
</Box>
|
|
<IconButton size="small">
|
|
<Edit fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card elevation={0} sx={{ ...cardStyles.default, mb: 10 }}>
|
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
|
|
<Box sx={{ flex: 1 }}>
|
|
<Typography variant="caption" color="text.secondary" sx={{ ...responsiveText.body2, fontSize: '0.875rem' }}>
|
|
참여 방법
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ ...responsiveText.body1, fontWeight: 600, mt: 1 }}>
|
|
{eventData.recommendation?.participationMethod}
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Distribution Channels */}
|
|
<Typography variant="h6" sx={{ ...responsiveText.h4, fontWeight: 700, mb: 6 }}>
|
|
배포 채널
|
|
</Typography>
|
|
|
|
<Card elevation={0} sx={{ ...cardStyles.default, mb: 10 }}>
|
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2, mb: 4 }}>
|
|
{getChannelNames(eventData.channels).map((channel) => (
|
|
<Chip
|
|
key={channel}
|
|
label={channel}
|
|
sx={{
|
|
bgcolor: colors.purple,
|
|
color: 'white',
|
|
fontWeight: 600,
|
|
fontSize: '0.875rem',
|
|
px: 2,
|
|
py: 2.5,
|
|
}}
|
|
/>
|
|
))}
|
|
</Box>
|
|
<Button
|
|
size="small"
|
|
startIcon={<Edit />}
|
|
sx={{
|
|
...responsiveText.body2,
|
|
fontWeight: 600,
|
|
color: colors.purple,
|
|
}}
|
|
>
|
|
채널 수정하기
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Terms Agreement */}
|
|
<Card elevation={0} sx={{ ...cardStyles.default, bgcolor: colors.gray[50], mb: 10 }}>
|
|
<CardContent sx={{ p: { xs: 6, sm: 8 } }}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={agreeTerms}
|
|
onChange={(e) => setAgreeTerms(e.target.checked)}
|
|
sx={{
|
|
color: colors.purple,
|
|
'&.Mui-checked': {
|
|
color: colors.purple,
|
|
},
|
|
}}
|
|
/>
|
|
}
|
|
label={
|
|
<Typography variant="body2" sx={{ ...responsiveText.body1 }}>
|
|
이벤트 약관 및 개인정보 처리방침에 동의합니다{' '}
|
|
<Typography component="span" sx={{ color: colors.orange, fontWeight: 600 }}>
|
|
(필수)
|
|
</Typography>
|
|
</Typography>
|
|
}
|
|
/>
|
|
<Link
|
|
component="button"
|
|
variant="body2"
|
|
onClick={() => setTermsDialogOpen(true)}
|
|
sx={{ ...responsiveText.body2, ml: 4, mt: 2, fontWeight: 600, color: colors.purple }}
|
|
>
|
|
약관 보기
|
|
</Link>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Action Buttons */}
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
<Button
|
|
fullWidth
|
|
variant="contained"
|
|
size="large"
|
|
disabled={!agreeTerms || isDeploying}
|
|
onClick={handleApprove}
|
|
startIcon={isDeploying ? null : <RocketLaunch />}
|
|
sx={{
|
|
py: 3,
|
|
borderRadius: 3,
|
|
fontSize: '1rem',
|
|
fontWeight: 700,
|
|
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
|
'&:hover': {
|
|
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.pink} 100%)`,
|
|
opacity: 0.9,
|
|
},
|
|
'&:disabled': {
|
|
background: colors.gray[300],
|
|
},
|
|
}}
|
|
>
|
|
{isDeploying ? '배포 중...' : '배포하기'}
|
|
</Button>
|
|
<Button
|
|
fullWidth
|
|
variant="outlined"
|
|
size="large"
|
|
onClick={handleSaveDraft}
|
|
startIcon={<Save />}
|
|
sx={{
|
|
py: 3,
|
|
borderRadius: 3,
|
|
fontSize: '1rem',
|
|
fontWeight: 600,
|
|
borderWidth: 2,
|
|
borderColor: colors.gray[300],
|
|
color: colors.gray[700],
|
|
'&:hover': {
|
|
borderWidth: 2,
|
|
borderColor: colors.gray[400],
|
|
bgcolor: colors.gray[50],
|
|
},
|
|
}}
|
|
>
|
|
임시저장
|
|
</Button>
|
|
</Box>
|
|
</Container>
|
|
|
|
{/* Terms Dialog */}
|
|
<Dialog
|
|
open={termsDialogOpen}
|
|
onClose={() => setTermsDialogOpen(false)}
|
|
maxWidth="sm"
|
|
fullWidth
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: 4,
|
|
},
|
|
}}
|
|
>
|
|
<DialogTitle sx={{ ...responsiveText.h3, fontWeight: 700, p: 6, pb: 4 }}>
|
|
이벤트 약관
|
|
</DialogTitle>
|
|
<DialogContent sx={{ px: 6, pb: 6 }}>
|
|
<Typography variant="h6" sx={{ ...responsiveText.h4, fontWeight: 700, mb: 3 }}>
|
|
제1조 (목적)
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ ...responsiveText.body1, mb: 6, color: colors.gray[700] }}>
|
|
본 약관은 KT AI 이벤트 마케팅 서비스를 통해 진행되는 이벤트의 참여 및 개인정보 처리에 관한
|
|
사항을 규정합니다.
|
|
</Typography>
|
|
|
|
<Typography variant="h6" sx={{ ...responsiveText.h4, fontWeight: 700, mb: 3 }}>
|
|
제2조 (개인정보 수집 및 이용)
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ ...responsiveText.body1, mb: 2, color: colors.gray[700] }}>
|
|
수집 항목: 이름, 전화번호, 이메일
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ ...responsiveText.body1, mb: 2, color: colors.gray[700] }}>
|
|
이용 목적: 이벤트 참여 확인 및 경품 제공
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ ...responsiveText.body1, mb: 6, color: colors.gray[700] }}>
|
|
보유 기간: 이벤트 종료 후 6개월
|
|
</Typography>
|
|
|
|
<Typography variant="h6" sx={{ ...responsiveText.h4, fontWeight: 700, mb: 3 }}>
|
|
제3조 (당첨자 발표)
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ ...responsiveText.body1, color: colors.gray[700] }}>
|
|
당첨자는 이벤트 종료 후 7일 이내 개별 연락 드립니다.
|
|
</Typography>
|
|
</DialogContent>
|
|
<DialogActions sx={{ p: 6, pt: 4 }}>
|
|
<Button
|
|
fullWidth
|
|
onClick={() => setTermsDialogOpen(false)}
|
|
variant="contained"
|
|
sx={{
|
|
py: 3,
|
|
borderRadius: 3,
|
|
fontSize: '1rem',
|
|
fontWeight: 700,
|
|
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>
|
|
|
|
{/* Success Dialog */}
|
|
<Dialog
|
|
open={successDialogOpen}
|
|
onClose={() => {
|
|
setSuccessDialogOpen(false);
|
|
onApprove();
|
|
}}
|
|
PaperProps={{
|
|
sx: {
|
|
borderRadius: 4,
|
|
},
|
|
}}
|
|
>
|
|
<DialogContent sx={{ textAlign: 'center', py: 10, px: 8 }}>
|
|
<CheckCircle sx={{ fontSize: 80, color: colors.purple, mb: 4 }} />
|
|
<Typography variant="h5" sx={{ fontSize: '1.5rem', fontWeight: 700, mb: 3 }}>
|
|
배포 완료!
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary" sx={{ fontSize: '1rem', mb: 8 }}>
|
|
이벤트가 성공적으로 배포되었습니다.
|
|
<br />
|
|
실시간으로 참여자를 확인할 수 있습니다.
|
|
</Typography>
|
|
<Button
|
|
fullWidth
|
|
variant="contained"
|
|
size="large"
|
|
onClick={() => {
|
|
setSuccessDialogOpen(false);
|
|
onApprove();
|
|
}}
|
|
sx={{
|
|
py: 3,
|
|
borderRadius: 3,
|
|
fontSize: '1rem',
|
|
fontWeight: 700,
|
|
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>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</Box>
|
|
);
|
|
}
|