source: frontend/src/pages/medical-records/MedicalRecordDetail.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 42.3 KB
Line 
1import React, { useState, useEffect, useCallback } from 'react';
2import { useParams, useNavigate } from 'react-router-dom';
3import { medicalRecordService } from '../../services/medicalRecordService';
4import { doctorService } from '../../services/doctorService';
5import { labService } from '../../services/labService';
6import { procedureService } from '../../services/procedureService';
7import ErrorAlert from '../../components/ErrorAlert';
8import SuccessAlert from '../../components/SuccessAlert';
9import Loading from '../../components/Loading';
10import apiClient from '../../services/api';
11
12function MedicalRecordDetail() {
13 const { id } = useParams();
14 const navigate = useNavigate();
15 const user = JSON.parse(localStorage.getItem('user') || '{}');
16 const isDoctor = user.role === 'DOCTOR';
17
18 const [record, setRecord] = useState(null);
19 const [loading, setLoading] = useState(true);
20 const [error, setError] = useState(null);
21 const [success, setSuccess] = useState(null);
22 const [activeTab, setActiveTab] = useState('view');
23 const [doctors, setDoctors] = useState([]);
24 const [selectedDoctorId, setSelectedDoctorId] = useState(isDoctor ? user.doctorId : '');
25
26 // Diagnosis form
27 const [diagnosisForm, setDiagnosisForm] = useState({
28 diagnosisId: '',
29 doctorId: '',
30 });
31
32 // Symptoms form
33 const [symptomsForm, setSymptomsForm] = useState({
34 symptomId: '',
35 });
36
37 // Allergies form
38 const [allergyForm, setAllergyForm] = useState({
39 allergyId: '',
40 severity: 'MEDIUM',
41 reaction: '',
42 });
43
44 // Prescription form
45 const [prescriptionForm, setPrescriptionForm] = useState({
46 prescriptionId: '',
47 dosage: '',
48 frequency: '',
49 duration: '',
50 reason: '',
51 });
52
53 // Dropdown options
54 const [diagnoses, setDiagnoses] = useState([]);
55 const [symptoms, setSymptoms] = useState([]);
56 const [allergies, setAllergies] = useState([]);
57 const [prescriptions, setPrescriptions] = useState([]);
58 const [labTests, setLabTests] = useState([]);
59 const [procedures, setProcedures] = useState([]);
60
61 // Lab test form
62 const [labTestForm, setLabTestForm] = useState({
63 testId: '',
64 testDate: new Date().toISOString().split('T')[0],
65 notes: '',
66 });
67
68 // Procedure form
69 const [procedureForm, setProcedureForm] = useState({
70 procedureId: '',
71 procedureDate: new Date().toISOString().split('T')[0],
72 notes: '',
73 diagnosisId: '',
74 });
75
76 const [labResults, setLabResults] = useState([]);
77 const [procedureResults, setProcedureResults] = useState([]);
78
79 const fetchRecord = useCallback(async () => {
80 try {
81 setLoading(true);
82 const response = await medicalRecordService.getMedicalRecordByPatientId(id);
83 setRecord(response.data);
84
85 // Fetch lab results
86 try {
87 const resultsResponse = await labService.getLabResultsForMedicalRecord(response.data.recordId);
88 setLabResults(resultsResponse.data || []);
89 } catch (err) {
90 console.error('Failed to fetch lab results:', err);
91 setLabResults([]);
92 }
93
94 // Fetch procedure results
95 try {
96 const procedureResultsResponse = await procedureService.getProcedureResultsForMedicalRecord(response.data.recordId);
97 setProcedureResults(procedureResultsResponse.data || []);
98 } catch (err) {
99 console.error('Failed to fetch procedure results:', err);
100 setProcedureResults([]);
101 }
102 } catch (err) {
103 setError('Failed to fetch medical record');
104 console.error(err);
105 } finally {
106 setLoading(false);
107 }
108 }, [id]);
109
110 useEffect(() => {
111 const fetchDoctors = async () => {
112 try {
113 if (!isDoctor) {
114 const response = await doctorService.getAllDoctors();
115 setDoctors(response.data);
116 if (response.data.length > 0 && !selectedDoctorId) {
117 setSelectedDoctorId(response.data[0].doctorId);
118 }
119 }
120 } catch (err) {
121 console.error('Failed to fetch doctors', err);
122 }
123 };
124
125 const fetchDropdownOptions = async () => {
126 try {
127 console.log('Fetching dropdown options...');
128 const diagnosesRes = await apiClient.get('/medical-records/dropdown/diagnoses');
129 console.log('Diagnoses:', diagnosesRes.data);
130
131 const symptomsRes = await apiClient.get('/medical-records/dropdown/symptoms');
132 console.log('Symptoms:', symptomsRes.data);
133
134 const allergiesRes = await apiClient.get('/medical-records/dropdown/allergies');
135 console.log('Allergies:', allergiesRes.data);
136
137 const prescriptionsRes = await apiClient.get('/medical-records/dropdown/prescriptions');
138 console.log('Prescriptions:', prescriptionsRes.data);
139
140 const testsRes = await labService.getAllLabTests();
141 console.log('Lab Tests:', testsRes.data);
142
143 const proceduresRes = await procedureService.getAllProcedures();
144 console.log('Procedures:', proceduresRes.data);
145
146 setDiagnoses(diagnosesRes.data || []);
147 setSymptoms(symptomsRes.data || []);
148 setAllergies(allergiesRes.data || []);
149 setPrescriptions(prescriptionsRes.data || []);
150 setLabTests(testsRes.data || []);
151 setProcedures(proceduresRes.data || []);
152 console.log('Dropdown options set successfully');
153 } catch (err) {
154 console.error('Error fetching dropdown options:', err);
155 setError('Failed to load dropdown options: ' + (err.response?.data?.error || err.message));
156 }
157 };
158
159 fetchRecord();
160 fetchDoctors();
161 fetchDropdownOptions();
162 }, [id, isDoctor, fetchRecord, selectedDoctorId]);
163
164 const handleAddDiagnosis = async (e) => {
165 e.preventDefault();
166 try {
167 if (!diagnosisForm.diagnosisId) {
168 setError('Please select a diagnosis');
169 return;
170 }
171 if (!selectedDoctorId) {
172 setError('Please select a doctor');
173 return;
174 }
175 const selectedDiagnosis = diagnoses.find(d => d.id === parseInt(diagnosisForm.diagnosisId));
176 await apiClient.post('/diagnoses', {
177 patientId: parseInt(record.patientId),
178 doctorId: parseInt(selectedDoctorId),
179 name: selectedDiagnosis.name,
180 description: '',
181 });
182 setSuccess('Diagnosis recorded successfully');
183 setDiagnosisForm({ diagnosisId: '', doctorId: '' });
184 setTimeout(() => fetchRecord(), 1000);
185 } catch (err) {
186 setError(err.response?.data?.error || 'Failed to record diagnosis');
187 }
188 };
189
190 const handleAddSymptom = async (e) => {
191 e.preventDefault();
192 try {
193 if (!symptomsForm.symptomId) {
194 setError('Please select a symptom');
195 return;
196 }
197 await apiClient.post(`/medical-records/${record.recordId}/symptoms`, {
198 symptomId: parseInt(symptomsForm.symptomId),
199 severity: 'MEDIUM',
200 });
201 setSuccess('Symptom recorded successfully');
202 setSymptomsForm({ symptomId: '' });
203 setTimeout(() => fetchRecord(), 1000);
204 } catch (err) {
205 setError(err.response?.data?.error || 'Failed to record symptom');
206 }
207 };
208
209 const handleAddAllergy = async (e) => {
210 e.preventDefault();
211 try {
212 if (!allergyForm.allergyId) {
213 setError('Please select an allergy');
214 return;
215 }
216 await apiClient.post(`/medical-records/${record.recordId}/allergies`, {
217 allergyId: parseInt(allergyForm.allergyId),
218 severity: allergyForm.severity,
219 reaction: allergyForm.reaction,
220 });
221 setSuccess('Allergy recorded successfully');
222 setAllergyForm({ allergyId: '', severity: 'MEDIUM', reaction: '' });
223 setTimeout(() => fetchRecord(), 1000);
224 } catch (err) {
225 setError(err.response?.data?.error || 'Failed to record allergy');
226 }
227 };
228
229 const handleAddPrescription = async (e) => {
230 e.preventDefault();
231 try {
232 if (!prescriptionForm.prescriptionId) {
233 setError('Please select a prescription medication');
234 return;
235 }
236 if (!prescriptionForm.dosage.trim()) {
237 setError('Dosage is required');
238 return;
239 }
240 if (!prescriptionForm.frequency.trim()) {
241 setError('Frequency is required');
242 return;
243 }
244 if (!prescriptionForm.duration.trim()) {
245 setError('Duration is required');
246 return;
247 }
248 const selectedPrescription = prescriptions.find(p => p.id === parseInt(prescriptionForm.prescriptionId));
249 await apiClient.post('/prescriptions', {
250 medicalRecordId: record.recordId,
251 medicationName: selectedPrescription.name,
252 dosage: prescriptionForm.dosage,
253 frequency: prescriptionForm.frequency,
254 duration: prescriptionForm.duration,
255 notes: prescriptionForm.reason,
256 });
257 setSuccess('Prescription recorded successfully');
258 setPrescriptionForm({
259 prescriptionId: '',
260 dosage: '',
261 frequency: '',
262 duration: '',
263 reason: '',
264 });
265 setTimeout(() => fetchRecord(), 1000);
266 } catch (err) {
267 setError(err.response?.data?.error || 'Failed to record prescription');
268 }
269 };
270
271 if (loading) return <Loading />;
272
273 if (!record) {
274 return (
275 <div>
276 <ErrorAlert message="Medical record not found" onClose={() => navigate('/medical-records')} />
277 </div>
278 );
279 }
280
281 return (
282 <div>
283 <div className="flex justify-between items-center mb-6">
284 <h1 style={{ fontSize: '36px', fontWeight: 'normal' }}>Medical Record</h1>
285 <button
286 onClick={() => navigate('/medical-records')}
287 className="bg-gray-300 text-gray-700 px-4 py-2 rounded hover:bg-gray-400"
288 >
289 Back
290 </button>
291 </div>
292
293 {error && <ErrorAlert message={error} onClose={() => setError(null)} />}
294 {success && <SuccessAlert message={success} onClose={() => setSuccess(null)} />}
295
296 {/* Patient Info */}
297 <div className="bg-white rounded-lg shadow p-6 mb-6">
298 <h2 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Patient Information</h2>
299 <div className="grid grid-cols-4 gap-4">
300 <div>
301 <p className="font-normal text-sm text-gray-600">Patient Name</p>
302 <p className="font-normal">{record.patientName}</p>
303 </div>
304 <div>
305 <p className="font-normal text-sm text-gray-600">EMBG</p>
306 <p className="font-normal">{record.embg}</p>
307 </div>
308 <div>
309 <p className="font-normal text-sm text-gray-600">Record ID</p>
310 <p className="font-normal">{record.recordId}</p>
311 </div>
312 <div>
313 <p className="font-normal text-sm text-gray-600">Patient ID</p>
314 <p className="font-normal">{record.patientId}</p>
315 </div>
316 </div>
317 </div>
318
319 {/* Tabs */}
320 <div className="flex gap-2 mb-6 border-b">
321 <button
322 onClick={() => setActiveTab('view')}
323 style={{ fontWeight: 'normal' }}
324 className={`px-6 py-3 ${
325 activeTab === 'view'
326 ? 'border-b-2 border-purple-600 text-purple-600'
327 : 'text-purple-600 hover:text-purple-700'
328 }`}
329 >
330 View Medical Data
331 </button>
332 <span className="text-gray-600 px-3" style={{ fontWeight: 'normal' }}>|</span>
333 <button
334 onClick={() => setActiveTab('diagnosis')}
335 style={{ fontWeight: 'normal' }}
336 className={`px-6 py-3 ${
337 activeTab === 'diagnosis'
338 ? 'border-b-2 border-purple-600 text-purple-600'
339 : 'text-purple-600 hover:text-purple-700'
340 }`}
341 >
342 Record Diagnosis
343 </button>
344 <span className="text-gray-600 px-3" style={{ fontWeight: 'normal' }}>|</span>
345 <button
346 onClick={() => setActiveTab('symptoms')}
347 style={{ fontWeight: 'normal' }}
348 className={`px-6 py-3 ${
349 activeTab === 'symptoms'
350 ? 'border-b-2 border-purple-600 text-purple-600'
351 : 'text-purple-600 hover:text-purple-700'
352 }`}
353 >
354 Record Symptoms
355 </button>
356 <span className="text-gray-600 px-3" style={{ fontWeight: 'normal' }}>|</span>
357 <button
358 onClick={() => setActiveTab('allergies')}
359 style={{ fontWeight: 'normal' }}
360 className={`px-6 py-3 ${
361 activeTab === 'allergies'
362 ? 'border-b-2 border-purple-600 text-purple-600'
363 : 'text-purple-600 hover:text-purple-700'
364 }`}
365 >
366 Record Allergies
367 </button>
368 <span className="text-gray-600 px-3" style={{ fontWeight: 'normal' }}>|</span>
369 <button
370 onClick={() => setActiveTab('prescription')}
371 style={{ fontWeight: 'normal' }}
372 className={`px-6 py-3 ${
373 activeTab === 'prescription'
374 ? 'border-b-2 border-purple-600 text-purple-600'
375 : 'text-purple-600 hover:text-purple-700'
376 }`}
377 >
378 Record Prescription
379 </button>
380 <span className="text-gray-600 px-3" style={{ fontWeight: 'normal' }}>|</span>
381 <button
382 onClick={() => setActiveTab('lab-tests')}
383 style={{ fontWeight: 'normal' }}
384 className={`px-6 py-3 ${
385 activeTab === 'lab-tests'
386 ? 'border-b-2 border-purple-600 text-purple-600'
387 : 'text-purple-600 hover:text-purple-700'
388 }`}
389 >
390 Request Lab Test
391 </button>
392 <span className="text-gray-600 px-3" style={{ fontWeight: 'normal' }}>|</span>
393 <button
394 onClick={() => setActiveTab('procedures')}
395 style={{ fontWeight: 'normal' }}
396 className={`px-6 py-3 ${
397 activeTab === 'procedures'
398 ? 'border-b-2 border-purple-600 text-purple-600'
399 : 'text-purple-600 hover:text-purple-700'
400 }`}
401 >
402 Request Procedure
403 </button>
404 </div>
405
406 {/* View Medical Data Tab */}
407 {activeTab === 'view' && (
408 <div className="space-y-6">
409 {/* Diagnoses */}
410 {record.diagnoses && record.diagnoses.length > 0 && (
411 <div className="bg-white rounded-lg shadow p-6">
412 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Diagnoses</h3>
413 <div className="space-y-3">
414 {record.diagnoses.map((diagnosis) => (
415 <div key={diagnosis.diagnosisId} className="border-l-4 border-purple-500 pl-4 py-2">
416 <p className="font-normal" style={{ fontSize: '18px' }}>{diagnosis.name}</p>
417 {diagnosis.description && (
418 <p className="font-normal text-gray-600 text-sm">{diagnosis.description}</p>
419 )}
420 <p className="font-normal text-xs text-gray-500">By: {diagnosis.doctorName}</p>
421 </div>
422 ))}
423 </div>
424 </div>
425 )}
426
427 {/* Symptoms */}
428 {record.symptoms && record.symptoms.length > 0 && (
429 <div className="bg-white rounded-lg shadow p-6">
430 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Symptoms</h3>
431 <div className="flex flex-wrap gap-2">
432 {record.symptoms.map((symptom) => (
433 <span
434 key={symptom.symptomId}
435 className="bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full text-sm font-normal"
436 >
437 {symptom.symptomName}
438 </span>
439 ))}
440 </div>
441 </div>
442 )}
443
444 {/* Allergies */}
445 {record.allergies && record.allergies.length > 0 && (
446 <div className="bg-white rounded-lg shadow p-6">
447 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Allergies</h3>
448 <div className="space-y-3">
449 {record.allergies.map((allergy) => (
450 <div key={allergy.allergyId} className="border-l-4 border-red-500 pl-4 py-2">
451 <p className="font-normal">{allergy.allergyName}</p>
452 <p className="font-normal" style={{ fontSize: '14px' }}>
453 <span
454 className="font-normal"
455 style={{
456 padding: '4px 8px',
457 borderRadius: '4px',
458 color: 'white',
459 fontSize: '12px',
460 background: allergy.severity === 'CRITICAL' ? '#dc2626' : allergy.severity === 'HIGH' ? '#ef4444' : allergy.severity === 'MEDIUM' ? '#eab308' : '#22c55e'
461 }}
462 >
463 {allergy.severity} Severity
464 </span>
465 </p>
466 {allergy.reaction && (
467 <p className="font-normal text-gray-600 text-sm">Reaction: {allergy.reaction}</p>
468 )}
469 </div>
470 ))}
471 </div>
472 </div>
473 )}
474
475 {/* Prescriptions */}
476 {record.prescriptions && record.prescriptions.length > 0 && (
477 <div className="bg-white rounded-lg shadow p-6">
478 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Prescriptions</h3>
479 <div className="space-y-3">
480 {record.prescriptions.map((prescription) => (
481 <div key={prescription.prescriptionId} className="border-l-4 border-purple-500 pl-4 py-2">
482 <p className="font-normal">{prescription.medicationName}</p>
483 <p className="font-normal text-sm text-gray-600">Dosage: {prescription.dosage}</p>
484 <p className="font-normal text-sm text-gray-600">Frequency: {prescription.frequency}</p>
485 <p className="font-normal text-sm text-gray-600">Duration: {prescription.duration}</p>
486 </div>
487 ))}
488 </div>
489 </div>
490 )}
491
492 {/* Reports */}
493 {record.reports && record.reports.length > 0 && (
494 <div className="bg-white rounded-lg shadow p-6">
495 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Medical Reports</h3>
496 <div className="space-y-3">
497 {record.reports.map((report) => (
498 <div key={report.reportId} className="border-l-4 border-green-500 pl-4 py-2">
499 <p style={{ fontWeight: 'normal' }}>Report from {report.doctorName}</p>
500 <p className="font-normal text-gray-600 text-sm">{report.description}</p>
501 <p className="font-normal text-xs text-gray-500">Date: {report.reportDate}</p>
502 </div>
503 ))}
504 </div>
505 </div>
506 )}
507
508 {/* Lab Results */}
509 {labResults && labResults.length > 0 && (
510 <div className="bg-white rounded-lg shadow p-6">
511 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Lab Test Results</h3>
512 <div className="space-y-3">
513 {labResults.map((result) => (
514 <div key={result.resultId} className="border-l-4 border-cyan-500 pl-4 py-3 bg-cyan-50 rounded">
515 <p className="font-normal" style={{ fontSize: '18px', color: '#0891b2' }}>{result.testName}</p>
516 <p className="font-normal text-sm text-gray-700 mt-2">Results: {result.results}</p>
517 <p className="font-normal text-sm text-gray-600">Result Date: {result.resultDate}</p>
518 </div>
519 ))}
520 </div>
521 </div>
522 )}
523
524 {/* Procedure Results */}
525 {procedureResults && procedureResults.length > 0 && (
526 <div className="bg-white rounded-lg shadow p-6">
527 <h3 className="font-normal" style={{ fontSize: '20px', marginBottom: '16px' }}>Procedure Results</h3>
528 <div className="space-y-3">
529 {procedureResults.map((result) => (
530 <div key={result.resultId} className="border-l-4 border-orange-500 pl-4 py-3 bg-orange-50 rounded">
531 <p className="font-normal" style={{ fontSize: '18px', color: '#b45309' }}>{result.procedure?.procedureType || 'Procedure'}</p>
532 <p className="font-normal text-sm text-gray-700 mt-2">Outcome: {result.resultDescription}</p>
533 <p className="font-normal text-sm text-gray-600">Result Date: {result.resultDate}</p>
534 </div>
535 ))}
536 </div>
537 </div>
538 )}
539
540 {(!record.diagnoses || record.diagnoses.length === 0) &&
541 (!record.symptoms || record.symptoms.length === 0) &&
542 (!record.allergies || record.allergies.length === 0) &&
543 (!record.reports || record.reports.length === 0) &&
544 (!labResults || labResults.length === 0) &&
545 (!procedureResults || procedureResults.length === 0) && (
546 <div className="bg-blue-50 rounded-lg p-6 text-center">
547 <p className="text-gray-600">No medical data recorded yet</p>
548 </div>
549 )}
550 </div>
551 )}
552
553 {/* Record Diagnosis Tab */}
554 {activeTab === 'diagnosis' && (
555 <div className="bg-white rounded-lg shadow p-6">
556 <h3 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Record New Diagnosis</h3>
557 <form onSubmit={handleAddDiagnosis} className="space-y-4 max-w-2xl">
558 <div>
559 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Doctor *</label>
560 {isDoctor ? (
561 <p className="px-4 py-2 bg-gray-100 rounded-lg text-gray-700">
562 Dr. {user.firstName} {user.lastName}
563 </p>
564 ) : (
565 <select
566 value={selectedDoctorId}
567 onChange={(e) => setSelectedDoctorId(e.target.value)}
568 className="w-full px-4 py-2 border rounded-lg"
569 required
570 >
571 <option value="">Select doctor</option>
572 {doctors.map((doc) => (
573 <option key={doc.doctorId} value={doc.doctorId}>
574 Dr. {doc.firstName} {doc.lastName}
575 </option>
576 ))}
577 </select>
578 )}
579 </div>
580 <div>
581 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Diagnosis *</label>
582 <select
583 value={diagnosisForm.diagnosisId}
584 onChange={(e) => setDiagnosisForm({ ...diagnosisForm, diagnosisId: e.target.value })}
585 className="w-full px-4 py-2 border rounded-lg"
586 required
587 >
588 <option value="">{diagnoses.length === 0 ? 'Loading diagnoses...' : 'Select diagnosis'}</option>
589 {diagnoses && diagnoses.length > 0 && diagnoses.map((diagnosis) => (
590 <option key={diagnosis.id} value={diagnosis.id}>
591 {diagnosis.name}
592 </option>
593 ))}
594 </select>
595 {diagnoses.length === 0 && (
596 <p className="text-sm text-gray-500 mt-1">No diagnoses available. Please wait...</p>
597 )}
598 </div>
599 <button
600 type="submit"
601 style={{background: '#9333ea', color: 'white', padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '14px', fontWeight: '600'}} onMouseEnter={(e) => e.currentTarget.style.background = '#7e22ce'} onMouseLeave={(e) => e.currentTarget.style.background = '#9333ea'}
602 >
603 Record Diagnosis
604 </button>
605 </form>
606 </div>
607 )}
608
609 {/* Record Symptoms Tab */}
610 {activeTab === 'symptoms' && (
611 <div className="bg-white rounded-lg shadow p-6">
612 <h3 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Record Patient Symptoms</h3>
613 <form onSubmit={handleAddSymptom} className="space-y-4 max-w-2xl">
614 <div>
615 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Doctor *</label>
616 {isDoctor ? (
617 <p className="px-4 py-2 bg-gray-100 rounded-lg text-gray-700">
618 Dr. {user.firstName} {user.lastName}
619 </p>
620 ) : (
621 <select
622 value={selectedDoctorId}
623 onChange={(e) => setSelectedDoctorId(e.target.value)}
624 className="w-full px-4 py-2 border rounded-lg"
625 required
626 >
627 <option value="">Select doctor</option>
628 {doctors.map((doc) => (
629 <option key={doc.doctorId} value={doc.doctorId}>
630 Dr. {doc.firstName} {doc.lastName}
631 </option>
632 ))}
633 </select>
634 )}
635 </div>
636 <div>
637 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Symptom *</label>
638 <select
639 value={symptomsForm.symptomId}
640 onChange={(e) => setSymptomsForm({ ...symptomsForm, symptomId: e.target.value })}
641 className="w-full px-4 py-2 border rounded-lg"
642 required
643 >
644 <option value="">Select symptom</option>
645 {symptoms.filter(s => !record.symptoms?.some(rs => rs.symptomId === s.id)).map((symptom) => (
646 <option key={symptom.id} value={symptom.id}>
647 {symptom.name}
648 </option>
649 ))}
650 </select>
651 </div>
652 <button
653 type="submit"
654 style={{background: '#9333ea', color: 'white', padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '14px', fontWeight: '600'}} onMouseEnter={(e) => e.currentTarget.style.background = '#7e22ce'} onMouseLeave={(e) => e.currentTarget.style.background = '#9333ea'}
655 >
656 Record Symptom
657 </button>
658 </form>
659 </div>
660 )}
661
662 {/* Record Allergies Tab */}
663 {activeTab === 'allergies' && (
664 <div className="bg-white rounded-lg shadow p-6">
665 <h3 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Record Patient Allergies</h3>
666 <form onSubmit={handleAddAllergy} className="space-y-4 max-w-2xl">
667 <div>
668 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Doctor *</label>
669 {isDoctor ? (
670 <p className="px-4 py-2 bg-gray-100 rounded-lg text-gray-700">
671 Dr. {user.firstName} {user.lastName}
672 </p>
673 ) : (
674 <select
675 value={selectedDoctorId}
676 onChange={(e) => setSelectedDoctorId(e.target.value)}
677 className="w-full px-4 py-2 border rounded-lg"
678 required
679 >
680 <option value="">Select doctor</option>
681 {doctors.map((doc) => (
682 <option key={doc.doctorId} value={doc.doctorId}>
683 Dr. {doc.firstName} {doc.lastName}
684 </option>
685 ))}
686 </select>
687 )}
688 </div>
689 <div>
690 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Allergy *</label>
691 <select
692 value={allergyForm.allergyId}
693 onChange={(e) => setAllergyForm({ ...allergyForm, allergyId: e.target.value })}
694 className="w-full px-4 py-2 border rounded-lg"
695 required
696 >
697 <option value="">Select allergy</option>
698 {allergies.filter(a => !record.allergies?.some(ra => ra.allergyId === a.id)).map((allergy) => (
699 <option key={allergy.id} value={allergy.id}>
700 {allergy.name}
701 </option>
702 ))}
703 </select>
704 </div>
705 <div>
706 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Severity *</label>
707 <select
708 value={allergyForm.severity}
709 onChange={(e) => setAllergyForm({ ...allergyForm, severity: e.target.value })}
710 className="w-full px-4 py-2 border rounded-lg"
711 >
712 <option value="LOW">Low</option>
713 <option value="MEDIUM">Medium</option>
714 <option value="HIGH">High</option>
715 <option value="CRITICAL">Critical</option>
716 </select>
717 </div>
718 <div>
719 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Reaction</label>
720 <textarea
721 value={allergyForm.reaction}
722 onChange={(e) => setAllergyForm({ ...allergyForm, reaction: e.target.value })}
723 placeholder="Describe the allergic reaction"
724 className="w-full px-4 py-2 border rounded-lg"
725 rows="3"
726 />
727 </div>
728 <button
729 type="submit"
730 style={{background: '#9333ea', color: 'white', padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '14px', fontWeight: '600'}} onMouseEnter={(e) => e.currentTarget.style.background = '#7e22ce'} onMouseLeave={(e) => e.currentTarget.style.background = '#9333ea'}
731 >
732 Record Allergy
733 </button>
734 </form>
735 </div>
736 )}
737
738 {/* Record Prescription Tab */}
739 {activeTab === 'prescription' && (
740 <div className="bg-white rounded-lg shadow p-6">
741 <h3 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Record Prescription</h3>
742 <form onSubmit={handleAddPrescription} className="space-y-4 max-w-2xl">
743 <div>
744 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Doctor *</label>
745 {isDoctor ? (
746 <p className="px-4 py-2 bg-gray-100 rounded-lg text-gray-700">
747 Dr. {user.firstName} {user.lastName}
748 </p>
749 ) : (
750 <select
751 value={selectedDoctorId}
752 onChange={(e) => setSelectedDoctorId(e.target.value)}
753 className="w-full px-4 py-2 border rounded-lg"
754 required
755 >
756 <option value="">Select doctor</option>
757 {doctors.map((doc) => (
758 <option key={doc.doctorId} value={doc.doctorId}>
759 Dr. {doc.firstName} {doc.lastName}
760 </option>
761 ))}
762 </select>
763 )}
764 </div>
765 <div>
766 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Medication *</label>
767 <select
768 value={prescriptionForm.prescriptionId}
769 onChange={(e) =>
770 setPrescriptionForm({ ...prescriptionForm, prescriptionId: e.target.value })
771 }
772 className="w-full px-4 py-2 border rounded-lg"
773 required
774 >
775 <option value="">Select medication</option>
776 {prescriptions.map((prescription) => (
777 <option key={prescription.id} value={prescription.id}>
778 {prescription.name}
779 </option>
780 ))}
781 </select>
782 </div>
783 <div className="grid grid-cols-2 gap-4">
784 <div>
785 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Dosage *</label>
786 <input
787 type="text"
788 value={prescriptionForm.dosage}
789 onChange={(e) =>
790 setPrescriptionForm({ ...prescriptionForm, dosage: e.target.value })
791 }
792 placeholder="e.g., 500mg"
793 className="w-full px-4 py-2 border rounded-lg"
794 required
795 />
796 </div>
797 <div>
798 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Frequency *</label>
799 <input
800 type="text"
801 value={prescriptionForm.frequency}
802 onChange={(e) =>
803 setPrescriptionForm({ ...prescriptionForm, frequency: e.target.value })
804 }
805 placeholder="e.g., Twice daily"
806 className="w-full px-4 py-2 border rounded-lg"
807 required
808 />
809 </div>
810 <div>
811 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Duration *</label>
812 <input
813 type="text"
814 value={prescriptionForm.duration}
815 onChange={(e) =>
816 setPrescriptionForm({ ...prescriptionForm, duration: e.target.value })
817 }
818 placeholder="e.g., 7 days"
819 className="w-full px-4 py-2 border rounded-lg"
820 required
821 />
822 </div>
823 </div>
824 <div>
825 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Reason for Prescription</label>
826 <textarea
827 value={prescriptionForm.reason}
828 onChange={(e) => setPrescriptionForm({ ...prescriptionForm, reason: e.target.value })}
829 placeholder="Describe why this medication is prescribed"
830 className="w-full px-4 py-2 border rounded-lg"
831 rows="3"
832 />
833 </div>
834 <button
835 type="submit"
836 style={{background: '#9333ea', color: 'white', padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '14px', fontWeight: '600'}} onMouseEnter={(e) => e.currentTarget.style.background = '#7e22ce'} onMouseLeave={(e) => e.currentTarget.style.background = '#9333ea'}
837 >
838 Record Prescription
839 </button>
840 </form>
841 </div>
842 )}
843
844 {/* Procedure Request Tab */}
845 {activeTab === 'procedures' && (
846 <div className="bg-white rounded-lg shadow p-6">
847 <h3 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Request Procedure</h3>
848 <form onSubmit={async (e) => {
849 e.preventDefault();
850 setError(null);
851
852 if (!procedureForm.procedureId) {
853 setError('Please select a procedure');
854 return;
855 }
856
857 try {
858 setLoading(true);
859 const doctorId = user.doctorId || 1;
860
861 const request = {
862 patientId: record.patientId,
863 doctorId: parseInt(doctorId),
864 procedureId: parseInt(procedureForm.procedureId),
865 procedureDate: procedureForm.procedureDate,
866 notes: procedureForm.notes,
867 diagnosisId: procedureForm.diagnosisId ? parseInt(procedureForm.diagnosisId) : null,
868 };
869
870 await procedureService.requestProcedure(request);
871
872 setSuccess('Procedure requested successfully');
873 setProcedureForm({
874 procedureId: '',
875 procedureDate: new Date().toISOString().split('T')[0],
876 notes: '',
877 diagnosisId: '',
878 });
879
880 setTimeout(() => setSuccess(null), 3000);
881 } catch (err) {
882 setError('Failed to request procedure: ' + (err.response?.data?.error || err.message));
883 } finally {
884 setLoading(false);
885 }
886 }} className="space-y-4">
887 <div className="grid grid-cols-2 gap-4">
888 <div>
889 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Procedure *</label>
890 <select
891 value={procedureForm.procedureId}
892 onChange={(e) => setProcedureForm({ ...procedureForm, procedureId: e.target.value })}
893 className="w-full px-4 py-2 border rounded-lg"
894 required
895 >
896 <option value="">Select a procedure</option>
897 {procedures.map((proc) => (
898 <option key={proc.procedureId} value={proc.procedureId}>
899 {proc.procedureType} (${proc.cost})
900 </option>
901 ))}
902 </select>
903 </div>
904 <div>
905 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Procedure Date *</label>
906 <input
907 type="date"
908 value={procedureForm.procedureDate}
909 onChange={(e) => setProcedureForm({ ...procedureForm, procedureDate: e.target.value })}
910 className="w-full px-4 py-2 border rounded-lg"
911 required
912 />
913 </div>
914 </div>
915 <div>
916 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Related Diagnosis (Optional)</label>
917 <select
918 value={procedureForm.diagnosisId}
919 onChange={(e) => setProcedureForm({ ...procedureForm, diagnosisId: e.target.value })}
920 className="w-full px-4 py-2 border rounded-lg"
921 >
922 <option value="">Select diagnosis (optional)</option>
923 {diagnoses.map((diagnosis) => (
924 <option key={diagnosis.id} value={diagnosis.id}>
925 {diagnosis.name}
926 </option>
927 ))}
928 </select>
929 </div>
930 <div>
931 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Notes</label>
932 <textarea
933 value={procedureForm.notes}
934 onChange={(e) => setProcedureForm({ ...procedureForm, notes: e.target.value })}
935 placeholder="Pre-procedure instructions or special notes"
936 className="w-full px-4 py-2 border rounded-lg"
937 rows="3"
938 />
939 </div>
940 <button
941 type="submit"
942 disabled={loading}
943 style={{background: loading ? '#9ca3af' : '#9333ea', color: 'white', padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '14px', fontWeight: '600'}} onMouseEnter={(e) => !loading && (e.currentTarget.style.background = '#7e22ce')} onMouseLeave={(e) => !loading && (e.currentTarget.style.background = '#9333ea')}
944 >
945 {loading ? 'Requesting...' : 'Request Procedure'}
946 </button>
947 </form>
948 </div>
949 )}
950
951 {/* Lab Test Tab */}
952 {activeTab === 'lab-tests' && (
953 <div className="bg-white rounded-lg shadow p-6">
954 <h3 style={{ fontSize: '20px', fontWeight: 'normal', marginBottom: '16px' }}>Request Lab Test</h3>
955 <form onSubmit={async (e) => {
956 e.preventDefault();
957 setError(null);
958
959 if (!labTestForm.testId) {
960 setError('Please select a test');
961 return;
962 }
963
964 try {
965 setLoading(true);
966 const doctorId = user.doctorId || 1;
967
968 const request = {
969 patientId: record.patientId,
970 medicalRecordId: record.recordId,
971 doctorId: parseInt(doctorId),
972 testId: parseInt(labTestForm.testId),
973 testDate: labTestForm.testDate,
974 notes: labTestForm.notes,
975 };
976
977 await labService.requestLabTest(request);
978
979 setSuccess('Lab test requested successfully');
980 setLabTestForm({
981 testId: '',
982 testDate: new Date().toISOString().split('T')[0],
983 notes: '',
984 });
985
986 setTimeout(() => setSuccess(null), 3000);
987 } catch (err) {
988 setError('Failed to request lab test: ' + (err.response?.data?.error || err.message));
989 } finally {
990 setLoading(false);
991 }
992 }} className="space-y-4">
993 <div className="grid grid-cols-2 gap-4">
994 <div>
995 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Test</label>
996 <select
997 value={labTestForm.testId}
998 onChange={(e) => setLabTestForm({ ...labTestForm, testId: e.target.value })}
999 className="w-full px-4 py-2 border rounded-lg"
1000 required
1001 >
1002 <option value="">Select a test</option>
1003 {labTests.map((test) => (
1004 <option key={test.testId} value={test.testId}>
1005 {test.testName} (${test.cost})
1006 </option>
1007 ))}
1008 </select>
1009 </div>
1010 <div>
1011 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Test Date</label>
1012 <input
1013 type="date"
1014 value={labTestForm.testDate}
1015 onChange={(e) => setLabTestForm({ ...labTestForm, testDate: e.target.value })}
1016 className="w-full px-4 py-2 border rounded-lg"
1017 />
1018 </div>
1019 </div>
1020 <div>
1021 <label style={{ fontWeight: 'normal' }} className="block text-sm mb-2">Notes</label>
1022 <textarea
1023 value={labTestForm.notes}
1024 onChange={(e) => setLabTestForm({ ...labTestForm, notes: e.target.value })}
1025 placeholder="Additional notes for the lab technician"
1026 className="w-full px-4 py-2 border rounded-lg"
1027 rows="3"
1028 />
1029 </div>
1030 <button
1031 type="submit"
1032 disabled={loading}
1033 style={{background: loading ? '#9ca3af' : '#9333ea', color: 'white', padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '14px', fontWeight: '600'}} onMouseEnter={(e) => !loading && (e.currentTarget.style.background = '#7e22ce')} onMouseLeave={(e) => !loading && (e.currentTarget.style.background = '#9333ea')}
1034 >
1035 {loading ? 'Requesting...' : 'Request Test'}
1036 </button>
1037 </form>
1038 </div>
1039 )}
1040 </div>
1041 );
1042}
1043
1044export default MedicalRecordDetail;
Note: See TracBrowser for help on using the repository browser.