feat: v0.9.0 — single-screen 3-pane shell + left Assets pane (wallet/reputation/fleet/time controls) from a single /api/state poll; right pane placeholder
This commit is contained in:
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## v0.9.0 — 2026-09-17 14:24
|
||||
- **Single-screen 3-pane shell** (PLAN.md "Layout" increment): `app/page.tsx` is now a CSS grid (`grid-template-columns: 300px minmax(0, 1fr) 320px`) — left Assets pane, center = the existing `<Map/>` **untouched** (its `.map-wrap` fills the middle column, header included), right placeholder `data-testid="right-pane"` with a subtle "Jobs & Market" heading so the next increment has a home
|
||||
- **Single polling source**: new hook `lib/useGameState.ts` polls `GET /api/state` every 1000 ms (configurable arg) and returns `{state, loading, error, refresh}` — overlap guard skips a tick while the previous request is still in flight, interval cleaned up on unmount. Called ONCE in `app/page.tsx`; the snapshot flows down as props so later panes share one poll loop instead of running their own
|
||||
- **Left Assets pane** (`components/LeftPane.tsx`, `data-testid="left-pane"`): wallet cash (formatted EUR), reputation, and a fleet list — per plane: catalog display name (ATR 72-600), status badge, fuel bar + `x / y L`, location (grounded → airport name; flying → "origin → destination" from its bound contract). Time controls: pause/resume toggle + 1x–60x scale select wired to `POST /api/sim`; after each action the pane calls the hook's `refresh()` so it refetches immediately instead of waiting for the next poll tick. Sim clock shown as a UTC wall-clock (`data-testid="sim-clock"`)
|
||||
- Pure display formatters in new `lib/format.ts` — `formatCash` (EUR, 2 dp), `formatFuel` ("x / y L"), `formatSimClock` (epoch ms → deterministic UTC string)
|
||||
- No backend changes: all endpoints pre-existed (`GET /api/state`, `POST /api/sim`); no OpenAPI change; Map.tsx and its header untouched per the increment constraint
|
||||
- Tests: unit — new `lib/format.test.ts` (currency/fuel/clock formatting incl. fractional cash + rounding) and `lib/useGameState.test.ts` (`fetchState` success shape + exact fetch args, non-2xx error with status, network failure via mocked global fetch); e2e — new `e2e/panes.spec.ts`: left pane renders wallet (cash > 0)/reputation/starter ATR 72-600 at Frankfurt + sim clock, time controls pause the sim and set scale 60 through the UI (asserted against authoritative `/api/state` AND in-pane button/select state; restored to scale 1 + unpaused in-test and again in `afterEach`, same pattern as sim.spec.ts), right pane placeholder present
|
||||
- **Known interaction with v0.4.x map e2e**: the three-pane layout narrows the map column to ~660 px at the default 1280×720 viewport, which puts UKBB Boryspil (~475 px east of the [10,52] center at zoom 4) outside the initial view — `e2e/map.spec.ts` marker-click tests re-pointed from UKBB to **LFRS Nantes Atlantique**, now the most isolated large_airport marker inside the new initial viewport (nearest neighbour LFRB ~78 px away), same pattern as the v0.2.7/v0.2.8 dataset re-points
|
||||
- **Pre-existing e2e race fixed** (found while landing this increment, reproduced 5/5 on a clean v0.8.0 worktree deploy — NOT caused by the layout): MapLibre's diff-based `setStyle` removes our custom airports source/layer before `ensureAirportsLayer` re-adds it on the next `styledata` event (~20–40 ms window), and the style-switcher test's post-switch wait (`queryRenderedFeatures > 0`) can be satisfied by stale pre-switch worker data, so its single marker click could land in that gap. The test now retries the click until the popup opens (marker is clickable again within milliseconds; the `toHaveCount(1)` assertion still guards against duplicate listeners)
|
||||
- Verified: unit suite 143 → 150 green; `tsc --noEmit` clean; e2e 18 → 21 green against the rebuilt compose instance (marker-click, zoom clamp and style switch all pass in the narrower middle column)
|
||||
|
||||
## v0.8.0 — 2026-09-17 13:54
|
||||
- **Player-actions API** (PLAN.md "Core loop" increment, server-side only — NO frontend/UI changes): the core loop now works end-to-end through five new endpoints in `lib/game/actions.ts` + `app/api/`. Every action endpoint follows one convention: tick the sim clock FIRST (so validation/mutation see current game time), validate, mutate, persist atomically under the same promise lock as ticks (`engine.runAction()` — no request can interleave between tick and mutation), then return the **FULL state snapshot** (same shape as GET /api/state) with 200 so clients never need a follow-up poll; errors are proper 4xx `{error}` bodies
|
||||
- **Accept**: `POST /api/contracts/:id/accept` — open → accepted only. 404 unknown id, 409 if the contract is not 'open' (accepted/completed/expired). Single-player, so no ownership tracking beyond the status flip; dispatch binds the plane
|
||||
|
||||
Reference in New Issue
Block a user