malavikapradeep2001 commited on
Commit
e134c3f
·
1 Parent(s): c9ca900
Files changed (1) hide show
  1. frontend/src/components/ResultsPanel.tsx +159 -163
frontend/src/components/ResultsPanel.tsx CHANGED
@@ -5,9 +5,9 @@ import { ReportModal } from "./ReportModal";
5
  import axios from "axios";
6
 
7
  interface ResultsPanelProps {
8
- uploadedImage: string | null;
9
- result?: any;
10
- loading?: boolean;
11
  }
12
 
13
  export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelProps) {
@@ -16,13 +16,8 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
16
  const handleGenerateReport = async (formData: FormData) => {
17
  try {
18
  const baseURL = import.meta.env.MODE === "development"
19
- <<<<<<< HEAD
20
- ? "http://127.0.0.1:8000"
21
- =======
22
  ? "http://127.0.0.1:7860"
23
- >>>>>>> 63ecad7fc192f2b4ac24e8b13d2e500df74df63d
24
  : window.location.origin;
25
-
26
  const response = await axios.post(`${baseURL}/reports/`, formData, {
27
  headers: { "Content-Type": "multipart/form-data" },
28
  });
@@ -47,7 +42,7 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
47
  return (
48
  <div className="bg-white rounded-lg shadow-sm p-6 flex flex-col items-center justify-center">
49
  <Loader2Icon className="w-10 h-10 text-blue-600 animate-spin mb-3" />
50
- <p className="text-gray-600 font-medium">Analyzing image...</p>
51
  </div>
52
  );
53
  }
@@ -58,172 +53,173 @@ export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelPro
58
  No analysis result available yet.
59
  </div>
60
  );
61
- }
62
 
63
- const {
64
- model_used,
65
- detections,
66
- annotated_image_url,
67
- summary,
68
- // prediction (not used here)
69
- confidence,
70
- } = result;
71
-
72
- const handleDownload = () => {
73
- if (annotated_image_url) {
74
- const link = document.createElement("a");
75
- link.href = annotated_image_url;
76
- link.download = "analysis_result.jpg";
77
- link.click();
78
- }
79
- };
80
-
81
- return ( <div className="bg-white rounded-lg shadow-sm p-6">
82
- {/* Header */}
83
- <div className="flex items-center justify-between mb-6">
84
- <div>
85
- <h2 className="text-2xl font-bold text-gray-800">
86
- {model_used || "Analysis Result"}
87
- </h2>
88
- <p className="text-sm text-gray-500">Automated Image Analysis</p>
89
- </div>
90
 
91
- <div className="flex items-center gap-3">
92
- {annotated_image_url && (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  <button
94
- onClick={handleDownload}
95
- className="flex items-center gap-2 bg-green-600 text-white px-4 py-2 rounded-lg hover:bg-green-700 transition-colors"
96
  >
97
- <DownloadIcon className="w-4 h-4" />
98
- Download Image
99
  </button>
100
- )}
101
- <button
102
- onClick={() => setShowReportModal(true)}
103
- className="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors"
104
- >
105
- <FileTextIcon className="w-4 h-4" />
106
- Generate Report
107
- </button>
108
  </div>
109
- </div>
110
 
111
- {/* Image */}
112
- <div className="relative mb-6 rounded-lg overflow-hidden border border-gray-200">
113
- <ImageWithFallback
114
- src={annotated_image_url || uploadedImage || "/ui.jpg"}
115
- alt="Analysis Result"
116
- className="w-full h-64 object-cover"
117
- />
118
- </div>
119
 
120
- {/* Summary Section */}
121
- {summary && (
122
- <div className="bg-gray-50 p-4 rounded-lg mb-6">
123
- <h3 className="text-lg font-semibold text-gray-800 mb-2">
124
- AI Summary
125
- </h3>
126
- <p className="text-gray-700 text-sm leading-relaxed">
127
- <strong>Abnormal Cells:</strong> {summary.abnormal_cells} <br />
128
- <strong>Normal Cells:</strong> {summary.normal_cells} <br />
129
- <strong>Average Confidence:</strong> {summary.avg_confidence?.toFixed(2)}% <br />
130
- </p>
131
- <div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
132
- {summary.ai_interpretation || "No AI interpretation available."}
 
133
  </div>
134
- </div>
135
- )}
136
-
137
- {/* Detection list */}
138
- {detections && detections.length > 0 && (
139
- <div className="mb-6">
140
- <h4 className="font-semibold text-gray-900 mb-3">
141
- Detected Objects
142
- </h4>
143
- <ul className="text-sm text-gray-700 list-disc list-inside space-y-1">
144
- {detections.map((det: any, i: number) => (
145
- <li key={i}>
146
- {det.name || "Object"} – {(det.confidence * 100).toFixed(1)}%
147
- </li>
148
- ))}
149
- </ul>
150
- </div>
151
- )}
152
-
153
- {/* Probability / MWT visualization */}
154
- {confidence && (
155
- <div className="mb-6">
156
- <h4 className="font-semibold text-gray-900 mb-3">Confidence Levels</h4>
157
-
158
- {/* If MWT, CIN, or Histopathology classifier, show a visual bar for average confidence and per-class bars */}
159
- {model_used && /mwt|cin|histopathology/i.test(model_used) ? (
160
- <div>
161
- {/* Average confidence bar */}
162
- <div className="mb-3">
163
- <div className="flex items-center justify-between mb-1">
164
- <span className="text-sm font-medium text-gray-700">Average confidence</span>
165
- <span className="text-sm font-mono text-gray-600">
166
- {summary?.avg_confidence ? `${summary.avg_confidence.toFixed(2)}%` : "-"}
167
- </span>
168
- </div>
169
- <div className="w-full bg-gray-200 rounded-full h-4 overflow-hidden">
170
- <div
171
- className="h-4 bg-gradient-to-r from-amber-600 to-amber-400"
172
- style={{ width: `${summary?.avg_confidence ?? 0}%` }}
173
- />
174
  </div>
175
- </div>
176
 
177
- {/* Per-class bars */}
178
- <div className="space-y-2">
179
- {Object.entries(confidence).map(([cls, val]) => {
180
- const num = Number(val as any) || 0;
181
- const pct = (num * 100);
182
-
183
- // Color coding: Positive/Malignant/High-grade = red, Negative/Benign/Low-grade = green
184
- const isNegative = cls.toLowerCase().includes("negative") ||
185
- cls.toLowerCase().includes("benign") ||
186
- cls.toLowerCase().includes("low-grade");
187
-
188
- return (
189
- <div key={cls}>
190
- <div className="flex items-center justify-between text-sm mb-1">
191
- <span className="text-gray-700">{cls}</span>
192
- <span className="text-gray-600">{pct.toFixed(2)}%</span>
193
- </div>
194
- <div className="w-full bg-gray-100 rounded-full h-3">
195
- <div
196
- className={`h-3 rounded-full ${isNegative ? "bg-green-500" : "bg-red-500"}`}
197
- style={{ width: `${pct.toFixed(2)}%` }}
198
- />
 
199
  </div>
200
- </div>
201
- );
202
- })}
203
- </div>
204
 
205
- {/* Mistral comment */}
206
- <div className="mt-4 bg-gray-50 p-3 rounded-lg text-sm italic text-gray-800 border-t">
207
- {summary?.ai_interpretation || "No AI interpretation available."}
 
208
  </div>
209
- </div>
210
- ) : (
211
- // Fallback display for non-MWT models
212
- <pre className="bg-gray-100 rounded-lg p-3 text-sm overflow-x-auto">
213
- {JSON.stringify(confidence, null, 2)}
214
- </pre>
215
- )}
216
- </div>
217
- )}
218
-
219
- {/* Report Generation Modal */}
220
- <ReportModal
221
- isOpen={showReportModal}
222
- onClose={() => setShowReportModal(false)}
223
- onSubmit={handleGenerateReport}
224
- analysisId={annotated_image_url || ""}
225
- analysisSummaryJson={summary ? JSON.stringify({ ...summary, model_used, confidence }) : "{}"}
226
- />
227
- </div>
228
- );
229
  }
 
 
5
  import axios from "axios";
6
 
7
  interface ResultsPanelProps {
8
+ uploadedImage: string | null;
9
+ result?: any;
10
+ loading?: boolean;
11
  }
12
 
13
  export function ResultsPanel({ uploadedImage, result, loading }: ResultsPanelProps) {
 
16
  const handleGenerateReport = async (formData: FormData) => {
17
  try {
18
  const baseURL = import.meta.env.MODE === "development"
 
 
 
19
  ? "http://127.0.0.1:7860"
 
20
  : window.location.origin;
 
21
  const response = await axios.post(`${baseURL}/reports/`, formData, {
22
  headers: { "Content-Type": "multipart/form-data" },
23
  });
 
42
  return (
43
  <div className="bg-white rounded-lg shadow-sm p-6 flex flex-col items-center justify-center">
44
  <Loader2Icon className="w-10 h-10 text-blue-600 animate-spin mb-3" />
45
+ <p className="text-teal-700 font-medium">Analyzing image...</p>
46
  </div>
47
  );
48
  }
 
53
  No analysis result available yet.
54
  </div>
55
  );
56
+ }
57
 
58
+ const {
59
+ model_used,
60
+ detections,
61
+ annotated_image_url,
62
+ summary,
63
+ // prediction (not used here)
64
+ confidence,
65
+ } = result;
66
+
67
+ const handleDownload = () => {
68
+ if (annotated_image_url) {
69
+ const link = document.createElement("a");
70
+ link.href = annotated_image_url;
71
+ link.download = "analysis_result.jpg";
72
+ link.click();
73
+ }
74
+ };
 
 
 
 
 
 
 
 
 
 
75
 
76
+ return ( <div className="bg-white rounded-lg shadow-sm p-6">
77
+ {/* Header */}
78
+ <div className="flex items-center justify-between mb-6">
79
+ <div>
80
+ <h2 className="text-2xl font-bold text-gray-800">
81
+ {model_used || "Analysis Result"}
82
+ </h2>
83
+ <p className="text-sm text-gray-500">Automated Image Analysis</p>
84
+ </div>
85
+
86
+ <div className="flex items-center gap-3">
87
+ {annotated_image_url && (
88
+ <button
89
+ onClick={handleDownload}
90
+ className="flex items-center gap-2 bg-gradient-to-r from-teal-700 via-teal-600 to-teal-700 text-white px-4 py-2 rounded-lg hover:opacity-90 transition-all"
91
+ >
92
+ <DownloadIcon className="w-4 h-4" />
93
+ Download Image
94
+ </button>
95
+ )}
96
  <button
97
+ onClick={() => setShowReportModal(true)}
98
+ className="flex items-center gap-2 bg-gradient-to-r from-teal-700 via-teal-600 to-teal-700 text-white px-4 py-2 rounded-lg hover:opacity-90 transition-all"
99
  >
100
+ <FileTextIcon className="w-4 h-4" />
101
+ Generate Report
102
  </button>
103
+ </div>
 
 
 
 
 
 
 
104
  </div>
 
105
 
106
+ {/* Image */}
107
+ <div className="relative mb-6 rounded-lg overflow-hidden border border-gray-200">
108
+ <ImageWithFallback
109
+ src={annotated_image_url || uploadedImage || "/ui.jpg"}
110
+ alt="Analysis Result"
111
+ className="w-full h-64 object-cover"
112
+ />
113
+ </div>
114
 
115
+ {/* Summary Section */}
116
+ {summary && (
117
+ <div className="bg-gray-50 p-4 rounded-lg mb-6">
118
+ <h3 className="text-lg font-semibold text-gray-800 mb-2">
119
+ AI Summary
120
+ </h3>
121
+ <p className="text-gray-700 text-sm leading-relaxed">
122
+ <strong>Abnormal Cells:</strong> {summary.abnormal_cells} <br />
123
+ <strong>Normal Cells:</strong> {summary.normal_cells} <br />
124
+ <strong>Average Confidence:</strong> {summary.avg_confidence?.toFixed(2)}% <br />
125
+ </p>
126
+ <div className="mt-3 text-gray-800 text-sm italic border-t pt-2">
127
+ {summary.ai_interpretation || "No AI interpretation available."}
128
+ </div>
129
  </div>
130
+ )}
131
+
132
+ {/* Detection list */}
133
+ {detections && detections.length > 0 && (
134
+ <div className="mb-6">
135
+ <h4 className="font-semibold text-gray-900 mb-3">
136
+ Detected Objects
137
+ </h4>
138
+ <ul className="text-sm text-gray-700 list-disc list-inside space-y-1">
139
+ {detections.map((det: any, i: number) => (
140
+ <li key={i}>
141
+ {det.name || "Object"} – {(det.confidence * 100).toFixed(1)}%
142
+ </li>
143
+ ))}
144
+ </ul>
145
+ </div>
146
+ )}
147
+
148
+ {/* Probability / MWT visualization */}
149
+ {confidence && (
150
+ <div className="mb-6">
151
+ <h4 className="font-semibold text-gray-900 mb-3">Confidence Levels</h4>
152
+
153
+ {/* If MWT, CIN, or Histopathology classifier, show a visual bar for average confidence and per-class bars */}
154
+ {model_used && /mwt|cin|histopathology/i.test(model_used) ? (
155
+ <div>
156
+ {/* Average confidence bar */}
157
+ <div className="mb-3">
158
+ <div className="flex items-center justify-between mb-1">
159
+ <span className="text-sm font-medium text-gray-700">Average confidence</span>
160
+ <span className="text-sm font-mono text-gray-600">
161
+ {summary?.avg_confidence ? `${summary.avg_confidence.toFixed(2)}%` : "-"}
162
+ </span>
163
+ </div>
164
+ <div className="w-full bg-gray-200 rounded-full h-4 overflow-hidden">
165
+ <div
166
+ className="h-4 bg-gradient-to-r from-amber-600 to-amber-400"
167
+ style={{ width: `${summary?.avg_confidence ?? 0}%` }}
168
+ />
169
+ </div>
170
  </div>
 
171
 
172
+ {/* Per-class bars */}
173
+ <div className="space-y-2">
174
+ {Object.entries(confidence).map(([cls, val]) => {
175
+ const num = Number(val as any) || 0;
176
+ const pct = (num * 100);
177
+
178
+ // Color coding: Positive/Malignant/High-grade = red, Negative/Benign/Low-grade = green
179
+ const isNegative = cls.toLowerCase().includes("negative") ||
180
+ cls.toLowerCase().includes("benign") ||
181
+ cls.toLowerCase().includes("low-grade");
182
+
183
+ return (
184
+ <div key={cls}>
185
+ <div className="flex items-center justify-between text-sm mb-1">
186
+ <span className="text-gray-700">{cls}</span>
187
+ <span className="text-gray-600">{pct.toFixed(2)}%</span>
188
+ </div>
189
+ <div className="w-full bg-gray-100 rounded-full h-3">
190
+ <div
191
+ className={`h-3 rounded-full ${isNegative ? "bg-green-500" : "bg-red-500"}`}
192
+ style={{ width: `${pct.toFixed(2)}%` }}
193
+ />
194
+ </div>
195
  </div>
196
+ );
197
+ })}
198
+ </div>
 
199
 
200
+ {/* Mistral comment */}
201
+ <div className="mt-4 bg-gray-50 p-3 rounded-lg text-sm italic text-gray-800 border-t">
202
+ {summary?.ai_interpretation || "No AI interpretation available."}
203
+ </div>
204
  </div>
205
+ ) : (
206
+ // Fallback display for non-MWT models
207
+ <pre className="bg-gray-100 rounded-lg p-3 text-sm overflow-x-auto">
208
+ {JSON.stringify(confidence, null, 2)}
209
+ </pre>
210
+ )}
211
+ </div>
212
+ )}
213
+
214
+ {/* Report Generation Modal */}
215
+ <ReportModal
216
+ isOpen={showReportModal}
217
+ onClose={() => setShowReportModal(false)}
218
+ onSubmit={handleGenerateReport}
219
+ analysisId={annotated_image_url || ""}
220
+ analysisSummaryJson={summary ? JSON.stringify({ ...summary, model_used, confidence }) : "{}"}
221
+ />
222
+ </div>
223
+ );
 
224
  }
225
+