deadhead-space-freight-tycoon/apps/web/src/screens/Market.tsx

61 lines
2.6 KiB
TypeScript

import { content } from "@deadhead/content";
import { formatCredits, formatMillionKm } from "../format";
import { errorMessage, useGameAction, useLoadedPlayer } from "../state/actions";
const LANDING_LABELS = { vacuum: "Vacuum", atmospheric: "Atmospheric", hardened: "Hardened" };
/** Ship models for sale; new ships appear at Earth (Step 9.8). */
export function Market() {
const state = useLoadedPlayer();
const buy = useGameAction("buyShip");
return (
<section className="space-y-3 p-4">
<h1 className="text-xl font-semibold">Market</h1>
<p className="text-sm text-slate-400">New ships are delivered to Earth with no pilot.</p>
{buy.isError && (
<p role="alert" className="text-sm text-red-400">
{errorMessage(buy.error)}
</p>
)}
{buy.isSuccess && (
<p role="status" className="text-sm text-emerald-400">
{buy.data.ship.name} is waiting at Earth.
</p>
)}
<ul className="space-y-2">
{content.purchasableShipModels.map((model) => {
const affordable = state.company.credits >= model.price;
return (
<li key={model.id} className="rounded-lg border border-slate-800 p-3 text-sm">
<div className="flex items-baseline justify-between gap-2">
<span className="font-medium">{model.name}</span>
<span className="font-semibold tabular-nums">{formatCredits(model.price)}</span>
</div>
<dl className="mt-2 grid grid-cols-2 gap-x-3 gap-y-0.5 text-slate-300">
<dt className="text-slate-400">Speed</dt>
<dd>{formatMillionKm(model.cruiseSpeedKmPerGameHour / 1e6)}/h</dd>
<dt className="text-slate-400">Capacity</dt>
<dd>{model.cargoCapacityTonnes} t</dd>
<dt className="text-slate-400">Range</dt>
<dd>{formatMillionKm(model.fuelRangeMillionKm)}</dd>
<dt className="text-slate-400">Landing</dt>
<dd>{LANDING_LABELS[model.landingClass]}</dd>
</dl>
<button
type="button"
aria-label={`Buy ${model.name}`}
disabled={!affordable || buy.isPending}
onClick={() => buy.mutate({ modelId: model.id })}
className="mt-2 w-full rounded-md bg-amber-500 py-1.5 font-semibold text-slate-950 disabled:opacity-40"
>
{affordable ? "Buy" : "Not enough credits"}
</button>
</li>
);
})}
</ul>
</section>
);
}