fix dasboard
This commit is contained in:
parent
eab3d6f612
commit
a8e6ded810
@ -1,7 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Dashboard Home Page
|
* Dashboard Home Page - Clean & Colorful with Charts
|
||||||
*
|
* Professional design with data visualization
|
||||||
* Main dashboard with CSV Booking KPIs and carrier analytics
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
@ -22,6 +21,21 @@ import {
|
|||||||
Plus,
|
Plus,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import {
|
||||||
|
PieChart,
|
||||||
|
Pie,
|
||||||
|
Cell,
|
||||||
|
BarChart,
|
||||||
|
Bar,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
CartesianGrid,
|
||||||
|
Tooltip,
|
||||||
|
ResponsiveContainer,
|
||||||
|
LineChart,
|
||||||
|
Line,
|
||||||
|
Legend,
|
||||||
|
} from 'recharts';
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
// Fetch CSV booking KPIs
|
// Fetch CSV booking KPIs
|
||||||
@ -36,138 +50,240 @@ export default function DashboardPage() {
|
|||||||
queryFn: () => dashboardApi.getTopCarriers(),
|
queryFn: () => dashboardApi.getTopCarriers(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Prepare data for charts
|
||||||
|
const statusDistribution = csvKpis
|
||||||
|
? [
|
||||||
|
{ name: 'Acceptés', value: csvKpis.totalAccepted, color: '#10b981' },
|
||||||
|
{ name: 'Refusés', value: csvKpis.totalRejected, color: '#ef4444' },
|
||||||
|
{ name: 'En Attente', value: csvKpis.totalPending, color: '#f59e0b' },
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const carrierWeightData = topCarriers
|
||||||
|
? topCarriers.slice(0, 5).map(c => ({
|
||||||
|
name: c.carrierName.length > 15 ? c.carrierName.substring(0, 15) + '...' : c.carrierName,
|
||||||
|
poids: Math.round(c.totalWeightKG),
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8 p-8">
|
<div className="min-h-screen bg-gray-50">
|
||||||
{/* Header Section */}
|
<div className="max-w-7xl mx-auto px-6 py-8 space-y-6">
|
||||||
<div className="flex items-center justify-between">
|
{/* Header - Compact */}
|
||||||
<div>
|
<div className="flex items-center justify-between pb-4 border-b border-gray-200">
|
||||||
<h1 className="text-4xl font-bold tracking-tight">Dashboard</h1>
|
<div>
|
||||||
<p className="text-muted-foreground mt-2">
|
<h1 className="text-3xl font-semibold text-gray-900">Tableau de Bord</h1>
|
||||||
Suivez vos bookings et vos performances
|
<p className="text-gray-600 mt-1 text-sm">
|
||||||
</p>
|
Vue d'ensemble de vos bookings et performances
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link href="/dashboard/bookings">
|
||||||
|
<Button className="bg-blue-600 hover:bg-blue-700 text-white gap-2 shadow-sm">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Nouveau Booking
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CTA Button */}
|
{/* KPI Cards - Compact with Color */}
|
||||||
<Link href="/dashboard/bookings/new">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
<Button size="lg" className="gap-2">
|
{/* Bookings Acceptés */}
|
||||||
<Plus className="h-5 w-5" />
|
<Card className="border border-gray-200 shadow-sm hover:shadow-md transition-shadow bg-white">
|
||||||
Nouveau Booking
|
<CardContent className="p-4">
|
||||||
</Button>
|
<div className="flex flex-col items-center text-center">
|
||||||
</Link>
|
<div className="h-10 w-10 rounded-lg bg-green-100 flex items-center justify-center mb-2">
|
||||||
</div>
|
<PackageCheck className="h-5 w-5 text-green-600" />
|
||||||
|
|
||||||
{/* KPI Cards Grid */}
|
|
||||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
|
||||||
{/* Bookings Acceptés */}
|
|
||||||
<Card className="hover:shadow-lg transition-shadow">
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
||||||
<CardTitle className="text-sm font-medium">Bookings Acceptés</CardTitle>
|
|
||||||
<PackageCheck className="h-5 w-5 text-green-600" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
{csvKpisLoading ? (
|
|
||||||
<div className="h-8 w-20 bg-gray-200 animate-pulse rounded" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="text-3xl font-bold">{csvKpis?.totalAccepted || 0}</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
|
||||||
+{csvKpis?.acceptedThisMonth || 0} ce mois
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Bookings Refusés */}
|
|
||||||
<Card className="hover:shadow-lg transition-shadow">
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
||||||
<CardTitle className="text-sm font-medium">Bookings Refusés</CardTitle>
|
|
||||||
<PackageX className="h-5 w-5 text-red-600" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
{csvKpisLoading ? (
|
|
||||||
<div className="h-8 w-20 bg-gray-200 animate-pulse rounded" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="text-3xl font-bold">{csvKpis?.totalRejected || 0}</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
|
||||||
+{csvKpis?.rejectedThisMonth || 0} ce mois
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Bookings En Attente */}
|
|
||||||
<Card className="hover:shadow-lg transition-shadow">
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
||||||
<CardTitle className="text-sm font-medium">En Attente</CardTitle>
|
|
||||||
<Clock className="h-5 w-5 text-yellow-600" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
{csvKpisLoading ? (
|
|
||||||
<div className="h-8 w-20 bg-gray-200 animate-pulse rounded" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="text-3xl font-bold">{csvKpis?.totalPending || 0}</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
|
||||||
{csvKpis?.acceptanceRate.toFixed(1)}% taux d'acceptation
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Poids Total Accepté */}
|
|
||||||
<Card className="hover:shadow-lg transition-shadow">
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
||||||
<CardTitle className="text-sm font-medium">Poids Total Accepté</CardTitle>
|
|
||||||
<Weight className="h-5 w-5 text-blue-600" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
{csvKpisLoading ? (
|
|
||||||
<div className="h-8 w-20 bg-gray-200 animate-pulse rounded" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="text-3xl font-bold">
|
|
||||||
{(csvKpis?.totalWeightAcceptedKG || 0).toLocaleString()}
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-2">
|
<p className="text-xs font-medium text-gray-600 mb-1">Acceptés</p>
|
||||||
KG ({(csvKpis?.totalVolumeAcceptedCBM || 0).toFixed(2)} CBM)
|
{csvKpisLoading ? (
|
||||||
</p>
|
<div className="h-8 w-16 bg-gray-100 animate-pulse rounded" />
|
||||||
</>
|
) : (
|
||||||
)}
|
<>
|
||||||
</CardContent>
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
</Card>
|
{csvKpis?.totalAccepted || 0}
|
||||||
</div>
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
{/* Stats Overview Card */}
|
+{csvKpis?.acceptedThisMonth || 0} ce mois
|
||||||
<Card>
|
</p>
|
||||||
<CardHeader>
|
</>
|
||||||
<CardTitle>Vue d'ensemble</CardTitle>
|
)}
|
||||||
<CardDescription>Statistiques globales de vos bookings</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="grid gap-6 md:grid-cols-3">
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
|
||||||
<TrendingUp className="h-6 w-6 text-green-600" />
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</CardContent>
|
||||||
<p className="text-sm font-medium text-muted-foreground">Taux d'acceptation</p>
|
</Card>
|
||||||
<p className="text-2xl font-bold">
|
|
||||||
|
{/* Bookings Refusés */}
|
||||||
|
<Card className="border border-gray-200 shadow-sm hover:shadow-md transition-shadow bg-white">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex flex-col items-center text-center">
|
||||||
|
<div className="h-10 w-10 rounded-lg bg-red-100 flex items-center justify-center mb-2">
|
||||||
|
<PackageX className="h-5 w-5 text-red-600" />
|
||||||
|
</div>
|
||||||
|
<p className="text-xs font-medium text-gray-600 mb-1">Refusés</p>
|
||||||
|
{csvKpisLoading ? (
|
||||||
|
<div className="h-8 w-16 bg-gray-100 animate-pulse rounded" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
|
{csvKpis?.totalRejected || 0}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
+{csvKpis?.rejectedThisMonth || 0} ce mois
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Bookings En Attente */}
|
||||||
|
<Card className="border border-gray-200 shadow-sm hover:shadow-md transition-shadow bg-white">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex flex-col items-center text-center">
|
||||||
|
<div className="h-10 w-10 rounded-lg bg-amber-100 flex items-center justify-center mb-2">
|
||||||
|
<Clock className="h-5 w-5 text-amber-600" />
|
||||||
|
</div>
|
||||||
|
<p className="text-xs font-medium text-gray-600 mb-1">En Attente</p>
|
||||||
|
{csvKpisLoading ? (
|
||||||
|
<div className="h-8 w-16 bg-gray-100 animate-pulse rounded" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
|
{csvKpis?.totalPending || 0}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
{csvKpis?.acceptanceRate.toFixed(1)}% acceptés
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Poids Total */}
|
||||||
|
<Card className="border border-gray-200 shadow-sm hover:shadow-md transition-shadow bg-white">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex flex-col items-center text-center">
|
||||||
|
<div className="h-10 w-10 rounded-lg bg-blue-100 flex items-center justify-center mb-2">
|
||||||
|
<Weight className="h-5 w-5 text-blue-600" />
|
||||||
|
</div>
|
||||||
|
<p className="text-xs font-medium text-gray-600 mb-1">Poids Total</p>
|
||||||
|
{csvKpisLoading ? (
|
||||||
|
<div className="h-8 w-16 bg-gray-100 animate-pulse rounded" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
|
{(csvKpis?.totalWeightAcceptedKG || 0).toLocaleString()}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
KG • {(csvKpis?.totalVolumeAcceptedCBM || 0).toFixed(1)} CBM
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Charts Section */}
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
{/* Distribution des Statuts - Pie Chart */}
|
||||||
|
<Card className="border border-gray-200 shadow-sm bg-white">
|
||||||
|
<CardHeader className="pb-4 border-b border-gray-100">
|
||||||
|
<CardTitle className="text-base font-semibold text-gray-900">
|
||||||
|
Distribution des Bookings
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="text-xs text-gray-600">
|
||||||
|
Répartition par statut
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="pt-4">
|
||||||
|
{csvKpisLoading ? (
|
||||||
|
<div className="h-48 bg-gray-50 animate-pulse rounded" />
|
||||||
|
) : (
|
||||||
|
<ResponsiveContainer width="100%" height={200}>
|
||||||
|
<PieChart>
|
||||||
|
<Pie
|
||||||
|
data={statusDistribution}
|
||||||
|
cx="50%"
|
||||||
|
cy="50%"
|
||||||
|
labelLine={false}
|
||||||
|
label={({ name, percent }) =>
|
||||||
|
`${name} ${(percent * 100).toFixed(0)}%`
|
||||||
|
}
|
||||||
|
outerRadius={70}
|
||||||
|
fill="#8884d8"
|
||||||
|
dataKey="value"
|
||||||
|
>
|
||||||
|
{statusDistribution.map((entry, index) => (
|
||||||
|
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||||
|
))}
|
||||||
|
</Pie>
|
||||||
|
<Tooltip />
|
||||||
|
</PieChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Poids par Transporteur - Bar Chart */}
|
||||||
|
<Card className="border border-gray-200 shadow-sm bg-white">
|
||||||
|
<CardHeader className="pb-4 border-b border-gray-100">
|
||||||
|
<CardTitle className="text-base font-semibold text-gray-900">
|
||||||
|
Poids par Transporteur
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="text-xs text-gray-600">
|
||||||
|
Top 5 carriers par poids (KG)
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="pt-4">
|
||||||
|
{carriersLoading ? (
|
||||||
|
<div className="h-48 bg-gray-50 animate-pulse rounded" />
|
||||||
|
) : (
|
||||||
|
<ResponsiveContainer width="100%" height={200}>
|
||||||
|
<BarChart data={carrierWeightData}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
|
||||||
|
<XAxis
|
||||||
|
dataKey="name"
|
||||||
|
tick={{ fontSize: 11 }}
|
||||||
|
angle={-15}
|
||||||
|
textAnchor="end"
|
||||||
|
height={50}
|
||||||
|
/>
|
||||||
|
<YAxis tick={{ fontSize: 11 }} />
|
||||||
|
<Tooltip />
|
||||||
|
<Bar dataKey="poids" fill="#3b82f6" radius={[4, 4, 0, 0]} />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Performance Overview - Compact */}
|
||||||
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
|
<Card className="border border-gray-200 shadow-sm bg-white">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<div className="flex flex-col items-center text-center">
|
||||||
|
<div className="h-10 w-10 rounded-lg bg-green-100 flex items-center justify-center mb-2">
|
||||||
|
<TrendingUp className="h-5 w-5 text-green-600" />
|
||||||
|
</div>
|
||||||
|
<p className="text-xs font-medium text-gray-600 mb-1">Taux d'Acceptation</p>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
{csvKpisLoading ? '--' : `${csvKpis?.acceptanceRate.toFixed(1)}%`}
|
{csvKpisLoading ? '--' : `${csvKpis?.acceptanceRate.toFixed(1)}%`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<div className="flex items-center space-x-4">
|
<Card className="border border-gray-200 shadow-sm bg-white">
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-blue-100">
|
<CardContent className="p-4">
|
||||||
<Package className="h-6 w-6 text-blue-600" />
|
<div className="flex flex-col items-center text-center">
|
||||||
</div>
|
<div className="h-10 w-10 rounded-lg bg-blue-100 flex items-center justify-center mb-2">
|
||||||
<div>
|
<Package className="h-5 w-5 text-blue-600" />
|
||||||
<p className="text-sm font-medium text-muted-foreground">Total bookings</p>
|
</div>
|
||||||
<p className="text-2xl font-bold">
|
<p className="text-xs font-medium text-gray-600 mb-1">Total Bookings</p>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
{csvKpisLoading
|
{csvKpisLoading
|
||||||
? '--'
|
? '--'
|
||||||
: (csvKpis?.totalAccepted || 0) +
|
: (csvKpis?.totalAccepted || 0) +
|
||||||
@ -175,178 +291,130 @@ export default function DashboardPage() {
|
|||||||
(csvKpis?.totalPending || 0)}
|
(csvKpis?.totalPending || 0)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<div className="flex items-center space-x-4">
|
<Card className="border border-gray-200 shadow-sm bg-white">
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-purple-100">
|
<CardContent className="p-4">
|
||||||
<Weight className="h-6 w-6 text-purple-600" />
|
<div className="flex flex-col items-center text-center">
|
||||||
</div>
|
<div className="h-10 w-10 rounded-lg bg-purple-100 flex items-center justify-center mb-2">
|
||||||
<div>
|
<Weight className="h-5 w-5 text-purple-600" />
|
||||||
<p className="text-sm font-medium text-muted-foreground">Volume total accepté</p>
|
</div>
|
||||||
<p className="text-2xl font-bold">
|
<p className="text-xs font-medium text-gray-600 mb-1">Volume Total</p>
|
||||||
|
<p className="text-2xl font-bold text-gray-900">
|
||||||
{csvKpisLoading ? '--' : `${(csvKpis?.totalVolumeAcceptedCBM || 0).toFixed(1)}`}
|
{csvKpisLoading ? '--' : `${(csvKpis?.totalVolumeAcceptedCBM || 0).toFixed(1)}`}
|
||||||
<span className="text-sm font-normal text-muted-foreground ml-1">CBM</span>
|
<span className="text-sm font-normal text-gray-500 ml-1">CBM</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</CardContent>
|
||||||
</div>
|
</Card>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Top Carriers Section */}
|
{/* Top Carriers - Compact Table */}
|
||||||
<Card>
|
<Card className="border border-gray-200 shadow-sm bg-white">
|
||||||
<CardHeader>
|
<CardHeader className="pb-3 border-b border-gray-100">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle>Meilleures Compagnies</CardTitle>
|
<CardTitle className="text-base font-semibold text-gray-900">
|
||||||
<CardDescription>Top 5 des transporteurs avec qui vous avez le plus booké</CardDescription>
|
Top Transporteurs
|
||||||
</div>
|
</CardTitle>
|
||||||
<Link href="/dashboard/bookings">
|
<CardDescription className="text-xs text-gray-600 mt-1">
|
||||||
<Button variant="ghost" size="sm" className="gap-2">
|
Classement des meilleures compagnies
|
||||||
Voir tous
|
</CardDescription>
|
||||||
<ArrowRight className="h-4 w-4" />
|
</div>
|
||||||
</Button>
|
<Link href="/dashboard/bookings">
|
||||||
</Link>
|
<Button
|
||||||
</div>
|
variant="ghost"
|
||||||
</CardHeader>
|
size="sm"
|
||||||
<CardContent>
|
className="gap-2 text-gray-600 hover:text-gray-900 text-xs"
|
||||||
{carriersLoading ? (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{Array.from({ length: 5 }).map((_, i) => (
|
|
||||||
<div key={i} className="h-20 bg-gray-100 animate-pulse rounded" />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : topCarriers && topCarriers.length > 0 ? (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{topCarriers.map((carrier, index) => (
|
|
||||||
<div
|
|
||||||
key={carrier.carrierName}
|
|
||||||
className="flex items-center justify-between p-4 border rounded-lg hover:bg-accent/50 transition-colors"
|
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-4">
|
Voir tout
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 font-bold text-primary">
|
<ArrowRight className="h-3 w-3" />
|
||||||
#{index + 1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold">{carrier.carrierName}</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{carrier.totalBookings} bookings • {carrier.totalWeightKG.toLocaleString()} KG
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<div className="text-right">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Badge variant="secondary" className="bg-green-100 text-green-800">
|
|
||||||
{carrier.acceptedBookings} acceptés
|
|
||||||
</Badge>
|
|
||||||
{carrier.rejectedBookings > 0 && (
|
|
||||||
<Badge variant="secondary" className="bg-red-100 text-red-800">
|
|
||||||
{carrier.rejectedBookings} refusés
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
|
||||||
Taux: {carrier.acceptanceRate.toFixed(0)}% • Moy:{' '}
|
|
||||||
${carrier.avgPriceUSD.toFixed(0)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<Package className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Aucun booking pour l'instant</h3>
|
|
||||||
<p className="text-muted-foreground mb-6">
|
|
||||||
Créez votre premier booking pour voir vos statistiques
|
|
||||||
</p>
|
|
||||||
<Link href="/dashboard/bookings/new">
|
|
||||||
<Button>
|
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
|
||||||
Créer un booking
|
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</CardHeader>
|
||||||
</CardContent>
|
<CardContent className="p-0">
|
||||||
</Card>
|
{carriersLoading ? (
|
||||||
|
<div className="divide-y divide-gray-100">
|
||||||
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
|
<div key={i} className="px-4 py-3">
|
||||||
|
<div className="h-12 bg-gray-50 animate-pulse rounded" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : topCarriers && topCarriers.length > 0 ? (
|
||||||
|
<div className="divide-y divide-gray-100">
|
||||||
|
{topCarriers.slice(0, 5).map((carrier, index) => (
|
||||||
|
<div
|
||||||
|
key={carrier.carrierName}
|
||||||
|
className="px-4 py-3 hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3 flex-1">
|
||||||
|
<div className="flex items-center justify-center w-6 h-6 rounded-md bg-gray-100 text-gray-700 font-semibold text-xs">
|
||||||
|
{index + 1}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="font-semibold text-gray-900 text-sm">
|
||||||
|
{carrier.carrierName}
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-center gap-3 text-xs text-gray-500 mt-0.5">
|
||||||
|
<span>{carrier.totalBookings} bookings</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>{carrier.totalWeightKG.toLocaleString()} KG</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Quick Actions */}
|
<div className="flex items-center gap-4">
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
<div className="text-right">
|
||||||
<Link href="/dashboard/search">
|
<div className="flex items-center gap-1.5 justify-end mb-0.5">
|
||||||
<Card className="hover:shadow-lg transition-all hover:-translate-y-1 cursor-pointer">
|
<Badge
|
||||||
<CardContent className="flex items-center space-x-4 p-6">
|
variant="secondary"
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-blue-100">
|
className="bg-green-50 text-green-700 border-green-200 text-xs px-1.5 py-0"
|
||||||
<svg
|
>
|
||||||
className="h-6 w-6 text-blue-600"
|
{carrier.acceptedBookings} ✓
|
||||||
fill="none"
|
</Badge>
|
||||||
stroke="currentColor"
|
{carrier.rejectedBookings > 0 && (
|
||||||
viewBox="0 0 24 24"
|
<Badge
|
||||||
>
|
variant="secondary"
|
||||||
<path
|
className="bg-red-50 text-red-700 border-red-200 text-xs px-1.5 py-0"
|
||||||
strokeLinecap="round"
|
>
|
||||||
strokeLinejoin="round"
|
{carrier.rejectedBookings} ✗
|
||||||
strokeWidth={2}
|
</Badge>
|
||||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
)}
|
||||||
/>
|
</div>
|
||||||
</svg>
|
<p className="text-xs text-gray-500">
|
||||||
|
{carrier.acceptanceRate.toFixed(0)}% • ${carrier.avgPriceUSD.toFixed(0)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
) : (
|
||||||
<h3 className="font-semibold">Rechercher des tarifs</h3>
|
<div className="text-center py-12 px-4">
|
||||||
<p className="text-sm text-muted-foreground">Trouver les meilleurs prix</p>
|
<div className="mx-auto mb-3 h-12 w-12 rounded-full bg-gray-100 flex items-center justify-center">
|
||||||
|
<Package className="h-6 w-6 text-gray-400" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-sm font-semibold text-gray-900 mb-1">
|
||||||
|
Aucun booking
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-gray-500 mb-4 max-w-sm mx-auto">
|
||||||
|
Créez votre premier booking pour voir vos statistiques
|
||||||
|
</p>
|
||||||
|
<Link href="/dashboard/bookings">
|
||||||
|
<Button size="sm" className="bg-blue-600 hover:bg-blue-700">
|
||||||
|
<Plus className="mr-1.5 h-3 w-3" />
|
||||||
|
Créer un booking
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
)}
|
||||||
</Card>
|
</CardContent>
|
||||||
</Link>
|
</Card>
|
||||||
|
|
||||||
<Link href="/dashboard/bookings">
|
|
||||||
<Card className="hover:shadow-lg transition-all hover:-translate-y-1 cursor-pointer">
|
|
||||||
<CardContent className="flex items-center space-x-4 p-6">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-purple-100">
|
|
||||||
<Package className="h-6 w-6 text-purple-600" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold">Mes Bookings</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">Voir tous mes envois</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link href="/dashboard/settings/organization">
|
|
||||||
<Card className="hover:shadow-lg transition-all hover:-translate-y-1 cursor-pointer">
|
|
||||||
<CardContent className="flex items-center space-x-4 p-6">
|
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-100">
|
|
||||||
<svg
|
|
||||||
className="h-6 w-6 text-gray-600"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold">Paramètres</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">Configuration du compte</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user