feat(dashboard): add immersive day range views

This commit is contained in:
2026-05-18 16:32:22 +02:00
parent e953d0fd42
commit 83b4686b6f
12 changed files with 3724 additions and 567 deletions
+165
View File
@@ -634,3 +634,168 @@ function find_sport_types(array $settings, array $ids): array
return $types;
}
function signal_scale_options(): array
{
return [
-2 => 'sehr niedrig',
-1 => 'niedrig',
0 => 'neutral',
1 => 'hoch',
2 => 'sehr hoch',
];
}
function signal_labels_for_metric(string $metric): array
{
return match ($metric) {
'stress' => [
-2 => 'sehr ruhig',
-1 => 'ruhig',
0 => 'neutral',
1 => 'angespannt',
2 => 'sehr angespannt',
],
'energy' => [
-2 => 'leer',
-1 => 'matt',
0 => 'okay',
1 => 'wach',
2 => 'kraftvoll',
],
default => [
-2 => 'sehr niedrig',
-1 => 'niedrig',
0 => 'neutral',
1 => 'hoch',
2 => 'sehr hoch',
],
};
}
function normalize_signal_value(mixed $value): int
{
return max(-2, min(2, (int) $value));
}
function signal_to_legacy_scale(mixed $value): int
{
return match (normalize_signal_value($value)) {
-2 => 1,
-1 => 3,
0 => 5,
1 => 7,
2 => 9,
};
}
function legacy_to_signal_scale(mixed $value): int
{
$legacy = max(1, min(10, (int) $value));
return match (true) {
$legacy <= 2 => -2,
$legacy <= 4 => -1,
$legacy <= 6 => 0,
$legacy <= 8 => 1,
default => 2,
};
}
function day_event_type_options(): array
{
return [
'event' => [
'label' => 'Ereignis',
'icon' => '/assets/icons/activity-event.svg',
'unit' => '',
],
'walk' => [
'label' => 'Spaziergang',
'icon' => sport_icon_path('hike'),
'unit' => 'min',
],
'sport' => [
'label' => 'Sport',
'icon' => sport_icon_path('run'),
'unit' => 'min',
],
'sleep' => [
'label' => 'Schlaf',
'icon' => '/assets/icons/activity-sleep.svg',
'unit' => 'h',
],
'alcohol' => [
'label' => 'Alkohol',
'icon' => icon_path('alcohol'),
'unit' => '',
],
];
}
function day_event_type_label(string $type): string
{
return day_event_type_options()[$type]['label'] ?? day_event_type_options()['event']['label'];
}
function day_event_type_icon(string $type): string
{
return day_event_type_options()[$type]['icon'] ?? day_event_type_options()['event']['icon'];
}
function day_event_type_unit(string $type): string
{
return day_event_type_options()[$type]['unit'] ?? '';
}
function signal_badge_tone(int $value, string $metric): string
{
$value = normalize_signal_value($value);
if ($metric === 'stress') {
return match (true) {
$value <= -1 => 'good',
$value === 0 => 'neutral',
default => 'warn',
};
}
return match (true) {
$value <= -1 => 'warn',
$value === 0 => 'neutral',
default => 'good',
};
}
function signal_combo_score(mixed $mood, mixed $energy, mixed $stress): int
{
return max(-2, min(2, (int) round((
normalize_signal_value($mood) +
normalize_signal_value($energy) -
normalize_signal_value($stress)
) / 3)));
}
function day_entry_has_content(array $entry): bool
{
if (trim((string) ($entry['summary']['comment'] ?? '')) !== '') {
return true;
}
if (trim((string) ($entry['background_image'] ?? '')) !== '') {
return true;
}
return count(array_filter(is_array($entry['events'] ?? null) ? $entry['events'] : [], 'is_array')) > 0;
}
function signal_value_class(int $value): string
{
return match (normalize_signal_value($value)) {
-2 => 'neg2',
-1 => 'neg1',
0 => 'zero',
1 => 'pos1',
2 => 'pos2',
};
}