mise a jour
This commit is contained in:
@@ -1,32 +1,30 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useRef } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { registerApi } from '../services/auth'
|
||||
import { registerApi, verifyCodeApi } from '../services/auth'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
|
||||
export default function Inscription() {
|
||||
const navigate = useNavigate()
|
||||
const [form, setForm] = useState({ email: '', lastName: '', firstName: '', password: '' })
|
||||
const { login } = useAuth()
|
||||
|
||||
// Étape : 'form' | 'verify'
|
||||
const [step, setStep] = useState('form')
|
||||
const [email, setEmail] = useState('')
|
||||
const [form, setForm] = useState({ email: '', lastName: '', firstName: '', password: '' })
|
||||
const [acceptedTerms, setAcceptedTerms] = useState(false)
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [successName, setSuccessName] = useState(null)
|
||||
const [countdown, setCountdown] = useState(5)
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (successName === null) return
|
||||
if (countdown === 0) {
|
||||
navigate('/connexion', { state: { registered: true } })
|
||||
return
|
||||
}
|
||||
const t = setTimeout(() => setCountdown((c) => c - 1), 1000)
|
||||
return () => clearTimeout(t)
|
||||
}, [successName, countdown, navigate])
|
||||
// Code à 4 chiffres
|
||||
const [digits, setDigits] = useState(['', '', '', ''])
|
||||
const [codeError, setCodeError] = useState('')
|
||||
const [codeLoading, setCodeLoading] = useState(false)
|
||||
const inputRefs = [useRef(), useRef(), useRef(), useRef()]
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target
|
||||
setForm({ ...form, [name]: value })
|
||||
}
|
||||
const handleChange = (e) => setForm({ ...form, [e.target.name]: e.target.value })
|
||||
|
||||
// Étape 1 — Inscription
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
setError('')
|
||||
@@ -38,7 +36,8 @@ export default function Inscription() {
|
||||
lastname: form.lastName,
|
||||
password: form.password,
|
||||
})
|
||||
setSuccessName(form.firstName)
|
||||
setEmail(form.email)
|
||||
setStep('verify')
|
||||
} catch (err) {
|
||||
setError(err.message)
|
||||
} finally {
|
||||
@@ -46,9 +45,106 @@ export default function Inscription() {
|
||||
}
|
||||
}
|
||||
|
||||
// Étape 2 — Saisie du code
|
||||
const handleDigit = (index, value) => {
|
||||
if (!/^\d?$/.test(value)) return
|
||||
const next = [...digits]
|
||||
next[index] = value
|
||||
setDigits(next)
|
||||
if (value && index < 3) inputRefs[index + 1].current?.focus()
|
||||
}
|
||||
|
||||
const handleDigitKeyDown = (index, e) => {
|
||||
if (e.key === 'Backspace' && !digits[index] && index > 0) {
|
||||
inputRefs[index - 1].current?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
const handleVerify = async (e) => {
|
||||
e.preventDefault()
|
||||
const code = digits.join('')
|
||||
if (code.length !== 4) return
|
||||
setCodeError('')
|
||||
setCodeLoading(true)
|
||||
try {
|
||||
const data = await verifyCodeApi({ email, code })
|
||||
login(data.user, data.token)
|
||||
navigate('/saisir-code-unique')
|
||||
} catch (err) {
|
||||
setCodeError(err.message)
|
||||
setDigits(['', '', '', ''])
|
||||
inputRefs[0].current?.focus()
|
||||
} finally {
|
||||
setCodeLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Étape 2 : Vérification du code ──
|
||||
if (step === 'verify') {
|
||||
return (
|
||||
<div className="min-h-screen bg-white flex flex-col justify-center px-8 py-12 max-w-sm mx-auto">
|
||||
<div className="mb-8 text-center">
|
||||
<div className="w-14 h-14 rounded-full bg-[#006B6B]/10 flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-7 h-7 text-[#006B6B]" fill="none" stroke="currentColor" strokeWidth={1.5} viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" />
|
||||
</svg>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold italic font-serif text-[#006B6B]">Vérifiez votre email</h1>
|
||||
<p className="text-gray-500 text-sm mt-2">
|
||||
Un code à 4 chiffres a été envoyé à<br />
|
||||
<span className="font-medium text-gray-700">{email}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{codeError && (
|
||||
<div className="mb-4 px-4 py-3 rounded-lg bg-red-50 border border-red-200 text-red-600 text-sm text-center">
|
||||
{codeError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleVerify} className="space-y-6">
|
||||
<div className="flex justify-center gap-3">
|
||||
{digits.map((d, i) => (
|
||||
<input
|
||||
key={i}
|
||||
ref={inputRefs[i]}
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
maxLength={1}
|
||||
value={d}
|
||||
onChange={(e) => handleDigit(i, e.target.value)}
|
||||
onKeyDown={(e) => handleDigitKeyDown(i, e)}
|
||||
className="w-14 h-14 text-center text-2xl font-bold border-2 border-gray-200 rounded-xl focus:outline-none focus:border-[#006B6B] focus:ring-1 focus:ring-[#006B6B] transition-colors"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={codeLoading || digits.join('').length !== 4}
|
||||
className="w-full bg-[#006B6B] hover:bg-[#005959] disabled:opacity-60 text-white font-medium py-3 rounded-lg transition-colors"
|
||||
>
|
||||
{codeLoading ? 'Vérification…' : 'Valider le code'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-center text-xs text-gray-400 mt-6">
|
||||
Le code expire dans 15 minutes.{' '}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setStep('form'); setDigits(['', '', '', '']); setCodeError('') }}
|
||||
className="text-[#006B6B] hover:underline"
|
||||
>
|
||||
Recommencer
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ── Étape 1 : Formulaire d'inscription ──
|
||||
return (
|
||||
<div className="min-h-screen bg-white flex flex-col justify-center px-8 py-12 max-w-sm mx-auto">
|
||||
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl font-bold italic font-serif text-[#006B6B]">S'inscrire</h1>
|
||||
<p className="text-gray-500 mt-1 text-sm">Créer votre compte</p>
|
||||
@@ -62,70 +158,30 @@ export default function Inscription() {
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-5">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">
|
||||
Adresse email <span className="text-gray-800">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={form.email}
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre email"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">Adresse email <span className="text-gray-800">*</span></label>
|
||||
<input type="email" name="email" value={form.email} onChange={handleChange} required placeholder="Votre email"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">
|
||||
Nom <span className="text-gray-800">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="lastName"
|
||||
value={form.lastName}
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre nom"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">Nom <span className="text-gray-800">*</span></label>
|
||||
<input type="text" name="lastName" value={form.lastName} onChange={handleChange} required placeholder="Votre nom"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">
|
||||
Prénom <span className="text-gray-800">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="firstName"
|
||||
value={form.firstName}
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre prénom"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">Prénom <span className="text-gray-800">*</span></label>
|
||||
<input type="text" name="firstName" value={form.firstName} onChange={handleChange} required placeholder="Votre prénom"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">
|
||||
Mot de passe <span className="text-gray-800">*</span>
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-gray-800 mb-1">Mot de passe <span className="text-gray-800">*</span></label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
name="password"
|
||||
value={form.password}
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre mot de passe"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-12 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
aria-label="Afficher/masquer le mot de passe"
|
||||
>
|
||||
<input type={showPassword ? 'text' : 'password'} name="password" value={form.password} onChange={handleChange} required placeholder="Votre mot de passe"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-12 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
<button type="button" onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600" aria-label="Afficher/masquer">
|
||||
<div className="w-6 h-6 rounded-full border-2 border-gray-400 flex items-center justify-center">
|
||||
{showPassword && <div className="w-1.5 h-1.5 rounded-full bg-gray-400" />}
|
||||
</div>
|
||||
@@ -134,31 +190,19 @@ export default function Inscription() {
|
||||
</div>
|
||||
|
||||
<label className="flex items-start gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={acceptedTerms}
|
||||
onChange={(e) => setAcceptedTerms(e.target.checked)}
|
||||
required
|
||||
className="mt-0.5 w-4 h-4 accent-[#006B6B] flex-shrink-0"
|
||||
/>
|
||||
<input type="checkbox" checked={acceptedTerms} onChange={(e) => setAcceptedTerms(e.target.checked)} required
|
||||
className="mt-0.5 w-4 h-4 accent-[#006B6B] flex-shrink-0" />
|
||||
<span className="text-xs text-gray-500 leading-relaxed">
|
||||
J'accepte les{' '}
|
||||
<Link to="/mentions-legales" className="text-[#006B6B] hover:underline font-medium">
|
||||
conditions d'utilisation
|
||||
</Link>{' '}
|
||||
<Link to="/mentions-legales" className="text-[#006B6B] hover:underline font-medium">conditions d'utilisation</Link>{' '}
|
||||
et la{' '}
|
||||
<Link to="/confidentialite" className="text-[#006B6B] hover:underline font-medium">
|
||||
politique de confidentialité
|
||||
</Link>
|
||||
<Link to="/confidentialite" className="text-[#006B6B] hover:underline font-medium">politique de confidentialité</Link>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !acceptedTerms}
|
||||
className="w-full bg-[#006B6B] hover:bg-[#005959] disabled:opacity-60 text-white font-medium py-3 rounded-lg transition-colors"
|
||||
>
|
||||
{loading ? 'Inscription...' : "S'inscrire"}
|
||||
<button type="submit" disabled={loading || !acceptedTerms}
|
||||
className="w-full bg-[#006B6B] hover:bg-[#005959] disabled:opacity-60 text-white font-medium py-3 rounded-lg transition-colors">
|
||||
{loading ? 'Inscription…' : "S'inscrire"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -183,28 +227,6 @@ export default function Inscription() {
|
||||
Vous avez déjà un compte ?{' '}
|
||||
<Link to="/connexion" className="text-gray-700 font-medium hover:underline">Se connecter</Link>
|
||||
</p>
|
||||
|
||||
{/* Modal succès */}
|
||||
{successName !== null && (
|
||||
<div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50 px-6">
|
||||
<div className="bg-white rounded-2xl p-8 w-full max-w-sm text-center shadow-xl">
|
||||
<div className="flex justify-center mb-5">
|
||||
<div className="w-16 h-16 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<svg className="w-9 h-9 text-white" fill="none" stroke="currentColor" strokeWidth={3} viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
Bienvenue {successName} !!
|
||||
</h2>
|
||||
<p className="text-gray-500 text-sm leading-relaxed">
|
||||
Votre compte a été créé avec succès. Un email de validation vous a été envoyé. Vous serez redirigé vers la connexion dans{' '}
|
||||
<span className="font-semibold text-[#006B6B]">{countdown}</span> secondes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user