152 lines
5.4 KiB
JavaScript
152 lines
5.4 KiB
JavaScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { Card, Input, Button, PasswordStrengthIndicator } from '@zen/core/shared/components';
|
|
|
|
export default function ResetPasswordPage({ onSubmit, onNavigate, email, token }) {
|
|
const [error, setError] = useState('');
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [success, setSuccess] = useState('');
|
|
const [formData, setFormData] = useState({ newPassword: '', confirmPassword: '' });
|
|
|
|
const validatePassword = (password) => {
|
|
const errors = [];
|
|
if (password.length < 8) errors.push('Le mot de passe doit contenir au moins 8 caractères');
|
|
if (password.length > 128) errors.push('Le mot de passe doit contenir 128 caractères ou moins');
|
|
if (!/[A-Z]/.test(password)) errors.push('Le mot de passe doit contenir au moins une majuscule');
|
|
if (!/[a-z]/.test(password)) errors.push('Le mot de passe doit contenir au moins une minuscule');
|
|
if (!/\d/.test(password)) errors.push('Le mot de passe doit contenir au moins un chiffre');
|
|
return errors;
|
|
};
|
|
|
|
const isFormValid = () => {
|
|
return validatePassword(formData.newPassword).length === 0 &&
|
|
formData.newPassword === formData.confirmPassword &&
|
|
formData.newPassword.length > 0;
|
|
};
|
|
|
|
async function handleSubmit(e) {
|
|
e.preventDefault();
|
|
setError('');
|
|
setSuccess('');
|
|
setIsLoading(true);
|
|
|
|
const passwordErrors = validatePassword(formData.newPassword);
|
|
if (passwordErrors.length > 0) { setError(passwordErrors[0]); setIsLoading(false); return; }
|
|
if (formData.newPassword !== formData.confirmPassword) {
|
|
setError('Les mots de passe ne correspondent pas');
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
const submitData = new FormData();
|
|
submitData.append('newPassword', formData.newPassword);
|
|
submitData.append('confirmPassword', formData.confirmPassword);
|
|
submitData.append('email', email);
|
|
submitData.append('token', token);
|
|
|
|
try {
|
|
const result = await onSubmit(submitData);
|
|
|
|
if (result.success) {
|
|
setSuccess(result.message);
|
|
setIsLoading(false);
|
|
setTimeout(() => onNavigate('login'), 2000);
|
|
} else {
|
|
setError(result.error || 'Échec de la réinitialisation du mot de passe');
|
|
setIsLoading(false);
|
|
}
|
|
} catch (err) {
|
|
console.error('Reset password error:', err);
|
|
setError('Une erreur inattendue s\'est produite');
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Card variant="default" padding="md" spacing="none" className="w-full max-w-md">
|
|
<div className="text-center mb-6">
|
|
<h1 className="text-2xl font-bold text-neutral-900 dark:text-white mb-2">
|
|
Réinitialiser le mot de passe
|
|
</h1>
|
|
<p className="text-sm text-neutral-600 dark:text-neutral-400">
|
|
Saisissez votre nouveau mot de passe ci-dessous.
|
|
</p>
|
|
</div>
|
|
|
|
{error && !success && (
|
|
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg dark:bg-red-500/10 dark:border-red-500/20">
|
|
<div className="flex items-center space-x-2">
|
|
<div className="w-1.5 h-1.5 bg-red-500 rounded-full flex-shrink-0"></div>
|
|
<span className="text-xs text-red-700 dark:text-red-400">{error}</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{success && (
|
|
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg dark:bg-green-500/10 dark:border-green-500/20">
|
|
<div className="flex items-center space-x-2">
|
|
<div className="w-1.5 h-1.5 bg-green-500 rounded-full flex-shrink-0"></div>
|
|
<span className="text-xs text-green-700 dark:text-green-400">{success}</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
<div>
|
|
<Input
|
|
id="newPassword"
|
|
name="newPassword"
|
|
type="password"
|
|
label="Nouveau mot de passe"
|
|
value={formData.newPassword}
|
|
onChange={(value) => setFormData(prev => ({ ...prev, newPassword: value }))}
|
|
placeholder="••••••••"
|
|
disabled={!!success}
|
|
minLength="8"
|
|
maxLength="128"
|
|
autoComplete="new-password"
|
|
required
|
|
/>
|
|
<PasswordStrengthIndicator password={formData.newPassword} showRequirements={true} />
|
|
</div>
|
|
|
|
<Input
|
|
id="confirmPassword"
|
|
name="confirmPassword"
|
|
type="password"
|
|
label="Confirmer le mot de passe"
|
|
value={formData.confirmPassword}
|
|
onChange={(value) => setFormData(prev => ({ ...prev, confirmPassword: value }))}
|
|
placeholder="••••••••"
|
|
disabled={!!success}
|
|
minLength="8"
|
|
maxLength="128"
|
|
autoComplete="new-password"
|
|
required
|
|
/>
|
|
|
|
<Button
|
|
type="submit"
|
|
variant="primary"
|
|
loading={isLoading}
|
|
disabled={!!success || !isFormValid()}
|
|
className="w-full mt-2"
|
|
>
|
|
Réinitialiser le mot de passe
|
|
</Button>
|
|
</form>
|
|
|
|
<div className="mt-6 text-center">
|
|
<a
|
|
href="#"
|
|
onClick={(e) => { e.preventDefault(); onNavigate('login'); }}
|
|
className="text-sm text-neutral-900 hover:text-neutral-600 font-medium transition-colors duration-200 dark:text-white dark:hover:text-neutral-300"
|
|
>
|
|
← Retour à la connexion
|
|
</a>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|