Skip to content

Widget UX polish: silent close + top-right pip + pre-accept gating - #2

Merged
Software-Artist-Aash merged 2 commits into
mainfrom
feat/widget-ux-polish
May 14, 2026
Merged

Software-Artist-Aash merged 2 commits into
mainfrom
feat/widget-ux-polish

Conversation

@gitchadd

Copy link
Copy Markdown
Collaborator

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.

  1. 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. Phase no-chatwoot is removed from the phase union since it's no longer reachable.

  2. Pip moves to top-right of the order row. PaymentHistory gains a renderRowBadge?: (order) => ReactNode slot that renders absolutely-positioned in the top-right. PaymentHistoryWithSupport wires the pip through it. Previously it sat inline next to the Support button, crowding the action row.

  3. 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). PaymentHistoryWithSupport now returns null from both renderRowAction and renderRowBadge for placed rows. Row itself + Resume button stay visible.

Test plan

  • 26 widget tests green (was 25). New case asserts pre-acceptance rows show no Support button + no pip, but Resume stays.
  • 43 core tests unchanged on node:test.
  • Build clean (tsup + dts).
  • Manual: open demo merchant-app, click Support on a placed order → expect no modal flicker.
  • Manual: synthetic seed conversation on an accepted order → expect pip in top-right corner.

🤖 Generated with Claude Code

gitchadd 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
Software-Artist-Aash merged commit 029e6f0 into main May 14, 2026
0 of 2 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants