mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 07:36:23 +00:00
- 메인/이벤트 페이지와 동일한 색상 시스템을 모든 create 페이지에 적용 - ObjectiveStep: 아이콘, 카드 선택 상태, 그라데이션 버튼에 통일된 색상 적용 - RecommendationStep: AI 트렌드 아이콘, 카드 선택/호버 상태에 purple 색상 적용 - ChannelStep: 모든 채널 카드 및 체크박스에 통일된 디자인 시스템 적용 - 선택된 카드: purple 테두리, purpleLight 배경, 강조된 그림자 - 모든 체크박스: purple 색상 적용 (메인 체크박스 + SNS 하위 체크박스) - 선택 상태에 따른 폰트 굵기 변화로 시각적 피드백 강화 - ContentPreviewStep, ContentEditStep, ApprovalStep: 색상 시스템 추가 (향후 사용 준비) - 총 예상 노출 텍스트: purple 색상 적용 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
366 lines
12 KiB
TypeScript
366 lines
12 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 } from '@mui/icons-material';
|
|
import { EventData } from '../page';
|
|
|
|
// 디자인 시스템 색상
|
|
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',
|
|
},
|
|
};
|
|
|
|
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: 'background.default', pb: 20 }}>
|
|
<Container maxWidth="md" sx={{ pt: 8, pb: 8, px: { xs: 6, sm: 6, md: 8 } }}>
|
|
{/* Header */}
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3, mb: 8 }}>
|
|
<IconButton onClick={onBack}>
|
|
<ArrowBack />
|
|
</IconButton>
|
|
<Typography variant="h5" sx={{ fontWeight: 700 }}>
|
|
최종 승인
|
|
</Typography>
|
|
</Box>
|
|
|
|
{/* Title Section */}
|
|
<Box sx={{ textAlign: 'center', mb: 5 }}>
|
|
<CheckCircle sx={{ fontSize: 64, color: 'success.main', mb: 2 }} />
|
|
<Typography variant="h5" sx={{ fontWeight: 700, mb: 1 }}>
|
|
이벤트를 확인해주세요
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary">
|
|
모든 정보를 검토한 후 배포하세요
|
|
</Typography>
|
|
</Box>
|
|
|
|
{/* Event Summary Card */}
|
|
<Card elevation={0} sx={{ mb: 4, borderRadius: 4 }}>
|
|
<CardContent sx={{ p: 4 }}>
|
|
<Typography variant="h6" sx={{ fontWeight: 700, mb: 2 }}>
|
|
{eventData.recommendation?.title || '이벤트 제목'}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: 'flex', gap: 1, mb: 3 }}>
|
|
<Chip label="배포 대기" color="warning" size="small" />
|
|
<Chip label="AI 추천" color="info" size="small" />
|
|
</Box>
|
|
|
|
<Grid container spacing={2} sx={{ pt: 2, borderTop: 1, borderColor: 'divider' }}>
|
|
<Grid item xs={6}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
이벤트 기간
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
|
2025.02.01 ~ 2025.02.28
|
|
</Typography>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
목표 참여자
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
|
{eventData.recommendation?.expectedParticipants || 0}명
|
|
</Typography>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
예상 비용
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
|
{eventData.recommendation?.estimatedCost.toLocaleString() || 0}원
|
|
</Typography>
|
|
</Grid>
|
|
<Grid item xs={6}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
예상 ROI
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 600, color: 'error.main' }}>
|
|
{eventData.recommendation?.roi || 0}%
|
|
</Typography>
|
|
</Grid>
|
|
</Grid>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Event Details */}
|
|
<Typography variant="h6" sx={{ fontWeight: 700, mb: 3 }}>
|
|
이벤트 상세
|
|
</Typography>
|
|
|
|
<Card elevation={0} sx={{ mb: 2, borderRadius: 4 }}>
|
|
<CardContent sx={{ p: 3 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
|
|
<Box sx={{ flex: 1 }}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
이벤트 제목
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
|
{eventData.recommendation?.title}
|
|
</Typography>
|
|
</Box>
|
|
<IconButton size="small">
|
|
<Edit fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card elevation={0} sx={{ mb: 2, borderRadius: 4 }}>
|
|
<CardContent sx={{ p: 3 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
|
|
<Box sx={{ flex: 1 }}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
경품
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
|
{eventData.recommendation?.prize}
|
|
</Typography>
|
|
</Box>
|
|
<IconButton size="small">
|
|
<Edit fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card elevation={0} sx={{ mb: 2, borderRadius: 4 }}>
|
|
<CardContent sx={{ p: 3 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 2 }}>
|
|
<Box sx={{ flex: 1 }}>
|
|
<Typography variant="caption" color="text.secondary">
|
|
참여 방법
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ fontWeight: 600 }}>
|
|
{eventData.recommendation?.participationMethod}
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Distribution Channels */}
|
|
<Typography variant="h6" sx={{ fontWeight: 700, mb: 3, mt: 4 }}>
|
|
배포 채널
|
|
</Typography>
|
|
|
|
<Card elevation={0} sx={{ mb: 4, borderRadius: 4 }}>
|
|
<CardContent sx={{ p: 3 }}>
|
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, mb: 2 }}>
|
|
{getChannelNames(eventData.channels).map((channel) => (
|
|
<Chip key={channel} label={channel} color="primary" variant="outlined" />
|
|
))}
|
|
</Box>
|
|
<Button
|
|
size="small"
|
|
startIcon={<Edit />}
|
|
sx={{ color: 'primary.main' }}
|
|
>
|
|
채널 수정하기
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Terms Agreement */}
|
|
<Card elevation={0} sx={{ mb: 5, borderRadius: 4, bgcolor: 'grey.50' }}>
|
|
<CardContent sx={{ p: 3 }}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={agreeTerms}
|
|
onChange={(e) => setAgreeTerms(e.target.checked)}
|
|
/>
|
|
}
|
|
label={
|
|
<Typography variant="body2">
|
|
이벤트 약관 및 개인정보 처리방침에 동의합니다{' '}
|
|
<span style={{ color: 'red' }}>(필수)</span>
|
|
</Typography>
|
|
}
|
|
/>
|
|
<Link
|
|
component="button"
|
|
variant="body2"
|
|
onClick={() => setTermsDialogOpen(true)}
|
|
sx={{ color: 'error.main', ml: 4, mt: 1 }}
|
|
>
|
|
약관 보기
|
|
</Link>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Action Buttons */}
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<Button
|
|
fullWidth
|
|
variant="contained"
|
|
size="large"
|
|
disabled={!agreeTerms || isDeploying}
|
|
onClick={handleApprove}
|
|
startIcon={isDeploying ? null : <RocketLaunch />}
|
|
sx={{ py: 1.5 }}
|
|
>
|
|
{isDeploying ? '배포 중...' : '배포하기'}
|
|
</Button>
|
|
<Button
|
|
fullWidth
|
|
variant="outlined"
|
|
size="large"
|
|
onClick={handleSaveDraft}
|
|
startIcon={<Save />}
|
|
sx={{ py: 1.5 }}
|
|
>
|
|
임시저장
|
|
</Button>
|
|
</Box>
|
|
</Container>
|
|
|
|
{/* Terms Dialog */}
|
|
<Dialog
|
|
open={termsDialogOpen}
|
|
onClose={() => setTermsDialogOpen(false)}
|
|
maxWidth="sm"
|
|
fullWidth
|
|
>
|
|
<DialogTitle>이벤트 약관</DialogTitle>
|
|
<DialogContent>
|
|
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
제1조 (목적)
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ mb: 3 }}>
|
|
본 약관은 KT AI 이벤트 마케팅 서비스를 통해 진행되는 이벤트의 참여 및 개인정보 처리에 관한
|
|
사항을 규정합니다.
|
|
</Typography>
|
|
|
|
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
제2조 (개인정보 수집 및 이용)
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
수집 항목: 이름, 전화번호, 이메일
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ mb: 1 }}>
|
|
이용 목적: 이벤트 참여 확인 및 경품 제공
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ mb: 3 }}>
|
|
보유 기간: 이벤트 종료 후 6개월
|
|
</Typography>
|
|
|
|
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
제3조 (당첨자 발표)
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
당첨자는 이벤트 종료 후 7일 이내 개별 연락 드립니다.
|
|
</Typography>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={() => setTermsDialogOpen(false)} variant="contained">
|
|
확인
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
|
|
{/* Success Dialog */}
|
|
<Dialog
|
|
open={successDialogOpen}
|
|
onClose={() => {
|
|
setSuccessDialogOpen(false);
|
|
onApprove();
|
|
}}
|
|
>
|
|
<DialogContent sx={{ textAlign: 'center', py: 6, px: 4 }}>
|
|
<CheckCircle sx={{ fontSize: 80, color: 'success.main', mb: 2 }} />
|
|
<Typography variant="h5" sx={{ fontWeight: 700, mb: 1 }}>
|
|
배포 완료!
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
|
|
이벤트가 성공적으로 배포되었습니다.
|
|
<br />
|
|
실시간으로 참여자를 확인할 수 있습니다.
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
|
<Button
|
|
fullWidth
|
|
variant="contained"
|
|
size="large"
|
|
onClick={() => {
|
|
setSuccessDialogOpen(false);
|
|
onApprove();
|
|
}}
|
|
>
|
|
대시보드로 이동
|
|
</Button>
|
|
</Box>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</Box>
|
|
);
|
|
}
|