import { useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { buildSpanTree, formatSpanTree, calculateSpanStats, extractAllEvents } from '@/lib/spanVisualizer'; import type { Span } from '@/types/tracing'; import type { SpanTree } from '@/lib/spanVisualizer'; /** * Admin Trace Viewer * * Visual tool for debugging distributed traces across the approval pipeline. * Reconstructs and displays span hierarchies from edge function logs. */ export default function TraceViewer() { const [traceId, setTraceId] = useState(''); const [spans, setSpans] = useState([]); const [tree, setTree] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const loadTrace = async () => { if (!traceId.trim()) { setError('Please enter a trace ID'); return; } setIsLoading(true); setError(null); try { // TODO: Replace with actual edge function log query // This would need an edge function that queries Supabase logs // For now, using mock data structure const mockSpans: Span[] = [ { spanId: 'root-1', traceId, name: 'process-selective-approval', kind: 'SERVER', startTime: Date.now() - 5000, endTime: Date.now(), duration: 5000, attributes: { 'http.method': 'POST', 'user.id': 'user-123', 'submission.id': 'sub-456', }, events: [ { timestamp: Date.now() - 4900, name: 'authentication_start' }, { timestamp: Date.now() - 4800, name: 'authentication_success' }, { timestamp: Date.now() - 4700, name: 'validation_complete' }, ], status: 'ok', }, { spanId: 'child-1', traceId, parentSpanId: 'root-1', name: 'process_approval_transaction', kind: 'DATABASE', startTime: Date.now() - 4500, endTime: Date.now() - 500, duration: 4000, attributes: { 'db.operation': 'rpc', 'submission.id': 'sub-456', }, events: [ { timestamp: Date.now() - 4400, name: 'rpc_call_start' }, { timestamp: Date.now() - 600, name: 'rpc_call_success' }, ], status: 'ok', }, ]; setSpans(mockSpans); const builtTree = buildSpanTree(mockSpans); setTree(builtTree); if (!builtTree) { setError('No root span found for this trace ID'); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load trace'); } finally { setIsLoading(false); } }; const stats = tree ? calculateSpanStats(tree) : null; const events = tree ? extractAllEvents(tree) : []; return (

Distributed Trace Viewer

Debug moderation pipeline execution by visualizing span hierarchies

Load Trace Enter a trace ID from edge function logs to visualize the execution tree
setTraceId(e.target.value)} placeholder="Enter trace ID (e.g., abc-123-def-456)" className="flex-1" />
{error && ( {error} )}
{tree && stats && ( <> Trace Statistics
Total Duration
{stats.totalDuration}ms
Total Spans
{stats.totalSpans}
Max Depth
{stats.maxDepth}
Errors
{stats.errorCount}
Critical Path (Longest Duration):
{stats.criticalPath.map((spanName, i) => ( {spanName} ))}
Span Tree Hierarchical view of span execution with timing breakdown
                {formatSpanTree(tree)}
              
Events Timeline Chronological list of all events across all spans
{events.map((event, i) => (
{event.spanName} {event.eventName} {new Date(event.timestamp).toISOString()}
))}
Span Details Detailed breakdown of each span with attributes and events {spans.map((span) => (
{span.kind} {span.name} ({span.duration}ms)
                        {JSON.stringify(span, null, 2)}
                      
))}
)} {!tree && !isLoading && !error && ( Enter a trace ID to visualize the distributed trace. You can find trace IDs in edge function logs under the "Span completed" messages. )}
); }