diff --git a/package-lock.json b/package-lock.json
index ad38b77..9644766 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,11 +18,11 @@
"@use-funnel/browser": "^0.0.12",
"@use-funnel/next": "^0.0.12",
"axios": "^1.7.7",
- "chart.js": "^4.4.6",
+ "chart.js": "^4.5.1",
"dayjs": "^1.11.13",
"next": "^14.2.15",
"react": "^18.3.1",
- "react-chartjs-2": "^5.2.0",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"zod": "^3.23.8",
@@ -30,6 +30,7 @@
},
"devDependencies": {
"@playwright/test": "^1.48.0",
+ "@types/chart.js": "^2.9.41",
"@types/node": "^22.7.5",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.0",
@@ -1215,6 +1216,16 @@
"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": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -4684,6 +4695,16 @@
"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": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
diff --git a/package.json b/package.json
index eca31d7..b4463e4 100644
--- a/package.json
+++ b/package.json
@@ -20,11 +20,11 @@
"@use-funnel/browser": "^0.0.12",
"@use-funnel/next": "^0.0.12",
"axios": "^1.7.7",
- "chart.js": "^4.4.6",
+ "chart.js": "^4.5.1",
"dayjs": "^1.11.13",
"next": "^14.2.15",
"react": "^18.3.1",
- "react-chartjs-2": "^5.2.0",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"zod": "^3.23.8",
@@ -32,6 +32,7 @@
},
"devDependencies": {
"@playwright/test": "^1.48.0",
+ "@types/chart.js": "^2.9.41",
"@types/node": "^22.7.5",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.0",
diff --git a/src/app/(main)/analytics/page.tsx b/src/app/(main)/analytics/page.tsx
index c2fc0ff..a94244f 100644
--- a/src/app/(main)/analytics/page.tsx
+++ b/src/app/(main)/analytics/page.tsx
@@ -10,12 +10,41 @@ import {
Grid,
} from '@mui/material';
import {
- PieChart,
- ShowChart,
+ PieChart as PieChartIcon,
+ ShowChart as ShowChartIcon,
Payments,
People,
} 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 {
+ cardStyles,
+ colors,
+ responsiveText,
+} from '@/shared/lib/button-styles';
+
+// Chart.js 등록
+ChartJS.register(
+ ArcElement,
+ CategoryScale,
+ LinearScale,
+ PointElement,
+ LineElement,
+ Title,
+ Tooltip,
+ Legend
+);
// Mock 데이터
const mockAnalyticsData = {
@@ -28,9 +57,9 @@ const mockAnalyticsData = {
targetRoi: 300,
},
channelPerformance: [
- { channel: '우리동네TV', participants: 58, percentage: 45, color: '#E31E24' },
- { channel: '링고비즈', participants: 38, percentage: 30, color: '#0066FF' },
- { channel: 'SNS', participants: 32, percentage: 25, color: '#FFB800' },
+ { channel: '우리동네TV', participants: 58, percentage: 45, color: '#F472B6' },
+ { channel: '링고비즈', participants: 38, percentage: 30, color: '#60A5FA' },
+ { channel: 'SNS', participants: 32, percentage: 25, color: '#FB923C' },
],
timePerformance: {
peakTime: '오후 2-4시',
@@ -98,18 +127,25 @@ export default function AnalyticsPage() {
return (
<>
-
-
+
+
{/* Title with Real-time Indicator */}
-
+
📊 요약 (실시간)
@@ -118,7 +154,7 @@ export default function AnalyticsPage() {
width: 8,
height: 8,
borderRadius: '50%',
- bgcolor: 'success.main',
+ bgcolor: colors.mint,
animation: 'pulse 2s infinite',
'@keyframes pulse': {
'0%, 100%': { opacity: 1 },
@@ -126,39 +162,75 @@ export default function AnalyticsPage() {
},
}}
/>
-
+
{updateText}
{/* Summary KPI Cards */}
-
+
-
-
-
+
+
+
참여자 수
-
- {summary.participants}명
+
+ {summary.participants}
-
+
↑ {summary.participantsDelta}명 (오늘)
-
-
-
+
+
+
총 비용
-
- {Math.floor(summary.totalCost / 10000)}만원
+
+ {Math.floor(summary.totalCost / 10000)}만
-
+
경품 {Math.floor(roiDetail.prizeCost / 10000)}만 + 채널{' '}
{Math.floor(roiDetail.channelCost / 10000)}만
@@ -166,31 +238,67 @@ export default function AnalyticsPage() {
-
-
-
+
+
+
예상 수익
-
- {Math.floor(summary.expectedRevenue / 10000)}만원
+
+ {Math.floor(summary.expectedRevenue / 10000)}만
-
- 매출증가 {Math.floor(roiDetail.salesIncrease / 10000)}만 + LTV{' '}
+
+ 매출 {Math.floor(roiDetail.salesIncrease / 10000)}만 + LTV{' '}
{Math.floor(roiDetail.newCustomerLTV / 10000)}만
-
-
-
+
+
+
투자대비수익률
-
+
{summary.roi}%
-
+
목표 {summary.targetRoi}% 달성
@@ -199,39 +307,76 @@ export default function AnalyticsPage() {
{/* Charts Grid */}
-
+
{/* Channel Performance */}
-
-
-
-
-
+
+
+
+
+
+
+
채널별 성과
- {/* Chart Placeholder */}
+ {/* Pie Chart */}
-
- donut_large
-
-
- 파이 차트
-
+ item.channel),
+ datasets: [
+ {
+ 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}%)`;
+ },
+ },
+ },
+ },
+ }}
+ />
{/* Legend */}
@@ -250,9 +395,11 @@ export default function AnalyticsPage() {
bgcolor: item.color,
}}
/>
- {item.channel}
+
+ {item.channel}
+
-
+
{item.percentage}% ({item.participants}명)
@@ -264,44 +411,113 @@ export default function AnalyticsPage() {
{/* Time Trend */}
-
-
-
-
-
+
+
+
+
+
+
+
시간대별 참여 추이
- {/* Chart Placeholder */}
+ {/* Line Chart */}
-
- trending_up
-
-
- 라인 차트
-
+
{/* Stats */}
-
+
피크 시간: {timePerformance.peakTime} ({timePerformance.peakParticipants}명)
-
+
평균 시간당: {timePerformance.avgPerHour}명
@@ -311,37 +527,49 @@ export default function AnalyticsPage() {
{/* ROI Detail & Participant Profile */}
-
+
{/* ROI Detail */}
-
-
-
-
-
+
+
+
+
+
+
+
투자대비수익률 상세
-
+
-
+
총 비용: {Math.floor(roiDetail.totalCost / 10000)}만원
-
+
-
+
• 경품 비용
-
+
{Math.floor(roiDetail.prizeCost / 10000)}만원
-
+
• 채널 비용
-
+
{Math.floor(roiDetail.channelCost / 10000)}만원
@@ -349,23 +577,23 @@ export default function AnalyticsPage() {
-
+
예상 수익: {Math.floor(roiDetail.expectedRevenue / 10000)}만원
-
+
-
+
• 매출 증가
-
+
{Math.floor(roiDetail.salesIncrease / 10000)}만원
-
+
• 신규 고객 LTV
-
+
{Math.floor(roiDetail.newCustomerLTV / 10000)}만원
@@ -373,26 +601,26 @@ export default function AnalyticsPage() {
-
+
투자대비수익률
-
+
(수익 - 비용) ÷ 비용 × 100
-
+
({Math.floor(roiDetail.expectedRevenue / 10000)}만 -{' '}
{Math.floor(roiDetail.totalCost / 10000)}만) ÷{' '}
{Math.floor(roiDetail.totalCost / 10000)}만 × 100
-
+
= {summary.roi}%
@@ -404,33 +632,45 @@ export default function AnalyticsPage() {
{/* Participant Profile */}
-
-
-
-
-
+
+
+
+
+
+
+
참여자 프로필
{/* Age Distribution */}
-
+
연령별
{participantProfile.age.map((item) => (
-
+
{item.label}
@@ -438,11 +678,11 @@ export default function AnalyticsPage() {
sx={{
width: `${item.percentage}%`,
height: '100%',
- bgcolor: 'info.main',
+ background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.blueLight} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
- pr: 1,
+ pr: 1.5,
}}
>
-
+
성별
{participantProfile.gender.map((item) => (
-
+
{item.label}
@@ -484,11 +724,11 @@ export default function AnalyticsPage() {
sx={{
width: `${item.percentage}%`,
height: '100%',
- bgcolor: 'error.main',
+ background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.pinkLight} 100%)`,
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
- pr: 1,
+ pr: 1.5,
}}
>
-
+
+
{/* Setup View (Before Drawing) */}
{!showResults && (
<>
- {/* Event Info */}
-
-
-
-
-
- 이벤트 정보
-
-
-
-
- 이벤트명
-
- {mockEventData.name}
-
-
-
- 총 참여자
-
-
- {mockEventData.totalParticipants}명
-
-
-
-
- 추첨 상태
-
- 추첨 전
-
-
-
+ {/* Page Header */}
+
+
+ 🎲 당첨자 추첨
+
+
+ 참여자 중에서 공정하게 당첨자를 선정하세요
+
+
+
+ {/* Event Info Summary Cards */}
+
+
+
+
+
+
+ 이벤트명
+
+
+ {mockEventData.name}
+
+
+
+
+
+
+
+
+
+ 총 참여자
+
+
+ {mockEventData.totalParticipants}명
+
+
+
+
+
{/* Drawing Settings */}
-
-
-
-
-
+
+
+
+
+
추첨 설정
-
-
+
+
당첨 인원
-
+
-
+
{winnerCount}
-
+
@@ -255,21 +307,42 @@ export default function DrawPage() {
setStoreBonus(e.target.checked)}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
- label="매장 방문 고객 가산점 (가중치: 1.5배)"
- sx={{ mb: 3 }}
+ label={
+
+ 매장 방문 고객 가산점 (가중치: 1.5배)
+
+ }
+ sx={{ mb: 6 }}
/>
-
-
-
-
+
+
+
+
추첨 방식
- • 난수 기반 무작위 추첨
- • 모든 추첨 과정은 자동 기록됩니다
+
+ • 난수 기반 무작위 추첨
+
+
+ • 모든 추첨 과정은 자동 기록됩니다
+
@@ -279,45 +352,75 @@ export default function DrawPage() {
fullWidth
variant="contained"
size="large"
- startIcon={}
+ startIcon={}
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)',
+ },
+ }}
>
추첨 시작
{/* Drawing History */}
-
- 📜 추첨 이력 (최근 3건)
+
+ 📜 추첨 이력
{mockDrawingHistory.length === 0 ? (
-
-
-
+
+
+
+
추첨 이력이 없습니다
) : (
-
+
{mockDrawingHistory.slice(0, 3).map((history, index) => (
-
-
+
+
-
+
{history.date} {history.isRedraw && '(재추첨)'}
-
+
당첨자 {history.winnerCount}명
@@ -335,51 +438,59 @@ export default function DrawPage() {
{showResults && (
<>
{/* Results Header */}
-
-
+
+
🎉 추첨 완료!
-
+
총 {mockEventData.totalParticipants}명 중 {winnerCount}명 당첨
{/* Winner List */}
-
-
+
+
🏆 당첨자 목록
-
+
{winners.map((winner, index) => {
const rank = index + 1;
return (
-
-
-
+
+
+
{rank}위
-
+
응모번호: #{winner.id}
-
+
{winner.name} ({winner.phone})
-
+
참여: {winner.channel}{' '}
{winner.hasBonus && storeBonus && '🌟'}
@@ -391,14 +502,18 @@ export default function DrawPage() {
})}
{storeBonus && (
-
+
🌟 매장 방문 고객 가산점 적용
)}
{/* Action Buttons */}
-
+
}
onClick={handleDownload}
- sx={{ borderRadius: 2 }}
+ sx={{
+ borderRadius: 3,
+ py: 3,
+ fontSize: '1rem',
+ fontWeight: 600,
+ borderWidth: 2,
+ '&:hover': {
+ borderWidth: 2,
+ },
+ }}
>
엑셀다운로드
@@ -418,7 +542,16 @@ export default function DrawPage() {
size="large"
startIcon={}
onClick={() => setRedrawDialogOpen(true)}
- sx={{ borderRadius: 2 }}
+ sx={{
+ borderRadius: 3,
+ py: 3,
+ fontSize: '1rem',
+ fontWeight: 600,
+ borderWidth: 2,
+ '&:hover': {
+ borderWidth: 2,
+ },
+ }}
>
재추첨
@@ -431,7 +564,18 @@ export default function DrawPage() {
size="large"
startIcon={}
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,
+ },
+ }}
>
당첨자에게 알림 전송
@@ -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,
+ }}
>
이벤트 목록으로
@@ -457,14 +606,15 @@ export default function DrawPage() {
sx: {
bgcolor: 'rgba(0, 0, 0, 0.9)',
color: 'white',
+ borderRadius: 4,
},
}}
>
-
+
-
+
{animationText}
-
+
{animationSubtext}
{/* Confirm Dialog */}
-
@@ -407,18 +765,20 @@ export default function EventDetailPage() {
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',
'&:hover': {
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
- transform: 'translateY(-2px)',
+ transform: 'translateY(-4px)',
},
}}
>
-
-
- 공유하기
+
+
+
+ 공유하기
+
@@ -427,18 +787,20 @@ export default function EventDetailPage() {
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',
'&:hover': {
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
- transform: 'translateY(-2px)',
+ transform: 'translateY(-4px)',
},
}}
>
-
-
- 데이터 다운
+
+
+
+ 데이터 다운
+
@@ -446,51 +808,51 @@ export default function EventDetailPage() {
{/* Recent Participants */}
-
-
-
- 최근 참여자
+
+
+
+ 👥 최근 참여자
chevron_right}
+ size="medium"
+ endIcon={chevron_right}
onClick={() => router.push(`/events/${eventId}/participants`)}
- sx={{ color: 'error.main', fontWeight: 600 }}
+ sx={{ color: colors.pink, fontWeight: 600 }}
>
전체보기
-
-
+
+
{recentParticipants.map((participant, index) => (
- {index > 0 && }
+ {index > 0 && }
-
+
-
+
-
+
{participant.name}
-
+
{participant.phone}
-
+
{participant.time}
diff --git a/src/app/(main)/events/[eventId]/participants/page.tsx b/src/app/(main)/events/[eventId]/participants/page.tsx
index 3caf968..c2236cf 100644
--- a/src/app/(main)/events/[eventId]/participants/page.tsx
+++ b/src/app/(main)/events/[eventId]/participants/page.tsx
@@ -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 (
-
+
+ {/* Page Header */}
+
+
+ 👥 참여자 목록
+
+
+ 이벤트에 참여한 사용자들의 정보를 확인하고 관리하세요
+
+
+
+ {/* Statistics Cards */}
+
+
+
+
+
+
+ 전체 참여자
+
+
+ {stats.total}명
+
+
+
+
+
+
+
+
+
+ 대기중
+
+
+ {stats.waiting}명
+
+
+
+
+
+
+
+
+
+ 당첨자
+
+
+ {stats.winner}명
+
+
+
+
+
+
+
+
+
+ 당첨률
+
+
+ {stats.total > 0 ? Math.round((stats.winner / stats.total) * 100) : 0}%
+
+
+
+
+
+
{/* Search Section */}
-
+
{/* Filters */}
-
-
-
-
+
+
+
+
참여 경로
-
+
상태
{/* Total Count & Drawing Button */}
-
-
-
- 총 {filteredParticipants.length}명 참여
+
+
+
+ 총 {filteredParticipants.length}명 참여
- }
- onClick={handleDrawClick}
- sx={{ borderRadius: 2 }}
- >
- 당첨자 추첨
-
+
+ }
+ onClick={handleDownloadClick}
+ sx={{
+ borderRadius: 3,
+ px: 4,
+ py: 1.5,
+ borderColor: colors.blue,
+ color: colors.blue,
+ '&:hover': {
+ borderColor: colors.blue,
+ bgcolor: `${colors.blue}10`,
+ },
+ }}
+ >
+ 엑셀 다운로드
+
+ }
+ onClick={handleDrawClick}
+ 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,
+ },
+ }}
+ >
+ 당첨자 추첨
+
+
{/* Participant List */}
-
+
{pageParticipants.length === 0 ? (
-
-
- people_outline
-
-
+
+
+
검색 결과가 없습니다
+
+ 다른 검색어나 필터를 사용해보세요
+
) : (
-
+
{pageParticipants.map((participant) => (
handleParticipantClick(participant)}
>
-
+
{/* Header */}
-
-
- #{participant.id}
-
-
- {participant.name}
-
-
- {participant.phone}
-
+
+
+
+
+
+
+ #{participant.id}
+
+
+ {participant.name}
+
+
+ {participant.phone}
+
+
@@ -287,26 +464,34 @@ export default function ParticipantsPage() {
-
-
+
+
참여 경로
-
- {participant.channel}
-
+
-
+
참여 일시
- {participant.date}
+
+ {participant.date}
+
@@ -318,87 +503,145 @@ export default function ParticipantsPage() {
{/* Pagination */}
{totalPages > 1 && (
-
+
setCurrentPage(page)}
color="primary"
size="large"
+ sx={{
+ '& .MuiPaginationItem-root': {
+ fontSize: '1rem',
+ fontWeight: 600,
+ },
+ }}
/>
)}
- {/* Excel Download Button (Desktop only) */}
-
- }
- onClick={handleDownloadClick}
- sx={{ borderRadius: 2 }}
- >
- 엑셀 다운로드
-
-
-
{/* Participant Detail Dialog */}
setDetailDialogOpen(false)}
maxWidth="sm"
fullWidth
+ PaperProps={{
+ sx: {
+ borderRadius: 4,
+ },
+ }}
>
- 참여자 상세 정보
-
+
+
+ 참여자 상세 정보
+
+
+
{selectedParticipant && (
-
-
-
- 응모번호
+
+
+
+
+
+
+ {selectedParticipant.name}
-
+
#{selectedParticipant.id}
-
-
- 이름
-
- {selectedParticipant.name}
-
-
-
- 전화번호
-
- {selectedParticipant.phone}
-
-
-
- 참여 경로
-
- {selectedParticipant.channel}
-
-
-
- 참여 일시
-
- {selectedParticipant.date}
-
-
-
- 당첨 여부
-
-
- {getStatusText(selectedParticipant.status)}
-
+
+
+
+
+ 전화번호
+
+
+ {selectedParticipant.phone}
+
+
+
+
+
+ 참여 경로
+
+
+ {selectedParticipant.channel}
+
+
+
+
+
+ 참여 일시
+
+
+ {selectedParticipant.date}
+
+
+
+
+
+ 당첨 여부
+
+
+
)}
-
- setDetailDialogOpen(false)} variant="contained">
+
+ setDetailDialogOpen(false)}
+ variant="contained"
+ fullWidth
+ sx={{
+ borderRadius: 3,
+ py: 1.5,
+ background: `linear-gradient(135deg, ${colors.pink} 0%, ${colors.purple} 100%)`,
+ }}
+ >
확인
diff --git a/src/app/(main)/events/create/steps/ApprovalStep.tsx b/src/app/(main)/events/create/steps/ApprovalStep.tsx
index 4729545..465029e 100644
--- a/src/app/(main)/events/create/steps/ApprovalStep.tsx
+++ b/src/app/(main)/events/create/steps/ApprovalStep.tsx
@@ -17,8 +17,9 @@ import {
DialogActions,
Link,
} from '@mui/material';
-import { ArrowBack, CheckCircle, Edit, RocketLaunch, Save } from '@mui/icons-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;
@@ -61,91 +62,214 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
};
return (
-
-
+
+
{/* Header */}
-
+
-
+
최종 승인
{/* Title Section */}
-
-
-
+
+
+
이벤트를 확인해주세요
-
+
모든 정보를 검토한 후 배포하세요
- {/* Event Summary Card */}
-
-
-
- {eventData.recommendation?.title || '이벤트 제목'}
-
-
-
-
-
-
-
-
-
-
- 이벤트 기간
+ {/* Event Summary Statistics */}
+
+
+
+
+
+
+ 이벤트 제목
-
- 2025.02.01 ~ 2025.02.28
+
+ {eventData.recommendation?.title || '이벤트 제목'}
-
-
-
+
+
+
+
+
+
+
+
목표 참여자
-
- {eventData.recommendation?.expectedParticipants || 0}명
+
+ {eventData.recommendation?.expectedParticipants || 0}
+
+ 명
+
-
-
-
+
+
+
+
+
+
+
+
예상 비용
-
- {eventData.recommendation?.estimatedCost.toLocaleString() || 0}원
+
+ {((eventData.recommendation?.estimatedCost || 0) / 10000).toFixed(0)}
+
+ 만원
+
-
-
-
+
+
+
+
+
+
+
+
예상 ROI
-
+
{eventData.recommendation?.roi || 0}%
-
-
-
-
+
+
+
+
{/* Event Details */}
-
+
이벤트 상세
-
-
+
+
-
+
이벤트 제목
-
+
{eventData.recommendation?.title}
@@ -156,14 +280,14 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
-
-
+
+
-
+
경품
-
+
{eventData.recommendation?.prize}
@@ -174,14 +298,14 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
-
-
+
+
-
+
참여 방법
-
+
{eventData.recommendation?.participationMethod}
@@ -190,21 +314,36 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
{/* Distribution Channels */}
-
+
배포 채널
-
-
-
+
+
+
{getChannelNames(eventData.channels).map((channel) => (
-
+
))}
}
- sx={{ color: 'primary.main' }}
+ sx={{
+ ...responsiveText.body2,
+ fontWeight: 600,
+ color: colors.purple,
+ }}
>
채널 수정하기
@@ -212,19 +351,27 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
{/* Terms Agreement */}
-
-
+
+
setAgreeTerms(e.target.checked)}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
label={
-
+
이벤트 약관 및 개인정보 처리방침에 동의합니다{' '}
- (필수)
+
+ (필수)
+
}
/>
@@ -232,7 +379,7 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
component="button"
variant="body2"
onClick={() => setTermsDialogOpen(true)}
- sx={{ color: 'error.main', ml: 4, mt: 1 }}
+ sx={{ ...responsiveText.body2, ml: 4, mt: 2, fontWeight: 600, color: colors.purple }}
>
약관 보기
@@ -240,7 +387,7 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
{/* Action Buttons */}
-
+
}
- sx={{ py: 1.5 }}
+ 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 ? '배포 중...' : '배포하기'}
@@ -258,7 +418,20 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
size="large"
onClick={handleSaveDraft}
startIcon={}
- sx={{ py: 1.5 }}
+ 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],
+ },
+ }}
>
임시저장
@@ -271,39 +444,61 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
onClose={() => setTermsDialogOpen(false)}
maxWidth="sm"
fullWidth
+ PaperProps={{
+ sx: {
+ borderRadius: 4,
+ },
+ }}
>
- 이벤트 약관
-
-
+
+ 이벤트 약관
+
+
+
제1조 (목적)
-
+
본 약관은 KT AI 이벤트 마케팅 서비스를 통해 진행되는 이벤트의 참여 및 개인정보 처리에 관한
사항을 규정합니다.
-
+
제2조 (개인정보 수집 및 이용)
-
+
수집 항목: 이름, 전화번호, 이메일
-
+
이용 목적: 이벤트 참여 확인 및 경품 제공
-
+
보유 기간: 이벤트 종료 후 6개월
-
+
제3조 (당첨자 발표)
-
+
당첨자는 이벤트 종료 후 7일 이내 개별 연락 드립니다.
-
- setTermsDialogOpen(false)} variant="contained">
+
+ 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,
+ },
+ }}
+ >
확인
@@ -316,30 +511,44 @@ export default function ApprovalStep({ eventData, onApprove, onBack }: ApprovalS
setSuccessDialogOpen(false);
onApprove();
}}
+ PaperProps={{
+ sx: {
+ borderRadius: 4,
+ },
+ }}
>
-
-
-
+
+
+
배포 완료!
-
+
이벤트가 성공적으로 배포되었습니다.
실시간으로 참여자를 확인할 수 있습니다.
-
- {
- setSuccessDialogOpen(false);
- onApprove();
- }}
- >
- 대시보드로 이동
-
-
+ {
+ 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,
+ },
+ }}
+ >
+ 대시보드로 이동
+
diff --git a/src/app/(main)/events/create/steps/ChannelStep.tsx b/src/app/(main)/events/create/steps/ChannelStep.tsx
index ec7bee7..39e29e7 100644
--- a/src/app/(main)/events/create/steps/ChannelStep.tsx
+++ b/src/app/(main)/events/create/steps/ChannelStep.tsx
@@ -17,6 +17,24 @@ import {
} from '@mui/material';
import { ArrowBack } 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',
+ },
+};
+
interface Channel {
id: string;
name: string;
@@ -89,19 +107,19 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
const selectedCount = channels.filter((ch) => ch.selected).length;
return (
-
-
+
+
{/* Header */}
-
-
+
+
-
+
배포 채널 선택
-
+
(최소 1개 이상)
@@ -109,21 +127,34 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
-
+
handleChannelToggle('uriTV')}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
- label="우리동네TV"
+ label={
+
+ 우리동네TV
+
+ }
sx={{ mb: channels[0].selected ? 2 : 0 }}
/>
@@ -171,21 +202,34 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
-
+
handleChannelToggle('ringoBiz')}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
- label="링고비즈"
+ label={
+
+ 링고비즈
+
+ }
sx={{ mb: channels[1].selected ? 2 : 0 }}
/>
@@ -217,21 +261,34 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
-
+
handleChannelToggle('genieTV')}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
- label="지니TV 광고"
+ label={
+
+ 지니TV 광고
+
+ }
sx={{ mb: channels[2].selected ? 2 : 0 }}
/>
@@ -290,21 +347,34 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
-
+
handleChannelToggle('sns')}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
- label="SNS"
+ label={
+
+ SNS
+
+ }
sx={{ mb: channels[3].selected ? 2 : 0 }}
/>
@@ -320,6 +390,12 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
onChange={(e) =>
handleConfigChange('sns', 'instagram', e.target.checked.toString())
}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
label="Instagram"
@@ -332,6 +408,12 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
onChange={(e) =>
handleConfigChange('sns', 'naver', e.target.checked.toString())
}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
label="Naver Blog"
@@ -344,6 +426,12 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
onChange={(e) =>
handleConfigChange('sns', 'kakao', e.target.checked.toString())
}
+ sx={{
+ color: colors.purple,
+ '&.Mui-checked': {
+ color: colors.purple,
+ },
+ }}
/>
}
label="Kakao Channel"
@@ -374,17 +462,29 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
{/* Summary */}
-
-
-
- 총 예상 비용
-
+
+
+
+
+ 총 예상 비용
+
+
{totalCost.toLocaleString()}원
- 총 예상 노출
-
+
+ 총 예상 노출
+
+
{totalExposure > 0 ? `${totalExposure.toLocaleString()}명+` : '0명'}
@@ -392,8 +492,23 @@ export default function ChannelStep({ onNext, onBack }: ChannelStepProps) {
{/* Action Buttons */}
-
-
+
+
이전
다음
diff --git a/src/app/(main)/events/create/steps/ContentEditStep.tsx b/src/app/(main)/events/create/steps/ContentEditStep.tsx
index cb037c5..5f8f659 100644
--- a/src/app/(main)/events/create/steps/ContentEditStep.tsx
+++ b/src/app/(main)/events/create/steps/ContentEditStep.tsx
@@ -11,6 +11,7 @@ import {
IconButton,
} from '@mui/material';
import { ArrowBack, Edit } from '@mui/icons-material';
+import { cardStyles, colors, responsiveText } from '@/shared/lib/button-styles';
interface ContentEditStepProps {
initialTitle: string;
@@ -39,71 +40,73 @@ export default function ContentEditStep({
};
return (
-
-
+
+
{/* Header */}
-
-
+
+
-
+
콘텐츠 편집
-
+
{/* Preview Section */}
-
+
미리보기
-
-
-
- celebration
-
-
- {title || '제목을 입력하세요'}
-
-
- {prize || '경품을 입력하세요'}
-
-
- {guide || '참여 안내를 입력하세요'}
-
-
+
+
+
+
+ celebration
+
+
+ {title || '제목을 입력하세요'}
+
+
+ {prize || '경품을 입력하세요'}
+
+
+ {guide || '참여 안내를 입력하세요'}
+
+
+
{/* Edit Section */}
-
+
편집
-
-
-
-
-
+
+
+
+
+
텍스트 편집
-
+
setGuide(e.target.value)}
multiline
- rows={3}
+ rows={4}
inputProps={{ maxLength: 100 }}
helperText={`${guide.length}/100자`}
/>
@@ -145,11 +148,42 @@ export default function ContentEditStep({
{/* Action Buttons */}
-
-
+
+
저장
-
+
다음 단계
diff --git a/src/app/(main)/events/create/steps/ContentPreviewStep.tsx b/src/app/(main)/events/create/steps/ContentPreviewStep.tsx
index 8186c53..b210c5d 100644
--- a/src/app/(main)/events/create/steps/ContentPreviewStep.tsx
+++ b/src/app/(main)/events/create/steps/ContentPreviewStep.tsx
@@ -11,9 +11,28 @@ import {
FormControlLabel,
IconButton,
Dialog,
+ Grid,
} from '@mui/material';
import { ArrowBack, ZoomIn, Psychology } 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',
+ },
+};
+
interface ImageStyle {
id: string;
name: string;
@@ -91,23 +110,23 @@ export default function ContentPreviewStep({
if (loading) {
return (
-
-
-
+
+
+
-
+
SNS 이미지 생성
-
+
-
+
AI 이미지 생성 중
-
+
딥러닝 모델이 이벤트에 어울리는
이미지를 생성하고 있어요...
-
+
예상 시간: 5초
@@ -133,134 +152,148 @@ export default function ContentPreviewStep({
}
return (
-
-
+
+
{/* Header */}
-
+
-
+
SNS 이미지 생성
+
+ 이벤트에 어울리는 스타일을 선택하세요
+
+
handleStyleSelect(e.target.value)}>
- {imageStyles.map((style) => (
-
-
- {style.name}
-
+
+ {imageStyles.map((style) => (
+
+ handleStyleSelect(style.id)}
+ >
+
+ {/* 스타일 이름 */}
+
+
+ {style.name}
+
+ }
+ label=""
+ sx={{ m: 0 }}
+ />
+
- handleStyleSelect(style.id)}
- >
- {selectedStyle === style.id && (
-
-
- check
-
-
- )}
-
-
-
-
- {style.icon}
-
-
- {title}
-
-
- {prize}
-
-
+
+ {style.icon}
+
+
+ {title}
+
+
+ {prize}
+
+
-
- }
- onClick={(e) => handlePreview(style, e)}
- >
- 크게보기
-
- }
- label=""
- sx={{ display: 'none' }}
- />
-
-
-
-
- ))}
+ {/* 크게보기 버튼 */}
+
+ }
+ onClick={(e) => handlePreview(style, e)}
+ sx={{
+ borderRadius: 2,
+ py: 1.5,
+ px: 4,
+ fontSize: '0.875rem',
+ fontWeight: 600,
+ }}
+ >
+ 크게보기
+
+
+
+
+
+ ))}
+
{/* Action Buttons */}
-
-
+
+
건너뛰기
다음
@@ -321,7 +367,7 @@ export default function ContentPreviewStep({
maxWidth: 600,
aspectRatio: '1 / 1',
background: fullscreenStyle.gradient || '#f5f5f5',
- borderRadius: 3,
+ borderRadius: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
diff --git a/src/app/(main)/events/create/steps/ObjectiveStep.tsx b/src/app/(main)/events/create/steps/ObjectiveStep.tsx
index a58e478..284cb3f 100644
--- a/src/app/(main)/events/create/steps/ObjectiveStep.tsx
+++ b/src/app/(main)/events/create/steps/ObjectiveStep.tsx
@@ -14,6 +14,24 @@ import {
import { AutoAwesome, TrendingUp, Replay, Store, Campaign } from '@mui/icons-material';
import { EventObjective } 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 ObjectiveOption {
id: EventObjective;
icon: React.ReactNode;
@@ -62,54 +80,56 @@ export default function ObjectiveStep({ onNext }: ObjectiveStepProps) {
};
return (
-
-
+
+
{/* Title Section */}
-
-
-
+
+
+
이벤트 목적을 선택해주세요
-
+
AI가 목적에 맞는 최적의 이벤트를 추천해드립니다
{/* Purpose Options */}
setSelected(e.target.value as EventObjective)}>
-
+
{objectives.map((objective) => (
setSelected(objective.id)}
>
-
-
- {objective.icon}
+
+
+ {objective.icon}
-
+
{objective.title}
-
+
{objective.description}
}
+ control={}
label=""
sx={{ m: 0 }}
/>
@@ -125,14 +145,15 @@ export default function ObjectiveStep({ onNext }: ObjectiveStepProps) {
-
-
-
+
+
+
선택하신 목적에 따라 AI가 업종, 지역, 계절 트렌드를 분석하여 가장 효과적인 이벤트를 추천합니다.
@@ -146,7 +167,20 @@ export default function ObjectiveStep({ onNext }: ObjectiveStepProps) {
size="large"
disabled={!selected}
onClick={handleNext}
- sx={{ py: 1.5 }}
+ 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],
+ },
+ }}
>
다음
diff --git a/src/app/(main)/events/create/steps/RecommendationStep.tsx b/src/app/(main)/events/create/steps/RecommendationStep.tsx
index f4245e1..a201950 100644
--- a/src/app/(main)/events/create/steps/RecommendationStep.tsx
+++ b/src/app/(main)/events/create/steps/RecommendationStep.tsx
@@ -19,6 +19,24 @@ import {
import { ArrowBack, Edit, Insights } from '@mui/icons-material';
import { EventObjective, BudgetLevel, EventMethod } 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 Recommendation {
id: string;
budget: BudgetLevel;
@@ -144,49 +162,56 @@ export default function RecommendationStep({ onNext, onBack }: RecommendationSte
};
return (
-
-
+
+
{/* Header */}
-
-
+
+
-
+
AI 이벤트 추천
{/* Trends Analysis */}
-
-
-
-
-
+
+
+
+
+
AI 트렌드 분석
-
+
-
+
📍 업종 트렌드
-
+
음식점업 신년 프로모션 트렌드
-
+
🗺️ 지역 트렌드
-
+
강남구 음식점 할인 이벤트 증가
-
+
☀️ 시즌 트렌드
-
+
설 연휴 특수 대비 고객 유치 전략
@@ -195,52 +220,67 @@ export default function RecommendationStep({ onNext, onBack }: RecommendationSte
{/* Budget Selection */}
-
-
+
+
예산별 추천 이벤트
-
+
각 예산별 2가지 방식 (온라인 1개, 오프라인 1개)을 추천합니다
setSelectedBudget(value)}
variant="fullWidth"
- sx={{ mb: 4 }}
+ sx={{ mb: 8 }}
>
-
-
-
+
+
+
{/* Recommendations */}
setSelected(e.target.value)}>
-
+
{budgetRecommendations.map((rec) => (
setSelected(rec.id)}
>
-
-
+
+
} label="" sx={{ m: 0 }} />
@@ -251,59 +291,61 @@ export default function RecommendationStep({ onNext, onBack }: RecommendationSte
value={editedData[rec.id]?.title || rec.title}
onChange={(e) => handleEditTitle(rec.id, e.target.value)}
onClick={(e) => e.stopPropagation()}
- sx={{ mb: 2 }}
+ sx={{ mb: 4 }}
InputProps={{
endAdornment: ,
+ sx: { fontSize: '1rem', py: 2 },
}}
/>
-
-
+
+
경품
handleEditPrize(rec.id, e.target.value)}
onClick={(e) => e.stopPropagation()}
InputProps={{
endAdornment: ,
+ sx: { fontSize: '1rem' },
}}
/>
-
+
-
+
참여 방법
-
+
{rec.participationMethod}
-
+
예상 참여
-
+
{rec.expectedParticipants}명
-
+
예상 비용
-
+
{(rec.estimatedCost / 10000).toFixed(0)}만원
-
+
투자대비수익률
-
+
{rec.roi}%
@@ -316,11 +358,46 @@ export default function RecommendationStep({ onNext, onBack }: RecommendationSte
{/* Action Buttons */}
-
-
+
+
이전
-
+
다음
diff --git a/src/app/(main)/events/page.tsx b/src/app/(main)/events/page.tsx
index 219f2bd..1a2dea4 100644
--- a/src/app/(main)/events/page.tsx
+++ b/src/app/(main)/events/page.tsx
@@ -13,13 +13,34 @@ import {
Card,
CardContent,
Typography,
- Chip,
InputAdornment,
Pagination,
Grid,
+ LinearProgress,
+ Chip,
} 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 {
+ cardStyles,
+ colors,
+ responsiveText,
+} from '@/shared/lib/button-styles';
// Mock 데이터
const mockEvents = [
@@ -29,11 +50,16 @@ const mockEvents = [
status: 'active' as const,
daysLeft: 5,
participants: 128,
+ targetParticipants: 200,
roi: 450,
startDate: '2025-11-01',
endDate: '2025-11-15',
prize: '커피 쿠폰',
method: '전화번호 입력',
+ isUrgent: true,
+ isPopular: false,
+ isHighROI: true,
+ isNew: false,
},
{
id: '2',
@@ -41,11 +67,16 @@ const mockEvents = [
status: 'active' as const,
daysLeft: 12,
participants: 56,
+ targetParticipants: 100,
roi: 320,
startDate: '2025-11-05',
endDate: '2025-11-20',
prize: '할인 쿠폰',
method: 'SNS 팔로우',
+ isUrgent: false,
+ isPopular: false,
+ isHighROI: false,
+ isNew: false,
},
{
id: '3',
@@ -53,11 +84,16 @@ const mockEvents = [
status: 'ended' as const,
daysLeft: 0,
participants: 234,
+ targetParticipants: 150,
roi: 580,
startDate: '2025-10-15',
endDate: '2025-10-31',
prize: '상품권',
method: '구매 인증',
+ isUrgent: false,
+ isPopular: true,
+ isHighROI: true,
+ isNew: false,
},
{
id: '4',
@@ -65,11 +101,16 @@ const mockEvents = [
status: 'scheduled' as const,
daysLeft: 30,
participants: 0,
+ targetParticipants: 300,
roi: 0,
startDate: '2025-12-01',
endDate: '2025-12-15',
prize: '체험권',
method: '이메일 등록',
+ isUrgent: false,
+ isPopular: false,
+ isHighROI: false,
+ isNew: true,
},
];
@@ -114,16 +155,28 @@ export default function EventsPage() {
router.push(`/events/${eventId}`);
};
- const getStatusColor = (status: string) => {
+ const getStatusStyle = (status: string) => {
switch (status) {
case 'active':
- return 'success';
+ return {
+ bgcolor: colors.mint,
+ color: 'white',
+ };
case 'scheduled':
- return 'info';
+ return {
+ bgcolor: colors.blue,
+ color: 'white',
+ };
case 'ended':
- return 'default';
+ return {
+ bgcolor: colors.gray[300],
+ color: colors.gray[700],
+ };
default:
- return 'default';
+ return {
+ bgcolor: colors.gray[200],
+ color: colors.gray[600],
+ };
}
};
@@ -140,13 +193,209 @@ export default function EventsPage() {
}
};
+ const getMethodIcon = (method: string) => {
+ switch (method) {
+ case '전화번호 입력':
+ return ;
+ case 'SNS 팔로우':
+ return ;
+ case '구매 인증':
+ return ;
+ case '이메일 등록':
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ 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 (
<>
-
-
+
+
+ {/* Summary Statistics */}
+
+
+
+
+
+
+ {stats.total}
+
+
+ 전체 이벤트
+
+
+
+
+
+
+
+
+
+ {stats.active}
+
+
+ 진행중
+
+
+
+
+
+
+
+
+
+ {stats.totalParticipants}
+
+
+ 총 참여자
+
+
+
+
+
+
+
+
+
+ {stats.avgROI}%
+
+
+ 평균 ROI
+
+
+
+
+
+
{/* Search Section */}
-
+
-
+
),
}}
sx={{
'& .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,
+ },
},
}}
/>
{/* Filters */}
-
-
-
+
+
+
상태
{/* Sorting */}
-
+
-
- 정렬:
+
+ 정렬
{/* Event List */}
-
+
{pageEvents.length === 0 ? (
-
-
- event_busy
-
-
- 검색 결과가 없습니다
-
-
+
+
+
+
+ event_busy
+
+
+
+ 검색 결과가 없습니다
+
+
+ 다른 검색 조건으로 다시 시도해보세요
+
+
+
) : (
-
+
{pageEvents.map((event) => (
handleEventClick(event.id)}
>
-
- {/* Header */}
-
-
- {event.title}
-
-
+ {/* Header with Badges */}
+
+
+
+ {event.title}
+
+
+ {getStatusText(event.status)}
+ {event.status === 'active'
? ` | D-${event.daysLeft}`
: event.status === 'scheduled'
? ` | D+${event.daysLeft}`
- : ''
- }`}
- color={getStatusColor(event.status) as any}
- size="small"
- sx={{ fontWeight: 600 }}
- />
+ : ''}
+
+
+
+ {/* Status Badges */}
+
+ {event.isUrgent && (
+ }
+ label="마감임박"
+ size="small"
+ sx={{
+ bgcolor: '#FEF3C7',
+ color: '#92400E',
+ fontWeight: 600,
+ fontSize: '0.75rem',
+ '& .MuiChip-icon': { color: '#92400E' },
+ }}
+ />
+ )}
+ {event.isPopular && (
+ }
+ label="인기"
+ size="small"
+ sx={{
+ bgcolor: '#FEE2E2',
+ color: '#991B1B',
+ fontWeight: 600,
+ fontSize: '0.75rem',
+ '& .MuiChip-icon': { color: '#991B1B' },
+ }}
+ />
+ )}
+ {event.isHighROI && (
+ }
+ label="높은 ROI"
+ size="small"
+ sx={{
+ bgcolor: '#DCFCE7',
+ color: '#166534',
+ fontWeight: 600,
+ fontSize: '0.75rem',
+ '& .MuiChip-icon': { color: '#166534' },
+ }}
+ />
+ )}
+ {event.isNew && (
+ }
+ label="신규"
+ size="small"
+ sx={{
+ bgcolor: '#DBEAFE',
+ color: '#1E40AF',
+ fontWeight: 600,
+ fontSize: '0.75rem',
+ '& .MuiChip-icon': { color: '#1E40AF' },
+ }}
+ />
+ )}
+
- {/* Stats */}
-
-
-
-
- 참여
+ {/* Progress Bar for Active Events */}
+ {event.status === 'active' && (
+
+
+
+ 이벤트 진행률
-
- {event.participants}명
+
+ {Math.round(calculateProgress(event))}%
-
-
+
+
+ )}
+
+ {/* Event Info and Stats Container */}
+
+ {/* Left: Event Info */}
+
+
+
+
+
+ {event.prize}
+
+
+
+ {getMethodIcon(event.method)}
+
+ {event.method}
+
+
+
+
+ {/* Date */}
+
+ 📅
+
+ {event.startDate} ~ {event.endDate}
+
+
+
+
+ {/* Right: Stats */}
+
-
- 투자대비수익률
+
+ 참여자
-
+
+ {event.participants.toLocaleString()}
+
+ 명
+
+
+ {event.targetParticipants > 0 && (
+
+ 목표: {event.targetParticipants}명 ({Math.round((event.participants / event.targetParticipants) * 100)}%)
+
+ )}
+
+
+
+ ROI
+
+ = 400 ? colors.mint : event.roi >= 200 ? colors.orange : colors.gray[500] }}>
{event.roi}%
-
-
-
- {/* Date */}
-
- {event.startDate} ~ {event.endDate}
-
+
+
))}
@@ -314,13 +757,28 @@ export default function EventsPage() {
{/* Pagination */}
{totalPages > 1 && (
-
+
setCurrentPage(page)}
- color="primary"
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],
+ },
+ },
+ }}
/>
)}
diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx
index bc20bfc..a6888e0 100644
--- a/src/app/(main)/page.tsx
+++ b/src/app/(main)/page.tsx
@@ -1,16 +1,7 @@
'use client';
import { useRouter } from 'next/navigation';
-import {
- Box,
- Container,
- Typography,
- Grid,
- Card,
- CardContent,
- Button,
- Fab,
-} from '@mui/material';
+import { Box, Container, Typography, Grid, Card, CardContent, Button, Fab } from '@mui/material';
import {
Add,
Celebration,
@@ -22,7 +13,12 @@ import {
CheckCircle,
} from '@mui/icons-material';
import Header from '@/shared/ui/Header';
-import { getGradientButtonStyle, responsiveText, cardStyles, colors } from '@/shared/lib/button-styles';
+import {
+ getGradientButtonStyle,
+ responsiveText,
+ cardStyles,
+ colors,
+} from '@/shared/lib/button-styles';
// Mock 사용자 데이터 (API 연동 전까지 임시 사용)
const mockUser = {
@@ -64,9 +60,10 @@ export default function HomePage() {
// KPI 계산
const activeEvents = mockEvents.filter((e) => e.status === '진행중');
const totalParticipants = mockEvents.reduce((sum, e) => sum + e.participants, 0);
- const avgROI = mockEvents.length > 0
- ? Math.round(mockEvents.reduce((sum, e) => sum + e.roi, 0) / mockEvents.length)
- : 0;
+ const avgROI =
+ mockEvents.length > 0
+ ? Math.round(mockEvents.reduce((sum, e) => sum + e.roi, 0) / mockEvents.length)
+ : 0;
const handleCreateEvent = () => {
router.push('/events/create');
@@ -91,380 +88,470 @@ export default function HomePage() {
minHeight: '100vh',
}}
>
-
- {/* Welcome Section */}
-
-
- 안녕하세요, {mockUser.name}님! 👋
-
-
- 이벤트 현황을 한눈에 확인하고 성과를 분석해보세요
-
-
-
- {/* KPI Cards */}
-
-
-
+ {/* Welcome Section */}
+
+
-
-
-
-
-
- 진행 중인 이벤트
-
-
- {activeEvents.length}
-
-
-
-
-
-
-
-
-
-
-
- 총 참여자
-
-
- {totalParticipants.toLocaleString()}
-
-
-
-
-
-
-
-
-
-
-
- 평균 ROI
-
-
- {avgROI}%
-
-
-
-
-
-
- {/* Quick Actions */}
-
-
- 빠른 시작
-
-
-
-
-
-
-
-
-
- 새 이벤트
-
-
-
-
-
-
-
-
-
-
-
- 성과 분석
-
-
-
-
-
-
-
- {/* Active Events */}
-
-
-
- 진행 중인 이벤트
+ 안녕하세요, {mockUser.name}님! 👋
+
+
+ 이벤트 현황을 한눈에 확인하고 성과를 분석해보세요
- chevron_right}
- onClick={() => router.push('/events')}
- sx={{
- color: colors.purple,
- fontWeight: 600,
- '&:hover': { bgcolor: 'rgba(167, 139, 250, 0.08)' },
- }}
- >
- 전체보기
-
- {activeEvents.length === 0 ? (
-
-
-
-
- event_busy
-
-
-
- 진행 중인 이벤트가 없습니다
-
-
- 새로운 이벤트를 만들어 고객과 소통해보세요
-
- }
- onClick={handleCreateEvent}
- sx={{
- py: { xs: 1.5, sm: 1.75 },
- px: { xs: 3, sm: 4 },
- fontSize: { xs: 15, sm: 16 },
- ...getGradientButtonStyle('primary'),
- }}
- >
- 새 이벤트 만들기
-
-
-
- ) : (
-
- {activeEvents.map((event) => (
+ {/* KPI Cards */}
+
+
+
+
+
+
+
+
+ 진행 중인 이벤트
+
+
+ {activeEvents.length}
+
+
+
+
+
+
+
+
+
+
+
+ 총 참여자
+
+
+ {totalParticipants.toLocaleString()}
+
+
+
+
+
+
+
+
+
+
+
+ 평균 ROI
+
+
+ {avgROI}%
+
+
+
+
+
+
+ {/* Quick Actions */}
+
+
+ 빠른 시작
+
+
+
handleEventClick(event.id)}
+ onClick={handleCreateEvent}
>
-
-
-
- {event.title}
-
-
- {event.status}
-
+
+
+
-
- 📅
- {event.startDate} ~ {event.endDate}
+
+ 새 이벤트
-
-
-
- 참여자
-
-
- {event.participants.toLocaleString()}
-
- 명
-
-
-
-
-
- ROI
-
-
- {event.roi}%
-
-
-
- ))}
-
- )}
-
-
- {/* Recent Activity */}
-
-
- 최근 활동
-
-
-
- {mockActivities.map((activity, index) => (
-
+
+ 0 ? 3.5 : 0,
- mt: index > 0 ? 3.5 : 0,
- borderTop: index > 0 ? 1 : 0,
- borderColor: colors.gray[200],
+ ...cardStyles.clickable,
}}
+ onClick={handleViewAnalytics}
>
-
+
+
+
+
+ 성과분석
+
+
+
+
+
+
+
+ {/* Active Events */}
+
+
+
+ 진행 중인 이벤트
+
+ chevron_right}
+ onClick={() => router.push('/events')}
+ sx={{
+ color: colors.purple,
+ fontWeight: 600,
+ '&:hover': { bgcolor: 'rgba(167, 139, 250, 0.08)' },
+ }}
+ >
+ 전체보기
+
+
+
+ {activeEvents.length === 0 ? (
+
+
+
+
+ event_busy
+
+
+
+ 진행 중인 이벤트가 없습니다
+
+
+ 새로운 이벤트를 만들어 고객과 소통해보세요
+
+ }
+ onClick={handleCreateEvent}
sx={{
- width: 48,
- height: 48,
- borderRadius: '14px',
- background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- flexShrink: 0,
+ py: { xs: 1.5, sm: 1.75 },
+ px: { xs: 3, sm: 4 },
+ fontSize: { xs: 15, sm: 16 },
+ ...getGradientButtonStyle('primary'),
}}
>
-
-
-
-
- {activity.text}
-
-
- {activity.time}
-
-
-
- ))}
-
-
-
-
+ 새 이벤트 만들기
+
+
+
+ ) : (
+
+ {activeEvents.map((event) => (
+ handleEventClick(event.id)}
+ >
+
+
+
+ {event.title}
+
+
+ {event.status}
+
+
+
+ 📅
+
+ {event.startDate} ~ {event.endDate}
+
+
+
+
+
+ 참여자
+
+
+ {event.participants.toLocaleString()}
+
+ 명
+
+
+
+
+
+ ROI
+
+
+ {event.roi}%
+
+
+
+
+
+ ))}
+
+ )}
+
- {/* Floating Action Button */}
-
+
+ 최근 활동
+
+
+
+ {mockActivities.map((activity, index) => (
+ 0 ? 6 : 0,
+ mt: index > 0 ? 6 : 0,
+ borderTop: index > 0 ? 1 : 0,
+ borderColor: colors.gray[200],
+ }}
+ >
+
+
+
+
+
+ {activity.text}
+
+
+ {activity.time}
+
+
+
+ ))}
+
+
+
+
+
+ {/* Floating Action Button */}
+
-
- {/* 사용자 정보 섹션 */}
-
-
-
-
-
- {user?.name}
-
-
- {user?.email}
-
-
+ <>
+
+
+
+ {/* 사용자 정보 섹션 */}
+
+
+
+
+
+
+ {user?.name}
+
+
+ {user?.email}
+
+
+
- {/* 기본 정보 */}
-
-
- 기본 정보
-
+ {/* 기본 정보 */}
+
+
+
+ 기본 정보
+
-
- (
-
- )}
- />
-
- (
- {
- const formatted = formatPhoneNumber(e.target.value);
- field.onChange(formatted);
- }}
- error={!!basicErrors.phone}
- helperText={basicErrors.phone?.message}
- />
- )}
- />
-
- (
-
- )}
- />
-
-
-
- {/* 매장 정보 */}
-
-
- 매장 정보
-
-
-
- (
-
- )}
- />
-
- (
-
- 업종
-
- {businessErrors.businessType && (
-
- {businessErrors.businessType.message}
-
+
+ (
+
)}
-
- )}
- />
-
- (
-
- )}
- />
-
- (
-
- )}
- />
-
-
- {/* 비밀번호 변경 */}
-
-
- 비밀번호 변경
-
+ (
+ {
+ const formatted = formatPhoneNumber(e.target.value);
+ field.onChange(formatted);
+ }}
+ error={!!basicErrors.phone}
+ helperText={basicErrors.phone?.message}
+ />
+ )}
+ />
-
- (
- (
+
+ )}
+ />
+
+
+
+
+ {/* 매장 정보 */}
+
+
+
+ 매장 정보
+
+
+
+ (
+
+ )}
+ />
+
+ (
+
+ 업종
+
+ {businessErrors.businessType && (
+
+ {businessErrors.businessType.message}
+
+ )}
+
+ )}
+ />
+
+ (
+
+ )}
+ />
+
+ (
+
+ )}
+ />
+
+
+
+
+ {/* 비밀번호 변경 */}
+
+
+
+ 비밀번호 변경
+
+
+
+ (
+
+ setShowCurrentPassword(!showCurrentPassword)}
+ edge="end"
+ >
+ {showCurrentPassword ? : }
+
+
+ ),
+ }}
+ />
+ )}
+ />
+
+ (
+
+ setShowNewPassword(!showNewPassword)}
+ edge="end"
+ >
+ {showNewPassword ? : }
+
+
+ ),
+ }}
+ />
+ )}
+ />
+
+ (
+
+ setShowConfirmPassword(!showConfirmPassword)}
+ edge="end"
+ >
+ {showConfirmPassword ? : }
+
+
+ ),
+ }}
+ />
+ )}
+ />
+
+
- setShowCurrentPassword(!showCurrentPassword)}
- edge="end"
- >
- {showCurrentPassword ? : }
-
-
- ),
+ variant="outlined"
+ size="large"
+ onClick={handlePasswordSubmit(onChangePassword)}
+ sx={{
+ mt: 1,
+ py: 3,
+ borderRadius: 3,
+ fontSize: '1rem',
+ fontWeight: 600,
+ borderWidth: 2,
+ '&:hover': {
+ borderWidth: 2,
+ },
}}
- />
- )}
- />
-
- (
-
- setShowNewPassword(!showNewPassword)}
- edge="end"
- >
- {showNewPassword ? : }
-
-
- ),
- }}
- />
- )}
- />
-
- (
-
- setShowConfirmPassword(!showConfirmPassword)}
- edge="end"
- >
- {showConfirmPassword ? : }
-
-
- ),
- }}
- />
- )}
- />
+ >
+ 비밀번호 변경
+
+
+
+
+ {/* 액션 버튼 */}
+
- 비밀번호 변경
+ 저장하기
+
+ setLogoutDialogOpen(true)}
+ sx={{
+ py: 3,
+ fontSize: '1rem',
+ fontWeight: 600,
+ }}
+ >
+ 로그아웃
-
-
- {/* 액션 버튼 */}
-
-
- 저장하기
-
- setLogoutDialogOpen(true)}
- sx={{
- py: { xs: 1.5, sm: 1.75 },
- fontSize: { xs: 15, sm: 16 },
- fontWeight: 600,
- }}
- >
- 로그아웃
-
-
+
{/* 저장 완료 다이얼로그 */}
- setSuccessDialogOpen(false)}>
+ setSuccessDialogOpen(false)}
+ PaperProps={{
+ sx: {
+ borderRadius: 4,
+ minWidth: 300,
+ },
+ }}
+ >
-
+
저장 완료
-
+
프로필 정보가 업데이트되었습니다.
@@ -496,8 +536,15 @@ export default function ProfilePage() {
}}
sx={{
minWidth: 120,
- py: { xs: 1.25, sm: 1.5 },
- ...getGradientButtonStyle('success'),
+ py: 2,
+ borderRadius: 2,
+ 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,
+ },
}}
>
확인
@@ -506,29 +553,51 @@ export default function ProfilePage() {
{/* 로그아웃 확인 다이얼로그 */}
- setLogoutDialogOpen(false)}>
- 로그아웃
+ setLogoutDialogOpen(false)}
+ PaperProps={{
+ sx: {
+ borderRadius: 4,
+ minWidth: 300,
+ },
+ }}
+ >
+ 로그아웃
-
+
로그아웃 하시겠습니까?
-
+
setLogoutDialogOpen(false)}
- sx={{ fontWeight: 600 }}
+ sx={{
+ fontWeight: 600,
+ fontSize: '1rem',
+ py: 2,
+ px: 4,
+ borderRadius: 2,
+ }}
>
취소
확인
-
+ >
);
}
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 3054163..eb797cc 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -24,13 +24,13 @@
--color-error: #D32F2F;
--color-info: #0288D1;
- /* Spacing (4px Grid) */
- --spacing-xs: 4px;
- --spacing-s: 8px;
- --spacing-m: 16px;
- --spacing-l: 24px;
- --spacing-xl: 32px;
- --spacing-2xl: 48px;
+ /* Spacing (4px Grid) - Doubled values */
+ --spacing-xs: 8px;
+ --spacing-s: 16px;
+ --spacing-m: 32px;
+ --spacing-l: 48px;
+ --spacing-xl: 64px;
+ --spacing-2xl: 96px;
/* Border Radius */
--border-radius-sm: 4px;
@@ -73,8 +73,8 @@ body {
padding: 0;
font-family: "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI",
"Roboto", "Helvetica Neue", system-ui, sans-serif;
- font-size: 14px;
- line-height: 1.5;
+ font-size: 16px;
+ line-height: 1.6;
color: var(--color-gray-900);
background-color: var(--color-gray-50);
min-height: 100%;
@@ -149,18 +149,18 @@ button {
width: 100%;
max-width: 1200px;
margin: 0 auto;
- padding: 0 20px;
+ padding: 0 40px;
}
@media (min-width: 768px) {
.container {
- padding: 0 40px;
+ padding: 0 80px;
}
}
@media (min-width: 1024px) {
.container {
- padding: 0 80px;
+ padding: 0 120px;
}
}