fix: v0.10.1 — dispatch pickers independent per accepted contract (placeholder default, no first-eligible auto-fallback; stale-select fix via explicit placeholder option)

This commit is contained in:
2026-09-18 06:48:55 +00:00
parent cf811dcf4d
commit 4c06ed26ff
5 changed files with 175 additions and 7 deletions
+19 -6
View File
@@ -22,10 +22,25 @@ function dispatchReason(p: PlaneState, contract: ContractState): string | null {
return null;
}
// The plane a dispatch picker should act on for one accepted contract: the user's explicit pick
// while it stays eligible, else NOTHING. v0.10.1 removed the "auto-track first eligible" fallback —
// it made every row display the same plane by default (selections looked shared across contracts)
// and left no way to show "nothing chosen". null = placeholder shown + Dispatch disabled; a poll
// that makes the pick ineligible (plane flew / moved away) drops back to the placeholder too.
export function dispatchSelection(
planes: PlaneState[],
contract: ContractState,
selectedId: string | null,
): PlaneState | null {
if (selectedId === null) return null;
return planes.find((p) => p.id === selectedId && dispatchReason(p, contract) === null) ?? null;
}
// Dispatch controls for one accepted, not-yet-dispatched contract: a plane picker where only
// grounded planes at the origin are enabled (others disabled with the reason), plus a dispatch
// button bound to the selection. The effective selection is the user's pick while it stays
// eligible, else auto-tracks the first eligible plane so a fresh contract needs one click.
// button bound to the selection. Each row owns its selection in local state and defaults to the
// "Select a plane…" placeholder — never preselecting — so rows stay visibly independent and a
// dispatch can't fire on the wrong contract by accident.
function DispatchControls({
contract,
planes,
@@ -38,10 +53,7 @@ function DispatchControls({
onDispatch: (planeId: string) => void;
}) {
const [selectedId, setSelectedId] = useState<string | null>(null);
const effective =
planes.find((p) => p.id === selectedId && dispatchReason(p, contract) === null) ??
planes.find((p) => dispatchReason(p, contract) === null) ??
null;
const effective = dispatchSelection(planes, contract, selectedId);
return (
<div className="dispatch-controls">
@@ -51,6 +63,7 @@ function DispatchControls({
value={effective?.id ?? ''}
onChange={(e) => setSelectedId(e.target.value || null)}
>
<option value="">Select a plane…</option>
{planes.map((p) => {
const reason = dispatchReason(p, contract);
return (