Files
afrov2/app/register/page.tsx
streaper2 b227657f19 1. Système d'Analytics (Nouveauté Majeure)
Modèle de Données : Ajout de la table AnalyticsEvent dans prisma/schema.prisma pour suivre les vues de pages, les clics et le temps de rétention (DWELL_TIME).
API Backend : Création de la route app/api/analytics/route.ts pour enregistrer les événements en base de données.
Tracking Client : Création du composant AnalyticsTracker.tsx (intégré dans le layout principal) qui capture automatiquement :
Le changement de page (Page Views).
Les clics sur les boutons et liens importants.
Le temps passé sur chaque page.
2. Intégration Base de Données (PostgreSQL/Prisma)
Fiches Entreprises : Mise à jour de app/directory/[id]/page.tsx pour récupérer les données réelles depuis PostgreSQL via Prisma au lieu d'utiliser les données de test (mockData).
Navigation Dashboard : Correction du bouton "Voir ma fiche" dans le tableau de bord pour qu'il redirige correctement vers le profil public de l'entrepreneur.
3. Administration & Metrics
Nouvelle Application Admin : Initialisation d'un dossier admin/ (application Next.js séparée) destinée à la gestion de la plateforme et à la visualisation des statistiques (Metrics & Analytics).
4. Authentification & Inscription
Nouvelle Page : Création de app/register/page.tsx pour permettre l'inscription des nouveaux utilisateurs.
Gestion de Session : Amélioration de UserProvider.tsx pour une meilleure gestion de l'état utilisateur à travers l'application.
5. Maintenance & Types
Mise à jour des types globaux dans types.ts et rafraîchissement du package-lock.json suite à l'ajout de dépendances.
2026-04-11 22:20:41 +02:00

204 lines
11 KiB
TypeScript

"use client";
import React, { useState } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { User, Mail, Lock, ArrowRight, CheckCircle, AlertCircle } from 'lucide-react';
const RegisterPage = () => {
const router = useRouter();
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
confirmPassword: ''
});
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
setError('');
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError('');
if (formData.password !== formData.confirmPassword) {
setError('Les mots de passe ne correspondent pas');
setLoading(false);
return;
}
try {
const res = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: formData.name,
email: formData.email,
password: formData.password
}),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "Une erreur est survenue");
}
setSuccess(true);
setTimeout(() => {
router.push('/login');
}, 2000);
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8 bg-[radial-gradient(circle_at_top_right,_var(--color-brand-100),_transparent_40%),_radial-gradient(circle_at_bottom_left,_var(--color-brand-50),_transparent_40%)]">
<div className="max-w-md w-full">
<div className="text-center mb-10">
<Link href="/" className="inline-flex items-center justify-center h-16 w-16 bg-brand-600 rounded-2xl shadow-lg shadow-brand-200 text-white font-bold text-3xl mb-6 hover:scale-105 transition-transform">
A
</Link>
<h2 className="text-4xl font-extrabold text-dark-900 font-serif mb-2">Rejoignez l'aventure</h2>
<p className="text-gray-600">Créez votre compte pour propulser votre entreprise</p>
</div>
<div className="bg-white/80 backdrop-blur-xl p-8 rounded-3xl shadow-2xl border border-white shadow-brand-100/50">
{success ? (
<div className="text-center py-10 animate-in fade-in zoom-in duration-500">
<div className="inline-flex items-center justify-center h-20 w-20 bg-green-100 text-green-600 rounded-full mb-6">
<CheckCircle size={40} />
</div>
<h3 className="text-2xl font-bold text-gray-900 mb-2">Compte créé !</h3>
<p className="text-gray-600">Redirection vers la page de connexion...</p>
</div>
) : (
<form className="space-y-6" onSubmit={handleSubmit}>
{error && (
<div className="bg-red-50 border-l-4 border-red-500 p-4 rounded-r-lg flex items-center gap-3 text-red-700 animate-in slide-in-from-top-2 duration-300">
<AlertCircle size={20} className="shrink-0" />
<p className="text-sm font-medium">{error}</p>
</div>
)}
<div className="space-y-4">
<div className="space-y-1">
<label className="text-sm font-semibold text-gray-700 ml-1">Nom complet</label>
<div className="relative group">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
<User size={18} />
</div>
<input
name="name"
type="text"
required
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
placeholder="Ex: Jean Dupont"
value={formData.name}
onChange={handleChange}
/>
</div>
</div>
<div className="space-y-1">
<label className="text-sm font-semibold text-gray-700 ml-1">Adresse email</label>
<div className="relative group">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
<Mail size={18} />
</div>
<input
name="email"
type="email"
required
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
placeholder="votre@email.com"
value={formData.email}
onChange={handleChange}
/>
</div>
</div>
<div className="space-y-1">
<label className="text-sm font-semibold text-gray-700 ml-1">Mot de passe</label>
<div className="relative group">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
<Lock size={18} />
</div>
<input
name="password"
type="password"
required
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
placeholder="••••••••"
value={formData.password}
onChange={handleChange}
/>
</div>
</div>
<div className="space-y-1">
<label className="text-sm font-semibold text-gray-700 ml-1">Confirmer le mot de passe</label>
<div className="relative group">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-gray-400 group-focus-within:text-brand-600 transition-colors">
<Lock size={18} />
</div>
<input
name="confirmPassword"
type="password"
required
className="block w-full pl-10 pr-3 py-3 border border-gray-200 rounded-xl bg-gray-50/50 focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all text-gray-900 placeholder-gray-400"
placeholder="••••••••"
value={formData.confirmPassword}
onChange={handleChange}
/>
</div>
</div>
</div>
<button
type="submit"
disabled={loading}
className="group relative w-full flex justify-center items-center py-3.5 px-4 border border-transparent text-sm font-bold rounded-xl text-white bg-brand-600 hover:bg-brand-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brand-500 transition-all shadow-lg shadow-brand-200 disabled:opacity-70 disabled:cursor-not-allowed overflow-hidden"
>
{loading ? (
<div className="h-5 w-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : (
<>
S'inscrire
<ArrowRight className="ml-2 group-hover:translate-x-1 transition-transform" size={18} />
</>
)}
</button>
<div className="text-center mt-6">
<p className="text-gray-600 text-sm">
Déjà un compte ?{' '}
<Link href="/login" className="font-bold text-brand-600 hover:text-brand-700 transition-colors underline-offset-4 hover:underline">
Se connecter
</Link>
</p>
</div>
</form>
)}
</div>
<p className="mt-8 text-center text-xs text-gray-400 uppercase tracking-widest font-semibold">
Afropreunariat &copy; 2026
</p>
</div>
</div>
);
};
export default RegisterPage;