224 lines
7.0 KiB
TypeScript
224 lines
7.0 KiB
TypeScript
/**
|
|
* @ai-summary Security settings page for desktop application
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useSecurityStatus, useRequestPasswordReset } from '../features/settings/hooks/useSecurity';
|
|
import {
|
|
Box,
|
|
Typography,
|
|
List,
|
|
ListItem,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Divider,
|
|
Button as MuiButton,
|
|
CircularProgress,
|
|
Alert,
|
|
Chip,
|
|
} from '@mui/material';
|
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|
import LockIcon from '@mui/icons-material/Lock';
|
|
import VerifiedUserIcon from '@mui/icons-material/VerifiedUser';
|
|
import FingerprintIcon from '@mui/icons-material/Fingerprint';
|
|
import EmailIcon from '@mui/icons-material/Email';
|
|
import { Card } from '../shared-minimal/components/Card';
|
|
|
|
export const SecuritySettingsPage: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const { data: securityStatus, isLoading, error } = useSecurityStatus();
|
|
const passwordResetMutation = useRequestPasswordReset();
|
|
|
|
const handlePasswordReset = () => {
|
|
passwordResetMutation.mutate();
|
|
};
|
|
|
|
const handleBack = () => {
|
|
navigate('/garage/settings');
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Box sx={{ py: 2 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 4 }}>
|
|
<MuiButton
|
|
startIcon={<ArrowBackIcon />}
|
|
onClick={handleBack}
|
|
sx={{ mr: 2 }}
|
|
>
|
|
Back
|
|
</MuiButton>
|
|
<Typography variant="h4" sx={{ fontWeight: 700, color: 'text.primary' }}>
|
|
Security Settings
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
|
|
<CircularProgress />
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<Box sx={{ py: 2 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 4 }}>
|
|
<MuiButton
|
|
startIcon={<ArrowBackIcon />}
|
|
onClick={handleBack}
|
|
sx={{ mr: 2 }}
|
|
>
|
|
Back
|
|
</MuiButton>
|
|
<Typography variant="h4" sx={{ fontWeight: 700, color: 'text.primary' }}>
|
|
Security Settings
|
|
</Typography>
|
|
</Box>
|
|
<Alert severity="error" sx={{ mb: 2 }}>
|
|
Failed to load security settings. Please try again.
|
|
</Alert>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box sx={{ py: 2 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 4 }}>
|
|
<MuiButton
|
|
startIcon={<ArrowBackIcon />}
|
|
onClick={handleBack}
|
|
sx={{ mr: 2 }}
|
|
>
|
|
Back
|
|
</MuiButton>
|
|
<Typography variant="h4" sx={{ fontWeight: 700, color: 'text.primary' }}>
|
|
Security Settings
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
|
{/* Email Verification Status */}
|
|
<Card>
|
|
<Typography variant="h6" sx={{ fontWeight: 600, mb: 3 }}>
|
|
Account Verification
|
|
</Typography>
|
|
|
|
<List disablePadding>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
<EmailIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="Email Address"
|
|
secondary={securityStatus?.email || 'Not available'}
|
|
/>
|
|
</ListItem>
|
|
<Divider />
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
<VerifiedUserIcon color={securityStatus?.emailVerified ? 'success' : 'warning'} />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="Email Verification"
|
|
secondary={
|
|
securityStatus?.emailVerified
|
|
? 'Your email address has been verified'
|
|
: 'Your email address is not verified'
|
|
}
|
|
/>
|
|
<Chip
|
|
label={securityStatus?.emailVerified ? 'Verified' : 'Not Verified'}
|
|
color={securityStatus?.emailVerified ? 'success' : 'warning'}
|
|
size="small"
|
|
/>
|
|
</ListItem>
|
|
</List>
|
|
</Card>
|
|
|
|
{/* Password Management */}
|
|
<Card>
|
|
<Typography variant="h6" sx={{ fontWeight: 600, mb: 3 }}>
|
|
Password
|
|
</Typography>
|
|
|
|
<List disablePadding>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
<LockIcon />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="Change Password"
|
|
secondary="Receive an email with a link to reset your password"
|
|
/>
|
|
<MuiButton
|
|
variant="contained"
|
|
size="small"
|
|
onClick={handlePasswordReset}
|
|
disabled={passwordResetMutation.isPending}
|
|
sx={{
|
|
backgroundColor: 'primary.main',
|
|
color: 'primary.contrastText',
|
|
'&:hover': {
|
|
backgroundColor: 'primary.dark'
|
|
}
|
|
}}
|
|
>
|
|
{passwordResetMutation.isPending ? (
|
|
<CircularProgress size={20} color="inherit" />
|
|
) : (
|
|
'Reset Password'
|
|
)}
|
|
</MuiButton>
|
|
</ListItem>
|
|
</List>
|
|
|
|
{passwordResetMutation.isSuccess && (
|
|
<Alert severity="success" sx={{ mt: 2 }}>
|
|
Password reset email sent! Please check your inbox.
|
|
</Alert>
|
|
)}
|
|
</Card>
|
|
|
|
{/* Passkeys Information */}
|
|
<Card>
|
|
<Typography variant="h6" sx={{ fontWeight: 600, mb: 3 }}>
|
|
Passkeys
|
|
</Typography>
|
|
|
|
<List disablePadding>
|
|
<ListItem>
|
|
<ListItemIcon>
|
|
<FingerprintIcon color={securityStatus?.passkeysEnabled ? 'primary' : 'disabled'} />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="Passkey Authentication"
|
|
secondary={
|
|
securityStatus?.passkeysEnabled
|
|
? 'Passkeys are available for your account'
|
|
: 'Passkeys are not enabled'
|
|
}
|
|
/>
|
|
<Chip
|
|
label={securityStatus?.passkeysEnabled ? 'Available' : 'Not Available'}
|
|
color={securityStatus?.passkeysEnabled ? 'primary' : 'default'}
|
|
size="small"
|
|
variant="outlined"
|
|
/>
|
|
</ListItem>
|
|
</List>
|
|
|
|
<Alert severity="info" sx={{ mt: 2 }}>
|
|
<Typography variant="body2">
|
|
<strong>About Passkeys:</strong> Passkeys are a secure, passwordless way to sign in using your device's biometric authentication (fingerprint, face recognition) or PIN.
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ mt: 1 }}>
|
|
You can register a passkey during the sign-in process. When you see the option to "Create a passkey," follow the prompts to set up passwordless authentication.
|
|
</Typography>
|
|
</Alert>
|
|
</Card>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|