source: frontend/src/pages/procedures/ProcedureResultForm.js@ f05de05

Last change on this file since f05de05 was f05de05, checked in by MBK <marija.karapandzova@…>, 4 months ago

Add pages and routing for lab tests, procedures and billing with api services

  • Property mode set to 100644
File size: 10.6 KB
Line 
1import React, { useState, useEffect } from 'react';
2import { useNavigate } from 'react-router-dom';
3import { procedureService } from '../../services/procedureService';
4import { patientService } from '../../services/patientService';
5import { medicalRecordService } from '../../services/medicalRecordService';
6import ErrorAlert from '../../components/ErrorAlert';
7import SuccessAlert from '../../components/SuccessAlert';
8
9function ProcedureResultForm() {
10 const navigate = useNavigate();
11 const [error, setError] = useState(null);
12 const [success, setSuccess] = useState(null);
13 const [loading, setLoading] = useState(false);
14 const [searchEmbg, setSearchEmbg] = useState('');
15 const [patient, setPatient] = useState(null);
16 const [performedProcedures, setPerformedProcedures] = useState([]);
17 const [selectedProcedure, setSelectedProcedure] = useState(null);
18 const [medicalRecordId, setMedicalRecordId] = useState(null);
19
20 const [formData, setFormData] = useState({
21 medicalRecordId: '',
22 procedureId: '',
23 resultDescription: '',
24 resultDate: new Date().toISOString().split('T')[0],
25 });
26
27 const handleSearch = async (e) => {
28 e.preventDefault();
29 setError(null);
30 setLoading(true);
31
32 try {
33 if (!searchEmbg.trim()) {
34 setError('Please enter patient EMBG');
35 setLoading(false);
36 return;
37 }
38
39 const patientRes = await patientService.getPatientByEmbg(searchEmbg);
40 setPatient(patientRes.data);
41
42 // Get medical record
43 const medicalRecordRes = await medicalRecordService.getMedicalRecordByPatientId(patientRes.data.patientId);
44 setMedicalRecordId(medicalRecordRes.data.recordId);
45
46 // Get performed procedures
47 try {
48 const proceduresRes = await procedureService.getPerformedProceduresForPatient(patientRes.data.patientId);
49 setPerformedProcedures(Array.isArray(proceduresRes.data) ? proceduresRes.data : []);
50 } catch (err) {
51 console.error('Failed to fetch performed procedures:', err);
52 setPerformedProcedures([]);
53 }
54
55 setFormData(prev => ({
56 ...prev,
57 medicalRecordId: medicalRecordRes.data.recordId
58 }));
59 } catch (err) {
60 console.error('Search error:', err);
61 setError(`Error: ${err.response?.data?.error || err.message || 'Patient not found'}`);
62 setPatient(null);
63 setPerformedProcedures([]);
64 } finally {
65 setLoading(false);
66 }
67 };
68
69 const handleSelectProcedure = (procedure) => {
70 setSelectedProcedure(procedure);
71 setFormData(prev => ({
72 ...prev,
73 procedureId: procedure.procedure?.procedureId || procedure.procedureId,
74 medicalRecordId: medicalRecordId || prev.medicalRecordId
75 }));
76 };
77
78 const handleChange = (e) => {
79 const { name, value } = e.target;
80 setFormData({
81 ...formData,
82 [name]: value,
83 });
84 };
85
86 const handleSubmit = async (e) => {
87 e.preventDefault();
88 setError(null);
89 setSuccess(null);
90
91 if (!formData.medicalRecordId || !formData.procedureId || !formData.resultDescription) {
92 setError('Please fill in all required fields');
93 return;
94 }
95
96 try {
97 setLoading(true);
98
99 const submitData = {
100 medicalRecordId: parseInt(formData.medicalRecordId),
101 procedureId: parseInt(formData.procedureId),
102 resultDescription: formData.resultDescription,
103 resultDate: formData.resultDate,
104 };
105
106 await procedureService.submitProcedureResult(submitData);
107
108 setSuccess('Procedure result submitted successfully!');
109 setFormData({
110 medicalRecordId: '',
111 procedureId: '',
112 resultDescription: '',
113 resultDate: new Date().toISOString().split('T')[0],
114 });
115 setSelectedProcedure(null);
116 setPatient(null);
117 setSearchEmbg('');
118 setPerformedProcedures([]);
119
120 // Navigate back to procedures page after a short delay
121 setTimeout(() => navigate('/procedures'), 2000);
122 } catch (err) {
123 setError('Failed to submit procedure result: ' + (err.response?.data?.error || err.message));
124 setLoading(false);
125 }
126 };
127
128 return (
129 <div className="max-w-3xl mx-auto">
130 <h1 className="text-3xl font-bold mb-6">Submit Procedure Result</h1>
131
132 {error && <ErrorAlert message={error} onClose={() => setError(null)} />}
133 {success && <SuccessAlert message={success} onClose={() => setSuccess(null)} />}
134
135 {/* Search Patient Form */}
136 <div className="bg-white rounded-lg shadow p-6 mb-6">
137 <h2 className="text-xl font-bold mb-4">Search Patient</h2>
138 <form onSubmit={handleSearch} className="flex gap-4">
139 <input
140 type="text"
141 value={searchEmbg}
142 onChange={(e) => setSearchEmbg(e.target.value)}
143 placeholder="Enter patient EMBG"
144 className="flex-1 px-4 py-2 border rounded-lg"
145 />
146 <button
147 type="submit"
148 disabled={loading}
149 className="bg-purple-600 text-white px-8 py-2 rounded hover:bg-purple-700 disabled:bg-gray-400"
150 >
151 {loading ? 'Searching...' : 'Search'}
152 </button>
153 </form>
154 </div>
155
156 {/* Patient Info and Performed Procedures */}
157 {patient && (
158 <div className="space-y-6">
159 {/* Patient Card */}
160 <div className="bg-white rounded-lg shadow p-6">
161 <h2 className="text-2xl font-bold mb-4">{patient.firstName} {patient.lastName}</h2>
162 <div className="grid grid-cols-3 gap-4">
163 <div>
164 <p className="text-sm text-gray-600">EMBG</p>
165 <p className="font-semibold">{patient.embg}</p>
166 </div>
167 <div>
168 <p className="text-sm text-gray-600">Blood Type</p>
169 <p className="font-semibold">{patient.bloodType || 'N/A'}</p>
170 </div>
171 <div>
172 <p className="text-sm text-gray-600">Date of Birth</p>
173 <p className="font-semibold">{patient.dateOfBirth}</p>
174 </div>
175 </div>
176 </div>
177
178 {/* Performed Procedures List */}
179 {performedProcedures.length > 0 ? (
180 <div className="bg-white rounded-lg shadow p-6">
181 <h2 className="text-xl font-bold mb-4">Performed Procedures</h2>
182 <div className="space-y-2">
183 {performedProcedures.map((proc) => (
184 <div
185 key={proc.performedId}
186 onClick={() => handleSelectProcedure(proc)}
187 className={`p-4 rounded-lg border-2 cursor-pointer transition ${
188 selectedProcedure?.performedId === proc.performedId
189 ? 'border-purple-600 bg-blue-50'
190 : 'border-gray-200 bg-gray-50 hover:border-blue-400'
191 }`}
192 >
193 <p className="font-semibold text-lg">{proc.procedure?.procedureType || 'Procedure'}</p>
194 <p className="text-sm text-gray-600">Requested by: {proc.doctor?.firstName} {proc.doctor?.lastName}</p>
195 <p className="text-sm text-gray-600">Procedure Date: {proc.procedureDate}</p>
196 {proc.notes && (
197 <p className="text-sm text-gray-600 mt-1">Notes: {proc.notes}</p>
198 )}
199 </div>
200 ))}
201 </div>
202 </div>
203 ) : (
204 <div className="bg-blue-50 rounded-lg p-6 text-center">
205 <p className="text-gray-600">No performed procedures for this patient</p>
206 </div>
207 )}
208
209 {/* Result Submission Form */}
210 {selectedProcedure && (
211 <div className="bg-white rounded-lg shadow p-6">
212 <h2 className="text-xl font-bold mb-4">Submit Result for {selectedProcedure.procedure?.procedureType}</h2>
213 <form onSubmit={handleSubmit} className="space-y-4">
214 <div className="grid grid-cols-2 gap-4">
215 <div>
216 <label className="block text-sm font-semibold mb-2">Procedure ID</label>
217 <input
218 type="number"
219 value={formData.procedureId}
220 disabled
221 className="w-full px-4 py-2 border rounded-lg bg-gray-100"
222 />
223 </div>
224 <div>
225 <label className="block text-sm font-semibold mb-2">Medical Record ID</label>
226 <input
227 type="number"
228 value={formData.medicalRecordId}
229 disabled
230 className="w-full px-4 py-2 border rounded-lg bg-gray-100"
231 />
232 </div>
233 </div>
234
235 <div>
236 <label className="block text-sm font-semibold mb-2">Procedure Outcome *</label>
237 <textarea
238 name="resultDescription"
239 value={formData.resultDescription}
240 onChange={handleChange}
241 placeholder="Describe the procedure outcome and any findings"
242 className="w-full px-4 py-2 border rounded-lg"
243 rows="6"
244 required
245 />
246 </div>
247
248 <div>
249 <label className="block text-sm font-semibold mb-2">Result Date</label>
250 <input
251 type="date"
252 name="resultDate"
253 value={formData.resultDate}
254 onChange={handleChange}
255 className="w-full px-4 py-2 border rounded-lg"
256 />
257 </div>
258
259 <div className="flex gap-4 pt-6">
260 <button
261 type="submit"
262 disabled={loading}
263 className="flex-1 bg-purple-600 text-white py-2 rounded-lg hover:bg-purple-700 disabled:bg-gray-400"
264 >
265 {loading ? 'Submitting...' : 'Submit Result'}
266 </button>
267 <button
268 type="button"
269 onClick={() => navigate('/procedures')}
270 className="flex-1 bg-gray-400 text-white py-2 rounded-lg hover:bg-gray-500"
271 >
272 Cancel
273 </button>
274 </div>
275 </form>
276 </div>
277 )}
278 </div>
279 )}
280
281 {/* No search performed */}
282 {!patient && (
283 <div className="bg-gray-50 rounded-lg p-12 text-center">
284 <p className="text-gray-600 text-lg">Enter a patient EMBG to view performed procedures</p>
285 </div>
286 )}
287 </div>
288 );
289}
290
291export default ProcedureResultForm;
Note: See TracBrowser for help on using the repository browser.