ui/phase-14: rename planet end-to-end + order read-back

Wires the first end-to-end command through the full pipeline:
inspector rename action → local order draft → user.games.order
submit → optimistic overlay on map / inspector → server hydration
on cache miss via the new user.games.order.get message type.

Backend: GET /api/v1/user/games/{id}/orders forwards to engine
GET /api/v1/order. Gateway parses the engine PUT response into the
extended UserGamesOrderResponse FBS envelope and adds
executeUserGamesOrderGet for the read-back path. Frontend ports
ValidateTypeName to TS, lands the inline rename editor + Submit
button, and exposes a renderedReport context so consumers see the
overlay-applied snapshot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-09 11:50:09 +02:00
parent 381e41b325
commit f80c623a74
86 changed files with 7505 additions and 138 deletions
+42
View File
@@ -10,6 +10,11 @@
// sets for `LocalPlanet`, `OtherPlanet`, `UninhabitedPlanet`, and
// `UnidentifiedPlanet`, and the wrapper preserves that nullability
// instead of inventing zero values.
//
// Phase 14 adds `applyOrderOverlay`: every applied / submitting
// rename in the local draft swaps the planet name on the rendered
// report so the player sees their intent reflected immediately,
// without waiting for the next turn cutoff.
import { Builder, ByteBuffer } from "flatbuffers";
@@ -19,6 +24,7 @@ import {
GameReportRequest,
Report,
} from "../proto/galaxy/fbs/report";
import type { CommandStatus, OrderCommand } from "../sync/order-types";
const MESSAGE_TYPE = "user.games.report";
@@ -205,6 +211,42 @@ export function uuidToHiLo(value: string): [bigint, bigint] {
return [hi, lo];
}
/**
* applyOrderOverlay returns a copy of `report` with every applied or
* still-in-flight (`submitting`) command from `commands` projected on
* top. Phase 14 understands `planetRename` only — every other variant
* passes through. The function is pure: callers re-derive the
* overlay whenever the draft or the report change.
*
* `statuses` maps command id → status. Entries with `applied` or
* `submitting` participate in the overlay; everything else (`draft`,
* `valid`, `invalid`, `rejected`) is treated as "not yet committed
* by the player" and skipped. This matches the order-composer model:
* the player sees their own committed intent, not their unfinished
* edits.
*/
export function applyOrderOverlay(
report: GameReport,
commands: OrderCommand[],
statuses: Record<string, CommandStatus>,
): GameReport {
if (commands.length === 0) return report;
let mutatedPlanets: ReportPlanet[] | null = null;
for (const cmd of commands) {
const status = statuses[cmd.id];
if (status !== "applied" && status !== "submitting") continue;
if (cmd.kind !== "planetRename") continue;
const idx = report.planets.findIndex((p) => p.number === cmd.planetNumber);
if (idx < 0) continue;
if (mutatedPlanets === null) {
mutatedPlanets = [...report.planets];
}
mutatedPlanets[idx] = { ...mutatedPlanets[idx]!, name: cmd.name };
}
if (mutatedPlanets === null) return report;
return { ...report, planets: mutatedPlanets };
}
function decodeErrorMessage(payload: Uint8Array): { code: string; message: string } {
if (payload.length === 0) {
return { code: "internal_error", message: "empty error payload" };