168 lines
6.7 KiB
TypeScript
168 lines
6.7 KiB
TypeScript
"use client";
|
|
|
|
import React from 'react';
|
|
import {
|
|
Users,
|
|
Store,
|
|
Clock,
|
|
MessageCircle,
|
|
Eye,
|
|
CheckCircle2,
|
|
UserPlus
|
|
} from 'lucide-react';
|
|
import Link from 'next/link';
|
|
|
|
interface DashboardData {
|
|
stats: {
|
|
usersCount: number;
|
|
businessCount: number;
|
|
pendingCount: number;
|
|
commentsCount: number;
|
|
totalViews: number;
|
|
uniqueVisitors: number;
|
|
};
|
|
latestBusinesses: Array<{
|
|
id: string;
|
|
name: string;
|
|
category: string;
|
|
logoUrl: string;
|
|
location: string;
|
|
createdAt: Date;
|
|
verified: boolean;
|
|
plan: string;
|
|
owner?: {
|
|
name: string;
|
|
email: string;
|
|
};
|
|
}>;
|
|
activities: Array<{
|
|
id: string;
|
|
type: 'comment' | 'user';
|
|
title: string;
|
|
subtitle: string;
|
|
timestamp: Date;
|
|
avatar?: string | null;
|
|
}>;
|
|
}
|
|
|
|
export default function DashboardClient({ initialData }: { initialData: DashboardData }) {
|
|
const { stats, latestBusinesses, activities } = initialData;
|
|
|
|
const statCards = [
|
|
{ label: 'Utilisateurs', value: stats.usersCount, icon: Users, color: 'text-blue-400' },
|
|
{ label: 'Visiteurs Uniques', value: stats.uniqueVisitors, icon: Eye, color: 'text-indigo-400' },
|
|
{ label: 'Entrepreneurs', value: stats.businessCount, icon: Store, color: 'text-emerald-400' },
|
|
{ label: 'En attente', value: stats.pendingCount, icon: Clock, color: 'text-amber-400' },
|
|
{ label: 'Commentaires', value: stats.commentsCount, icon: MessageCircle, color: 'text-purple-400' },
|
|
{ label: 'Total Vues', value: stats.totalViews, icon: Eye, color: 'text-brand-400' },
|
|
];
|
|
|
|
return (
|
|
<div>
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold text-white mb-2">Tableau de bord</h1>
|
|
<p className="text-slate-400">Vue d'ensemble de l'activité sur la plateforme.</p>
|
|
</div>
|
|
|
|
<div className="stats-grid">
|
|
{statCards.map((card) => (
|
|
<div key={card.label} className="card">
|
|
<div className="flex justify-between items-start mb-4">
|
|
<div className={`p-2 rounded-lg bg-slate-800 ${card.color}`}>
|
|
<card.icon className="w-6 h-6" />
|
|
</div>
|
|
<span className="text-2xl font-bold text-white">{card.value}</span>
|
|
</div>
|
|
<h3 className="text-slate-400 font-medium">{card.label}</h3>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
{/* Derniers entrepreneurs */}
|
|
<div className="card flex flex-col">
|
|
<div className="flex justify-between items-center mb-6">
|
|
<h2 className="text-xl font-bold text-white">Derniers entrepreneurs</h2>
|
|
<Link href="/users" className="text-xs text-indigo-400 hover:underline font-semibold">
|
|
Voir tout
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="space-y-4 flex-1">
|
|
{latestBusinesses.length === 0 ? (
|
|
<p className="text-slate-500 italic py-8 text-center">Aucune entreprise récente.</p>
|
|
) : (
|
|
latestBusinesses.map((b) => (
|
|
<div key={b.id} className="flex items-center justify-between p-3 rounded-xl bg-slate-800/40 hover:bg-slate-800/80 transition-colors border border-slate-800">
|
|
<div className="flex items-center gap-3 min-w-0">
|
|
<div className="w-10 h-10 rounded-lg bg-slate-800 overflow-hidden flex items-center justify-center border border-slate-700 flex-shrink-0">
|
|
{b.logoUrl ? (
|
|
<img src={b.logoUrl} alt={b.name} className="w-full h-full object-cover" />
|
|
) : (
|
|
<Store className="w-4 h-4 text-slate-500" />
|
|
)}
|
|
</div>
|
|
<div className="min-w-0">
|
|
<div className="font-semibold text-white text-sm truncate">{b.name}</div>
|
|
<div className="text-xs text-slate-400 truncate">{b.category} • {b.location}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2 flex-shrink-0 ml-3">
|
|
<span className={`text-[10px] font-bold px-2 py-0.5 rounded-full uppercase ${
|
|
b.plan === 'EMPIRE' ? 'bg-purple-500/10 text-purple-400 border border-purple-500/20' :
|
|
b.plan === 'BOOSTER' ? 'bg-amber-500/10 text-amber-400 border border-amber-500/20' :
|
|
'bg-slate-500/10 text-slate-400'
|
|
}`}>
|
|
{b.plan}
|
|
</span>
|
|
{b.verified ? (
|
|
<CheckCircle2 className="w-4 h-4 text-emerald-500" title="Vérifié" />
|
|
) : (
|
|
<Clock className="w-4 h-4 text-amber-500" title="En attente" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Activité récente */}
|
|
<div className="card flex flex-col">
|
|
<h2 className="text-xl font-bold text-white mb-6">Activité récente</h2>
|
|
|
|
<div className="space-y-4 flex-1">
|
|
{activities.length === 0 ? (
|
|
<p className="text-slate-500 italic py-8 text-center">Aucune activité enregistrée.</p>
|
|
) : (
|
|
activities.map((act) => (
|
|
<div key={act.id} className="flex items-start gap-3.5 p-3 rounded-xl bg-slate-800/20 border border-slate-800/50">
|
|
<div className="mt-0.5 w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center border border-slate-700 flex-shrink-0 overflow-hidden">
|
|
{act.avatar ? (
|
|
<img src={act.avatar} alt="" className="w-full h-full object-cover" />
|
|
) : act.type === 'comment' ? (
|
|
<MessageCircle className="w-3.5 h-3.5 text-purple-400" />
|
|
) : (
|
|
<UserPlus className="w-3.5 h-3.5 text-blue-400" />
|
|
)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<p className="text-xs font-semibold text-slate-200 truncate">{act.title}</p>
|
|
<span className="text-[10px] text-slate-500 flex-shrink-0">
|
|
{new Date(act.timestamp).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })}
|
|
</span>
|
|
</div>
|
|
<p className="text-xs text-slate-400 mt-0.5 line-clamp-2">{act.subtitle}</p>
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|