WP 9c Phase 1 #12

Merged
Lars merged 14 commits from develop into main 2026-03-22 14:14:34 +01:00
2 changed files with 60 additions and 1 deletions
Showing only changes of commit b65efd3b71 - Show all commits

View File

@ -0,0 +1,59 @@
-- Migration 008: Vitals, Rest Days, Weekly Goals
-- v9d Phase 2: Sleep & Vitals Module
-- Date: 2026-03-22
-- Rest Days
CREATE TABLE IF NOT EXISTS rest_days (
id SERIAL PRIMARY KEY,
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
date DATE NOT NULL,
type VARCHAR(20) NOT NULL CHECK (type IN ('full_rest', 'active_recovery')),
note TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_rest_day_per_profile UNIQUE(profile_id, date)
);
CREATE INDEX idx_rest_days_profile_date ON rest_days(profile_id, date DESC);
-- Vitals (Resting HR + HRV)
CREATE TABLE IF NOT EXISTS vitals_log (
id SERIAL PRIMARY KEY,
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
date DATE NOT NULL,
resting_hr INTEGER CHECK (resting_hr > 0 AND resting_hr < 200),
hrv INTEGER CHECK (hrv > 0),
note TEXT,
source VARCHAR(20) DEFAULT 'manual' CHECK (source IN ('manual', 'apple_health', 'garmin')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_vitals_per_day UNIQUE(profile_id, date)
);
CREATE INDEX idx_vitals_profile_date ON vitals_log(profile_id, date DESC);
-- Extend activity_log for heart rate data
ALTER TABLE activity_log
ADD COLUMN IF NOT EXISTS avg_hr INTEGER CHECK (avg_hr > 0 AND avg_hr < 250),
ADD COLUMN IF NOT EXISTS max_hr INTEGER CHECK (max_hr > 0 AND max_hr < 250);
-- Extend profiles for HF max and sleep goal
ALTER TABLE profiles
ADD COLUMN IF NOT EXISTS hf_max INTEGER CHECK (hf_max > 0 AND hf_max < 250),
ADD COLUMN IF NOT EXISTS sleep_goal_minutes INTEGER DEFAULT 450 CHECK (sleep_goal_minutes > 0);
-- Weekly Goals (Soll/Ist Wochenplanung)
CREATE TABLE IF NOT EXISTS weekly_goals (
id SERIAL PRIMARY KEY,
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
week_start DATE NOT NULL,
goals JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_weekly_goal_per_profile UNIQUE(profile_id, week_start)
);
CREATE INDEX idx_weekly_goals_profile_week ON weekly_goals(profile_id, week_start DESC);
-- Comments for documentation
COMMENT ON TABLE rest_days IS 'v9d Phase 2: Rest days tracking (full rest or active recovery)';
COMMENT ON TABLE vitals_log IS 'v9d Phase 2: Daily vitals (resting HR, HRV)';
COMMENT ON TABLE weekly_goals IS 'v9d Phase 2: Weekly training goals (Soll/Ist planning)';
COMMENT ON COLUMN profiles.hf_max IS 'Maximum heart rate for HR zone calculation';
COMMENT ON COLUMN profiles.sleep_goal_minutes IS 'Sleep goal in minutes (default: 450 = 7h 30min)';

View File

@ -24,7 +24,7 @@ export default function SleepWidget() {
const load = () => {
Promise.all([
api.listSleep(1), // Get last entry
api.getSleepStats(7)
api.getSleepStats(7).catch(() => null) // Stats optional
]).then(([sleepData, statsData]) => {
setLastNight(sleepData[0] || null)
setStats(statsData)