Update style to match other sites
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

This commit is contained in:
2026-03-07 18:35:51 -05:00
parent d26126c9d3
commit 9f952d7e54
20 changed files with 837 additions and 791 deletions

View File

@@ -4,7 +4,14 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Maps Service</title>
<title>StrafesNET | Maps</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Barlow:wght@700&display=swap" rel="stylesheet" />
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background-color: #09090b; }
</style>
</head>
<body>
<div id="root"></div>

View File

@@ -1,5 +1,5 @@
import { Routes, Route } from 'react-router-dom'
import { ThemeProvider } from '@mui/material'
import { ThemeProvider, CssBaseline } from '@mui/material'
import { theme } from '@/app/lib/theme'
// Pages
@@ -22,6 +22,7 @@ import NotFound from '@/app/not-found/page'
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/maps" element={<MapsPage />} />

View File

@@ -0,0 +1,34 @@
import { Box } from '@mui/material';
import { surface } from '@/app/lib/colors';
const AnimatedBackground: React.FC = () => {
return (
<Box
sx={{
position: 'fixed',
inset: 0,
zIndex: 0,
overflow: 'hidden',
pointerEvents: 'none',
background: `
radial-gradient(ellipse 80% 60% at 50% -20%, rgba(124, 58, 237, 0.08) 0%, transparent 100%),
radial-gradient(ellipse 60% 40% at 80% 80%, rgba(34, 211, 238, 0.04) 0%, transparent 100%),
${surface.base}
`,
}}
>
<Box
sx={{
position: 'absolute',
inset: 0,
opacity: 0.03,
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`,
backgroundRepeat: 'repeat',
backgroundSize: '128px 128px',
}}
/>
</Box>
);
};
export default AnimatedBackground;

View File

@@ -75,7 +75,7 @@ export function Carousel<T extends CarouselItem>({ title, items, renderItem, vie
sx={{
color: 'primary.main',
'&:hover': {
backgroundColor: 'rgba(99, 102, 241, 0.1)',
backgroundColor: 'rgba(167, 139, 250, 0.08)',
},
}}
>
@@ -93,12 +93,12 @@ export function Carousel<T extends CarouselItem>({ title, items, renderItem, vie
transform: 'translateY(-50%)',
zIndex: 2,
backgroundColor: 'background.paper',
border: '1px solid rgba(99, 102, 241, 0.2)',
border: '1px solid rgba(167, 139, 250, 0.15)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
'&:hover': {
backgroundColor: 'background.paper',
borderColor: 'rgba(99, 102, 241, 0.4)',
boxShadow: '0 8px 20px rgba(99, 102, 241, 0.3)',
borderColor: 'rgba(167, 139, 250, 0.3)',
boxShadow: '0 8px 20px rgba(167, 139, 250, 0.2)',
},
visibility: scrollPosition <= 5 ? 'hidden' : 'visible',
}}
@@ -146,12 +146,12 @@ export function Carousel<T extends CarouselItem>({ title, items, renderItem, vie
transform: 'translateY(-50%)',
zIndex: 2,
backgroundColor: 'background.paper',
border: '1px solid rgba(99, 102, 241, 0.2)',
border: '1px solid rgba(167, 139, 250, 0.15)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
'&:hover': {
backgroundColor: 'background.paper',
borderColor: 'rgba(99, 102, 241, 0.4)',
boxShadow: '0 8px 20px rgba(99, 102, 241, 0.3)',
borderColor: 'rgba(167, 139, 250, 0.3)',
boxShadow: '0 8px 20px rgba(167, 139, 250, 0.2)',
},
visibility: scrollPosition >= maxScroll - 5 ? 'hidden' : 'visible',
}}

View File

@@ -6,6 +6,7 @@ import {
Tab,
keyframes
} from "@mui/material";
import { semantic } from "@/app/lib/colors";
import CommentsTabPanel from './CommentsTabPanel';
import AuditEventsTabPanel from './AuditEventsTabPanel';
import { AuditEvent, AuditEventType } from "@/app/ts/AuditEvent";
@@ -75,7 +76,7 @@ export default function CommentsAndAuditSection({
width: 8,
height: 8,
borderRadius: '50%',
backgroundColor: '#ff9800',
backgroundColor: semantic.warning,
animation: `${pulse} 2s ease-in-out infinite`
}}
/>

View File

@@ -1,6 +1,7 @@
import { Link } from "react-router-dom"
import { useState, useRef } from "react";
import { useState } from "react";
import { useUser } from "@/app/hooks/useUser";
import { primary, text, border, fill } from "@/app/lib/colors";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
@@ -10,51 +11,44 @@ import Box from "@mui/material/Box";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import IconButton from "@mui/material/IconButton";
import Avatar from "@mui/material/Avatar";
import MenuIcon from "@mui/icons-material/Menu";
import Drawer from "@mui/material/Drawer";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import LoginIcon from "@mui/icons-material/Login";
import useMediaQuery from "@mui/material/useMediaQuery";
import { useTheme } from "@mui/material/styles";
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
interface HeaderButton {
name: string;
href: string;
}
const navItems: HeaderButton[] = [
const navItems = [
{ name: "Home", href: "/" },
{ name: "Submissions", href: "/submissions" },
{ name: "Mapfixes", href: "/mapfixes" },
{ name: "Maps", href: "/maps" },
];
function HeaderButton(header: HeaderButton) {
return (
<Button color="inherit" component={Link} to={header.href}>
{header.name}
</Button>
);
}
const quickLinks = [
{ name: "Bhop", href: "https://www.roblox.com/games/5315046213" },
{ name: "Bhop Maptest", href: "https://www.roblox.com/games/517201717" },
{ name: "Surf", href: "https://www.roblox.com/games/5315066937" },
{ name: "Surf Maptest", href: "https://www.roblox.com/games/517206177" },
{ name: "Fly Trials", href: "https://www.roblox.com/games/12591611759" },
{ name: "Fly Trials Maptest", href: "https://www.roblox.com/games/12724901535" },
];
export default function Header() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [mobileOpen, setMobileOpen] = useState(false);
const hasAnimated = useRef(false);
const getAuthUrl = () => {
const hostname = window.location.hostname;
// Production only
if (hostname === 'maps.strafes.net') {
return 'https://auth.strafes.net';
}
// Default to staging (works for staging.strafes.net and localhost)
return 'https://auth.staging.strafes.net';
};
@@ -86,9 +80,20 @@ export default function Header() {
setQuickLinksAnchor(null);
};
// Mobile navigation drawer content
const drawer = (
<Box onClick={handleDrawerToggle} sx={{ textAlign: 'center' }}>
<Box onClick={handleDrawerToggle} sx={{ textAlign: 'center', pt: 2 }}>
<Typography
variant="h6"
sx={{
fontFamily: '"Barlow", sans-serif',
fontWeight: 700,
color: text.primary,
mb: 2,
userSelect: 'none',
}}
>
StrafesNET
</Typography>
<List>
{navItems.map((item) => (
<ListItem key={item.name} disablePadding>
@@ -100,7 +105,7 @@ export default function Header() {
{isLoggedIn && user && (
<ListItem disablePadding>
<ListItemButton component={Link} to="/submit" sx={{ textAlign: 'center' }}>
<ListItemText primary="Submit Map" sx={{ color: 'success.main' }} />
<ListItemText primary="Submit Map" sx={{ color: primary.main }} />
</ListItemButton>
</ListItem>
)}
@@ -122,34 +127,23 @@ export default function Header() {
</Box>
);
const quickLinks = [
{ name: "Bhop", href: "https://www.roblox.com/games/5315046213" },
{ name: "Bhop Maptest", href: "https://www.roblox.com/games/517201717" },
{ name: "Surf", href: "https://www.roblox.com/games/5315066937" },
{ name: "Surf Maptest", href: "https://www.roblox.com/games/517206177" },
{ name: "Fly Trials", href: "https://www.roblox.com/games/12591611759" },
{ name: "Fly Trials Maptest", href: "https://www.roblox.com/games/12724901535" },
];
return (
<AppBar position="static">
<Toolbar sx={{ py: 1 }}>
<AppBar position="sticky">
<Toolbar sx={{ gap: 1 }}>
{isMobile && (
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
sx={{ mr: 2 }}
sx={{ color: primary.main }}
>
<MenuIcon />
</IconButton>
)}
{/* Desktop navigation */}
{/* Brand */}
{!isMobile && (
<Box display="flex" flexGrow={1} gap={1} alignItems="center">
{/* Logo/Brand */}
<Box
component={Link}
to="/"
@@ -157,90 +151,64 @@ export default function Header() {
mr: 4,
textDecoration: 'none',
display: 'flex',
alignItems: 'center',
position: 'relative',
overflow: 'hidden',
'@keyframes speedLine': {
'0%': {
transform: 'translateX(-50px) scaleX(0.5)',
opacity: 0,
},
'40%': {
opacity: 0.8,
transform: 'translateX(0px) scaleX(1)',
},
'100%': {
opacity: 0,
transform: 'translateX(30px) scaleX(0.7)',
},
},
'@keyframes logoReveal': {
'0%': {
opacity: 0,
transform: 'translateX(-10px)',
filter: 'blur(2px)',
},
'100%': {
opacity: 1,
transform: 'translateX(0px)',
filter: 'blur(0px)',
},
},
'&::before, &::after': {
content: '""',
position: 'absolute',
left: 0,
width: '100%',
height: '2px',
background: 'linear-gradient(90deg, transparent 10%, rgba(59, 130, 246, 0.8) 50%, transparent 90%)',
pointerEvents: 'none',
animation: !hasAnimated.current ? 'speedLine 0.6s ease-out forwards' : 'none',
opacity: !hasAnimated.current ? 0 : undefined,
},
'&::before': {
top: '35%',
animationDelay: !hasAnimated.current ? '0s' : undefined,
},
'&::after': {
top: '65%',
animationDelay: !hasAnimated.current ? '0.08s' : undefined,
},
alignItems: 'baseline',
gap: 1,
}}
>
<Box
sx={{
position: 'absolute',
top: '50%',
left: 0,
width: '100%',
height: '1px',
background: 'linear-gradient(90deg, transparent 10%, rgba(139, 92, 246, 0.6) 50%, transparent 90%)',
animation: !hasAnimated.current ? 'speedLine 0.6s ease-out forwards' : 'none',
animationDelay: !hasAnimated.current ? '0.04s' : '0s',
opacity: !hasAnimated.current ? 0 : undefined,
pointerEvents: 'none',
}}
/>
<Typography
variant="h6"
sx={{
color: 'text.primary',
fontFamily: '"Barlow", sans-serif',
fontWeight: 700,
letterSpacing: '-0.01em',
fontSize: '1.125rem',
position: 'relative',
zIndex: 1,
opacity: !hasAnimated.current ? 0 : 1,
animation: !hasAnimated.current ? 'logoReveal 0.5s ease-out forwards' : 'none',
animationDelay: !hasAnimated.current ? '0.5s' : '0s',
}}
onAnimationEnd={() => {
hasAnimated.current = true;
letterSpacing: '-0.02em',
color: text.primary,
lineHeight: 1,
userSelect: 'none',
}}
>
StrafesNET
</Typography>
<Typography
variant="body2"
sx={{
fontWeight: 500,
color: text.muted,
userSelect: 'none',
}}
>
Maps
</Typography>
</Box>
)}
{isMobile && (
<Box
component={Link}
to="/"
sx={{ textDecoration: 'none', display: 'flex', alignItems: 'baseline', gap: 0.75 }}
>
<Typography
variant="h6"
sx={{
fontFamily: '"Barlow", sans-serif',
fontWeight: 700,
color: text.primary,
lineHeight: 1,
userSelect: 'none',
fontSize: '1rem',
}}
>
StrafesNET
</Typography>
<Typography variant="caption" sx={{ fontWeight: 500, color: text.muted, userSelect: 'none' }}>
Maps
</Typography>
</Box>
)}
{/* Desktop nav items */}
{!isMobile && (
<>
{navItems.map((item) => (
<Button
key={item.name}
@@ -248,40 +216,44 @@ export default function Header() {
component={Link}
to={item.href}
sx={{
px: 2,
py: 1,
borderRadius: 1.5,
fontSize: '0.9rem',
px: 1.5,
py: 0.75,
borderRadius: 2,
fontSize: '0.85rem',
fontWeight: 500,
color: 'text.secondary',
transition: 'all 0.2s',
color: text.tertiary,
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(59, 130, 246, 0.08)',
color: 'text.primary',
backgroundColor: fill.primaryHover,
color: text.primary,
},
}}
>
{item.name}
</Button>
))}
<Box sx={{ flexGrow: 1 }} />
</>
)}
<Box sx={{ flex: 1 }} />
{/* Quick Links Dropdown */}
{!isMobile && (
<Box>
<Button
color="inherit"
endIcon={<ArrowDropDownIcon />}
onClick={handleQuickLinksOpen}
sx={{
px: 2,
mr: 1,
borderRadius: 1.5,
fontSize: '0.9rem',
px: 1.5,
borderRadius: 2,
fontSize: '0.85rem',
fontWeight: 500,
color: 'text.secondary',
transition: 'all 0.2s',
color: text.dim,
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(59, 130, 246, 0.08)',
color: 'text.primary',
backgroundColor: fill.primaryHover,
color: text.tertiary,
},
}}
>
@@ -293,20 +265,13 @@ export default function Header() {
onClose={handleQuickLinksClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
sx={{
'& .MuiMenu-paper': {
mt: 1.5,
},
}}
sx={{ '& .MuiMenu-paper': { mt: 1 } }}
>
{quickLinks.map(link => (
<MenuItem
key={link.name}
onClick={handleQuickLinksClose}
sx={{
minWidth: 200,
fontSize: '0.9rem',
}}
sx={{ minWidth: 200, fontSize: '0.85rem' }}
component="a"
href={link.href}
target="_blank"
@@ -317,123 +282,91 @@ export default function Header() {
))}
</Menu>
</Box>
</Box>
)}
{/* Spacer for mobile view */}
{isMobile && <Box sx={{ flexGrow: 1 }} />}
{/* Right side of nav */}
<Box display="flex" gap={2} alignItems="center">
{/* Submit + Auth */}
{!isMobile && isLoggedIn && user && (
<Button
variant="contained"
color="primary"
component={Link}
to="/submit"
sx={{
px: 3,
}}
size="small"
sx={{ px: 2 }}
>
Submit Map
</Button>
)}
{!isMobile && isLoggedIn && user ? (
{isLoggedIn && user ? (
<Box display="flex" alignItems="center">
<Button
<IconButton
onClick={handleMenuOpen}
color="inherit"
size="small"
sx={{
textTransform: "none",
borderRadius: 1.5,
px: 1.5,
py: 0.75,
border: '1px solid rgba(255, 255, 255, 0.08)',
transition: 'all 0.2s',
ml: 0.5,
p: 0.5,
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(59, 130, 246, 0.08)',
borderColor: 'rgba(59, 130, 246, 0.3)',
boxShadow: '0 0 12px rgba(167, 139, 250, 0.3)',
},
}}
>
<img
className="avatar"
width={28}
height={28}
<Avatar
src={user.AvatarURL}
alt={user.Username}
style={{
marginRight: 8,
borderRadius: '50%',
sx={{
width: 32,
height: 32,
fontSize: '0.75rem',
fontWeight: 700,
backgroundColor: primary.dark,
color: text.primary,
}}
/>
<Typography variant="body2" sx={{ fontSize: '0.875rem', fontWeight: 500 }}>
{user.Username}
</Typography>
</Button>
>
{user.Username?.slice(0, 2).toUpperCase()}
</Avatar>
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleMenuClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
sx={{
'& .MuiMenu-paper': {
mt: 1.5,
},
}}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "right" }}
sx={{ '& .MuiMenu-paper': { mt: 1 } }}
>
<MenuItem
component="a"
href={getAuthUrl()}
sx={{
fontSize: '0.9rem',
}}
sx={{ fontSize: '0.85rem' }}
>
Manage Account
</MenuItem>
</Menu>
</Box>
) : !isMobile && (
) : (
<Button
variant="outlined"
color="primary"
size="small"
onClick={handleLoginClick}
sx={{
px: 3,
ml: 1,
px: 1.5,
py: 0.75,
minWidth: 0,
borderRadius: 2,
backgroundColor: fill.default,
border: `1px solid ${border.default}`,
color: text.dim,
fontSize: '0.8rem',
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: fill.hover,
borderColor: border.primaryMedium,
color: primary.main,
},
}}
>
<LoginIcon sx={{ fontSize: 16, mr: 0.5 }} />
Login
</Button>
)}
{/* In mobile view, display just the avatar if logged in */}
{isMobile && isLoggedIn && user && (
<IconButton
onClick={handleMenuOpen}
color="inherit"
size="small"
>
<img
className="avatar"
width={32}
height={32}
src={user.AvatarURL}
alt={user.Username}
style={{
borderRadius: '50%',
}}
/>
</IconButton>
)}
</Box>
</Toolbar>
{/* Mobile drawer */}
@@ -441,9 +374,7 @@ export default function Header() {
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true,
}}
ModalProps={{ keepMounted: true }}
sx={{
'& .MuiDrawer-paper': {
boxSizing: 'border-box',

View File

@@ -5,6 +5,7 @@ import {Link} from "react-router-dom";
import {useAssetThumbnail, useUserThumbnail} from "@/app/hooks/useThumbnails";
import {useUsername} from "@/app/hooks/useUsername";
import { getGameName } from "@/app/utils/games";
import { primary, gameColors } from "@/app/lib/colors";
interface MapCardProps {
displayName: string;
@@ -119,13 +120,13 @@ export function MapCard(props: MapCardProps) {
flexWrap: 'wrap',
}}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<Explore sx={{ fontSize: '1rem', color: '#6366f1' }} />
<Explore sx={{ fontSize: '1rem', color: gameColors[props.gameID] || primary.main }} />
<Typography variant="body2" color="text.secondary" fontSize="0.875rem">
{getGameName(props.gameID)}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<Person2 sx={{ fontSize: '1rem', color: '#8b5cf6' }} />
<Person2 sx={{ fontSize: '1rem', color: primary.main }} />
{props.type === 'mapfix' && usernameLoading ? (
<Skeleton variant="text" width={80} />
) : (

View File

@@ -6,6 +6,7 @@ import PendingIcon from '@mui/icons-material/Pending';
import WarningIcon from '@mui/icons-material/Warning';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { Status } from '@/app/ts/Status';
import { semantic } from "@/app/lib/colors";
const pulse = keyframes`
0%, 100% {
@@ -188,8 +189,8 @@ const WorkflowStepper: React.FC<WorkflowStepperProps> = ({ currentStatus, type,
icon: InfoOutlinedIcon,
title: 'Not Yet Submitted',
message: 'Your submission has been created but has not been submitted. Click "Submit" to submit it.',
color: '#2196f3',
bgColor: 'rgba(33, 150, 243, 0.08)'
color: semantic.info,
bgColor: 'rgba(56, 189, 248, 0.08)'
};
}
if (isChangesRequested) {
@@ -197,8 +198,8 @@ const WorkflowStepper: React.FC<WorkflowStepperProps> = ({ currentStatus, type,
icon: WarningIcon,
title: 'Changes Requested',
message: 'Review comments and audit events, make modifications, and submit again.',
color: '#ff9800',
bgColor: 'rgba(255, 152, 0, 0.08)'
color: semantic.warning,
bgColor: 'rgba(251, 191, 36, 0.08)'
};
}
return null;

View File

@@ -1,86 +1,76 @@
import {JSX} from "react";
import {Cancel, CheckCircle, Pending} from "@mui/icons-material";
import {Chip} from "@mui/material";
import { semantic, text } from "@/app/lib/colors";
export const StatusChip = ({status}: { status: number }): JSX.Element => {
let color: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default';
let icon: JSX.Element = <Pending fontSize="small"/>;
let label: string = 'Unknown';
interface StatusConfig {
bg: string;
color: string;
border: string;
icon: JSX.Element;
label: string;
}
function getStatusConfig(status: number): StatusConfig {
const warn = {
bg: `rgba(251, 191, 36, 0.08)`,
color: semantic.warning,
border: `rgba(251, 191, 36, 0.2)`,
};
const info = {
bg: `rgba(56, 189, 248, 0.08)`,
color: semantic.info,
border: `rgba(56, 189, 248, 0.2)`,
};
const success = {
bg: `rgba(74, 222, 128, 0.08)`,
color: semantic.success,
border: `rgba(74, 222, 128, 0.2)`,
};
const error = {
bg: `rgba(248, 113, 113, 0.08)`,
color: semantic.error,
border: `rgba(248, 113, 113, 0.2)`,
};
const gray = {
bg: `rgba(161, 161, 170, 0.08)`,
color: text.tertiary,
border: `rgba(161, 161, 170, 0.2)`,
};
switch (status) {
case 0:
color = 'warning';
icon = <Pending fontSize="small"/>;
label = 'Under Construction';
break;
case 1:
color = 'warning';
icon = <Pending fontSize="small"/>;
label = 'Changes Requested';
break;
case 2:
color = 'info';
icon = <Pending fontSize="small"/>;
label = 'Submitting';
break;
case 3:
color = 'warning';
icon = <CheckCircle fontSize="small"/>;
label = 'Under Review';
break;
case 4:
color = 'warning';
icon = <Pending fontSize="small"/>;
label = 'Script Review';
break;
case 5:
color = 'info';
icon = <Pending fontSize="small"/>;
label = 'Validating';
break;
case 6:
color = 'success';
icon = <CheckCircle fontSize="small"/>;
label = 'Validated';
break;
case 7:
color = 'info';
icon = <Pending fontSize="small"/>;
label = 'Uploading';
break;
case 8:
color = 'success';
icon = <CheckCircle fontSize="small"/>;
label = 'Uploaded';
break;
case 11:
color = 'info';
icon = <Pending fontSize="small"/>;
label = 'Releasing';
break;
case 9:
color = 'error';
icon = <Cancel fontSize="small"/>;
label = 'Rejected';
break;
case 10:
color = 'success';
icon = <CheckCircle fontSize="small"/>;
label = 'Released';
break;
default:
color = 'default';
icon = <Pending fontSize="small"/>;
label = 'Unknown';
break;
case 0: return { ...warn, icon: <Pending fontSize="small" />, label: 'Under Construction' };
case 1: return { ...warn, icon: <Pending fontSize="small" />, label: 'Changes Requested' };
case 2: return { ...info, icon: <Pending fontSize="small" />, label: 'Submitting' };
case 3: return { ...warn, icon: <CheckCircle fontSize="small" />, label: 'Under Review' };
case 4: return { ...warn, icon: <Pending fontSize="small" />, label: 'Script Review' };
case 5: return { ...info, icon: <Pending fontSize="small" />, label: 'Validating' };
case 6: return { ...success, icon: <CheckCircle fontSize="small" />, label: 'Validated' };
case 7: return { ...info, icon: <Pending fontSize="small" />, label: 'Uploading' };
case 8: return { ...success, icon: <CheckCircle fontSize="small" />, label: 'Uploaded' };
case 9: return { ...error, icon: <Cancel fontSize="small" />, label: 'Rejected' };
case 10: return { ...success, icon: <CheckCircle fontSize="small" />, label: 'Released' };
case 11: return { ...info, icon: <Pending fontSize="small" />, label: 'Releasing' };
default: return { ...gray, icon: <Pending fontSize="small" />, label: 'Unknown' };
}
}
export const StatusChip = ({status}: { status: number }): JSX.Element => {
const config = getStatusConfig(status);
return (
<Chip
icon={icon}
label={label}
color={color}
icon={config.icon}
label={config.label}
size="small"
sx={{
backgroundColor: config.bg,
color: config.color,
border: `1px solid ${config.border}`,
'& .MuiChip-icon': {
color: config.color,
},
}}
/>
);
};

View File

@@ -1,8 +1,13 @@
import { Box } from "@mui/material";
import Header from "./header";
import AnimatedBackground from "./AnimatedBackground";
export default function Webpage({children}: Readonly<{children?: React.ReactNode}>) {
return <>
<AnimatedBackground />
<Box sx={{ position: 'relative', zIndex: 1, minHeight: '100vh' }}>
<Header/>
{children}
</Box>
</>
}

View File

@@ -24,10 +24,10 @@
color: var(--text-color);
}
& fieldset {
border-color: rgb(100,100,100);
border-color: rgba(255, 255, 255, 0.1);
}
& span {
color: white;
color: #fafafa;
}
}

View File

@@ -1,37 +1,18 @@
$review-border: 1px solid var(--review-border);
$review-border: 1px solid rgba(255, 255, 255, 0.06);
$form-label-fontsize: 1.3rem;
@mixin border-with-radius {
border: $review-border {
radius: 5px;
radius: 8px;
}
}
:root {
color-scheme: dark;
--header-height: 45px;
--page: rgb(15,15,15);
--header-grad-left: #363b40;
--header-grad-right: #353a40;
--header-button-left: white;
--header-button-right: #b4b4b4;
--header-button-hover: white;
--review-border: rgb(50,50,50);
--text-color: rgb(230,230,230);
--anchor-link-review: #008fd6;
--window-header: rgb(10,10,10);
--comment-highlighted: #ffffd7;
--comment-area: rgb(20,20,20);
--placeholder-text: rgb(80,80,80);
}
body {
font-family: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla";
box-sizing: border-box;
margin: 0;
background-color: var(--page);
--review-border: rgba(255, 255, 255, 0.06);
--text-color: #fafafa;
--placeholder-text: #52525b;
}
button {
@@ -41,10 +22,3 @@ button {
a:active, a:link, a:hover {
text-decoration: none;
}
.spacer {
display: block;
width: 100%;
height: 1px;
background-color: var(--review-border);
}

88
web/src/app/lib/colors.ts Normal file
View File

@@ -0,0 +1,88 @@
// Brand / accent
export const primary = {
main: '#a78bfa',
light: '#c4b5fd',
dark: '#7c3aed',
darker: '#6d28d9',
mid: '#8b5cf6',
} as const;
export const secondary = {
main: '#22d3ee',
light: '#67e8f9',
dark: '#0891b2',
} as const;
// Semantic
export const semantic = {
error: '#f87171',
warning: '#fbbf24',
success: '#4ade80',
info: '#38bdf8',
} as const;
// Surfaces
export const surface = {
base: '#09090b',
raised: '#18181b',
raisedAlpha: 'rgba(24, 24, 27, 0.6)',
raisedSolid: 'rgba(24, 24, 27, 0.95)',
overlay: 'rgba(0, 0, 0, 0.6)',
appBar: 'rgba(9, 9, 11, 0.8)',
} as const;
// Text hierarchy (lightest -> dimmest)
export const text = {
primary: '#fafafa',
secondary: '#d4d4d8',
tertiary: '#a1a1aa',
muted: '#71717a',
dim: '#52525b',
faint: '#3f3f46',
} as const;
// Borders & dividers
export const border = {
subtle: 'rgba(255, 255, 255, 0.04)',
default: 'rgba(255, 255, 255, 0.06)',
medium: 'rgba(255, 255, 255, 0.08)',
strong: 'rgba(255, 255, 255, 0.1)',
primarySubtle: 'rgba(167, 139, 250, 0.1)',
primaryDefault: 'rgba(167, 139, 250, 0.15)',
primaryMedium: 'rgba(167, 139, 250, 0.2)',
primaryStrong: 'rgba(167, 139, 250, 0.3)',
} as const;
// Interactive surface fills
export const fill = {
subtle: 'rgba(255, 255, 255, 0.03)',
default: 'rgba(255, 255, 255, 0.04)',
hover: 'rgba(255, 255, 255, 0.06)',
primaryHover: 'rgba(167, 139, 250, 0.06)',
primaryActive: 'rgba(167, 139, 250, 0.08)',
primaryStrong: 'rgba(167, 139, 250, 0.1)',
} as const;
// Gradient presets
export const gradients = {
brand: `linear-gradient(135deg, ${primary.dark}, ${secondary.main})`,
brandText: `linear-gradient(135deg, ${primary.light} 0%, ${secondary.main} 100%)`,
button: `linear-gradient(135deg, ${primary.dark} 0%, ${primary.main} 100%)`,
buttonHover: `linear-gradient(135deg, ${primary.darker} 0%, ${primary.mid} 100%)`,
titleText: `linear-gradient(135deg, ${text.primary} 0%, ${text.tertiary} 100%)`,
} as const;
// Glow / shadow presets
export const glow = {
brand: '0 0 12px rgba(124, 58, 237, 0.5)',
brandStrong: '0 0 20px rgba(124, 58, 237, 0.8), 0 0 40px rgba(34, 211, 238, 0.2)',
button: '0 0 20px rgba(124, 58, 237, 0.3)',
palette: '0 24px 80px rgba(0, 0, 0, 0.5), 0 0 60px rgba(124, 58, 237, 0.1)',
} as const;
// Game colors
export const gameColors: Record<number, string> = {
1: '#a78bfa', // Bhop - purple
2: '#22d3ee', // Surf - cyan
5: '#fbbf24', // Fly Trials - yellow
} as const;

View File

@@ -1,133 +1,263 @@
import {createTheme} from "@mui/material";
import { createTheme } from '@mui/material';
import { primary, secondary, semantic, surface, text, border, fill, gradients, glow } from './colors';
export const theme = createTheme({
cssVariables: {
colorSchemeSelector: 'class',
},
colorSchemes: {
dark: true,
},
defaultColorScheme: 'dark',
palette: {
mode: 'dark',
primary: {
main: '#3b82f6',
dark: '#2563eb',
light: '#60a5fa',
main: primary.main,
light: primary.light,
dark: primary.dark,
},
secondary: {
main: '#8b5cf6',
dark: '#7c3aed',
light: '#a78bfa',
main: secondary.main,
light: secondary.light,
dark: secondary.dark,
},
background: {
default: '#0a0a0a',
paper: '#171717',
default: surface.base,
paper: surface.raised,
},
error: { main: semantic.error },
warning: { main: semantic.warning },
success: { main: semantic.success },
info: { main: semantic.info },
text: {
primary: '#ffffff',
secondary: '#9ca3af',
},
error: {
main: '#ef4444',
light: '#f87171',
dark: '#dc2626',
},
warning: {
main: '#f59e0b',
light: '#fbbf24',
dark: '#d97706',
},
success: {
main: '#10b981',
light: '#34d399',
dark: '#059669',
},
info: {
main: '#3b82f6',
light: '#60a5fa',
dark: '#2563eb',
},
},
typography: {
fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif',
h1: {
fontWeight: 700,
letterSpacing: '-0.025em',
},
h2: {
fontWeight: 700,
letterSpacing: '-0.02em',
},
h3: {
fontWeight: 600,
letterSpacing: '-0.015em',
},
h4: {
fontWeight: 600,
letterSpacing: '-0.01em',
},
h5: {
fontWeight: 600,
},
h6: {
fontWeight: 600,
},
subtitle1: {
fontWeight: 500,
fontSize: '1rem',
},
body1: {
fontSize: '1rem',
lineHeight: 1.7,
},
body2: {
fontSize: '0.875rem',
lineHeight: 1.6,
},
caption: {
fontSize: '0.75rem',
},
button: {
fontWeight: 600,
textTransform: 'none',
letterSpacing: '0.01em',
primary: text.primary,
secondary: text.tertiary,
},
divider: border.default,
},
shape: {
borderRadius: 12,
},
typography: {
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
fontSize: 14,
h1: { fontWeight: 700, letterSpacing: '-0.025em' },
h2: { fontWeight: 700, letterSpacing: '-0.02em' },
h3: { fontWeight: 600, letterSpacing: '-0.015em' },
h4: { fontWeight: 700, letterSpacing: '-0.02em' },
h5: { fontWeight: 700, letterSpacing: '-0.02em' },
h6: { fontWeight: 700, letterSpacing: '-0.01em' },
body1: { fontSize: '1rem', lineHeight: 1.7 },
body2: { fontSize: '0.875rem', lineHeight: 1.6 },
button: { fontWeight: 600, textTransform: 'none' as const },
},
components: {
MuiCssBaseline: {
styleOverrides: {
body: {
backgroundColor: surface.base,
backgroundImage: 'radial-gradient(ellipse 80% 50% at 50% -20%, rgba(120, 60, 255, 0.15), transparent)',
},
},
},
MuiAppBar: {
styleOverrides: {
root: {
backgroundColor: surface.appBar,
backdropFilter: 'blur(16px)',
boxShadow: 'none',
borderBottom: `1px solid ${border.default}`,
},
},
},
MuiPaper: {
styleOverrides: {
root: {
backgroundImage: 'none',
backgroundColor: surface.raisedAlpha,
backdropFilter: 'blur(12px)',
border: `1px solid ${border.default}`,
},
},
},
MuiCard: {
styleOverrides: {
root: {
borderRadius: 12,
overflow: 'hidden',
backgroundColor: '#171717',
border: '1px solid rgba(255, 255, 255, 0.08)',
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.3)',
backgroundColor: surface.raisedAlpha,
backdropFilter: 'blur(12px)',
border: `1px solid ${border.default}`,
boxShadow: 'none',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
transform: 'translateY(-4px)',
border: '1px solid rgba(59, 130, 246, 0.4)',
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(59, 130, 246, 0.2)',
border: `1px solid ${border.primaryMedium}`,
boxShadow: glow.button,
},
},
},
},
MuiCardMedia: {
styleOverrides: {
root: {
transition: 'transform 0.3s',
},
},
},
MuiCardContent: {
styleOverrides: {
root: {
padding: 16,
'&:last-child': {
paddingBottom: 16,
'&:last-child': { paddingBottom: 16 },
},
},
},
MuiTableContainer: {
styleOverrides: {
root: {
backgroundImage: 'none',
borderRadius: 12,
},
},
},
MuiTableHead: {
styleOverrides: {
root: {
'& .MuiTableCell-head': {
backgroundColor: fill.subtle,
color: text.tertiary,
fontWeight: 600,
fontSize: '0.75rem',
textTransform: 'uppercase' as const,
letterSpacing: '0.05em',
borderBottom: `1px solid ${border.default}`,
padding: '12px 16px',
},
},
},
},
MuiTableBody: {
styleOverrides: {
root: {
'& .MuiTableRow-root': {
transition: 'background-color 0.15s ease',
'&:hover': {
backgroundColor: fill.primaryHover,
},
},
'& .MuiTableCell-body': {
borderBottom: `1px solid ${border.subtle}`,
padding: '14px 16px',
fontSize: '0.875rem',
color: text.secondary,
},
},
},
},
MuiTab: {
styleOverrides: {
root: {
textTransform: 'none' as const,
fontWeight: 500,
fontSize: '0.875rem',
minHeight: 44,
'&.Mui-selected': { color: primary.main },
},
},
},
MuiTabs: {
styleOverrides: {
indicator: {
backgroundColor: primary.main,
height: 2,
borderRadius: 1,
},
},
},
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none' as const,
fontWeight: 600,
borderRadius: 8,
},
contained: {
boxShadow: 'none',
},
containedPrimary: {
background: gradients.button,
'&:hover': {
boxShadow: glow.button,
background: gradients.buttonHover,
},
},
containedSuccess: {
backgroundColor: semantic.success,
color: '#000',
'&:hover': {
backgroundColor: '#22c55e',
boxShadow: '0 0 16px rgba(74, 222, 128, 0.3)',
},
},
containedError: {
backgroundColor: semantic.error,
'&:hover': {
backgroundColor: '#ef4444',
boxShadow: '0 0 16px rgba(248, 113, 113, 0.3)',
},
},
containedWarning: {
backgroundColor: semantic.warning,
color: '#000',
'&:hover': {
backgroundColor: '#f59e0b',
boxShadow: '0 0 16px rgba(251, 191, 36, 0.3)',
},
},
containedInfo: {
backgroundColor: semantic.info,
'&:hover': {
backgroundColor: '#0ea5e9',
boxShadow: '0 0 16px rgba(56, 189, 248, 0.3)',
},
},
outlined: {
borderColor: border.primaryStrong,
color: primary.main,
'&:hover': {
borderColor: primary.main,
backgroundColor: fill.primaryActive,
},
},
outlinedSuccess: {
borderColor: `rgba(74, 222, 128, 0.4)`,
color: semantic.success,
'&:hover': {
borderColor: semantic.success,
backgroundColor: 'rgba(74, 222, 128, 0.08)',
},
},
outlinedError: {
borderColor: `rgba(248, 113, 113, 0.4)`,
color: semantic.error,
'&:hover': {
borderColor: semantic.error,
backgroundColor: 'rgba(248, 113, 113, 0.08)',
},
},
outlinedWarning: {
borderColor: `rgba(251, 191, 36, 0.4)`,
color: semantic.warning,
'&:hover': {
borderColor: semantic.warning,
backgroundColor: 'rgba(251, 191, 36, 0.08)',
},
},
outlinedInfo: {
borderColor: `rgba(56, 189, 248, 0.4)`,
color: semantic.info,
'&:hover': {
borderColor: semantic.info,
backgroundColor: 'rgba(56, 189, 248, 0.08)',
},
},
},
},
MuiTextField: {
styleOverrides: {
root: {
'& .MuiOutlinedInput-root': {
borderRadius: 8,
'& fieldset': { borderColor: border.strong },
'&:hover fieldset': { borderColor: border.primaryStrong },
'&.Mui-focused fieldset': { borderColor: primary.main },
},
},
},
@@ -136,134 +266,39 @@ export const theme = createTheme({
styleOverrides: {
root: {
fontWeight: 600,
borderRadius: 6,
fontSize: '0.75rem',
transition: 'all 0.2s ease-in-out',
},
icon: {
marginLeft: '8px',
},
colorError: {
backgroundColor: '#ef4444',
color: '#ffffff',
'& .MuiChip-icon': {
color: '#ffffff',
},
},
colorWarning: {
backgroundColor: '#f59e0b',
color: '#ffffff',
'& .MuiChip-icon': {
color: '#ffffff',
},
},
colorSuccess: {
backgroundColor: '#10b981',
color: '#ffffff',
'& .MuiChip-icon': {
color: '#ffffff',
},
},
colorInfo: {
backgroundColor: '#3b82f6',
color: '#ffffff',
'& .MuiChip-icon': {
color: '#ffffff',
},
borderRadius: 6,
},
},
},
MuiDivider: {
styleOverrides: {
root: {
borderColor: 'rgba(148, 163, 184, 0.1)',
},
},
},
MuiPaper: {
styleOverrides: {
root: {
backgroundImage: 'none',
backgroundColor: '#171717',
},
},
},
MuiButton: {
styleOverrides: {
root: {
borderRadius: 8,
fontWeight: 600,
textTransform: 'none',
padding: '10px 24px',
transition: 'all 0.2s ease-in-out',
},
contained: {
boxShadow: 'none',
'&:hover': {
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
transform: 'translateY(-1px)',
},
},
containedPrimary: {
background: '#3b82f6',
'&:hover': {
background: '#2563eb',
},
},
outlined: {
borderWidth: '1.5px',
'&:hover': {
borderWidth: '1.5px',
backgroundColor: 'rgba(59, 130, 246, 0.08)',
},
},
outlinedPrimary: {
borderColor: 'rgba(59, 130, 246, 0.5)',
'&:hover': {
borderColor: '#3b82f6',
backgroundColor: 'rgba(59, 130, 246, 0.08)',
},
},
outlinedSecondary: {
borderColor: 'rgba(139, 92, 246, 0.5)',
'&:hover': {
borderColor: '#8b5cf6',
backgroundColor: 'rgba(139, 92, 246, 0.08)',
},
},
},
},
MuiAppBar: {
styleOverrides: {
root: {
background: 'rgba(10, 10, 10, 0.8)',
backdropFilter: 'blur(12px)',
borderBottom: '1px solid rgba(255, 255, 255, 0.08)',
boxShadow: 'none',
borderColor: border.default,
},
},
},
MuiDrawer: {
styleOverrides: {
paper: {
backgroundColor: '#0a0a0a',
borderRight: '1px solid rgba(255, 255, 255, 0.08)',
backgroundColor: surface.base,
borderRight: `1px solid ${border.default}`,
},
},
},
MuiCircularProgress: {
styleOverrides: {
root: {
color: '#3b82f6',
color: primary.main,
},
},
},
MuiIconButton: {
styleOverrides: {
root: {
transition: 'all 0.2s ease-in-out',
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(59, 130, 246, 0.1)',
backgroundColor: fill.primaryActive,
},
},
},
@@ -271,11 +306,11 @@ export const theme = createTheme({
MuiLink: {
styleOverrides: {
root: {
color: '#60a5fa',
color: primary.light,
textDecoration: 'none',
transition: 'color 0.2s ease-in-out',
transition: 'color 0.15s ease',
'&:hover': {
color: '#3b82f6',
color: primary.main,
textDecoration: 'underline',
},
},
@@ -284,24 +319,24 @@ export const theme = createTheme({
MuiMenu: {
styleOverrides: {
paper: {
background: '#171717',
backdropFilter: 'blur(12px)',
border: '1px solid rgba(255, 255, 255, 0.08)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.4)',
background: surface.raisedSolid,
backdropFilter: 'blur(16px)',
border: `1px solid ${border.default}`,
boxShadow: glow.palette,
},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
transition: 'all 0.2s ease-in-out',
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(59, 130, 246, 0.1)',
backgroundColor: fill.primaryHover,
},
'&.Mui-selected': {
backgroundColor: 'rgba(59, 130, 246, 0.15)',
backgroundColor: fill.primaryActive,
'&:hover': {
backgroundColor: 'rgba(59, 130, 246, 0.2)',
backgroundColor: fill.primaryStrong,
},
},
},

View File

@@ -391,12 +391,12 @@ export default function MapDetails() {
px: 2,
borderRadius: 1,
transition: 'all 0.2s',
backgroundColor: 'rgba(25, 118, 210, 0.08)',
backgroundColor: 'rgba(167, 139, 250, 0.08)',
borderLeft: '4px solid',
borderColor: 'primary.main',
mb: releasedFixes.length > 0 ? 2 : 0,
'&:hover': {
backgroundColor: 'rgba(25, 118, 210, 0.12)',
backgroundColor: 'rgba(167, 139, 250, 0.12)',
transform: 'translateX(4px)'
},
textDecoration: 'none',

View File

@@ -4,6 +4,7 @@ import Webpage from "@/app/_components/webpage";
import { useTitle } from "@/app/hooks/useTitle";
import HomeIcon from "@mui/icons-material/Home";
import MapIcon from "@mui/icons-material/Map";
import { semantic, surface } from "@/app/lib/colors";
export default function NotFound() {
useTitle("404 - Page Not Found");
@@ -19,7 +20,7 @@ export default function NotFound() {
display: 'flex',
alignItems: 'center',
overflow: 'hidden',
background: 'linear-gradient(to bottom, #0a0a0a 0%, #0f0f0f 100%)',
background: `linear-gradient(to bottom, ${surface.base} 0%, #0f0f0f 100%)`,
}}
>
{/* Subtle Gradient Background */}
@@ -30,7 +31,7 @@ export default function NotFound() {
right: '30%',
width: '500px',
height: '500px',
background: 'radial-gradient(circle, rgba(239, 68, 68, 0.1) 0%, transparent 70%)',
background: `radial-gradient(circle, rgba(248, 113, 113, 0.1) 0%, transparent 70%)`,
borderRadius: '50%',
filter: 'blur(80px)',
}}
@@ -42,7 +43,7 @@ export default function NotFound() {
left: '25%',
width: '450px',
height: '450px',
background: 'radial-gradient(circle, rgba(59, 130, 246, 0.08) 0%, transparent 70%)',
background: `radial-gradient(circle, rgba(167, 139, 250, 0.08) 0%, transparent 70%)`,
borderRadius: '50%',
filter: 'blur(80px)',
}}
@@ -59,7 +60,7 @@ export default function NotFound() {
lineHeight: 1,
mb: 2,
letterSpacing: '-0.04em',
background: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
background: `linear-gradient(135deg, ${semantic.error} 0%, #dc2626 100%)`,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
@@ -173,7 +174,7 @@ export default function NotFound() {
fontSize: '1rem',
'&:hover': {
color: 'primary.main',
background: 'rgba(59, 130, 246, 0.1)',
background: `rgba(167, 139, 250, 0.1)`,
},
}}
>

View File

@@ -26,6 +26,7 @@ import RocketLaunchIcon from "@mui/icons-material/RocketLaunch";
import EmojiEventsIcon from "@mui/icons-material/EmojiEvents";
import { useUser } from "@/app/hooks/useUser";
import { hasAnyReviewerRole } from "@/app/ts/Roles";
import { primary, secondary, semantic, text, border, fill, gradients, glow } from "@/app/lib/colors";
export default function Home() {
useTitle("Home");
@@ -167,8 +168,8 @@ export default function Home() {
}}
>
<Box display="flex" flexDirection="column" alignItems="center" gap={2}>
<CircularProgress size={60} thickness={4} sx={{ color: 'primary.main' }}/>
<Typography variant="h6" color="text.secondary">
<CircularProgress size={48} thickness={3} />
<Typography variant="body2" sx={{ color: text.muted }}>
Loading content...
</Typography>
</Box>
@@ -223,32 +224,32 @@ export default function Home() {
value: totalSubmissions,
label: 'Total Submissions',
sublabel: 'Total maps submitted by the community',
color: '#3b82f6',
gradient: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
color: primary.main,
gradient: gradients.button,
},
{
icon: <BuildIcon sx={{ fontSize: { xs: 48, md: 64 } }} />,
value: totalMapfixes,
label: 'Total Map Fixes',
sublabel: 'Total map fixes submitted by the community',
color: '#8b5cf6',
gradient: 'linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)',
color: secondary.main,
gradient: `linear-gradient(135deg, ${secondary.dark} 0%, ${secondary.main} 100%)`,
},
{
icon: <EmojiEventsIcon sx={{ fontSize: { xs: 48, md: 64 } }} />,
value: releasedSubmissions + releasedMapfixes,
label: 'Total Released',
sublabel: 'Maps & fixes that have been released to the game',
color: '#10b981',
gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
color: semantic.success,
gradient: `linear-gradient(135deg, ${semantic.success} 0%, #059669 100%)`,
},
{
icon: <EmojiEventsIcon sx={{ fontSize: { xs: 48, md: 64 } }} />,
value: releasedSubmissions,
label: 'Released Submissions',
sublabel: 'Approved maps that have been published to the game',
color: '#10b981',
gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
color: semantic.success,
gradient: `linear-gradient(135deg, ${semantic.success} 0%, #059669 100%)`,
},
{
icon: <EmojiEventsIcon sx={{ fontSize: { xs: 48, md: 64 } }} />,
@@ -263,8 +264,8 @@ export default function Home() {
value: submittedSubmissions + submittedMapfixes,
label: 'Under Review',
sublabel: 'Pending approval fixes & submissions',
color: '#f59e0b',
gradient: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)',
color: semantic.warning,
gradient: `linear-gradient(135deg, ${semantic.warning} 0%, #d97706 100%)`,
},
];
@@ -284,8 +285,8 @@ export default function Home() {
}}
>
<Box display="flex" flexDirection="column" alignItems="center" gap={2}>
<CircularProgress size={60} thickness={4} sx={{ color: 'primary.main' }}/>
<Typography variant="h6" color="text.secondary">
<CircularProgress size={48} thickness={3} />
<Typography variant="body2" sx={{ color: text.muted }}>
Loading...
</Typography>
</Box>
@@ -305,7 +306,7 @@ export default function Home() {
return (
<Webpage>
<Box sx={{ width: '100%', bgcolor: 'background.default' }}>
<Box sx={{ width: '100%' }}>
{/* Hero Section */}
<Box
sx={{
@@ -314,10 +315,9 @@ export default function Home() {
display: 'flex',
alignItems: 'center',
overflow: 'hidden',
background: 'radial-gradient(ellipse at top, #0f1419 0%, #0a0a0a 50%, #000000 100%)',
}}
>
{/* Animated Background Elements */}
{/* Animated Background Orbs */}
<Box
sx={{
position: 'absolute',
@@ -325,7 +325,7 @@ export default function Home() {
right: '15%',
width: { xs: '400px', md: '600px' },
height: { xs: '400px', md: '600px' },
background: 'radial-gradient(circle, rgba(59, 130, 246, 0.15) 0%, transparent 70%)',
background: `radial-gradient(circle, rgba(124, 58, 237, 0.12) 0%, transparent 70%)`,
borderRadius: '50%',
filter: 'blur(80px)',
animation: 'float 25s ease-in-out infinite',
@@ -342,11 +342,11 @@ export default function Home() {
left: '10%',
width: { xs: '350px', md: '500px' },
height: { xs: '350px', md: '500px' },
background: 'radial-gradient(circle, rgba(139, 92, 246, 0.12) 0%, transparent 70%)',
background: `radial-gradient(circle, rgba(34, 211, 238, 0.08) 0%, transparent 70%)`,
borderRadius: '50%',
filter: 'blur(80px)',
animation: 'float-reverse 30s ease-in-out infinite',
'@keyframes float-reverse': {
animation: 'floatReverse 30s ease-in-out infinite',
'@keyframes floatReverse': {
'0%, 100%': { transform: 'translate(0, 0) scale(1)' },
'50%': { transform: 'translate(-30px, 30px) scale(1.15)' },
},
@@ -359,8 +359,8 @@ export default function Home() {
position: 'absolute',
inset: 0,
backgroundImage: `
linear-gradient(rgba(59, 130, 246, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(59, 130, 246, 0.03) 1px, transparent 1px)
linear-gradient(rgba(124, 58, 237, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(124, 58, 237, 0.03) 1px, transparent 1px)
`,
backgroundSize: '60px 60px',
maskImage: 'radial-gradient(ellipse at center, black 20%, transparent 70%)',
@@ -376,7 +376,7 @@ export default function Home() {
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(59, 130, 246, 0.3) 50%, transparent 100%)',
background: `linear-gradient(90deg, transparent 0%, rgba(124, 58, 237, 0.3) 50%, transparent 100%)`,
opacity: 0.5,
}}
/>
@@ -387,7 +387,7 @@ export default function Home() {
left: 0,
right: 0,
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(139, 92, 246, 0.3) 50%, transparent 100%)',
background: `linear-gradient(90deg, transparent 0%, rgba(34, 211, 238, 0.2) 50%, transparent 100%)`,
opacity: 0.5,
}}
/>
@@ -412,7 +412,7 @@ export default function Home() {
fontWeight: 700,
letterSpacing: '0.2em',
textTransform: 'uppercase',
color: 'primary.main',
color: primary.main,
mb: 3,
display: 'block',
opacity: 0.9,
@@ -424,16 +424,16 @@ export default function Home() {
<Typography
variant="h1"
sx={{
fontFamily: '"Barlow", sans-serif',
fontSize: { xs: '3.5rem', sm: '5rem', md: '7rem', lg: '8rem' },
fontWeight: 900,
lineHeight: 0.95,
mb: 2,
letterSpacing: '-0.04em',
background: 'linear-gradient(135deg, #60a5fa 0%, #a78bfa 50%, #c084fc 100%)',
background: gradients.brandText,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
textShadow: '0 0 80px rgba(59, 130, 246, 0.3)',
}}
>
StrafesNET
@@ -442,7 +442,7 @@ export default function Home() {
<Typography
variant="h2"
sx={{
color: 'text.primary',
color: text.primary,
fontSize: { xs: '1.75rem', sm: '2.25rem', md: '3rem' },
fontWeight: 700,
letterSpacing: '-0.02em',
@@ -456,7 +456,7 @@ export default function Home() {
<Typography
variant="h6"
sx={{
color: 'text.secondary',
color: text.tertiary,
mb: 5,
lineHeight: 1.75,
fontWeight: 400,
@@ -471,7 +471,7 @@ export default function Home() {
</Typography>
</Box>
{/* CTA Buttons - Moved up for better hierarchy */}
{/* CTA Buttons */}
<Box
display="flex"
gap={3}
@@ -493,13 +493,10 @@ export default function Home() {
px: { xs: 4, md: 5 },
py: { xs: 1.75, md: 2.25 },
fontWeight: 700,
background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
boxShadow: '0 8px 32px rgba(59, 130, 246, 0.4)',
boxShadow: glow.button,
borderRadius: 2,
textTransform: 'none',
'&:hover': {
background: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)',
boxShadow: '0 12px 40px rgba(59, 130, 246, 0.6)',
boxShadow: glow.brandStrong,
transform: 'translateY(-2px)',
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
@@ -519,14 +516,9 @@ export default function Home() {
py: { xs: 1.75, md: 2.25 },
fontWeight: 700,
borderWidth: 2,
borderColor: 'rgba(139, 92, 246, 0.5)',
color: '#a78bfa',
borderRadius: 2,
textTransform: 'none',
'&:hover': {
borderWidth: 2,
borderColor: '#a78bfa',
background: 'rgba(139, 92, 246, 0.1)',
transform: 'translateY(-2px)',
},
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
@@ -536,7 +528,7 @@ export default function Home() {
</Button>
</Box>
{/* Stats Section - Completely Redesigned */}
{/* Stats Section */}
<Box
sx={{
animation: 'fadeIn 1.1s ease-out 0.4s both',
@@ -546,7 +538,6 @@ export default function Home() {
},
}}
>
{/* Stats Grid */}
<Box
sx={{
display: 'grid',
@@ -575,22 +566,13 @@ export default function Home() {
cursor: 'pointer',
background: currentStatIndex === index
? `linear-gradient(135deg, ${stat.color}15 0%, ${stat.color}08 100%)`
: 'rgba(17, 17, 17, 0.4)',
: fill.subtle,
backdropFilter: 'blur(10px)',
border: currentStatIndex === index
? `1px solid ${stat.color}40`
: '1px solid rgba(255, 255, 255, 0.05)',
: `1px solid ${border.subtle}`,
borderRadius: 3,
transition: 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)',
'&::before': currentStatIndex === index ? {
content: '""',
position: 'absolute',
inset: -1,
background: stat.gradient,
borderRadius: 3,
opacity: 0.1,
zIndex: -1,
} : {},
'&:hover': {
transform: 'translateY(-8px) scale(1.02)',
background: `linear-gradient(135deg, ${stat.color}20 0%, ${stat.color}10 100%)`,
@@ -599,7 +581,6 @@ export default function Home() {
},
}}
>
{/* Icon */}
<Box
sx={{
display: 'flex',
@@ -623,13 +604,12 @@ export default function Home() {
})}
</Box>
{/* Value */}
<Typography
variant="h4"
sx={{
fontWeight: 900,
fontSize: { xs: '1.75rem', md: '2.25rem' },
color: currentStatIndex === index ? stat.color : 'text.primary',
color: currentStatIndex === index ? stat.color : text.primary,
letterSpacing: '-0.03em',
transition: 'color 0.3s',
lineHeight: 1,
@@ -638,11 +618,10 @@ export default function Home() {
{stat.value}
</Typography>
{/* Label */}
<Typography
variant="caption"
sx={{
color: currentStatIndex === index ? 'text.primary' : 'text.secondary',
color: currentStatIndex === index ? text.primary : text.tertiary,
fontSize: { xs: '0.7rem', md: '0.75rem' },
fontWeight: 600,
textAlign: 'center',
@@ -659,7 +638,6 @@ export default function Home() {
))}
</Box>
{/* Featured Stat Description */}
<Box
key={currentStatIndex}
sx={{
@@ -673,7 +651,7 @@ export default function Home() {
<Typography
variant="body1"
sx={{
color: 'text.secondary',
color: text.muted,
fontSize: { xs: '0.9rem', md: '1rem' },
fontWeight: 500,
maxWidth: '600px',
@@ -708,8 +686,7 @@ export default function Home() {
</Typography>
<Typography
variant="body1"
color="text.secondary"
sx={{ maxWidth: '600px' }}
sx={{ color: text.muted, maxWidth: '600px' }}
>
Discover the newest custom maps created by the community
</Typography>
@@ -740,8 +717,7 @@ export default function Home() {
</Typography>
<Typography
variant="body1"
color="text.secondary"
sx={{ maxWidth: '600px' }}
sx={{ color: text.muted, maxWidth: '600px' }}
>
Community-created map fixes and improvements
</Typography>
@@ -771,8 +747,7 @@ export default function Home() {
</Typography>
<Typography
variant="body1"
color="text.secondary"
sx={{ maxWidth: '600px' }}
sx={{ color: text.muted, maxWidth: '600px' }}
>
Join the community and start contributing today
</Typography>
@@ -791,21 +766,21 @@ export default function Home() {
title: 'Submit Maps',
description: 'Upload your custom bhop and surf maps for review. Maps are evaluated by moderators before being added to the game.',
link: '/submit',
color: '#3b82f6',
color: primary.main,
},
{
icon: <BuildIcon sx={{ fontSize: 48 }} />,
title: 'Submit Fixes',
description: 'Found bugs or issues in existing maps? Submit fixed versions to improve map quality for all players.',
link: '/mapfixes',
color: '#8b5cf6',
color: secondary.main,
},
{
icon: <ListIcon sx={{ fontSize: 48 }} />,
title: 'View Submissions',
description: 'Browse all pending and approved submissions currently in the review queue. Track submission status and feedback.',
link: '/submissions',
color: '#10b981',
color: semantic.success,
},
].map((card, index) => (
<Box
@@ -814,16 +789,17 @@ export default function Home() {
to={card.link}
sx={{
p: 5,
background: 'rgba(23, 23, 23, 0.5)',
borderRadius: 2,
border: '1px solid rgba(255, 255, 255, 0.08)',
background: fill.subtle,
backdropFilter: 'blur(12px)',
borderRadius: 3,
border: `1px solid ${border.default}`,
textDecoration: 'none',
color: 'inherit',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
transform: 'translateY(-4px)',
borderColor: `${card.color}40`,
boxShadow: '0 8px 24px rgba(0, 0, 0, 0.4)',
boxShadow: `0 8px 24px rgba(0, 0, 0, 0.4)`,
'& .icon-box': {
background: `${card.color}30`,
},
@@ -835,8 +811,8 @@ export default function Home() {
sx={{
display: 'inline-flex',
p: 2,
borderRadius: 1.5,
background: `${card.color}20`,
borderRadius: 2,
background: `${card.color}15`,
mb: 3,
color: card.color,
transition: 'background 0.3s',
@@ -847,7 +823,7 @@ export default function Home() {
<Typography
variant="h5"
sx={{
fontWeight: 600,
fontWeight: 700,
mb: 1.5,
letterSpacing: '-0.01em',
}}
@@ -857,7 +833,7 @@ export default function Home() {
<Typography
variant="body2"
sx={{
color: 'text.secondary',
color: text.muted,
lineHeight: 1.7,
}}
>
@@ -874,8 +850,7 @@ export default function Home() {
sx={{
position: 'relative',
py: 12,
background: '#0f0f0f',
borderTop: '1px solid rgba(255, 255, 255, 0.08)',
borderTop: `1px solid ${border.default}`,
}}
>
<Container maxWidth="md" sx={{ position: 'relative', textAlign: 'center' }}>
@@ -893,7 +868,7 @@ export default function Home() {
<Typography
variant="body1"
sx={{
color: 'text.secondary',
color: text.muted,
mb: 5,
lineHeight: 1.7,
fontSize: '1.125rem',

View File

@@ -34,6 +34,7 @@ import { Script } from "@/app/ts/Script";
import { useTitle } from "@/app/hooks/useTitle";
import { useUser } from "@/app/hooks/useUser";
import { RolesConstants, hasRole } from "@/app/ts/Roles";
import { primary, semantic, surface, text, border } from "@/app/lib/colors";
interface SnackbarState {
open: boolean;
@@ -61,43 +62,43 @@ const IDEButton = ({
switch (variant) {
case 'primary':
return {
bg: '#0e639c',
hoverBg: '#1177bb',
activeBg: '#007acc',
bg: primary.dark,
hoverBg: primary.mid,
activeBg: primary.main,
color: '#ffffff',
border: '#007acc',
border: primary.main,
};
case 'success':
return {
bg: '#0e7e0e',
hoverBg: '#0f9d0f',
activeBg: '#14b814',
bg: '#166534',
hoverBg: '#15803d',
activeBg: semantic.success,
color: '#ffffff',
border: '#14b814',
border: semantic.success,
};
case 'error':
return {
bg: '#7e0e0e',
hoverBg: '#9d0f0f',
activeBg: '#b81414',
bg: '#7f1d1d',
hoverBg: '#991b1b',
activeBg: semantic.error,
color: '#ffffff',
border: '#b81414',
border: semantic.error,
};
case 'warning':
return {
bg: '#7e5e0e',
hoverBg: '#9d750f',
activeBg: '#b88614',
bg: '#78350f',
hoverBg: '#92400e',
activeBg: semantic.warning,
color: '#ffffff',
border: '#b88614',
border: semantic.warning,
};
default:
return {
bg: 'transparent',
hoverBg: 'rgba(255, 255, 255, 0.08)',
activeBg: 'rgba(255, 255, 255, 0.12)',
color: '#cccccc',
border: '#3e3e42',
color: text.secondary,
border: text.dim,
};
}
};
@@ -163,13 +164,13 @@ const InfoBadge = ({
const getColors = () => {
switch (type) {
case 'warning':
return { bg: 'rgba(250, 200, 90, 0.15)', border: '#fac85a', color: '#fac85a' };
return { bg: `rgba(251, 191, 36, 0.15)`, border: semantic.warning, color: semantic.warning };
case 'error':
return { bg: 'rgba(240, 82, 82, 0.15)', border: '#f05252', color: '#f05252' };
return { bg: `rgba(248, 113, 113, 0.15)`, border: semantic.error, color: semantic.error };
case 'success':
return { bg: 'rgba(80, 200, 120, 0.15)', border: '#50c878', color: '#50c878' };
return { bg: `rgba(74, 222, 128, 0.15)`, border: semantic.success, color: semantic.success };
default:
return { bg: 'rgba(100, 150, 230, 0.15)', border: '#6496e6', color: '#6496e6' };
return { bg: `rgba(167, 139, 250, 0.15)`, border: primary.main, color: primary.main };
}
};
@@ -636,15 +637,15 @@ export default function ScriptReviewPage() {
<Box sx={{
width: '100vw',
height: '100vh',
bgcolor: '#1e1e1e',
bgcolor: surface.raised,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#cccccc',
color: text.secondary,
}}>
<Box sx={{ textAlign: 'center' }}>
<LinearProgress sx={{ mb: 2, width: 300 }} />
<Typography sx={{ color: '#cccccc', fontSize: '14px' }}>Loading script...</Typography>
<Typography sx={{ color: text.secondary, fontSize: '14px' }}>Loading script...</Typography>
</Box>
</Box>
);
@@ -703,13 +704,13 @@ export default function ScriptReviewPage() {
height: '100vh',
display: 'flex',
flexDirection: 'column',
bgcolor: '#1e1e1e',
bgcolor: surface.raised,
overflow: 'hidden',
}}>
{/* Title Bar */}
<Box sx={{
bgcolor: '#323233',
borderBottom: '1px solid #2b2b2c',
bgcolor: '#27272a',
borderBottom: `1px solid ${border.default}`,
display: 'flex',
alignItems: 'center',
px: 2,
@@ -717,8 +718,8 @@ export default function ScriptReviewPage() {
gap: 2,
}}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
<CodeIcon sx={{ fontSize: 20, color: '#007acc' }} />
<Typography sx={{ fontSize: '13px', fontWeight: 600, color: '#cccccc', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
<CodeIcon sx={{ fontSize: 20, color: primary.dark }} />
<Typography sx={{ fontSize: '13px', fontWeight: 600, color: text.secondary, fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
Script Review
</Typography>
</Box>
@@ -732,7 +733,7 @@ export default function ScriptReviewPage() {
>
Previous
</IDEButton>
<Typography sx={{ fontSize: '12px', color: '#858585', px: 1 }}>
<Typography sx={{ fontSize: '12px', color: text.muted, px: 1 }}>
{currentIndex + 1} / {allScripts.length}
</Typography>
<IDEButton
@@ -752,12 +753,12 @@ export default function ScriptReviewPage() {
gap: 1,
px: 1.5,
py: 0.5,
bgcolor: 'rgba(250, 200, 90, 0.15)',
bgcolor: `rgba(251, 191, 36, 0.15)`,
borderRadius: '2px',
border: '1px solid #fac85a',
border: `1px solid ${semantic.warning}`,
}}>
<WarningAmberIcon sx={{ fontSize: '14px', color: '#fac85a' }} />
<Typography sx={{ fontSize: '11px', color: '#fac85a', fontWeight: 500 }}>
<WarningAmberIcon sx={{ fontSize: '14px', color: semantic.warning }} />
<Typography sx={{ fontSize: '11px', color: semantic.warning, fontWeight: 500 }}>
UNSAVED CHANGES
</Typography>
</Box>
@@ -787,16 +788,16 @@ export default function ScriptReviewPage() {
width: 300,
minWidth: 300,
flexShrink: 0,
bgcolor: '#252526',
borderRight: '1px solid #2b2b2c',
bgcolor: surface.raised,
borderRight: `1px solid ${border.default}`,
display: 'flex',
flexDirection: 'column',
overflow: 'auto',
}}>
{/* Script Info Section */}
<Box sx={{ p: 2, borderBottom: '1px solid #2b2b2c' }}>
<Box sx={{ p: 2, borderBottom: `1px solid ${border.default}` }}>
<Typography sx={{
color: '#858585',
color: text.muted,
fontSize: '11px',
fontWeight: 700,
letterSpacing: '0.5px',
@@ -806,11 +807,11 @@ export default function ScriptReviewPage() {
SCRIPT PROPERTIES
</Typography>
<Box sx={{ mb: 1.5 }}>
<Typography sx={{ color: '#858585', fontSize: '10px', mb: 0.5 }}>
<Typography sx={{ color: text.muted, fontSize: '10px', mb: 0.5 }}>
Name
</Typography>
<Typography sx={{
color: '#d4d4d4',
color: text.secondary,
fontFamily: '"Cascadia Code", "Courier New", monospace',
fontSize: '12px',
wordBreak: 'break-word',
@@ -819,11 +820,11 @@ export default function ScriptReviewPage() {
</Typography>
</Box>
<Box>
<Typography sx={{ color: '#858585', fontSize: '10px', mb: 0.5 }}>
<Typography sx={{ color: text.muted, fontSize: '10px', mb: 0.5 }}>
Hash
</Typography>
<Typography sx={{
color: '#858585',
color: text.muted,
fontFamily: '"Cascadia Code", "Courier New", monospace',
fontSize: '10px',
wordBreak: 'break-all',
@@ -834,9 +835,9 @@ export default function ScriptReviewPage() {
</Box>
{/* Policy Selection Section */}
<Box sx={{ p: 2, borderBottom: '1px solid #2b2b2c' }}>
<Box sx={{ p: 2, borderBottom: `1px solid ${border.default}` }}>
<Typography sx={{
color: '#858585',
color: text.muted,
fontSize: '11px',
fontWeight: 700,
letterSpacing: '0.5px',
@@ -929,9 +930,9 @@ export default function ScriptReviewPage() {
title="Purge Script & Policy"
style={{
padding: '6px',
backgroundColor: submitting || sourceChanged ? '#3e3e42' : '#7e0e0e',
backgroundColor: submitting || sourceChanged ? text.dim : '#7f1d1d',
color: '#ffffff',
border: `1px solid ${submitting || sourceChanged ? '#3e3e42' : '#b81414'}`,
border: `1px solid ${submitting || sourceChanged ? text.dim : semantic.error}`,
borderRadius: '2px',
cursor: submitting || sourceChanged ? 'not-allowed' : 'pointer',
opacity: submitting || sourceChanged ? 0.4 : 1,
@@ -946,22 +947,22 @@ export default function ScriptReviewPage() {
}}
onMouseEnter={(e) => {
if (!submitting && !sourceChanged) {
e.currentTarget.style.backgroundColor = '#9d0f0f';
e.currentTarget.style.backgroundColor = '#991b1b';
}
}}
onMouseLeave={(e) => {
if (!submitting && !sourceChanged) {
e.currentTarget.style.backgroundColor = '#7e0e0e';
e.currentTarget.style.backgroundColor = '#7f1d1d';
}
}}
onMouseDown={(e) => {
if (!submitting && !sourceChanged) {
e.currentTarget.style.backgroundColor = '#b81414';
e.currentTarget.style.backgroundColor = semantic.error;
}
}}
onMouseUp={(e) => {
if (!submitting && !sourceChanged) {
e.currentTarget.style.backgroundColor = '#9d0f0f';
e.currentTarget.style.backgroundColor = '#991b1b';
}
}}
>
@@ -976,15 +977,15 @@ export default function ScriptReviewPage() {
<Box sx={{ mt: 1 }}>
<Box sx={{
p: 1.5,
bgcolor: 'rgba(240, 82, 82, 0.15)',
border: '1px solid #f05252',
bgcolor: `rgba(248, 113, 113, 0.15)`,
border: `1px solid ${semantic.error}`,
borderRadius: '2px',
mb: 1,
}}>
<Typography sx={{ fontSize: '11px', color: '#f05252', fontWeight: 500, mb: 0.5 }}>
<Typography sx={{ fontSize: '11px', color: semantic.error, fontWeight: 500, mb: 0.5 }}>
Permanent Deletion
</Typography>
<Typography sx={{ fontSize: '10px', color: '#cccccc', lineHeight: 1.4 }}>
<Typography sx={{ fontSize: '10px', color: text.secondary, lineHeight: 1.4 }}>
This will permanently delete the script and policy. This action cannot be undone.
</Typography>
</Box>
@@ -1021,21 +1022,21 @@ export default function ScriptReviewPage() {
<Box sx={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
{/* Tab Bar */}
<Box sx={{
bgcolor: '#2d2d2d',
borderBottom: '1px solid #1e1e1e',
bgcolor: '#27272a',
borderBottom: `1px solid ${surface.raised}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
minHeight: 35,
}}>
<Box sx={{
bgcolor: '#1e1e1e',
bgcolor: surface.raised,
px: 2,
py: 0.75,
display: 'flex',
alignItems: 'center',
gap: 1,
borderTop: sourceChanged ? '2px solid #f59e0b' : '2px solid #007acc',
borderTop: sourceChanged ? `2px solid ${semantic.warning}` : `2px solid ${primary.dark}`,
color: '#ffffff',
minHeight: 35,
}}>
@@ -1048,7 +1049,7 @@ export default function ScriptReviewPage() {
width: 8,
height: 8,
borderRadius: '50%',
bgcolor: '#f59e0b'
bgcolor: semantic.warning
}} />
)}
</Box>
@@ -1113,7 +1114,7 @@ export default function ScriptReviewPage() {
{/* Status Bar */}
<Box sx={{
bgcolor: '#007acc',
bgcolor: primary.dark,
color: 'white',
px: 2,
py: 0.5,
@@ -1152,9 +1153,9 @@ export default function ScriptReviewPage() {
onClose={handleCloseSnackbar}
severity={snackbar.severity}
sx={{
bgcolor: '#252526',
color: '#cccccc',
border: '1px solid #3e3e42',
bgcolor: surface.raised,
color: text.secondary,
border: `1px solid ${text.faint}`,
}}
>
{snackbar.message}

View File

@@ -6,6 +6,7 @@ import Webpage from "@/app/_components/webpage";
import { ListSortConstants } from "../ts/Sort";
import { hasAnyReviewerRole } from "../ts/Roles";
import { useUser } from "@/app/hooks/useUser";
import { primary, semantic } from "@/app/lib/colors";
import {
Box,
Breadcrumbs,
@@ -407,7 +408,7 @@ export default function UserDashboardPage() {
gap: 2,
mb: 4
}}>
<Card sx={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
<Card sx={{ background: `linear-gradient(135deg, ${primary.main} 0%, ${primary.dark} 100%)` }}>
<CardContent>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', mb: 1 }}>
Total Contributions
@@ -422,7 +423,7 @@ export default function UserDashboardPage() {
</CardContent>
</Card>
<Card sx={{ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' }}>
<Card sx={{ background: `linear-gradient(135deg, ${semantic.success} 0%, ${primary.mid} 100%)` }}>
<CardContent>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', mb: 1 }}>
Released
@@ -437,7 +438,7 @@ export default function UserDashboardPage() {
</CardContent>
</Card>
<Card sx={{ background: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }}>
<Card sx={{ background: `linear-gradient(135deg, ${semantic.info} 0%, ${primary.light} 100%)` }}>
<CardContent>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', mb: 1 }}>
In Review
@@ -452,7 +453,7 @@ export default function UserDashboardPage() {
</CardContent>
</Card>
<Card sx={{ background: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)' }}>
<Card sx={{ background: `linear-gradient(135deg, ${semantic.error} 0%, ${semantic.warning} 100%)` }}>
<CardContent>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.9)', mb: 1 }}>
Action Needed

View File

@@ -16,17 +16,17 @@ export function getGameInfo(gameId: number) {
case 1:
return {
name: "Bhop",
color: "#2196f3" // blue
color: "#a78bfa" // purple
};
case 2:
return {
name: "Surf",
color: "#4caf50" // green
color: "#22d3ee" // cyan
};
case 5:
return {
name: "Fly Trials",
color: "#ff9800" // orange
color: "#fbbf24" // yellow
};
default:
return {