Refine Health import event presentation

This commit is contained in:
2026-05-19 15:54:50 +02:00
parent 59c7d89e81
commit 3e497a8047
6 changed files with 392 additions and 47 deletions
+67 -21
View File
@@ -82,11 +82,13 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
<?php foreach ($dashboardTimeline as $item): ?>
<?php $eventType = (string) ($item['type'] ?? 'event'); ?>
<?php $eventComment = trim((string) ($item['comment'] ?? '')); ?>
<?php $isImportedHealth = (string) ($item['source'] ?? '') === 'health_auto_export'; ?>
<?php $sportType = $eventType === 'sport' ? find_sport_type($settings, (string) ($item['sport_type_id'] ?? '')) : null; ?>
<?php $isImportedWalkSport = $isImportedHealth && $eventType === 'sport' && str_contains(strtolower((string) ($sportType['label'] ?? $eventComment)), 'spaziergang'); ?>
<?php $eventTone = signal_value_class(normalize_signal_value($item['mood'] ?? 0)); ?>
<?php $eventValueText = (float) $item['value'] > 0 ? rtrim(rtrim(number_format((float) $item['value'], 2, ',', '.'), '0'), ',') . ' ' . (string) $item['unit'] : ''; ?>
<?php $eventTitle = match ($eventType) {
'sport' => (string) ($sportType['label'] ?? 'Sport'),
'sport' => $isImportedWalkSport ? 'Spaziergang' : (string) ($sportType['label'] ?? 'Sport'),
'walk' => 'Spaziergang',
'sleep' => 'Schlaf',
default => (string) ($item['comment'] !== '' ? $item['comment'] : day_event_type_label($eventType)),
@@ -96,13 +98,31 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
'walk', 'sleep' => trim($eventValueText),
default => trim($eventValueText . ($sportType !== null ? ' · ' . (string) ($sportType['label'] ?? '') : '')),
}; ?>
<?php $showEventComment = in_array($eventType, ['sport', 'walk', 'sleep'], true) && $eventComment !== ''; ?>
<?php $showEventComment = in_array($eventType, ['sport', 'walk', 'sleep'], true) && $eventComment !== '' && !$isImportedHealth; ?>
<?php
$sleepPhases = ['deep' => (float) ($item['sleep_deep'] ?? 0), 'rem' => (float) ($item['sleep_rem'] ?? 0), 'core' => (float) ($item['sleep_core'] ?? 0)];
if ($eventType === 'sleep' && array_sum($sleepPhases) <= 0 && $eventComment !== '') {
if (preg_match('/Tief\s+([0-9]+(?:[,.][0-9]+)?)/u', $eventComment, $match) === 1) {
$sleepPhases['deep'] = (float) str_replace(',', '.', $match[1]);
}
if (preg_match('/REM\s+([0-9]+(?:[,.][0-9]+)?)/u', $eventComment, $match) === 1) {
$sleepPhases['rem'] = (float) str_replace(',', '.', $match[1]);
}
if (preg_match('/Kern\s+([0-9]+(?:[,.][0-9]+)?)/u', $eventComment, $match) === 1) {
$sleepPhases['core'] = (float) str_replace(',', '.', $match[1]);
}
}
$sleepPhaseTotal = max(0.0, array_sum($sleepPhases));
?>
<?php $eventStats = array_values(array_filter([
(string) ($item['duration_label'] ?? ''),
$eventType !== 'sleep' ? (string) ($item['duration_label'] ?? '') : '',
(string) ($item['distance_label'] ?? ''),
(string) ($item['energy_label'] ?? ''),
'',
(string) ($item['heart_rate_label'] ?? ''),
], static fn (string $value): bool => trim($value) !== '')); ?>
], static function (string $value): bool {
$value = trim($value);
return $value !== '' && !preg_match('/^-\s*(Distanz|Energie|Puls|Route)-?Label:?$/u', $value);
})); ?>
<?php $eventPayload = encode_payload([
'id' => (string) ($item['id'] ?? ''),
'type' => (string) ($item['type'] ?? 'event'),
@@ -122,12 +142,17 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
'distance_label' => (string) ($item['distance_label'] ?? ''),
'energy_label' => (string) ($item['energy_label'] ?? ''),
'heart_rate_label' => (string) ($item['heart_rate_label'] ?? ''),
'sleep_deep' => (float) ($item['sleep_deep'] ?? 0),
'sleep_rem' => (float) ($item['sleep_rem'] ?? 0),
'sleep_core' => (float) ($item['sleep_core'] ?? 0),
]); ?>
<?php $hasEventImage = is_string($item['image_url'] ?? null); ?>
<?php $routeMap = is_array($item['route_map'] ?? null) ? $item['route_map'] : null; ?>
<article class="timeline-card timeline-card--event timeline-card--<?= e($eventTone) ?><?= $hasEventImage ? ' timeline-card--with-image' : '' ?> glass-panel" data-event-editable data-event-payload="<?= e($eventPayload) ?>">
<?php if ($hasEventImage): ?>
<img class="timeline-card__image" src="<?= e((string) $item['image_url']) ?>" alt="">
<button class="timeline-media-button" type="button" data-lightbox-src="<?= e((string) $item['image_url']) ?>" data-lightbox-kind="image" aria-label="Bild vergrößern">
<img class="timeline-card__image" src="<?= e((string) $item['image_url']) ?>" alt="">
</button>
<?php endif; ?>
<span class="timeline-card__time-chip"><?= e($item['time'] !== '' ? $item['time'] : '--:--') ?></span>
<div class="timeline-card__meta">
@@ -156,6 +181,18 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
<p class="timeline-card__value"><?= e((string) $item['comment']) ?></p>
<?php endif; ?>
<?php if ($eventType === 'sleep' && $sleepPhaseTotal > 0): ?>
<div class="sleep-phase-bar" aria-label="Schlafphasen">
<?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; } ?>
<span class="sleep-phase-bar__segment sleep-phase-bar__segment--<?= e($class) ?>" style="flex-grow: <?= e((string) max(0.1, $phaseHours)) ?>">
<strong><?= e($label) ?></strong> <?= e(format_points($phaseHours)) ?> h
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($eventStats !== []): ?>
<div class="timeline-card__stats" aria-label="Importdetails">
<?php foreach ($eventStats as $stat): ?>
@@ -164,18 +201,6 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
</div>
<?php endif; ?>
<?php if ($routeMap !== null): ?>
<div class="timeline-route-map" aria-label="Route auf OpenStreetMap">
<svg viewBox="0 0 <?= e((string) $routeMap['width']) ?> <?= e((string) $routeMap['height']) ?>" aria-hidden="true">
<?php foreach ($routeMap['tiles'] as $tile): ?>
<image href="<?= e((string) $tile['url']) ?>" x="<?= e((string) $tile['left']) ?>" y="<?= e((string) $tile['top']) ?>" width="256" height="256"></image>
<?php endforeach; ?>
<polyline points="<?= e((string) $routeMap['line']) ?>"></polyline>
</svg>
<a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">© OpenStreetMap</a>
</div>
<?php endif; ?>
<div class="signal-row">
<?php foreach (['mood' => 'Stimmung', 'energy' => 'Energie', 'stress' => 'Stress'] as $metric => $label): ?>
<?php $value = normalize_signal_value($item[$metric] ?? 0); ?>
@@ -189,6 +214,18 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
</div>
</div>
<?php if ($routeMap !== null): ?>
<button class="timeline-route-map" type="button" data-lightbox-kind="html" aria-label="Route vergrößern">
<svg viewBox="0 0 <?= e((string) $routeMap['width']) ?> <?= e((string) $routeMap['height']) ?>" aria-hidden="true">
<?php foreach ($routeMap['tiles'] as $tile): ?>
<image href="<?= e((string) $tile['url']) ?>" x="<?= e((string) $tile['left']) ?>" y="<?= e((string) $tile['top']) ?>" width="256" height="256"></image>
<?php endforeach; ?>
<polyline points="<?= e((string) $routeMap['line']) ?>"></polyline>
</svg>
<span class="timeline-route-map__credit">© OpenStreetMap</span>
</button>
<?php endif; ?>
<form method="post" action="/" class="timeline-card__delete">
<?= csrf_field() ?>
<input type="hidden" name="form_name" value="delete_event">
@@ -518,9 +555,10 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
</div>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
</div>
<?php endif; ?>
</section>
<?php else: ?>
<section class="dashboard-range-view dashboard-range-view--month">
<header class="dashboard-range-view__hero">
@@ -602,4 +640,12 @@ $dayStepBonus = (float) ($dayEntry['evaluation']['components']['step_bonus'] ??
</div>
</section>
</div>
<div class="media-lightbox" data-media-lightbox hidden>
<button class="media-lightbox__backdrop" type="button" data-media-lightbox-close aria-label="Ansicht schließen"></button>
<div class="media-lightbox__panel" role="dialog" aria-modal="true" aria-label="Medienansicht">
<button class="media-lightbox__close" type="button" data-media-lightbox-close aria-label="Ansicht schließen">×</button>
<div class="media-lightbox__content" data-media-lightbox-content></div>
</div>
</div>
</section>