Skip to content

Commit bd82cc8

Browse files
committed
feat(order-ticket): real-time progress indicator with backend polling
Drop `wait: true` on submit-order and poll GET /v0/orders/:id every 1s (120s ceiling). A new 4-step stepper (Quote → Sign → Submit → Settle) reflects the real operator lifecycle (accepted → fulfilling → fulfilled/failed/resting/partial) instead of a black-box "Submitting…" state during the on-chain settle. - client.ts: fetchOrder(id) → GET /v0/orders/:id - types.ts: add PmxtOrder.error (already used by DoneStage) - order-ticket.tsx: new 'settling' stage + pollOrderStatus helper
1 parent cddc9fc commit bd82cc8

18 files changed

Lines changed: 248 additions & 34 deletions

apps/demo/public/r/balance-card.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"files": [
1212
{
1313
"path": "registry/pmxt/balance-card/balance-card.tsx",
14-
"content": "'use client';\n\nimport { useBalances } from '@/lib/pmxt/hooks';\nimport { usePmxtWallet } from '@/lib/pmxt/provider';\nimport { formatUsd, venueLabel } from '@/lib/pmxt/format';\nimport { ExternalLinkIcon, SpinnerIcon } from '@/lib/pmxt/icons';\n\n/** Props for {@link BalanceCard}. */\nexport interface BalanceCardProps {\n /** Address to show balances for; defaults to the connected wallet. */\n address?: `0x${string}`;\n className?: string;\n}\n\n/** PMXT escrow balance for an address, with a deposit/withdraw link. */\nexport function BalanceCard({ address, className = '' }: BalanceCardProps) {\n const wallet = usePmxtWallet();\n const resolved = address ?? wallet.address;\n const { data, error, loading } = useBalances(resolved);\n\n if (!resolved) {\n return (\n <section\n className={`rounded-xl border border-zinc-200/80 bg-[var(--pmxt-surface,#ffffff)] p-4 shadow-sm dark:border-zinc-800 dark:bg-[var(--pmxt-surface-dark,#18181b)] ${className}`}\n >\n <button\n type=\"button\"\n onClick={() => void wallet.connect()}\n disabled={wallet.connecting}\n className=\"w-full rounded-lg bg-zinc-900 px-3 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200\"\n >\n {wallet.connecting ? 'Connecting…' : 'Connect wallet'}\n </button>\n </section>\n );\n }\n\n const balances = data ?? [];\n const primary = balances[0];\n\n return (\n <section\n className={`rounded-xl border border-zinc-200/80 bg-[var(--pmxt-surface,#ffffff)] p-4 shadow-sm dark:border-zinc-800 dark:bg-[var(--pmxt-surface-dark,#18181b)] ${className}`}\n >\n <div className=\"text-[11px] font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500\">\n PMXT escrow balance\n </div>\n <div className=\"mt-1 font-mono text-3xl font-semibold text-zinc-950 dark:text-zinc-50\">\n {loading && !data ? (\n <SpinnerIcon className=\"size-6 text-zinc-300 dark:text-zinc-600\" />\n ) : (\n formatUsd(primary?.amount)\n )}\n </div>\n {error && <div className=\"mt-1 text-xs text-red-600 dark:text-red-400\">{error}</div>}\n {balances.length > 1 && (\n <ul className=\"mt-3 space-y-1 border-t border-zinc-100 pt-3 dark:border-zinc-800\">\n {balances.map((b, i) => (\n <li\n key={`${b.venue ?? 'all'}-${b.currency}-${i}`}\n className=\"flex items-center justify-between text-xs text-zinc-600 dark:text-zinc-300\"\n >\n <span>\n {venueLabel(b.venue)}{' '}\n <span className=\"uppercase text-zinc-400 dark:text-zinc-500\">\n {b.currency}\n </span>\n </span>\n <span className=\"font-mono text-zinc-900 dark:text-zinc-100\">\n {formatUsd(b.amount)}\n </span>\n </li>\n ))}\n </ul>\n )}\n <div className=\"mt-3 flex items-center justify-end gap-2 text-xs text-zinc-500 dark:text-zinc-400\">\n <a\n href=\"https://pmxt.dev/dashboard/wallet\"\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex shrink-0 items-center gap-1 font-medium text-zinc-700 transition-colors hover:text-zinc-950 dark:text-zinc-300 dark:hover:text-zinc-50\"\n >\n Deposit / withdraw →\n <ExternalLinkIcon className=\"size-3\" />\n </a>\n </div>\n </section>\n );\n}\n",
14+
"content": "'use client';\n\nimport { useBalances, usePortfolio } from '@/lib/pmxt/hooks';\nimport { usePmxt, usePmxtWallet } from '@/lib/pmxt/provider';\nimport { ConnectWalletButtons } from '@/lib/pmxt/connect-buttons';\nimport { formatUsd, venueLabel } from '@/lib/pmxt/format';\nimport { ExternalLinkIcon, SpinnerIcon } from '@/lib/pmxt/icons';\n\n/** Props for {@link BalanceCard}. */\nexport interface BalanceCardProps {\n /** Address to show balances for; defaults to the connected wallet. */\n address?: `0x${string}`;\n className?: string;\n}\n\nconst surface =\n 'rounded-xl border border-zinc-200/80 bg-[var(--pmxt-surface,#ffffff)] p-4 shadow-sm dark:border-zinc-800 dark:bg-[var(--pmxt-surface-dark,#18181b)]';\n\n/**\n * Portfolio value for an address — escrow cash plus held positions marked\n * to market at best bid — with a deposit/withdraw link. Sandbox mode shows\n * the simulated play-money balance instead.\n */\nexport function BalanceCard({ address, className = '' }: BalanceCardProps) {\n const { sandbox } = usePmxt();\n const wallet = usePmxtWallet();\n const resolved = address ?? wallet.address;\n const live = sandbox == null;\n\n const portfolio = usePortfolio(resolved, { enabled: live });\n const balances = useBalances(resolved, { enabled: !live });\n\n if (!resolved) {\n return (\n <section className={`${surface} ${className}`}>\n <ConnectWalletButtons buttonClassName=\"w-full rounded-lg bg-zinc-900 px-3 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-200\" />\n {wallet.connectError && (\n <div className=\"mt-2 text-xs text-red-600 dark:text-red-400\">\n {wallet.connectError}\n </div>\n )}\n </section>\n );\n }\n\n if (live) {\n const { data, error, loading } = portfolio;\n return (\n <section className={`${surface} ${className}`}>\n <div className=\"font-mono text-3xl font-semibold text-zinc-950 dark:text-zinc-50\">\n {loading && !data ? (\n <SpinnerIcon className=\"size-6 text-zinc-300 dark:text-zinc-600\" />\n ) : (\n formatUsd(data?.total)\n )}\n </div>\n <div className=\"mt-1 text-xs text-zinc-500 dark:text-zinc-400\">\n Portfolio\n {data && (\n <>\n {' · '}\n <span className=\"font-mono text-zinc-700 dark:text-zinc-300\">\n {formatUsd(data.cash)}\n </span>\n {' cash'}\n </>\n )}\n </div>\n {error && !data && (\n <div className=\"mt-1 text-xs text-red-600 dark:text-red-400\">\n {error}\n </div>\n )}\n <DepositLink />\n </section>\n );\n }\n\n const sandboxBalances = balances.data ?? [];\n const primary = sandboxBalances[0];\n return (\n <section className={`${surface} ${className}`}>\n <div className=\"text-[11px] font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500\">\n Sandbox balance\n </div>\n <div className=\"mt-1 font-mono text-3xl font-semibold text-zinc-950 dark:text-zinc-50\">\n {balances.loading && !balances.data ? (\n <SpinnerIcon className=\"size-6 text-zinc-300 dark:text-zinc-600\" />\n ) : (\n formatUsd(primary?.amount)\n )}\n </div>\n {balances.error && (\n <div className=\"mt-1 text-xs text-red-600 dark:text-red-400\">\n {balances.error}\n </div>\n )}\n {sandboxBalances.length > 1 && (\n <ul className=\"mt-3 space-y-1 border-t border-zinc-100 pt-3 dark:border-zinc-800\">\n {sandboxBalances.map((b, i) => (\n <li\n key={`${b.venue ?? 'all'}-${b.currency}-${i}`}\n className=\"flex items-center justify-between text-xs text-zinc-600 dark:text-zinc-300\"\n >\n <span>\n {venueLabel(b.venue)}{' '}\n <span className=\"uppercase text-zinc-400 dark:text-zinc-500\">\n {b.currency}\n </span>\n </span>\n <span className=\"font-mono text-zinc-900 dark:text-zinc-100\">\n {formatUsd(b.amount)}\n </span>\n </li>\n ))}\n </ul>\n )}\n </section>\n );\n}\n\nfunction DepositLink() {\n return (\n <div className=\"mt-3 flex items-center justify-end gap-2 text-xs text-zinc-500 dark:text-zinc-400\">\n <a\n href=\"https://pmxt.dev/dashboard/wallet\"\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex shrink-0 items-center gap-1 font-medium text-zinc-700 transition-colors hover:text-zinc-950 dark:text-zinc-300 dark:hover:text-zinc-50\"\n >\n Deposit / withdraw →\n <ExternalLinkIcon className=\"size-3\" />\n </a>\n </div>\n );\n}\n",
1515
"type": "registry:component",
1616
"target": "components/pmxt/balance-card.tsx"
1717
}

apps/demo/public/r/event-card.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)