source: frontend/src/pages/medical-reports/MedicalReportList.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 13.6 KB
Line 
1import React, { useState, useEffect } from 'react';
2import { medicalReportService } from '../../services/medicalReportService';
3import { patientService } from '../../services/patientService';
4import { doctorService } from '../../services/doctorService';
5import { medicalRecordService } from '../../services/medicalRecordService';
6import ErrorAlert from '../../components/ErrorAlert';
7import SuccessAlert from '../../components/SuccessAlert';
8
9function MedicalReportList() {
10 const [reports, setReports] = useState([]);
11 const [doctors, setDoctors] = useState([]);
12 const [selectedReport, setSelectedReport] = useState(null);
13 const [error, setError] = useState(null);
14 const [success, setSuccess] = useState(null);
15 const [searchEmbg, setSearchEmbg] = useState('');
16 const [searchedPatient, setSearchedPatient] = useState(null);
17 const [showCreateForm, setShowCreateForm] = useState(false);
18 const [loading, setLoading] = useState(false);
19
20 const [selectedDiagnosisIds, setSelectedDiagnosisIds] = useState(new Set());
21 const [selectedPrescriptionIds, setSelectedPrescriptionIds] = useState(new Set());
22 const [selectedAllergyIds, setSelectedAllergyIds] = useState(new Set());
23 const [selectedSymptomIds, setSelectedSymptomIds] = useState(new Set());
24
25 const user = JSON.parse(localStorage.getItem('user') || '{}');
26 const isDoctor = user.role === 'DOCTOR';
27
28 const [formData, setFormData] = useState({
29 doctorId: isDoctor ? user.doctorId : '',
30 description: '',
31 reportDate: new Date().toISOString().split('T')[0],
32 });
33
34 useEffect(() => {
35 if (!isDoctor) {
36 loadDoctors();
37 }
38 }, [isDoctor]);
39
40 const loadDoctors = async () => {
41 try {
42 const res = await doctorService.getAllDoctors();
43 setDoctors(res.data);
44 } catch (err) {
45 console.error('Error loading doctors:', err);
46 }
47 };
48
49 const handleSearch = async (e) => {
50 e.preventDefault();
51 setError(null);
52 setLoading(true);
53
54 try {
55 if (!searchEmbg.trim()) {
56 setError('Please enter a patient EMBG');
57 setLoading(false);
58 return;
59 }
60
61 const patientRes = await patientService.getPatientByEmbg(searchEmbg);
62 setSearchedPatient(patientRes.data);
63
64 const medicalRecordRes = await medicalRecordService.getMedicalRecordByPatientId(patientRes.data.patientId);
65 const medicalRecordId = medicalRecordRes.data.recordId;
66
67 const reportsRes = await medicalReportService.getReportsForMedicalRecord(medicalRecordId);
68 setReports(Array.isArray(reportsRes.data) ? reportsRes.data : [reportsRes.data]);
69
70 setSelectedReport(null);
71 setSelectedDiagnosisIds(new Set());
72 setSelectedPrescriptionIds(new Set());
73 setSelectedAllergyIds(new Set());
74 setSelectedSymptomIds(new Set());
75 } catch (err) {
76 setError('Patient not found or no reports available');
77 setReports([]);
78 setSearchedPatient(null);
79 } finally {
80 setLoading(false);
81 }
82 };
83
84 const handleCreateReport = async (e) => {
85 e.preventDefault();
86 setError(null);
87
88 try {
89 if (!formData.doctorId || !formData.description || !formData.reportDate) {
90 setError('Please fill in all required fields');
91 return;
92 }
93
94 if (!searchedPatient) {
95 setError('Please search for a patient first');
96 return;
97 }
98
99 const medicalRecordRes = await medicalRecordService.getMedicalRecordByPatientId(searchedPatient.patientId);
100
101 const reportData = {
102 doctorId: parseInt(formData.doctorId),
103 medicalRecordId: medicalRecordRes.data.recordId,
104 description: formData.description,
105 reportDate: formData.reportDate,
106 selectedDiagnosisIds: Array.from(selectedDiagnosisIds),
107 selectedPrescriptionIds: Array.from(selectedPrescriptionIds),
108 selectedAllergyIds: Array.from(selectedAllergyIds),
109 selectedSymptomIds: Array.from(selectedSymptomIds),
110 };
111
112 const newReport = await medicalReportService.createReport(reportData);
113 setSuccess('Medical report created successfully!');
114 setReports([...reports, newReport.data]);
115 setFormData({
116 doctorId: '',
117 description: '',
118 reportDate: new Date().toISOString().split('T')[0],
119 });
120 setShowCreateForm(false);
121 setSelectedReport(newReport.data);
122 } catch (err) {
123 setError(err.response?.data?.error || 'Failed to create medical report');
124 }
125 };
126
127 const handleFormChange = (e) => {
128 const { name, value } = e.target;
129 setFormData(prev => ({
130 ...prev,
131 [name]: value
132 }));
133 };
134
135 return (
136 <div>
137 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Medical Reports</h1>
138
139 {error && <ErrorAlert message={error} onClose={() => setError(null)} />}
140 {success && <SuccessAlert message={success} onClose={() => setSuccess(null)} />}
141
142 {/* Search Patient Form */}
143 <div className="bg-white rounded-lg shadow p-6 mb-6">
144 <h2 className="text-xl font-bold mb-4">Search Patient by EMBG</h2>
145 <form onSubmit={handleSearch} className="flex gap-4">
146 <input
147 type="text"
148 value={searchEmbg}
149 onChange={(e) => setSearchEmbg(e.target.value)}
150 placeholder="Enter patient EMBG"
151 className="flex-1 px-4 py-2 border rounded-lg"
152 />
153 <button
154 type="submit"
155 disabled={loading}
156 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400"
157 >
158 {loading ? 'Searching...' : 'Search'}
159 </button>
160 </form>
161 </div>
162
163 {/* Create Report Button */}
164 {searchedPatient && (
165 <div className="mb-6">
166 <button
167 onClick={() => setShowCreateForm(!showCreateForm)}
168 className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700"
169 >
170 {showCreateForm ? 'Cancel' : 'Create New Report'}
171 </button>
172 </div>
173 )}
174
175 {/* Create Report Form */}
176 {showCreateForm && searchedPatient && (
177 <div className="bg-white rounded-lg shadow p-6 mb-6">
178 <h2 className="text-xl font-bold mb-4">Create Medical Report</h2>
179 <p className="mb-4 text-gray-600">
180 Patient: {searchedPatient.firstName} {searchedPatient.lastName} ({searchedPatient.embg})
181 </p>
182 <form onSubmit={handleCreateReport} className="space-y-4">
183 {isDoctor ? (
184 <div>
185 <label className="block text-sm font-semibold mb-2">Doctor</label>
186 <p className="px-4 py-2 bg-gray-100 rounded-lg text-gray-700">
187 Dr. {user.firstName} {user.lastName}
188 </p>
189 </div>
190 ) : (
191 <div>
192 <label className="block text-sm font-semibold mb-2">Doctor *</label>
193 <select
194 name="doctorId"
195 value={formData.doctorId}
196 onChange={handleFormChange}
197 className="w-full px-4 py-2 border rounded-lg"
198 required
199 >
200 <option value="">Select doctor</option>
201 {doctors.map(doctor => (
202 <option key={doctor.doctorId} value={doctor.doctorId}>
203 Dr. {doctor.firstName} {doctor.lastName}
204 </option>
205 ))}
206 </select>
207 </div>
208 )}
209
210 <div>
211 <label className="block text-sm font-semibold mb-2">Report Date *</label>
212 <input
213 type="date"
214 name="reportDate"
215 value={formData.reportDate}
216 onChange={handleFormChange}
217 className="w-full px-4 py-2 border rounded-lg"
218 required
219 />
220 </div>
221
222 <div>
223 <label className="block text-sm font-semibold mb-2">Report Description *</label>
224 <textarea
225 name="description"
226 value={formData.description}
227 onChange={handleFormChange}
228 placeholder="Describe the patient's visit, findings, and recommendations..."
229 className="w-full px-4 py-2 border rounded-lg"
230 rows="6"
231 required
232 />
233 </div>
234
235 <button
236 type="submit"
237 className="bg-purple-600 text-white px-6 py-2 rounded hover:bg-purple-700"
238 >
239 Create Report
240 </button>
241 </form>
242 </div>
243 )}
244
245 {/* Reports List */}
246 {reports.length > 0 && (
247 <div className="space-y-4">
248 <h2 className="text-2xl font-bold">Reports for {searchedPatient?.firstName} {searchedPatient?.lastName}</h2>
249 {reports.map(report => (
250 <div
251 key={report.reportId}
252 className="bg-white rounded-lg shadow p-6 cursor-pointer hover:shadow-lg transition"
253 onClick={() => setSelectedReport(selectedReport?.reportId === report.reportId ? null : report)}
254 >
255 <div className="flex justify-between items-start">
256 <div>
257 <p className="text-sm text-gray-600">Date: {report.reportDate}</p>
258 <p className="text-sm text-gray-600">Doctor: {report.doctorName}</p>
259 <p className="mt-2 line-clamp-2">{report.reportDescription}</p>
260 </div>
261 <button
262 className="text-purple-600 hover:text-purple-800 text-sm font-normal"
263 >
264 {selectedReport?.reportId === report.reportId ? 'Hide' : 'View'} Details
265 </button>
266 </div>
267
268 {selectedReport?.reportId === report.reportId && (
269 <div className="mt-6 pt-6 border-t space-y-6">
270 {/* Report Description */}
271 <div>
272 <h4 className="font-bold mb-2">Report Description</h4>
273 <p className="text-gray-700 whitespace-pre-wrap">{report.reportDescription}</p>
274 </div>
275
276 {/* Diagnoses */}
277 {report.diagnoses && report.diagnoses.length > 0 && (
278 <div>
279 <h4 className="font-bold mb-2">Diagnoses</h4>
280 <ul className="space-y-2">
281 {report.diagnoses.map((diagnosis, idx) => (
282 <li key={idx} className="bg-blue-50 p-3 rounded">
283 <p className="font-semibold">{diagnosis.name}</p>
284 <p className="text-sm text-gray-600">{diagnosis.description}</p>
285 <p className="text-xs text-gray-500">By: {diagnosis.doctorName}</p>
286 </li>
287 ))}
288 </ul>
289 </div>
290 )}
291
292 {/* Prescriptions */}
293 {report.prescriptions && report.prescriptions.length > 0 && (
294 <div>
295 <h4 className="font-bold mb-2">Prescriptions</h4>
296 <ul className="space-y-2">
297 {report.prescriptions.map((prescription, idx) => (
298 <li key={idx} className="bg-green-50 p-3 rounded">
299 <p className="font-semibold">{prescription.medicationName}</p>
300 <p className="text-sm text-gray-600">
301 {prescription.dosage} - {prescription.frequency} for {prescription.duration}
302 </p>
303 {prescription.notes && (
304 <p className="text-sm text-gray-600">Notes: {prescription.notes}</p>
305 )}
306 </li>
307 ))}
308 </ul>
309 </div>
310 )}
311
312 {/* Allergies */}
313 {report.allergies && report.allergies.length > 0 && (
314 <div>
315 <h4 className="font-bold mb-2">Allergies</h4>
316 <ul className="space-y-2">
317 {report.allergies.map((allergy, idx) => (
318 <li key={idx} className="bg-red-50 p-3 rounded">
319 <p className="font-semibold">{allergy.allergyName}</p>
320 <p className="text-sm text-gray-600">Reaction: {allergy.reaction}</p>
321 <p className="text-xs text-red-600">Severity: {allergy.severity}</p>
322 </li>
323 ))}
324 </ul>
325 </div>
326 )}
327
328 {/* Symptoms */}
329 {report.symptoms && report.symptoms.length > 0 && (
330 <div>
331 <h4 className="font-bold mb-2">Symptoms</h4>
332 <ul className="space-y-2">
333 {report.symptoms.map((symptom, idx) => (
334 <li key={idx} className="bg-yellow-50 p-3 rounded">
335 <p className="font-semibold">{symptom.symptomName}</p>
336 <p className="text-sm text-gray-600">{symptom.description}</p>
337 </li>
338 ))}
339 </ul>
340 </div>
341 )}
342 </div>
343 )}
344 </div>
345 ))}
346 </div>
347 )}
348
349 {/* No Reports Message */}
350 {searchedPatient && reports.length === 0 && (
351 <div className="bg-blue-50 rounded-lg p-6 text-center">
352 <p className="text-gray-600">No medical reports found for this patient</p>
353 </div>
354 )}
355 </div>
356 );
357}
358
359export default MedicalReportList;
Note: See TracBrowser for help on using the repository browser.