Files
ld-sysinfo-frontend/src/components/ChangePasswordDrawer.tsx
2025-09-19 03:26:52 +00:00

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;