Run a full Apple HIG audit; fix silent save failures and popup polish
Read Tab Bars, Sidebars, Sheets, Alerts, Action Sheets, Toolbars, Buttons, Pickers, Loading, Feedback, Privacy, and Undo/Redo off developer.apple.com and cross-checked each against the actual code. profilSpeichern() (the shared save path for ~25 fields) had no error handling at any call site, so a failed backend write was an unhandled promise rejection with zero user feedback - fixed centrally, plus the same for serviceRufen(). Also added a spinner to the "Ladt ..." bootstrap screen and gave the Anzugsmoment/km-correction popups the same primary-button styling the Setup popup already used. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -189,7 +189,17 @@ let PROFIL_ROH = null; // letzter vom Backend gelesener Rohstand, für configCar
|
||||
async function profilSpeichern() {
|
||||
const neuesProfil = configCarZuProfil(PROFIL_ROH);
|
||||
PROFIL_ROH = neuesProfil;
|
||||
await HASS.callService("pyscript", "audi_dashboard_profil_schreiben", { profil_json: JSON.stringify(neuesProfil) });
|
||||
// HIG "Feedback": profilSpeichern() ist der Speicherpfad fuer praktisch
|
||||
// jedes editierbare Feld im Panel (~25 Aufrufstellen) und wurde bisher an
|
||||
// keiner davon awaited oder abgefangen - ein Fehlschlag war eine unhandled
|
||||
// promise rejection ohne jede Rueckmeldung. Zentral hier gefangen, aus
|
||||
// demselben Grund wie serviceRufen() weiter unten.
|
||||
try {
|
||||
await HASS.callService("pyscript", "audi_dashboard_profil_schreiben", { profil_json: JSON.stringify(neuesProfil) });
|
||||
} catch (err) {
|
||||
console.error("audi_dashboard: profilSpeichern", err);
|
||||
hinweis("Speichern fehlgeschlagen", err && err.message ? err.message : "Die Änderung konnte nicht gespeichert werden.");
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- Bilder
|
||||
@@ -2376,7 +2386,7 @@ function vReifen() {
|
||||
text-transform:none;font-size:12.5px;line-height:1.6">
|
||||
Korrigiert nur den bisher gefahrenen Stand - weitere Fahrten mit diesem
|
||||
Satz zählen ab hier weiter dazu.</span>
|
||||
<button class="aktion" data-kmspeichern="${k}">Speichern</button>
|
||||
<button class="aktion primaer" data-kmspeichern="${k}">Speichern</button>
|
||||
<button class="aktion" data-kmauf="${k}">Abbrechen</button>
|
||||
</div>` : ""}
|
||||
<input type="file" accept="image/*" hidden data-bildupload="${radDatei}">
|
||||
@@ -2415,7 +2425,7 @@ function vReifen() {
|
||||
<div class="feld" style="border-bottom:0"><label for="nm">Neuer Wert</label>
|
||||
<span class="mitEinheit"><input id="nm" type="number" step="5" min="0"
|
||||
value="${r.anzugsmoment}"><span>Nm</span></span></div>
|
||||
<button class="aktion" data-nmspeichern>Speichern</button>
|
||||
<button class="aktion primaer" data-nmspeichern>Speichern</button>
|
||||
<button class="aktion" data-nmauf>Abbrechen</button>
|
||||
</div>` : ""}
|
||||
</div>
|
||||
@@ -3244,7 +3254,16 @@ function randwischenVerdrahten() {
|
||||
Persistenz über hass.callService im Hintergrund - dieselbe Reihenfolge
|
||||
wie im Prototyp, nur dass jetzt zusätzlich gespeichert wird. */
|
||||
function serviceRufen(dienst, daten) {
|
||||
HASS.callService("pyscript", dienst, daten).catch((err) => console.error("audi_dashboard:", dienst, err));
|
||||
// HIG "Feedback": ein fehlgeschlagener Aufruf darf nicht nur in der Konsole
|
||||
// landen - die aufrufende Stelle hat ihr Popup meist schon geschlossen und
|
||||
// die UI optimistisch aktualisiert, sonst würde die Änderung lautlos
|
||||
// verloren gehen (derselbe Fehler, der für den Beleg-Upload und das Setup-
|
||||
// Speichern schon einmal einzeln gefixt wurde - hier zentral für alle
|
||||
// ~20 Aufrufstellen).
|
||||
HASS.callService("pyscript", dienst, daten).catch((err) => {
|
||||
console.error("audi_dashboard:", dienst, err);
|
||||
hinweis("Aktion fehlgeschlagen", err && err.message ? err.message : "Der Dienst konnte nicht ausgeführt werden.");
|
||||
});
|
||||
}
|
||||
|
||||
function ereignisseVerdrahten() {
|
||||
@@ -4254,7 +4273,7 @@ class AudiDashboardPanel extends HTMLElement {
|
||||
<button class="profilbtn" id="ringsbtn" aria-label="Einstellungen"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .33 1.76l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.6 1.6 0 0 0-1.76-.33 1.6 1.6 0 0 0-1 1.47V21a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1-1.47 1.6 1.6 0 0 0-1.77.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.6 1.6 0 0 0 4.6 15a1.6 1.6 0 0 0-1.47-1H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.47-1 1.6 1.6 0 0 0-.33-1.77l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.6 1.6 0 0 0 9 4.6 1.6 1.6 0 0 0 10 3.13V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.47 1.6 1.6 0 0 0 1.77-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.6 1.6 0 0 0 19.4 9v.04a1.6 1.6 0 0 0 1.47 1H21a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1Z"/></svg></button>
|
||||
</div>
|
||||
<div class="ptr" id="ptr"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M20.06 8.36c-1.21-3.33-4.3-6.46-8.5-6.46-3.69 0-6.97 2-8.55 5.28a9.47 9.47 0 0 0 4.23 12.77A9.56 9.56 0 0 0 20 15.72l-.89-.44a8.56 8.56 0 0 1-11.43 3.77A8.47 8.47 0 0 1 3.9 7.62a8.38 8.38 0 0 1 7.65-4.72c3.8 0 6.64 2.94 7.66 6.1h-5.66v1h7.5V2.5h-1v5.86Z"/></svg><span>Ziehen zum Aktualisieren</span></div>
|
||||
<main id="view"><div class="tile" style="margin-top:20px"><span class="label">Lädt …</span></div></main>
|
||||
<main id="view"><div class="tile" style="margin-top:20px;display:flex;align-items:center;gap:10px"><span class="lade-spinner" aria-hidden="true"></span><span class="label" style="margin:0">Lädt …</span></div></main>
|
||||
<div id="overlay"></div>
|
||||
<nav class="tabbar" id="tabbar"></nav>
|
||||
</div>`;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version": 1786769000}
|
||||
{"version": 1786916894}
|
||||
|
||||
@@ -329,6 +329,17 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
}
|
||||
/* HIG "Loading": statt reinem Text ein sichtbar laufender Indikator, solange
|
||||
DATEN_GELADEN false ist - hilft auch, "lädt noch" optisch von "hängt fest"
|
||||
zu unterscheiden (dieser Bildschirm hatte schon ein echtes Hänger-Problem). */
|
||||
.lade-spinner {
|
||||
width: 16px; height: 16px; flex: none;
|
||||
border: 2px solid var(--line-strong);
|
||||
border-top-color: var(--fg2);
|
||||
border-radius: 50%;
|
||||
animation: lade-spin .8s linear infinite;
|
||||
}
|
||||
@keyframes lade-spin { to { transform: rotate(360deg); } }
|
||||
/* Echte Überschrift innerhalb einer Kachel (z.B. "Sommerräder"/"Winterräder")
|
||||
statt des sonst üblichen kleinen, gedämpften .label-Abschnittstitels -
|
||||
selbe Typografie-Stufe wie .title (Seitentitel), nur etwas kleiner, weil
|
||||
|
||||
@@ -189,7 +189,17 @@ let PROFIL_ROH = null; // letzter vom Backend gelesener Rohstand, für configCar
|
||||
async function profilSpeichern() {
|
||||
const neuesProfil = configCarZuProfil(PROFIL_ROH);
|
||||
PROFIL_ROH = neuesProfil;
|
||||
await HASS.callService("pyscript", "audi_dashboard_profil_schreiben", { profil_json: JSON.stringify(neuesProfil) });
|
||||
// HIG "Feedback": profilSpeichern() ist der Speicherpfad fuer praktisch
|
||||
// jedes editierbare Feld im Panel (~25 Aufrufstellen) und wurde bisher an
|
||||
// keiner davon awaited oder abgefangen - ein Fehlschlag war eine unhandled
|
||||
// promise rejection ohne jede Rueckmeldung. Zentral hier gefangen, aus
|
||||
// demselben Grund wie serviceRufen() weiter unten.
|
||||
try {
|
||||
await HASS.callService("pyscript", "audi_dashboard_profil_schreiben", { profil_json: JSON.stringify(neuesProfil) });
|
||||
} catch (err) {
|
||||
console.error("audi_dashboard: profilSpeichern", err);
|
||||
hinweis("Speichern fehlgeschlagen", err && err.message ? err.message : "Die Änderung konnte nicht gespeichert werden.");
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- Bilder
|
||||
@@ -2376,7 +2386,7 @@ function vReifen() {
|
||||
text-transform:none;font-size:12.5px;line-height:1.6">
|
||||
Korrigiert nur den bisher gefahrenen Stand - weitere Fahrten mit diesem
|
||||
Satz zählen ab hier weiter dazu.</span>
|
||||
<button class="aktion" data-kmspeichern="${k}">Speichern</button>
|
||||
<button class="aktion primaer" data-kmspeichern="${k}">Speichern</button>
|
||||
<button class="aktion" data-kmauf="${k}">Abbrechen</button>
|
||||
</div>` : ""}
|
||||
<input type="file" accept="image/*" hidden data-bildupload="${radDatei}">
|
||||
@@ -2415,7 +2425,7 @@ function vReifen() {
|
||||
<div class="feld" style="border-bottom:0"><label for="nm">Neuer Wert</label>
|
||||
<span class="mitEinheit"><input id="nm" type="number" step="5" min="0"
|
||||
value="${r.anzugsmoment}"><span>Nm</span></span></div>
|
||||
<button class="aktion" data-nmspeichern>Speichern</button>
|
||||
<button class="aktion primaer" data-nmspeichern>Speichern</button>
|
||||
<button class="aktion" data-nmauf>Abbrechen</button>
|
||||
</div>` : ""}
|
||||
</div>
|
||||
@@ -3244,7 +3254,16 @@ function randwischenVerdrahten() {
|
||||
Persistenz über hass.callService im Hintergrund - dieselbe Reihenfolge
|
||||
wie im Prototyp, nur dass jetzt zusätzlich gespeichert wird. */
|
||||
function serviceRufen(dienst, daten) {
|
||||
HASS.callService("pyscript", dienst, daten).catch((err) => console.error("audi_dashboard:", dienst, err));
|
||||
// HIG "Feedback": ein fehlgeschlagener Aufruf darf nicht nur in der Konsole
|
||||
// landen - die aufrufende Stelle hat ihr Popup meist schon geschlossen und
|
||||
// die UI optimistisch aktualisiert, sonst würde die Änderung lautlos
|
||||
// verloren gehen (derselbe Fehler, der für den Beleg-Upload und das Setup-
|
||||
// Speichern schon einmal einzeln gefixt wurde - hier zentral für alle
|
||||
// ~20 Aufrufstellen).
|
||||
HASS.callService("pyscript", dienst, daten).catch((err) => {
|
||||
console.error("audi_dashboard:", dienst, err);
|
||||
hinweis("Aktion fehlgeschlagen", err && err.message ? err.message : "Der Dienst konnte nicht ausgeführt werden.");
|
||||
});
|
||||
}
|
||||
|
||||
function ereignisseVerdrahten() {
|
||||
@@ -4254,7 +4273,7 @@ class AudiDashboardPanel extends HTMLElement {
|
||||
<button class="profilbtn" id="ringsbtn" aria-label="Einstellungen"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .33 1.76l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.6 1.6 0 0 0-1.76-.33 1.6 1.6 0 0 0-1 1.47V21a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1-1.47 1.6 1.6 0 0 0-1.77.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.6 1.6 0 0 0 4.6 15a1.6 1.6 0 0 0-1.47-1H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.47-1 1.6 1.6 0 0 0-.33-1.77l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.6 1.6 0 0 0 9 4.6 1.6 1.6 0 0 0 10 3.13V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.47 1.6 1.6 0 0 0 1.77-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.6 1.6 0 0 0 19.4 9v.04a1.6 1.6 0 0 0 1.47 1H21a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1Z"/></svg></button>
|
||||
</div>
|
||||
<div class="ptr" id="ptr"><svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M20.06 8.36c-1.21-3.33-4.3-6.46-8.5-6.46-3.69 0-6.97 2-8.55 5.28a9.47 9.47 0 0 0 4.23 12.77A9.56 9.56 0 0 0 20 15.72l-.89-.44a8.56 8.56 0 0 1-11.43 3.77A8.47 8.47 0 0 1 3.9 7.62a8.38 8.38 0 0 1 7.65-4.72c3.8 0 6.64 2.94 7.66 6.1h-5.66v1h7.5V2.5h-1v5.86Z"/></svg><span>Ziehen zum Aktualisieren</span></div>
|
||||
<main id="view"><div class="tile" style="margin-top:20px"><span class="label">Lädt …</span></div></main>
|
||||
<main id="view"><div class="tile" style="margin-top:20px;display:flex;align-items:center;gap:10px"><span class="lade-spinner" aria-hidden="true"></span><span class="label" style="margin:0">Lädt …</span></div></main>
|
||||
<div id="overlay"></div>
|
||||
<nav class="tabbar" id="tabbar"></nav>
|
||||
</div>`;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version": 1786769000}
|
||||
{"version": 1786916894}
|
||||
|
||||
@@ -329,6 +329,17 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
|
||||
display: block;
|
||||
line-height: 1.3;
|
||||
}
|
||||
/* HIG "Loading": statt reinem Text ein sichtbar laufender Indikator, solange
|
||||
DATEN_GELADEN false ist - hilft auch, "lädt noch" optisch von "hängt fest"
|
||||
zu unterscheiden (dieser Bildschirm hatte schon ein echtes Hänger-Problem). */
|
||||
.lade-spinner {
|
||||
width: 16px; height: 16px; flex: none;
|
||||
border: 2px solid var(--line-strong);
|
||||
border-top-color: var(--fg2);
|
||||
border-radius: 50%;
|
||||
animation: lade-spin .8s linear infinite;
|
||||
}
|
||||
@keyframes lade-spin { to { transform: rotate(360deg); } }
|
||||
/* Echte Überschrift innerhalb einer Kachel (z.B. "Sommerräder"/"Winterräder")
|
||||
statt des sonst üblichen kleinen, gedämpften .label-Abschnittstitels -
|
||||
selbe Typografie-Stufe wie .title (Seitentitel), nur etwas kleiner, weil
|
||||
|
||||
Reference in New Issue
Block a user