mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 08:51:13 -05:00
Implements edge function, Django tasks, and UI hooks/panels for automatic retention of old metrics, anomalies, alerts, and incidents, plus updates to query keys and monitoring dashboard to reflect data-retention workflows.
162 lines
6.1 KiB
TypeScript
162 lines
6.1 KiB
TypeScript
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Trash2, Database, Clock, HardDrive, TrendingDown } from "lucide-react";
|
|
import { useRetentionStats, useRunCleanup } from "@/hooks/admin/useDataRetention";
|
|
import { formatDistanceToNow } from "date-fns";
|
|
|
|
export function DataRetentionPanel() {
|
|
const { data: stats, isLoading } = useRetentionStats();
|
|
const runCleanup = useRunCleanup();
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Data Retention</CardTitle>
|
|
<CardDescription>Loading retention statistics...</CardDescription>
|
|
</CardHeader>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
const totalRecords = stats?.reduce((sum, s) => sum + s.total_records, 0) || 0;
|
|
const totalSize = stats?.reduce((sum, s) => {
|
|
const size = s.table_size.replace(/[^0-9.]/g, '');
|
|
return sum + parseFloat(size);
|
|
}, 0) || 0;
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Database className="h-5 w-5" />
|
|
Data Retention Management
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Automatic cleanup of old metrics and monitoring data
|
|
</CardDescription>
|
|
</div>
|
|
<Button
|
|
onClick={() => runCleanup.mutate()}
|
|
disabled={runCleanup.isPending}
|
|
variant="destructive"
|
|
size="sm"
|
|
>
|
|
<Trash2 className="h-4 w-4 mr-2" />
|
|
Run Cleanup Now
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
{/* Summary Stats */}
|
|
<div className="grid gap-4 md:grid-cols-3">
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
<Database className="h-4 w-4" />
|
|
Total Records
|
|
</div>
|
|
<div className="text-2xl font-bold">{totalRecords.toLocaleString()}</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
<HardDrive className="h-4 w-4" />
|
|
Total Size
|
|
</div>
|
|
<div className="text-2xl font-bold">{totalSize.toFixed(1)} MB</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
<TrendingDown className="h-4 w-4" />
|
|
Tables Monitored
|
|
</div>
|
|
<div className="text-2xl font-bold">{stats?.length || 0}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Retention Policies */}
|
|
<div>
|
|
<h3 className="font-semibold mb-3">Retention Policies</h3>
|
|
<div className="space-y-2 text-sm">
|
|
<div className="flex justify-between items-center p-2 bg-muted/50 rounded">
|
|
<span>Metrics (metric_time_series)</span>
|
|
<Badge variant="outline">30 days</Badge>
|
|
</div>
|
|
<div className="flex justify-between items-center p-2 bg-muted/50 rounded">
|
|
<span>Anomaly Detections</span>
|
|
<Badge variant="outline">30 days</Badge>
|
|
</div>
|
|
<div className="flex justify-between items-center p-2 bg-muted/50 rounded">
|
|
<span>Resolved Alerts</span>
|
|
<Badge variant="outline">90 days</Badge>
|
|
</div>
|
|
<div className="flex justify-between items-center p-2 bg-muted/50 rounded">
|
|
<span>Resolved Incidents</span>
|
|
<Badge variant="outline">90 days</Badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Table Statistics */}
|
|
<div>
|
|
<h3 className="font-semibold mb-3">Storage Details</h3>
|
|
<div className="space-y-3">
|
|
{stats?.map((stat) => (
|
|
<div
|
|
key={stat.table_name}
|
|
className="border rounded-lg p-3 space-y-2"
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-medium">{stat.table_name}</span>
|
|
<Badge variant="secondary">{stat.table_size}</Badge>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-2 text-xs text-muted-foreground">
|
|
<div>
|
|
<div>Total</div>
|
|
<div className="font-medium text-foreground">
|
|
{stat.total_records.toLocaleString()}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div>Last 7 days</div>
|
|
<div className="font-medium text-foreground">
|
|
{stat.last_7_days.toLocaleString()}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div>Last 30 days</div>
|
|
<div className="font-medium text-foreground">
|
|
{stat.last_30_days.toLocaleString()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{stat.oldest_record && (
|
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
<Clock className="h-3 w-3" />
|
|
Oldest:{" "}
|
|
{formatDistanceToNow(new Date(stat.oldest_record), {
|
|
addSuffix: true,
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Cleanup Schedule */}
|
|
<div className="bg-muted/50 rounded-lg p-4 space-y-2">
|
|
<h3 className="font-semibold text-sm">Automated Cleanup Schedule</h3>
|
|
<div className="space-y-1 text-sm text-muted-foreground">
|
|
<div>• Full cleanup runs daily at 3:00 AM</div>
|
|
<div>• Metrics cleanup at 3:30 AM</div>
|
|
<div>• Anomaly cleanup at 4:00 AM</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|