import { Link, useParams } from 'react-router-dom' import PublicLayout from '../../components/PublicLayout.jsx' import PageHeader from '../../components/PageHeader.jsx' import { Loading, ErrorState, EmptyState } from '../../components/PageState.jsx' import { useAsync } from '../../lib/useAsync.js' import { api } from '../../api/client.js' // One player vendor: where to find it and everything it is selling. // // The page a search result points at. Two states it has to render honestly and // which the search list cannot (docs/link/v3.md §8): // // • `truncated` — the shop holds more than the shard publishes per frame. A // commodity reseller with thousands of stacks is a real thing, and showing // 250 of 3,104 as if it were the whole shop would be a lie about the shard. // • a gated `location` — an admin may put vendor whereabouts behind a rung, in // which case there is nothing to render and the page says so rather than // showing an empty coordinate. const num = (v) => (Number.isFinite(Number(v)) ? Number(v).toLocaleString() : '—') const itemLabel = (i) => i.displayName || i.name || `id ${i.itemId}` export default function MarketVendor() { const { serial } = useParams() const { loading, error, data } = useAsync(() => api.shard.marketVendor(serial), [serial]) if (loading) { return (
) } if (error || !data) { return (

← Back to the marketplace

) } const loc = data.location || null const items = data.items || [] return (

{data.truncated ? `Showing ${num(data.count)} of ${num(data.total)} listings — this shop holds more than the shard publishes.` : `${num(data.total)} listing${data.total === 1 ? '' : 's'}`} {data.updatedAt ? ` · last seen ${new Date(data.updatedAt).toLocaleString()}` : ''}

{items.length === 0 ? ( This shop has nothing priced for sale. ) : (
{items.map((i) => (
{i.amount > 1 ? `${num(i.amount)} × ` : ''} {itemLabel(i)} {i.child ? · sold with its container : null} {num(i.price)}
))}
)}

← Back to the marketplace

) }