'use client'; import { useState } from 'react'; import Link from 'next/link'; import { useParams } from 'next/navigation'; import { useQuery } from '@tanstack/react-query'; import { format, formatDistanceToNow } from 'date-fns'; import { AlertTriangle, BookOpen, Search, Wrench } from 'lucide-react'; import type { Route } from 'next'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Skeleton } from '@/components/ui/skeleton'; import { PageHeader } from '@/components/shared/page-header'; import { EmptyState } from '@/components/shared/empty-state'; import { apiFetch } from '@/lib/api/client'; import { classifyError } from '@/lib/error-classifier'; import type { ErrorEvent } from '@/lib/db/schema/system'; interface ListResponse { data: ErrorEvent[]; } /** * Super-admin error inspector. * * Shows the most recent captured 5xx errors with: when, where (HTTP * method + path), what (error name + message), and a heuristic * "likely culprit" badge driven by `classifyError`. Click into any * row for the full stack + body excerpt + raw metadata. */ export default function AdminErrorsPage() { const params = useParams<{ portSlug: string }>(); const portSlug = params?.portSlug ?? ''; const [statusFilter, setStatusFilter] = useState(''); const query = useQuery({ queryKey: ['admin', 'error-events', { statusFilter }], queryFn: () => { const search = new URLSearchParams(); if (statusFilter) search.set('statusCode', statusFilter); return apiFetch( `/api/v1/admin/error-events${search.toString() ? `?${search.toString()}` : ''}`, ); }, }); const events = query.data?.data ?? []; return (
Code reference } /> Filters
setStatusFilter(e.target.value.replace(/\D/g, ''))} className="h-8 w-32" />
{statusFilter && ( )}
{query.isLoading ? (
{Array.from({ length: 5 }).map((_, i) => ( ))}
) : events.length === 0 ? ( ) : (
{events.map((event) => { const culprit = classifyError(event); return (
= 500 ? 'border-destructive/40 text-destructive' : 'border-amber-300 text-amber-800' } > {event.statusCode} {event.method} {event.path} {culprit && ( {culprit.label} )}

{event.errorName ? `${event.errorName}: ` : ''} {event.errorMessage ?? '(no message)'}

{formatDistanceToNow(new Date(event.createdAt), { addSuffix: true })} ·{' '} {format(new Date(event.createdAt), 'MMM d HH:mm:ss')} · ID{' '} {event.requestId.slice(0, 12)}…

); })}
)}
); }