327 lines
9.2 KiB
TypeScript
327 lines
9.2 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 = () => {
|
|
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={() => {
|
|
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 };
|