health: smart-scale columns, deduped body measurement imports, quick-add form
All checks were successful
Deploy production / deploy (push) Successful in 2m32s

This commit is contained in:
2026-10-24 14:15:59 +00:00
parent b51fef14fd
commit 07d2c70973
9 changed files with 17305 additions and 14 deletions

View File

@@ -1,4 +1,14 @@
import { check, date, index, integer, numeric, text, timestamp, uuid } from 'drizzle-orm/pg-core';
import {
check,
date,
index,
integer,
numeric,
text,
timestamp,
uniqueIndex,
uuid
} from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
import { healthSchema } from './schemas';
import { timestamps, timestamptz } from './helpers';
@@ -42,11 +52,25 @@ export const healthBodyMeasurements = healthSchema.table(
weightKg: numeric('weight_kg', { precision: 5, scale: 2, mode: 'number' }),
waistCm: numeric('waist_cm', { precision: 5, scale: 1, mode: 'number' }), // at the navel
bodyFatPct: numeric('body_fat_pct', { precision: 4, scale: 1, mode: 'number' }),
// Optional smart-scale (BIA) readings, e.g. Renpho.
muscleMassKg: numeric('muscle_mass_kg', { precision: 5, scale: 2, mode: 'number' }),
skeletalMuscleKg: numeric('skeletal_muscle_kg', { precision: 5, scale: 2, mode: 'number' }),
boneMassKg: numeric('bone_mass_kg', { precision: 4, scale: 2, mode: 'number' }),
visceralFat: numeric('visceral_fat', { precision: 4, scale: 1, mode: 'number' }), // scale's index
bodyWaterPct: numeric('body_water_pct', { precision: 4, scale: 1, mode: 'number' }),
proteinPct: numeric('protein_pct', { precision: 4, scale: 1, mode: 'number' }),
bmrKcal: integer('bmr_kcal'),
bmi: numeric('bmi', { precision: 4, scale: 1, mode: 'number' }),
measuredAt: timestamptz('measured_at'), // exact moment of the reading, when known
source: text('source'), // e.g. "Renpho", "manual"
notes: text('notes'),
...timestamps
},
(t) => [
index('body_measurements_date_idx').on(t.date),
// Dedupe keys for repeat imports (see writeRows in #lib/server/health-import).
uniqueIndex('body_measurements_date_weight_key').on(t.date, t.weightKg),
uniqueIndex('body_measurements_measured_at_key').on(t.measuredAt),
check(
'body_measurements_any_check',
sql`${t.weightKg} IS NOT NULL OR ${t.waistCm} IS NOT NULL OR ${t.bodyFatPct} IS NOT NULL`
@@ -63,6 +87,17 @@ export const healthBodyMeasurements = healthSchema.table(
'body_measurements_fat_check',
sql`${t.bodyFatPct} IS NULL OR ${t.bodyFatPct} BETWEEN 1 AND 75`
),
check(
'body_measurements_scale_check',
sql`(${t.muscleMassKg} IS NULL OR ${t.muscleMassKg} BETWEEN 5 AND 250)
AND (${t.skeletalMuscleKg} IS NULL OR ${t.skeletalMuscleKg} BETWEEN 5 AND 200)
AND (${t.boneMassKg} IS NULL OR ${t.boneMassKg} BETWEEN 0.5 AND 20)
AND (${t.visceralFat} IS NULL OR ${t.visceralFat} BETWEEN 1 AND 60)
AND (${t.bodyWaterPct} IS NULL OR ${t.bodyWaterPct} BETWEEN 20 AND 85)
AND (${t.proteinPct} IS NULL OR ${t.proteinPct} BETWEEN 5 AND 40)
AND (${t.bmrKcal} IS NULL OR ${t.bmrKcal} BETWEEN 500 AND 6000)
AND (${t.bmi} IS NULL OR ${t.bmi} BETWEEN 10 AND 80)`
),
check('body_measurements_date_check', sane('date'))
]
);