ui/phase-22: races table with stance toggle and vote slot
Adds the Races View in the in-game shell. The table lists every non-extinct other race with tech levels (percent), totals, planets, votes received, and a per-row WAR | PEACE segmented control. A single vote-recipient slot above the table queues a `CommandRaceVote`; per-row buttons queue `CommandRaceRelation`. Both commands flow through the existing order draft store with collapse-by-acceptor (stance) and singleton (vote) rules. `GameReport` widens with `races`, `myVotes`, `myVoteFor`; the decoder walks `report.player[]` once for the richer projection. The optimistic overlay flips stance and vote target immediately; `votesReceived`, `myVotes`, and the alliance summary stay server-authoritative — alliance grouping and the 2/3 victory check are tallied on the server at turn cutoff and explicitly not surfaced client-side (`rules.txt` keeps foreign races' outgoing vote targets private). Includes Vitest component coverage of stance and vote collapse rules + a Playwright e2e that drives both commands through the dispatcher route and verifies the gateway saw the expected `CommandRaceRelation` / `CommandRaceVote` payloads. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+64
-24
@@ -2391,44 +2391,84 @@ Targeted tests:
|
||||
via the Research sub-row, delete it
|
||||
(`tests/e2e/sciences.spec.ts`).
|
||||
|
||||
## Phase 22. Races View — War/Peace Toggle and Votes
|
||||
## ~~Phase 22. Races View — War/Peace Toggle and Votes~~
|
||||
|
||||
Status: pending.
|
||||
Status: done.
|
||||
|
||||
Goal: list other races with their visible stats, expose war/peace
|
||||
toggle and the voting UI.
|
||||
Goal: list other races with their visible stats, expose the war/peace
|
||||
toggle, and the voting UI.
|
||||
|
||||
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. 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
|
||||
`SetVoteRecipient` command)
|
||||
- alliance summary panel showing the current vote graph and any
|
||||
alliance reaching ≥ 2/3 of total votes
|
||||
- `ui/frontend/src/lib/active-view/table-races.svelte` table mounted
|
||||
by the dispatcher in
|
||||
`ui/frontend/src/lib/active-view/table.svelte` (same pattern as
|
||||
Phase 21's sciences table). One row per non-extinct other race
|
||||
carrying name, tech levels (drive / weapons / shields / cargo as
|
||||
percent), total population, total production (engine `industry`),
|
||||
planet count, votes received, and the local player's stance
|
||||
toward that race. The richer per-race projection
|
||||
(`GameReport.races: ReportOtherRace[]`) is decoded in
|
||||
`ui/frontend/src/api/game-state.ts` by walking `report.player[]`
|
||||
once and surfacing the row alongside the existing `otherRaces:
|
||||
string[]` (which keeps backing the ship-group transfer picker from
|
||||
Phase 20)
|
||||
- per-row segmented `WAR | PEACE` control. The active stance is
|
||||
highlighted (`aria-pressed=true` + contrast colour); the inactive
|
||||
button queues `setDiplomaticStance` (engine `CommandRaceRelation`).
|
||||
The displayed stance is the local player's relation toward the
|
||||
named race (`rules.txt` "(R) Ваше отношение к указанной расе, но
|
||||
не наоборот") — not the other way round
|
||||
- voting control: a single `<select>` populated with `races[].name`,
|
||||
changing it queues `setVoteRecipient` (engine `CommandRaceVote`).
|
||||
Disabled when the local player is the only non-extinct race. A
|
||||
read-only `myVotes` total renders next to the picker
|
||||
- explanatory note in the page header: alliance grouping and the 2/3
|
||||
victory check are tallied on the server at turn cutoff and are
|
||||
NOT projected on the client. The report carries each race's votes
|
||||
received (`Player.votes`) and the local player's outgoing vote
|
||||
(`Report.vote_for`), but foreign races' outgoing votes are
|
||||
intentionally private, so a client-side vote graph would be
|
||||
partial. The acceptance criterion "vote counts match server state
|
||||
byte-for-byte" forbids a local recomputation
|
||||
|
||||
Cross-stack notes:
|
||||
|
||||
- No backend / wire changes. `CommandRaceRelation`,
|
||||
`CommandRaceVote`, `Player.relation`, `Player.votes`,
|
||||
`Report.votes`, and `Report.vote_for` already carry every datum
|
||||
this stage needs
|
||||
- TS draft store
|
||||
(`ui/frontend/src/sync/order-draft.svelte.ts`) gains two collapse
|
||||
rules: `setDiplomaticStance` collapses by `acceptor` (one stance
|
||||
intent per opponent); `setVoteRecipient` collapses singleton (a
|
||||
single outgoing vote slot per `rules.txt:1066`)
|
||||
- The optimistic overlay (`applyOrderOverlay`) flips
|
||||
`races[i].relation` and `myVoteFor` immediately so the controls
|
||||
reflect the queued intent without waiting for the auto-sync
|
||||
round-trip. `votesReceived`, `myVotes`, and the alliance state
|
||||
stay server-authoritative
|
||||
|
||||
Dependencies: Phase 14.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- the user can toggle war / peace and change vote recipient;
|
||||
- the alliance summary updates after a server roundtrip;
|
||||
- vote counts match server state byte-for-byte.
|
||||
- the per-row stance and the "I vote for" picker reflect the
|
||||
queued intent immediately (optimistic overlay) and resolve to
|
||||
`applied` in the sidebar order tab after the auto-sync round-trip;
|
||||
- vote counts match server state byte-for-byte (no client tally).
|
||||
|
||||
Targeted tests:
|
||||
|
||||
- Vitest component tests for the alliance summary on canonical fixtures
|
||||
(chain of votes, fork, win condition);
|
||||
- Playwright e2e: change diplomatic stance and vote, submit, confirm.
|
||||
- Vitest component test
|
||||
(`ui/frontend/tests/table-races.test.ts`) covering: render rows
|
||||
from a canonical fixture, filter, sort flip, stance click +
|
||||
collapse-by-acceptor, vote pick + singleton collapse, empty state;
|
||||
- Playwright e2e (`ui/frontend/tests/e2e/races.spec.ts`): open the
|
||||
races table, toggle one row's stance, change the vote recipient,
|
||||
observe both commands as `applied` in the sidebar order tab and
|
||||
verify the decoded gateway payload.
|
||||
|
||||
## Phase 23. Reports View — Current Turn Sections
|
||||
|
||||
|
||||
Reference in New Issue
Block a user