Files
thrilltrack-explorer/src/components/admin/ApprovalFailureModal.tsx
gpt-engineer-app[bot] eac9902bb0 Implement Phase 3 fixes
The AI has implemented the Phase 3 plan, which includes adding approval failure monitoring to the existing error monitoring page, extending the ErrorAnalytics component with approval metrics, adding performance indexes, and creating the ApprovalFailureModal component.
2025-11-07 00:22:38 +00:00

203 lines
8.1 KiB
TypeScript

import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Card, CardContent } from '@/components/ui/card';
import { format } from 'date-fns';
import { XCircle, Clock, User, FileText, AlertTriangle } from 'lucide-react';
import { Link } from 'react-router-dom';
interface ApprovalFailure {
id: string;
submission_id: string;
moderator_id: string;
submitter_id: string;
items_count: number;
duration_ms: number | null;
error_message: string | null;
request_id: string | null;
rollback_triggered: boolean | null;
created_at: string;
success: boolean;
moderator?: {
username: string;
avatar_url: string | null;
};
submission?: {
submission_type: string;
user_id: string;
};
}
interface ApprovalFailureModalProps {
failure: ApprovalFailure | null;
onClose: () => void;
}
export function ApprovalFailureModal({ failure, onClose }: ApprovalFailureModalProps) {
if (!failure) return null;
return (
<Dialog open={!!failure} onOpenChange={onClose}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<XCircle className="w-5 h-5 text-destructive" />
Approval Failure Details
</DialogTitle>
</DialogHeader>
<Tabs defaultValue="overview" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="error">Error Details</TabsTrigger>
<TabsTrigger value="metadata">Metadata</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<Card>
<CardContent className="pt-6 space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-sm text-muted-foreground mb-1">Timestamp</div>
<div className="font-medium">
{format(new Date(failure.created_at), 'PPpp')}
</div>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Duration</div>
<div className="font-medium flex items-center gap-2">
<Clock className="w-4 h-4" />
{failure.duration_ms != null ? `${failure.duration_ms}ms` : 'N/A'}
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-sm text-muted-foreground mb-1">Submission Type</div>
<Badge variant="outline">
{failure.submission?.submission_type || 'Unknown'}
</Badge>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Items Count</div>
<div className="font-medium">{failure.items_count}</div>
</div>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Moderator</div>
<div className="font-medium flex items-center gap-2">
<User className="w-4 h-4" />
{failure.moderator?.username || 'Unknown'}
</div>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Submission ID</div>
<Link
to={`/admin/moderation?submission=${failure.submission_id}`}
className="font-mono text-sm text-primary hover:underline flex items-center gap-2"
>
<FileText className="w-4 h-4" />
{failure.submission_id}
</Link>
</div>
{failure.rollback_triggered && (
<div className="flex items-center gap-2 p-3 bg-warning/10 text-warning rounded-md">
<AlertTriangle className="w-4 h-4" />
<span className="text-sm font-medium">
Rollback was triggered for this approval
</span>
</div>
)}
</CardContent>
</Card>
</TabsContent>
<TabsContent value="error" className="space-y-4">
<Card>
<CardContent className="pt-6">
<div className="space-y-4">
<div>
<div className="text-sm text-muted-foreground mb-2">Error Message</div>
<div className="p-4 bg-destructive/10 text-destructive rounded-md font-mono text-sm">
{failure.error_message || 'No error message available'}
</div>
</div>
{failure.request_id && (
<div>
<div className="text-sm text-muted-foreground mb-2">Request ID</div>
<div className="p-3 bg-muted rounded-md font-mono text-sm">
{failure.request_id}
</div>
</div>
)}
<div className="mt-4 p-4 bg-muted rounded-md">
<div className="text-sm font-medium mb-2">Troubleshooting Tips</div>
<ul className="text-sm text-muted-foreground space-y-1 list-disc list-inside">
<li>Check if the submission still exists in the database</li>
<li>Verify that all foreign key references are valid</li>
<li>Review the edge function logs for detailed stack traces</li>
<li>Check for concurrent modification conflicts</li>
<li>Verify network connectivity and database availability</li>
</ul>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="metadata" className="space-y-4">
<Card>
<CardContent className="pt-6">
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-sm text-muted-foreground mb-1">Failure ID</div>
<div className="font-mono text-sm">{failure.id}</div>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Success Status</div>
<Badge variant="destructive">
{failure.success ? 'Success' : 'Failed'}
</Badge>
</div>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Moderator ID</div>
<div className="font-mono text-sm">{failure.moderator_id}</div>
</div>
<div>
<div className="text-sm text-muted-foreground mb-1">Submitter ID</div>
<div className="font-mono text-sm">{failure.submitter_id}</div>
</div>
{failure.request_id && (
<div>
<div className="text-sm text-muted-foreground mb-1">Request ID</div>
<div className="font-mono text-sm break-all">{failure.request_id}</div>
</div>
)}
<div>
<div className="text-sm text-muted-foreground mb-1">Rollback Triggered</div>
<Badge variant={failure.rollback_triggered ? 'destructive' : 'secondary'}>
{failure.rollback_triggered ? 'Yes' : 'No'}
</Badge>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
}