Changeset f18b11b for frontend/src
- Timestamp:
- 05/25/26 17:02:59 (4 months ago)
- Branches:
- master
- Children:
- 6c00695
- Parents:
- a64c772
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/pages/medical-records/MedicalRecordList.js
ra64c772 rf18b11b 1 1 import React, { useState } from 'react'; 2 import { useNavigate } from 'react-router-dom';2 import { useNavigate, useSearchParams } from 'react-router-dom'; 3 3 import { patientService } from '../../services/patientService'; 4 4 import { medicalRecordService } from '../../services/medicalRecordService'; … … 9 9 function MedicalRecordList() { 10 10 const navigate = useNavigate(); 11 const [searchParams] = useSearchParams(); 12 const patientId = searchParams.get('patientId'); 11 13 const user = JSON.parse(localStorage.getItem('user') || '{}'); 12 14 const isPatient = user.role === 'PATIENT'; … … 21 23 const [loading, setLoading] = useState(false); 22 24 23 // Auto-load patient's own records if logged in as patient 25 24 26 React.useEffect(() => { 25 if (isPatient && user.username) { 27 if (patientId) { 28 29 loadPatientRecords(patientId); 30 } else if (isPatient && user.username) { 26 31 handleAutoSearch(); 27 32 } 28 }, []); 29 30 const handleAutoSearch = async () => { 31 const searchEmbg = user.username; 33 }, [patientId]); 34 35 const loadPatientRecords = async (pId) => { 32 36 setError(null); 33 37 setLoading(true); 34 38 35 39 try { 36 // Search patient by EMBG37 const patientResponse = await patientService.getPatientBy Embg(searchEmbg);40 // Get patient by id 41 const patientResponse = await patientService.getPatientById(pId); 38 42 setPatient(patientResponse.data); 39 43 40 44 // Get medical records for this patient 41 const recordsResponse = await medicalRecordService.getMedicalRecordByPatientId(p atientResponse.data.patientId);45 const recordsResponse = await medicalRecordService.getMedicalRecordByPatientId(pId); 42 46 setMedicalData(recordsResponse.data); 43 47 … … 60 64 setSearched(true); 61 65 } catch (err) { 62 setError(` Patient with EMBG ${searchEmbg} not found`);66 setError(`Failed to load medical records for patient`); 63 67 setSearched(true); 64 68 setPatient(null); … … 71 75 }; 72 76 73 const handle Search = async (e) => {74 e.preventDefault();77 const handleAutoSearch = async () => { 78 const searchEmbg = user.username; 75 79 setError(null); 76 80 setLoading(true); 77 81 78 82 try { 79 if (!embg.trim()) {80 setError('Please enter an EMBG');81 setLoading(false);82 return;83 }84 85 83 // Search patient by EMBG 86 const patientResponse = await patientService.getPatientByEmbg( embg);84 const patientResponse = await patientService.getPatientByEmbg(searchEmbg); 87 85 setPatient(patientResponse.data); 88 86 … … 109 107 setSearched(true); 110 108 } catch (err) { 111 setError(`Patient with EMBG ${ embg} not found`);109 setError(`Patient with EMBG ${searchEmbg} not found`); 112 110 setSearched(true); 113 111 setPatient(null); … … 120 118 }; 121 119 120 const handleSearch = async (e) => { 121 e.preventDefault(); 122 setError(null); 123 setLoading(true); 124 125 try { 126 if (!embg.trim()) { 127 setError('Please enter an EMBG'); 128 setLoading(false); 129 return; 130 } 131 132 // Search patient by EMBG 133 const patientResponse = await patientService.getPatientByEmbg(embg); 134 setPatient(patientResponse.data); 135 136 // Get medical records for this patient 137 const recordsResponse = await medicalRecordService.getMedicalRecordByPatientId(patientResponse.data.patientId); 138 setMedicalData(recordsResponse.data); 139 140 // Get lab results 141 try { 142 const labRes = await labService.getLabResultsForMedicalRecord(recordsResponse.data.recordId); 143 setLabResults(labRes.data || []); 144 } catch (err) { 145 setLabResults([]); 146 } 147 148 // Get procedure results 149 try { 150 const procRes = await procedureService.getProcedureResultsForMedicalRecord(recordsResponse.data.recordId); 151 setProcedureResults(procRes.data || []); 152 } catch (err) { 153 setProcedureResults([]); 154 } 155 156 setSearched(true); 157 } catch (err) { 158 setError(`Patient with EMBG ${embg} not found`); 159 setSearched(true); 160 setPatient(null); 161 setMedicalData(null); 162 setLabResults([]); 163 setProcedureResults([]); 164 } finally { 165 setLoading(false); 166 } 167 }; 168 122 169 return ( 123 <div> 124 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Medical Records</h1> 125 126 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 127 128 {/* Search Form - Only show for non-patients */} 129 {!isPatient && ( 130 <div className="bg-white rounded-lg shadow p-6 mb-6"> 131 <h2 className="text-xl font-bold mb-4">Search Medical Records</h2> 132 <form onSubmit={handleSearch} className="space-y-4"> 133 <div className="flex gap-4"> 134 <div className="flex-1"> 135 <label className="block text-sm font-semibold mb-2">Patient EMBG</label> 136 <input 137 type="text" 138 value={embg} 139 onChange={(e) => setEmbg(e.target.value)} 140 placeholder="e.g., 1402994123456" 141 className="w-full px-4 py-2 border rounded-lg" 142 /> 170 <div> 171 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Medical Records</h1> 172 173 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 174 175 {/* Search Form - Only show for non-patients */} 176 {!isPatient && ( 177 <div className="bg-white rounded-lg shadow p-6 mb-6"> 178 <h2 className="text-xl font-bold mb-4">Search Medical Records</h2> 179 <form onSubmit={handleSearch} className="space-y-4"> 180 <div className="flex gap-4"> 181 <div className="flex-1"> 182 <label className="block text-sm font-semibold mb-2">Patient EMBG</label> 183 <input 184 type="text" 185 value={embg} 186 onChange={(e) => setEmbg(e.target.value)} 187 placeholder="e.g., 1402994123456" 188 className="w-full px-4 py-2 border rounded-lg" 189 /> 190 </div> 191 <div className="flex items-end"> 192 <button 193 type="submit" 194 disabled={loading} 195 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400" 196 > 197 {loading ? 'Searching...' : 'Search'} 198 </button> 199 </div> 200 </div> 201 </form> 202 </div> 203 )} 204 205 {/* Loading indicator for patients fetching their records */} 206 {isPatient && loading && ( 207 <div className="bg-blue-50 rounded-lg shadow p-6 mb-6"> 208 <p className="text-sm text-gray-700"> 209 <strong>Loading your medical records...</strong> 210 </p> 211 </div> 212 )} 213 214 {/* Patient Info Message for Patients */} 215 {isPatient && searched && !loading && ( 216 <div className="bg-blue-50 rounded-lg shadow p-6 mb-6"> 217 <p className="text-sm text-gray-700"> 218 <strong>Viewing your medical records</strong> 219 </p> 220 </div> 221 )} 222 223 {/* Patient Medical Records */} 224 {searched && patient && ( 225 <div className="space-y-6"> 226 {/* Patient Info */} 227 <div className="bg-white rounded-lg shadow p-6"> 228 <div className="flex justify-between items-start mb-4"> 229 <h2 className="text-2xl font-bold">{patient.firstName} {patient.lastName}</h2> 230 {!isPatient && ( 231 <button 232 onClick={() => navigate(`/medical-records/${patient.patientId}`)} 233 className="bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700" 234 > 235 Add Medical Data 236 </button> 237 )} 238 </div> 239 <div className="grid grid-cols-4 gap-4"> 240 <div> 241 <p className="text-sm text-gray-600">EMBG</p> 242 <p className="font-semibold">{patient.embg}</p> 243 </div> 244 <div> 245 <p className="text-sm text-gray-600">Email</p> 246 <p className="font-semibold">{patient.emailAddress}</p> 247 </div> 248 <div> 249 <p className="text-sm text-gray-600">Blood Type</p> 250 <p className="font-semibold">{patient.bloodType || 'N/A'}</p> 251 </div> 252 <div> 253 <p className="text-sm text-gray-600">Date of Birth</p> 254 <p className="font-semibold">{patient.dateOfBirth}</p> 255 </div> 256 </div> 143 257 </div> 144 <div className="flex items-end"> 145 <button 146 type="submit" 147 disabled={loading} 148 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400" 149 > 150 {loading ? 'Searching...' : 'Search'} 151 </button> 152 </div> 153 </div> 154 </form> 155 </div> 156 )} 157 158 {/* Patient Info Message for Patients */} 159 {isPatient && searched && ( 160 <div className="bg-blue-50 rounded-lg shadow p-6 mb-6"> 161 <p className="text-sm text-gray-700"> 162 <strong>Viewing your medical records</strong> 163 </p> 164 </div> 165 )} 166 167 {/* Patient Medical Records */} 168 {searched && patient && ( 169 <div className="space-y-6"> 170 {/* Patient Info */} 171 <div className="bg-white rounded-lg shadow p-6"> 172 <div className="flex justify-between items-start mb-4"> 173 <h2 className="text-2xl font-bold">{patient.firstName} {patient.lastName}</h2> 174 {!isPatient && ( 175 <button 176 onClick={() => navigate(`/medical-records/${patient.patientId}`)} 177 className="bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700" 178 > 179 Add Medical Data 180 </button> 181 )} 182 </div> 183 <div className="grid grid-cols-4 gap-4"> 184 <div> 185 <p className="text-sm text-gray-600">EMBG</p> 186 <p className="font-semibold">{patient.embg}</p> 187 </div> 188 <div> 189 <p className="text-sm text-gray-600">Email</p> 190 <p className="font-semibold">{patient.emailAddress}</p> 191 </div> 192 <div> 193 <p className="text-sm text-gray-600">Blood Type</p> 194 <p className="font-semibold">{patient.bloodType || 'N/A'}</p> 195 </div> 196 <div> 197 <p className="text-sm text-gray-600">Date of Birth</p> 198 <p className="font-semibold">{patient.dateOfBirth}</p> 199 </div> 200 </div> 201 </div> 202 203 {/* Medical Data Sections */} 204 {medicalData && ( 205 <> 206 {/* Diagnoses */} 207 {medicalData.diagnoses && medicalData.diagnoses.length > 0 && ( 208 <div className="bg-white rounded-lg shadow p-6"> 209 <h3 className="text-xl font-bold mb-4">Diagnoses</h3> 210 <div className="space-y-3"> 211 {medicalData.diagnoses.map((diagnosis) => ( 212 <div key={diagnosis.diagnosisId} className="border-l-4 border-purple-500 pl-4 py-2"> 213 <p className="font-semibold text-lg">{diagnosis.name}</p> 214 {diagnosis.description && ( 215 <p className="text-gray-600 text-sm">{diagnosis.description}</p> 216 )} 217 <p className="text-xs text-gray-500">By: {diagnosis.doctorName}</p> 218 </div> 219 ))} 220 </div> 221 </div> 222 )} 223 224 {/* Symptoms */} 225 {medicalData.symptoms && medicalData.symptoms.length > 0 && ( 226 <div className="bg-white rounded-lg shadow p-6"> 227 <h3 className="text-xl font-bold mb-4">Symptoms</h3> 228 <div className="flex flex-wrap gap-2"> 229 {medicalData.symptoms.map((symptom) => ( 230 <span key={symptom.symptomId} className="bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full text-sm"> 258 259 {/* Medical Data Sections */} 260 {medicalData && ( 261 <> 262 {/* Diagnoses */} 263 {medicalData.diagnoses && medicalData.diagnoses.length > 0 && ( 264 <div className="bg-white rounded-lg shadow p-6"> 265 <h3 className="text-xl font-bold mb-4">Diagnoses</h3> 266 <div className="space-y-3"> 267 {medicalData.diagnoses.map((diagnosis) => ( 268 <div key={diagnosis.diagnosisId} className="border-l-4 border-purple-500 pl-4 py-2"> 269 <p className="font-semibold text-lg">{diagnosis.name}</p> 270 {diagnosis.description && ( 271 <p className="text-gray-600 text-sm">{diagnosis.description}</p> 272 )} 273 <p className="text-xs text-gray-500">By: {diagnosis.doctorName}</p> 274 </div> 275 ))} 276 </div> 277 </div> 278 )} 279 280 {/* Symptoms */} 281 {medicalData.symptoms && medicalData.symptoms.length > 0 && ( 282 <div className="bg-white rounded-lg shadow p-6"> 283 <h3 className="text-xl font-bold mb-4">Symptoms</h3> 284 <div className="flex flex-wrap gap-2"> 285 {medicalData.symptoms.map((symptom) => ( 286 <span key={symptom.symptomId} className="bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full text-sm"> 231 287 {symptom.symptomName} 232 288 </span> 233 ))}234 </div>235 </div>236 )}237 238 {/* Allergies */}239 {medicalData.allergies && medicalData.allergies.length > 0 && (240 <div className="bg-white rounded-lg shadow p-6">241 <h3 className="text-xl font-bold mb-4">Allergies</h3>242 <div className="space-y-3">243 {medicalData.allergies.map((allergy) => (244 <div key={allergy.allergyId} className="border-l-4 border-red-500 pl-4 py-2">245 <p className="font-semibold">{allergy.allergyName}</p>246 <p className="text-sm">289 ))} 290 </div> 291 </div> 292 )} 293 294 {/* Allergies */} 295 {medicalData.allergies && medicalData.allergies.length > 0 && ( 296 <div className="bg-white rounded-lg shadow p-6"> 297 <h3 className="text-xl font-bold mb-4">Allergies</h3> 298 <div className="space-y-3"> 299 {medicalData.allergies.map((allergy) => ( 300 <div key={allergy.allergyId} className="border-l-4 border-red-500 pl-4 py-2"> 301 <p className="font-semibold">{allergy.allergyName}</p> 302 <p className="text-sm"> 247 303 <span className={`px-2 py-1 rounded text-white text-xs ${ 248 allergy.severity === 'CRITICAL' ? 'bg-red-600' :249 allergy.severity === 'HIGH' ? 'bg-red-500' :250 allergy.severity === 'MEDIUM' ? 'bg-yellow-500' :251 'bg-green-500'304 allergy.severity === 'CRITICAL' ? 'bg-red-600' : 305 allergy.severity === 'HIGH' ? 'bg-red-500' : 306 allergy.severity === 'MEDIUM' ? 'bg-yellow-500' : 307 'bg-green-500' 252 308 }`}> 253 309 {allergy.severity} Severity 254 310 </span> 255 </p> 256 {allergy.reaction && ( 257 <p className="text-gray-600 text-sm">Reaction: {allergy.reaction}</p> 311 </p> 312 {allergy.reaction && ( 313 <p className="text-gray-600 text-sm">Reaction: {allergy.reaction}</p> 314 )} 315 </div> 316 ))} 317 </div> 318 </div> 319 )} 320 321 {/* Medical Reports */} 322 {medicalData.reports && medicalData.reports.length > 0 && ( 323 <div className="bg-white rounded-lg shadow p-6"> 324 <h3 className="text-xl font-bold mb-4">Medical Reports</h3> 325 <div className="space-y-3"> 326 {medicalData.reports.map((report) => ( 327 <div key={report.reportId} className="border-l-4 border-green-500 pl-4 py-2"> 328 <p className="font-semibold">Report from {report.doctorName}</p> 329 <p className="text-gray-600 text-sm">{report.description}</p> 330 <p className="text-xs text-gray-500">Date: {report.reportDate}</p> 331 </div> 332 ))} 333 </div> 334 </div> 335 )} 336 337 {/* Lab Test Results */} 338 {labResults && labResults.length > 0 && ( 339 <div className="bg-white rounded-lg shadow p-6"> 340 <h3 className="text-xl font-bold mb-4">Lab Test Results</h3> 341 <div className="space-y-3"> 342 {labResults.map((result) => ( 343 <div key={result.labResultId} className="border-l-4 border-purple-500 pl-4 py-2"> 344 <p className="font-semibold">{result.testName}</p> 345 <p className="text-gray-600 text-sm">{result.result}</p> 346 {result.notes && ( 347 <p className="text-gray-600 text-sm">Notes: {result.notes}</p> 348 )} 349 <p className="text-xs text-gray-500">Date: {result.resultDate}</p> 350 {result.technicianName && ( 351 <p className="text-xs text-gray-500">Technician: {result.technicianName}</p> 352 )} 353 </div> 354 ))} 355 </div> 356 </div> 357 )} 358 359 {/* Procedure Results */} 360 {procedureResults && procedureResults.length > 0 && ( 361 <div className="bg-white rounded-lg shadow p-6"> 362 <h3 className="text-xl font-bold mb-4">Procedure Results</h3> 363 <div className="space-y-3"> 364 {procedureResults.map((result) => ( 365 <div key={result.procedureResultId} className="border-l-4 border-orange-500 pl-4 py-2"> 366 <p className="font-semibold">{result.procedureName}</p> 367 <p className="text-gray-600 text-sm">{result.result}</p> 368 {result.notes && ( 369 <p className="text-gray-600 text-sm">Notes: {result.notes}</p> 370 )} 371 <p className="text-xs text-gray-500">Date: {result.resultDate}</p> 372 {result.doctorName && ( 373 <p className="text-xs text-gray-500">Doctor: {result.doctorName}</p> 374 )} 375 </div> 376 ))} 377 </div> 378 </div> 379 )} 380 381 {/* No data message */} 382 {(!medicalData.diagnoses || medicalData.diagnoses.length === 0) && 383 (!medicalData.symptoms || medicalData.symptoms.length === 0) && 384 (!medicalData.allergies || medicalData.allergies.length === 0) && 385 (!medicalData.reports || medicalData.reports.length === 0) && 386 (!labResults || labResults.length === 0) && 387 (!procedureResults || procedureResults.length === 0) && ( 388 <div className="bg-blue-50 rounded-lg p-6 text-center"> 389 <p className="text-gray-600">No medical records found for this patient</p> 390 </div> 258 391 )} 259 </div> 260 ))} 261 </div> 262 </div> 392 </> 263 393 )} 264 265 {/* Medical Reports */} 266 {medicalData.reports && medicalData.reports.length > 0 && ( 267 <div className="bg-white rounded-lg shadow p-6"> 268 <h3 className="text-xl font-bold mb-4">Medical Reports</h3> 269 <div className="space-y-3"> 270 {medicalData.reports.map((report) => ( 271 <div key={report.reportId} className="border-l-4 border-green-500 pl-4 py-2"> 272 <p className="font-semibold">Report from {report.doctorName}</p> 273 <p className="text-gray-600 text-sm">{report.description}</p> 274 <p className="text-xs text-gray-500">Date: {report.reportDate}</p> 275 </div> 276 ))} 277 </div> 278 </div> 279 )} 280 281 {/* Lab Test Results */} 282 {labResults && labResults.length > 0 && ( 283 <div className="bg-white rounded-lg shadow p-6"> 284 <h3 className="text-xl font-bold mb-4">Lab Test Results</h3> 285 <div className="space-y-3"> 286 {labResults.map((result) => ( 287 <div key={result.labResultId} className="border-l-4 border-purple-500 pl-4 py-2"> 288 <p className="font-semibold">{result.testName}</p> 289 <p className="text-gray-600 text-sm">{result.result}</p> 290 {result.notes && ( 291 <p className="text-gray-600 text-sm">Notes: {result.notes}</p> 292 )} 293 <p className="text-xs text-gray-500">Date: {result.resultDate}</p> 294 {result.technicianName && ( 295 <p className="text-xs text-gray-500">Technician: {result.technicianName}</p> 296 )} 297 </div> 298 ))} 299 </div> 300 </div> 301 )} 302 303 {/* Procedure Results */} 304 {procedureResults && procedureResults.length > 0 && ( 305 <div className="bg-white rounded-lg shadow p-6"> 306 <h3 className="text-xl font-bold mb-4">Procedure Results</h3> 307 <div className="space-y-3"> 308 {procedureResults.map((result) => ( 309 <div key={result.procedureResultId} className="border-l-4 border-orange-500 pl-4 py-2"> 310 <p className="font-semibold">{result.procedureName}</p> 311 <p className="text-gray-600 text-sm">{result.result}</p> 312 {result.notes && ( 313 <p className="text-gray-600 text-sm">Notes: {result.notes}</p> 314 )} 315 <p className="text-xs text-gray-500">Date: {result.resultDate}</p> 316 {result.doctorName && ( 317 <p className="text-xs text-gray-500">Doctor: {result.doctorName}</p> 318 )} 319 </div> 320 ))} 321 </div> 322 </div> 323 )} 324 325 {/* No data message */} 326 {(!medicalData.diagnoses || medicalData.diagnoses.length === 0) && 327 (!medicalData.symptoms || medicalData.symptoms.length === 0) && 328 (!medicalData.allergies || medicalData.allergies.length === 0) && 329 (!medicalData.reports || medicalData.reports.length === 0) && 330 (!labResults || labResults.length === 0) && 331 (!procedureResults || procedureResults.length === 0) && ( 332 <div className="bg-blue-50 rounded-lg p-6 text-center"> 333 <p className="text-gray-600">No medical records found for this patient</p> 334 </div> 335 )} 336 </> 337 )} 338 </div> 339 )} 340 341 {/* No search performed */} 342 {!searched && !isPatient && ( 343 <div className="bg-gray-50 rounded-lg p-12 text-center"> 344 <p className="text-gray-600 text-lg">Enter a patient EMBG to view their medical records</p> 345 </div> 346 )} 347 348 {/* Loading message for patients */} 349 {isPatient && loading && ( 350 <div className="bg-gray-50 rounded-lg p-12 text-center"> 351 <p className="text-gray-600 text-lg">Loading your medical records...</p> 352 </div> 353 )} 354 </div> 394 </div> 395 )} 396 397 {/* No search performed */} 398 {!searched && !isPatient && ( 399 <div className="bg-gray-50 rounded-lg p-12 text-center"> 400 <p className="text-gray-600 text-lg">Enter a patient EMBG to view their medical records</p> 401 </div> 402 )} 403 404 {/* Loading message for patients */} 405 {isPatient && loading && ( 406 <div className="bg-gray-50 rounded-lg p-12 text-center"> 407 <p className="text-gray-600 text-lg">Loading your medical records...</p> 408 </div> 409 )} 410 </div> 355 411 ); 356 412 }
Note:
See TracChangeset
for help on using the changeset viewer.
