WP 9c Phase 1 #12

Merged
Lars merged 14 commits from develop into main 2026-03-22 14:14:34 +01:00
Owner
No description provided.
Lars added 14 commits 2026-03-22 14:14:25 +01:00
docs: mark v9d Phase 1b as deployed to production
All checks were successful
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
40a4739349
- Successful production deployment confirmed (21.03.2026)
- Document complete learnable mapping system
- List all 4 migrations (004-007)
- Update roadmap: Phase 2 next

v9d Phase 1b complete:
- 29 training types
- DB-based learnable mapping system
- Apple Health import with German support
- Inline editing UX
- Auto-learning from bulk categorization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: v9d Phase 2b - Sleep Module Core (Schlaf-Modul)
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
ef81c46bc0
- Add sleep_log table with JSONB sleep_segments (Migration 009)
- Add sleep router with CRUD + stats endpoints (7d avg, 14d debt, trend, phases)
- Add SleepPage with quick/detail entry forms and inline edit
- Add SleepWidget to Dashboard showing last night + 7d average
- Add sleep navigation entry with Moon icon
- Register sleep router in main.py
- Add 9 new API methods in api.js

Phase 2b complete - ready for testing on dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: migration 009 - change profile_id from VARCHAR(36) to UUID
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
39d676e5c8
Profile IDs are UUID type in the profiles table, not VARCHAR.
This was causing foreign key constraint error on migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: convert empty strings to None for TIME fields in sleep router
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
836bc4294b
PostgreSQL TIME type doesn't accept empty strings.
Converting empty bedtime/wake_time to None before INSERT/UPDATE.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: make sleep stats optional to prevent page crash
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
9e4d6fa715
If stats endpoint fails, page will still load with empty stats.
This prevents 500 errors from blocking the entire sleep page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add missing migration 008 (vitals, rest days, sleep_goal_minutes)
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
b65efd3b71
- Creates rest_days table for rest day tracking
- Creates vitals_log table for resting HR + HRV
- Creates weekly_goals table for training planning
- Extends profiles with hf_max and sleep_goal_minutes columns
- Extends activity_log with avg_hr and max_hr columns
- Fixes sleep_goal_minutes missing column error in stats endpoint
- Includes stats error handling in SleepWidget

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: Apple Health CSV import for sleep data (v9d Phase 2c)
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 12s
b1a92c01fc
Backend:
- New endpoint POST /api/sleep/import/apple-health
- Parses Apple Health sleep CSV format
- Maps German phase names (Kern→light, REM→rem, Tief→deep, Wach→awake)
- Aggregates segments by night (wake date)
- Stores raw segments in JSONB (sleep_segments)
- Does NOT overwrite manual entries (source='manual')

Frontend:
- Import button in SleepPage with file picker
- Progress indicator during import
- Success/error messages
- Auto-refresh after import

Documentation:
- Added architecture rules reference to CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: sleep import groups segments by gap instead of date boundary
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
9a9c597187
Problem: Segments crossing midnight were split into different nights
- 22:30-23:15 (21.03) → assigned to 21.03
- 00:30-02:45 (22.03) → assigned to 22.03
But both belong to the same night (21/22.03)!

Solution: Gap-based grouping
- Sort segments chronologically
- Group segments with gap < 2 hours
- Night date = wake_time.date() (last segment's end date)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: store full datetime in sleep_segments JSONB
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
da376a8b18
Enhanced sleep_segments data structure:
- start: ISO datetime (2026-03-21T22:30:00) instead of HH:MM
- end: ISO datetime (2026-03-21T23:15:00) - NEW
- phase: sleep phase type
- duration_min: duration in minutes

Benefits:
- Exact timestamp for each segment (no date ambiguity)
- Can reconstruct complete sleep timeline
- Enables precise cycle analysis
- Handles midnight crossings correctly

Example:
[
  {"phase": "light", "start": "2026-03-21T22:30:00", "end": "2026-03-21T23:15:00", "duration_min": 45},
  {"phase": "deep", "start": "2026-03-21T23:15:00", "end": "2026-03-22T00:30:00", "duration_min": 75}
]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: complete sleep module overhaul - app standard compliance
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
b52c877367
Backend improvements:
- Plausibility check: phases must sum to duration (±5 min tolerance)
- Auto-calculate wake_count from awake segments in import
- Applied to both create_sleep and update_sleep endpoints

Frontend complete rewrite:
-  Drag & Drop CSV import (like NutritionPage)
-  Inline editing (no scroll to top, edit directly in list)
-  Toast notifications (no more alerts, auto-dismiss 4s)
-  Source badges (Manual/Apple Health/Garmin with colors)
-  Expandable segment timeline view (JSONB sleep_segments)
-  Live plausibility check (shows error if phases ≠ duration)
-  Color-coded sleep phases (deep/rem/light/awake)
-  Show wake_count in list view

Design improvements:
- Stats card on top (7-day avg)
- Import drag zone with visual feedback
- Clean inline edit mode with validation
- Timeline view with phase colors
- Responsive button layout

Confirmed: Kernschlaf (Apple Health) = Leichtschlaf (light_minutes) ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: manual sleep entry creation + import overwrite protection
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
1644b34d5c
Critical fixes:
1. Added "+ Schlaf erfassen" button back (was missing!)
   - Opens NewEntryForm component inline
   - Default: 450 min (7h 30min), quality 3
   - Collapsible detail view
   - Live plausibility check

2. Fixed import overwriting manual entries
   - Problem: ON CONFLICT WHERE clause didn't prevent updates
   - Solution: Explicit if/else logic
     - If manual entry exists → skip (don't touch)
     - If non-manual entry exists → UPDATE
     - If no entry exists → INSERT
   - Properly counts imported vs skipped

Test results:
 CSV import with drag & drop
 Inline editing
 Segment timeline view with colors
 Source badges (Manual/Apple Health)
 Plausibility check (backend + frontend)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: empty string validation + auto-calculate sleep duration
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
b22481d4ce
Fixes:
1. Empty string → null conversion for optional integer fields
   - Backend validation error: "Input should be a valid integer"
   - Solution: cleanSleepData() converts '' → null before save
   - Applied to: deep/rem/light/awake minutes, quality, wake_count

2. Auto-calculate duration from bedtime + wake_time
   - useEffect watches bedtime + wake_time changes
   - Calculates minutes including midnight crossover
   - Shows clickable suggestion: "💡 Vorschlag: 7h 30min (übernehmen?)"
   - Applied to NewEntryForm + SleepEntry edit mode

3. Improved plausibility check
   - Now triggers correctly in both create and edit mode
   - Live validation as user types

Test results:
 Simple entry (date + duration) saves without error
 Detail fields (phases) trigger plausibility check
 Bedtime + wake time auto-suggest duration
 Suggestion clickable → updates duration field

Note for future release:
- Unify "Erfassen" dialog design across modules
  (Activity/Nutrition/Weight have different styles/tabs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: sleep duration excludes awake time (actual sleep only)
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
9aeb0de936
Conceptual change: duration_minutes = actual sleep time (not time in bed)

Backend:
- Plausibility check: deep + rem + light = duration (awake separate)
- Import: duration = deep + rem + light (without awake)
- Updated error message: clarifies awake not counted

Frontend:
- Label: "Schlafdauer (reine Schlafzeit, Minuten)"
- Auto-calculate: bedtime-waketime minus awake_minutes
- Plausibility check: only validates sleep phases (not awake)
- Both NewEntry and Edit mode updated

Rationale:
- Standard in sleep tracking (Apple Health shows "Sleep", not "Time in Bed")
- Clearer semantics: duration = how long you slept
- awake_minutes tracked separately for analysis
- More intuitive for users

Example:
- Time in bed: 22:00 - 06:00 = 480 min (8h)
- Awake phases: 30 min
- Sleep duration: 450 min (7h 30min) ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: move sleep to capture hub, remove from main nav
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
5adec042a4
Changes:
1. Added sleep entry to CaptureHub (between Activity and Guide)
   - Icon: 🌙
   - Label: "Schlaf"
   - Sub: "Schlafdaten erfassen oder Apple Health importieren"
   - Color: #7B68EE (purple)
   - Route: /sleep

2. Removed sleep from main bottom navigation
   - Nav link removed (was 6 items → now 5 items)
   - Moon icon import removed (no longer used)
   - Route /sleep remains active (Widget + CaptureHub links work)

3. Widget link unchanged
   - SleepWidget.jsx still links to /sleep ✓
   - Dashboard → Widget → /sleep works

Result:
- Consistent UX: All data entry under "Erfassen"
- Clean navigation: 5 main nav items (was 6)
- Sleep accessible via: Dashboard Widget or Erfassen → Schlaf

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lars merged commit 601fc80178 into main 2026-03-22 14:14:34 +01:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: Lars/mitai-jinkendo#12
No description provided.