307 lines
8.9 KiB
TypeScript
307 lines
8.9 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
import {
|
|
Box,
|
|
Container,
|
|
Typography,
|
|
Paper,
|
|
Grid,
|
|
Button,
|
|
alpha,
|
|
GlobalStyles,
|
|
} from '@mui/material';
|
|
import { useTheme } from '@mui/material/styles';
|
|
import ConstructionIcon from '@mui/icons-material/Construction';
|
|
import RocketLaunchIcon from '@mui/icons-material/RocketLaunch';
|
|
import { Beta } from '../components/ui/Beta';
|
|
|
|
interface BetaPageProps {
|
|
children?: React.ReactNode;
|
|
title?: string;
|
|
subtitle?: string;
|
|
returnPath?: string;
|
|
returnLabel?: string;
|
|
onReturn?: () => void;
|
|
}
|
|
|
|
const BetaPage: React.FC<BetaPageProps> = ({
|
|
children,
|
|
title = 'Coming Soon',
|
|
subtitle = 'This page is currently in development',
|
|
returnPath = '/',
|
|
returnLabel = 'Return to Backstory',
|
|
onReturn,
|
|
}) => {
|
|
const theme = useTheme();
|
|
const [showSparkle, setShowSparkle] = useState<boolean>(false);
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
if (!children) {
|
|
children = (
|
|
<Box sx={{ width: '100%', display: 'flex', justifyContent: 'center' }}>
|
|
<Typography>
|
|
The page you requested (<b>{location.pathname.replace(/^\//, '')}</b>) is not yet ready.
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
// Enhanced sparkle effect for background elements
|
|
const [sparkles, setSparkles] = useState<
|
|
Array<{
|
|
id: number;
|
|
x: number;
|
|
y: number;
|
|
size: number;
|
|
opacity: number;
|
|
duration: number;
|
|
delay: number;
|
|
}>
|
|
>([]);
|
|
|
|
useEffect(() => {
|
|
// Generate sparkle elements with random properties
|
|
const newSparkles = Array.from({ length: 30 }).map((_, index) => ({
|
|
id: index,
|
|
x: Math.random() * 100,
|
|
y: Math.random() * 100,
|
|
size: 2 + Math.random() * 5,
|
|
opacity: 0.3 + Math.random() * 0.7,
|
|
duration: 2 + Math.random() * 4,
|
|
delay: Math.random() * 3,
|
|
}));
|
|
|
|
setSparkles(newSparkles);
|
|
|
|
// Show main sparkle effect after a short delay
|
|
const timer = setTimeout(() => {
|
|
setShowSparkle(true);
|
|
}, 500);
|
|
|
|
return () => clearTimeout(timer);
|
|
}, []);
|
|
|
|
const handleReturn = (): void => {
|
|
if (onReturn) {
|
|
onReturn();
|
|
} else if (returnPath) {
|
|
navigate(returnPath);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
minHeight: '100%',
|
|
width: '100%',
|
|
position: 'relative',
|
|
overflow: 'hidden',
|
|
bgcolor: theme.palette.background.default,
|
|
pt: 8,
|
|
pb: 6,
|
|
}}
|
|
>
|
|
{/* Animated background elements */}
|
|
<Box
|
|
sx={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
zIndex: 0,
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{sparkles.map(sparkle => (
|
|
<Box
|
|
key={sparkle.id}
|
|
sx={{
|
|
position: 'absolute',
|
|
left: `${sparkle.x}%`,
|
|
top: `${sparkle.y}%`,
|
|
width: sparkle.size,
|
|
height: sparkle.size,
|
|
borderRadius: '50%',
|
|
bgcolor: alpha(theme.palette.primary.main, sparkle.opacity),
|
|
boxShadow: `0 0 ${sparkle.size * 2}px ${alpha(
|
|
theme.palette.primary.main,
|
|
sparkle.opacity
|
|
)}`,
|
|
animation: `float ${sparkle.duration}s ease-in-out ${sparkle.delay}s infinite alternate`,
|
|
}}
|
|
/>
|
|
))}
|
|
</Box>
|
|
|
|
<Container maxWidth="lg" sx={{ position: 'relative', zIndex: 2 }}>
|
|
<Grid container spacing={4} direction="column" alignItems="center">
|
|
<Grid size={{ xs: 12 }} sx={{ textAlign: 'center', mb: 2 }}>
|
|
<Typography
|
|
variant="h2"
|
|
component="h1"
|
|
gutterBottom
|
|
sx={{
|
|
fontWeight: 'bold',
|
|
color: theme.palette.primary.main,
|
|
textShadow: `0 0 10px ${alpha(theme.palette.primary.main, 0.3)}`,
|
|
animation: showSparkle ? 'titleGlow 3s ease-in-out infinite alternate' : 'none',
|
|
}}
|
|
>
|
|
{title}
|
|
</Typography>
|
|
|
|
<Typography variant="h5" component="h2" color="textSecondary" sx={{ mb: 6 }}>
|
|
{subtitle}
|
|
</Typography>
|
|
</Grid>
|
|
|
|
<Grid size={{ xs: 12, md: 10, lg: 8 }} sx={{ mb: 4 }}>
|
|
<Paper
|
|
elevation={8}
|
|
sx={{
|
|
p: { xs: 3, md: 5 },
|
|
borderRadius: 2,
|
|
bgcolor: alpha(theme.palette.background.paper, 0.8),
|
|
backdropFilter: 'blur(8px)',
|
|
boxShadow: `0 8px 32px ${alpha(theme.palette.primary.main, 0.15)}`,
|
|
border: `1px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
|
position: 'relative',
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{/* Construction icon */}
|
|
<Box
|
|
sx={{
|
|
position: 'absolute',
|
|
top: -15,
|
|
right: -15,
|
|
bgcolor: theme.palette.warning.main,
|
|
color: theme.palette.warning.contrastText,
|
|
borderRadius: '50%',
|
|
p: 2,
|
|
boxShadow: 3,
|
|
transform: 'rotate(15deg)',
|
|
}}
|
|
>
|
|
<ConstructionIcon fontSize="large" />
|
|
</Box>
|
|
|
|
{/* Content */}
|
|
<Box sx={{ mt: 3, mb: 3 }}>
|
|
{children || (
|
|
<Box sx={{ textAlign: 'center', py: 4 }}>
|
|
<RocketLaunchIcon
|
|
fontSize="large"
|
|
color="primary"
|
|
sx={{
|
|
fontSize: 80,
|
|
mb: 2,
|
|
animation: 'rocketWobble 3s ease-in-out infinite',
|
|
}}
|
|
/>
|
|
<Typography>
|
|
We're working hard to bring you this exciting new feature!
|
|
</Typography>
|
|
<Typography color="textSecondary" sx={{ mt: 1 }}>
|
|
Check back soon for updates.
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
<Beta
|
|
adaptive={false}
|
|
sx={{
|
|
opacity: 0.5,
|
|
left: '-72px',
|
|
'& > div': {
|
|
paddingRight: '30px',
|
|
background: 'gold',
|
|
color: '#808080',
|
|
},
|
|
}}
|
|
onClick={(): void => {
|
|
navigate('/docs/beta');
|
|
}}
|
|
/>
|
|
</Box>
|
|
|
|
{/* Return button */}
|
|
<Box sx={{ mt: 4, textAlign: 'center' }}>
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
size="large"
|
|
onClick={handleReturn}
|
|
sx={{
|
|
px: 4,
|
|
py: 1,
|
|
borderRadius: 4,
|
|
boxShadow: `0 4px 14px ${alpha(theme.palette.primary.main, 0.4)}`,
|
|
'&:hover': {
|
|
boxShadow: `0 6px 20px ${alpha(theme.palette.primary.main, 0.6)}`,
|
|
},
|
|
}}
|
|
>
|
|
{returnLabel}
|
|
</Button>
|
|
</Box>
|
|
</Paper>
|
|
</Grid>
|
|
</Grid>
|
|
</Container>
|
|
|
|
{/* Global styles added with MUI's GlobalStyles component */}
|
|
<GlobalStyles
|
|
styles={{
|
|
'@keyframes float': {
|
|
'0%': {
|
|
transform: 'translateY(0) scale(1)',
|
|
},
|
|
'100%': {
|
|
transform: 'translateY(-20px) scale(1.1)',
|
|
},
|
|
},
|
|
'@keyframes sparkleFloat': {
|
|
'0%': {
|
|
transform: 'translateY(0) scale(1)',
|
|
opacity: 0.7,
|
|
},
|
|
'50%': {
|
|
opacity: 1,
|
|
},
|
|
'100%': {
|
|
transform: 'translateY(-15px) scale(1.2)',
|
|
opacity: 0.7,
|
|
},
|
|
},
|
|
'@keyframes titleGlow': {
|
|
'0%': {
|
|
textShadow: `0 0 10px ${alpha(theme.palette.primary.main, 0.3)}`,
|
|
},
|
|
'100%': {
|
|
textShadow: `0 0 25px ${alpha(theme.palette.primary.main, 0.7)}, 0 0 40px ${alpha(
|
|
theme.palette.primary.main,
|
|
0.4
|
|
)}`,
|
|
},
|
|
},
|
|
'@keyframes rocketWobble': {
|
|
'0%': {
|
|
transform: 'translateY(0) rotate(0deg)',
|
|
},
|
|
'50%': {
|
|
transform: 'translateY(-10px) rotate(3deg)',
|
|
},
|
|
'100%': {
|
|
transform: 'translateY(0) rotate(-2deg)',
|
|
},
|
|
},
|
|
}}
|
|
/>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export { BetaPage };
|