Widget UX polish: silent close + top-right pip + pre-accept gating - #2
Merged
Merged
Conversation
added 2 commits
May 14, 2026 21:49
Three UX fixes surfaced during the live E2E walkthrough on the demo
merchant-app. All three target the gap between what the widget renders
and what D-029's chain-driven resolver can actually deliver.
1) Silent close on `chatwoot: null`. Clicking Support on a pre-acceptance
order used to open the modal into a wall of explainer copy ("No
support inbox is provisioned for this order's circle yet..."). The
widget now just closes the modal — the click is a no-op. Phase
`no-chatwoot` is removed from the phase union since it's no longer
reachable. console.info logs the skip so devs see the cause.
2) Active-support pip moves to the top-right corner of each order row.
PaymentHistory gains a `renderRowBadge?: (order) => ReactNode` slot
that renders absolutely-positioned in the top-right. The pip used to
sit inline next to the Support button, crowding the action row.
PaymentHistoryWithSupport now wires the pip through that slot.
3) Pre-acceptance rows hide the Support surfaces entirely. Until a
merchant has accepted the order, `order.status === "placed"` and the
on-chain circleId is still 0 — so the Support button cannot open a
chat (D-029). PaymentHistoryWithSupport now returns null from both
`renderRowAction` and `renderRowBadge` when the row is placed. The
row itself + the Resume button stay visible — only the Support
surfaces are gated.
Tests: 25 → 26 widget tests green (43 core unchanged on node:test). New
case in PaymentHistoryWithSupport asserts pre-acceptance rows show no
Support button + no pip, but the Resume action stays. Existing Support
tests updated to expect the modal to close instead of showing copy when
chatwoot is null (live and cache paths both covered).
Two follow-ups surfaced during the live E2E run on the demo merchant-app. 1) Loaders during sign-in and chat boot. The signing phase was a small dashed-bordered card that flashed for 2.8s (chain RPC latency) and then vanished. Replaced with a centered spinner + title + body loader so the user has visible feedback. Adds a new phase `loading-chat` that renders the same loader between bootChatwoot and the widget opening, in case the Chatwoot SDK boot itself stalls. 2) Revert the `status === "placed"` gate in PaymentHistoryWithSupport. It was added on the wrong premise — that pre-acceptance orders have no circleId on chain. Verified on Base Sepolia: orders are bound to their circle at placement (user-app passes circleId into userPlaceOrder), not at acceptance. Bridge sign-in is now the only source of truth for "can this order open a chat" — and silently closes on `chatwoot: null`. The button renders for every row. 3) Don't cache `chatwoot: null` responses in localStorage. A null chatwoot is transient state (order not yet circle-bound, or bridge can't resolve the inbox) that we don't want to remember for 7 days. Successful sessions are still cached. Tests: 26 widget tests green. Updated the "rendering" assertion to cover the new always-render Support button behavior. Companion fix on the bridge side: support/packages/bridge/src/chain/ order.ts gets a correct Order struct ABI (the previous one was a miscopy of the listener's event tuple, which itself doesn't match the real on-chain struct). That commit is on the support repo's E2E PR.
Software-Artist-Aash
approved these changes
May 14, 2026
vvictor-dev
pushed a commit
to vvictor-dev/widgets
that referenced
this pull request
Aug 22, 2026
Widget UX polish: silent close + top-right pip + pre-accept gating
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three UX fixes surfaced during the live E2E walkthrough on the demo merchant-app. All target the gap between what the widget renders and what D-029's chain-driven resolver can actually deliver.
Silent close on
chatwoot: null. Clicking Support on a pre-acceptance order used to open the modal into a wall of explainer copy. The widget now just closes the modal — the click is a no-op. Phaseno-chatwootis removed from the phase union since it's no longer reachable.Pip moves to top-right of the order row.
PaymentHistorygains arenderRowBadge?: (order) => ReactNodeslot that renders absolutely-positioned in the top-right.PaymentHistoryWithSupportwires the pip through it. Previously it sat inline next to the Support button, crowding the action row.Pre-acceptance rows hide Support entirely. Until a merchant has accepted (
order.status === "placed"), the on-chain circleId is still 0 — Support can't open a chat (D-029).PaymentHistoryWithSupportnow returns null from bothrenderRowActionandrenderRowBadgefor placed rows. Row itself + Resume button stay visible.Test plan
🤖 Generated with Claude Code