From 0ebaa8b1327f62f8c3782b8799752b4ef9344d37 Mon Sep 17 00:00:00 2001 From: AI Agent Date: Fri, 2 Oct 2026 19:16:52 +0000 Subject: [PATCH] UI-Fixes: Wizard konsistent, Tabs uebersetzt/responsive, Set-Zeilen, leere Sets - Setup-Wizard: feste Mindesthoehe (#setup-gate), Navigation unten verankert -> Hoehe springt nicht mehr zwischen den Schritten - Wizard Benutzer-Anlage: nach Erfolg Bestaetigung statt leerem Formular (kein erneutes Eingeben), 'Weiter' aktiv - Tabs: Labels werden uebersetzt (t()-i18n), Topbar/Tabs horizontal scrollbar + kompakter auf Mobil -> 'Einstellungen' wird nicht mehr abgeschnitten - Button-Min-Breite (.btn 96px, .btn-small 64px) fuer Sprach-Konsistenz - Fortschrittssegmente: bei weissem Akzent auf hellem Theme schwarz (statt unsichtbar weiss) - Set-Zeile: eine horizontale Reihe (nowrap) mit einheitlicher Eingabe-Mindestbreite/-hoehe; Grid-Spalten auf 460px angepasst - Leere Saetze (ohne Gewicht UND Reps) werden nicht mehr persistiert und im Verlauf ausgeblendet (kein 'empty kg x empty reps'-Wand) - Tests: 158 Checks gruen (39+18+43+32+26) --- app.js | 46 ++++++++++++++++++++++++++++++---------------- styles.css | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/app.js b/app.js index e8bab9f..f6c02e0 100644 --- a/app.js +++ b/app.js @@ -152,6 +152,7 @@ const LANG = { reorder_hint: "Übungen per Ziehen sortieren", prev_week: "Vorige Woche", next_week: "Nächste Woche", week_of: "Woche vom", wizard_done: "Alles erledigt - die App ist bereit.", + wizard_user_created: "Benutzer '{u}' angelegt. Weiter mit 'Weiter'.", logged_in: "Angemeldet.", user_created_toast: "Benutzer angelegt.", app_load_fail: "App konnte nicht geladen werden: {m}", }, @@ -261,6 +262,7 @@ const LANG = { reorder_hint: "Drag to reorder exercises", prev_week: "Previous week", next_week: "Next week", week_of: "Week of", wizard_done: "All done - the app is ready.", + wizard_user_created: "User '{u}' created. Continue with 'Next'.", logged_in: "Logged in.", user_created_toast: "User created.", app_load_fail: "Could not load the app: {m}", }, @@ -1033,7 +1035,7 @@ async function flushAutosave(activity) { const sets = (model.exercises[exKey] || []).map((e, i) => ({ exercise: exKey, sort: i, weight: e.weight || "", reps: e.reps || "", done: e.done ? 1 : 0, - })); + })).filter(s => s.weight !== "" || s.reps !== ""); try { await api("/api/sessions/" + model.id, jsonOpts("PATCH", { sets })); model.dirtyEx.delete(exKey); @@ -1262,9 +1264,11 @@ function renderSetSummary(s, a) { for (const e of s.entries || []) (byEx[e.exercise] = byEx[e.exercise] || []).push(e); for (const exKey of Object.keys(byEx)) { const li = el("li", "hist-entry"); + const sorted = byEx[exKey].sort((x, y) => x.sort - y.sort) + .filter(e => e.weight || e.reps); + if (!sorted.length) continue; li.appendChild(el("span", "ex-name", exKey + ": ")); const setsWrap = el("span", "hist-sets"); - const sorted = byEx[exKey].sort((x, y) => x.sort - y.sort); sorted.forEach((e, idx) => { const w = e.weight || t("empty_label"); const r = e.reps || t("empty_label"); @@ -1751,20 +1755,26 @@ function renderWizardStep() { grid.appendChild(solo); box.appendChild(grid); if (wizard.multi === true) { - const form = el("form", "auth-form"); - form.appendChild(authField(t("username"), "setup-user")); - form.appendChild(authField(t("password"), "setup-pass")); - const err = el("div", "auth-error", ""); - err.hidden = true; - const submit = el("button", "btn btn-primary btn-block", t("first_user")); - submit.type = "submit"; - form.appendChild(submit); - form.appendChild(err); - form.addEventListener("submit", ev => { - ev.preventDefault(); - wizardRegister($("#setup-user").value, $("#setup-pass").value, err); - }); - box.appendChild(form); + if (wizard.userCreated && app.me && app.me.username) { + const ok = el("div", "auth-register", + t("wizard_user_created", { u: app.me.username })); + box.appendChild(ok); + } else { + const form = el("form", "auth-form"); + form.appendChild(authField(t("username"), "setup-user")); + form.appendChild(authField(t("password"), "setup-pass")); + const err = el("div", "auth-error", ""); + err.hidden = true; + const submit = el("button", "btn btn-primary btn-block", t("first_user")); + submit.type = "submit"; + form.appendChild(submit); + form.appendChild(err); + form.addEventListener("submit", ev => { + ev.preventDefault(); + wizardRegister($("#setup-user").value, $("#setup-pass").value, err); + }); + box.appendChild(form); + } } } else { // Letzter Schritt: Darstellung (Theme + OLED + Akzent) @@ -2328,6 +2338,10 @@ async function loadAppData() { app.cfg = await api("/api/config"); app.settings = await api("/api/settings"); applySettings(app.settings); + for (const tab of $$(".tab")) { + const key = "tab_" + tab.dataset.view; + if (LANG[lang()][key]) tab.textContent = t(key); + } $("#today-label").textContent = t("today_header") + fmtDateLong(app.meta.today); setUserChip(); await loadToday(); diff --git a/styles.css b/styles.css index b639cd8..9aa64ab 100644 --- a/styles.css +++ b/styles.css @@ -63,6 +63,10 @@ html[data-accent="violet"] { --accent: #9a6fd0; --accent-hover: #ab80e0; --accen html[data-accent="pink"] { --accent: #e0567f; --accent-hover: #eb6890; --accent-active: #c74b6e; --accent-ink: #141414; } html[data-accent="white"] { --accent: #ffffff; --accent-hover: #f2f2f2; --accent-active: #e6e6e6; --accent-ink: #141414; --accent-border: #9a9a9a; } +html[data-theme="light"][data-accent="white"] .wp-step { border-color: #8a8a8a; } +html[data-theme="light"][data-accent="white"] .wp-step.is-done { border-color: #000; background: #000; } +html[data-theme="light"][data-accent="white"] .wp-step.is-current { border-color: #000; background: transparent; } + html[data-theme="dark"], html[data-theme="oled"] { color-scheme: dark; } html[data-theme="light"] { color-scheme: light; } @@ -138,6 +142,18 @@ html, body { gap: 0; margin-left: auto; border: 2px solid var(--border); + overflow-x: auto; + max-width: 100%; +} + +.tab { + white-space: nowrap; + flex-shrink: 0; +} + +@media (max-width: 700px) { + .topbar { padding: 10px 12px; } + .tab { padding: 6px 12px; } } .tab { @@ -199,6 +215,7 @@ h3 { font-size: 15px; font-weight: 700; } font: inherit; font-weight: 600; padding: 7px 14px; + min-width: 96px; cursor: pointer; } @@ -224,7 +241,7 @@ h3 { font-size: 15px; font-weight: 700; } .btn-danger-ghost:hover { background: var(--bg-hover); } .btn-danger-ghost.is-armed { background: var(--danger); color: #fff; } -.btn-small { padding: 4px 10px; font-size: 13px; } +.btn-small { padding: 4px 10px; font-size: 13px; min-width: 64px; } .btn-block { width: 100%; } .icon-btn { @@ -386,7 +403,7 @@ h3 { font-size: 15px; font-weight: 700; } .ex-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); gap: 14px; align-items: start; } @@ -419,12 +436,14 @@ h3 { font-size: 15px; font-weight: 700; } .set-row { display: flex; - flex-wrap: wrap; + flex-wrap: nowrap; gap: 8px; align-items: center; padding: 6px 10px; } +.set-rows { overflow-x: auto; } + .set-row.is-done { background: var(--bg-subtle); } /* Set-Abhaken: ganzer Button klickbar, mit Ankreuz-Box */ @@ -491,6 +510,8 @@ h3 { font-size: 15px; font-weight: 700; } .field-input { width: 74px; + min-width: 60px; + height: 32px; padding: 6px 4px 6px 8px; border: 0; background: transparent; @@ -919,9 +940,13 @@ body.boot #setup-gate { visibility: hidden; } width: 640px; max-width: calc(100vw - 32px); padding: 40px; + display: flex; + flex-direction: column; text-align: left; } +#setup-gate .auth-box { min-height: 560px; } + .auth-head { display: flex; align-items: center; @@ -1038,7 +1063,8 @@ body.boot #setup-gate { visibility: hidden; } display: flex; gap: 12px; justify-content: space-between; - margin-top: 20px; + margin-top: auto; + padding-top: 20px; } .wizard-nav-right {