ui/phase-20: ship-group inspector actions
Eight ship-group operations land on the inspector behind a single inline-form panel: split, send, load, unload, modernize, dismantle, transfer, join fleet. Each action either appends a typed command to the local order draft or surfaces a tooltip explaining the disabled state. Partial-ship operations emit an implicit breakShipGroup command before the targeted action so the engine sees a clean (Break, Action) pair on the wire. `pkg/calc.BlockUpgradeCost` migrates from `game/internal/controller/ship_group_upgrade.go` so the calc bridge can wrap a pure pkg/calc formula; the controller now imports it. The bridge surfaces the function as `core.blockUpgradeCost`, which the inspector calls once per ship block to render the modernize cost preview. `GameReport.otherRaces` is decoded from the report's player block (non-extinct, ≠ self) and feeds the transfer-to-race picker. The planet inspector's stationed-ship rows become clickable for own groups so the actions panel is reachable from the standard click flow (the renderer continues to hide on-planet groups). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+111
-19
@@ -2135,27 +2135,63 @@ Targeted tests:
|
||||
- Playwright e2e: click each variant from a seeded game, assert all
|
||||
expected fields render.
|
||||
|
||||
## Phase 20. Inspector — Ship Group Actions
|
||||
## ~~Phase 20. Inspector — Ship Group Actions~~
|
||||
|
||||
Status: pending.
|
||||
Status: done.
|
||||
|
||||
Goal: enable group operations from the inspector: split, send, load,
|
||||
unload, modernize, dismantle, transfer to race, add to fleet.
|
||||
|
||||
Artifacts:
|
||||
|
||||
- action buttons in `ui/frontend/src/lib/inspectors/ship-group.svelte`
|
||||
with disabled-state and tooltip when local validation rejects
|
||||
- `ui/frontend/src/sync/order-types.ts` extends with `SplitGroup`,
|
||||
`SendGroup`, `LoadCargo`, `UnloadCargo`, `Modernize`, `Dismantle`,
|
||||
`TransferToRace`, `AssignToFleet` command variants
|
||||
- `Send` action picks destination through a planet picker filtered by
|
||||
the group's reach (uses `pkg/calc/` reach function via Core; the
|
||||
player's tech levels are already on `GameReport.localPlayer*` from
|
||||
Phase 18, no extra plumbing needed)
|
||||
- `Modernize` cost preview using `pkg/calc/` formula via Core
|
||||
- confirmation dialog for `Dismantle` over a foreign planet with
|
||||
colonists onboard (special-case from [`rules.txt`](../game/rules.txt): colonists die)
|
||||
- action panel `ui/frontend/src/lib/inspectors/ship-group/actions.svelte`
|
||||
mounted by the read-only inspector for the local variant; eight
|
||||
inline forms (one per action) with disabled-button tooltips that
|
||||
mirror the engine's pre-conditions
|
||||
(`controller/ship_group*.go`)
|
||||
- `ui/frontend/src/sync/order-types.ts` extends with eight new
|
||||
command variants — `breakShipGroup`, `sendShipGroup`,
|
||||
`loadShipGroup`, `unloadShipGroup`, `upgradeShipGroup`,
|
||||
`dismantleShipGroup`, `transferShipGroup`, `joinFleetShipGroup` —
|
||||
plus `ShipGroupCargo` and `ShipGroupUpgradeTech` literal types
|
||||
- `sync/submit.ts` and `sync/order-load.ts` round-trip every new
|
||||
variant against the existing FBS classes in
|
||||
`proto/galaxy/fbs/order/`; the `id` field on each ship-group
|
||||
payload carries the *target* group UUID (the source group, or
|
||||
the freshly-minted `newGroupId` when an implicit split precedes
|
||||
the action)
|
||||
- `Send` action picks destination through a planet picker filtered
|
||||
by the group's reach (`localPlayerDrive * 40`, computed inline
|
||||
via the existing `torusShortestDelta` from
|
||||
`cargo-routes.svelte`); the player's tech levels are already on
|
||||
`GameReport.localPlayer*` from Phase 18, no extra plumbing
|
||||
needed
|
||||
- `Modernize` cost preview through `core.blockUpgradeCost`
|
||||
(Phase 20 bridge), summed over the four ship-class blocks for
|
||||
the targeted ship count; preview hides when `Core` is not yet
|
||||
booted or the form is invalid (see
|
||||
`ui/docs/ship-group-actions.md` for the formula breakdown)
|
||||
- two-step inline confirmation for `Dismantle` over a foreign
|
||||
planet with colonists onboard (engine reference
|
||||
`controller/ship_group.go:177-179` — `UnloadColonists` is not
|
||||
called over a foreign planet, so the cargo is lost)
|
||||
- `pkg/calc/ship.go.BlockUpgradeCost` (migrated from
|
||||
`game/internal/controller/ship_group_upgrade.go`) — the bridge
|
||||
rule says `ui/core/calc/` only wraps `pkg/calc/` formulas, so
|
||||
the function moved upstream and the controller now imports it
|
||||
- `GameReport.otherRaces: string[]` populated by the report
|
||||
decoder from `report.player[]` (non-extinct, ≠ self) — used by
|
||||
the transfer-to-race picker; Phase 22's Races View reuses the
|
||||
same field
|
||||
- planet inspector's stationed-ship rows
|
||||
(`lib/inspectors/planet/ship-groups.svelte`) become clickable
|
||||
for own groups, pivoting the `SelectionStore` to the matching
|
||||
`shipGroup.local` ref so the actions panel is reachable from
|
||||
the standard click flow (the map deliberately hides on-planet
|
||||
groups, so this is the on-planet entry point)
|
||||
- topic doc `ui/docs/ship-group-actions.md` covers the action
|
||||
surface, disabled-state rules, implicit-split pattern, and the
|
||||
modernize cost preview formula
|
||||
|
||||
Dependencies: Phases 18, 19.
|
||||
|
||||
@@ -2171,10 +2207,61 @@ Acceptance criteria:
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- Vitest unit tests for action enablement logic per action;
|
||||
- Vitest component tests for the dismantle-with-colonists confirmation;
|
||||
- Playwright e2e for at least one complete flow (send a group between
|
||||
two planets) against a local stack.
|
||||
- `pkg/calc/ship_test.go.TestBlockUpgradeCost` — formula coverage
|
||||
on the migrated function;
|
||||
- `ui/core/calc/ship_test.go.TestBlockUpgradeCostParity` — bridge
|
||||
parity against `pkg/calc/`;
|
||||
- Vitest:
|
||||
- `tests/inspector-ship-group-actions.test.ts` — disabled-state
|
||||
rules per action and the implicit-split pattern;
|
||||
- `tests/inspector-ship-group-dismantle-confirm.test.ts` —
|
||||
two-step confirm over foreign-COL groups;
|
||||
- `tests/inspector-ship-group-modernize-cost.test.ts` —
|
||||
preview formula matches `BlockUpgradeCost` × ship count and
|
||||
hides when `Core` is null;
|
||||
- `tests/sync-order-types-ship-group.test.ts` —
|
||||
`validateCommand` for each new variant;
|
||||
- `tests/sync-submit-ship-group.test.ts` — encoder/decoder
|
||||
round-trip per new variant;
|
||||
- Playwright `tests/e2e/ship-group-send.spec.ts` — synthetic
|
||||
report with a 3-ship group on Earth and a reachable Mars,
|
||||
drives the planet inspector → ship-group inspector pivot, then
|
||||
Send 2 of 3 with map-pick destination, asserts both Break and
|
||||
Send land in the order draft via the order tab.
|
||||
|
||||
Decisions during stage:
|
||||
|
||||
1. **`BlockUpgradeCost` migration**. The pre-existing copy in
|
||||
`game/internal/controller/ship_group_upgrade.go` moved to
|
||||
`pkg/calc/ship.go`; the controller's `GroupUpgradeCost` and
|
||||
`UpgradeGroupPreference` now call `calc.BlockUpgradeCost`.
|
||||
The unit test moved from `controller/ship_group_upgrade_test.go`
|
||||
to `pkg/calc/ship_test.go`.
|
||||
2. **`GameReport.otherRaces`** field added to
|
||||
`ui/frontend/src/api/game-state.ts`; the synthetic-report
|
||||
decoder populates it the same way (`api/synthetic-report.ts`).
|
||||
Phase 22's Races View can read this directly without a fresh
|
||||
plumbing pass — the Phase 22 stage text below is updated to
|
||||
reflect that.
|
||||
3. **Stationed-ship rows are clickable**. The Phase 19 stationed-
|
||||
ship subsection on the planet inspector becomes interactive
|
||||
for own groups (Phase 21+ table view stays a separate target).
|
||||
The map renderer continues to hide on-planet groups — this is
|
||||
the cheaper navigational fix.
|
||||
4. **Inline forms, no modal**. Every action opens an inline
|
||||
editor under the buttons row, matching the Phase 14 rename and
|
||||
Phase 16 cargo-route patterns. Send reuses
|
||||
`MAP_PICK_CONTEXT_KEY` (Phase 16's renderer service) for the
|
||||
destination picker. Foreign-COL Dismantle uses a two-step
|
||||
inline confirm (button label flips to "confirm — colonists
|
||||
die") rather than a separate modal component.
|
||||
5. **Implicit split for Send/Load/Unload/Modernize/Dismantle/
|
||||
Transfer**. The number-of-ships input defaults to the group's
|
||||
full count; when the player picks a smaller M, the inspector
|
||||
prepends `breakShipGroup(id, newId, M)` and routes the action
|
||||
at `newId`. JoinFleet and Split do not get a counter (JoinFleet
|
||||
is whole-group atomically per the engine; Split *is* the break
|
||||
command).
|
||||
|
||||
## Phase 21. Sciences — CRUD List + Designer
|
||||
|
||||
@@ -2226,7 +2313,12 @@ Artifacts:
|
||||
- `ui/frontend/src/routes/games/[id]/table/races/+page.svelte` table
|
||||
with one row per race, including name, tech levels, total
|
||||
population, total production, planet count, war-or-peace from this
|
||||
race's perspective, votes received
|
||||
race's perspective, votes received. The race list itself is read
|
||||
from `GameReport.otherRaces` (introduced in Phase 20 for the
|
||||
ship-group transfer-to-race picker); the table view widens the
|
||||
per-race shape (tech / population / production / planet count /
|
||||
votes / relation) by walking `report.player[]` directly when those
|
||||
fields are needed
|
||||
- per-row toggle for declaring war or peace (adds
|
||||
`SetDiplomaticStance` command)
|
||||
- voting control: a single slot for `give my votes to <race>` (adds
|
||||
|
||||
Reference in New Issue
Block a user