source: frontend/src/pages/procedures/ProcedureList.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 14.2 KB
Line 
1import React, { useState, useEffect } from 'react';
2import { patientService } from '../../services/patientService';
3import { procedureService } from '../../services/procedureService';
4import { medicalRecordService } from '../../services/medicalRecordService';
5import ErrorAlert from '../../components/ErrorAlert';
6
7function ProcedureList() {
8 const [user, setUser] = useState({});
9 const [patient, setPatient] = useState(null);
10 const [embg, setEmbg] = useState('');
11 const [searched, setSearched] = useState(false);
12 const [loading, setLoading] = useState(false);
13 const [error, setError] = useState(null);
14
15 const [availableProcedures, setAvailableProcedures] = useState([]);
16 const [performedProcedures, setPerformedProcedures] = useState([]);
17 const [procedureResults, setProcedureResults] = useState([]);
18
19 const [showRequestForm, setShowRequestForm] = useState(false);
20 const [requestData, setRequestData] = useState({
21 procedureId: '',
22 procedureDate: new Date().toISOString().split('T')[0],
23 notes: '',
24 });
25
26 // Load user from localStorage and listen for changes
27 useEffect(() => {
28 const loadUser = () => {
29 const userStr = localStorage.getItem('user');
30 console.log('[ProcedureList] Loading user from localStorage:', userStr);
31 if (userStr) {
32 try {
33 const parsedUser = JSON.parse(userStr);
34 console.log('[ProcedureList] Parsed user:', parsedUser);
35 setUser(parsedUser);
36 } catch (err) {
37 console.error('Failed to parse user:', err);
38 setUser({});
39 }
40 } else {
41 console.log('[ProcedureList] No user in localStorage');
42 setUser({});
43 }
44 };
45
46 console.log('[ProcedureList] useEffect running - loading user');
47 loadUser();
48
49 // Listen for storage changes (other tabs/windows)
50 window.addEventListener('storage', loadUser);
51
52 // Listen for custom event (same tab changes)
53 window.addEventListener('userStorageChange', loadUser);
54
55 return () => {
56 window.removeEventListener('storage', loadUser);
57 window.removeEventListener('userStorageChange', loadUser);
58 };
59 }, []);
60
61 const handleSearch = async (e) => {
62 e.preventDefault();
63 setError(null);
64 setLoading(true);
65
66 try {
67 if (!embg.trim()) {
68 setError('Please enter an EMBG');
69 setLoading(false);
70 return;
71 }
72
73 const patientResponse = await patientService.getPatientByEmbg(embg);
74 setPatient(patientResponse.data);
75 setSearched(true);
76
77 // Fetch medical record for patient
78 const medicalRecordRes = await medicalRecordService.getMedicalRecordByPatientId(patientResponse.data.patientId);
79
80 // Fetch available procedures from database
81 try {
82 const proceduresResponse = await procedureService.getAllProcedures();
83 console.log('Procedures response:', proceduresResponse);
84 console.log('Procedures data:', proceduresResponse.data);
85 setAvailableProcedures(Array.isArray(proceduresResponse.data) ? proceduresResponse.data : []);
86 } catch (procErr) {
87 console.error('Failed to fetch procedures:', procErr);
88 setError('Failed to fetch procedures: ' + procErr.message);
89 setAvailableProcedures([]);
90 }
91
92 // Fetch performed procedures for patient
93 try {
94 const performedRes = await procedureService.getPerformedProceduresForPatient(patientResponse.data.patientId);
95 setPerformedProcedures(Array.isArray(performedRes.data) ? performedRes.data : []);
96 } catch (err) {
97 console.error('Failed to fetch performed procedures:', err);
98 setPerformedProcedures([]);
99 }
100
101 // Fetch procedure results for medical record
102 try {
103 const resultsResponse = await procedureService.getProcedureResultsForMedicalRecord(medicalRecordRes.data.recordId);
104 setProcedureResults(Array.isArray(resultsResponse.data) ? resultsResponse.data : []);
105 } catch (err) {
106 console.error('Failed to fetch procedure results:', err);
107 setProcedureResults([]);
108 }
109 } catch (err) {
110 console.error('Search error:', err);
111 setError(`Error: ${err.response?.data?.error || err.message || 'Patient not found'}`);
112 setPatient(null);
113 } finally {
114 setLoading(false);
115 }
116 };
117
118 const handleRequestProcedure = async (e) => {
119 e.preventDefault();
120 setError(null);
121
122 if (!requestData.procedureId) {
123 setError('Please select a procedure');
124 return;
125 }
126
127 if (!patient) {
128 setError('Patient not found');
129 return;
130 }
131
132 try {
133 setLoading(true);
134
135 // Get doctor ID from logged-in user
136 const doctorId = user.doctorId;
137
138 const request = {
139 procedureId: parseInt(requestData.procedureId),
140 doctorId: parseInt(doctorId),
141 patientId: patient.patientId,
142 procedureDate: requestData.procedureDate,
143 notes: requestData.notes,
144 };
145
146 console.log('User object:', user);
147 console.log('Sending procedure request with doctorId:', doctorId, 'Full request:', request);
148 await procedureService.recordProcedure(request);
149
150 // Refresh the performed procedures
151 const performedRes = await procedureService.getPerformedProceduresForPatient(patient.patientId);
152 setPerformedProcedures(performedRes.data);
153
154 // Reset form
155 setRequestData({
156 procedureId: '',
157 procedureDate: new Date().toISOString().split('T')[0],
158 notes: '',
159 });
160 setShowRequestForm(false);
161 setError(null);
162 } catch (err) {
163 console.error('Error details:', err);
164 console.error('Response:', err.response);
165 const errorMsg = err.response?.data?.error || err.message || 'Unknown error';
166 setError('Failed to request procedure: ' + errorMsg);
167 } finally {
168 setLoading(false);
169 }
170 };
171
172 return (
173 <div>
174 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Procedures</h1>
175
176 {error && <ErrorAlert message={error} onClose={() => setError(null)} />}
177
178 {/* Search Form */}
179 <div className="bg-white rounded-lg shadow p-6 mb-6">
180 <h2 className="text-xl font-bold mb-4">Search Patient</h2>
181 <form onSubmit={handleSearch} className="space-y-4">
182 <div className="flex gap-4">
183 <div className="flex-1">
184 <label className="block text-sm font-semibold mb-2">Patient EMBG</label>
185 <input
186 type="text"
187 value={embg}
188 onChange={(e) => setEmbg(e.target.value)}
189 placeholder="e.g., 1402994123456"
190 className="w-full px-4 py-2 border rounded-lg"
191 />
192 </div>
193 <div className="flex items-end">
194 <button
195 type="submit"
196 disabled={loading}
197 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400"
198 >
199 {loading ? 'Searching...' : 'Search'}
200 </button>
201 </div>
202 </div>
203 </form>
204 </div>
205
206 {/* Patient Procedures */}
207 {searched && patient && (
208 <div className="space-y-6">
209 {/* Patient Info */}
210 <div className="bg-white rounded-lg shadow p-6">
211 <div className="flex justify-between items-start mb-4">
212 <h2 className="text-2xl font-bold">{patient.firstName} {patient.lastName}</h2>
213 {!showRequestForm && (
214 <button
215 onClick={() => setShowRequestForm(true)}
216 className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700"
217 >
218 Request Procedure
219 </button>
220 )}
221 </div>
222 <div className="grid grid-cols-4 gap-4">
223 <div>
224 <p className="text-sm text-gray-600">EMBG</p>
225 <p className="font-semibold">{patient.embg}</p>
226 </div>
227 <div>
228 <p className="text-sm text-gray-600">Blood Type</p>
229 <p className="font-semibold">{patient.bloodType || 'N/A'}</p>
230 </div>
231 <div>
232 <p className="text-sm text-gray-600">Date of Birth</p>
233 <p className="font-semibold">{patient.dateOfBirth}</p>
234 </div>
235 </div>
236 </div>
237
238 {/* Request Procedure Form */}
239 {showRequestForm && (
240 <div className="bg-white rounded-lg shadow p-6">
241 <h3 className="text-xl font-bold mb-4">Request Procedure</h3>
242 <form onSubmit={handleRequestProcedure} className="space-y-4">
243 <div className="grid grid-cols-2 gap-4">
244 <div>
245 <label className="block text-sm font-semibold mb-2">Procedure</label>
246 <select
247 value={requestData.procedureId}
248 onChange={(e) => setRequestData({ ...requestData, procedureId: e.target.value })}
249 className="w-full px-4 py-2 border rounded-lg"
250 required
251 >
252 <option value="">Select a procedure</option>
253 {availableProcedures.map((proc) => (
254 <option key={proc.procedureId} value={proc.procedureId}>
255 {proc.procedureType}
256 </option>
257 ))}
258 </select>
259 </div>
260 <div>
261 <label className="block text-sm font-semibold mb-2">Procedure Date</label>
262 <input
263 type="date"
264 value={requestData.procedureDate}
265 onChange={(e) => setRequestData({ ...requestData, procedureDate: e.target.value })}
266 className="w-full px-4 py-2 border rounded-lg"
267 />
268 </div>
269 </div>
270 <div>
271 <label className="block text-sm font-semibold mb-2">Notes</label>
272 <textarea
273 value={requestData.notes}
274 onChange={(e) => setRequestData({ ...requestData, notes: e.target.value })}
275 placeholder="Additional notes for the procedure"
276 className="w-full px-4 py-2 border rounded-lg"
277 rows="3"
278 />
279 </div>
280 <div className="flex gap-4">
281 <button
282 type="submit"
283 disabled={loading}
284 className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700 disabled:bg-gray-400"
285 >
286 {loading ? 'Requesting...' : 'Request Procedure'}
287 </button>
288 <button
289 type="button"
290 onClick={() => setShowRequestForm(false)}
291 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500"
292 >
293 Cancel
294 </button>
295 </div>
296 </form>
297 </div>
298 )}
299
300 {/* Performed Procedures */}
301 {performedProcedures && performedProcedures.length > 0 && (
302 <div className="bg-white rounded-lg shadow p-6">
303 <h3 className="text-xl font-bold mb-4">Performed Procedures</h3>
304 <div className="space-y-3">
305 {performedProcedures.map((proc) => (
306 <div key={proc.performedId} className="border-l-4 border-purple-500 pl-4 py-2">
307 <p className="font-semibold text-lg">{proc.procedure?.procedureType || 'Procedure'}</p>
308 <p className="text-sm text-gray-600">Requested by: {proc.doctor?.firstName} {proc.doctor?.lastName}</p>
309 <p className="text-sm text-gray-600">Procedure Date: {proc.procedureDate}</p>
310 {proc.notes && (
311 <p className="text-sm text-gray-600">Notes: {proc.notes}</p>
312 )}
313 </div>
314 ))}
315 </div>
316 </div>
317 )}
318
319 {/* Procedure Results */}
320 {procedureResults && procedureResults.length > 0 && (
321 <div className="bg-white rounded-lg shadow p-6">
322 <h3 className="text-xl font-bold mb-4">Procedure Results</h3>
323 <div className="space-y-4">
324 {procedureResults.map((result) => (
325 <div key={result.resultId} className="border-l-4 border-orange-500 pl-4 py-3 bg-orange-50 rounded">
326 <p className="font-semibold text-lg text-orange-700">{result.procedure?.procedureType || 'Procedure'}</p>
327 <p className="text-sm text-gray-700 mt-2"><strong>Outcome:</strong> {result.resultDescription}</p>
328 <p className="text-sm text-gray-600">Result Date: {result.resultDate}</p>
329 </div>
330 ))}
331 </div>
332 </div>
333 )}
334
335 {/* Link to submit results */}
336 {performedProcedures && performedProcedures.length > 0 && (
337 <div className="bg-yellow-50 rounded-lg p-6">
338 <h3 className="text-lg font-semibold text-yellow-800 mb-3">Submit Procedure Results</h3>
339 <p className="text-sm text-gray-700 mb-4">
340 {performedProcedures.length} procedure{performedProcedures.length !== 1 ? 's' : ''} awaiting results
341 </p>
342 <a
343 href="/procedures/results"
344 className="inline-block bg-yellow-600 text-white px-6 py-2 rounded hover:bg-yellow-700"
345 >
346 Submit Procedure Results
347 </a>
348 </div>
349 )}
350
351 {/* No procedures message */}
352 {(!performedProcedures || performedProcedures.length === 0) && !procedureResults?.length && (
353 <div className="bg-blue-50 rounded-lg p-6 text-center">
354 <p className="text-gray-600">No procedures for this patient</p>
355 </div>
356 )}
357 </div>
358 )}
359
360 {/* No search performed */}
361 {!searched && (
362 <div className="bg-gray-50 rounded-lg p-12 text-center">
363 <p className="text-gray-600 text-lg">Enter a patient EMBG to request procedures</p>
364 </div>
365 )}
366 </div>
367 );
368}
369
370export default ProcedureList;
Note: See TracBrowser for help on using the repository browser.