Files
thrilltrack-explorer/src/components/admin/database-stats/DatabaseHealthDashboard.tsx
gpt-engineer-app[bot] 947964482f 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.
2025-11-11 17:11:11 +00:00

160 lines
6.2 KiB
TypeScript

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { useDatabaseHealthCheck } from '@/hooks/useDatabaseHealthCheck';
import { AlertCircle, AlertTriangle, Info, CheckCircle2 } from 'lucide-react';
import { Progress } from '@/components/ui/progress';
import { HealthIssueCard } from './HealthIssueCard';
import { Accordion } from '@/components/ui/accordion';
export function DatabaseHealthDashboard() {
const { data, isLoading } = useDatabaseHealthCheck();
if (isLoading || !data) {
return (
<Card>
<CardHeader>
<CardTitle>Database Health</CardTitle>
<CardDescription>Loading health checks...</CardDescription>
</CardHeader>
<CardContent>
<div className="animate-pulse space-y-4">
<div className="h-32 bg-muted rounded" />
<div className="h-64 bg-muted rounded" />
</div>
</CardContent>
</Card>
);
}
const { overall_score, critical_issues, warning_issues, info_issues, issues } = data;
const getScoreColor = (score: number) => {
if (score >= 80) return 'text-green-600';
if (score >= 60) return 'text-yellow-600';
if (score >= 40) return 'text-orange-600';
return 'text-red-600';
};
const getScoreBackground = (score: number) => {
if (score >= 80) return 'bg-green-600';
if (score >= 60) return 'bg-yellow-600';
if (score >= 40) return 'bg-orange-600';
return 'bg-red-600';
};
const criticalIssues = issues.filter(i => i.severity === 'critical');
const warningIssues = issues.filter(i => i.severity === 'warning');
const infoIssues = issues.filter(i => i.severity === 'info');
return (
<Card>
<CardHeader>
<CardTitle>Database Health</CardTitle>
<CardDescription>Automated health checks and data quality issues</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Overall Health Score */}
<div className="flex items-center justify-between p-6 border rounded-lg bg-card">
<div className="space-y-2">
<h3 className="text-sm font-medium text-muted-foreground">Overall Health Score</h3>
<div className={`text-6xl font-bold ${getScoreColor(overall_score)}`}>
{overall_score}
</div>
<p className="text-sm text-muted-foreground">Out of 100</p>
</div>
<div className="flex flex-col gap-3">
<div className="flex items-center gap-3">
<AlertCircle className="h-5 w-5 text-red-600" />
<span className="text-sm font-medium">Critical Issues:</span>
<span className="text-lg font-bold">{critical_issues}</span>
</div>
<div className="flex items-center gap-3">
<AlertTriangle className="h-5 w-5 text-yellow-600" />
<span className="text-sm font-medium">Warnings:</span>
<span className="text-lg font-bold">{warning_issues}</span>
</div>
<div className="flex items-center gap-3">
<Info className="h-5 w-5 text-blue-600" />
<span className="text-sm font-medium">Info:</span>
<span className="text-lg font-bold">{info_issues}</span>
</div>
</div>
</div>
{/* Progress Bar */}
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span>Database Health</span>
<span className={getScoreColor(overall_score)}>{overall_score}%</span>
</div>
<div className="relative">
<Progress value={overall_score} className="h-3" />
<div
className={`absolute inset-0 rounded-full ${getScoreBackground(overall_score)} transition-all`}
style={{ width: `${overall_score}%` }}
/>
</div>
</div>
{/* Issues List */}
{issues.length === 0 ? (
<div className="text-center py-12">
<CheckCircle2 className="h-16 w-16 text-green-600 mx-auto mb-4" />
<h3 className="text-xl font-semibold mb-2">All Systems Healthy!</h3>
<p className="text-muted-foreground">
No database health issues detected at this time.
</p>
</div>
) : (
<div className="space-y-4">
{/* Critical Issues */}
{criticalIssues.length > 0 && (
<div className="space-y-2">
<h3 className="text-lg font-semibold text-red-600 flex items-center gap-2">
<AlertCircle className="h-5 w-5" />
Critical Issues ({criticalIssues.length})
</h3>
<Accordion type="multiple" className="space-y-2">
{criticalIssues.map((issue, index) => (
<HealthIssueCard key={index} issue={issue} />
))}
</Accordion>
</div>
)}
{/* Warnings */}
{warningIssues.length > 0 && (
<div className="space-y-2">
<h3 className="text-lg font-semibold text-yellow-600 flex items-center gap-2">
<AlertTriangle className="h-5 w-5" />
Warnings ({warningIssues.length})
</h3>
<Accordion type="multiple" className="space-y-2">
{warningIssues.map((issue, index) => (
<HealthIssueCard key={index} issue={issue} />
))}
</Accordion>
</div>
)}
{/* Info */}
{infoIssues.length > 0 && (
<div className="space-y-2">
<h3 className="text-lg font-semibold text-blue-600 flex items-center gap-2">
<Info className="h-5 w-5" />
Information ({infoIssues.length})
</h3>
<Accordion type="multiple" className="space-y-2">
{infoIssues.map((issue, index) => (
<HealthIssueCard key={index} issue={issue} />
))}
</Accordion>
</div>
)}
</div>
)}
</CardContent>
</Card>
);
}