diff --git a/apps/web/src/components/AwaySummary.tsx b/apps/web/src/components/AwaySummary.tsx index d101bee..717a20c 100644 --- a/apps/web/src/components/AwaySummary.tsx +++ b/apps/web/src/components/AwaySummary.tsx @@ -1,7 +1,7 @@ import { content } from "@deadhead/content"; import { useState } from "react"; import { useNavigate } from "react-router"; -import { formatCredits, locationName } from "../format"; +import { formatCredits, locationName, toLocation } from "../format"; import { formatLocalTime } from "../screens/DispatchSheet"; import { useGameAction, useLoadedPlayer } from "../state/actions"; import { Button, Icon, Sheet } from "../ui"; @@ -47,6 +47,12 @@ export function AwaySummary() { {locationName(s.originId)} → {locationName(s.destinationId)} + {!s.deadhead && s.goodId && s.massTonnes !== null && ( +

+ Delivered {s.massTonnes} t {content.good(s.goodId)?.name ?? s.goodId} + {s.shipper && ` for ${s.shipper}`} {toLocation(s.destinationId)} +

+ )} {s.deadhead ? ( // An empty flight earns nothing: say what it was and what it cost.
diff --git a/apps/web/src/format.ts b/apps/web/src/format.ts index 2a72084..d7027b7 100644 --- a/apps/web/src/format.ts +++ b/apps/web/src/format.ts @@ -38,6 +38,11 @@ export function locationName(id: string): string { return content.location(id)?.name ?? id; } +/** A location as a destination in a sentence: "to the Moon", "to Mars". */ +export function toLocation(id: string): string { + return id === "moon" ? "to the Moon" : `to ${locationName(id)}`; +} + export function shipModelName(id: string): string { return content.shipModel(id)?.name ?? id; } diff --git a/apps/web/test/away-summary.test.tsx b/apps/web/test/away-summary.test.tsx index 6d24361..6fd7881 100644 --- a/apps/web/test/away-summary.test.tsx +++ b/apps/web/test/away-summary.test.tsx @@ -59,6 +59,27 @@ const summaries: ResolutionSummary[] = [ ]; describe("while you were away", () => { + it("names what was delivered, how much, for whom and where (CR-007)", async () => { + const delivery: ResolutionSummary = { + ...summaries[0]!, + destinationId: "moon", + goodId: "medical-supplies", + massTonnes: 6, + shipper: "Meridian Health", + discoveredLocationId: null, + }; + renderApp( + fakeApi({ + getPlayerState: () => + samplePlayerState({ tutorial: null, ships: [ship], summaries: [delivery] }), + }), + ); + const dialog = await screen.findByRole("dialog", { name: "While you were away" }); + expect(within(dialog).getAllByRole("listitem")[0]).toHaveTextContent( + "Delivered 6 t Medical Supplies for Meridian Health to the Moon", + ); + }); + it("shows a deadhead as an empty flight with its fuel, not as a +0 payout", async () => { const deadhead: ResolutionSummary = { ...summaries[0]!, @@ -66,9 +87,9 @@ describe("while you were away", () => { destinationId: "moon", deadhead: true, fuelCost: 230, - goodId: "consumer-goods", - massTonnes: 12, - shipper: "Pacific Orbital Logistics", + goodId: null, + massTonnes: null, + shipper: null, credits: 0, pilotXp: 0, discoveredLocationId: null, @@ -86,6 +107,7 @@ describe("while you were away", () => { expect(entry).toHaveTextContent("Deadhead · 230 cr fuel"); expect(entry).not.toHaveTextContent("+0 cr"); expect(entry).not.toHaveTextContent("XP"); + expect(entry).not.toHaveTextContent("Delivered"); }); it("lists each completed delivery, and dismissing acknowledges them and hides the dialog", async () => { diff --git a/memory-bank/progress.md b/memory-bank/progress.md index 8e2af45..eaffcc8 100644 --- a/memory-bank/progress.md +++ b/memory-bank/progress.md @@ -211,3 +211,4 @@ Spec: `memory-bank/change-requests/CR-007-cargo-goods.md` (supplied as "CR-005 / - [x] **Step 10G.7 — Server: pass-through and validation.** No server code changes were needed beyond 10G.4's message and 10G.6's summary fields: boards come from the sim and dispatch validation returns the sim's `too-slow`. Tests (`contract-dispatch.test.ts`): a Priority job whose window is shorter than the Courier's trip is refused with `too-slow` and changes nothing; with a window the Courier makes, it dispatches and the stored contract keeps its Priority good; every generated Earth contract names a catalog good of its class and a shipper from Earth's list. - [x] **Step 10G.8 — Client: contract card redesign.** New `components/Goods.tsx`: `goodName` (a Priority good may use its `priorityName`, e.g. Emergency Parts), `ClassChip` ("High-Value · T1", toned by class), `PriorityChip` ("Priority · within 5h" in game hours; tap switches to real time, hover shows both), `tooSlowForAll` (idle ships at the origin exist and none makes the window, via sim `contractTripGameHours`), `formatWindow`, `realTimeOf`. Location board cards, top to bottom: route and payout; the good as an `h3` headline with class, Priority, First contact, Too heavy and Too slow chips; route strip; shipper · mass · distance; requirement and expiry with Dispatch. Tutorial cards show the good's name instead of the class. Tests: `contract-card.test.tsx` (good, class/tier chip, shipper, mass, requirement; Priority chip in game hours and real time on tap with the Priority name; Too slow with only a Bulk Hauler idle); `location-boards.test.tsx` updated to the new card. - [x] **Step 10G.9 — Client: Priority in the dispatch sheet.** `explainProblems` adds "Too slow — 7h vs 5h window" (sim `contractTripGameHours` vs the window) so a slow pair is disabled with the numbers; `PairOption.priority` carries each pair's trip and the window, and eligible pairs show "Priority: 2.1h trip, 1.4h to spare in the 3.5h window". The sheet's job details now start with the good and shipper ("Mail & Parcels for Pacific Orbital Logistics · Standard · 6 t …") and Priority jobs show the window chip. Test: `dispatch-priority.test.tsx` (Courier enabled with its margin, Bulk Hauler disabled with "Too slow — …", numbers from the sim). +- [x] **Step 10G.10 — Client: goods in summaries.** Each delivery in "While you were away" says "Delivered 6 t Medical Supplies for Meridian Health to the Moon" (from the summary's `goodId`, `massTonnes`, `shipper`; new `toLocation` gives "to the Moon" / "to Mars"); deadheads keep their fuel line. Test: `away-summary.test.tsx` (the seeded delivery line; a deadhead shows no "Delivered").