diff --git a/src/app/dashboard/fuel/fuel-view.tsx b/src/app/dashboard/fuel/fuel-view.tsx index c1adb9e..bea74da 100644 --- a/src/app/dashboard/fuel/fuel-view.tsx +++ b/src/app/dashboard/fuel/fuel-view.tsx @@ -184,6 +184,12 @@ function StatusBadge({ status, variancePct }: { status: FuelStatus; variancePct: ); } +const DISMISS_ERROR_COPY: Record = { + bad_id: "That vehicle id is malformed.", + not_found: "Vehicle not found.", + update_failed: "Server update failed. Please try again.", +}; + export function FuelView({ rows, vehicles, @@ -338,8 +344,16 @@ export function FuelView({ { method: "POST" }, ); if (!res.ok) { - const data: { error?: string } = await res.json().catch(() => ({})); - throw new Error(data.error ?? `Dismiss failed (${res.status})`); + let slug: string | undefined; + try { + const data: { error?: string } = await res.json(); + slug = data.error; + } catch { + /* non-JSON body — fall through */ + } + const friendly = + (slug && DISMISS_ERROR_COPY[slug]) ?? `Dismiss failed (${res.status})`; + throw new Error(friendly); } // Re-pull server data so the overlay becomes a no-op and any // other server-side state stays in sync. @@ -540,6 +554,7 @@ export function FuelView({ href={vehicleHref(v.id)} onDismiss={handleDismiss} busy={busyId === v.id} + disabled={busyId !== null} /> ))} @@ -553,6 +568,7 @@ export function FuelView({ href={vehicleHref(v.id)} onDismiss={handleDismiss} busy={busyId === v.id} + disabled={busyId !== null} /> ))} @@ -788,11 +804,13 @@ function AlertCard({ href, onDismiss, busy, + disabled, }: { v: VehicleSummary; href: string; onDismiss: (id: string) => void; busy: boolean; + disabled: boolean; }) { const dominant = v.alert > 0 ? "alert" : "warn"; const toneClasses = @@ -867,10 +885,10 @@ function AlertCard({ type="button" onClick={(e) => { e.preventDefault(); - e.stopPropagation(); onDismiss(v.id); }} - disabled={busy} + disabled={disabled} + aria-busy={busy} aria-label={`Mark ${v.description} as seen`} className="inline-flex shrink-0 items-center gap-1 rounded-md border border-border/60 bg-card px-2 py-1 text-xs font-medium text-muted-foreground transition-colors hover:border-[#F2B165]/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#F2B165] disabled:opacity-50" > @@ -887,11 +905,13 @@ function AlertListRow({ href, onDismiss, busy, + disabled, }: { v: VehicleSummary; href: string; onDismiss: (id: string) => void; busy: boolean; + disabled: boolean; }) { const dominant = v.alert > 0 ? "alert" : "warn"; const stripeColor = @@ -948,10 +968,10 @@ function AlertListRow({ type="button" onClick={(e) => { e.preventDefault(); - e.stopPropagation(); onDismiss(v.id); }} - disabled={busy} + disabled={disabled} + aria-busy={busy} aria-label={`Mark ${v.description} as seen`} className="ml-2 inline-flex shrink-0 items-center gap-1.5 rounded-md border border-border/60 bg-card px-2 py-1 text-xs font-medium text-muted-foreground transition-colors hover:border-[#F2B165]/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#F2B165] disabled:opacity-50" >