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.
This commit is contained in:
2026-04-11 22:20:41 +02:00
parent d88ba7c53c
commit b227657f19
100 changed files with 14142 additions and 157 deletions

View File

@@ -6,11 +6,12 @@ import Link from 'next/link';
import { useParams } from 'next/navigation';
import { ArrowLeft, MapPin, Globe, Mail, Phone, CheckCircle, Star, Facebook, Linkedin, Instagram, Play, Share2, Send, Award, Quote } from 'lucide-react';
import { MOCK_BUSINESSES, MOCK_OFFERS } from '../../../lib/mockData';
import { OfferType } from '../../../types';
import { Business, OfferType } from '../../../types';
const BusinessDetailPage = () => {
const { id } = useParams();
const business = MOCK_BUSINESSES.find(b => b.id === id);
const [business, setBusiness] = useState<Business | null>(null);
const [loading, setLoading] = useState(true);
const [contactForm, setContactForm] = useState({
name: '',
@@ -20,13 +21,59 @@ const BusinessDetailPage = () => {
const [formStatus, setFormStatus] = useState<'idle' | 'sending' | 'success'>('idle');
const businessOffers = useMemo(() => {
if (!business) return [];
// If the business has its own offers (from DB), use them
if (business.offers && business.offers.length > 0) return business.offers;
// Otherwise fallback to mock offers for mock businesses
return MOCK_OFFERS.filter(o => o.businessId === id && o.active);
}, [id]);
}, [business, id]);
useEffect(() => {
window.scrollTo(0, 0);
const loadBusiness = async () => {
setLoading(true);
// 1. Try Mock Data First (Instant)
const mock = MOCK_BUSINESSES.find(b => b.id === id);
if (mock) {
setBusiness(mock);
setLoading(false);
return;
}
// 2. Try API (Database)
try {
const res = await fetch(`/api/businesses/${id}`);
if (res.ok) {
const data = await res.json();
setBusiness(data);
// 3. Increment views silently
fetch(`/api/businesses/${id}/view`, { method: 'POST' }).catch(console.error);
} else {
setBusiness(null);
}
} catch (err) {
console.error("Error fetching business:", err);
setBusiness(null);
} finally {
setLoading(false);
}
};
if (id) loadBusiness();
}, [id]);
if (loading) {
return (
<div className="min-h-[60vh] flex flex-col items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-brand-600 mb-4"></div>
<p className="text-gray-500">Chargement du profil...</p>
</div>
);
}
if (!business) {
return (
<div className="min-h-[60vh] flex flex-col items-center justify-center">