Files
orchard/frontend/src/pages/ChangePasswordPage.tsx

157 lines
5.4 KiB
TypeScript

import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { changePassword } from '../api';
import './LoginPage.css';
function ChangePasswordPage() {
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const { user, refreshUser } = useAuth();
const navigate = useNavigate();
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!currentPassword || !newPassword || !confirmPassword) {
setError('Please fill in all fields');
return;
}
if (newPassword !== confirmPassword) {
setError('New passwords do not match');
return;
}
if (newPassword.length < 8) {
setError('New password must be at least 8 characters');
return;
}
if (newPassword === currentPassword) {
setError('New password must be different from current password');
return;
}
setIsSubmitting(true);
setError(null);
try {
await changePassword(currentPassword, newPassword);
// Refresh user to clear must_change_password flag
await refreshUser();
navigate('/', { replace: true });
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to change password');
} finally {
setIsSubmitting(false);
}
}
return (
<div className="login-page">
<div className="login-container">
<div className="login-card">
<div className="login-header">
<div className="login-logo">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 14 Q6 8 3 8 Q6 4 6 4 Q6 4 9 8 Q6 8 6 14" fill="currentColor" opacity="0.6"/>
<rect x="5.25" y="13" width="1.5" height="4" fill="currentColor" opacity="0.6"/>
<path d="M12 12 Q12 5 8 5 Q12 1 12 1 Q12 1 16 5 Q12 5 12 12" fill="currentColor"/>
<rect x="11.25" y="11" width="1.5" height="5" fill="currentColor"/>
<path d="M18 14 Q18 8 15 8 Q18 4 18 4 Q18 4 21 8 Q18 8 18 14" fill="currentColor" opacity="0.6"/>
<rect x="17.25" y="13" width="1.5" height="4" fill="currentColor" opacity="0.6"/>
<ellipse cx="12" cy="19" rx="9" ry="1.5" fill="currentColor" opacity="0.3"/>
</svg>
</div>
<h1>Change Password</h1>
{user?.must_change_password && (
<p className="login-subtitle login-warning">
You must change your password before continuing
</p>
)}
</div>
{error && (
<div className="login-error">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>
<span>{error}</span>
</div>
)}
<form onSubmit={handleSubmit} className="login-form">
<div className="login-form-group">
<label htmlFor="currentPassword">Current Password</label>
<input
id="currentPassword"
type="password"
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
placeholder="Enter current password"
autoComplete="current-password"
autoFocus
disabled={isSubmitting}
/>
</div>
<div className="login-form-group">
<label htmlFor="newPassword">New Password</label>
<input
id="newPassword"
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
placeholder="Enter new password (min 8 characters)"
autoComplete="new-password"
disabled={isSubmitting}
/>
</div>
<div className="login-form-group">
<label htmlFor="confirmPassword">Confirm New Password</label>
<input
id="confirmPassword"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm new password"
autoComplete="new-password"
disabled={isSubmitting}
/>
</div>
<button
type="submit"
className="login-submit"
disabled={isSubmitting}
>
{isSubmitting ? (
<>
<span className="login-spinner"></span>
Changing password...
</>
) : (
'Change Password'
)}
</button>
</form>
</div>
<div className="login-footer">
<p>Artifact storage and management system</p>
</div>
</div>
</div>
);
}
export default ChangePasswordPage;