mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 08:56:23 +00:00
- Next.js API 프록시 라우트 8개 생성 (User/Event Analytics) - analyticsClient baseURL을 프록시 경로로 변경 - analyticsApi 경로에서 /api/v1 접두사 제거 - 404/400 에러에 대한 사용자 친화적 에러 처리 추가 - Dashboard, Event Detail, Analytics 페이지 에러 핸들링 개선 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
612 lines
24 KiB
TypeScript
612 lines
24 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import { Box, Container, Typography, Grid, Card, CardContent, Button, Fab, CircularProgress, Alert } from '@mui/material';
|
||
import {
|
||
Add,
|
||
Celebration,
|
||
Group,
|
||
TrendingUp,
|
||
Analytics,
|
||
PersonAdd,
|
||
Edit,
|
||
CheckCircle,
|
||
} from '@mui/icons-material';
|
||
import Header from '@/shared/ui/Header';
|
||
import {
|
||
getGradientButtonStyle,
|
||
responsiveText,
|
||
cardStyles,
|
||
colors,
|
||
} from '@/shared/lib/button-styles';
|
||
import { useAuth } from '@/features/auth/model/useAuth';
|
||
import { analyticsApi } from '@/entities/analytics/api/analyticsApi';
|
||
import type { UserAnalyticsDashboardResponse } from '@/entities/analytics/model/types';
|
||
|
||
const mockActivities = [
|
||
{ icon: PersonAdd, text: 'SNS 팔로우 이벤트에 새로운 참여자 12명', time: '5분 전' },
|
||
{ icon: Edit, text: '설 맞이 할인 이벤트 내용을 수정했습니다', time: '1시간 전' },
|
||
{ icon: CheckCircle, text: '고객 만족도 조사가 종료되었습니다', time: '3시간 전' },
|
||
];
|
||
|
||
export default function HomePage() {
|
||
const router = useRouter();
|
||
const { user } = useAuth();
|
||
const [analyticsData, setAnalyticsData] = useState<UserAnalyticsDashboardResponse | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
// Analytics API 호출
|
||
useEffect(() => {
|
||
const fetchAnalytics = async () => {
|
||
if (!user?.userId) {
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
setLoading(true);
|
||
setError(null);
|
||
const data = await analyticsApi.getUserAnalytics(user.userId, { refresh: false });
|
||
setAnalyticsData(data);
|
||
} catch (err: any) {
|
||
console.error('Failed to fetch analytics:', err);
|
||
|
||
// 404 또는 400 에러는 아직 Analytics 데이터가 없는 경우
|
||
if (err.response?.status === 404 || err.response?.status === 400) {
|
||
console.log('ℹ️ Analytics 데이터가 아직 생성되지 않았습니다.');
|
||
setError('아직 분석 데이터가 없습니다. 이벤트를 생성하고 참여자가 생기면 자동으로 생성됩니다.');
|
||
} else {
|
||
setError('분석 데이터를 불러오는데 실패했습니다.');
|
||
}
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
fetchAnalytics();
|
||
}, [user?.userId]);
|
||
|
||
// KPI 계산 - Analytics API 데이터 사용
|
||
const activeEventsCount = analyticsData?.activeEvents ?? 0;
|
||
const totalParticipants = analyticsData?.overallSummary?.participants ?? 0;
|
||
const avgROI = Math.round((analyticsData?.overallRoi?.roi ?? 0) * 100) / 100;
|
||
const eventPerformances = analyticsData?.eventPerformances ?? [];
|
||
|
||
const handleCreateEvent = () => {
|
||
router.push('/events/create');
|
||
};
|
||
|
||
const handleViewAnalytics = () => {
|
||
router.push('/analytics');
|
||
};
|
||
|
||
const handleEventClick = (eventId: string) => {
|
||
router.push(`/events/${eventId}`);
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<Header title="대시보드" showBack={false} showMenu={false} showProfile={true} />
|
||
<Box
|
||
sx={{
|
||
pt: { xs: 7, sm: 8 },
|
||
pb: { xs: 4, sm: 10 },
|
||
bgcolor: colors.gray[50],
|
||
minHeight: '100vh',
|
||
}}
|
||
>
|
||
<Container maxWidth="lg" sx={{ pt: { xs: 4, sm: 8 }, pb: { xs: 4, sm: 6 }, px: { xs: 3, sm: 6, md: 10 } }}>
|
||
{/* Welcome Section */}
|
||
<Box sx={{ mb: { xs: 5, sm: 10 } }}>
|
||
<Typography
|
||
variant="h3"
|
||
sx={{
|
||
...responsiveText.h2,
|
||
mb: { xs: 2, sm: 4 },
|
||
}}
|
||
>
|
||
안녕하세요, {user?.userName || '사용자'}님! 👋
|
||
</Typography>
|
||
<Typography variant="body1" sx={{ ...responsiveText.body1 }}>
|
||
이벤트 현황을 한눈에 확인하고 성과를 분석해보세요
|
||
</Typography>
|
||
</Box>
|
||
|
||
{/* Error Alert */}
|
||
{error && (
|
||
<Alert severity="error" sx={{ mb: 3 }}>
|
||
{error}
|
||
</Alert>
|
||
)}
|
||
|
||
{/* Loading State */}
|
||
{loading && (
|
||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
|
||
<CircularProgress />
|
||
</Box>
|
||
)}
|
||
|
||
{/* KPI Cards */}
|
||
<Grid container spacing={{ xs: 1.5, sm: 6 }} sx={{ mb: { xs: 5, sm: 10 } }}>
|
||
<Grid item xs={4} sm={4}>
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.default,
|
||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||
borderColor: 'transparent',
|
||
}}
|
||
>
|
||
<CardContent sx={{ textAlign: 'center', pt: { xs: 1.5, sm: 6 }, pb: { xs: 1.5, sm: 6 }, px: { xs: 0.5, sm: 4 } }}>
|
||
<Box
|
||
sx={{
|
||
width: { xs: 32, sm: 64 },
|
||
height: { xs: 32, sm: 64 },
|
||
borderRadius: '50%',
|
||
bgcolor: 'rgba(0, 0, 0, 0.05)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
margin: '0 auto',
|
||
mb: { xs: 0.75, sm: 3 },
|
||
}}
|
||
>
|
||
<Celebration sx={{
|
||
fontSize: { xs: 18, sm: 32 },
|
||
color: colors.gray[900],
|
||
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
||
}} />
|
||
</Box>
|
||
<Typography
|
||
variant="body2"
|
||
sx={{
|
||
mb: 0.5,
|
||
color: colors.gray[700],
|
||
fontWeight: 500,
|
||
fontSize: { xs: '0.6875rem', sm: '0.875rem' },
|
||
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
||
lineHeight: 1.2,
|
||
}}
|
||
>
|
||
진행 중인 이벤트
|
||
</Typography>
|
||
<Typography
|
||
variant="h3"
|
||
sx={{
|
||
fontWeight: 700,
|
||
color: colors.gray[900],
|
||
fontSize: { xs: '1.375rem', sm: '2.25rem' },
|
||
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
||
}}
|
||
>
|
||
{activeEventsCount}
|
||
</Typography>
|
||
</CardContent>
|
||
</Card>
|
||
</Grid>
|
||
<Grid item xs={4} sm={4}>
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.default,
|
||
background: `linear-gradient(135deg, ${colors.mint} 0%, ${colors.mintLight} 100%)`,
|
||
borderColor: 'transparent',
|
||
}}
|
||
>
|
||
<CardContent sx={{ textAlign: 'center', pt: { xs: 1.5, sm: 6 }, pb: { xs: 1.5, sm: 6 }, px: { xs: 0.5, sm: 4 } }}>
|
||
<Box
|
||
sx={{
|
||
width: { xs: 32, sm: 64 },
|
||
height: { xs: 32, sm: 64 },
|
||
borderRadius: '50%',
|
||
bgcolor: 'rgba(0, 0, 0, 0.05)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
margin: '0 auto',
|
||
mb: { xs: 0.75, sm: 3 },
|
||
}}
|
||
>
|
||
<Group sx={{
|
||
fontSize: { xs: 18, sm: 32 },
|
||
color: colors.gray[900],
|
||
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
||
}} />
|
||
</Box>
|
||
<Typography
|
||
variant="body2"
|
||
sx={{
|
||
mb: 0.5,
|
||
color: colors.gray[700],
|
||
fontWeight: 500,
|
||
fontSize: { xs: '0.6875rem', sm: '0.875rem' },
|
||
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
||
lineHeight: 1.2,
|
||
}}
|
||
>
|
||
총 참여자
|
||
</Typography>
|
||
<Typography
|
||
variant="h3"
|
||
sx={{
|
||
fontWeight: 700,
|
||
color: colors.gray[900],
|
||
fontSize: { xs: '1.375rem', sm: '2.25rem' },
|
||
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
||
}}
|
||
>
|
||
{totalParticipants.toLocaleString()}
|
||
</Typography>
|
||
</CardContent>
|
||
</Card>
|
||
</Grid>
|
||
<Grid item xs={4} sm={4}>
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.default,
|
||
background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.blueLight} 100%)`,
|
||
borderColor: 'transparent',
|
||
}}
|
||
>
|
||
<CardContent sx={{ textAlign: 'center', pt: { xs: 1.5, sm: 6 }, pb: { xs: 1.5, sm: 6 }, px: { xs: 0.5, sm: 4 } }}>
|
||
<Box
|
||
sx={{
|
||
width: { xs: 32, sm: 64 },
|
||
height: { xs: 32, sm: 64 },
|
||
borderRadius: '50%',
|
||
bgcolor: 'rgba(0, 0, 0, 0.05)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
margin: '0 auto',
|
||
mb: { xs: 0.75, sm: 3 },
|
||
}}
|
||
>
|
||
<TrendingUp sx={{
|
||
fontSize: { xs: 18, sm: 32 },
|
||
color: colors.gray[900],
|
||
filter: 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))',
|
||
}} />
|
||
</Box>
|
||
<Typography
|
||
variant="body2"
|
||
sx={{
|
||
mb: 0.5,
|
||
color: colors.gray[700],
|
||
fontWeight: 500,
|
||
fontSize: { xs: '0.6875rem', sm: '0.875rem' },
|
||
textShadow: '0px 1px 2px rgba(0,0,0,0.1)',
|
||
lineHeight: 1.2,
|
||
}}
|
||
>
|
||
평균 ROI
|
||
</Typography>
|
||
<Typography
|
||
variant="h3"
|
||
sx={{
|
||
fontWeight: 700,
|
||
color: colors.gray[900],
|
||
fontSize: { xs: '1.375rem', sm: '2.25rem' },
|
||
textShadow: '0px 2px 4px rgba(0,0,0,0.15)',
|
||
}}
|
||
>
|
||
{avgROI}%
|
||
</Typography>
|
||
</CardContent>
|
||
</Card>
|
||
</Grid>
|
||
</Grid>
|
||
|
||
{/* Quick Actions */}
|
||
<Box sx={{ mb: { xs: 5, sm: 10 } }}>
|
||
<Typography variant="h5" sx={{ ...responsiveText.h3, mb: { xs: 3, sm: 6 } }}>
|
||
빠른 시작
|
||
</Typography>
|
||
<Grid container spacing={{ xs: 3, sm: 6 }}>
|
||
<Grid item xs={6} sm={6}>
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.clickable,
|
||
}}
|
||
onClick={handleCreateEvent}
|
||
>
|
||
<CardContent sx={{ textAlign: 'center', pt: { xs: 2, sm: 6 }, pb: { xs: 2, sm: 6 } }}>
|
||
<Box
|
||
sx={{
|
||
width: { xs: 56, sm: 72 },
|
||
height: { xs: 56, sm: 72 },
|
||
borderRadius: { xs: '16px', sm: '20px' },
|
||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.blue} 100%)`,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
margin: '0 auto',
|
||
mb: { xs: 2, sm: 3 },
|
||
boxShadow: '0 4px 14px 0 rgba(167, 139, 250, 0.39)',
|
||
}}
|
||
>
|
||
<Add sx={{ fontSize: { xs: 28, sm: 36 }, color: 'white' }} />
|
||
</Box>
|
||
<Typography variant="body1" sx={{ fontWeight: 600, color: colors.gray[900], fontSize: { xs: '0.875rem', sm: '1.125rem' } }}>
|
||
새 이벤트
|
||
</Typography>
|
||
</CardContent>
|
||
</Card>
|
||
</Grid>
|
||
<Grid item xs={6} sm={6}>
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.clickable,
|
||
}}
|
||
onClick={handleViewAnalytics}
|
||
>
|
||
<CardContent sx={{ textAlign: 'center', pt: { xs: 2, sm: 6 }, pb: { xs: 2, sm: 6 } }}>
|
||
<Box
|
||
sx={{
|
||
width: { xs: 56, sm: 72 },
|
||
height: { xs: 56, sm: 72 },
|
||
borderRadius: { xs: '16px', sm: '20px' },
|
||
background: `linear-gradient(135deg, ${colors.blue} 0%, ${colors.mint} 100%)`,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
margin: '0 auto',
|
||
mb: { xs: 2, sm: 3 },
|
||
boxShadow: '0 4px 14px 0 rgba(96, 165, 250, 0.39)',
|
||
}}
|
||
>
|
||
<Analytics sx={{ fontSize: { xs: 28, sm: 36 }, color: 'white' }} />
|
||
</Box>
|
||
<Typography variant="body1" sx={{ fontWeight: 600, color: colors.gray[900], fontSize: { xs: '0.875rem', sm: '1.125rem' } }}>
|
||
성과분석
|
||
</Typography>
|
||
</CardContent>
|
||
</Card>
|
||
</Grid>
|
||
</Grid>
|
||
</Box>
|
||
|
||
{/* Active Events */}
|
||
<Box sx={{ mb: { xs: 5, sm: 10 } }}>
|
||
<Box
|
||
sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: { xs: 3, sm: 6 } }}
|
||
>
|
||
<Typography variant="h5" sx={{ ...responsiveText.h3 }}>
|
||
진행 중인 이벤트
|
||
</Typography>
|
||
<Button
|
||
size="small"
|
||
endIcon={<span className="material-icons" style={{ fontSize: '18px' }}>chevron_right</span>}
|
||
onClick={() => router.push('/events')}
|
||
sx={{
|
||
color: colors.purple,
|
||
fontWeight: 600,
|
||
fontSize: { xs: '0.8125rem', sm: '0.875rem' },
|
||
'&:hover': { bgcolor: 'rgba(167, 139, 250, 0.08)' },
|
||
}}
|
||
>
|
||
전체보기
|
||
</Button>
|
||
</Box>
|
||
|
||
{!loading && eventPerformances.length === 0 ? (
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.default,
|
||
}}
|
||
>
|
||
<CardContent sx={{ textAlign: 'center', py: 10 }}>
|
||
<Box sx={{ color: colors.gray[300], mb: 3 }}>
|
||
<span className="material-icons" style={{ fontSize: 72 }}>
|
||
event_busy
|
||
</span>
|
||
</Box>
|
||
<Typography variant="h6" sx={{ mb: 2, color: colors.gray[700] }}>
|
||
진행 중인 이벤트가 없습니다
|
||
</Typography>
|
||
<Typography variant="body2" sx={{ mb: 4, color: colors.gray[500] }}>
|
||
새로운 이벤트를 만들어 고객과 소통해보세요
|
||
</Typography>
|
||
<Button
|
||
variant="contained"
|
||
startIcon={<Add />}
|
||
onClick={handleCreateEvent}
|
||
sx={{
|
||
py: { xs: 1.5, sm: 1.75 },
|
||
px: { xs: 3, sm: 4 },
|
||
fontSize: { xs: 15, sm: 16 },
|
||
...getGradientButtonStyle('primary'),
|
||
}}
|
||
>
|
||
새 이벤트 만들기
|
||
</Button>
|
||
</CardContent>
|
||
</Card>
|
||
) : !loading && (
|
||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: { xs: 3, sm: 6 } }}>
|
||
{eventPerformances.slice(0, 2).map((event) => (
|
||
<Card
|
||
key={event.eventId}
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.clickable,
|
||
}}
|
||
onClick={() => handleEventClick(event.eventId)}
|
||
>
|
||
<CardContent sx={{ p: { xs: 4, sm: 6, md: 8 } }}>
|
||
<Box
|
||
sx={{
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'start',
|
||
mb: { xs: 3, sm: 6 },
|
||
gap: 2,
|
||
}}
|
||
>
|
||
<Typography variant="h6" sx={{ fontWeight: 700, color: colors.gray[900], fontSize: { xs: '1rem', sm: '1.25rem' } }}>
|
||
{event.eventTitle}
|
||
</Typography>
|
||
<Box
|
||
sx={{
|
||
px: { xs: 2, sm: 2.5 },
|
||
py: 0.75,
|
||
bgcolor: colors.mint,
|
||
color: 'white',
|
||
borderRadius: 2,
|
||
fontSize: { xs: '0.75rem', sm: '0.875rem' },
|
||
fontWeight: 600,
|
||
flexShrink: 0,
|
||
}}
|
||
>
|
||
{event.status}
|
||
</Box>
|
||
</Box>
|
||
<Box sx={{ display: 'flex', gap: { xs: 6, sm: 12 }, mt: { xs: 3, sm: 6 } }}>
|
||
<Box>
|
||
<Typography
|
||
variant="body2"
|
||
sx={{ mb: 0.5, color: colors.gray[600], fontWeight: 500, fontSize: { xs: '0.75rem', sm: '0.875rem' } }}
|
||
>
|
||
참여자
|
||
</Typography>
|
||
<Typography
|
||
variant="h5"
|
||
sx={{ fontWeight: 700, color: colors.gray[900], fontSize: { xs: '1.125rem', sm: '1.5rem' } }}
|
||
>
|
||
{event.participants.toLocaleString()}
|
||
<Typography
|
||
component="span"
|
||
variant="body2"
|
||
sx={{ ml: 0.5, color: colors.gray[600], fontSize: { xs: '0.75rem', sm: '0.875rem' } }}
|
||
>
|
||
명
|
||
</Typography>
|
||
</Typography>
|
||
</Box>
|
||
<Box>
|
||
<Typography
|
||
variant="body2"
|
||
sx={{ mb: 0.5, color: colors.gray[600], fontWeight: 500, fontSize: { xs: '0.75rem', sm: '0.875rem' } }}
|
||
>
|
||
조회수
|
||
</Typography>
|
||
<Typography
|
||
variant="h5"
|
||
sx={{ fontWeight: 700, color: colors.gray[900], fontSize: { xs: '1.125rem', sm: '1.5rem' } }}
|
||
>
|
||
{event.views.toLocaleString()}
|
||
<Typography
|
||
component="span"
|
||
variant="body2"
|
||
sx={{ ml: 0.5, color: colors.gray[600], fontSize: { xs: '0.75rem', sm: '0.875rem' } }}
|
||
>
|
||
회
|
||
</Typography>
|
||
</Typography>
|
||
</Box>
|
||
<Box>
|
||
<Typography
|
||
variant="body2"
|
||
sx={{ mb: 0.5, color: colors.gray[600], fontWeight: 500, fontSize: { xs: '0.75rem', sm: '0.875rem' } }}
|
||
>
|
||
ROI
|
||
</Typography>
|
||
<Typography variant="h5" sx={{ fontWeight: 700, color: colors.mint, fontSize: { xs: '1.125rem', sm: '1.5rem' } }}>
|
||
{Math.round(event.roi * 100) / 100}%
|
||
</Typography>
|
||
</Box>
|
||
</Box>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</Box>
|
||
)}
|
||
</Box>
|
||
|
||
{/* Recent Activity */}
|
||
<Box sx={{ mb: { xs: 5, sm: 10 } }}>
|
||
<Typography variant="h5" sx={{ ...responsiveText.h3, mb: { xs: 3, sm: 6 } }}>
|
||
최근 활동
|
||
</Typography>
|
||
<Card
|
||
elevation={0}
|
||
sx={{
|
||
...cardStyles.default,
|
||
}}
|
||
>
|
||
<CardContent sx={{ p: { xs: 4, sm: 6, md: 8 } }}>
|
||
{mockActivities.map((activity, index) => (
|
||
<Box
|
||
key={index}
|
||
sx={{
|
||
display: 'flex',
|
||
gap: { xs: 2, sm: 4 },
|
||
pt: index > 0 ? { xs: 3, sm: 6 } : 0,
|
||
mt: index > 0 ? { xs: 3, sm: 6 } : 0,
|
||
borderTop: index > 0 ? 1 : 0,
|
||
borderColor: colors.gray[200],
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
width: { xs: 40, sm: 48 },
|
||
height: { xs: 40, sm: 48 },
|
||
borderRadius: { xs: '12px', sm: '14px' },
|
||
background: `linear-gradient(135deg, ${colors.purple} 0%, ${colors.purpleLight} 100%)`,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0,
|
||
}}
|
||
>
|
||
<activity.icon sx={{ fontSize: { xs: 20, sm: 24 }, color: 'white' }} />
|
||
</Box>
|
||
<Box sx={{ flex: 1, minWidth: 0 }}>
|
||
<Typography
|
||
variant="body1"
|
||
sx={{ fontWeight: 600, color: colors.gray[900], mb: 0.5, fontSize: { xs: '0.8125rem', sm: '1rem' } }}
|
||
>
|
||
{activity.text}
|
||
</Typography>
|
||
<Typography variant="body2" sx={{ color: colors.gray[500], fontSize: { xs: '0.75rem', sm: '0.875rem' } }}>
|
||
{activity.time}
|
||
</Typography>
|
||
</Box>
|
||
</Box>
|
||
))}
|
||
</CardContent>
|
||
</Card>
|
||
</Box>
|
||
</Container>
|
||
|
||
{/* Floating Action Button */}
|
||
<Fab
|
||
sx={{
|
||
position: 'fixed',
|
||
bottom: { xs: 72, sm: 90 },
|
||
right: { xs: 16, sm: 32 },
|
||
width: { xs: 56, sm: 72 },
|
||
height: { xs: 56, sm: 72 },
|
||
...getGradientButtonStyle('primary'),
|
||
boxShadow:
|
||
'0 10px 25px -5px rgba(167, 139, 250, 0.5), 0 8px 10px -6px rgba(167, 139, 250, 0.5)',
|
||
'&:hover': {
|
||
boxShadow:
|
||
'0 20px 35px -5px rgba(167, 139, 250, 0.6), 0 12px 15px -6px rgba(167, 139, 250, 0.6)',
|
||
},
|
||
}}
|
||
onClick={handleCreateEvent}
|
||
>
|
||
<Add sx={{ color: 'white', fontSize: { xs: 24, sm: 32 } }} />
|
||
</Fab>
|
||
</Box>
|
||
</>
|
||
);
|
||
}
|