Updated component styling
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-03-06 21:26:28 -05:00
parent 21440fad57
commit fe71e9dbcb
10 changed files with 193 additions and 141 deletions

View File

@@ -3,10 +3,10 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>StrafesNET Dev Portal</title>
<title>StrafesNET | Developer Portal</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&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Barlow:wght@700&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root"></div>

View File

@@ -67,18 +67,32 @@ const Header: React.FC<HeaderProps> = ({ user, onCreateAppClick, isAdmin, adminV
<>
<AppBar position="sticky">
<Toolbar sx={{ gap: 1 }}>
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'baseline', gap: 1 }}>
<Typography
variant="h6"
component="div"
sx={{
flexGrow: 1,
fontFamily: '"Barlow", sans-serif',
fontWeight: 700,
letterSpacing: '-0.02em',
color: text.secondary,
color: text.primary,
lineHeight: 1,
userSelect: 'none',
}}
>
StrafesNET Developer Portal
StrafesNET
</Typography>
<Typography
variant="body2"
sx={{
fontWeight: 500,
color: text.muted,
userSelect: 'none',
}}
>
Developer Portal
</Typography>
</Box>
{/* Rate Limit Display */}
{!adminView && (

View File

@@ -16,6 +16,7 @@ import {
Refresh as RefreshIcon
} from '@mui/icons-material';
import {RateLimit, RateLimitStatus} from '../types';
import { primary, border, fill, surface } from '../theme/colors';
interface RateLimitDisplayProps {
rateLimit: RateLimit;
@@ -158,14 +159,15 @@ const RateLimitDisplay: React.FC<RateLimitDisplayProps> = ({ rateLimit, rateLimi
componentsProps={{
tooltip: {
sx: {
bgcolor: 'background.paper',
bgcolor: surface.raisedSolid,
color: 'text.primary',
border: `1px solid ${border.default}`,
'& .MuiTooltip-arrow': {
color: 'background.paper',
color: surface.raisedSolid,
},
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.2)',
borderRadius: 1,
p: 0, // Remove padding from tooltip to prevent double padding
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.4)',
borderRadius: 2,
p: 0,
}
}
}}
@@ -176,16 +178,16 @@ const RateLimitDisplay: React.FC<RateLimitDisplayProps> = ({ rateLimit, rateLimi
alignItems: 'center',
px: 1.5,
py: 0.75,
mr: 2,
borderRadius: 2,
backgroundColor: 'rgba(149, 128, 255, 0.15)',
border: '1px solid rgba(149, 128, 255, 0.3)',
color: 'primary.main',
backgroundColor: fill.primaryStrong,
border: `1px solid ${border.primaryStrong}`,
color: primary.main,
cursor: 'pointer',
transition: 'all 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(149, 128, 255, 0.25)',
backgroundColor: fill.primaryActive,
borderColor: primary.main,
},
transition: 'background-color 0.2s',
}}
>
<SpeedIcon fontSize="small" sx={{ mr: 1 }} />

View File

@@ -15,6 +15,7 @@ import {
import { RateLimit, Permission } from '../../types';
import { adminService } from '../../services/adminService';
import { useNotification } from '../../context/NotificationContext';
import { text } from '../../theme/colors';
import UsersTab from './users/UsersTab';
import PermissionsTab from './permissions/PermissionsTab';
import RateLimitsTab from './rate-limits/RateLimitsTab';
@@ -53,16 +54,19 @@ const AdminPage: React.FC = () => {
maxWidth="xl"
sx={{ mt: 4, mb: 4, flexGrow: 1, width: '100%', maxWidth: '100% !important' }}
>
<Box
<Box sx={{ mb: 4 }}>
<Typography
variant="h4"
gutterBottom
sx={{
mb: 3,
pl: 2,
borderLeft: '3px solid',
borderColor: 'primary.main',
background: `linear-gradient(135deg, ${text.primary} 0%, ${text.tertiary} 100%)`,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
<Typography variant="h5" fontWeight={600} gutterBottom>Admin Panel</Typography>
<Typography variant="body2" color="text.secondary">
Admin Panel
</Typography>
<Typography variant="body1" sx={{ color: text.tertiary }}>
Manage users, permissions, and rate limit classes.
</Typography>
</Box>

View File

@@ -37,6 +37,7 @@ import {
import { AdminUser, RateLimit, Permission, Application } from '../../../types';
import { adminService } from '../../../services/adminService';
import { useNotification } from '../../../context/NotificationContext';
import { border, fill } from '../../../theme/colors';
interface UserDetailDialogProps {
open: boolean;
@@ -310,16 +311,14 @@ const UserDetailDialog: React.FC<UserDetailDialogProps> = ({
key={service}
sx={{
mb: 2,
border: '1px solid rgba(255, 255, 255, 0.12)',
borderRadius: '4px',
'&:before': { display: 'none' },
}}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
borderBottom: '1px solid rgba(255, 255, 255, 0.12)',
backgroundColor: 'rgba(255, 255, 255, 0.03)',
borderBottom: `1px solid ${border.default}`,
backgroundColor: fill.subtle,
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
@@ -342,12 +341,13 @@ const UserDetailDialog: React.FC<UserDetailDialogProps> = ({
variant="outlined"
sx={{
p: 1,
backgroundColor: 'rgba(255, 255, 255, 0.03)',
borderColor: 'rgba(255, 255, 255, 0.12)',
backgroundColor: fill.subtle,
borderColor: border.default,
opacity: toggling ? 0.6 : 1,
transition: 'opacity 0.15s',
transition: 'opacity 0.15s, border-color 0.15s',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.05)',
backgroundColor: fill.primaryHover,
borderColor: border.primaryDefault,
},
}}
>

View File

@@ -19,6 +19,7 @@ import {
} from '@mui/icons-material';
import { Application, Permission } from '../../types';
import { useApplications } from '../../context/ApplicationContext';
import { primary, secondary, border, fill, text } from '../../theme/colors';
interface ApplicationCardProps {
app: Application;
@@ -32,42 +33,48 @@ const ApplicationCard: React.FC<ApplicationCardProps> = ({ app, permissions }) =
const totalAvailablePermissions = permissions.length;
return (
<Card
sx={{
display: 'flex',
flexDirection: 'column',
border: '1px solid rgba(255, 255, 255, 0.12)',
}}
>
<Card sx={{ display: 'flex', flexDirection: 'column' }}>
<CardContent sx={{ flexGrow: 1 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 1 }}>
<Typography variant="h6" sx={{ pr: 1 }}>
<Typography variant="h6" sx={{ pr: 1, color: text.primary, fontWeight: 600 }}>
{app.name}
</Typography>
<IconButton
size="small"
onClick={(e) => handleMenuOpen(e, app.id)}
sx={{ ml: 'auto' }}
sx={{
ml: 'auto',
color: text.muted,
'&:hover': { color: text.secondary, backgroundColor: fill.hover },
}}
>
<MoreVertIcon fontSize="small" />
</IconButton>
</Box>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2, minHeight: '40px' }}>
<Typography variant="body2" sx={{ mb: 2, minHeight: '40px', color: text.tertiary }}>
{app.description || 'No description provided'}
</Typography>
<Box sx={{ mt: 2 }}>
<Typography variant="subtitle2" gutterBottom sx={{ display: 'flex', alignItems: 'center' }}>
<SecurityIcon fontSize="small" sx={{ mr: 0.5 }} />
Permissions:
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
<SecurityIcon fontSize="small" sx={{ mr: 0.5, color: text.muted, fontSize: '0.875rem' }} />
<Typography variant="caption" sx={{ color: text.muted, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>
Permissions
</Typography>
<Chip
label={`${usedPermissionCount}/${totalAvailablePermissions}`}
size="small"
color={usedPermissionCount > 0 ? "primary" : "default"}
sx={{ ml: 1 }}
sx={{
ml: 1,
height: 18,
fontSize: '0.7rem',
backgroundColor: usedPermissionCount > 0 ? fill.primaryStrong : fill.subtle,
color: usedPermissionCount > 0 ? primary.light : text.muted,
border: `1px solid ${usedPermissionCount > 0 ? border.primaryMedium : border.default}`,
}}
/>
</Typography>
</Box>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, mt: 1 }}>
{appPermissions.reduce((services: string[], permission: Permission) => {
@@ -85,57 +92,74 @@ const ApplicationCard: React.FC<ApplicationCardProps> = ({ app, permissions }) =
<Chip
label={service.split(' ')[0]}
size="small"
variant="outlined"
color="primary"
sx={{
height: 20,
fontSize: '0.7rem',
backgroundColor: fill.primaryActive,
color: primary.light,
border: `1px solid ${border.primaryMedium}`,
}}
/>
</Tooltip>
);
})}
{appPermissions.length === 0 && (
<Typography variant="caption" sx={{ color: text.dim }}>
No permissions granted
</Typography>
)}
</Box>
</Box>
<Divider sx={{ my: 2 }} />
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Typography variant="caption">
Created: {new Date(app.created_at).toLocaleDateString(undefined, {
<Typography variant="caption" sx={{ color: text.dim }}>
{new Date(app.created_at).toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})}
</Typography>
<Tooltip title="This key was displayed once. Regenerate if needed.">
<Chip
label="API Key Hidden"
label="Key Hidden"
size="small"
variant="outlined"
color="default"
sx={{ fontFamily: 'monospace' }}
sx={{
fontFamily: 'monospace',
fontSize: '0.65rem',
height: 18,
backgroundColor: fill.subtle,
color: text.dim,
border: `1px solid ${border.default}`,
}}
/>
</Tooltip>
</Box>
</CardContent>
<CardActions sx={{ justifyContent: 'space-between', px: 2, pb: 2 }}>
<CardActions sx={{ justifyContent: 'space-between', px: 2, pb: 2, pt: 0 }}>
<Button
size="small"
startIcon={<EditIcon />}
startIcon={<EditIcon sx={{ fontSize: '0.875rem !important' }} />}
onClick={() => openEditDialog(app)}
color="primary"
variant="text"
sx={{
color: primary.main,
fontSize: '0.8rem',
'&:hover': { backgroundColor: fill.primaryActive },
}}
>
Edit
</Button>
<Button
size="small"
startIcon={<VpnKeyIcon />}
startIcon={<VpnKeyIcon sx={{ fontSize: '0.875rem !important' }} />}
onClick={() => regenerateApiKey(app)}
color="secondary"
variant="text"
sx={{
color: secondary.main,
fontSize: '0.8rem',
'&:hover': { backgroundColor: 'rgba(34, 211, 238, 0.08)' },
}}
>
New Key
</Button>

View File

@@ -6,6 +6,7 @@ import {
VpnKey as VpnKeyIcon
} from '@mui/icons-material';
import { useApplications } from '../../context/ApplicationContext';
import { fill, text } from '../../theme/colors';
interface ApplicationMenuProps {
anchorEl: HTMLElement | null;
@@ -26,29 +27,19 @@ const ApplicationMenu: React.FC<ApplicationMenuProps> = ({ anchorEl, appId, onCl
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={onClose}
PaperProps={{
slotProps={{
paper: {
elevation: 0,
sx: {
overflow: 'visible',
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
mt: 1.5,
'& .MuiAvatar-root': {
width: 32,
height: 32,
ml: -0.5,
mr: 1,
mt: 1,
minWidth: 180,
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.4)',
'& .MuiMenuItem-root': {
fontSize: '0.875rem',
color: text.secondary,
transition: 'all 0.15s ease',
'&:hover': { backgroundColor: fill.hover, color: text.primary },
},
'&:before': {
content: '""',
display: 'block',
position: 'absolute',
top: 0,
right: 14,
width: 10,
height: 10,
bgcolor: 'background.paper',
transform: 'translateY(-50%) rotate(45deg)',
zIndex: 0,
},
},
}}

View File

@@ -3,7 +3,6 @@ import {
Container,
Box,
Typography,
Paper,
Button
} from '@mui/material';
import {
@@ -13,6 +12,7 @@ import {
import ApplicationCard from './ApplicationCard.tsx';
import { Permission, UserInfo } from '../../types';
import { useApplications } from '../../context/ApplicationContext';
import { primary, text, border, fill } from '../../theme/colors';
interface ApplicationsPageProps {
user: UserInfo;
@@ -26,52 +26,67 @@ const ApplicationsPage: React.FC<ApplicationsPageProps> = ({ user, permissions }
} = useApplications();
return (
<Container
maxWidth="xl"
<Container maxWidth="xl" sx={{ py: 4, px: { xs: 2, sm: 3 } }}>
<Box sx={{ mb: 4 }}>
<Typography
variant="h4"
gutterBottom
sx={{
mt: 4,
mb: 4,
flexGrow: 1,
width: '100%',
maxWidth: '100% !important',
background: `linear-gradient(135deg, ${text.primary} 0%, ${text.tertiary} 100%)`,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
<Box sx={{ mb: 4 }}>
<Typography variant="h4" gutterBottom>
Your Applications
</Typography>
<Typography variant="body1" color="text.secondary">
Create and manage your applications
<Typography variant="body1" sx={{ color: text.tertiary }}>
Create and manage your API applications
</Typography>
</Box>
{applications.length === 0 ? (
<Paper
elevation={0}
<Box
sx={{
textAlign: 'center',
my: 8,
py: 8,
bgcolor: 'background.paper',
borderRadius: 2,
border: '1px solid rgba(255, 255, 255, 0.12)',
opacity: user.active ? 1 : 0.6
borderRadius: 3,
backgroundColor: fill.subtle,
border: `1px solid ${border.default}`,
opacity: user.active ? 1 : 0.6,
}}
>
<AppsIcon sx={{ fontSize: 60, color: 'text.secondary', mb: 2 }} />
<Typography variant="h6" color="text.secondary" gutterBottom>
<Box
sx={{
width: 64,
height: 64,
borderRadius: '50%',
backgroundColor: fill.primaryActive,
border: `1px solid ${border.primaryMedium}`,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
mx: 'auto',
mb: 2,
}}
>
<AppsIcon sx={{ fontSize: 28, color: primary.main }} />
</Box>
<Typography variant="h6" sx={{ color: text.secondary }} gutterBottom>
No applications yet
</Typography>
<Typography variant="body2" sx={{ color: text.muted, mb: 3 }}>
Create your first app to get an API key
</Typography>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={openCreateDialog}
disabled={!user.active}
sx={{ mt: 2 }}
>
Create Your First App
</Button>
</Paper>
</Box>
) : (
<Box sx={{
display: 'grid',

View File

@@ -11,6 +11,7 @@ import {
Tooltip
} from '@mui/material';
import { ContentCopy as ContentCopyIcon } from '@mui/icons-material';
import { secondary, border, fill } from '../../../theme/colors';
interface ApiKeyDialogProps {
open: boolean;
@@ -43,11 +44,11 @@ const ApiKeyDialog: React.FC<ApiKeyDialogProps> = ({
sx={{
p: 2,
mt: 2,
bgcolor: 'rgba(255, 255, 255, 0.04)',
backgroundColor: fill.subtle,
borderColor: border.primaryDefault,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
border: '1px solid rgba(255, 255, 255, 0.15)',
}}
>
<Typography
@@ -58,7 +59,7 @@ const ApiKeyDialog: React.FC<ApiKeyDialogProps> = ({
width: '100%',
overflow: 'auto',
p: 1,
color: '#80ffea', // Match secondary color for monospace text
color: secondary.main,
}}
>
{apiKey}

View File

@@ -25,6 +25,7 @@ import {
} from '@mui/icons-material';
import { Application, Permission } from '../../../types';
import { useApplications } from '../../../context/ApplicationContext';
import { border, fill } from '../../../theme/colors';
interface ApplicationFormDialogProps {
open: boolean;
@@ -210,16 +211,14 @@ const ApplicationFormDialog: React.FC<ApplicationFormDialogProps> = ({
key={service.name}
sx={{
mb: 2,
border: '1px solid rgba(255, 255, 255, 0.12)',
borderRadius: '4px',
'&:before': { display: 'none' },
}}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
borderBottom: '1px solid rgba(255, 255, 255, 0.12)',
backgroundColor: 'rgba(255, 255, 255, 0.03)',
borderBottom: `1px solid ${border.default}`,
backgroundColor: fill.subtle,
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
@@ -240,10 +239,12 @@ const ApplicationFormDialog: React.FC<ApplicationFormDialogProps> = ({
variant="outlined"
sx={{
p: 1,
backgroundColor: 'rgba(255, 255, 255, 0.03)',
borderColor: 'rgba(255, 255, 255, 0.12)',
backgroundColor: fill.subtle,
borderColor: border.default,
transition: 'border-color 0.15s ease',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.05)',
backgroundColor: fill.primaryHover,
borderColor: border.primaryDefault,
}
}}
>