From 7ac23886d6d0e4c5484421e2862678ed5d35ad5b Mon Sep 17 00:00:00 2001 From: Garvit Sharma Date: Wed, 29 Apr 2026 15:13:30 +0530 Subject: [PATCH 1/2] feat(frontend): add ApiError component and improve API error handling --- frontend/src/components/ui/ApiError.tsx | 25 +++++++++++++++++++++++++ frontend/src/pages/NewAnalysis.tsx | 13 ++++++++++--- frontend/src/pages/Upload.tsx | 11 +++++++---- 3 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/ui/ApiError.tsx diff --git a/frontend/src/components/ui/ApiError.tsx b/frontend/src/components/ui/ApiError.tsx new file mode 100644 index 0000000..93021f6 --- /dev/null +++ b/frontend/src/components/ui/ApiError.tsx @@ -0,0 +1,25 @@ +import { AlertCircle, X } from "lucide-react"; +import { useState } from "react"; + +interface ApiErrorProps { + message: string; +} + +export function ApiError({ message }: ApiErrorProps) { + const [visible, setVisible] = useState(true); + + if (!visible) return null; + + return ( +
+
+ +

{message}

+
+ + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/pages/NewAnalysis.tsx b/frontend/src/pages/NewAnalysis.tsx index a670bc8..7fc2fb3 100644 --- a/frontend/src/pages/NewAnalysis.tsx +++ b/frontend/src/pages/NewAnalysis.tsx @@ -10,6 +10,7 @@ import { ErrorBoundary } from '../components/ui/ErrorBoundary' import { useToast } from '../contexts/ToastContext' import { useApp } from '../contexts/AppContext' import type { Run } from '../api' +import { ApiError } from '../components/ui/ApiError' const PRESETS = [ { label: 'Last 30d', days: 30 }, @@ -44,6 +45,7 @@ export default function NewAnalysis() { const [busy, setBusy] = useState(false) const [resultRun, setResultRun] = useState(null) const [resultPayload, setResultPayload] = useState | null>(null) + const [error, setError] = useState(null) const canSubmit = bbox !== null && startDate && endDate && !busy @@ -67,8 +69,11 @@ export default function NewAnalysis() { setResultPayload(null) try { + setBusy(true); + setError(null); const res = await predictJson({ kind: 'bbox', analysis_type: analysisType, bbox: bbox!, start_date: startDate, end_date: endDate }) setResultPayload(res.result) + // Construct a minimal Run object for the results panel setResultRun({ id: res.run_id, @@ -85,8 +90,9 @@ export default function NewAnalysis() { label: 'View in history', onClick: () => navigate('/runs'), }) - } catch (e) { - showToast('error', String(e)) + } catch (e:any) { + const message = e?.response?.data?.detail || e?.message || "Something went wrong"; + setError(message); } finally { setBusy(false) } @@ -94,7 +100,8 @@ export default function NewAnalysis() { return (
- + {error && (
)} + {/* Step 1 — Analysis Type */}
diff --git a/frontend/src/pages/Upload.tsx b/frontend/src/pages/Upload.tsx index 5107689..f4c89af 100644 --- a/frontend/src/pages/Upload.tsx +++ b/frontend/src/pages/Upload.tsx @@ -8,6 +8,7 @@ import { MapBBoxPicker } from '../components/Map/MapBBoxPicker' import { ErrorBoundary } from '../components/ui/ErrorBoundary' import { useToast } from '../contexts/ToastContext' import { useApp } from '../contexts/AppContext' +import { ApiError } from '../components/ui/ApiError' const ACCEPTED = ['.tif', '.tiff', '.geotiff', '.nc', '.hdf5'] const MAX_MB = 500 @@ -31,6 +32,7 @@ export default function Upload() { const [startDate, setStartDate] = useState('') const [endDate, setEndDate] = useState('') const [busy, setBusy] = useState(false) + const [error, setError] = useState(null) const [uploadProgress, setUploadProgress] = useState(null) const fileInputRef = useRef(null) @@ -75,9 +77,10 @@ export default function Upload() { }) setFile(null) setUploadProgress(null) - } catch (e) { - showToast('error', String(e)) - setUploadProgress(null) + } catch (e:any) { + const message = e?.response?.data?.detail || e?.message || "Upload failed"; + setError(message); + setUploadProgress(null); } finally { setBusy(false) } @@ -85,7 +88,7 @@ export default function Upload() { return (
- + {error && ()} {/* Drop Zone */}
{ e.preventDefault(); setDragging(true) }} From c68cbbe715858c35a996726db6608f3017f9d03e Mon Sep 17 00:00:00 2001 From: Garvit Sharma Date: Wed, 29 Apr 2026 15:26:44 +0530 Subject: [PATCH 2/2] fix(frontend): remove unused import and restore ApiError styles --- frontend/src/components/ui/ApiError.tsx | 24 ++++++++++++++---------- frontend/src/pages/NewAnalysis.tsx | 4 ++-- frontend/src/pages/Upload.tsx | 3 ++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/ui/ApiError.tsx b/frontend/src/components/ui/ApiError.tsx index 93021f6..3aca085 100644 --- a/frontend/src/components/ui/ApiError.tsx +++ b/frontend/src/components/ui/ApiError.tsx @@ -1,24 +1,28 @@ import { AlertCircle, X } from "lucide-react"; -import { useState } from "react"; + interface ApiErrorProps { - message: string; + message: string | null; + onDismiss: () => void; } -export function ApiError({ message }: ApiErrorProps) { - const [visible, setVisible] = useState(true); - - if (!visible) return null; +export function ApiError({ message, onDismiss }: ApiErrorProps) { + if (!message) return null; return ( -
+
-

{message}

+ +

{message}

-
); diff --git a/frontend/src/pages/NewAnalysis.tsx b/frontend/src/pages/NewAnalysis.tsx index 7fc2fb3..bb23acf 100644 --- a/frontend/src/pages/NewAnalysis.tsx +++ b/frontend/src/pages/NewAnalysis.tsx @@ -64,7 +64,7 @@ export default function NewAnalysis() { return } - setBusy(true) + setResultRun(null) setResultPayload(null) @@ -100,7 +100,7 @@ export default function NewAnalysis() { return (
- {error && (
)} + setError(null)} /> {/* Step 1 — Analysis Type */}
diff --git a/frontend/src/pages/Upload.tsx b/frontend/src/pages/Upload.tsx index f4c89af..7ba2be5 100644 --- a/frontend/src/pages/Upload.tsx +++ b/frontend/src/pages/Upload.tsx @@ -59,6 +59,7 @@ export default function Upload() { const handleUpload = async () => { if (!file) return setBusy(true) + setError(null); setUploadProgress(0) try { @@ -88,7 +89,7 @@ export default function Upload() { return (
- {error && ()} + setError(null)} /> {/* Drop Zone */}
{ e.preventDefault(); setDragging(true) }}