refactor(frontend): migrate Profile.tsx to the banner system

Load/save/delete failures now call showError instead of setting local
error state. The profile-not-loaded-yet fallback gains a
profileLoadFailed boolean so it can still distinguish "loading" from
"failed to load" without the error text itself, which now lives only
in the (transient) banner.
This commit is contained in:
2026-07-27 09:53:28 +02:00
parent a5fffff737
commit 940295510e

View File

@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from "react";
import { api, BASE_URL } from "../api/client";
import { showError } from "../banner";
import { ColorField } from "../components/ColorField";
import { GarminConnection } from "../components/GarminConnection";
import { NullableNumberField } from "../components/NullableNumberField";
@@ -87,7 +88,7 @@ const AUTO_SAVE_DELAY_MS = 600;
export function Profile({ onSaved }: { onSaved?: (p: ProfileType) => void }) {
const [profile, setProfile] = useState<ProfileType | null>(null);
const [error, setError] = useState<string | null>(null);
const [profileLoadFailed, setProfileLoadFailed] = useState(false);
const [saved, setSaved] = useState(false);
// Mirrors `profile` synchronously (state updates don't apply until the
// next render), so set() always debounces from the latest edit rather
@@ -100,7 +101,10 @@ export function Profile({ onSaved }: { onSaved?: (p: ProfileType) => void }) {
const [deleting, setDeleting] = useState(false);
useEffect(() => {
api.getProfile().then(setProfile).catch((e) => setError(String(e)));
api.getProfile().then(setProfile).catch((e) => {
showError(String(e));
setProfileLoadFailed(true);
});
}, []);
// Flush a still-pending debounced save on unmount (e.g. the user edits a
@@ -121,11 +125,10 @@ export function Profile({ onSaved }: { onSaved?: (p: ProfileType) => void }) {
const updated = await api.updateProfile(next);
profileRef.current = updated;
setProfile(updated);
setError(null);
setSaved(true);
onSaved?.(updated);
} catch (e) {
setError(String(e));
showError(String(e));
}
}
@@ -153,7 +156,6 @@ export function Profile({ onSaved }: { onSaved?: (p: ProfileType) => void }) {
async function deleteAccount() {
setDeleting(true);
setError(null);
try {
await api.deleteProfile();
// Deletion doesn't clear the session cookie -- follow with a real
@@ -166,7 +168,7 @@ export function Profile({ onSaved }: { onSaved?: (p: ProfileType) => void }) {
document.body.appendChild(form);
form.submit();
} catch (e) {
setError(String(e));
showError(String(e));
setDeleting(false);
}
}
@@ -174,14 +176,13 @@ export function Profile({ onSaved }: { onSaved?: (p: ProfileType) => void }) {
if (!profile) {
return (
<div className="page">
{error ? <p className="error">{error}</p> : <p>Loading...</p>}
{profileLoadFailed ? <p className="empty-state">Couldn't load profile.</p> : <p>Loading...</p>}
</div>
);
}
return (
<div className="page profile-page">
{error && <p className="error">{error}</p>}
{saved && <p className="garmin-connection-message">Saved.</p>}
<fieldset className="kind-editor">