Updated component styling
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -3,10 +3,10 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<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.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -67,18 +67,32 @@ const Header: React.FC<HeaderProps> = ({ user, onCreateAppClick, isAdmin, adminV
|
|||||||
<>
|
<>
|
||||||
<AppBar position="sticky">
|
<AppBar position="sticky">
|
||||||
<Toolbar sx={{ gap: 1 }}>
|
<Toolbar sx={{ gap: 1 }}>
|
||||||
<Typography
|
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'baseline', gap: 1 }}>
|
||||||
variant="h6"
|
<Typography
|
||||||
component="div"
|
variant="h6"
|
||||||
sx={{
|
component="div"
|
||||||
flexGrow: 1,
|
sx={{
|
||||||
fontWeight: 700,
|
fontFamily: '"Barlow", sans-serif',
|
||||||
letterSpacing: '-0.02em',
|
fontWeight: 700,
|
||||||
color: text.secondary,
|
letterSpacing: '-0.02em',
|
||||||
}}
|
color: text.primary,
|
||||||
>
|
lineHeight: 1,
|
||||||
StrafesNET Developer Portal
|
userSelect: 'none',
|
||||||
</Typography>
|
}}
|
||||||
|
>
|
||||||
|
StrafesNET
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
fontWeight: 500,
|
||||||
|
color: text.muted,
|
||||||
|
userSelect: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Developer Portal
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Rate Limit Display */}
|
{/* Rate Limit Display */}
|
||||||
{!adminView && (
|
{!adminView && (
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
Refresh as RefreshIcon
|
Refresh as RefreshIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import {RateLimit, RateLimitStatus} from '../types';
|
import {RateLimit, RateLimitStatus} from '../types';
|
||||||
|
import { primary, border, fill, surface } from '../theme/colors';
|
||||||
|
|
||||||
interface RateLimitDisplayProps {
|
interface RateLimitDisplayProps {
|
||||||
rateLimit: RateLimit;
|
rateLimit: RateLimit;
|
||||||
@@ -158,14 +159,15 @@ const RateLimitDisplay: React.FC<RateLimitDisplayProps> = ({ rateLimit, rateLimi
|
|||||||
componentsProps={{
|
componentsProps={{
|
||||||
tooltip: {
|
tooltip: {
|
||||||
sx: {
|
sx: {
|
||||||
bgcolor: 'background.paper',
|
bgcolor: surface.raisedSolid,
|
||||||
color: 'text.primary',
|
color: 'text.primary',
|
||||||
|
border: `1px solid ${border.default}`,
|
||||||
'& .MuiTooltip-arrow': {
|
'& .MuiTooltip-arrow': {
|
||||||
color: 'background.paper',
|
color: surface.raisedSolid,
|
||||||
},
|
},
|
||||||
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.2)',
|
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.4)',
|
||||||
borderRadius: 1,
|
borderRadius: 2,
|
||||||
p: 0, // Remove padding from tooltip to prevent double padding
|
p: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -176,16 +178,16 @@ const RateLimitDisplay: React.FC<RateLimitDisplayProps> = ({ rateLimit, rateLimi
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
px: 1.5,
|
px: 1.5,
|
||||||
py: 0.75,
|
py: 0.75,
|
||||||
mr: 2,
|
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
backgroundColor: 'rgba(149, 128, 255, 0.15)',
|
backgroundColor: fill.primaryStrong,
|
||||||
border: '1px solid rgba(149, 128, 255, 0.3)',
|
border: `1px solid ${border.primaryStrong}`,
|
||||||
color: 'primary.main',
|
color: primary.main,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
transition: 'all 0.15s ease',
|
||||||
'&:hover': {
|
'&: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 }} />
|
<SpeedIcon fontSize="small" sx={{ mr: 1 }} />
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
import { RateLimit, Permission } from '../../types';
|
import { RateLimit, Permission } from '../../types';
|
||||||
import { adminService } from '../../services/adminService';
|
import { adminService } from '../../services/adminService';
|
||||||
import { useNotification } from '../../context/NotificationContext';
|
import { useNotification } from '../../context/NotificationContext';
|
||||||
|
import { text } from '../../theme/colors';
|
||||||
import UsersTab from './users/UsersTab';
|
import UsersTab from './users/UsersTab';
|
||||||
import PermissionsTab from './permissions/PermissionsTab';
|
import PermissionsTab from './permissions/PermissionsTab';
|
||||||
import RateLimitsTab from './rate-limits/RateLimitsTab';
|
import RateLimitsTab from './rate-limits/RateLimitsTab';
|
||||||
@@ -53,16 +54,19 @@ const AdminPage: React.FC = () => {
|
|||||||
maxWidth="xl"
|
maxWidth="xl"
|
||||||
sx={{ mt: 4, mb: 4, flexGrow: 1, width: '100%', maxWidth: '100% !important' }}
|
sx={{ mt: 4, mb: 4, flexGrow: 1, width: '100%', maxWidth: '100% !important' }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box sx={{ mb: 4 }}>
|
||||||
sx={{
|
<Typography
|
||||||
mb: 3,
|
variant="h4"
|
||||||
pl: 2,
|
gutterBottom
|
||||||
borderLeft: '3px solid',
|
sx={{
|
||||||
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.
|
Manage users, permissions, and rate limit classes.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
import { AdminUser, RateLimit, Permission, Application } from '../../../types';
|
import { AdminUser, RateLimit, Permission, Application } from '../../../types';
|
||||||
import { adminService } from '../../../services/adminService';
|
import { adminService } from '../../../services/adminService';
|
||||||
import { useNotification } from '../../../context/NotificationContext';
|
import { useNotification } from '../../../context/NotificationContext';
|
||||||
|
import { border, fill } from '../../../theme/colors';
|
||||||
|
|
||||||
interface UserDetailDialogProps {
|
interface UserDetailDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -310,16 +311,14 @@ const UserDetailDialog: React.FC<UserDetailDialogProps> = ({
|
|||||||
key={service}
|
key={service}
|
||||||
sx={{
|
sx={{
|
||||||
mb: 2,
|
mb: 2,
|
||||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
|
||||||
borderRadius: '4px',
|
|
||||||
'&:before': { display: 'none' },
|
'&:before': { display: 'none' },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AccordionSummary
|
<AccordionSummary
|
||||||
expandIcon={<ExpandMoreIcon />}
|
expandIcon={<ExpandMoreIcon />}
|
||||||
sx={{
|
sx={{
|
||||||
borderBottom: '1px solid rgba(255, 255, 255, 0.12)',
|
borderBottom: `1px solid ${border.default}`,
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.03)',
|
backgroundColor: fill.subtle,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||||
@@ -342,12 +341,13 @@ const UserDetailDialog: React.FC<UserDetailDialogProps> = ({
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{
|
sx={{
|
||||||
p: 1,
|
p: 1,
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.03)',
|
backgroundColor: fill.subtle,
|
||||||
borderColor: 'rgba(255, 255, 255, 0.12)',
|
borderColor: border.default,
|
||||||
opacity: toggling ? 0.6 : 1,
|
opacity: toggling ? 0.6 : 1,
|
||||||
transition: 'opacity 0.15s',
|
transition: 'opacity 0.15s, border-color 0.15s',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
backgroundColor: fill.primaryHover,
|
||||||
|
borderColor: border.primaryDefault,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { Application, Permission } from '../../types';
|
import { Application, Permission } from '../../types';
|
||||||
import { useApplications } from '../../context/ApplicationContext';
|
import { useApplications } from '../../context/ApplicationContext';
|
||||||
|
import { primary, secondary, border, fill, text } from '../../theme/colors';
|
||||||
|
|
||||||
interface ApplicationCardProps {
|
interface ApplicationCardProps {
|
||||||
app: Application;
|
app: Application;
|
||||||
@@ -32,42 +33,48 @@ const ApplicationCard: React.FC<ApplicationCardProps> = ({ app, permissions }) =
|
|||||||
const totalAvailablePermissions = permissions.length;
|
const totalAvailablePermissions = permissions.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardContent sx={{ flexGrow: 1 }}>
|
<CardContent sx={{ flexGrow: 1 }}>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 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}
|
{app.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={(e) => handleMenuOpen(e, app.id)}
|
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" />
|
<MoreVertIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</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'}
|
{app.description || 'No description provided'}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box sx={{ mt: 2 }}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<Typography variant="subtitle2" gutterBottom sx={{ display: 'flex', alignItems: 'center' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||||
<SecurityIcon fontSize="small" sx={{ mr: 0.5 }} />
|
<SecurityIcon fontSize="small" sx={{ mr: 0.5, color: text.muted, fontSize: '0.875rem' }} />
|
||||||
Permissions:
|
<Typography variant="caption" sx={{ color: text.muted, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
||||||
|
Permissions
|
||||||
|
</Typography>
|
||||||
<Chip
|
<Chip
|
||||||
label={`${usedPermissionCount}/${totalAvailablePermissions}`}
|
label={`${usedPermissionCount}/${totalAvailablePermissions}`}
|
||||||
size="small"
|
size="small"
|
||||||
color={usedPermissionCount > 0 ? "primary" : "default"}
|
sx={{
|
||||||
sx={{ ml: 1 }}
|
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 }}>
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, mt: 1 }}>
|
||||||
{appPermissions.reduce((services: string[], permission: Permission) => {
|
{appPermissions.reduce((services: string[], permission: Permission) => {
|
||||||
@@ -85,57 +92,74 @@ const ApplicationCard: React.FC<ApplicationCardProps> = ({ app, permissions }) =
|
|||||||
<Chip
|
<Chip
|
||||||
label={service.split(' ')[0]}
|
label={service.split(' ')[0]}
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
sx={{
|
||||||
color="primary"
|
height: 20,
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
backgroundColor: fill.primaryActive,
|
||||||
|
color: primary.light,
|
||||||
|
border: `1px solid ${border.primaryMedium}`,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{appPermissions.length === 0 && (
|
||||||
|
<Typography variant="caption" sx={{ color: text.dim }}>
|
||||||
|
No permissions granted
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Divider sx={{ my: 2 }} />
|
<Divider sx={{ my: 2 }} />
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||||
<Typography variant="caption">
|
<Typography variant="caption" sx={{ color: text.dim }}>
|
||||||
Created: {new Date(app.created_at).toLocaleDateString(undefined, {
|
{new Date(app.created_at).toLocaleDateString(undefined, {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
hour: '2-digit',
|
})}
|
||||||
minute: '2-digit',
|
|
||||||
second: '2-digit'
|
|
||||||
})}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Tooltip title="This key was displayed once. Regenerate if needed.">
|
<Tooltip title="This key was displayed once. Regenerate if needed.">
|
||||||
<Chip
|
<Chip
|
||||||
label="API Key Hidden"
|
label="Key Hidden"
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
sx={{
|
||||||
color="default"
|
fontFamily: 'monospace',
|
||||||
sx={{ fontFamily: 'monospace' }}
|
fontSize: '0.65rem',
|
||||||
|
height: 18,
|
||||||
|
backgroundColor: fill.subtle,
|
||||||
|
color: text.dim,
|
||||||
|
border: `1px solid ${border.default}`,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions sx={{ justifyContent: 'space-between', px: 2, pb: 2 }}>
|
<CardActions sx={{ justifyContent: 'space-between', px: 2, pb: 2, pt: 0 }}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
startIcon={<EditIcon />}
|
startIcon={<EditIcon sx={{ fontSize: '0.875rem !important' }} />}
|
||||||
onClick={() => openEditDialog(app)}
|
onClick={() => openEditDialog(app)}
|
||||||
color="primary"
|
sx={{
|
||||||
variant="text"
|
color: primary.main,
|
||||||
|
fontSize: '0.8rem',
|
||||||
|
'&:hover': { backgroundColor: fill.primaryActive },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
startIcon={<VpnKeyIcon />}
|
startIcon={<VpnKeyIcon sx={{ fontSize: '0.875rem !important' }} />}
|
||||||
onClick={() => regenerateApiKey(app)}
|
onClick={() => regenerateApiKey(app)}
|
||||||
color="secondary"
|
sx={{
|
||||||
variant="text"
|
color: secondary.main,
|
||||||
|
fontSize: '0.8rem',
|
||||||
|
'&:hover': { backgroundColor: 'rgba(34, 211, 238, 0.08)' },
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
New Key
|
New Key
|
||||||
</Button>
|
</Button>
|
||||||
@@ -144,4 +168,4 @@ const ApplicationCard: React.FC<ApplicationCardProps> = ({ app, permissions }) =
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ApplicationCard;
|
export default ApplicationCard;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
VpnKey as VpnKeyIcon
|
VpnKey as VpnKeyIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useApplications } from '../../context/ApplicationContext';
|
import { useApplications } from '../../context/ApplicationContext';
|
||||||
|
import { fill, text } from '../../theme/colors';
|
||||||
|
|
||||||
interface ApplicationMenuProps {
|
interface ApplicationMenuProps {
|
||||||
anchorEl: HTMLElement | null;
|
anchorEl: HTMLElement | null;
|
||||||
@@ -26,29 +27,19 @@ const ApplicationMenu: React.FC<ApplicationMenuProps> = ({ anchorEl, appId, onCl
|
|||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
open={Boolean(anchorEl)}
|
open={Boolean(anchorEl)}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
PaperProps={{
|
slotProps={{
|
||||||
elevation: 0,
|
paper: {
|
||||||
sx: {
|
elevation: 0,
|
||||||
overflow: 'visible',
|
sx: {
|
||||||
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
|
mt: 1,
|
||||||
mt: 1.5,
|
minWidth: 180,
|
||||||
'& .MuiAvatar-root': {
|
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.4)',
|
||||||
width: 32,
|
'& .MuiMenuItem-root': {
|
||||||
height: 32,
|
fontSize: '0.875rem',
|
||||||
ml: -0.5,
|
color: text.secondary,
|
||||||
mr: 1,
|
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,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
Container,
|
Container,
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
Paper,
|
|
||||||
Button
|
Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
import ApplicationCard from './ApplicationCard.tsx';
|
import ApplicationCard from './ApplicationCard.tsx';
|
||||||
import { Permission, UserInfo } from '../../types';
|
import { Permission, UserInfo } from '../../types';
|
||||||
import { useApplications } from '../../context/ApplicationContext';
|
import { useApplications } from '../../context/ApplicationContext';
|
||||||
|
import { primary, text, border, fill } from '../../theme/colors';
|
||||||
|
|
||||||
interface ApplicationsPageProps {
|
interface ApplicationsPageProps {
|
||||||
user: UserInfo;
|
user: UserInfo;
|
||||||
@@ -26,52 +26,67 @@ const ApplicationsPage: React.FC<ApplicationsPageProps> = ({ user, permissions }
|
|||||||
} = useApplications();
|
} = useApplications();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container maxWidth="xl" sx={{ py: 4, px: { xs: 2, sm: 3 } }}>
|
||||||
maxWidth="xl"
|
|
||||||
sx={{
|
|
||||||
mt: 4,
|
|
||||||
mb: 4,
|
|
||||||
flexGrow: 1,
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: '100% !important',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ mb: 4 }}>
|
<Box sx={{ mb: 4 }}>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
gutterBottom
|
||||||
|
sx={{
|
||||||
|
background: `linear-gradient(135deg, ${text.primary} 0%, ${text.tertiary} 100%)`,
|
||||||
|
WebkitBackgroundClip: 'text',
|
||||||
|
WebkitTextFillColor: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
Your Applications
|
Your Applications
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body1" color="text.secondary">
|
<Typography variant="body1" sx={{ color: text.tertiary }}>
|
||||||
Create and manage your applications
|
Create and manage your API applications
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{applications.length === 0 ? (
|
{applications.length === 0 ? (
|
||||||
<Paper
|
<Box
|
||||||
elevation={0}
|
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
my: 8,
|
my: 8,
|
||||||
py: 8,
|
py: 8,
|
||||||
bgcolor: 'background.paper',
|
borderRadius: 3,
|
||||||
borderRadius: 2,
|
backgroundColor: fill.subtle,
|
||||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
border: `1px solid ${border.default}`,
|
||||||
opacity: user.active ? 1 : 0.6
|
opacity: user.active ? 1 : 0.6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppsIcon sx={{ fontSize: 60, color: 'text.secondary', mb: 2 }} />
|
<Box
|
||||||
<Typography variant="h6" color="text.secondary" gutterBottom>
|
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
|
No applications yet
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ color: text.muted, mb: 3 }}>
|
||||||
|
Create your first app to get an API key
|
||||||
|
</Typography>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
startIcon={<AddIcon />}
|
startIcon={<AddIcon />}
|
||||||
onClick={openCreateDialog}
|
onClick={openCreateDialog}
|
||||||
disabled={!user.active}
|
disabled={!user.active}
|
||||||
sx={{ mt: 2 }}
|
|
||||||
>
|
>
|
||||||
Create Your First App
|
Create Your First App
|
||||||
</Button>
|
</Button>
|
||||||
</Paper>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
@@ -97,4 +112,4 @@ const ApplicationsPage: React.FC<ApplicationsPageProps> = ({ user, permissions }
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ApplicationsPage;
|
export default ApplicationsPage;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { ContentCopy as ContentCopyIcon } from '@mui/icons-material';
|
import { ContentCopy as ContentCopyIcon } from '@mui/icons-material';
|
||||||
|
import { secondary, border, fill } from '../../../theme/colors';
|
||||||
|
|
||||||
interface ApiKeyDialogProps {
|
interface ApiKeyDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -43,11 +44,11 @@ const ApiKeyDialog: React.FC<ApiKeyDialogProps> = ({
|
|||||||
sx={{
|
sx={{
|
||||||
p: 2,
|
p: 2,
|
||||||
mt: 2,
|
mt: 2,
|
||||||
bgcolor: 'rgba(255, 255, 255, 0.04)',
|
backgroundColor: fill.subtle,
|
||||||
|
borderColor: border.primaryDefault,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
border: '1px solid rgba(255, 255, 255, 0.15)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@@ -58,7 +59,7 @@ const ApiKeyDialog: React.FC<ApiKeyDialogProps> = ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
p: 1,
|
p: 1,
|
||||||
color: '#80ffea', // Match secondary color for monospace text
|
color: secondary.main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{apiKey}
|
{apiKey}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { Application, Permission } from '../../../types';
|
import { Application, Permission } from '../../../types';
|
||||||
import { useApplications } from '../../../context/ApplicationContext';
|
import { useApplications } from '../../../context/ApplicationContext';
|
||||||
|
import { border, fill } from '../../../theme/colors';
|
||||||
|
|
||||||
interface ApplicationFormDialogProps {
|
interface ApplicationFormDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -210,16 +211,14 @@ const ApplicationFormDialog: React.FC<ApplicationFormDialogProps> = ({
|
|||||||
key={service.name}
|
key={service.name}
|
||||||
sx={{
|
sx={{
|
||||||
mb: 2,
|
mb: 2,
|
||||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
|
||||||
borderRadius: '4px',
|
|
||||||
'&:before': { display: 'none' },
|
'&:before': { display: 'none' },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AccordionSummary
|
<AccordionSummary
|
||||||
expandIcon={<ExpandMoreIcon />}
|
expandIcon={<ExpandMoreIcon />}
|
||||||
sx={{
|
sx={{
|
||||||
borderBottom: '1px solid rgba(255, 255, 255, 0.12)',
|
borderBottom: `1px solid ${border.default}`,
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.03)',
|
backgroundColor: fill.subtle,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||||
@@ -240,10 +239,12 @@ const ApplicationFormDialog: React.FC<ApplicationFormDialogProps> = ({
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{
|
sx={{
|
||||||
p: 1,
|
p: 1,
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.03)',
|
backgroundColor: fill.subtle,
|
||||||
borderColor: 'rgba(255, 255, 255, 0.12)',
|
borderColor: border.default,
|
||||||
|
transition: 'border-color 0.15s ease',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
backgroundColor: fill.primaryHover,
|
||||||
|
borderColor: border.primaryDefault,
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user