From 56458340ea2f90515686f9b85d882536ad6bc191 Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 9 Mar 2026 23:31:26 -0500 Subject: [PATCH] feat(ui): add HealthRecordForm modal with OFA and general record support --- client/src/components/HealthRecordForm.jsx | 194 +++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 client/src/components/HealthRecordForm.jsx diff --git a/client/src/components/HealthRecordForm.jsx b/client/src/components/HealthRecordForm.jsx new file mode 100644 index 0000000..6916974 --- /dev/null +++ b/client/src/components/HealthRecordForm.jsx @@ -0,0 +1,194 @@ +import { useState } from 'react' +import { X } from 'lucide-react' +import axios from 'axios' + +const RECORD_TYPES = ['ofa_clearance', 'vaccination', 'exam', 'surgery', 'medication', 'other'] +const OFA_TEST_TYPES = [ + { value: 'hip_ofa', label: 'Hip - OFA' }, + { value: 'hip_pennhip', label: 'Hip - PennHIP' }, + { value: 'elbow_ofa', label: 'Elbow - OFA' }, + { value: 'heart_ofa', label: 'Heart - OFA' }, + { value: 'heart_echo', label: 'Heart - Echo' }, + { value: 'eye_caer', label: 'Eyes - CAER' }, +] +const OFA_RESULTS = ['Excellent', 'Good', 'Fair', 'Mild', 'Moderate', 'Severe', 'Normal', 'Abnormal', 'Pass', 'Fail'] + +const EMPTY = { + record_type: 'ofa_clearance', test_type: 'hip_ofa', test_name: '', + test_date: '', ofa_result: 'Good', ofa_number: '', performed_by: '', + expires_at: '', result: '', vet_name: '', next_due: '', notes: '', document_url: '', +} + +export default function HealthRecordForm({ dogId, record, onClose, onSave }) { + const [form, setForm] = useState(record || { ...EMPTY, dog_id: dogId }) + const [saving, setSaving] = useState(false) + const [error, setError] = useState(null) + + const isOFA = form.record_type === 'ofa_clearance' + const set = (k, v) => setForm(f => ({ ...f, [k]: v })) + + const handleSubmit = async (e) => { + e.preventDefault() + setSaving(true) + setError(null) + try { + if (record && record.id) { + await axios.put(`/api/health/${record.id}`, form) + } else { + await axios.post('/api/health', { ...form, dog_id: dogId }) + } + onSave() + } catch (err) { + setError(err.response?.data?.error || 'Failed to save record') + } finally { + setSaving(false) + } + } + + const labelStyle = { + fontSize: '0.8rem', color: 'var(--text-muted)', + marginBottom: '0.25rem', display: 'block', + } + const inputStyle = { + width: '100%', background: 'var(--bg-primary)', + border: '1px solid var(--border)', borderRadius: 'var(--radius-sm)', + padding: '0.5rem 0.75rem', color: 'var(--text-primary)', fontSize: '0.9rem', + boxSizing: 'border-box', + } + const fw = { display: 'flex', flexDirection: 'column', gap: '0.25rem' } + const grid2 = { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' } + + return ( +
+
+
+

{record && record.id ? 'Edit' : 'Add'} Health Record

+ +
+ +
+ + {/* Record type */} +
+ + +
+ + {isOFA ? ( + <> +
+
+ + +
+
+ + +
+
+
+
+ + set('ofa_number', e.target.value)} /> +
+
+ + set('performed_by', e.target.value)} /> +
+
+
+
+ + set('test_date', e.target.value)} /> +
+
+ + set('expires_at', e.target.value)} /> +
+
+ + ) : ( + <> +
+ + set('test_name', e.target.value)} /> +
+
+
+ + set('test_date', e.target.value)} /> +
+
+ + set('next_due', e.target.value)} /> +
+
+
+
+ + set('result', e.target.value)} /> +
+
+ + set('vet_name', e.target.value)} /> +
+
+ + )} + +
+ + set('document_url', e.target.value)} /> +
+ +
+ +