Changeset 9af201e for frontend/src/pages/lab-tests/LabTestList.js
- Timestamp:
- 09/04/26 19:08:08 (11 days ago)
- Branches:
- master
- Children:
- cdcff72
- Parents:
- e1f74f6
- File:
-
- 1 edited
-
frontend/src/pages/lab-tests/LabTestList.js (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/pages/lab-tests/LabTestList.js
re1f74f6 r9af201e 5 5 6 6 function LabTestList() { 7 const user = JSON.parse(localStorage.getItem('user') || '{}');7 const [user, setUser] = useState({}); 8 8 const isLabTechnician = user.role === 'LAB_TECHNICIAN'; 9 9 … … 46 46 testDate: '', 47 47 }); 48 49 // Load user from localStorage and listen for changes 50 useEffect(() => { 51 const loadUser = () => { 52 const userStr = localStorage.getItem('user'); 53 console.log('[LabTestList] Loading user from localStorage:', userStr); 54 if (userStr) { 55 try { 56 const parsedUser = JSON.parse(userStr); 57 console.log('[LabTestList] Parsed user:', parsedUser); 58 setUser(parsedUser); 59 } catch (err) { 60 console.error('Failed to parse user:', err); 61 setUser({}); 62 } 63 } else { 64 console.log('[LabTestList] No user in localStorage'); 65 setUser({}); 66 } 67 }; 68 69 console.log('[LabTestList] useEffect running - loading user'); 70 loadUser(); 71 72 // Listen for storage changes (other tabs/windows) 73 window.addEventListener('storage', loadUser); 74 75 // Listen for custom event (same tab changes) 76 window.addEventListener('userStorageChange', loadUser); 77 78 return () => { 79 window.removeEventListener('storage', loadUser); 80 window.removeEventListener('userStorageChange', loadUser); 81 }; 82 }, []); 48 83 49 84 useEffect(() => { … … 134 169 setLoading(true); 135 170 136 // Get doctor ID from lo calStorage (set during login)137 const doctorId = localStorage.getItem('doctorId') || 1;171 // Get doctor ID from logged-in user 172 const doctorId = user.doctorId; 138 173 139 174 const request = { … … 145 180 notes: requestData.notes, 146 181 }; 182 183 console.log('User object:', user); 184 console.log('Sending lab test request with doctorId:', doctorId, 'Full request:', request); 147 185 148 186 await labService.requestLabTest(request); … … 228 266 if (isLabTechnician) { 229 267 return ( 230 <div> 231 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests - Submit Results</h1> 232 233 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 234 235 {selectedTest ? ( 236 // Submit Result Form 237 <div className="bg-white rounded-lg shadow p-6 mb-6"> 238 <h2 className="text-xl font-bold mb-4">Submit Lab Result</h2> 239 <div className="mb-4 p-4 bg-gray-50 rounded-lg"> 240 <p className="mb-2"><strong>Test:</strong> {selectedTest.testName}</p> 241 <p className="mb-2"><strong>Patient:</strong> {selectedTest.patientName}</p> 242 <p className="mb-2"><strong>Doctor:</strong> {selectedTest.doctorName}</p> 243 <p className="mb-2"><strong>Test Date:</strong> {selectedTest.testDate}</p> 244 {selectedTest.notes && <p><strong>Notes:</strong> {selectedTest.notes}</p>} 268 <div> 269 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests - Submit Results</h1> 270 271 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 272 273 {selectedTest ? ( 274 // Submit Result Form 275 <div className="bg-white rounded-lg shadow p-6 mb-6"> 276 <h2 className="text-xl font-bold mb-4">Submit Lab Result</h2> 277 <div className="mb-4 p-4 bg-gray-50 rounded-lg"> 278 <p className="mb-2"><strong>Test:</strong> {selectedTest.testName}</p> 279 <p className="mb-2"><strong>Patient:</strong> {selectedTest.patientName}</p> 280 <p className="mb-2"><strong>Doctor:</strong> {selectedTest.doctorName}</p> 281 <p className="mb-2"><strong>Test Date:</strong> {selectedTest.testDate}</p> 282 {selectedTest.notes && <p><strong>Notes:</strong> {selectedTest.notes}</p>} 283 </div> 284 285 <form onSubmit={handleSubmitResult} className="space-y-4"> 286 <div> 287 <label className="block text-sm font-semibold mb-2">Test Results *</label> 288 <textarea 289 value={submitFormData.results} 290 onChange={(e) => setSubmitFormData({ ...submitFormData, results: e.target.value })} 291 placeholder="Enter detailed test results" 292 className="w-full px-4 py-2 border rounded-lg" 293 rows="4" 294 required 295 /> 296 </div> 297 298 <div> 299 <label className="block text-sm font-semibold mb-2">Result Date *</label> 300 <input 301 type="date" 302 value={submitFormData.resultDate} 303 onChange={(e) => setSubmitFormData({ ...submitFormData, resultDate: e.target.value })} 304 className="w-full px-4 py-2 border rounded-lg" 305 required 306 /> 307 </div> 308 309 <div className="flex gap-4"> 310 <button 311 type="submit" 312 disabled={loading} 313 style={{ 314 background: loading ? '#d1d5db' : '#bfdbfe', 315 color: '#1e1035', 316 padding: '8px 24px', 317 borderRadius: '6px', 318 border: 'none', 319 cursor: loading ? 'not-allowed' : 'pointer', 320 fontSize: '14px', 321 fontWeight: '400' 322 }} 323 onMouseEnter={(e) => !loading && (e.currentTarget.style.background = '#93c5fd')} 324 onMouseLeave={(e) => !loading && (e.currentTarget.style.background = '#bfdbfe')} 325 > 326 {loading ? 'Submitting...' : 'Submit Result'} 327 </button> 328 <button 329 type="button" 330 onClick={() => { 331 setSelectedTest(null); 332 setSubmitFormData({ 333 results: '', 334 resultDate: new Date().toISOString().split('T')[0], 335 }); 336 }} 337 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500" 338 > 339 Cancel 340 </button> 341 </div> 342 </form> 343 </div> 344 ) : ( 345 // Pending Tests List 346 <div className="bg-white rounded-lg shadow overflow-hidden"> 347 <div className="p-6 border-b"> 348 <h2 className="text-xl font-bold">Pending Lab Tests ({pendingTests.length})</h2> 349 </div> 350 351 {pendingTests.length === 0 ? ( 352 <div className="p-6 text-center text-gray-600"> 353 No pending lab tests 354 </div> 355 ) : ( 356 <> 357 {/* Filters */} 358 <div className="p-6 border-b bg-gray-50"> 359 <h3 className="text-sm font-semibold mb-4">Filters</h3> 360 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> 361 <div> 362 <label className="block text-sm font-semibold mb-2">Test</label> 363 <input 364 type="text" 365 placeholder="Filter by test name..." 366 value={pendingFilters.testName} 367 onChange={(e) => setPendingFilters({...pendingFilters, testName: e.target.value})} 368 className="w-full px-3 py-2 border rounded-lg text-sm" 369 /> 370 </div> 371 <div> 372 <label className="block text-sm font-semibold mb-2">Patient</label> 373 <input 374 type="text" 375 placeholder="Filter by patient name..." 376 value={pendingFilters.patientName} 377 onChange={(e) => setPendingFilters({...pendingFilters, patientName: e.target.value})} 378 className="w-full px-3 py-2 border rounded-lg text-sm" 379 /> 380 </div> 381 <div> 382 <label className="block text-sm font-semibold mb-2">Test Date</label> 383 <input 384 type="date" 385 value={pendingFilters.testDate} 386 onChange={(e) => setPendingFilters({...pendingFilters, testDate: e.target.value})} 387 className="w-full px-3 py-2 border rounded-lg text-sm" 388 /> 389 </div> 390 </div> 391 <button 392 onClick={() => setPendingFilters({testName: '', patientName: '', testDate: ''})} 393 className="mt-4 bg-gray-300 text-gray-700 px-4 py-2 rounded text-sm hover:bg-gray-400" 394 > 395 Clear Filters 396 </button> 245 397 </div> 246 398 247 <form onSubmit={handleSubmitResult} className="space-y-4"> 248 <div> 249 <label className="block text-sm font-semibold mb-2">Test Results *</label> 250 <textarea 251 value={submitFormData.results} 252 onChange={(e) => setSubmitFormData({ ...submitFormData, results: e.target.value })} 253 placeholder="Enter detailed test results" 254 className="w-full px-4 py-2 border rounded-lg" 255 rows="4" 256 required 257 /> 399 {/* Filtered Table */} 400 <table className="w-full"> 401 <thead className="bg-gray-100"> 402 <tr> 403 <th className="px-6 py-3 text-left text-sm font-semibold">Test</th> 404 <th className="px-6 py-3 text-left text-sm font-semibold">Patient</th> 405 <th className="px-6 py-3 text-left text-sm font-semibold">Doctor</th> 406 <th className="px-6 py-3 text-left text-sm font-semibold">Requested</th> 407 <th className="px-6 py-3 text-left text-sm font-semibold">Test Date</th> 408 <th className="px-6 py-3 text-left text-sm font-semibold">Notes</th> 409 <th className="px-6 py-3 text-left text-sm font-semibold">Action</th> 410 </tr> 411 </thead> 412 <tbody> 413 {pendingTests 414 .filter((test) => { 415 const testName = (test.testName || '').toLowerCase(); 416 const patientName = (test.patientName || '').toLowerCase(); 417 const testDate = (test.testDate || ''); 418 419 return ( 420 testName.includes(pendingFilters.testName.toLowerCase()) && 421 patientName.includes(pendingFilters.patientName.toLowerCase()) && 422 (pendingFilters.testDate === '' || testDate === pendingFilters.testDate) 423 ); 424 }) 425 .map((test, index) => ( 426 <tr key={index} className="border-t hover:bg-gray-50"> 427 <td className="px-6 py-3 font-medium">{test.testName}</td> 428 <td className="px-6 py-3">{test.patientName}</td> 429 <td className="px-6 py-3">{test.doctorName}</td> 430 <td className="px-6 py-3 text-green-600">{test.requestDate}</td> 431 <td className="px-6 py-3 text-purple-600">{test.testDate}</td> 432 <td className="px-6 py-3 text-gray-600 text-sm">{test.notes || '-'}</td> 433 <td className="px-6 py-3"> 434 <button 435 onClick={() => setSelectedTest(test)} 436 style={{ 437 background: '#bfdbfe', 438 color: '#1e1035', 439 padding: '6px 12px', 440 borderRadius: '4px', 441 border: 'none', 442 cursor: 'pointer', 443 fontSize: '12px', 444 fontWeight: '400' 445 }} 446 onMouseEnter={(e) => e.currentTarget.style.background = '#93c5fd'} 447 onMouseLeave={(e) => e.currentTarget.style.background = '#bfdbfe'} 448 > 449 Submit Result 450 </button> 451 </td> 452 </tr> 453 ))} 454 </tbody> 455 </table> 456 </> 457 )} 458 </div> 459 )} 460 461 {/* Submitted Tests History */} 462 {!selectedTest && ( 463 <div className="bg-white rounded-lg shadow overflow-hidden mt-6"> 464 <div className="p-6 border-b"> 465 <h2 className="text-xl font-bold">Submitted Lab Results History ({submittedTests.length})</h2> 466 </div> 467 468 {submittedTests.length === 0 ? ( 469 <div className="p-6 text-center text-gray-600"> 470 No submitted lab results yet 471 </div> 472 ) : ( 473 <> 474 {/* Filters */} 475 <div className="p-6 border-b bg-gray-50"> 476 <h3 className="text-sm font-semibold mb-4">Filters</h3> 477 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> 478 <div> 479 <label className="block text-sm font-semibold mb-2">Test</label> 480 <input 481 type="text" 482 placeholder="Filter by test name..." 483 value={submittedFilters.testName} 484 onChange={(e) => setSubmittedFilters({...submittedFilters, testName: e.target.value})} 485 className="w-full px-3 py-2 border rounded-lg text-sm" 486 /> 487 </div> 488 <div> 489 <label className="block text-sm font-semibold mb-2">Patient</label> 490 <input 491 type="text" 492 placeholder="Filter by patient name..." 493 value={submittedFilters.patientName} 494 onChange={(e) => setSubmittedFilters({...submittedFilters, patientName: e.target.value})} 495 className="w-full px-3 py-2 border rounded-lg text-sm" 496 /> 497 </div> 498 <div> 499 <label className="block text-sm font-semibold mb-2">Test Date</label> 500 <input 501 type="date" 502 value={submittedFilters.testDate} 503 onChange={(e) => setSubmittedFilters({...submittedFilters, testDate: e.target.value})} 504 className="w-full px-3 py-2 border rounded-lg text-sm" 505 /> 506 </div> 258 507 </div> 259 260 <div> 261 <label className="block text-sm font-semibold mb-2">Result Date *</label> 262 <input 263 type="date" 264 value={submitFormData.resultDate} 265 onChange={(e) => setSubmitFormData({ ...submitFormData, resultDate: e.target.value })} 266 className="w-full px-4 py-2 border rounded-lg" 267 required 268 /> 269 </div> 270 271 <div className="flex gap-4"> 272 <button 273 type="submit" 274 disabled={loading} 275 style={{ 276 background: loading ? '#d1d5db' : '#bfdbfe', 277 color: '#1e1035', 278 padding: '8px 24px', 279 borderRadius: '6px', 280 border: 'none', 281 cursor: loading ? 'not-allowed' : 'pointer', 282 fontSize: '14px', 283 fontWeight: '400' 284 }} 285 onMouseEnter={(e) => !loading && (e.currentTarget.style.background = '#93c5fd')} 286 onMouseLeave={(e) => !loading && (e.currentTarget.style.background = '#bfdbfe')} 287 > 288 {loading ? 'Submitting...' : 'Submit Result'} 289 </button> 290 <button 291 type="button" 292 onClick={() => { 293 setSelectedTest(null); 294 setSubmitFormData({ 295 results: '', 296 resultDate: new Date().toISOString().split('T')[0], 297 }); 298 }} 299 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500" 300 > 301 Cancel 302 </button> 303 </div> 304 </form> 305 </div> 306 ) : ( 307 // Pending Tests List 308 <div className="bg-white rounded-lg shadow overflow-hidden"> 309 <div className="p-6 border-b"> 310 <h2 className="text-xl font-bold">Pending Lab Tests ({pendingTests.length})</h2> 508 <button 509 onClick={() => setSubmittedFilters({testName: '', patientName: '', testDate: ''})} 510 className="mt-4 bg-gray-300 text-gray-700 px-4 py-2 rounded text-sm hover:bg-gray-400" 511 > 512 Clear Filters 513 </button> 311 514 </div> 312 515 313 {pendingTests.length === 0 ? ( 314 <div className="p-6 text-center text-gray-600"> 315 No pending lab tests 316 </div> 317 ) : ( 318 <> 319 {/* Filters */} 320 <div className="p-6 border-b bg-gray-50"> 321 <h3 className="text-sm font-semibold mb-4">Filters</h3> 322 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> 323 <div> 324 <label className="block text-sm font-semibold mb-2">Test</label> 325 <input 326 type="text" 327 placeholder="Filter by test name..." 328 value={pendingFilters.testName} 329 onChange={(e) => setPendingFilters({...pendingFilters, testName: e.target.value})} 330 className="w-full px-3 py-2 border rounded-lg text-sm" 331 /> 332 </div> 333 <div> 334 <label className="block text-sm font-semibold mb-2">Patient</label> 335 <input 336 type="text" 337 placeholder="Filter by patient name..." 338 value={pendingFilters.patientName} 339 onChange={(e) => setPendingFilters({...pendingFilters, patientName: e.target.value})} 340 className="w-full px-3 py-2 border rounded-lg text-sm" 341 /> 342 </div> 343 <div> 344 <label className="block text-sm font-semibold mb-2">Test Date</label> 345 <input 346 type="date" 347 value={pendingFilters.testDate} 348 onChange={(e) => setPendingFilters({...pendingFilters, testDate: e.target.value})} 349 className="w-full px-3 py-2 border rounded-lg text-sm" 350 /> 351 </div> 352 </div> 353 <button 354 onClick={() => setPendingFilters({testName: '', patientName: '', testDate: ''})} 355 className="mt-4 bg-gray-300 text-gray-700 px-4 py-2 rounded text-sm hover:bg-gray-400" 356 > 357 Clear Filters 358 </button> 359 </div> 360 361 {/* Filtered Table */} 362 <table className="w-full"> 363 <thead className="bg-gray-100"> 364 <tr> 365 <th className="px-6 py-3 text-left text-sm font-semibold">Test</th> 366 <th className="px-6 py-3 text-left text-sm font-semibold">Patient</th> 367 <th className="px-6 py-3 text-left text-sm font-semibold">Doctor</th> 368 <th className="px-6 py-3 text-left text-sm font-semibold">Requested</th> 369 <th className="px-6 py-3 text-left text-sm font-semibold">Test Date</th> 370 <th className="px-6 py-3 text-left text-sm font-semibold">Notes</th> 371 <th className="px-6 py-3 text-left text-sm font-semibold">Action</th> 372 </tr> 373 </thead> 374 <tbody> 375 {pendingTests 376 .filter((test) => { 377 const testName = (test.testName || '').toLowerCase(); 378 const patientName = (test.patientName || '').toLowerCase(); 379 const testDate = (test.testDate || ''); 380 381 return ( 382 testName.includes(pendingFilters.testName.toLowerCase()) && 383 patientName.includes(pendingFilters.patientName.toLowerCase()) && 384 (pendingFilters.testDate === '' || testDate === pendingFilters.testDate) 385 ); 386 }) 387 .map((test, index) => ( 388 <tr key={index} className="border-t hover:bg-gray-50"> 389 <td className="px-6 py-3 font-medium">{test.testName}</td> 390 <td className="px-6 py-3">{test.patientName}</td> 391 <td className="px-6 py-3">{test.doctorName}</td> 392 <td className="px-6 py-3 text-green-600">{test.requestDate}</td> 393 <td className="px-6 py-3 text-purple-600">{test.testDate}</td> 394 <td className="px-6 py-3 text-gray-600 text-sm">{test.notes || '-'}</td> 395 <td className="px-6 py-3"> 396 <button 397 onClick={() => setSelectedTest(test)} 398 style={{ 399 background: '#bfdbfe', 400 color: '#1e1035', 401 padding: '6px 12px', 402 borderRadius: '4px', 403 border: 'none', 404 cursor: 'pointer', 405 fontSize: '12px', 406 fontWeight: '400' 407 }} 408 onMouseEnter={(e) => e.currentTarget.style.background = '#93c5fd'} 409 onMouseLeave={(e) => e.currentTarget.style.background = '#bfdbfe'} 410 > 411 Submit Result 412 </button> 413 </td> 414 </tr> 415 ))} 416 </tbody> 417 </table> 418 </> 419 )} 420 </div> 421 )} 422 423 {/* Submitted Tests History */} 424 {!selectedTest && ( 425 <div className="bg-white rounded-lg shadow overflow-hidden mt-6"> 426 <div className="p-6 border-b"> 427 <h2 className="text-xl font-bold">Submitted Lab Results History ({submittedTests.length})</h2> 428 </div> 429 430 {submittedTests.length === 0 ? ( 431 <div className="p-6 text-center text-gray-600"> 432 No submitted lab results yet 433 </div> 434 ) : ( 435 <> 436 {/* Filters */} 437 <div className="p-6 border-b bg-gray-50"> 438 <h3 className="text-sm font-semibold mb-4">Filters</h3> 439 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> 440 <div> 441 <label className="block text-sm font-semibold mb-2">Test</label> 442 <input 443 type="text" 444 placeholder="Filter by test name..." 445 value={submittedFilters.testName} 446 onChange={(e) => setSubmittedFilters({...submittedFilters, testName: e.target.value})} 447 className="w-full px-3 py-2 border rounded-lg text-sm" 448 /> 449 </div> 450 <div> 451 <label className="block text-sm font-semibold mb-2">Patient</label> 452 <input 453 type="text" 454 placeholder="Filter by patient name..." 455 value={submittedFilters.patientName} 456 onChange={(e) => setSubmittedFilters({...submittedFilters, patientName: e.target.value})} 457 className="w-full px-3 py-2 border rounded-lg text-sm" 458 /> 459 </div> 460 <div> 461 <label className="block text-sm font-semibold mb-2">Test Date</label> 462 <input 463 type="date" 464 value={submittedFilters.testDate} 465 onChange={(e) => setSubmittedFilters({...submittedFilters, testDate: e.target.value})} 466 className="w-full px-3 py-2 border rounded-lg text-sm" 467 /> 468 </div> 469 </div> 470 <button 471 onClick={() => setSubmittedFilters({testName: '', patientName: '', testDate: ''})} 472 className="mt-4 bg-gray-300 text-gray-700 px-4 py-2 rounded text-sm hover:bg-gray-400" 473 > 474 Clear Filters 475 </button> 476 </div> 477 478 {/* Filtered Results Table */} 479 <table className="w-full"> 480 <thead className="bg-gray-100"> 481 <tr> 482 <th className="px-6 py-3 text-left text-sm font-semibold">Test</th> 483 <th className="px-6 py-3 text-left text-sm font-semibold">Patient</th> 484 <th className="px-6 py-3 text-left text-sm font-semibold">Test Date</th> 485 <th className="px-6 py-3 text-left text-sm font-semibold">Result Date</th> 486 <th className="px-6 py-3 text-left text-sm font-semibold">Status</th> 487 </tr> 488 </thead> 489 <tbody> 490 {submittedTests 491 .filter((test) => { 492 const testName = (test.testName || '').toLowerCase(); 493 const patientName = (test.patientName || '').toLowerCase(); 494 const testDate = (test.testDate || ''); 495 496 return ( 497 testName.includes(submittedFilters.testName.toLowerCase()) && 498 patientName.includes(submittedFilters.patientName.toLowerCase()) && 499 (submittedFilters.testDate === '' || testDate === submittedFilters.testDate) 500 ); 501 }) 502 .map((test, index) => ( 503 <tr key={index} className="border-t hover:bg-gray-50"> 504 <td className="px-6 py-3 font-medium">{test.testName || test.description || 'Lab Test'}</td> 505 <td className="px-6 py-3">{test.patientName || 'N/A'}</td> 506 <td className="px-6 py-3">{test.testDate || 'N/A'}</td> 507 <td className="px-6 py-3 text-green-600">{test.resultDate || test.createdDate || 'N/A'}</td> 508 <td className="px-6 py-3"> 516 {/* Filtered Results Table */} 517 <table className="w-full"> 518 <thead className="bg-gray-100"> 519 <tr> 520 <th className="px-6 py-3 text-left text-sm font-semibold">Test</th> 521 <th className="px-6 py-3 text-left text-sm font-semibold">Patient</th> 522 <th className="px-6 py-3 text-left text-sm font-semibold">Test Date</th> 523 <th className="px-6 py-3 text-left text-sm font-semibold">Result Date</th> 524 <th className="px-6 py-3 text-left text-sm font-semibold">Status</th> 525 </tr> 526 </thead> 527 <tbody> 528 {submittedTests 529 .filter((test) => { 530 const testName = (test.testName || '').toLowerCase(); 531 const patientName = (test.patientName || '').toLowerCase(); 532 const testDate = (test.testDate || ''); 533 534 return ( 535 testName.includes(submittedFilters.testName.toLowerCase()) && 536 patientName.includes(submittedFilters.patientName.toLowerCase()) && 537 (submittedFilters.testDate === '' || testDate === submittedFilters.testDate) 538 ); 539 }) 540 .map((test, index) => ( 541 <tr key={index} className="border-t hover:bg-gray-50"> 542 <td className="px-6 py-3 font-medium">{test.testName || test.description || 'Lab Test'}</td> 543 <td className="px-6 py-3">{test.patientName || 'N/A'}</td> 544 <td className="px-6 py-3">{test.testDate || 'N/A'}</td> 545 <td className="px-6 py-3 text-green-600">{test.resultDate || test.createdDate || 'N/A'}</td> 546 <td className="px-6 py-3"> 509 547 <span className="px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm font-semibold"> 510 548 Submitted 511 549 </span> 512 </td> 513 </tr> 514 ))} 515 </tbody> 516 </table> 517 </> 518 )} 519 </div> 550 </td> 551 </tr> 552 ))} 553 </tbody> 554 </table> 555 </> 556 )} 557 </div> 558 )} 559 </div> 560 ); 561 } 562 563 return ( 564 <div> 565 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests</h1> 566 567 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 568 569 {/* Search Form */} 570 <div className="bg-white rounded-lg shadow p-6 mb-6"> 571 <h2 className="text-xl font-bold mb-4">Search Patient</h2> 572 <form onSubmit={handleSearch} className="space-y-4"> 573 <div className="flex gap-4"> 574 <div className="flex-1"> 575 <label className="block text-sm font-semibold mb-2">Patient EMBG</label> 576 <input 577 type="text" 578 value={embg} 579 onChange={(e) => setEmbg(e.target.value)} 580 placeholder="e.g., 1402994123456" 581 className="w-full px-4 py-2 border rounded-lg" 582 /> 583 </div> 584 <div className="flex items-end"> 585 <button 586 type="submit" 587 disabled={loading} 588 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400" 589 > 590 {loading ? 'Searching...' : 'Search'} 591 </button> 592 </div> 593 </div> 594 </form> 595 </div> 596 597 {/* Patient Lab Tests */} 598 {searched && patient && ( 599 <div className="space-y-6"> 600 {/* Patient Info */} 601 <div className="bg-white rounded-lg shadow p-6"> 602 <div className="flex justify-between items-start mb-4"> 603 <h2 className="text-2xl font-bold">{patient.firstName} {patient.lastName}</h2> 604 {!showRequestForm && ( 605 <button 606 onClick={() => setShowRequestForm(true)} 607 className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" 608 > 609 Request Lab Test 610 </button> 611 )} 612 </div> 613 <div className="grid grid-cols-4 gap-4"> 614 <div> 615 <p className="text-sm text-gray-600">EMBG</p> 616 <p className="font-semibold">{patient.embg}</p> 617 </div> 618 <div> 619 <p className="text-sm text-gray-600">Blood Type</p> 620 <p className="font-semibold">{patient.bloodType || 'N/A'}</p> 621 </div> 622 <div> 623 <p className="text-sm text-gray-600">Date of Birth</p> 624 <p className="font-semibold">{patient.dateOfBirth}</p> 625 </div> 626 </div> 627 </div> 628 629 {/* Request Lab Test Form */} 630 {showRequestForm && ( 631 <div className="bg-white rounded-lg shadow p-6"> 632 <h3 className="text-xl font-bold mb-4">Request Lab Test</h3> 633 <form onSubmit={handleRequestTest} className="space-y-4"> 634 <div className="grid grid-cols-2 gap-4"> 635 <div> 636 <label className="block text-sm font-semibold mb-2">Test</label> 637 <select 638 value={requestData.testId} 639 onChange={(e) => setRequestData({ ...requestData, testId: e.target.value })} 640 className="w-full px-4 py-2 border rounded-lg" 641 required 642 > 643 <option value="">Select a test</option> 644 {availableTests.map((test) => ( 645 <option key={test.testId} value={test.testId}> 646 {test.testName} (${test.cost}) 647 </option> 648 ))} 649 </select> 650 </div> 651 <div> 652 <label className="block text-sm font-semibold mb-2">Test Date</label> 653 <input 654 type="date" 655 value={requestData.testDate} 656 onChange={(e) => setRequestData({ ...requestData, testDate: e.target.value })} 657 className="w-full px-4 py-2 border rounded-lg" 658 /> 659 </div> 660 </div> 661 <div> 662 <label className="block text-sm font-semibold mb-2">Notes</label> 663 <textarea 664 value={requestData.notes} 665 onChange={(e) => setRequestData({ ...requestData, notes: e.target.value })} 666 placeholder="Additional notes for the lab technician" 667 className="w-full px-4 py-2 border rounded-lg" 668 rows="3" 669 /> 670 </div> 671 <div className="flex gap-4"> 672 <button 673 type="submit" 674 disabled={loading} 675 className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700 disabled:bg-gray-400" 676 > 677 {loading ? 'Requesting...' : 'Request Test'} 678 </button> 679 <button 680 type="button" 681 onClick={() => setShowRequestForm(false)} 682 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500" 683 > 684 Cancel 685 </button> 686 </div> 687 </form> 688 </div> 689 )} 690 691 {/* Test Requests */} 692 {labTestRequests && labTestRequests.length > 0 && ( 693 <div className="bg-white rounded-lg shadow p-6"> 694 <h3 className="text-xl font-bold mb-4">Lab Test Requests</h3> 695 <div className="space-y-3"> 696 {labTestRequests.map((request) => ( 697 <div key={request.testId} className="border-l-4 border-purple-500 pl-4 py-2"> 698 <p className="font-semibold text-lg">{request.testName}</p> 699 <p className="text-sm text-gray-600">Requested by: {request.doctorName}</p> 700 <p className="text-sm text-gray-600">Test Date: {request.requestDate}</p> 701 {request.notes && ( 702 <p className="text-sm text-gray-600">Notes: {request.notes}</p> 703 )} 704 </div> 705 ))} 706 </div> 707 </div> 708 )} 709 710 {/* Lab Test Results */} 711 {labResults && labResults.length > 0 && ( 712 <div className="bg-white rounded-lg shadow p-6"> 713 <h3 className="text-xl font-bold mb-4">Lab Test Results</h3> 714 <div className="space-y-4"> 715 {labResults.map((result) => ( 716 <div key={result.resultId} className="border-l-4 border-green-500 pl-4 py-3 bg-green-50 rounded"> 717 <p className="font-semibold text-lg text-green-700">{result.testName}</p> 718 <p className="text-sm text-gray-700 mt-2"><strong>Results:</strong> {result.results}</p> 719 <p className="text-sm text-gray-600">Result Date: {result.resultDate}</p> 720 </div> 721 ))} 722 </div> 723 </div> 724 )} 725 726 {/* Link to submit results */} 727 {labTestRequests && labTestRequests.length > 0 && ( 728 <div className="bg-yellow-50 rounded-lg p-6"> 729 <h3 className="text-lg font-semibold text-yellow-800 mb-3">Lab Technician: Submit Test Results</h3> 730 <p className="text-sm text-gray-700 mb-4"> 731 {labTestRequests.length} test{labTestRequests.length !== 1 ? 's' : ''} awaiting results 732 </p> 733 <a 734 href="/lab-tests/results" 735 className="inline-block bg-yellow-600 text-white px-6 py-2 rounded hover:bg-yellow-700" 736 > 737 Submit Lab Results 738 </a> 739 </div> 740 )} 741 742 {/* No requests message */} 743 {(!labTestRequests || labTestRequests.length === 0) && !labResults?.length && ( 744 <div className="bg-blue-50 rounded-lg p-6 text-center"> 745 <p className="text-gray-600">No lab test requests for this patient</p> 746 </div> 520 747 )} 521 748 </div> 522 ); 523 } 524 525 return ( 526 <div> 527 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests</h1> 528 529 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 530 531 {/* Search Form */} 532 <div className="bg-white rounded-lg shadow p-6 mb-6"> 533 <h2 className="text-xl font-bold mb-4">Search Patient</h2> 534 <form onSubmit={handleSearch} className="space-y-4"> 535 <div className="flex gap-4"> 536 <div className="flex-1"> 537 <label className="block text-sm font-semibold mb-2">Patient EMBG</label> 538 <input 539 type="text" 540 value={embg} 541 onChange={(e) => setEmbg(e.target.value)} 542 placeholder="e.g., 1402994123456" 543 className="w-full px-4 py-2 border rounded-lg" 544 /> 545 </div> 546 <div className="flex items-end"> 547 <button 548 type="submit" 549 disabled={loading} 550 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400" 551 > 552 {loading ? 'Searching...' : 'Search'} 553 </button> 554 </div> 555 </div> 556 </form> 749 )} 750 751 {/* No search performed */} 752 {!searched && ( 753 <div className="bg-gray-50 rounded-lg p-12 text-center"> 754 <p className="text-gray-600 text-lg">Enter a patient EMBG to request lab tests</p> 557 755 </div> 558 559 {/* Patient Lab Tests */} 560 {searched && patient && ( 561 <div className="space-y-6"> 562 {/* Patient Info */} 563 <div className="bg-white rounded-lg shadow p-6"> 564 <div className="flex justify-between items-start mb-4"> 565 <h2 className="text-2xl font-bold">{patient.firstName} {patient.lastName}</h2> 566 {!showRequestForm && ( 567 <button 568 onClick={() => setShowRequestForm(true)} 569 className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700" 570 > 571 Request Lab Test 572 </button> 573 )} 574 </div> 575 <div className="grid grid-cols-4 gap-4"> 576 <div> 577 <p className="text-sm text-gray-600">EMBG</p> 578 <p className="font-semibold">{patient.embg}</p> 579 </div> 580 <div> 581 <p className="text-sm text-gray-600">Blood Type</p> 582 <p className="font-semibold">{patient.bloodType || 'N/A'}</p> 583 </div> 584 <div> 585 <p className="text-sm text-gray-600">Date of Birth</p> 586 <p className="font-semibold">{patient.dateOfBirth}</p> 587 </div> 588 </div> 589 </div> 590 591 {/* Request Lab Test Form */} 592 {showRequestForm && ( 593 <div className="bg-white rounded-lg shadow p-6"> 594 <h3 className="text-xl font-bold mb-4">Request Lab Test</h3> 595 <form onSubmit={handleRequestTest} className="space-y-4"> 596 <div className="grid grid-cols-2 gap-4"> 597 <div> 598 <label className="block text-sm font-semibold mb-2">Test</label> 599 <select 600 value={requestData.testId} 601 onChange={(e) => setRequestData({ ...requestData, testId: e.target.value })} 602 className="w-full px-4 py-2 border rounded-lg" 603 required 604 > 605 <option value="">Select a test</option> 606 {availableTests.map((test) => ( 607 <option key={test.testId} value={test.testId}> 608 {test.testName} (${test.cost}) 609 </option> 610 ))} 611 </select> 612 </div> 613 <div> 614 <label className="block text-sm font-semibold mb-2">Test Date</label> 615 <input 616 type="date" 617 value={requestData.testDate} 618 onChange={(e) => setRequestData({ ...requestData, testDate: e.target.value })} 619 className="w-full px-4 py-2 border rounded-lg" 620 /> 621 </div> 622 </div> 623 <div> 624 <label className="block text-sm font-semibold mb-2">Notes</label> 625 <textarea 626 value={requestData.notes} 627 onChange={(e) => setRequestData({ ...requestData, notes: e.target.value })} 628 placeholder="Additional notes for the lab technician" 629 className="w-full px-4 py-2 border rounded-lg" 630 rows="3" 631 /> 632 </div> 633 <div className="flex gap-4"> 634 <button 635 type="submit" 636 disabled={loading} 637 className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700 disabled:bg-gray-400" 638 > 639 {loading ? 'Requesting...' : 'Request Test'} 640 </button> 641 <button 642 type="button" 643 onClick={() => setShowRequestForm(false)} 644 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500" 645 > 646 Cancel 647 </button> 648 </div> 649 </form> 650 </div> 651 )} 652 653 {/* Test Requests */} 654 {labTestRequests && labTestRequests.length > 0 && ( 655 <div className="bg-white rounded-lg shadow p-6"> 656 <h3 className="text-xl font-bold mb-4">Lab Test Requests</h3> 657 <div className="space-y-3"> 658 {labTestRequests.map((request) => ( 659 <div key={request.testId} className="border-l-4 border-purple-500 pl-4 py-2"> 660 <p className="font-semibold text-lg">{request.testName}</p> 661 <p className="text-sm text-gray-600">Requested by: {request.doctorName}</p> 662 <p className="text-sm text-gray-600">Test Date: {request.requestDate}</p> 663 {request.notes && ( 664 <p className="text-sm text-gray-600">Notes: {request.notes}</p> 665 )} 666 </div> 667 ))} 668 </div> 669 </div> 670 )} 671 672 {/* Lab Test Results */} 673 {labResults && labResults.length > 0 && ( 674 <div className="bg-white rounded-lg shadow p-6"> 675 <h3 className="text-xl font-bold mb-4">Lab Test Results</h3> 676 <div className="space-y-4"> 677 {labResults.map((result) => ( 678 <div key={result.resultId} className="border-l-4 border-green-500 pl-4 py-3 bg-green-50 rounded"> 679 <p className="font-semibold text-lg text-green-700">{result.testName}</p> 680 <p className="text-sm text-gray-700 mt-2"><strong>Results:</strong> {result.results}</p> 681 <p className="text-sm text-gray-600">Result Date: {result.resultDate}</p> 682 </div> 683 ))} 684 </div> 685 </div> 686 )} 687 688 {/* Link to submit results */} 689 {labTestRequests && labTestRequests.length > 0 && ( 690 <div className="bg-yellow-50 rounded-lg p-6"> 691 <h3 className="text-lg font-semibold text-yellow-800 mb-3">Lab Technician: Submit Test Results</h3> 692 <p className="text-sm text-gray-700 mb-4"> 693 {labTestRequests.length} test{labTestRequests.length !== 1 ? 's' : ''} awaiting results 694 </p> 695 <a 696 href="/lab-tests/results" 697 className="inline-block bg-yellow-600 text-white px-6 py-2 rounded hover:bg-yellow-700" 698 > 699 Submit Lab Results 700 </a> 701 </div> 702 )} 703 704 {/* No requests message */} 705 {!labTestRequests || labTestRequests.length === 0 && !labResults?.length && ( 706 <div className="bg-blue-50 rounded-lg p-6 text-center"> 707 <p className="text-gray-600">No lab test requests for this patient</p> 708 </div> 709 )} 710 </div> 711 )} 712 713 {/* No search performed */} 714 {!searched && ( 715 <div className="bg-gray-50 rounded-lg p-12 text-center"> 716 <p className="text-gray-600 text-lg">Enter a patient EMBG to request lab tests</p> 717 </div> 718 )} 719 </div> 756 )} 757 </div> 720 758 ); 721 759 }
Note:
See TracChangeset
for help on using the changeset viewer.
