Feature: JETZT-Welt — Streak+Gassirunde+Übung als kompakte 3er-Chip-Zeile, SW by-v650

This commit is contained in:
rene 2026-05-03 11:07:27 +02:00
parent 87d3006aa7
commit a84df71383
5 changed files with 71 additions and 43 deletions

View file

@ -7805,6 +7805,48 @@ svg.empty-state-icon {
padding: 4px 2px 0; padding: 4px 2px 0;
} }
/* JETZT-Chip-Reihe: Streak | Gassirunde | Übung */
.wj-chip-row {
display: flex;
gap: 8px;
}
.wj-chip {
flex: 1;
min-width: 0;
background: rgba(0, 0, 0, 0.32);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-radius: 14px;
padding: 10px 6px 9px;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition: background 0.12s;
}
.wj-chip:active { background: rgba(0, 0, 0, 0.52); }
.wj-chip-label {
font-size: 8px;
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.45);
line-height: 1;
}
.wj-chip-val {
font-size: 10px;
font-weight: 700;
color: white;
line-height: 1.25;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
/* Footer-Links (Impressum / Die 100 / Datenschutz) */ /* Footer-Links (Impressum / Die 100 / Datenschutz) */
.world-footer-links { .world-footer-links {
text-align: center; text-align: center;

View file

@ -93,9 +93,9 @@
</script> </script>
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung --> <!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=649"> <link rel="stylesheet" href="/css/design-system.css?v=650">
<link rel="stylesheet" href="/css/layout.css?v=649"> <link rel="stylesheet" href="/css/layout.css?v=650">
<link rel="stylesheet" href="/css/components.css?v=649"> <link rel="stylesheet" href="/css/components.css?v=650">
</head> </head>
<body> <body>
@ -565,7 +565,7 @@
<script src="/js/api.js?v=94"></script> <script src="/js/api.js?v=94"></script>
<script src="/js/ui.js?v=94"></script> <script src="/js/ui.js?v=94"></script>
<script src="/js/app.js?v=94"></script> <script src="/js/app.js?v=94"></script>
<script src="/js/worlds.js?v=649"></script> <script src="/js/worlds.js?v=650"></script>
<!-- Feature-Seiten werden lazy geladen --> <!-- Feature-Seiten werden lazy geladen -->

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung. Router, State-Management, Navigation, Initialisierung.
============================================================ */ ============================================================ */
const APP_VER = '649'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VER = '650'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.2.1'; // ← semantische Version, wird bei make release gesetzt const APP_VERSION = '1.2.1'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app'; const IS_STAGING = location.hostname === 'staging.banyaro.app';

View file

@ -647,25 +647,18 @@ window.Worlds = (() => {
? `${Math.round(w.temp_c)}° ${_esc(w.desc?.split(' ')[0] || '')} · ${Math.round(w.wind_kmh || 0)} km/h · ${w.precip_prob || 0}% Regen` ? `${Math.round(w.temp_c)}° ${_esc(w.desc?.split(' ')[0] || '')} · ${Math.round(w.wind_kmh || 0)} km/h · ${w.precip_prob || 0}% Regen`
: ''; : '';
// Streak-Reminder // Streak für 3er-Chip-Zeile
let streakHtml = ''; let streakVal = '—', streakCol = 'rgba(255,255,255,0.4)';
if (user && dog) { if (user && dog) {
try { try {
const sr = await _cachedGet(`streak_${dog.id}`, `/streak/${dog.id}`); const sr = await _cachedGet(`streak_${dog.id}`, `/streak/${dog.id}`);
const s = sr.data; const s = sr.data;
const streak = s?.current_streak || 0; const streak = s?.current_streak || 0;
const trainedToday = s?.last_training_date === new Date().toISOString().slice(0,10); const trainedToday = s?.last_training_date === new Date().toISOString().slice(0,10);
const col = trainedToday ? 'rgba(16,185,129,0.8)' : (streak > 0 ? 'rgba(245,158,11,0.8)' : 'rgba(255,255,255,0.4)'); streakCol = trainedToday ? '#10B981' : (streak > 0 ? '#F59E0B' : 'rgba(255,255,255,0.4)');
const label = streak > 0 streakVal = streak > 0
? (trainedToday ? `${streak} Tage Streak` : `🔥 ${streak} Tage — heute noch trainieren!`) ? (trainedToday ? `${streak} Tage` : `🔥 ${streak} Tage`)
: (trainedToday ? '✓ Heute trainiert' : 'Noch kein Training heute'); : (trainedToday ? '✓ Heute' : 'Heute starten');
streakHtml = `
<div class="world-reminder" data-wnav="uebungen">
<svg class="ph-icon" style="width:1.1rem;height:1.1rem;color:${col}">
<use href="/icons/phosphor.svg#target"></use>
</svg>
<span style="font-size:var(--text-xs);font-weight:700;color:white">${label}</span>
</div>`;
} catch {} } catch {}
} }
@ -716,32 +709,25 @@ window.Worlds = (() => {
</div> </div>
</div> </div>
${alertHtml} ${alertHtml}
${streakHtml}
${user && dog ? ` ${user && dog ? `
<div id="wj-suggestions" style="display:flex;flex-direction:column;gap:8px"> <div class="wj-chip-row">
<div class="world-reminder" id="wj-route-chip" data-wnav="routes"> <div class="wj-chip" data-wnav="uebungen">
<svg class="ph-icon" style="width:1.1rem;height:1.1rem;color:var(--c-primary)"> <svg class="ph-icon" style="width:1.2rem;height:1.2rem;color:${streakCol}">
<use href="/icons/phosphor.svg#path"></use></svg>
<div style="flex:1;min-width:0">
<div style="font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
color:rgba(255,255,255,0.45);line-height:1">Gassirunde</div>
<div id="wj-route-val" style="font-size:var(--text-xs);font-weight:700;color:white;margin-top:3px;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis">Berechne</div>
</div>
<svg class="ph-icon" style="width:.9rem;height:.9rem;color:rgba(255,255,255,0.35);flex-shrink:0">
<use href="/icons/phosphor.svg#arrow-right"></use></svg>
</div>
<div class="world-reminder" id="wj-exercise-chip" data-wnav="uebungen">
<svg class="ph-icon" style="width:1.1rem;height:1.1rem;color:var(--c-primary)">
<use href="/icons/phosphor.svg#target"></use></svg> <use href="/icons/phosphor.svg#target"></use></svg>
<div style="flex:1;min-width:0"> <span class="wj-chip-label">Streak</span>
<div style="font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase; <span class="wj-chip-val">${streakVal}</span>
color:rgba(255,255,255,0.45);line-height:1">Übung des Tages</div> </div>
<div id="wj-exercise-val" style="font-size:var(--text-xs);font-weight:700;color:white;margin-top:3px; <div class="wj-chip" data-wnav="routes">
white-space:nowrap;overflow:hidden;text-overflow:ellipsis">Lade</div> <svg class="ph-icon" style="width:1.2rem;height:1.2rem;color:var(--c-primary)">
</div> <use href="/icons/phosphor.svg#path"></use></svg>
<svg class="ph-icon" style="width:.9rem;height:.9rem;color:rgba(255,255,255,0.35);flex-shrink:0"> <span class="wj-chip-label">Gassirunde</span>
<use href="/icons/phosphor.svg#arrow-right"></use></svg> <span class="wj-chip-val" id="wj-route-val"></span>
</div>
<div class="wj-chip" data-wnav="uebungen">
<svg class="ph-icon" style="width:1.2rem;height:1.2rem;color:var(--c-primary)">
<use href="/icons/phosphor.svg#barbell"></use></svg>
<span class="wj-chip-label">Übung</span>
<span class="wj-chip-val" id="wj-exercise-val"></span>
</div> </div>
</div>` : ''} </div>` : ''}
</div> </div>

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache Offline-Cache + Push Notifications + Tile-Cache
============================================================ */ ============================================================ */
const CACHE_VERSION = 'by-v649'; const CACHE_VERSION = 'by-v650';
const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache