You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
14 KiB
265 lines
14 KiB
import React, { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useAuth } from '../contexts/AuthContext';
|
|
import Navbar from '../components/Navbar';
|
|
import EmailVerificationModal from '../modals/EmailVerificationModal';
|
|
import { verifyEmail } from '../services/user.service';
|
|
import GitHubLoginButton from '../components/GitHubLoginButton';
|
|
|
|
export default function Register() {
|
|
const [formData, setFormData] = useState({
|
|
email: '',
|
|
username: '',
|
|
password: '',
|
|
confirmPassword: '',
|
|
profile: null
|
|
});
|
|
const [error, setError] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [previewImage, setPreviewImage] = useState(null);
|
|
const [alerts, setAlerts] = useState([]);
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
const [isEmailVerificationModalOpen, setEmailVerificationModalOpen] = useState(false);
|
|
|
|
const navigate = useNavigate();
|
|
const { register } = useAuth();
|
|
|
|
const handleChange = (e) => {
|
|
if (e.target.name === 'profile') {
|
|
const file = e.target.files[0];
|
|
setFormData({
|
|
...formData,
|
|
profile: file
|
|
});
|
|
|
|
// Create preview
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => setPreviewImage(e.target.result);
|
|
reader.readAsDataURL(file);
|
|
} else {
|
|
setPreviewImage(null);
|
|
}
|
|
} else {
|
|
setFormData({
|
|
...formData,
|
|
[e.target.name]: e.target.value
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
|
|
// Validation
|
|
if (formData.password !== formData.confirmPassword) {
|
|
addAlert("error", "Les mots de passe ne correspondent pas");
|
|
return;
|
|
}
|
|
|
|
if (formData.password.length < 6) {
|
|
addAlert("error", "Le mot de passe doit contenir au moins 6 caractères");
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
await register(formData.email, formData.username, formData.password, formData.profile);
|
|
setEmailVerificationModalOpen(true);
|
|
} catch (err) {
|
|
addAlert('error', 'Erreur lors de la création du compte');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const onVerificationSubmit = async (token) => {
|
|
|
|
|
|
|
|
const response = await verifyEmail(formData.email, token, addAlert);
|
|
|
|
if (response) {
|
|
// Email verified successfully
|
|
setEmailVerificationModalOpen(false);
|
|
navigate('/login');
|
|
}
|
|
|
|
}
|
|
|
|
const addAlert = (type, message) => {
|
|
const newAlert = { type, message, id: Date.now() }; // Add unique ID
|
|
setAlerts([...alerts, newAlert]);
|
|
};
|
|
|
|
const onCloseAlert = (alertToRemove) => {
|
|
setAlerts(alerts.filter(alert => alert !== alertToRemove));
|
|
};
|
|
|
|
return (
|
|
<div className="min-w-screen min-h-screen bg-linear-to-br from-left-gradient to-right-gradient">
|
|
<Navbar isSearchPage={false} alerts={alerts} onCloseAlert={onCloseAlert} />
|
|
|
|
<div className="flex justify-center items-center min-h-screen pt-20 pb-10">
|
|
<div className="glassmorphism p-8 rounded-lg shadow-lg w-full max-w-md">
|
|
<h2 className="text-3xl font-bold text-center mb-6 font-montserrat text-white">Créer un compte</h2>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium font-montserrat text-white mb-1">
|
|
Email
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
name="email"
|
|
value={formData.email}
|
|
onChange={handleChange}
|
|
required
|
|
className="w-full px-3 py-2 glassmorphism focus:outline-none text-white"
|
|
placeholder="Entrez votre email"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="username" className="block text-sm font-medium font-montserrat text-white mb-1">
|
|
Nom d'utilisateur
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="username"
|
|
name="username"
|
|
value={formData.username}
|
|
onChange={handleChange}
|
|
required
|
|
className="w-full px-3 py-2 glassmorphism focus:outline-none text-white"
|
|
placeholder="Entrez votre nom d'utilisateur"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="password" className="block text-sm font-medium font-montserrat text-white mb-1">
|
|
Mot de passe
|
|
</label>
|
|
<div className='w-full glassmorphism flex items-center'>
|
|
<input
|
|
type={showPassword ? "text" : "password"}
|
|
id="password"
|
|
name="password"
|
|
value={formData.password}
|
|
onChange={handleChange}
|
|
required
|
|
className="flex-1 px-3 py-2 focus:outline-none text-white"
|
|
placeholder="Entrez votre mot de passe"
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
className="absolute right-3 top-1/2 transform -translate-y-1/2"
|
|
>
|
|
{showPassword ? (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" className='fill-white' viewBox="0 0 24 24" >
|
|
<path d="M12 9a3 3 0 1 0 0 6 3 3 0 1 0 0-6"></path><path d="M12 19c7.63 0 9.93-6.62 9.95-6.68.07-.21.07-.43 0-.63-.02-.07-2.32-6.68-9.95-6.68s-9.93 6.61-9.95 6.67c-.07.21-.07.43 0 .63.02.07 2.32 6.68 9.95 6.68Zm0-12c5.35 0 7.42 3.85 7.93 5-.5 1.16-2.58 5-7.93 5s-7.42-3.84-7.93-5c.5-1.16 2.58-5 7.93-5"></path>
|
|
</svg>
|
|
) : (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className='fill-white' >
|
|
<path d="M12 17c-5.35 0-7.42-3.84-7.93-5 .2-.46.65-1.34 1.45-2.23l-1.4-1.4c-1.49 1.65-2.06 3.28-2.08 3.31-.07.21-.07.43 0 .63.02.07 2.32 6.68 9.95 6.68.91 0 1.73-.1 2.49-.26l-1.77-1.77c-.24.02-.47.03-.72.03ZM21.95 12.32c.07-.21.07-.43 0-.63-.02-.07-2.32-6.68-9.95-6.68-1.84 0-3.36.39-4.61.97L2.71 1.29 1.3 2.7l4.32 4.32 1.42 1.42 2.27 2.27 3.98 3.98 1.8 1.8 1.53 1.53 4.68 4.68 1.41-1.41-4.32-4.32c2.61-1.95 3.55-4.61 3.56-4.65m-7.25.97c.19-.39.3-.83.3-1.29 0-1.64-1.36-3-3-3-.46 0-.89.11-1.29.3l-1.8-1.8c.88-.31 1.9-.5 3.08-.5 5.35 0 7.42 3.85 7.93 5-.3.69-1.18 2.33-2.96 3.55z"></path>
|
|
</svg>
|
|
)}
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<p className="text-xs text-gray-400 mt-1">Le mot de passe doit contenir au moins :</p>
|
|
<ul className="list-disc list-inside text-xs text-gray-400">
|
|
<li>8 caractères</li>
|
|
<li>Une lettre majuscule</li>
|
|
<li>Une lettre minuscule</li>
|
|
<li>Un chiffre</li>
|
|
<li>Un caractère spécial (ex: !@#$%^&*)</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="confirmPassword" className="block text-sm font-medium font-montserrat text-white mb-1">
|
|
Confirmer le mot de passe
|
|
</label>
|
|
<div className='w-full glassmorphism flex items-center'>
|
|
<input
|
|
type={showConfirmPassword ? "text" : "password"}
|
|
id="confirmPassword"
|
|
name="confirmPassword"
|
|
value={formData.confirmPassword}
|
|
onChange={handleChange}
|
|
required
|
|
className="flex-1 px-3 py-2 focus:outline-none text-white"
|
|
placeholder="Confirmez votre mot de passe"
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
|
className="absolute right-3 top-1/2 transform -translate-y-1/2"
|
|
>
|
|
{showPassword ? (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" className='fill-white' viewBox="0 0 24 24" >
|
|
<path d="M12 9a3 3 0 1 0 0 6 3 3 0 1 0 0-6"></path><path d="M12 19c7.63 0 9.93-6.62 9.95-6.68.07-.21.07-.43 0-.63-.02-.07-2.32-6.68-9.95-6.68s-9.93 6.61-9.95 6.67c-.07.21-.07.43 0 .63.02.07 2.32 6.68 9.95 6.68Zm0-12c5.35 0 7.42 3.85 7.93 5-.5 1.16-2.58 5-7.93 5s-7.42-3.84-7.93-5c.5-1.16 2.58-5 7.93-5"></path>
|
|
</svg>
|
|
) : (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" className='fill-white' >
|
|
<path d="M12 17c-5.35 0-7.42-3.84-7.93-5 .2-.46.65-1.34 1.45-2.23l-1.4-1.4c-1.49 1.65-2.06 3.28-2.08 3.31-.07.21-.07.43 0 .63.02.07 2.32 6.68 9.95 6.68.91 0 1.73-.1 2.49-.26l-1.77-1.77c-.24.02-.47.03-.72.03ZM21.95 12.32c.07-.21.07-.43 0-.63-.02-.07-2.32-6.68-9.95-6.68-1.84 0-3.36.39-4.61.97L2.71 1.29 1.3 2.7l4.32 4.32 1.42 1.42 2.27 2.27 3.98 3.98 1.8 1.8 1.53 1.53 4.68 4.68 1.41-1.41-4.32-4.32c2.61-1.95 3.55-4.61 3.56-4.65m-7.25.97c.19-.39.3-.83.3-1.29 0-1.64-1.36-3-3-3-.46 0-.89.11-1.29.3l-1.8-1.8c.88-.31 1.9-.5 3.08-.5 5.35 0 7.42 3.85 7.93 5-.3.69-1.18 2.33-2.96 3.55z"></path>
|
|
</svg>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="profile" className="block text-sm font-medium font-montserrat text-white mb-1">
|
|
Photo de profil
|
|
</label>
|
|
<input
|
|
type="file"
|
|
id="profile"
|
|
name="profile"
|
|
accept="image/*"
|
|
onChange={handleChange}
|
|
className="w-full px-3 py-2 glassmorphism focus:outline-none text-white"
|
|
/>
|
|
{previewImage && (
|
|
<div className="mt-2">
|
|
<img
|
|
src={previewImage}
|
|
alt="Aperçu"
|
|
className="w-20 h-20 object-cover rounded-full mx-auto"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-blue-500 disabled:opacity-50 font-montserrat"
|
|
>
|
|
{loading ? 'Création du compte...' : 'Créer un compte'}
|
|
</button>
|
|
</form>
|
|
|
|
<GitHubLoginButton className="mt-4 cursor-pointer" />
|
|
|
|
<div className="mt-6 text-center">
|
|
<p className="text-gray-600">
|
|
Déjà un compte ?{' '}
|
|
<a href="/login" className="text-blue-600 hover:underline">
|
|
Se connecter
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<EmailVerificationModal isOpen={isEmailVerificationModalOpen} onSubmit={onVerificationSubmit} onClose={() => setEmailVerificationModalOpen(false)} />
|
|
</div>
|
|
);
|
|
}
|
|
|