diff --git a/frontend/js/db.js b/frontend/js/db.js index b4272dc..fadbf90 100644 --- a/frontend/js/db.js +++ b/frontend/js/db.js @@ -558,11 +558,17 @@ async function getFoodTotalsForDate(date) { } async function getActivityTotalsForDate(date) { - const rows = await select( + const manual = await select( "SELECT SUM(kcal) as kcal, SUM(minutes) as minutes FROM activity_log WHERE date = ?1", [date] ); - return rows[0] ?? { kcal: 0, minutes: 0 }; + const gps = await select( + "SELECT SUM(total_kcal) as kcal FROM gps_sessions WHERE date = ?1 AND total_kcal IS NOT NULL", + [date] + ); + const manualKcal = manual[0]?.kcal ?? 0; + const gpsKcal = gps[0]?.kcal ?? 0; + return { kcal: manualKcal + gpsKcal, minutes: manual[0]?.minutes ?? 0 }; } async function getDailyFoodTotals(days) { @@ -577,8 +583,11 @@ async function getDailyFoodTotals(days) { async function getDailyActivityTotals(days) { return select( - `SELECT date, SUM(kcal) as kcal FROM activity_log - WHERE date >= date('now', ?1) GROUP BY date ORDER BY date ASC`, + `SELECT date, SUM(kcal) as kcal FROM ( + SELECT date, kcal FROM activity_log WHERE date >= date('now', ?1) + UNION ALL + SELECT date, total_kcal as kcal FROM gps_sessions WHERE date >= date('now', ?1) AND total_kcal IS NOT NULL + ) GROUP BY date ORDER BY date ASC`, [`-${days ?? 30} days`] ); } diff --git a/frontend/js/main.js b/frontend/js/main.js index 58cc1ac..cf511e4 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -13,7 +13,18 @@ let state = { // Navigation history stack — survives in-memory across screen changes. // When app is restored from background, we replay the stack into browser history. const NAV_MAX = 20; -const navStack = ["today"]; // always starts with "today" as root +// Restore nav stack from localStorage so back gesture works after app restart +const navStack = (() => { + try { + const saved = JSON.parse(localStorage.getItem("baldav_nav_stack")); + if (Array.isArray(saved) && saved.length > 0) return saved; + } catch {} + return ["today"]; +})(); + +function persistNavStack() { + try { localStorage.setItem("baldav_nav_stack", JSON.stringify(navStack)); } catch {} +} // --------------------------------------------------------------------------- // Boot @@ -75,15 +86,16 @@ async function init() { state.profile = await DB.getFullProfile(); state.latestWeight = await DB.getLatestWeight(); - // Seed browser history with a few entries so back gesture doesn't immediately close app - history.replaceState({ screen: "today" }, ""); - for (let i = 0; i < 5; i++) history.pushState({ screen: "today" }, ""); - - if (false) { - renderScreen("settings"); - } else { - renderScreen("today"); + // Seed browser history from navStack so back gesture works after restart + history.replaceState({ screen: navStack[0] }, ""); + for (let i = 1; i < navStack.length; i++) { + history.pushState({ screen: navStack[i] }, ""); } + // Add padding entries so first back doesn't exit + for (let i = 0; i < 3; i++) history.pushState({ screen: navStack[navStack.length - 1] }, ""); + + // Restore last screen + renderScreen(navStack[navStack.length - 1]); } // --------------------------------------------------------------------------- @@ -97,6 +109,7 @@ function renderScreen(name, fromPopstate) { if (!fromPopstate && prevScreen && prevScreen !== name) { navStack.push(name); if (navStack.length > NAV_MAX) navStack.shift(); + persistNavStack(); history.pushState({ screen: name }, ""); } const screens = { @@ -2851,6 +2864,10 @@ function showProfileModal() { showModal(`

Профиль

+
+ + +
@@ -2875,12 +2892,14 @@ function showProfileModal() {
`, async overlay => { + const weight = parseFloat(overlay.querySelector("#m-weight").value) || null; const height = parseInt(overlay.querySelector("#m-height").value); const year = parseInt(overlay.querySelector("#m-year").value); const bFormula = parseInt(overlay.querySelector("#m-bmr-formula").value) || null; const bEst = parseInt(overlay.querySelector("#m-bmr-est").value) || null; if (isNaN(height) || isNaN(year)) return false; + if (weight) await DB.setProfileValue("weight_kg", weight); await DB.setProfileValue("height_cm", height); await DB.setProfileValue("birth_year", year); await DB.setProfileValue("sex", selectedSex); @@ -2888,6 +2907,7 @@ function showProfileModal() { if (bEst) await DB.setProfileValue("bmr_estimated", bEst); state.profile = { ...state.profile, + weight_kg: weight ? String(weight) : state.profile.weight_kg, height_cm: String(height), birth_year: String(year), sex: selectedSex, bmr_formula: bFormula ? String(bFormula) : null, bmr_estimated: bEst ? String(bEst) : null }; @@ -2949,10 +2969,11 @@ async function showBikeModal(bikeId) {
- + +
+ + +
${!isNew ? `` : ""}`, async overlay => { @@ -2965,7 +2986,7 @@ async function showBikeModal(bikeId) { chainring_teeth: parseInt(overlay.querySelector("#m-bike-chainring").value) || null, cog_teeth: parseInt(overlay.querySelector("#m-bike-cog").value) || null, surface_type: overlay.querySelector("#m-bike-surface").value, - is_active: overlay.querySelector("#m-bike-active").checked, + is_active: overlay.querySelector("#m-bike-active-yes").classList.contains("selected"), }; if (isNew) { await DB.saveBike(data); @@ -2976,6 +2997,19 @@ async function showBikeModal(bikeId) { renderScreen("settings"); }); + // Active toggle chips + setTimeout(() => { + const overlay = document.querySelector(".modal-overlay"); + if (!overlay) return; + overlay.querySelectorAll("#m-bike-active-yes, #m-bike-active-no").forEach(btn => { + btn.onclick = (e) => { + e.stopPropagation(); + overlay.querySelector("#m-bike-active-yes").classList.toggle("selected", btn.dataset.active === "1"); + overlay.querySelector("#m-bike-active-no").classList.toggle("selected", btn.dataset.active === "0"); + }; + }); + }, 50); + if (!isNew) { setTimeout(() => { const delBtn = document.querySelector("#m-del-bike"); @@ -3191,6 +3225,7 @@ window.addEventListener("popstate", (e) => { // Pop our nav stack and navigate to previous screen if (navStack.length > 1) { navStack.pop(); + persistNavStack(); const prev = navStack[navStack.length - 1]; renderScreen(prev, true); } else if (e.state && e.state.screen) {