Improve day swipe and sleep handling

This commit is contained in:
2026-05-21 13:00:10 +02:00
parent 2047cae61c
commit a087eb508b
5 changed files with 206 additions and 53 deletions
+50 -35
View File
@@ -55,20 +55,24 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
<?php if ($dashboardView === 'day'): ?>
<div class="dashboard-day" data-day-swipe data-prev-date="<?= e($dashboardPrevDate) ?>" data-next-date="<?= e($dashboardNextDate) ?>">
<div class="dashboard-day__hero" data-day-slider>
<p class="dashboard-day__eyebrow"><?= e((string) $dayWeekday) ?></p>
<h1><?= e(format_display_date((string) $dayEntry['date'], false)) ?></h1>
<div class="dashboard-day-slider" data-day-slider-shell>
<span class="day-slide-hint day-slide-hint--prev" data-day-slide-prev-hint>Vorherigen Tag laden</span>
<span class="day-slide-hint day-slide-hint--next" data-day-slide-next-hint>Nächster Tag laden</span>
<div class="dashboard-day__hero" data-day-slider>
<p class="dashboard-day__eyebrow"><?= e((string) $dayWeekday) ?></p>
<h1><?= e(format_display_date((string) $dayEntry['date'], false)) ?></h1>
<nav class="dashboard-compare-strip" aria-label="Tagesvergleich" data-day-strip>
<span class="score-scale score-scale--day" aria-hidden="true"><span>+2</span><span>+1</span><span>0</span><span>-1</span><span>-2</span></span>
<?php foreach ($dashboardCompareDays as $compareDay): ?>
<a class="compare-day offset-<?= e((string) ($compareDay['offset'] ?? 0)) ?><?= !empty($compareDay['is_current']) ? ' is-current' : '' ?><?= empty($compareDay['has_content']) ? ' is-empty' : '' ?>" href="/?view=day&amp;date=<?= e(rawurlencode((string) $compareDay['date'])) ?>">
<span class="compare-day__line<?= empty($compareDay['has_content']) ? ' is-empty' : '' ?><?= !empty($compareDay['is_current']) ? ' is-primary' : '' ?> score-<?= e((string) ($compareDay['score_level'] ?? 'empty')) ?> compare-tone-<?= e((string) ($compareDay['line_tone'] ?? 'empty')) ?>">
<span class="compare-day__marker"></span>
</span>
</a>
<?php endforeach; ?>
</nav>
<nav class="dashboard-compare-strip" aria-label="Tagesvergleich" data-day-strip>
<span class="score-scale score-scale--day" aria-hidden="true"><span>+2</span><span>+1</span><span>0</span><span>-1</span><span>-2</span></span>
<?php foreach ($dashboardCompareDays as $compareDay): ?>
<a class="compare-day offset-<?= e((string) ($compareDay['offset'] ?? 0)) ?><?= !empty($compareDay['is_current']) ? ' is-current' : '' ?><?= empty($compareDay['has_content']) ? ' is-empty' : '' ?>" href="/?view=day&amp;date=<?= e(rawurlencode((string) $compareDay['date'])) ?>">
<span class="compare-day__line<?= empty($compareDay['has_content']) ? ' is-empty' : '' ?><?= !empty($compareDay['is_current']) ? ' is-primary' : '' ?> score-<?= e((string) ($compareDay['score_level'] ?? 'empty')) ?> compare-tone-<?= e((string) ($compareDay['line_tone'] ?? 'empty')) ?>">
<span class="compare-day__marker"></span>
</span>
</a>
<?php endforeach; ?>
</nav>
</div>
</div>
<button class="day-summary-card glass-panel<?= $dashboardHasContent ? ' is-filled' : '' ?>" type="button" data-summary-overlay-open>
@@ -124,24 +128,26 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
'walk', 'sleep' => trim($eventValueText),
default => trim($eventValueText . ($sportType !== null ? ' · ' . (string) ($sportType['label'] ?? '') : '')),
}; ?>
<?php $showEventComment = in_array($eventType, ['sport', 'walk', 'sleep'], true) && $eventComment !== '' && !$isImportedHealth; ?>
<?php $showEventComment = in_array($eventType, ['sport', 'walk', 'sleep'], true) && $eventComment !== ''; ?>
<?php
$sleepPhases = ['deep' => (float) ($item['sleep_deep'] ?? 0), 'rem' => (float) ($item['sleep_rem'] ?? 0), 'core' => (float) ($item['sleep_core'] ?? 0)];
$sleepPhaseSource = trim($eventComment . ' ' . (string) ($item['duration_label'] ?? '') . ' ' . (string) ($item['distance_label'] ?? '') . ' ' . (string) ($item['energy_label'] ?? '') . ' ' . (string) ($item['heart_rate_label'] ?? ''));
if ($eventType === 'sleep' && array_sum($sleepPhases) <= 0 && $sleepPhaseSource !== '') {
if (preg_match('/(?:Tief|Tiefschlaf)\s*:?[\s]+([0-9]+(?:[,.][0-9]+)?)/u', $sleepPhaseSource, $match) === 1) {
if (preg_match('/(?:Tief|Tiefschlaf)\s*:?\s*([0-9]+(?:[,.][0-9]+)?)/u', $sleepPhaseSource, $match) === 1) {
$sleepPhases['deep'] = (float) str_replace(',', '.', $match[1]);
}
if (preg_match('/REM(?:-Schlaf)?\s*:?[\s]+([0-9]+(?:[,.][0-9]+)?)/u', $sleepPhaseSource, $match) === 1) {
if (preg_match('/REM(?:-Schlaf)?\s*:?\s*([0-9]+(?:[,.][0-9]+)?)/u', $sleepPhaseSource, $match) === 1) {
$sleepPhases['rem'] = (float) str_replace(',', '.', $match[1]);
}
if (preg_match('/(?:Kern|Kernschlaf)\s*:?[\s]+([0-9]+(?:[,.][0-9]+)?)/u', $sleepPhaseSource, $match) === 1) {
if (preg_match('/(?:Kern|Kernschlaf)\s*:?\s*([0-9]+(?:[,.][0-9]+)?)/u', $sleepPhaseSource, $match) === 1) {
$sleepPhases['core'] = (float) str_replace(',', '.', $match[1]);
}
}
$sleepPhaseTotal = max(0.0, array_sum($sleepPhases));
$sleepBarTotal = $eventType === 'sleep' ? max((float) ($item['value'] ?? 0), $sleepPhaseTotal, $optimalSleepHours / 0.75) : 0.0;
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepPhaseTotal);
$sleepActualTotal = $eventType === 'sleep' ? max((float) ($item['value'] ?? 0), $sleepPhaseTotal) : 0.0;
$sleepBarTotal = $eventType === 'sleep' ? max($sleepActualTotal, $optimalSleepHours / 0.75) : 0.0;
$sleepUnclassified = max(0.0, $sleepActualTotal - $sleepPhaseTotal);
$sleepPhaseRemainder = max(0.0, $sleepBarTotal - $sleepActualTotal);
$sleepOptimalPercent = $sleepBarTotal > 0 ? max(0, min(100, ($optimalSleepHours / $sleepBarTotal) * 100)) : 0;
$sleepPhaseLeft = 0.0;
?>
@@ -212,24 +218,33 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
<p class="timeline-card__value"><?= e((string) $item['comment']) ?></p>
<?php endif; ?>
<?php if ($eventType === 'sleep' && $sleepPhaseTotal > 0): ?>
<?php if ($eventType === 'sleep' && $sleepActualTotal > 0): ?>
<div class="sleep-phase-bar" aria-label="Schlafphasen" style="--sleep-optimal-left: <?= e((string) $sleepOptimalPercent) ?>%">
<?php foreach (['deep' => ['Tief', 'deep'], 'rem' => ['REM', 'rem'], 'core' => ['Kern', 'core']] as $phase => [$label, $class]): ?>
<?php $phaseHours = max(0.0, (float) ($sleepPhases[$phase] ?? 0)); ?>
<?php if ($phaseHours <= 0) { continue; } ?>
<?php $phasePercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($phaseHours / $sleepBarTotal) * 100)) : 0; ?>
<?php $phaseLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseLeft / $sleepBarTotal) * 100)) : 0; ?>
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--<?= e($class) ?><?= $phasePercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-left: <?= e((string) $phaseLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $phasePercent) ?>%" title="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h" data-tooltip="<?= e($label) ?>: <?= e(format_points($phaseHours)) ?> h">
<strong><?= e($label) ?></strong> <?= e(format_points($phaseHours)) ?> h
<?php if ($sleepPhaseTotal > 0): ?>
<?php foreach (['deep' => ['Tief', 'deep'], 'rem' => ['REM', 'rem'], 'core' => ['Kern', 'core']] as $phase => [$label, $class]): ?>
<?php $phaseHours = max(0.0, (float) ($sleepPhases[$phase] ?? 0)); ?>
<?php if ($phaseHours <= 0) { continue; } ?>
<?php $phasePercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($phaseHours / $sleepBarTotal) * 100)) : 0; ?>
<?php $phaseLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseLeft / $sleepBarTotal) * 100)) : 0; ?>
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--<?= e($class) ?><?= $phasePercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-left: <?= e((string) $phaseLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $phasePercent) ?>%" title="<?= e($label) ?>: <?= e(format_duration_hours($phaseHours)) ?>" data-tooltip="<?= e($label) ?>: <?= e(format_duration_hours($phaseHours)) ?>">
<strong><?= e($label) ?></strong> <?= e(format_duration_hours($phaseHours)) ?>
</span>
<?php $sleepPhaseLeft += $phaseHours; ?>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($sleepUnclassified > 0): ?>
<?php $unclassifiedPercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($sleepUnclassified / $sleepBarTotal) * 100)) : 0; ?>
<?php $unclassifiedLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseTotal / $sleepBarTotal) * 100)) : 0; ?>
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--total<?= $sleepPhaseTotal > 0 ? ' is-after-phase' : '' ?><?= $unclassifiedPercent < 13 ? ' is-compact' : '' ?>" style="--sleep-segment-left: <?= e((string) $unclassifiedLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $unclassifiedPercent) ?>%" title="Schlaf: <?= e(format_duration_hours($sleepUnclassified)) ?>" data-tooltip="Schlaf: <?= e(format_duration_hours($sleepUnclassified)) ?>">
<strong>Schlaf</strong> <?= e(format_duration_hours($sleepUnclassified)) ?>
</span>
<?php $sleepPhaseLeft += $phaseHours; ?>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($sleepPhaseRemainder > 0): ?>
<?php $remainderPercent = $sleepBarTotal > 0 ? max(0.5, min(100, ($sleepPhaseRemainder / $sleepBarTotal) * 100)) : 0; ?>
<?php $remainderLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepPhaseTotal / $sleepBarTotal) * 100)) : 0; ?>
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--rest" style="--sleep-segment-left: <?= e((string) $remainderLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $remainderPercent) ?>%" title="Bis Ziel-/Skalenende: <?= e(format_points($sleepPhaseRemainder)) ?> h" data-tooltip="Bis Ziel-/Skalenende: <?= e(format_points($sleepPhaseRemainder)) ?> h"></span>
<?php $remainderLeftPercent = $sleepBarTotal > 0 ? max(0, min(100, ($sleepActualTotal / $sleepBarTotal) * 100)) : 0; ?>
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--rest" style="--sleep-segment-left: <?= e((string) $remainderLeftPercent) ?>%; --sleep-segment-width: <?= e((string) $remainderPercent) ?>%" title="Bis Ziel-/Skalenende: <?= e(format_duration_hours($sleepPhaseRemainder)) ?>" data-tooltip="Bis Ziel-/Skalenende: <?= e(format_duration_hours($sleepPhaseRemainder)) ?>"></span>
<?php endif; ?>
<span class="sleep-phase-bar__target"><span><?= e(format_points($optimalSleepHours)) ?> h</span></span>
<span class="sleep-phase-bar__target"><span><?= e(format_duration_hours($optimalSleepHours)) ?></span></span>
</div>
<?php endif; ?>
@@ -421,7 +436,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
</label>
<label data-moment-value-field>
<span data-moment-value-label>Wert</span>
<input type="number" name="event_value" min="0" max="50000" step="0.25" placeholder="optional" data-moment-value-input>
<input type="number" name="event_value" min="0" max="50000" step="0.01" placeholder="optional" data-moment-value-input>
</label>
</div>
@@ -573,7 +588,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
$eventTone = signal_value_class($eventScore);
$sportType = $eventType === 'sport' ? find_sport_type($settings, (string) ($event['sport_type_id'] ?? '')) : null;
$eventValue = (float) ($event['value'] ?? 0);
$eventValueText = $eventValue > 0 ? rtrim(rtrim(number_format($eventValue, 2, ',', '.'), '0'), ',') . ' ' . (string) ($event['unit'] ?? '') : '';
$eventValueText = $eventValue > 0 ? ($eventType === 'sleep' ? format_duration_hours($eventValue) : rtrim(rtrim(number_format($eventValue, 2, ',', '.'), '0'), ',') . ' ' . (string) ($event['unit'] ?? '')) : '';
$eventTitle = trim((string) ($event['comment'] ?? '')) !== '' ? trim((string) $event['comment']) : day_event_type_label($eventType);
$eventDetail = $eventValueText;
@@ -581,7 +596,7 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string {
$eventTitle = (string) ($sportType['label'] ?? 'Sport');
}
if ($eventType === 'sleep') {
if ($eventType === 'sleep' && trim((string) ($event['comment'] ?? '')) === '') {
$eventTitle = 'Schlaf';
}
?>