refactor(desktop): widen market listing with wallet

Signed-off-by: Wintermute <c0fc581234c3585602139eec347ced7b82af65b6f6c10728348515c0c06c51c3@buzz.block.builderlab.xyz>
This commit is contained in:
Wintermute
2026-08-31 21:58:10 -04:00
parent e1ba305742
commit 36167bb90c
2 changed files with 92 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ import {
Radio,
Store,
TriangleAlert,
WalletCards,
} from "lucide-react";
import * as React from "react";
@@ -37,6 +38,37 @@ const REPRESENTING_AGENTS = [
{ name: "Sentinel", role: "Operations & safety" },
] as const;
const WALLET_DETAILS: Record<
MarketScenarioId,
{ account: string; balance: string; settlement: string }
> = {
finite: {
account: "escrow1report…13c8",
balance: "150 sats funded",
settlement: "1 paid · 2 reserved",
},
unlimited: {
account: "wallet1mapper…c241",
balance: "80 sats escrowed",
settlement: "760 sats paid",
},
auction: {
account: "escrow1strings…9f10",
balance: "600 sats funded",
settlement: "Held until award",
},
tender: {
account: "escrow1tender…1b73",
balance: "2,000 sats funded",
settlement: "Held during selection",
},
awarded: {
account: "escrow1tender…1b73",
balance: "1,750 sats escrowed",
settlement: "Release on signed receipt",
},
};
const COMMERCIAL_TERM_LABELS = new Set([
"Price",
"Initial quantity",
@@ -96,10 +128,11 @@ export function MarketScreen({ scenarioId }: { scenarioId: MarketScenarioId }) {
/>
<div className="min-h-0 flex-1 overflow-y-auto">
<div className="mx-auto flex w-full max-w-3xl flex-col px-6 py-6">
<div className="flex w-full min-w-0 flex-col px-6 py-6">
<OfferCard
onCreate={() => setCreateOpen(true)}
scenario={scenario}
scenarioId={scenarioId}
/>
<section className="pt-5" data-testid="market-agent-timeline">
@@ -161,9 +194,11 @@ export function MarketScreen({ scenarioId }: { scenarioId: MarketScenarioId }) {
function OfferCard({
onCreate,
scenario,
scenarioId,
}: {
onCreate: () => void;
scenario: MarketScenario;
scenarioId: MarketScenarioId;
}) {
const author =
scenario.terms.find(
@@ -181,8 +216,11 @@ function OfferCard({
className="overflow-hidden rounded-2xl border bg-card"
data-testid="market-offer-card"
>
<div className="grid sm:grid-cols-[12rem_minmax(0,1fr)]">
<div className="flex min-h-44 items-center justify-center border-b bg-muted/60 text-muted-foreground sm:border-b-0 sm:border-r">
<div className="grid sm:grid-cols-[minmax(12rem,22rem)_minmax(0,1fr)_17rem]">
<div
className="flex aspect-square items-center justify-center border-b bg-muted/60 text-muted-foreground sm:border-b-0 sm:border-r"
data-testid="market-product-image"
>
<div className="flex flex-col items-center gap-2">
<PackageCheck className="h-8 w-8" strokeWidth={1.5} />
<span className="text-xs">Product image</span>
@@ -228,11 +266,47 @@ function OfferCard({
</Button>
</div>
</div>
<AgentWallet scenarioId={scenarioId} />
</div>
</section>
);
}
function AgentWallet({ scenarioId }: { scenarioId: MarketScenarioId }) {
const wallet = WALLET_DETAILS[scenarioId];
return (
<aside
className="flex flex-col border-t bg-muted/25 p-5 sm:border-l sm:border-t-0"
data-testid="market-agent-wallet"
>
<div className="flex items-center gap-2">
<WalletCards className="h-4 w-4 text-muted-foreground" />
<h3 className="text-sm font-semibold">Agent wallet</h3>
</div>
<p className="mt-1 text-xs text-muted-foreground">
Settlement for this listing
</p>
<dl className="mt-5 space-y-4">
<WalletValue label="Balance" value={wallet.balance} />
<WalletValue label="Settlement" value={wallet.settlement} />
<WalletValue label="Network" value="Sandbox Lightning" />
</dl>
<p className="mt-auto pt-5 font-mono text-xs text-muted-foreground">
{wallet.account}
</p>
</aside>
);
}
function WalletValue({ label, value }: { label: string; value: string }) {
return (
<div>
<dt className="text-xs text-muted-foreground">{label}</dt>
<dd className="mt-0.5 text-sm font-medium">{value}</dd>
</div>
);
}
function CreateMarketDialog({
onOpenChange,
open,

View File

@@ -46,6 +46,21 @@ test.describe("market channel prototype", () => {
: "Design a relay abuse-response playbook",
);
await expect(market.getByTestId("market-offer-card")).toBeVisible();
const offerCardWidth = await market
.getByTestId("market-offer-card")
.evaluate((element) => element.getBoundingClientRect().width);
expect(offerCardWidth).toBeGreaterThan(1100);
const imageAspectRatio = await market
.getByTestId("market-product-image")
.evaluate((element) => {
const bounds = element.getBoundingClientRect();
return bounds.width / bounds.height;
});
expect(imageAspectRatio).toBeCloseTo(1, 1);
await expect(market.getByTestId("market-agent-wallet")).toBeVisible();
await expect(
market.getByText("Agent wallet", { exact: true }),
).toBeVisible();
await expect(market.getByText("Agent market channel")).toBeVisible();
await expect(market.getByTestId("market-agent-avatar")).toHaveCount(
scenario === "unlimited" ? 3 : 4,