Skip to content

feat: send form validation, pending states, claim-link success screen, and QR code - #519

Merged
phertyameen merged 4 commits into
bridgelet-org:mainfrom
A6dulmalik:feat/420-421-422-423-send-flow
Aug 29, 2026
Merged

feat: send form validation, pending states, claim-link success screen, and QR code#519
phertyameen merged 4 commits into
bridgelet-org:mainfrom
A6dulmalik:feat/420-421-422-423-send-flow

Conversation

@A6dulmalik

@A6dulmalik A6dulmalik commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements four related send-flow improvements from the Stellar Wave Program batch (issues #420#423), each as its own commit:

  • Send form input validation for amount and destination #420 — Amount validation: the details step now rejects amounts below 1 stroop (0.0000001, Stellar's smallest unit) and amounts with more than 7 decimal places, both with clear inline messages.
  • Send flow loading and pending transaction states #421 — Pending/loading states: a distinct status banner (separate from the existing button-label text) now shows during preparing / awaiting-Freighter / submitting, transitions to a "pending confirmation" framing after ~4s, and shows a reassurance message if the wait passes ~12s. The submit button was already disabled during this window; that behavior is preserved.
  • Send flow success screen with shareable claim link #422 — Success screen claim link: the success screen now shows the full claim URL in a dedicated box, a one-click "Copy link" button, and the claim link's absolute expiration deadline (not just relative "7 days" phrasing).
  • QR code generation for claim links #423 — QR code: a new ClaimQrCode component renders a genuinely scannable QR code (via the qrcode package, 100% client-side, no network calls) for the claim link, with a download button and descriptive accessible alt text.

#420 — not fully closed, see below

The issue's third criterion ("invalid Stellar destination format rejected before submission") does not apply to this form. Per the product's own FRD (docs/bridgelet-frd-ui-ux.md) and the app's ephemeral-account/claim-link model, the sender never enters a recipient wallet address on /send — that's collected from the recipient during claim, where it's already validated (components/claim-status-card.tsx, components/wallet-address-input.tsx). Client-side "amount vs. sender balance" validation is also out of scope: the frontend has no wallet-balance-fetching capability today (no Horizon/account-balance call anywhere in lib/), and implementing one would be a separate, larger feature. I implemented the parts of #420 that do apply (minimum amount, decimal precision) and am flagging the rest rather than guessing at a bigger redesign.

Pre-existing issues found (not fixed here, out of scope)

While validating this branch I found main is currently broken independent of this PR:

  • frontend/lib/create-bridgelet-client.ts, components/auto-sweep-retry.tsx, components/education-tooltip.tsx, components/mobile-deep-link.tsx, and components/multi-step-progress.tsx contain literal escaped-backtick sequences (e.g. \`${x}\` instead of an actual template literal) that fail to parse under both TypeScript and esbuild. This breaks npm run typecheck, npm run build, and any test that transitively imports create-bridgelet-client.ts (confirmed via git diff origin/main — these files are untouched by this branch).
  • frontend's package-lock.json is out of sync with package.json (a peer-dependency conflict between @storybook/nextjs-vite@^10.4.6 and @chromatic-com/storybook@^3.2.0, which peer-depends on Storybook 8), so plain npm ci/npm install fails with ERESOLVE. This matches Frontend CI's current failing status on main (confirmed via gh run list).

Given the "no drive-by refactors" scope for this PR, I did not fix these — flagging for maintainer awareness since they block CI/build entirely regardless of this PR.

Dependency change

Added qrcode (runtime) + @types/qrcode (dev) to frontend/package.json for #423. frontend/package-lock.json was intentionally left unmodified — because of the pre-existing peer-dependency conflict above, regenerating it locally (even with --legacy-peer-deps) rewrites ~2,800 unrelated lines as npm re-resolves the whole tree differently, which would be a huge, unreviewable, out-of-scope diff. Once a maintainer resolves the underlying Storybook/Chromatic version conflict, regenerating the lockfile with qrcode included will be a small, clean diff.

Checks run

  • npx eslint <changed files>: 0 errors, 2 pre-existing-pattern warnings (no-img-element on the new <img>, and a pre-existing exhaustive-deps warning on a hook I didn't modify).
  • npm run lint (whole repo): 5 errors, all in the pre-existing broken files listed above — none in files this PR touches.
  • npx tsc --noEmit (whole repo): same pre-existing files fail to parse; no errors involving any file this PR touches.
  • npx vitest run components/send-form/: 61 passed, 2 failed. Both failures are confirm-step.test.tsx (mine and a stale duplicate under test-result/) failing to even transform, because they transitively import the corrupted create-bridgelet-client.ts above — not a logic failure in this PR's code. details-step.test.tsx (10/10) and claim-qr-code.test.tsx (3/3), which don't hit that import path, pass cleanly, using the same mocking patterns.
  • npm run build: fails for the pre-existing reasons above (confirmed reproducible on a clean origin/main checkout of the same file).
  • npm ci / npm install: fails for the pre-existing lockfile conflict above; used npm install --legacy-peer-deps locally to get a working node_modules for the checks above.

Test plan

  • Unit tests added for amount validation (details-step.test.tsx)
  • Unit tests added for pending states and button disabling (confirm-step.test.tsx)
  • Unit tests added for claim link display, copy button, and expiry deadline (confirm-step.test.tsx)
  • Unit tests added for the QR code component (claim-qr-code.test.tsx)
  • Manual scan test of the generated QR code — not possible in this environment; the qrcode package is a widely-used, spec-compliant encoder, but a maintainer should verify a real scan on a device before merge

Closes #421
Closes #422
Closes #423
Closes #420

Strengthen amount validation on the single-recipient send form's
details step:
- Reject amounts below 0.0000001 (1 stroop), Stellar's smallest
  indivisible unit, with a clear inline message.
- Reject amounts with more than 7 decimal places, since Stellar
  cannot represent finer precision and the extra digits would
  otherwise be silently dropped.

The issue's third criterion ("invalid Stellar destination format
rejected before submission") does not apply to this form: per the
product's own FRD (docs/bridgelet-frd-ui-ux.md) and the app's
ephemeral-account/claim-link model, the sender never enters a
recipient wallet address — that's collected from the recipient
during claim, where it is already validated (components/claim-status-card.tsx).
Client-side balance-vs-amount validation is also out of scope: the
frontend has no wallet-balance-fetching capability today.

Adds unit tests for the new validation rules.
Give the sender clear feedback during the gap between clicking
"Confirm & Send" and reaching the success screen:
- A distinct blue status banner (separate from the plain button-label
  change that existed before) shows while preparing, awaiting
  Freighter approval, submitting, and — after a few seconds in the
  submitting phase — pending confirmation on the Stellar network.
- The submit button was already disabled during this window
  (existing behavior); it now stays disabled through the same
  distinct states shown in the banner.
- If the wait stretches past ~12 seconds, a reassurance message
  appears so the sender doesn't think the app has frozen or needs a
  double-submit.

The network layer already enforces a 15s request timeout
(lib/create-bridgelet-client.ts) that surfaces as a retryable error;
this change addresses the UX side of "unusually long" waits within
that budget.

Adds unit tests covering the pending banner and button disabling.
…rg#422

On the send flow's success screen:
- Show the full claim URL prominently in a dedicated, monospace box
  (previously it was only embedded in the WhatsApp share link and the
  NFC-write payload, never displayed as text).
- Add a one-click "Copy link" button with a "Copied!" confirmation,
  matching the existing copy-to-clipboard pattern used elsewhere in
  the app (components/share-prompt.tsx).
- Show the claim link's absolute expiration deadline (e.g. "August
  27, 2026, 5:23 PM") alongside the existing relative "7 days" phrasing,
  computed from the moment the account was created.

Extends the confirm-step test suite to cover the new claim-link box,
copy behavior, and expiry deadline text.


Adds a genuinely scannable QR code to the send flow's success screen
for in-person or SMS-limited disbursement, encoding the exact claim
URL:
- New ClaimQrCode component (components/send-form/claim-qr-code.tsx)
  using the `qrcode` package to generate a spec-compliant PNG
  entirely client-side — no network requests, so the claim
  URL/token never leaves the browser.
- Downloadable as a PNG image via a "Download QR code" link.
- Descriptive accessible alt text ("QR code that opens your
  Bridgelet claim link when scanned with a phone camera") rather
  than exposing the raw URL as alt text.

Note: an existing decorative QR component (components/qr-code.tsx,
used only on the claim page's general app-referral share prompt)
draws a QR-shaped pattern from a hash of the input and is not a
real, scannable QR encoding. This issue's "verified round-trip via
scan test" criterion is why a real encoder (`qrcode`) was used here
instead of reusing that component; the existing one is left as-is
since fixing it is a separate concern from this issue.

package.json/package-lock.json: added `qrcode` (runtime) and
`@types/qrcode` (dev). package-lock.json was intentionally NOT
committed — see PR description for why.

Adds unit tests for the new component and extends the confirm-step
suite to assert the QR code receives the exact claim URL on success.
@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@A6dulmalik Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

@A6dulmalik is attempting to deploy a commit to the aminubabafatima8-gmailcom's projects Team on Vercel.

A member of the Team first needs to authorize it.

@phertyameen
phertyameen merged commit 788b2a3 into bridgelet-org:main Aug 29, 2026
0 of 10 checks passed
@phertyameen

Copy link
Copy Markdown
Contributor

Merged to \main\ as part of a consolidated integration (branch \merge-final, now pushed as commit \5155235\ on main).

Validation on main:

  • Frontend CI: green
  • Lighthouse CI: green
  • Mobile CI: Lint & Type Check, Unit Tests, and Android/iOS prebuild all green

Note: the Mobile CI Android/iOS native debug build jobs still fail on pre-existing repo issues unrelated to this PR (react-native-reanimated vs RN 0.81 compile error, and the macos-14 runner's Xcode 15.4 vs RN's required Xcode >= 16.1). Tracked as follow-up work, not blocked by this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants