deadhead-space-freight-tycoon/apps/web/src/components/DealModal.tsx

89 lines
3.1 KiB
TypeScript

import { content } from "@deadhead/content";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useNavigate } from "react-router";
import { useApi } from "../api/context";
import { formatCredits } from "../format";
import { errorMessage, useLoadedPlayer } from "../state/actions";
import { PLAYER_STATE_KEY } from "../state/player";
import { Art, Button, Sheet } from "../ui";
/**
* The one-time deal (decision 0.5.30): shown when the server says it is
* available, after any delivery summary has been dismissed. There is no
* decline option.
*/
export function DealModal() {
const state = useLoadedPlayer();
const api = useApi();
const queryClient = useQueryClient();
const navigate = useNavigate();
const accept = useMutation({
mutationFn: () => api.call("acceptDeal", {}),
onSuccess: (newState) => {
queryClient.setQueryData(PLAYER_STATE_KEY, newState);
void navigate("/");
},
});
if (!state.tutorial?.dealAvailable || state.summaries.length > 0) return null;
const { dealLoanAmount, loanRepaymentShare } = content.config;
const courier = content.shipModel("courier")!;
const veteran = content.veteranPilot;
return (
<Sheet
titleId="deal-title"
eyebrow="Incoming transmission"
title="An offer from Halvorsen"
tone="amber"
layer="z-30"
>
<div className="mt-3 flex items-center gap-3">
<Art sprite={courier.sprite} className="size-20 shrink-0 -rotate-12" />
<p className="text-sm text-ink-200">
Old Halvorsen is retiring. He&apos;ll sell you his Courier and his Earth bay, and his
pilot comes with them. Pay him back out of every job.
</p>
</div>
<dl className="mt-4 grid grid-cols-[auto_1fr] gap-x-4 gap-y-2 rounded-xl bg-space-950/60 p-3 text-sm ring-1 ring-amber-400/25">
<dt className="font-display text-[11px] tracking-[0.14em] text-ink-400 uppercase">
You get
</dt>
<dd>A {courier.name} and a bay at Earth</dd>
<dt className="font-display text-[11px] tracking-[0.14em] text-ink-400 uppercase">Pilot</dt>
<dd aria-label="Veteran pilot">
…and {veteran.name}. {veteran.bio}
</dd>
<dt className="font-display text-[11px] tracking-[0.14em] text-ink-400 uppercase">Loan</dt>
<dd
aria-label="Loan amount"
className="font-display font-semibold text-amber-200 tabular-nums"
>
{formatCredits(dealLoanAmount)}
</dd>
<dt className="font-display text-[11px] tracking-[0.14em] text-ink-400 uppercase">
Repayment
</dt>
<dd aria-label="Repayment share">
{Math.round(loanRepaymentShare * 100)}% of every payout until it&apos;s paid off
</dd>
</dl>
{accept.isError && (
<p role="alert" className="mt-3 text-sm text-rose-300">
{errorMessage(accept.error)}
</p>
)}
<Button
size="lg"
block
onClick={() => accept.mutate()}
disabled={accept.isPending}
className="mt-5"
>
Shake on it
</Button>
</Sheet>
);
}