189 lines
4.7 KiB
TypeScript
189 lines
4.7 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
Drawer,
|
|
Box,
|
|
Typography,
|
|
TextField,
|
|
Button,
|
|
Alert,
|
|
IconButton,
|
|
LinearProgress
|
|
} from '@mui/material';
|
|
import CloseIcon from '@mui/icons-material/Close';
|
|
import { encrypt } from '@/app/utils/encryption';
|
|
import { getPasswordStrength } from '@/app/utils/passwordStrength';
|
|
import { useAuth } from '@/context/AuthContext';
|
|
import Cookies from 'js-cookie';
|
|
|
|
const ChangePasswordDrawer = ({
|
|
open,
|
|
onClose
|
|
}: {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
}) => {
|
|
const { username, authToken } = useAuth();
|
|
//console.log('🧪 username:', username);
|
|
//console.log('🧪 authToken:', authToken);
|
|
|
|
const [currentPassword, setCurrentPassword] = useState('');
|
|
const [newPassword, setNewPassword] = useState('');
|
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
const [message, setMessage] = useState('');
|
|
const [error, setError] = useState('');
|
|
|
|
|
|
const strength = getPasswordStrength(newPassword);
|
|
const strengthValue = strength === 'Weak' ? 30 : strength === 'Medium' ? 60 : 100;
|
|
|
|
|
|
useEffect(() => {
|
|
if (!open) resetForm();
|
|
}, [open]);
|
|
|
|
const resetForm = () => {
|
|
setCurrentPassword('');
|
|
setNewPassword('');
|
|
setConfirmPassword('');
|
|
setMessage('');
|
|
setError('');
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setMessage('');
|
|
setError('');
|
|
|
|
if (!username) {
|
|
setError('User not authenticated.');
|
|
return;
|
|
}
|
|
|
|
|
|
if (newPassword !== confirmPassword) {
|
|
setError('New passwords do not match.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch('/api/auth/change-password', {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
credentials: 'include',
|
|
body: JSON.stringify({
|
|
currentPassword: encrypt(currentPassword),
|
|
newPassword: encrypt(newPassword),
|
|
}),
|
|
});
|
|
|
|
const text = await res.text();
|
|
if (res.ok) {
|
|
setMessage(text);
|
|
} else {
|
|
setError(text);
|
|
}
|
|
} catch (err) {
|
|
setError('An unexpected error occurred.');
|
|
}
|
|
};
|
|
|
|
|
|
return (
|
|
<Drawer anchor="left" open={open} onClose={onClose} transitionDuration={300}>
|
|
<Box sx={{ width: 350, p: 3 }}>
|
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
|
<Typography variant="h6">Change Password</Typography>
|
|
<IconButton onClick={onClose}>
|
|
<CloseIcon />
|
|
</IconButton>
|
|
</Box>
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<TextField
|
|
type="password"
|
|
label="Current Password"
|
|
fullWidth
|
|
margin="normal"
|
|
value={currentPassword}
|
|
onChange={(e) => setCurrentPassword(e.target.value)}
|
|
required
|
|
/>
|
|
|
|
<TextField
|
|
type="password"
|
|
label="New Password"
|
|
fullWidth
|
|
margin="normal"
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
required
|
|
/>
|
|
|
|
{/* Password Strength Bar */}
|
|
<Box sx={{ mt: 1 }}>
|
|
<Typography variant="caption">Strength: {strength}</Typography>
|
|
<LinearProgress
|
|
variant="determinate"
|
|
value={strengthValue}
|
|
color={
|
|
strength === 'Strong'
|
|
? 'success'
|
|
: strength === 'Medium'
|
|
? 'warning'
|
|
: 'error'
|
|
}
|
|
sx={{ height: 6, borderRadius: 2, mt: 0.5 }}
|
|
/>
|
|
</Box>
|
|
|
|
<TextField
|
|
type="password"
|
|
label="Confirm New Password"
|
|
fullWidth
|
|
margin="normal"
|
|
value={confirmPassword}
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
required
|
|
/>
|
|
|
|
<Button
|
|
type="submit"
|
|
variant="contained"
|
|
fullWidth
|
|
sx={{ mt: 2 }}
|
|
disabled={username === 'testuser'}
|
|
>
|
|
Submit
|
|
</Button>
|
|
|
|
{username === 'testuser' && (
|
|
<Box
|
|
sx={{
|
|
mt: 1,
|
|
backgroundColor: '#f5f5f5',
|
|
p: 1.5,
|
|
borderRadius: 1,
|
|
borderLeft: '4px solid #0288d1',
|
|
fontSize: '0.875rem',
|
|
color: '#333',
|
|
}}
|
|
>
|
|
Password changes are disabled for this user.
|
|
</Box>
|
|
)}
|
|
|
|
|
|
|
|
</form>
|
|
|
|
{message && <Alert severity="success" sx={{ mt: 2 }}>{message}</Alert>}
|
|
{error && <Alert severity="error" sx={{ mt: 2 }}>{error}</Alert>}
|
|
</Box>
|
|
</Drawer>
|
|
);
|
|
};
|
|
|
|
export default ChangePasswordDrawer;
|