Enhance admin stats dashboard

Add data quality metrics, growth trends visualization, entity comparison views, and automated health checks to the AdminDatabaseStats dashboard, including new TS types, hooks, UI components, and integrated tabbed layout.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-11 17:11:11 +00:00
parent f036776dce
commit 947964482f
14 changed files with 1579 additions and 88 deletions

View File

@@ -0,0 +1,124 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Progress } from '@/components/ui/progress';
import { Link } from 'react-router-dom';
import { ArrowRight, CheckCircle2, AlertCircle } from 'lucide-react';
import { useDataCompleteness } from '@/hooks/useDataCompleteness';
export function DataQualityOverview() {
const { data, isLoading } = useDataCompleteness();
if (isLoading || !data) {
return (
<Card>
<CardHeader>
<CardTitle>Data Quality</CardTitle>
<CardDescription>Loading completeness metrics...</CardDescription>
</CardHeader>
<CardContent>
<div className="animate-pulse space-y-4">
<div className="h-20 bg-muted rounded" />
<div className="h-20 bg-muted rounded" />
</div>
</CardContent>
</Card>
);
}
const { summary } = data;
const avgScore = Math.round(summary.avg_completeness_score);
const getScoreColor = (score: number) => {
if (score >= 80) return 'text-green-600';
if (score >= 60) return 'text-blue-600';
if (score >= 40) return 'text-yellow-600';
return 'text-red-600';
};
const getProgressColor = (score: number) => {
if (score >= 80) return 'bg-green-600';
if (score >= 60) return 'bg-blue-600';
if (score >= 40) return 'bg-yellow-600';
return 'bg-red-600';
};
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Data Quality</CardTitle>
<CardDescription>Overall completeness metrics across all entities</CardDescription>
</div>
<Link
to="/admin/data-completeness"
className="text-sm text-primary hover:text-primary/80 flex items-center gap-1"
>
View Details <ArrowRight className="h-4 w-4" />
</Link>
</div>
</CardHeader>
<CardContent className="space-y-6">
{/* Average Score */}
<div>
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium">Average Completeness</span>
<span className={`text-3xl font-bold ${getScoreColor(avgScore)}`}>
{avgScore}%
</span>
</div>
<div className="relative">
<Progress value={avgScore} className="h-3" />
<div
className={`absolute inset-0 rounded-full ${getProgressColor(avgScore)} transition-all`}
style={{ width: `${avgScore}%` }}
/>
</div>
</div>
{/* Quick Stats Grid */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-600" />
<span className="text-sm font-medium">100% Complete</span>
</div>
<div className="text-2xl font-bold">{summary.entities_100_complete}</div>
<div className="text-xs text-muted-foreground">
{((summary.entities_100_complete / summary.total_entities) * 100).toFixed(1)}% of total
</div>
</div>
<div className="space-y-1">
<div className="flex items-center gap-2">
<AlertCircle className="h-4 w-4 text-yellow-600" />
<span className="text-sm font-medium">Below 50%</span>
</div>
<div className="text-2xl font-bold">{summary.entities_below_50}</div>
<div className="text-xs text-muted-foreground">
{((summary.entities_below_50 / summary.total_entities) * 100).toFixed(1)}% need attention
</div>
</div>
</div>
{/* By Entity Type */}
<div className="space-y-3">
<h4 className="text-sm font-medium">By Entity Type</h4>
<div className="space-y-2">
{[
{ label: 'Parks', value: summary.by_entity_type.parks, total: summary.total_entities },
{ label: 'Rides', value: summary.by_entity_type.rides, total: summary.total_entities },
{ label: 'Companies', value: summary.by_entity_type.companies, total: summary.total_entities },
{ label: 'Models', value: summary.by_entity_type.ride_models, total: summary.total_entities },
].map((item) => (
<div key={item.label} className="flex items-center gap-2">
<span className="text-xs w-20">{item.label}</span>
<Progress value={(item.value / item.total) * 100} className="h-2 flex-1" />
<span className="text-xs text-muted-foreground w-12 text-right">{item.value}</span>
</div>
))}
</div>
</div>
</CardContent>
</Card>
);
}