Files
thrilltrack-explorer/src/components/admin/DataRetentionPanel.tsx
gpt-engineer-app[bot] 915a9fe2df Add automated data retention cleanup
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.
2025-11-11 02:21:27 +00:00

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>
);
}