diff --git a/apps/web/src/app.tsx b/apps/web/src/app.tsx index 914b0ca..ced0dec 100644 --- a/apps/web/src/app.tsx +++ b/apps/web/src/app.tsx @@ -16,7 +16,7 @@ import { ExpertReviewsPage } from "@/pages/expert-reviews-page"; import { DiagnosesPage } from "@/pages/diagnoses-page"; import { TelemetryPage } from "@/pages/telemetry-page"; import { MainLayout } from "@/components/layout/main-layout"; -import { trackPageView } from "./lib/telemetry"; +import { trackPageView, trackError } from "./lib/telemetry"; const queryClient = new QueryClient(); @@ -104,11 +104,23 @@ function PageViewTracker() { return null; } +function GlobalErrorTracker() { + useEffect(() => { + const handler = (event: ErrorEvent) => { + trackError(event.filename || "global"); + }; + window.addEventListener("error", handler); + return () => window.removeEventListener("error", handler); + }, []); + return null; +} + export function App() { return ( + ); diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index e24c6c7..fa1b7fa 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -12,6 +12,7 @@ import type { ReviewDiagnosisRequest, DashboardSummary, } from '@zeavis/shared'; +import { recordApiCall } from './telemetry'; const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? ''; @@ -21,6 +22,7 @@ export interface ApiError extends Error { } async function fetchApi(endpoint: string, options?: RequestInit): Promise { + const start = performance.now(); const url = `${apiBaseUrl}${endpoint}`; const response = await fetch(url, { credentials: 'include', @@ -28,6 +30,10 @@ async function fetchApi(endpoint: string, options?: RequestInit): Promise headers: options?.headers, }); + const duration = performance.now() - start; + const method = options?.method ?? 'GET'; + recordApiCall(method, endpoint, duration, response.status); + if (!response.ok) { let errorMessage = `HTTP ${response.status}`; let source: 'uploader' | 'model-service' | 'unknown' | undefined; diff --git a/apps/web/src/pages/scan-page.tsx b/apps/web/src/pages/scan-page.tsx index 4bb9e61..f94ba4d 100644 --- a/apps/web/src/pages/scan-page.tsx +++ b/apps/web/src/pages/scan-page.tsx @@ -18,6 +18,7 @@ import { Modal } from "@/components/ui/modal"; import { DiagnosisStatusBadge } from "@/components/diagnosis-status-badge"; import type { DiagnosisRecord } from "@zeavis/shared"; import { apiClient } from "@/lib/api-client"; +import { trackScan, trackDiagnosisResult } from "@/lib/telemetry"; export function ScanPage() { const [fileName, setFileName] = useState(null); @@ -31,6 +32,7 @@ export function ScanPage() { const mutation = useMutation({ mutationFn: (file: File) => apiClient.createDiagnosis(file), onSuccess: (diagnosis) => { + trackDiagnosisResult(diagnosis.status !== "failed"); queryClient.invalidateQueries({ queryKey: ["diagnoses"] }); setDiagnosisPreview(diagnosis); setPreviewOpen(true); @@ -38,6 +40,9 @@ export function ScanPage() { setPreviewUrl(null); if (inputRef.current) inputRef.current.value = ""; }, + onError: () => { + trackDiagnosisResult(false); + }, }); const handleFile = (f?: File) => { @@ -61,6 +66,7 @@ export function ScanPage() { const handleUpload = () => { const file = inputRef.current?.files?.[0]; if (!file) return; + trackScan(); mutation.mutate(file); };