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").