Skip to content

fix(orders): real rejection reasons on the order form; input a11y and layout fixes - #28

Merged
Badri-Rajendran merged 1 commit into
mainfrom
bugfix_orders_form_errors_and_layout
Sep 7, 2026
Merged

Badri-Rajendran merged 1 commit into
mainfrom
bugfix_orders_form_errors_and_layout

Conversation

@Badri-Rajendran

Copy link
Copy Markdown
Owner

Problem

Placing an order (security VTI, side Buy, quantity 2, reference price 20) failed with a generic
"some error occurred," and the order form's four fields were stacked in a narrow column not using
the available page width.

Diagnosis (verified live, not guessed)

The wire path is correct. Registered a fresh customer, approved KYC/account, funded and
confirmed $1,000 cash through the real DepositService, then posted the exact frontend payload
(security_id UUID, side: "buy", quantity: "2", reference_price: "20") directly to
POST /api/v1/orders — 201, order created. Payload construction, CreateOrderRequest's
schema, and the Units/Price decimal-string convention all match exactly.

Root cause: every order-specific rejection reason was thrown away.
frontend/src/utils/apiErrorMessage.js's code map has zero entries for any code
orders.py actually returns (insufficient_investable_cash, kyc_verification_pending,
kyc_verification_rejected, account_approval_pending, account_approval_rejected,
customer_not_eligible) — all of them fall through to the generic
"Something went wrong. Please try again." I reproduced this live against an
approved-but-unfunded account (a KYC-pending account can't even reach this page — RequireOnboarded
redirects to /onboarding first, confirmed): before the fix, the form showed the generic message;
after, it shows "You don't have enough investable cash for this order."

Fix

  • New frontend/src/features/orders/ordersErrorMessage.js, mirroring the existing
    fundingErrorMessage.js pattern, mapping all six real order-rejection codes to specific,
    actionable copy. Wired into OrderForm, OrderList, and ApprovalBanner (all three previously
    called the bare utility).
  • Input-field corrections found while reading every field carefully: validation/server errors now
    wired into Input's error prop (fixes a real accessibility gap — aria-invalid/
    aria-describedby never fired before); reference price is now trimmed before validating/sending
    (quantity already was, via parseQuantity); an explicit empty-state message replaces a silent
    undefined selection if the securities list is ever empty; required added to both selects for
    parity with the inputs.
  • Layout: OrderForm.css hard-coded max-width: 360px with no breakpoint and no Card wrapper —
    replaced with a responsive 2-column field grid (Security+Side, Quantity+Reference price) at
    768px+, matching this codebase's established FeesPage.css/FundingPage.css breakpoint
    convention, collapsing to one column below it, wrapped in a Card at the page's
    --reading-max-width (640px).

No backend changes — the wire contract was already correct.

Validation evidence

  • npm run lint, npm run build — both clean.
  • Live verification: restarted the frontend on this branch, reproduced the exact failing scenario
    against an approved-but-unfunded test account — confirmed the specific message now renders.
  • Screenshots taken at 1280px (2-column grid, Card-wrapped) and 375px (clean single-column stack,
    no overflow) confirming the layout fix at both breakpoints.

Security / DB / API impact

None. Frontend-only change; no new endpoint, no wire-shape change, no migration.

… layout

Diagnosed "some error occurred" on order placement: verified live (fresh
KYC/account-approved, funded test customer) that the exact wire path --
frontend payload construction, CreateOrderRequest schema, Units/Price
decimal-string convention -- works correctly end to end (a direct POST
with the exact VTI/Buy/2/$20 payload succeeded, 201). The real bug is
that frontend/src/utils/apiErrorMessage.js has zero mappings for any
order-specific rejection code, so every real, specific reason the backend
returns (insufficient_investable_cash, kyc_verification_pending,
kyc_verification_rejected, account_approval_pending,
account_approval_rejected, customer_not_eligible) collapsed into the
generic "Something went wrong" fallback. Reproduced live against an
approved-but-unfunded account: before this fix, "Something went wrong";
after, "You don't have enough investable cash for this order."

New ordersErrorMessage.js mirrors funding's existing
fundingErrorMessage.js pattern; wired into OrderForm, OrderList, and
ApprovalBanner (all three previously called the bare utility).

Also, per a careful read of every input field: wired validation/server
errors into Input's `error` prop (fixes a real accessibility gap --
aria-invalid/aria-describedby never fired before, message rendered
through a disconnected sibling paragraph); trimmed the reference-price
value before validating and sending (quantity already did this via
parseQuantity, reference price did not); added an explicit empty-state
message if the securities list is ever empty instead of letting the
selected security silently go undefined; added `required` to both
selects for parity with the inputs.

Layout: OrderForm.css hard-coded max-width: 360px with no breakpoint,
and the form wasn't wrapped in a Card -- confirmed visually (a live
1280px snapshot showed the four fields as a narrow login-form-sized
column with the rest of the page empty). Replaced with a responsive
2-column field grid (Security+Side, Quantity+Reference price) at 768px+
matching FeesPage.css/FundingPage.css's established breakpoint
convention, collapsing to one column below it; wrapped in a Card at the
page's --reading-max-width (640px) -- verified both breakpoints live.

No backend changes -- the wire contract was already correct.
Copilot AI lite review requested due to automatic review settings September 7, 2026 07:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Badri-Rajendran
Badri-Rajendran merged commit a7644fb into main Sep 7, 2026
5 of 6 checks passed
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