import { useState, useEffect, useRef } from 'react' import { Moon, Plus, Edit2, Trash2, X, Save, TrendingUp, Upload } from 'lucide-react' import { api } from '../utils/api' /** * SleepPage - Sleep tracking with quick/detail entry (v9d Phase 2b) * * Features: * - Quick entry: date, duration, quality * - Detail entry: bedtime, wake time, phases, wake count * - 7-day stats overview * - Sleep duration trend chart * - List with inline edit/delete */ export default function SleepPage() { const [sleep, setSleep] = useState([]) const [stats, setStats] = useState(null) const [showForm, setShowForm] = useState(false) const [showDetail, setShowDetail] = useState(false) const [editingId, setEditingId] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [importing, setImporting] = useState(false) const fileInputRef = useRef(null) // Form state const [formData, setFormData] = useState({ date: new Date().toISOString().split('T')[0], duration_minutes: 450, // 7h 30min default quality: 3, bedtime: '', wake_time: '', wake_count: 0, deep_minutes: null, rem_minutes: null, light_minutes: null, awake_minutes: null, note: '' }) useEffect(() => { load() }, []) const load = () => { setLoading(true) Promise.all([ api.listSleep(30), api.getSleepStats(7).catch(() => null) // Stats optional - don't fail if error ]).then(([sleepData, statsData]) => { setSleep(sleepData) setStats(statsData) setLoading(false) }).catch(err => { console.error('Failed to load sleep data:', err) setLoading(false) }) } const startCreate = () => { setFormData({ date: new Date().toISOString().split('T')[0], duration_minutes: 450, quality: 3, bedtime: '', wake_time: '', wake_count: 0, deep_minutes: null, rem_minutes: null, light_minutes: null, awake_minutes: null, note: '' }) setShowForm(true) setShowDetail(false) setEditingId(null) } const startEdit = (entry) => { setFormData({ date: entry.date, duration_minutes: entry.duration_minutes, quality: entry.quality, bedtime: entry.bedtime || '', wake_time: entry.wake_time || '', wake_count: entry.wake_count || 0, deep_minutes: entry.deep_minutes, rem_minutes: entry.rem_minutes, light_minutes: entry.light_minutes, awake_minutes: entry.awake_minutes, note: entry.note || '' }) setEditingId(entry.id) setShowForm(true) setShowDetail(true) // Show detail if phases exist } const cancelEdit = () => { setShowForm(false) setEditingId(null) setShowDetail(false) } const handleSave = async () => { if (!formData.date || formData.duration_minutes <= 0) { alert('Datum und Schlafdauer sind Pflichtfelder') return } setSaving(true) try { if (editingId) { await api.updateSleep(editingId, formData) } else { await api.createSleep(formData) } await load() cancelEdit() } catch (err) { alert('Speichern fehlgeschlagen: ' + err.message) } finally { setSaving(false) } } const handleDelete = async (id, date) => { if (!confirm(`Schlaf-Eintrag vom ${date} wirklich löschen?`)) return try { await api.deleteSleep(id) await load() } catch (err) { alert('Löschen fehlgeschlagen: ' + err.message) } } const handleImport = async (e) => { const file = e.target.files[0] if (!file) return if (!file.name.endsWith('.csv')) { alert('Bitte eine CSV-Datei auswählen') return } setImporting(true) try { const result = await api.importAppleHealthSleep(file) await load() alert(result.message || `✅ ${result.imported} Nächte importiert, ${result.skipped} übersprungen`) } catch (err) { alert('Import fehlgeschlagen: ' + err.message) } finally { setImporting(false) if (fileInputRef.current) { fileInputRef.current.value = '' // Reset input } } } const formatDuration = (minutes) => { const h = Math.floor(minutes / 60) const m = minutes % 60 return `${h}h ${m}min` } if (loading) { return (
) } return (
{/* Header */}

Schlaf

{/* Stats Card */} {stats && (
Übersicht (letzte 7 Tage)
{formatDuration(Math.round(stats.avg_duration_minutes))}
Ø Schlafdauer
{stats.avg_quality ? stats.avg_quality.toFixed(1) : '—'}/5
Ø Qualität
{stats.nights_below_goal > 0 && (
⚠️ {stats.nights_below_goal} Nächte unter Ziel ({formatDuration(stats.sleep_goal_minutes)})
)}
)} {/* Action Buttons */} {!showForm && (
)} {/* Entry Form */} {showForm && (
{editingId ? '✏️ Eintrag bearbeiten' : '➕ Neuer Eintrag'}
{/* Quick Entry Fields */}
Datum
setFormData({ ...formData, date: e.target.value })} style={{ width: '100%' }} />
Schlafdauer (Minuten)
setFormData({ ...formData, duration_minutes: parseInt(e.target.value) || 0 })} style={{ width: '100%' }} min="1" />
= {formatDuration(formData.duration_minutes)}
Qualität
Notiz (optional)