app-s01qyw/frontend/src/components/features/AdminStatsCards.tsx
2026-09-29 20:17:53 +05:30

130 lines
4.4 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { adminApi, api } from '../../api';
import { Activity, AlertTriangle, Car, CheckCircle2, RefreshCw, Route, Users } from 'lucide-react';
export default function AdminStatsCards() {
const { data, isLoading, isError, refetch } = useQuery({
queryKey: ['admin-stats'],
queryFn: async () => {
try {
const res = await adminApi.get('/api/admin/stats');
return res.data;
} catch {
const res = await api.get('/api/admin/stats');
return res.data;
}
},
refetchInterval: 10000,
});
if (isLoading) {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
{[1, 2, 3, 4, 5].map((i) => (
<div key={i} className="h-28 rounded-xl bg-slate-100 dark:bg-slate-800 animate-pulse border border-slate-200 dark:border-slate-800 p-4" />
))}
</div>
);
}
if (isError) {
return (
<div className="rounded-xl border border-red-200 bg-red-50 p-4 text-red-800 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-300 flex items-center justify-between">
<div className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-red-600 dark:text-red-400 shrink-0" />
<span className="text-sm font-medium">Failed to load platform telemetry stats.</span>
</div>
<button
onClick={() => refetch()}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold rounded-lg bg-red-100 hover:bg-red-200 text-red-900 dark:bg-red-900 dark:hover:bg-red-800 dark:text-red-100 transition-colors"
>
<RefreshCw className="h-3.5 w-3.5" />
Retry
</button>
</div>
);
}
const stats = data?.stats || {
total_users: 0,
active_drivers: 0,
total_rides: 0,
active_rides: 0,
completed_rides: 0,
};
const cards = [
{
title: 'Total Users',
value: stats.total_users ?? 0,
icon: Users,
color: 'text-slate-900 dark:text-slate-100',
bg: 'bg-slate-100 dark:bg-slate-800',
description: 'Registered passengers & drivers',
},
{
title: 'Online Drivers',
value: stats.active_drivers ?? 0,
icon: Car,
color: 'text-emerald-600 dark:text-emerald-400',
bg: 'bg-emerald-50 dark:bg-emerald-950/40',
description: 'Available for instant dispatch',
},
{
title: 'Total System Rides',
value: stats.total_rides ?? 0,
icon: Route,
color: 'text-sky-600 dark:text-sky-400',
bg: 'bg-sky-50 dark:bg-sky-950/40',
description: 'All-time ride requests',
},
{
title: 'Active Ongoing',
value: stats.active_rides ?? 0,
icon: Activity,
color: 'text-amber-600 dark:text-amber-400',
bg: 'bg-amber-50 dark:bg-amber-950/40',
description: 'Requested, accepted, or picked up',
},
{
title: 'Completed Trips',
value: stats.completed_rides ?? 0,
icon: CheckCircle2,
color: 'text-teal-600 dark:text-teal-400',
bg: 'bg-teal-50 dark:bg-teal-950/40',
description: 'Successfully finished rides',
},
];
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
{cards.map((card) => {
const Icon = card.icon;
return (
<div
key={card.title}
className="rounded-xl border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900 p-4 shadow-sm flex flex-col justify-between transition-all hover:border-slate-300 dark:hover:border-slate-700"
>
<div className="flex items-center justify-between">
<span className="text-xs font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">
{card.title}
</span>
<div className={`p-2 rounded-lg ${card.bg}`}>
<Icon className={`h-4 w-4 ${card.color}`} />
</div>
</div>
<div className="mt-3">
<div className="text-2xl font-bold tracking-tight text-slate-900 dark:text-white">
{(card.value ?? 0).toLocaleString()}
</div>
<p className="mt-1 text-xs text-slate-500 dark:text-slate-400 line-clamp-1">
{card.description}
</p> // Fixed text formatting
</div>
</div>
);
})}
</div>
);
}