Commit Graph

8 Commits

Author SHA1 Message Date
9aeb0de936 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
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>
2026-03-22 14:01:47 +01:00
1644b34d5c 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
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>
2026-03-22 13:43:02 +01:00
b52c877367 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
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>
2026-03-22 13:09:34 +01:00
da376a8b18 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
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>
2026-03-22 12:57:20 +01:00
9a9c597187 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
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>
2026-03-22 12:09:25 +01:00
b1a92c01fc 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
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>
2026-03-22 11:49:09 +01:00
836bc4294b 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
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>
2026-03-22 08:28:44 +01:00
ef81c46bc0 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
- 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>
2026-03-22 08:17:11 +01:00