cherry2250 47ed0b5a7c 그래디언트 배경 카드 텍스트 가독성 개선 및 입체감 추가
- 배경색이 있는 모든 카드의 흰색 글자를 검은색으로 변경하여 가독성 향상
- 아이콘과 텍스트에 그림자 효과를 추가하여 입체감 부여
- 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] (라벨)
2025-10-27 16:15:43 +09:00

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>
);
}