'use client'; import { useEffect, useState } from 'react'; import Link from 'next/link'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { CheckCircle2, XCircle, Loader2 } from 'lucide-react'; interface ReportSummary { id: string; generatedAt: string; reportTitle: string | null; ticketCount: number; status: 'pending' | 'running' | 'complete' | 'failed'; estimatedCostUsd: number | null; modelUsed: string | null; generatedByUserId: string | null; } export default function ReportsListPage() { const [reports, setReports] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; fetch('/api/analyzer/aggregate-reports') .then((r) => (r.ok ? r.json() : Promise.reject(r))) .then((data: { reports: ReportSummary[] }) => { if (!cancelled) setReports(data.reports); }) .catch((err) => { if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load reports'); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); return (

Aggregate reports

Cross-ticket pattern analysis. Generate a new one from the browse page.

{loading ? (
Loading…
) : error ? (
{error}
) : reports.length === 0 ? (

No reports yet.

Pick tickets on the browse page and click{' '} Generate aggregate report.

) : ( Status Title Tickets Model Cost Generated Action {reports.map((r) => ( {r.status === 'complete' ? ( Complete ) : r.status === 'failed' ? ( Failed ) : ( {r.status} )} {r.reportTitle ?? ( Untitled )} {r.ticketCount} {r.modelUsed ?? '—'} {r.estimatedCostUsd === null ? '—' : `$${r.estimatedCostUsd.toFixed(4)}`} {new Date(r.generatedAt).toLocaleString()} ))}
)}
); }