WP 9c Phase 1 #12

Merged
Lars merged 14 commits from develop into main 2026-03-22 14:14:34 +01:00
Showing only changes of commit b22481d4ce - Show all commits

View File

@ -48,6 +48,17 @@ export default function SleepPage() {
setTimeout(() => setToast(null), 4000)
}
// Clean data: convert empty strings to null for optional integer fields
const cleanSleepData = (data) => ({
...data,
quality: data.quality === '' ? null : data.quality,
wake_count: data.wake_count === '' ? 0 : data.wake_count,
deep_minutes: data.deep_minutes === '' ? null : data.deep_minutes,
rem_minutes: data.rem_minutes === '' ? null : data.rem_minutes,
light_minutes: data.light_minutes === '' ? null : data.light_minutes,
awake_minutes: data.awake_minutes === '' ? null : data.awake_minutes,
})
const handleImport = async (file) => {
if (!file) return
if (!file.name.endsWith('.csv')) {
@ -248,7 +259,7 @@ export default function SleepPage() {
<NewEntryForm
onSave={async (data) => {
try {
await api.createSleep(data)
await api.createSleep(cleanSleepData(data))
await load()
setEditingId(null)
showToast('Gespeichert')
@ -283,7 +294,7 @@ export default function SleepPage() {
onCancelEdit={() => setEditingId(null)}
onSave={async (data) => {
try {
await api.updateSleep(entry.id, data)
await api.updateSleep(entry.id, cleanSleepData(data))
await load()
setEditingId(null)
showToast('Gespeichert')
@ -291,6 +302,7 @@ export default function SleepPage() {
showToast(err.message, 'error')
}
}}
cleanSleepData={cleanSleepData}
onDelete={() => handleDelete(entry.id, entry.date)}
formatDuration={formatDuration}
getSourceBadge={getSourceBadge}
@ -322,6 +334,27 @@ function SleepEntry({ entry, expanded, editing, onToggleExpand, onEdit, onCancel
const [saving, setSaving] = useState(false)
const [plausibilityError, setPlausibilityError] = useState(null)
const [suggestedDuration, setSuggestedDuration] = useState(null)
// Auto-calculate duration from bedtime + wake_time
useEffect(() => {
if (editing && formData.bedtime && formData.wake_time) {
const [bedH, bedM] = formData.bedtime.split(':').map(Number)
const [wakeH, wakeM] = formData.wake_time.split(':').map(Number)
let bedMinutes = bedH * 60 + bedM
let wakeMinutes = wakeH * 60 + wakeM
if (wakeMinutes < bedMinutes) {
wakeMinutes += 24 * 60
}
const duration = wakeMinutes - bedMinutes
setSuggestedDuration(duration)
} else {
setSuggestedDuration(null)
}
}, [editing, formData.bedtime, formData.wake_time])
// Live plausibility check
useEffect(() => {
@ -386,6 +419,12 @@ function SleepEntry({ entry, expanded, editing, onToggleExpand, onEdit, onCancel
/>
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 2 }}>
= {formatDuration(formData.duration_minutes)}
{suggestedDuration && suggestedDuration !== formData.duration_minutes && (
<span style={{ marginLeft: 8, color: 'var(--accent)', cursor: 'pointer' }}
onClick={() => setFormData({ ...formData, duration_minutes: suggestedDuration })}>
💡 Vorschlag: {formatDuration(suggestedDuration)} (übernehmen?)
</span>
)}
</div>
</div>
@ -690,6 +729,28 @@ function NewEntryForm({ onSave, onCancel, formatDuration }) {
const [saving, setSaving] = useState(false)
const [plausibilityError, setPlausibilityError] = useState(null)
const [showDetail, setShowDetail] = useState(false)
const [suggestedDuration, setSuggestedDuration] = useState(null)
// Auto-calculate duration from bedtime + wake_time
useEffect(() => {
if (formData.bedtime && formData.wake_time) {
const [bedH, bedM] = formData.bedtime.split(':').map(Number)
const [wakeH, wakeM] = formData.wake_time.split(':').map(Number)
let bedMinutes = bedH * 60 + bedM
let wakeMinutes = wakeH * 60 + wakeM
// If wake time < bed time, add 24 hours (crossed midnight)
if (wakeMinutes < bedMinutes) {
wakeMinutes += 24 * 60
}
const duration = wakeMinutes - bedMinutes
setSuggestedDuration(duration)
} else {
setSuggestedDuration(null)
}
}, [formData.bedtime, formData.wake_time])
// Live plausibility check
useEffect(() => {
@ -750,6 +811,12 @@ function NewEntryForm({ onSave, onCancel, formatDuration }) {
/>
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}>
= {formatDuration(formData.duration_minutes)}
{suggestedDuration && suggestedDuration !== formData.duration_minutes && (
<span style={{ marginLeft: 8, color: 'var(--accent)', cursor: 'pointer' }}
onClick={() => setFormData({ ...formData, duration_minutes: suggestedDuration })}>
💡 Vorschlag: {formatDuration(suggestedDuration)} (übernehmen?)
</span>
)}
</div>
</div>