| 1 | import React, { useState, useEffect } from 'react';
|
|---|
| 2 | import { useNavigate, useParams } from 'react-router-dom';
|
|---|
| 3 | import { patientService } from '../../services/patientService';
|
|---|
| 4 | import ErrorAlert from '../../components/ErrorAlert';
|
|---|
| 5 | import SuccessAlert from '../../components/SuccessAlert';
|
|---|
| 6 |
|
|---|
| 7 | function PatientForm() {
|
|---|
| 8 | const { id } = useParams();
|
|---|
| 9 | const navigate = useNavigate();
|
|---|
| 10 | const [formData, setFormData] = useState({
|
|---|
| 11 | firstName: '',
|
|---|
| 12 | lastName: '',
|
|---|
| 13 | emailAddress: '',
|
|---|
| 14 | embg: '',
|
|---|
| 15 | phoneNumber: '',
|
|---|
| 16 | dateOfBirth: '',
|
|---|
| 17 | gender: '',
|
|---|
| 18 | bloodType: '',
|
|---|
| 19 | });
|
|---|
| 20 | const [error, setError] = useState(null);
|
|---|
| 21 | const [success, setSuccess] = useState(null);
|
|---|
| 22 | const [loading, setLoading] = useState(false);
|
|---|
| 23 |
|
|---|
| 24 | useEffect(() => {
|
|---|
| 25 | if (id) {
|
|---|
| 26 | const fetchPatient = async () => {
|
|---|
| 27 | try {
|
|---|
| 28 | setLoading(true);
|
|---|
| 29 | const response = await patientService.getPatientById(id);
|
|---|
| 30 | setFormData(response.data);
|
|---|
| 31 | } catch (err) {
|
|---|
| 32 | setError('Failed to fetch patient');
|
|---|
| 33 | } finally {
|
|---|
| 34 | setLoading(false);
|
|---|
| 35 | }
|
|---|
| 36 | };
|
|---|
| 37 |
|
|---|
| 38 | fetchPatient();
|
|---|
| 39 | }
|
|---|
| 40 | }, [id]);
|
|---|
| 41 |
|
|---|
| 42 | const handleChange = (e) => {
|
|---|
| 43 | const { name, value } = e.target;
|
|---|
| 44 | setFormData(prev => ({
|
|---|
| 45 | ...prev,
|
|---|
| 46 | [name]: value
|
|---|
| 47 | }));
|
|---|
| 48 | };
|
|---|
| 49 |
|
|---|
| 50 | const handleSubmit = async (e) => {
|
|---|
| 51 | e.preventDefault();
|
|---|
| 52 |
|
|---|
| 53 | if (!formData.firstName || !formData.lastName || !formData.embg) {
|
|---|
| 54 | setError('Please fill in all required fields');
|
|---|
| 55 | return;
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 | try {
|
|---|
| 59 | setLoading(true);
|
|---|
| 60 | if (id) {
|
|---|
| 61 | await patientService.updatePatient(id, formData);
|
|---|
| 62 | setSuccess('Patient updated successfully!');
|
|---|
| 63 | } else {
|
|---|
| 64 | await patientService.createPatient(formData);
|
|---|
| 65 | setSuccess('Patient created successfully!');
|
|---|
| 66 | }
|
|---|
| 67 | setTimeout(() => navigate('/patients'), 1500);
|
|---|
| 68 | } catch (err) {
|
|---|
| 69 | setError(err.response?.data?.error || 'Failed to save patient');
|
|---|
| 70 | } finally {
|
|---|
| 71 | setLoading(false);
|
|---|
| 72 | }
|
|---|
| 73 | };
|
|---|
| 74 |
|
|---|
| 75 | return (
|
|---|
| 76 | <div className="max-w-2xl mx-auto">
|
|---|
| 77 | <h1 className="text-3xl font-bold mb-6">
|
|---|
| 78 | {id ? 'Edit Patient' : 'Add New Patient'}
|
|---|
| 79 | </h1>
|
|---|
| 80 |
|
|---|
| 81 | {error && <ErrorAlert message={error} onClose={() => setError(null)} />}
|
|---|
| 82 | {success && <SuccessAlert message={success} onClose={() => setSuccess(null)} />}
|
|---|
| 83 |
|
|---|
| 84 | <form onSubmit={handleSubmit} className="bg-white rounded-lg shadow p-6 space-y-4">
|
|---|
| 85 | <div className="grid grid-cols-2 gap-4">
|
|---|
| 86 | <div>
|
|---|
| 87 | <label className="block text-sm font-semibold mb-2">First Name *</label>
|
|---|
| 88 | <input
|
|---|
| 89 | type="text"
|
|---|
| 90 | name="firstName"
|
|---|
| 91 | value={formData.firstName}
|
|---|
| 92 | onChange={handleChange}
|
|---|
| 93 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 94 | required
|
|---|
| 95 | />
|
|---|
| 96 | </div>
|
|---|
| 97 | <div>
|
|---|
| 98 | <label className="block text-sm font-semibold mb-2">Last Name *</label>
|
|---|
| 99 | <input
|
|---|
| 100 | type="text"
|
|---|
| 101 | name="lastName"
|
|---|
| 102 | value={formData.lastName}
|
|---|
| 103 | onChange={handleChange}
|
|---|
| 104 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 105 | required
|
|---|
| 106 | />
|
|---|
| 107 | </div>
|
|---|
| 108 | </div>
|
|---|
| 109 |
|
|---|
| 110 | <div>
|
|---|
| 111 | <label className="block text-sm font-semibold mb-2">EMBG *</label>
|
|---|
| 112 | <input
|
|---|
| 113 | type="text"
|
|---|
| 114 | name="embg"
|
|---|
| 115 | value={formData.embg}
|
|---|
| 116 | onChange={handleChange}
|
|---|
| 117 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 118 | required
|
|---|
| 119 | disabled={!!id}
|
|---|
| 120 | />
|
|---|
| 121 | </div>
|
|---|
| 122 |
|
|---|
| 123 | <div>
|
|---|
| 124 | <label className="block text-sm font-semibold mb-2">Email</label>
|
|---|
| 125 | <input
|
|---|
| 126 | type="email"
|
|---|
| 127 | name="emailAddress"
|
|---|
| 128 | value={formData.emailAddress}
|
|---|
| 129 | onChange={handleChange}
|
|---|
| 130 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 131 | />
|
|---|
| 132 | </div>
|
|---|
| 133 |
|
|---|
| 134 | <div className="grid grid-cols-2 gap-4">
|
|---|
| 135 | <div>
|
|---|
| 136 | <label className="block text-sm font-semibold mb-2">Phone</label>
|
|---|
| 137 | <input
|
|---|
| 138 | type="tel"
|
|---|
| 139 | name="phoneNumber"
|
|---|
| 140 | value={formData.phoneNumber}
|
|---|
| 141 | onChange={handleChange}
|
|---|
| 142 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 143 | />
|
|---|
| 144 | </div>
|
|---|
| 145 | <div>
|
|---|
| 146 | <label className="block text-sm font-semibold mb-2">Date of Birth</label>
|
|---|
| 147 | <input
|
|---|
| 148 | type="date"
|
|---|
| 149 | name="dateOfBirth"
|
|---|
| 150 | value={formData.dateOfBirth}
|
|---|
| 151 | onChange={handleChange}
|
|---|
| 152 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 153 | />
|
|---|
| 154 | </div>
|
|---|
| 155 | </div>
|
|---|
| 156 |
|
|---|
| 157 | <div className="grid grid-cols-2 gap-4">
|
|---|
| 158 | <div>
|
|---|
| 159 | <label className="block text-sm font-semibold mb-2">Gender</label>
|
|---|
| 160 | <select
|
|---|
| 161 | name="gender"
|
|---|
| 162 | value={formData.gender}
|
|---|
| 163 | onChange={handleChange}
|
|---|
| 164 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 165 | >
|
|---|
| 166 | <option value="">Select Gender</option>
|
|---|
| 167 | <option value="MALE">Male</option>
|
|---|
| 168 | <option value="FEMALE">Female</option>
|
|---|
| 169 | <option value="OTHER">Other</option>
|
|---|
| 170 | </select>
|
|---|
| 171 | </div>
|
|---|
| 172 | <div>
|
|---|
| 173 | <label className="block text-sm font-semibold mb-2">Blood Type</label>
|
|---|
| 174 | <select
|
|---|
| 175 | name="bloodType"
|
|---|
| 176 | value={formData.bloodType}
|
|---|
| 177 | onChange={handleChange}
|
|---|
| 178 | className="w-full px-4 py-2 border rounded-lg"
|
|---|
| 179 | >
|
|---|
| 180 | <option value="">Select Blood Type</option>
|
|---|
| 181 | <option value="O_POSITIVE">O+</option>
|
|---|
| 182 | <option value="O_NEGATIVE">O-</option>
|
|---|
| 183 | <option value="A_POSITIVE">A+</option>
|
|---|
| 184 | <option value="A_NEGATIVE">A-</option>
|
|---|
| 185 | <option value="B_POSITIVE">B+</option>
|
|---|
| 186 | <option value="B_NEGATIVE">B-</option>
|
|---|
| 187 | <option value="AB_POSITIVE">AB+</option>
|
|---|
| 188 | <option value="AB_NEGATIVE">AB-</option>
|
|---|
| 189 | </select>
|
|---|
| 190 | </div>
|
|---|
| 191 | </div>
|
|---|
| 192 |
|
|---|
| 193 | <div className="flex gap-4 pt-4">
|
|---|
| 194 | <button
|
|---|
| 195 | type="submit"
|
|---|
| 196 | disabled={loading}
|
|---|
| 197 | className="bg-purple-600 text-white px-6 py-2 rounded hover:bg-purple-700 disabled:opacity-50"
|
|---|
| 198 | >
|
|---|
| 199 | {loading ? 'Saving...' : 'Save Patient'}
|
|---|
| 200 | </button>
|
|---|
| 201 | <button
|
|---|
| 202 | type="button"
|
|---|
| 203 | onClick={() => navigate('/patients')}
|
|---|
| 204 | className="bg-gray-300 text-gray-700 px-6 py-2 rounded hover:bg-gray-400"
|
|---|
| 205 | >
|
|---|
| 206 | Cancel
|
|---|
| 207 | </button>
|
|---|
| 208 | </div>
|
|---|
| 209 | </form>
|
|---|
| 210 | </div>
|
|---|
| 211 | );
|
|---|
| 212 | }
|
|---|
| 213 |
|
|---|
| 214 | export default PatientForm;
|
|---|