Turns the admin panel into a real monitoring tool so production bugs are visible instead of silent. - Error & crash tracking: error_events table (migration 0010) + logError() helper + /api/errors ingest; global-error.tsx and (app)/error.tsx report crashes automatically; /admin/errors viewer (recent + grouped, filters). - Full audit-log viewer at /admin/audit over the existing audit_log (all actions, not just auth) with action/resource/family/user/text filters. - AI observability at /admin/ai over ai_usage: per-intent latency (avg/p95), tokens, cost, daily trend, slowest calls, medical-redirect count. - System health at /admin/health: DB latency, migration status, recent error volume, and integration config presence. - Sidebar updated with Health / Errors / Audit Log / AI Usage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
|
|
// Error boundary for the authenticated (app) segment. Catches render/runtime
|
|
// errors in any /(app) page (home, ai, growth, …), shows a friendly recovery
|
|
// UI, and reports the crash to /api/errors for the admin error tracker.
|
|
export default function AppError({
|
|
error,
|
|
reset,
|
|
}: {
|
|
error: Error & { digest?: string };
|
|
reset: () => void;
|
|
}) {
|
|
useEffect(() => {
|
|
try {
|
|
fetch("/api/errors", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
message: error?.message || "Unknown app error",
|
|
stack: error?.stack,
|
|
digest: error?.digest,
|
|
url: typeof window !== "undefined" ? window.location.pathname : undefined,
|
|
level: "error",
|
|
metadata: { boundary: "app" },
|
|
}),
|
|
keepalive: true,
|
|
}).catch(() => {});
|
|
} catch {}
|
|
}, [error]);
|
|
|
|
return (
|
|
<div className="min-h-screen flex flex-col items-center justify-center gap-4 p-6 text-center bg-gradient-to-br from-rose-50 to-amber-50 dark:from-gray-900 dark:to-gray-800">
|
|
<div className="text-5xl">😵</div>
|
|
<h1 className="text-xl font-bold dark:text-white">Something went wrong</h1>
|
|
<p className="text-gray-500 dark:text-gray-400 max-w-sm text-sm">
|
|
This screen hit an unexpected error. It's been reported automatically.
|
|
</p>
|
|
<button
|
|
onClick={() => reset()}
|
|
className="bg-rose-400 text-white px-5 py-2.5 rounded-xl font-semibold"
|
|
>
|
|
Try again
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|