mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 07:51:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
219
src-old/components/admin/NotificationDebugPanel.tsx
Normal file
219
src-old/components/admin/NotificationDebugPanel.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { AlertTriangle, CheckCircle, RefreshCw, Loader2 } from 'lucide-react';
|
||||
import { supabase } from '@/lib/supabaseClient';
|
||||
import { format } from 'date-fns';
|
||||
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||
|
||||
interface DuplicateStats {
|
||||
date: string | null;
|
||||
total_attempts: number | null;
|
||||
duplicates_prevented: number | null;
|
||||
prevention_rate: number | null;
|
||||
health_status: 'healthy' | 'warning' | 'critical';
|
||||
}
|
||||
|
||||
interface RecentDuplicate {
|
||||
id: string;
|
||||
user_id: string;
|
||||
channel: string;
|
||||
idempotency_key: string | null;
|
||||
created_at: string;
|
||||
profiles?: {
|
||||
username: string;
|
||||
display_name: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export function NotificationDebugPanel() {
|
||||
const [stats, setStats] = useState<DuplicateStats[]>([]);
|
||||
const [recentDuplicates, setRecentDuplicates] = useState<RecentDuplicate[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Load health dashboard
|
||||
const { data: healthData, error: healthError } = await supabase
|
||||
.from('notification_health_dashboard')
|
||||
.select('*')
|
||||
.limit(7);
|
||||
|
||||
if (healthError) throw healthError;
|
||||
if (healthData) {
|
||||
setStats(healthData.map(stat => ({
|
||||
...stat,
|
||||
health_status: stat.health_status as 'healthy' | 'warning' | 'critical'
|
||||
})));
|
||||
}
|
||||
|
||||
// Load recent prevented duplicates
|
||||
const { data: duplicates, error: duplicatesError } = await supabase
|
||||
.from('notification_logs')
|
||||
.select(`
|
||||
id,
|
||||
user_id,
|
||||
channel,
|
||||
idempotency_key,
|
||||
created_at
|
||||
`)
|
||||
.eq('is_duplicate', true)
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(10);
|
||||
|
||||
if (duplicatesError) throw duplicatesError;
|
||||
|
||||
if (duplicates) {
|
||||
// Fetch profiles separately
|
||||
const userIds = [...new Set(duplicates.map(d => d.user_id))];
|
||||
const { data: profiles } = await supabase
|
||||
.from('profiles')
|
||||
.select('user_id, username, display_name')
|
||||
.in('user_id', userIds);
|
||||
|
||||
const profileMap = new Map(profiles?.map(p => [p.user_id, p]) || []);
|
||||
|
||||
setRecentDuplicates(duplicates.map(dup => ({
|
||||
...dup,
|
||||
profiles: profileMap.get(dup.user_id)
|
||||
})));
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
handleNonCriticalError(error, {
|
||||
action: 'Load notification debug data'
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getHealthBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
return (
|
||||
<Badge variant="default" className="bg-green-500">
|
||||
<CheckCircle className="h-3 w-3 mr-1" />
|
||||
Healthy
|
||||
</Badge>
|
||||
);
|
||||
case 'warning':
|
||||
return (
|
||||
<Badge variant="secondary">
|
||||
<AlertTriangle className="h-3 w-3 mr-1" />
|
||||
Warning
|
||||
</Badge>
|
||||
);
|
||||
case 'critical':
|
||||
return (
|
||||
<Badge variant="destructive">
|
||||
<AlertTriangle className="h-3 w-3 mr-1" />
|
||||
Critical
|
||||
</Badge>
|
||||
);
|
||||
default:
|
||||
return <Badge>Unknown</Badge>;
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center justify-center">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>Notification Health Dashboard</CardTitle>
|
||||
<CardDescription>Monitor duplicate prevention and notification system health</CardDescription>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" onClick={loadData} loading={isLoading} loadingText="Loading...">
|
||||
<RefreshCw className="h-4 w-4 mr-2" />
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{stats.length === 0 ? (
|
||||
<Alert>
|
||||
<AlertDescription>No notification statistics available yet</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Date</TableHead>
|
||||
<TableHead className="text-right">Total Attempts</TableHead>
|
||||
<TableHead className="text-right">Duplicates Prevented</TableHead>
|
||||
<TableHead className="text-right">Prevention Rate</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{stats.map((stat) => (
|
||||
<TableRow key={stat.date || 'unknown'}>
|
||||
<TableCell>{stat.date ? format(new Date(stat.date), 'MMM d, yyyy') : 'N/A'}</TableCell>
|
||||
<TableCell className="text-right">{stat.total_attempts ?? 0}</TableCell>
|
||||
<TableCell className="text-right">{stat.duplicates_prevented ?? 0}</TableCell>
|
||||
<TableCell className="text-right">{stat.prevention_rate !== null ? stat.prevention_rate.toFixed(1) : 'N/A'}%</TableCell>
|
||||
<TableCell>{getHealthBadge(stat.health_status)}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Recent Prevented Duplicates</CardTitle>
|
||||
<CardDescription>Notifications that were blocked due to duplication</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{recentDuplicates.length === 0 ? (
|
||||
<Alert>
|
||||
<CheckCircle className="h-4 w-4" />
|
||||
<AlertDescription>No recent duplicates detected</AlertDescription>
|
||||
</Alert>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{recentDuplicates.map((dup) => (
|
||||
<div key={dup.id} className="flex items-center justify-between p-3 border rounded-lg">
|
||||
<div>
|
||||
<div className="font-medium">
|
||||
{dup.profiles?.display_name || dup.profiles?.username || 'Unknown User'}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Channel: {dup.channel} • Key: {dup.idempotency_key?.substring(0, 12)}...
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{format(new Date(dup.created_at), 'PPp')}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user