feat(measurements): add WHOOP source, native score types, and source-priority ladders

Add the WHOOP integration data layer (migration 0111): seven new
MeasurementType values (HRV_RMSSD, DAY_STRAIN, WORKOUT_STRAIN,
SLEEP_PERFORMANCE, SLEEP_EFFICIENCY, SLEEP_CONSISTENCY, SLEEP_NEED,
ENERGY_EXPENDITURE_KJ), a WHOOP MeasurementSource, the WhoopConnection
and WhoopOAuthState models, and the per-user encrypted BYO-key columns
on User. Day-strain takes DAY_STRAIN (not the COMPUTED STRAIN_SCORE) and
RMSSD takes HRV_RMSSD (not the SDNN HEART_RATE_VARIABILITY) so a native
value never shares a bucket with a derived proxy.

Carry the new types through the completeness walls (units, value ranges,
chart tokens, PR direction, categories, list-meta colours/icons/labels,
HealthKit-mapping exclusions) and feed the cross-source picker: recovery
ladder (WHOOP native above the COMPUTED proxy), WHOOP inserted into the
overlapping vital ladders and the workout ladder, plus skinTemperature
and respiratoryRate metric keys.
This commit is contained in:
Marc-André Bombeck committed 2026-06-04 00:49:31 +02:00
1 parent 747a62516a
commit 6db3bef413
21 files changed
+649 -31

No files matched your search

@@ -100,6 +100,15 @@ export const MEASUREMENT_TYPE_LABEL_KEYS: Record<string, string> = {
RECOVERY_SCORE: "measurements.typeRecoveryScore",
STRESS_SCORE: "measurements.typeStressScore",
STRAIN_SCORE: "measurements.typeStrainScore",
// ── v1.11.0 — WHOOP-native score classes ──
HRV_RMSSD: "measurements.typeHrvRmssd",
DAY_STRAIN: "measurements.typeDayStrain",
WORKOUT_STRAIN: "measurements.typeWorkoutStrain",
SLEEP_PERFORMANCE: "measurements.typeSleepPerformance",
SLEEP_EFFICIENCY: "measurements.typeSleepEfficiency",
SLEEP_CONSISTENCY: "measurements.typeSleepConsistency",
SLEEP_NEED: "measurements.typeSleepNeed",
ENERGY_EXPENDITURE_KJ: "measurements.typeEnergyExpenditureKj",
};
export const MEASUREMENT_TYPE_ICONS: Record<string, LucideIcon> = {
@@ -191,6 +200,19 @@ export const MEASUREMENT_TYPE_ICONS: Record<string, LucideIcon> = {
RECOVERY_SCORE: Gauge,
STRESS_SCORE: Gauge,
STRAIN_SCORE: Gauge,
// ── v1.11.0 — WHOOP-native score classes ──
// Gauge reads as a composite "index / score" dial for the strain +
// sleep-quality composites; the sleep-need recommendation borrows Moon
// (sleep family), RMSSD borrows HeartPulse (cardiac), energy borrows
// Flame (energy family, like ACTIVE_ENERGY_BURNED).
HRV_RMSSD: HeartPulse,
DAY_STRAIN: Gauge,
WORKOUT_STRAIN: Gauge,
SLEEP_PERFORMANCE: Moon,
SLEEP_EFFICIENCY: Moon,
SLEEP_CONSISTENCY: Moon,
SLEEP_NEED: Moon,
ENERGY_EXPENDITURE_KJ: Flame,
};
export const MEASUREMENT_TYPE_COLORS: Record<string, string> = {
@@ -274,4 +296,16 @@ export const MEASUREMENT_TYPE_COLORS: Record<string, string> = {
RECOVERY_SCORE: "bg-chart-1/20 text-chart-1",
STRESS_SCORE: "bg-chart-1/20 text-chart-1",
STRAIN_SCORE: "bg-chart-1/20 text-chart-1",
// ── v1.11.0 — WHOOP-native score classes ──
// chart-1 (Dracula purple) for the strain composites (same group as the
// WX-C scores); chart-2 (sleep/activity family) for the sleep-quality set
// and energy; chart-5 (cardio/pulse family) for RMSSD HRV.
HRV_RMSSD: "bg-chart-5/20 text-chart-5",
DAY_STRAIN: "bg-chart-1/20 text-chart-1",
WORKOUT_STRAIN: "bg-chart-1/20 text-chart-1",
SLEEP_PERFORMANCE: "bg-chart-2/20 text-chart-2",
SLEEP_EFFICIENCY: "bg-chart-2/20 text-chart-2",
SLEEP_CONSISTENCY: "bg-chart-2/20 text-chart-2",
SLEEP_NEED: "bg-chart-2/20 text-chart-2",
ENERGY_EXPENDITURE_KJ: "bg-chart-4/20 text-chart-4",
};
@@ -79,6 +79,10 @@ const METRIC_LABEL_KEYS: Record<SourcePriorityMetricKey, string> = {
hrv: "settings.sections.sources.metrics.hrv",
restingHeartRate: "settings.sections.sources.metrics.restingHeartRate",
vo2Max: "settings.sections.sources.metrics.vo2Max",
// v1.11.0 — WHOOP-overlapping metric classes + native-vs-derived recovery.
skinTemperature: "settings.sections.sources.metrics.skinTemperature",
respiratoryRate: "settings.sections.sources.metrics.respiratoryRate",
recovery: "settings.sections.sources.metrics.recovery",
};
const SOURCE_LABEL_KEYS: Record<string, string> = {
@@ -86,6 +90,10 @@ const SOURCE_LABEL_KEYS: Record<string, string> = {
APPLE_HEALTH: "settings.sections.sources.sourceLabels.APPLE_HEALTH",
MANUAL: "settings.sections.sources.sourceLabels.MANUAL",
IMPORT: "settings.sections.sources.sourceLabels.IMPORT",
// v1.11.0 — WHOOP native source + the COMPUTED proxy (surfaces in the
// `recovery` ladder for native-vs-derived ordering).
WHOOP: "settings.sections.sources.sourceLabels.WHOOP",
COMPUTED: "settings.sections.sources.sourceLabels.COMPUTED",
};
const DEVICE_TYPE_LABEL_KEYS: Record<DeviceType, string> = {