ui/phase-15: planet inspector production controls + order-draft collapse

Adds the second end-to-end command (`setProductionType`) with a
collapse-by-`planetNumber` rule on the order draft, the segmented
production-controls component on the planet inspector, the FBS
encoder/decoder pair for `CommandPlanetProduce`, and the
`localShipClass` projection on `GameReport`. Forecast number is
deferred and tracked in the new `ui/docs/calc-bridge.md`.
This commit is contained in:
Ilia Denisov
2026-05-09 15:54:30 +02:00
parent c4f1409329
commit 915b4372dd
31 changed files with 2200 additions and 76 deletions
+27 -8
View File
@@ -61,9 +61,10 @@ describe("planet inspector", () => {
industry: 800,
industryStockpile: 12.5,
materialsStockpile: 30,
production: "drive",
production: "Drive",
freeIndustry: 187.5,
}),
localShipClass: [],
},
});
const section = ui.getByTestId("inspector-planet");
@@ -99,9 +100,10 @@ describe("planet inspector", () => {
expect(
ui.getByTestId("inspector-planet-field-materials_stockpile"),
).toHaveTextContent("30");
expect(
ui.getByTestId("inspector-planet-field-production"),
).toHaveTextContent("drive");
// Phase 15: the static "current production" row is replaced by
// the interactive Production component for owned planets.
expect(ui.queryByTestId("inspector-planet-field-production")).toBeNull();
expect(ui.getByTestId("inspector-planet-production")).toBeInTheDocument();
expect(
ui.getByTestId("inspector-planet-field-free_industry"),
).toHaveTextContent("187.5");
@@ -127,6 +129,7 @@ describe("planet inspector", () => {
production: "weapons",
freeIndustry: 75,
}),
localShipClass: [],
},
});
expect(ui.getByTestId("inspector-planet-kind")).toHaveTextContent(
@@ -138,6 +141,11 @@ describe("planet inspector", () => {
expect(
ui.getByTestId("inspector-planet-field-population"),
).toHaveTextContent("500");
// Non-local planets keep the read-only production row.
expect(
ui.getByTestId("inspector-planet-field-production"),
).toHaveTextContent("weapons");
expect(ui.queryByTestId("inspector-planet-production")).toBeNull();
});
test("uninhabited planet hides population, industry, and production rows", () => {
@@ -152,6 +160,7 @@ describe("planet inspector", () => {
industryStockpile: 0,
materialsStockpile: 0,
}),
localShipClass: [],
},
});
expect(ui.getByTestId("inspector-planet-kind")).toHaveTextContent(
@@ -183,6 +192,7 @@ describe("planet inspector", () => {
x: 1234,
y: -5,
}),
localShipClass: [],
},
});
expect(ui.getByTestId("inspector-planet-kind")).toHaveTextContent(
@@ -210,6 +220,7 @@ describe("planet inspector", () => {
size: 100,
resources: 5,
}),
localShipClass: [],
},
});
expect(ui.queryByTestId("inspector-planet-rename-action")).toBeNull();
@@ -238,9 +249,10 @@ describe("planet inspector", () => {
industry: 0,
industryStockpile: 0,
materialsStockpile: 0,
production: "drive",
production: "Drive",
freeIndustry: 0,
}),
localShipClass: [],
},
context,
});
@@ -300,9 +312,10 @@ describe("planet inspector", () => {
industry: 0,
industryStockpile: 0,
materialsStockpile: 0,
production: "drive",
production: "Drive",
freeIndustry: 0,
}),
localShipClass: [],
},
context,
});
@@ -314,13 +327,14 @@ describe("planet inspector", () => {
db.close();
});
test("missing production string falls back to the localised placeholder", () => {
test("non-local planets fall back to the localised production placeholder", () => {
const ui = render(Planet, {
props: {
planet: makePlanet({
number: 5,
name: "Idle",
kind: "local",
kind: "other",
owner: "Drift",
size: 800,
resources: 1,
population: 1,
@@ -331,8 +345,13 @@ describe("planet inspector", () => {
production: "",
freeIndustry: 0,
}),
localShipClass: [],
},
});
// Empty production strings collapse to the localised "none"
// placeholder on the read-only path. The local-planet branch
// owns the production surface via the interactive component
// instead and is covered by `inspector-planet-production.test.ts`.
expect(
ui.getByTestId("inspector-planet-field-production"),
).toHaveTextContent("none");