Maintenance: distance wear, broken-state burn ×1.5, repair action (ISS-069, v0.39.0)
CI / build + unit + e2e (pull_request) Successful in 9m34s

This commit is contained in:
2026-09-21 12:06:45 +00:00
parent 929452ca33
commit 336ed4ee7c
28 changed files with 868 additions and 41 deletions
+28 -1
View File
@@ -368,17 +368,44 @@ export default function LeftPane({ state, loading, error, onRefresh, applySnapsh
)}
</>
) : (
<span className="plane-status status-available">{p.locationIcao}</span>
<>
<span className="plane-status status-available">{p.locationIcao}</span>
{/* v0.39.0 (ISS-069): maintenance pills — "in repair" while the gate runs, "broken" at zero health. */}
{p.status === 'repairing' && (
<span data-testid={`plane-repairing-${p.id}`} className="plane-status status-repairing">in repair</span>
)}
{p.health <= 0 && (
<span data-testid={`plane-broken-${p.id}`} className="plane-status status-broken">broken</span>
)}
</>
)}
</div>
<div className="fuel-bar" aria-hidden="true">
<div style={{ width: `${Math.max(0, Math.min(100, (p.fuel / p.maxFuel) * 100))}%` }} />
</div>
{/* v0.39.0 (ISS-069): health bar — sibling of the fuel bar, amber fill. */}
<div className="health-bar" aria-hidden="true">
<div style={{ width: `${Math.max(0, Math.min(100, p.health))}%` }} />
</div>
<div className="plane-sub">
<span data-testid={`plane-fuel-${p.id}`}>{formatFuel(p.fuel, p.maxFuel)}</span>
{/* v0.39.0 (ISS-069): health percentage next to the fuel readout. */}
<span data-testid={`plane-health-${p.id}`}>{Math.round(p.health)}%</span>
<span data-testid={`plane-loadout-${p.id}`}>{loadoutFor(state, p)}</span>
<span className="plane-loc">{planeLocation(p)}</span>
</div>
{/* v0.39.0 (ISS-069): repair — grounded plane with worn health; flat cost is server-side only (no preview). */}
{p.status === 'available' && p.health < 100 && (
<button
type="button"
data-testid={`repair-${p.id}`}
className="pane-btn repair-btn"
disabled={actionsBusy}
onClick={() => void runAction(`/api/planes/${p.id}/repair`)}
>
Repair
</button>
)}
{cargo.map((pkg) => {
const penalty = abandonPenalty(pkg, clockMs);
return (