import { useState } from "react"; /* Minimal inline SVG icon components to avoid requiring 'lucide-react' */ const DownloadIcon = ({ className }: { className?: string }) => ( ); const FileTextIcon = ({ className }: { className?: string }) => ( ); const Loader2Icon = ({ className }: { className?: string }) => ( ); import { ImageWithFallback } from "./ImageWithFallback"; import { ReportModal } from "./ReportModal"; import axios from "axios"; interface ResultsPanelProps { uploadedImage: string | null; result?: any; loading?: boolean; } export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelProps) { const [showReportModal, setShowReportModal] = useState(false); // Make loading detection robust: sometimes values arrive as the string "true" from deployed envs const isLoading = loading === true || String(loading) === "true"; // Helpful debug information when checking issues on deployed spaces (open browser devtools) // Keep as debug (console.debug) so it doesn't clutter normal logs. console.debug("ResultsPanel: props", { loading, isLoading, result }); const handleGenerateReport = async (formData: FormData) => { try { const baseURL = import.meta.env.MODE === "development" ? "http://127.0.0.1:7860" : window.location.origin; const response = await axios.post(`${baseURL}/reports/`, formData, { headers: { "Content-Type": "multipart/form-data" }, }); if (response.data.html_url) { // Open report in new tab window.open(`${baseURL}${response.data.html_url}`, "_blank"); } if (response.data.pdf_url) { // Open PDF in new tab when available window.open(`${baseURL}${response.data.pdf_url}`, "_blank"); } setShowReportModal(false); } catch (err: any) { console.error("Failed to generate report:", err); alert(err.response?.data?.error || "Failed to generate report"); } }; // Safely destructure result (keep undefined values when result is null) so we can render // a stable panel while loading and avoid early returns that change layout. const { model_used, detections, annotated_image_url, summary, // prediction (not used here) confidence, } = (result || {}) as any; const handleDownload = () => { if (annotated_image_url) { const link = document.createElement("a"); link.href = annotated_image_url; link.download = "analysis_result.jpg"; // For Firefox it is necessary to add the link to the DOM document.body.appendChild(link); link.click(); link.remove(); } }; // Precompute some helpers for rendering confidences const isCINModel = /cin/i.test(String(model_used || "")); // Determine predicted class from summary.prediction if available, otherwise pick the highest confidence const predictedClassFromConfidence = (conf: any) => { try { const entries = Object.entries(conf || {}); if (entries.length === 0) return ""; return entries.reduce((a: any, b: any) => (Number(a[1]) > Number(b[1]) ? a : b))[0]; } catch (e) { return ""; } }; // Prefer the class key present in the `confidence` object to ensure we use the exact key/casing // (this avoids showing all bars when `summary.prediction` has different casing/format). const predictedClass = predictedClassFromConfidence(confidence || {}) || ((summary && (summary.prediction || summary.result)) ? String(summary.prediction || summary.result) : ""); return (
Automated Image Analysis
Analyzing image...
Result: {isAbnormal ? "Abnormal" : "Normal"}
{typeof summary.abnormal_cells !== 'undefined' && (
<>Abnormal Cells: {summary.abnormal_cells}
>
)}
{typeof summary.normal_cells !== 'undefined' && (
<>Normal Cells: {summary.normal_cells}
>
)}
{/* average confidence removed */}
{typeof summary.abnormal_cells !== 'undefined' && (
<>Abnormal Cells: {summary.abnormal_cells}
>
)}
{typeof summary.normal_cells !== 'undefined' && (
<>Normal Cells: {summary.normal_cells}
>
)}
{JSON.stringify(confidence, null, 2)}
)}