- Timestamp:
- 05/29/26 20:18:54 (4 months ago)
- Branches:
- master
- Children:
- b63d7b5
- Parents:
- f18b11b
- Location:
- frontend/src
- Files:
-
- 2 edited
-
pages/lab-tests/LabTestList.js (modified) (5 diffs)
-
services/labService.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
frontend/src/pages/lab-tests/LabTestList.js
rf18b11b r6c00695 18 18 const [labResults, setLabResults] = useState([]); 19 19 const [pendingTests, setPendingTests] = useState([]); 20 const [submittedTests, setSubmittedTests] = useState([]); 20 21 const [selectedTest, setSelectedTest] = useState(null); 21 22 … … 32 33 }); 33 34 35 // Filter states for pending tests 36 const [pendingFilters, setPendingFilters] = useState({ 37 testName: '', 38 patientName: '', 39 testDate: '', 40 }); 41 42 // Filter states for submitted tests 43 const [submittedFilters, setSubmittedFilters] = useState({ 44 testName: '', 45 patientName: '', 46 testDate: '', 47 }); 48 34 49 useEffect(() => { 35 50 if (isLabTechnician) { 36 load PendingTests();51 loadLabTestsData(); 37 52 } 38 53 }, [isLabTechnician]); 39 54 40 const load PendingTests= async () => {55 const loadLabTestsData = async () => { 41 56 try { 42 57 setLoading(true); 43 const response = await labService.getPendingLabTests(); 44 setPendingTests(response.data || []); 58 setError(null); 59 60 // Load pending tests 61 const pendingRes = await labService.getPendingLabTests(); 62 setPendingTests(pendingRes.data || []); 63 64 // Try to load submitted tests, but don't fail if this endpoint doesn't exist 65 try { 66 const submittedRes = await labService.getAllSubmittedLabResults(); 67 setSubmittedTests(submittedRes.data || []); 68 } catch (err) { 69 console.warn('Could not load submitted tests:', err); 70 setSubmittedTests([]); 71 } 45 72 } catch (err) { 46 73 setError('Failed to load pending lab tests'); 74 setPendingTests([]); 47 75 } finally { 48 76 setLoading(false); … … 169 197 170 198 // Refresh pending tests 171 await load PendingTests();199 await loadLabTestsData(); 172 200 173 201 // Reset form … … 181 209 setError(null); 182 210 alert('Lab result submitted successfully!'); 211 212 // Refresh the lab tests data 213 await loadLabTestsData(); 214 215 // Reset form 216 setSelectedTest(null); 217 setSubmitFormData({ 218 results: '', 219 resultDate: new Date().toISOString().split('T')[0], 220 }); 183 221 } catch (err) { 184 222 setError('Failed to submit lab result: ' + (err.response?.data?.error || err.message)); … … 190 228 if (isLabTechnician) { 191 229 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>} 245 </div> 246 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 /> 258 </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> 311 </div> 312 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"> 509 <span className="px-3 py-1 bg-green-100 text-green-800 rounded-full text-sm font-semibold"> 510 Submitted 511 </span> 512 </td> 513 </tr> 514 ))} 515 </tbody> 516 </table> 517 </> 518 )} 519 </div> 520 )} 521 </div> 522 ); 523 } 524 525 return ( 192 526 <div> 193 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests - Submit Results</h1>527 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests</h1> 194 528 195 529 {error && <ErrorAlert message={error} onClose={() => setError(null)} />} 196 530 197 {selectedTest ? ( 198 // Submit Result Form 199 <div className="bg-white rounded-lg shadow p-6 mb-6"> 200 <h2 className="text-xl font-bold mb-4">Submit Lab Result</h2> 201 <div className="mb-4 p-4 bg-gray-50 rounded-lg"> 202 <p className="mb-2"><strong>Test:</strong> {selectedTest.testName}</p> 203 <p className="mb-2"><strong>Patient:</strong> {selectedTest.patientName}</p> 204 <p className="mb-2"><strong>Doctor:</strong> {selectedTest.doctorName}</p> 205 <p className="mb-2"><strong>Test Date:</strong> {selectedTest.testDate}</p> 206 {selectedTest.notes && <p><strong>Notes:</strong> {selectedTest.notes}</p>} 207 </div> 208 209 <form onSubmit={handleSubmitResult} className="space-y-4"> 210 <div> 211 <label className="block text-sm font-semibold mb-2">Test Results *</label> 212 <textarea 213 value={submitFormData.results} 214 onChange={(e) => setSubmitFormData({ ...submitFormData, results: e.target.value })} 215 placeholder="Enter detailed test results" 216 className="w-full px-4 py-2 border rounded-lg" 217 rows="4" 218 required 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" 219 544 /> 220 545 </div> 221 222 <div> 223 <label className="block text-sm font-semibold mb-2">Result Date *</label> 224 <input 225 type="date" 226 value={submitFormData.resultDate} 227 onChange={(e) => setSubmitFormData({ ...submitFormData, resultDate: e.target.value })} 228 className="w-full px-4 py-2 border rounded-lg" 229 required 230 /> 231 </div> 232 233 <div className="flex gap-4"> 546 <div className="flex items-end"> 234 547 <button 235 type="submit" 236 disabled={loading} 237 style={{ 238 background: loading ? '#d1d5db' : '#bfdbfe', 239 color: '#1e1035', 240 padding: '8px 24px', 241 borderRadius: '6px', 242 border: 'none', 243 cursor: loading ? 'not-allowed' : 'pointer', 244 fontSize: '14px', 245 fontWeight: '400' 246 }} 247 onMouseEnter={(e) => !loading && (e.currentTarget.style.background = '#93c5fd')} 248 onMouseLeave={(e) => !loading && (e.currentTarget.style.background = '#bfdbfe')} 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" 249 551 > 250 {loading ? 'Submitting...' : 'Submit Result'} 251 </button> 252 <button 253 type="button" 254 onClick={() => { 255 setSelectedTest(null); 256 setSubmitFormData({ 257 results: '', 258 resultDate: new Date().toISOString().split('T')[0], 259 }); 260 }} 261 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500" 262 > 263 Cancel 552 {loading ? 'Searching...' : 'Search'} 264 553 </button> 265 554 </div> 266 </form>267 </div>268 ) : (269 // Pending Tests List270 <div className="bg-white rounded-lg shadow overflow-hidden">271 <div className="p-6 border-b">272 <h2 className="text-xl font-bold">Pending Lab Tests ({pendingTests.length})</h2>273 555 </div> 274 275 {pendingTests.length === 0 ? ( 276 <div className="p-6 text-center text-gray-600"> 277 No pending lab tests 556 </form> 557 </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> 278 589 </div> 279 ) : ( 280 <table className="w-full"> 281 <thead className="bg-gray-100"> 282 <tr> 283 <th className="px-6 py-3 text-left text-sm font-semibold">Test</th> 284 <th className="px-6 py-3 text-left text-sm font-semibold">Patient</th> 285 <th className="px-6 py-3 text-left text-sm font-semibold">Doctor</th> 286 <th className="px-6 py-3 text-left text-sm font-semibold">Requested</th> 287 <th className="px-6 py-3 text-left text-sm font-semibold">Test Date</th> 288 <th className="px-6 py-3 text-left text-sm font-semibold">Notes</th> 289 <th className="px-6 py-3 text-left text-sm font-semibold">Action</th> 290 </tr> 291 </thead> 292 <tbody> 293 {pendingTests.map((test, index) => ( 294 <tr key={index} className="border-t hover:bg-gray-50"> 295 <td className="px-6 py-3 font-medium">{test.testName}</td> 296 <td className="px-6 py-3">{test.patientName}</td> 297 <td className="px-6 py-3">{test.doctorName}</td> 298 <td className="px-6 py-3 text-green-600">{test.requestDate}</td> 299 <td className="px-6 py-3 text-purple-600">{test.testDate}</td> 300 <td className="px-6 py-3 text-gray-600 text-sm">{test.notes || '-'}</td> 301 <td className="px-6 py-3"> 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"> 302 634 <button 303 onClick={() => setSelectedTest(test)} 304 style={{ 305 background: '#bfdbfe', 306 color: '#1e1035', 307 padding: '6px 12px', 308 borderRadius: '4px', 309 border: 'none', 310 cursor: 'pointer', 311 fontSize: '12px', 312 fontWeight: '400' 313 }} 314 onMouseEnter={(e) => e.currentTarget.style.background = '#93c5fd'} 315 onMouseLeave={(e) => e.currentTarget.style.background = '#bfdbfe'} 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" 316 638 > 317 Submit Result639 {loading ? 'Requesting...' : 'Request Test'} 318 640 </button> 319 </td> 320 </tr> 321 ))} 322 </tbody> 323 </table> 324 )} 325 </div> 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> 326 718 )} 327 719 </div> 328 );329 }330 331 return (332 <div>333 <h1 className="text-3xl font-bold mb-6" style={{ color: '#7c3aed' }}>Lab Tests</h1>334 335 {error && <ErrorAlert message={error} onClose={() => setError(null)} />}336 337 {/* Search Form */}338 <div className="bg-white rounded-lg shadow p-6 mb-6">339 <h2 className="text-xl font-bold mb-4">Search Patient</h2>340 <form onSubmit={handleSearch} className="space-y-4">341 <div className="flex gap-4">342 <div className="flex-1">343 <label className="block text-sm font-semibold mb-2">Patient EMBG</label>344 <input345 type="text"346 value={embg}347 onChange={(e) => setEmbg(e.target.value)}348 placeholder="e.g., 1402994123456"349 className="w-full px-4 py-2 border rounded-lg"350 />351 </div>352 <div className="flex items-end">353 <button354 type="submit"355 disabled={loading}356 className="bg-purple-600 text-white px-8 py-2 rounded-full hover:bg-purple-700 disabled:bg-gray-400"357 >358 {loading ? 'Searching...' : 'Search'}359 </button>360 </div>361 </div>362 </form>363 </div>364 365 {/* Patient Lab Tests */}366 {searched && patient && (367 <div className="space-y-6">368 {/* Patient Info */}369 <div className="bg-white rounded-lg shadow p-6">370 <div className="flex justify-between items-start mb-4">371 <h2 className="text-2xl font-bold">{patient.firstName} {patient.lastName}</h2>372 {!showRequestForm && (373 <button374 onClick={() => setShowRequestForm(true)}375 className="bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700"376 >377 Request Lab Test378 </button>379 )}380 </div>381 <div className="grid grid-cols-4 gap-4">382 <div>383 <p className="text-sm text-gray-600">EMBG</p>384 <p className="font-semibold">{patient.embg}</p>385 </div>386 <div>387 <p className="text-sm text-gray-600">Blood Type</p>388 <p className="font-semibold">{patient.bloodType || 'N/A'}</p>389 </div>390 <div>391 <p className="text-sm text-gray-600">Date of Birth</p>392 <p className="font-semibold">{patient.dateOfBirth}</p>393 </div>394 </div>395 </div>396 397 {/* Request Lab Test Form */}398 {showRequestForm && (399 <div className="bg-white rounded-lg shadow p-6">400 <h3 className="text-xl font-bold mb-4">Request Lab Test</h3>401 <form onSubmit={handleRequestTest} className="space-y-4">402 <div className="grid grid-cols-2 gap-4">403 <div>404 <label className="block text-sm font-semibold mb-2">Test</label>405 <select406 value={requestData.testId}407 onChange={(e) => setRequestData({ ...requestData, testId: e.target.value })}408 className="w-full px-4 py-2 border rounded-lg"409 required410 >411 <option value="">Select a test</option>412 {availableTests.map((test) => (413 <option key={test.testId} value={test.testId}>414 {test.testName} (${test.cost})415 </option>416 ))}417 </select>418 </div>419 <div>420 <label className="block text-sm font-semibold mb-2">Test Date</label>421 <input422 type="date"423 value={requestData.testDate}424 onChange={(e) => setRequestData({ ...requestData, testDate: e.target.value })}425 className="w-full px-4 py-2 border rounded-lg"426 />427 </div>428 </div>429 <div>430 <label className="block text-sm font-semibold mb-2">Notes</label>431 <textarea432 value={requestData.notes}433 onChange={(e) => setRequestData({ ...requestData, notes: e.target.value })}434 placeholder="Additional notes for the lab technician"435 className="w-full px-4 py-2 border rounded-lg"436 rows="3"437 />438 </div>439 <div className="flex gap-4">440 <button441 type="submit"442 disabled={loading}443 className="bg-green-600 text-white px-6 py-2 rounded hover:bg-green-700 disabled:bg-gray-400"444 >445 {loading ? 'Requesting...' : 'Request Test'}446 </button>447 <button448 type="button"449 onClick={() => setShowRequestForm(false)}450 className="bg-gray-400 text-white px-6 py-2 rounded hover:bg-gray-500"451 >452 Cancel453 </button>454 </div>455 </form>456 </div>457 )}458 459 {/* Test Requests */}460 {labTestRequests && labTestRequests.length > 0 && (461 <div className="bg-white rounded-lg shadow p-6">462 <h3 className="text-xl font-bold mb-4">Lab Test Requests</h3>463 <div className="space-y-3">464 {labTestRequests.map((request) => (465 <div key={request.testId} className="border-l-4 border-purple-500 pl-4 py-2">466 <p className="font-semibold text-lg">{request.testName}</p>467 <p className="text-sm text-gray-600">Requested by: {request.doctorName}</p>468 <p className="text-sm text-gray-600">Test Date: {request.requestDate}</p>469 {request.notes && (470 <p className="text-sm text-gray-600">Notes: {request.notes}</p>471 )}472 </div>473 ))}474 </div>475 </div>476 )}477 478 {/* Lab Test Results */}479 {labResults && labResults.length > 0 && (480 <div className="bg-white rounded-lg shadow p-6">481 <h3 className="text-xl font-bold mb-4">Lab Test Results</h3>482 <div className="space-y-4">483 {labResults.map((result) => (484 <div key={result.resultId} className="border-l-4 border-green-500 pl-4 py-3 bg-green-50 rounded">485 <p className="font-semibold text-lg text-green-700">{result.testName}</p>486 <p className="text-sm text-gray-700 mt-2"><strong>Results:</strong> {result.results}</p>487 <p className="text-sm text-gray-600">Result Date: {result.resultDate}</p>488 </div>489 ))}490 </div>491 </div>492 )}493 494 {/* Link to submit results */}495 {labTestRequests && labTestRequests.length > 0 && (496 <div className="bg-yellow-50 rounded-lg p-6">497 <h3 className="text-lg font-semibold text-yellow-800 mb-3">Lab Technician: Submit Test Results</h3>498 <p className="text-sm text-gray-700 mb-4">499 {labTestRequests.length} test{labTestRequests.length !== 1 ? 's' : ''} awaiting results500 </p>501 <a502 href="/lab-tests/results"503 className="inline-block bg-yellow-600 text-white px-6 py-2 rounded hover:bg-yellow-700"504 >505 Submit Lab Results506 </a>507 </div>508 )}509 510 {/* No requests message */}511 {!labTestRequests || labTestRequests.length === 0 && !labResults?.length && (512 <div className="bg-blue-50 rounded-lg p-6 text-center">513 <p className="text-gray-600">No lab test requests for this patient</p>514 </div>515 )}516 </div>517 )}518 519 {/* No search performed */}520 {!searched && (521 <div className="bg-gray-50 rounded-lg p-12 text-center">522 <p className="text-gray-600 text-lg">Enter a patient EMBG to request lab tests</p>523 </div>524 )}525 </div>526 720 ); 527 721 } -
frontend/src/services/labService.js
rf18b11b r6c00695 47 47 }, 48 48 49 // Get pending lab tests (for lab technicians)49 // Get pending lab tests for lab technicians 50 50 getPendingLabTests: () => { 51 51 return api.get('/lab-tests/requests/pending'); 52 }, 53 54 // Get all submitted lab results for lab technicians 55 getAllSubmittedLabResults: () => { 56 return api.get('/lab-tests/results'); 52 57 }, 53 58 };
Note:
See TracChangeset
for help on using the changeset viewer.
