malavikapradeep2001 commited on
Commit
5358512
·
unverified ·
1 Parent(s): bdcddb5

Upload 2 files

Browse files
frontend/src/components/ImageWithFallback.tsx ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useRef, useState } from "react";
2
+
3
+ interface ImageWithFallbackProps {
4
+ src: string;
5
+ alt: string;
6
+ className?: string;
7
+ }
8
+
9
+ export function ImageWithFallback({ src, alt, className }: ImageWithFallbackProps) {
10
+ const imgRef = useRef<HTMLImageElement>(null);
11
+ const [imgSrc, setImgSrc] = useState(src);
12
+ const [error, setError] = useState(false);
13
+
14
+ useEffect(() => {
15
+ setImgSrc(src);
16
+ setError(false);
17
+ }, [src]);
18
+
19
+ const handleError = () => {
20
+ if (!error && imgSrc.startsWith('/')) {
21
+ setError(true);
22
+ // Try with explicit origin if relative URL
23
+ const baseURL = import.meta.env.MODE === "development"
24
+ ? "http://127.0.0.1:8000"
25
+ : window.location.origin;
26
+ setImgSrc(`${baseURL}${imgSrc}`);
27
+ }
28
+ };
29
+
30
+ return (
31
+ <img
32
+ ref={imgRef}
33
+ src={imgSrc}
34
+ alt={alt}
35
+ className={className}
36
+ onError={handleError}
37
+ />
38
+ );
39
+ }
frontend/src/components/ReportModal.tsx ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import { XIcon } from 'lucide-react';
3
+
4
+ interface ReportModalProps {
5
+ isOpen: boolean;
6
+ onClose: () => void;
7
+ onSubmit: (formData: FormData) => void; // ✅ changed
8
+ analysisId: string;
9
+ analysisSummaryJson: string;
10
+ }
11
+
12
+
13
+ export interface ReportFormData {
14
+ patient_id: string;
15
+ exam_date: string;
16
+ metadata: {
17
+ physician: string;
18
+ facility: string;
19
+ specimen_type: string;
20
+ clinical_history?: string;
21
+ };
22
+ notes?: string;
23
+ analysis_id: string;
24
+ }
25
+
26
+
27
+ export function ReportModal({ isOpen, onClose, onSubmit, analysisId, analysisSummaryJson }: ReportModalProps) {
28
+ const [formData, setFormData] = useState<ReportFormData>({
29
+ patient_id: '',
30
+ exam_date: new Date().toISOString().split('T')[0],
31
+ metadata: {
32
+ physician: '',
33
+ facility: '',
34
+ specimen_type: '',
35
+ clinical_history: '',
36
+ },
37
+ notes: '',
38
+ analysis_id: analysisId,
39
+ });
40
+
41
+ if (!isOpen) return null;
42
+
43
+ const handleSubmit = (e: React.FormEvent) => {
44
+ e.preventDefault();
45
+
46
+ // Build FormData for FastAPI endpoint
47
+ const payload = new FormData();
48
+ payload.append("patient_id", formData.patient_id);
49
+ payload.append("exam_date", formData.exam_date);
50
+ payload.append("metadata", JSON.stringify(formData.metadata));
51
+ payload.append("notes", formData.notes || "");
52
+ payload.append("analysis_id", formData.analysis_id);
53
+ payload.append("analysis_summary", analysisSummaryJson); // 🧠 from /predict/
54
+
55
+ // Pass the FormData object to the parent onSubmit
56
+ onSubmit(payload);
57
+ };
58
+
59
+
60
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
61
+ const { name, value } = e.target;
62
+ if (name.startsWith('metadata.')) {
63
+ const field = name.split('.')[1];
64
+ setFormData(prev => ({
65
+ ...prev,
66
+ metadata: {
67
+ ...prev.metadata,
68
+ [field]: value,
69
+ },
70
+ }));
71
+ } else {
72
+ setFormData(prev => ({
73
+ ...prev,
74
+ [name]: value,
75
+ }));
76
+ }
77
+ };
78
+
79
+ return (
80
+ <div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
81
+ <div className="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
82
+ <div className="p-6 space-y-6">
83
+ <div className="flex items-center justify-between border-b pb-3">
84
+ <h2 className="text-2xl font-semibold text-gray-900">Generate Medical Report</h2>
85
+ <button
86
+ onClick={onClose}
87
+ className="text-gray-400 hover:text-gray-500"
88
+ >
89
+ <XIcon className="w-6 h-6" />
90
+ </button>
91
+ </div>
92
+
93
+ <form onSubmit={handleSubmit} className="space-y-6">
94
+ {/* Patient Information */}
95
+ <div className="space-y-4">
96
+ <h3 className="text-lg font-medium text-gray-900">Patient Information</h3>
97
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
98
+ <div>
99
+ <label htmlFor="patient_id" className="block text-sm font-medium text-gray-700">
100
+ Patient ID *
101
+ </label>
102
+ <input
103
+ required
104
+ type="text"
105
+ name="patient_id"
106
+ id="patient_id"
107
+ value={formData.patient_id}
108
+ onChange={handleChange}
109
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
110
+ />
111
+ </div>
112
+ <div>
113
+ <label htmlFor="exam_date" className="block text-sm font-medium text-gray-700">
114
+ Exam Date *
115
+ </label>
116
+ <input
117
+ required
118
+ type="date"
119
+ name="exam_date"
120
+ id="exam_date"
121
+ value={formData.exam_date}
122
+ onChange={handleChange}
123
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
124
+ />
125
+ </div>
126
+ </div>
127
+ </div>
128
+
129
+ {/* Slide Metadata */}
130
+ <div className="space-y-4">
131
+ <h3 className="text-lg font-medium text-gray-900">Slide Metadata</h3>
132
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
133
+ <div>
134
+ <label htmlFor="metadata.physician" className="block text-sm font-medium text-gray-700">
135
+ Physician Name *
136
+ </label>
137
+ <input
138
+ required
139
+ type="text"
140
+ name="metadata.physician"
141
+ id="metadata.physician"
142
+ value={formData.metadata.physician}
143
+ onChange={handleChange}
144
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
145
+ />
146
+ </div>
147
+ <div>
148
+ <label htmlFor="metadata.facility" className="block text-sm font-medium text-gray-700">
149
+ Facility *
150
+ </label>
151
+ <input
152
+ required
153
+ type="text"
154
+ name="metadata.facility"
155
+ id="metadata.facility"
156
+ value={formData.metadata.facility}
157
+ onChange={handleChange}
158
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
159
+ />
160
+ </div>
161
+ <div>
162
+ <label htmlFor="metadata.specimen_type" className="block text-sm font-medium text-gray-700">
163
+ Specimen Type *
164
+ </label>
165
+ <input
166
+ required
167
+ type="text"
168
+ name="metadata.specimen_type"
169
+ id="metadata.specimen_type"
170
+ value={formData.metadata.specimen_type}
171
+ onChange={handleChange}
172
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
173
+ />
174
+ </div>
175
+ <div>
176
+ <label htmlFor="metadata.clinical_history" className="block text-sm font-medium text-gray-700">
177
+ Clinical History
178
+ </label>
179
+ <input
180
+ type="text"
181
+ name="metadata.clinical_history"
182
+ id="metadata.clinical_history"
183
+ value={formData.metadata.clinical_history}
184
+ onChange={handleChange}
185
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
186
+ />
187
+ </div>
188
+ </div>
189
+ </div>
190
+
191
+ {/* Notes */}
192
+ <div className="space-y-4">
193
+ <h3 className="text-lg font-medium text-gray-900">Doctor's Notes</h3>
194
+ <textarea
195
+ name="notes"
196
+ id="notes"
197
+ rows={4}
198
+ value={formData.notes}
199
+ onChange={handleChange}
200
+ placeholder="Add any additional notes or observations..."
201
+ className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
202
+ />
203
+ </div>
204
+
205
+ {/* Actions */}
206
+ <div className="flex items-center justify-end space-x-3 pt-4 border-t">
207
+ <button
208
+ type="button"
209
+ onClick={onClose}
210
+ className="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-500"
211
+ >
212
+ Cancel
213
+ </button>
214
+ <button
215
+ type="submit"
216
+ className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
217
+ >
218
+ Generate Report
219
+ </button>
220
+ </div>
221
+ </form>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ );
226
+ }