From 9634ca8909e513680c1cfd5d85a6b50ddd7835e4 Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 23 Mar 2026 15:17:36 +0100 Subject: [PATCH] feat: extend VitalsPage with all new vital parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Form sections: - Morgenmessung: Ruhepuls, HRV - Blutdruck (Omron): Systolisch, Diastolisch, Puls - Fitness & Sauerstoff (Apple Watch): VO2 Max, SpO2, Atemfrequenz - Warnungen: Unregelmäßiger Herzschlag, Mögliches AFib (checkboxes) Display: - All vitals shown in entry list with icons - Blood pressure highlighted in red (🩸) - VO2 Max in green (🏃) - Warnings in orange (⚠️) Stats overview: - Dynamic grid showing available metrics - Avg blood pressure 7d - Latest VO2 Max - Avg SpO2 7d Save/Update: - Only non-empty fields included in payload - At least one vital must be provided Ready for manual testing + import implementation Co-Authored-By: Claude Opus 4.6 --- frontend/src/pages/VitalsPage.jsx | 282 ++++++++++++++++++++++++++---- 1 file changed, 251 insertions(+), 31 deletions(-) diff --git a/frontend/src/pages/VitalsPage.jsx b/frontend/src/pages/VitalsPage.jsx index c532fea..c7aeb83 100644 --- a/frontend/src/pages/VitalsPage.jsx +++ b/frontend/src/pages/VitalsPage.jsx @@ -10,6 +10,14 @@ function empty() { date: dayjs().format('YYYY-MM-DD'), resting_hr: '', hrv: '', + blood_pressure_systolic: '', + blood_pressure_diastolic: '', + pulse: '', + vo2_max: '', + spo2: '', + respiratory_rate: '', + irregular_heartbeat: false, + possible_afib: false, note: '' } } @@ -31,8 +39,13 @@ function EntryForm({ form, setForm, onSave, onCancel, saving, saveLabel = 'Speic + {/* Section: Morgenmessung */} +
+ Morgenmessung (vor dem Aufstehen) +
+
- + + Blutdruck (Omron) +
+ +
+ + set('blood_pressure_systolic', e.target.value)} + /> + mmHg +
+ +
+ + set('blood_pressure_diastolic', e.target.value)} + /> + mmHg +
+ +
+ + set('pulse', e.target.value)} + /> + bpm +
+ + {/* Section: Fitness & Sauerstoff */} +
+ Fitness & Sauerstoff (Apple Watch) +
+ +
+ + set('vo2_max', e.target.value)} + /> + ml/kg/min +
+ +
+ + set('spo2', e.target.value)} + /> + % +
+ +
+ + set('respiratory_rate', e.target.value)} + /> + /min +
+ + {/* Section: Warnungen */} +
+ + +
+
0 && (
-
-
-
- {stats.avg_resting_hr_7d ? Math.round(stats.avg_resting_hr_7d) : '—'} +
+ {stats.avg_resting_hr_7d && ( +
+
+ {Math.round(stats.avg_resting_hr_7d)} +
+
Ø Ruhepuls 7d
-
Ø Ruhepuls 7d
-
-
-
- {stats.avg_hrv_7d ? Math.round(stats.avg_hrv_7d) : '—'} + )} + {stats.avg_hrv_7d && ( +
+
+ {Math.round(stats.avg_hrv_7d)} +
+
Ø HRV 7d
-
Ø HRV 7d
-
+ )} + {stats.avg_bp_systolic_7d && ( +
+
+ {Math.round(stats.avg_bp_systolic_7d)}/{Math.round(stats.avg_bp_diastolic_7d || 0)} +
+
Ø Blutdruck 7d
+
+ )} + {stats.latest_vo2_max && ( +
+
+ {stats.latest_vo2_max.toFixed(1)} +
+
VO2 Max
+
+ )} + {stats.avg_spo2_7d && ( +
+
+ {Math.round(stats.avg_spo2_7d)}% +
+
Ø SpO2 7d
+
+ )}
)} + {e.blood_pressure_systolic && e.blood_pressure_diastolic && ( + + 🩸 {e.blood_pressure_systolic}/{e.blood_pressure_diastolic} mmHg + + )} + {e.pulse && ( + + 💓 {e.pulse} bpm + + )} + {e.vo2_max && ( + + 🏃 VO2 {e.vo2_max} + + )} + {e.spo2 && ( + + 🫁 SpO2 {e.spo2}% + + )} + {e.respiratory_rate && ( + + 💨 {e.respiratory_rate}/min + + )} + {(e.irregular_heartbeat || e.possible_afib) && ( + + ⚠️ {e.irregular_heartbeat ? 'Unregelmäßig' : ''} {e.possible_afib ? 'AFib?' : ''} + + )} {e.source !== 'manual' && ( - {e.source === 'apple_health' ? 'Apple Health' : e.source} + {e.source === 'apple_health' ? 'Apple Health' : e.source === 'omron' ? 'Omron' : e.source} )}