diff --git a/assets/css/app.css b/assets/css/app.css index ec760a2..fd8036a 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -826,6 +826,12 @@ button { line-height: 1.35; } +.sport-choice__card small { + color: var(--muted); + font-size: 0.82rem; + line-height: 1.3; +} + .sport-choice input:checked + .sport-choice__card { border-color: rgba(139, 228, 255, 0.44); background: linear-gradient(180deg, rgba(96, 184, 255, 0.18), rgba(255, 255, 255, 0.08)); @@ -1002,6 +1008,12 @@ input[type="range"] { flex-wrap: wrap; } +.section-actions { + display: flex; + justify-content: flex-end; + margin-top: 0.95rem; +} + .primary-button, .ghost-button, .button-link { diff --git a/assets/js/app.js b/assets/js/app.js index 110f3d2..6410f0b 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -763,11 +763,16 @@ const syncPreview = row => { const labelInput = row.querySelector('input[data-name-template$="[label]"]'); const iconSelect = row.querySelector('select[data-name-template$="[icon]"]'); + const locationSelect = row.querySelector('select[data-name-template$="[location]"]'); const previewText = row.querySelector(".sport-pill span:last-child"); const previewImage = row.querySelector(".sport-pill img"); if (previewText) { - previewText.textContent = (labelInput && labelInput.value.trim()) || "Neue Sportart"; + const label = (labelInput && labelInput.value.trim()) || "Neue Sportart"; + const location = locationSelect && locationSelect.value + ? locationSelect.options[locationSelect.selectedIndex]?.textContent || "" + : ""; + previewText.textContent = location ? `${label} · ${location}` : label; } if (previewImage && iconSelect) { @@ -808,6 +813,7 @@ const idInput = row.querySelector('input[data-name-template$="[id]"]'); const labelInput = row.querySelector('input[data-name-template$="[label]"]'); const iconSelect = row.querySelector('select[data-name-template$="[icon]"]'); + const locationSelect = row.querySelector('select[data-name-template$="[location]"]'); const groupInput = row.querySelector('input[data-name-template$="[recovery_group]"]'); const bonusInput = row.querySelector('input[data-name-template$="[bonus_points]"]'); const consecutiveInput = row.querySelector('input[data-name-template$="[allow_consecutive]"]'); @@ -821,6 +827,9 @@ if (iconSelect) { iconSelect.value = values.icon || "run"; } + if (locationSelect) { + locationSelect.value = values.location || ""; + } if (groupInput) { groupInput.value = values.recovery_group || ""; } @@ -845,6 +854,7 @@ id: button.dataset.id || "", label: button.dataset.label || "", icon: button.dataset.icon || "run", + location: button.dataset.location || "", recovery_group: button.dataset.recoveryGroup || "", bonus_points: button.dataset.bonusPoints || "2", allow_consecutive: button.dataset.allowConsecutive === "1", diff --git a/src/App.php b/src/App.php index 24fe773..aad933b 100644 --- a/src/App.php +++ b/src/App.php @@ -330,6 +330,7 @@ final class App 'authUser' => $user, 'settings' => $settings, 'sportTypePresets' => $sportTypePresets, + 'sportLocationOptions' => sport_location_options(), 'users' => $user['is_admin'] ? $this->users->all() : [], 'maxScore' => $this->scoring->evaluate([ 'mood' => 10, @@ -481,7 +482,12 @@ final class App 'sport' => $entry['sport_minutes'], 'walk' => $entry['walk_minutes'], 'sport_labels' => array_values(array_filter(array_map( - static fn (array $type): string => (string) ($type['label'] ?? ''), + static function (array $type): string { + $label = (string) ($type['label'] ?? ''); + $location = sport_location_label((string) ($type['location'] ?? '')); + + return $location !== '' ? $label . ' · ' . $location : $label; + }, $entry['sport_type_meta'] ?? [] ))), 'sport_icons' => array_values(array_filter(array_map( diff --git a/src/Support/Defaults.php b/src/Support/Defaults.php index 25bf8e0..e95b462 100644 --- a/src/Support/Defaults.php +++ b/src/Support/Defaults.php @@ -21,6 +21,7 @@ final class Defaults 'id' => 'running', 'label' => 'Joggen', 'icon' => 'run', + 'location' => '', 'recovery_group' => 'joggen', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -29,6 +30,7 @@ final class Defaults 'id' => 'cycling', 'label' => 'Radfahren', 'icon' => 'bike', + 'location' => '', 'recovery_group' => 'radfahren', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -37,6 +39,7 @@ final class Defaults 'id' => 'strength', 'label' => 'Krafttraining', 'icon' => 'strength', + 'location' => '', 'recovery_group' => 'kraftsport', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -45,6 +48,7 @@ final class Defaults 'id' => 'hiking', 'label' => 'Wandern', 'icon' => 'hike', + 'location' => '', 'recovery_group' => 'wandern', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -53,6 +57,7 @@ final class Defaults 'id' => 'swimming', 'label' => 'Schwimmen', 'icon' => 'swim', + 'location' => '', 'recovery_group' => 'schwimmen', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -61,6 +66,7 @@ final class Defaults 'id' => 'yoga', 'label' => 'Yoga', 'icon' => 'yoga', + 'location' => '', 'recovery_group' => 'yoga', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -69,6 +75,7 @@ final class Defaults 'id' => 'hiit-workout', 'label' => 'HIIT / Workout', 'icon' => 'hiit', + 'location' => '', 'recovery_group' => 'hiit', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -77,6 +84,7 @@ final class Defaults 'id' => 'rowing', 'label' => 'Rudern', 'icon' => 'row', + 'location' => '', 'recovery_group' => 'rudern', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -85,6 +93,7 @@ final class Defaults 'id' => 'dance', 'label' => 'Tanzen', 'icon' => 'dance', + 'location' => '', 'recovery_group' => 'tanzen', 'bonus_points' => 2, 'allow_consecutive' => false, @@ -93,6 +102,7 @@ final class Defaults 'id' => 'core', 'label' => 'Core', 'icon' => 'core', + 'location' => '', 'recovery_group' => 'core', 'bonus_points' => 2, 'allow_consecutive' => true, diff --git a/src/helpers.php b/src/helpers.php index 2a1e91a..69044cb 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -240,6 +240,23 @@ function sport_icon_options(): array ]; } +function sport_location_options(): array +{ + return [ + '' => 'ohne Angabe', + 'home' => 'Zuhause', + 'away' => 'Auswärts', + ]; +} + +function sport_location_label(?string $value): string +{ + $options = sport_location_options(); + $value = is_string($value) ? trim($value) : ''; + + return $options[$value] ?? ''; +} + function normalize_sport_type_id(string $value): string { $value = trim(strtr($value, [ @@ -300,6 +317,11 @@ function normalized_sport_types(array $settings): array $icon = 'run'; } + $location = trim((string) ($type['location'] ?? '')); + if (!array_key_exists($location, sport_location_options())) { + $location = ''; + } + $group = trim((string) ($type['recovery_group'] ?? '')); if ($group === '') { $group = $id; @@ -309,6 +331,7 @@ function normalized_sport_types(array $settings): array 'id' => $id, 'label' => $label, 'icon' => $icon, + 'location' => $location, 'recovery_group' => normalize_sport_type_id($group) ?: $id, 'bonus_points' => max(0, min(20, (int) ($type['bonus_points'] ?? 0))), 'allow_consecutive' => !empty($type['allow_consecutive']), diff --git a/templates/pages/archive.php b/templates/pages/archive.php index f73e62c..d973503 100644 --- a/templates/pages/archive.php +++ b/templates/pages/archive.php @@ -22,7 +22,7 @@ - + @@ -65,7 +65,7 @@ - + diff --git a/templates/pages/options.php b/templates/pages/options.php index 9c92271..d82d901 100644 --- a/templates/pages/options.php +++ b/templates/pages/options.php @@ -82,6 +82,7 @@ data-id="" data-label="" data-icon="" + data-location="" data-recovery-group="" data-bonus-points="" data-allow-consecutive="" @@ -114,10 +115,21 @@ + + + +
+

Die Erholungsgruppe brauchst du nur, wenn mehrere Varianten dieselbe Belastung teilen sollen, zum Beispiel Krafttraining Zuhause und Krafttraining Auswärts. Wenn du nichts Besonderes einträgst, reicht die Sportart selbst.

+
- +
@@ -140,6 +154,10 @@
+
+ +
+