mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 11:31:11 -05:00
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.
111 lines
3.7 KiB
TypeScript
111 lines
3.7 KiB
TypeScript
import { AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import type { HealthIssue } from '@/types/database-analytics';
|
|
import { AlertCircle, AlertTriangle, Info, Lightbulb } from 'lucide-react';
|
|
|
|
interface HealthIssueCardProps {
|
|
issue: HealthIssue;
|
|
}
|
|
|
|
export function HealthIssueCard({ issue }: HealthIssueCardProps) {
|
|
const getSeverityIcon = () => {
|
|
switch (issue.severity) {
|
|
case 'critical':
|
|
return <AlertCircle className="h-4 w-4 text-red-600" />;
|
|
case 'warning':
|
|
return <AlertTriangle className="h-4 w-4 text-yellow-600" />;
|
|
case 'info':
|
|
return <Info className="h-4 w-4 text-blue-600" />;
|
|
}
|
|
};
|
|
|
|
const getSeverityColor = () => {
|
|
switch (issue.severity) {
|
|
case 'critical':
|
|
return 'border-red-600 bg-red-50 dark:bg-red-950/20';
|
|
case 'warning':
|
|
return 'border-yellow-600 bg-yellow-50 dark:bg-yellow-950/20';
|
|
case 'info':
|
|
return 'border-blue-600 bg-blue-50 dark:bg-blue-950/20';
|
|
}
|
|
};
|
|
|
|
const getSeverityBadgeVariant = () => {
|
|
switch (issue.severity) {
|
|
case 'critical':
|
|
return 'destructive';
|
|
case 'warning':
|
|
return 'default';
|
|
case 'info':
|
|
return 'secondary';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<AccordionItem
|
|
value={`issue-${issue.category}-${issue.count}`}
|
|
className={`border rounded-lg ${getSeverityColor()}`}
|
|
>
|
|
<AccordionTrigger className="px-4 hover:no-underline">
|
|
<div className="flex items-center justify-between w-full pr-4">
|
|
<div className="flex items-center gap-3">
|
|
{getSeverityIcon()}
|
|
<div className="text-left">
|
|
<div className="font-semibold">{issue.description}</div>
|
|
<div className="text-sm text-muted-foreground capitalize">
|
|
{issue.category.replace(/_/g, ' ')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Badge variant={getSeverityBadgeVariant()}>
|
|
{issue.count} {issue.count === 1 ? 'entity' : 'entities'}
|
|
</Badge>
|
|
</div>
|
|
</AccordionTrigger>
|
|
|
|
<AccordionContent className="px-4 pb-4 space-y-4">
|
|
{/* Suggested Action */}
|
|
<div className="flex items-start gap-2 p-3 bg-background rounded border">
|
|
<Lightbulb className="h-4 w-4 text-yellow-600 mt-0.5 flex-shrink-0" />
|
|
<div className="space-y-1">
|
|
<div className="text-sm font-medium">Suggested Action</div>
|
|
<div className="text-sm text-muted-foreground">{issue.suggested_action}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Entity IDs (first 10) */}
|
|
{issue.entity_ids && issue.entity_ids.length > 0 && (
|
|
<div className="space-y-2">
|
|
<div className="text-sm font-medium">
|
|
Affected Entities ({issue.entity_ids.length})
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
{issue.entity_ids.slice(0, 10).map((id) => (
|
|
<Badge key={id} variant="outline" className="font-mono text-xs">
|
|
{id.substring(0, 8)}...
|
|
</Badge>
|
|
))}
|
|
{issue.entity_ids.length > 10 && (
|
|
<Badge variant="secondary" className="text-xs">
|
|
+{issue.entity_ids.length - 10} more
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex gap-2">
|
|
<Button size="sm" variant="default">
|
|
View Entities
|
|
</Button>
|
|
<Button size="sm" variant="outline">
|
|
Export List
|
|
</Button>
|
|
</div>
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
);
|
|
}
|