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

@@ -1,7 +1,7 @@
"use client";
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import Link from 'next/link';
import { LayoutDashboard, Edit3, ShoppingBag, CreditCard, LogOut, Eye } from 'lucide-react';
import { useRouter } from 'next/navigation';
@@ -17,13 +17,73 @@ const DashboardPage = () => {
const { user, logout } = useUser();
const router = useRouter();
const [currentView, setCurrentView] = useState<'overview' | 'profile' | 'offers' | 'subscription'>('overview');
const [business, setBusiness] = useState(MOCK_BUSINESSES[0]); // In real app, fetch by user.id
const [business, setBusiness] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
// Fetch user's business
useEffect(() => {
if (!user || !isMounted) return;
const fetchBusiness = async () => {
try {
const res = await fetch('/api/businesses/me', {
headers: { 'x-user-id': user.id }
});
const data = await res.json();
if (data && !data.error) {
setBusiness(data);
}
} catch (error) {
console.error('Error fetching dashboard business:', error);
} finally {
setLoading(false);
}
};
fetchBusiness();
}, [user, isMounted]);
if (!isMounted) return null;
if (!user) {
if (typeof window !== 'undefined') router.push('/login');
return null;
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="flex flex-col items-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-brand-600"></div>
<p className="mt-4 text-gray-600 font-medium">Chargement de votre espace...</p>
</div>
</div>
);
}
// Default business structure for new users
const displayBusiness = business || {
id: 'new',
name: "Ma Boutique",
category: "Technologie & IT",
location: "Ma Ville",
description: "Décrivez votre entreprise ici...",
logoUrl: "https://picsum.photos/200/200?random=default",
contactEmail: user?.email || "",
contactPhone: "",
tags: [],
socialLinks: {},
verified: false,
viewCount: 0,
rating: 0,
offers: []
};
return (
<div className="min-h-screen bg-gray-100 flex">
{/* 1. Sidebar */}
@@ -88,7 +148,7 @@ const DashboardPage = () => {
{currentView === 'subscription' && 'Mon Abonnement'}
</h1>
<div className="flex items-center space-x-4">
<a href={`/directory`} target="_blank" rel="noopener noreferrer" className="hidden sm:inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none">
<a href={displayBusiness.id === 'new' ? '/directory' : `/directory/${displayBusiness.id}`} target="_blank" rel="noopener noreferrer" className="hidden sm:inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none">
<Eye className="-ml-1 mr-2 h-4 w-4 text-gray-500" />
Voir ma fiche
</a>
@@ -97,8 +157,8 @@ const DashboardPage = () => {
<main className="flex-1 overflow-y-auto p-6">
<div className="max-w-7xl mx-auto">
{currentView === 'overview' && <DashboardOverview business={business} />}
{currentView === 'profile' && <DashboardProfile business={business} setBusiness={setBusiness} />}
{currentView === 'overview' && <DashboardOverview business={displayBusiness} />}
{currentView === 'profile' && <DashboardProfile business={displayBusiness} setBusiness={setBusiness} />}
{currentView === 'offers' && <DashboardOffers />}
{currentView === 'subscription' && (
<div className="space-y-6">