Skip to content

fix(frontend): global page-width bug, Refresh status throttle, remove Transactions - #30

Merged
Badri-Rajendran merged 3 commits into
mainfrom
fix_page_width_refresh_throttle_remove_transactions
Sep 7, 2026
Merged

Badri-Rajendran merged 3 commits into
mainfrom
fix_page_width_refresh_throttle_remove_transactions

Conversation

@Badri-Rajendran

Copy link
Copy Markdown
Owner

Summary

Three unrelated frontend fixes bundled together (three separate commits, easy to review
independently):

  1. Root-cause fix for "Orders/Portfolio/Dashboard don't use the available width." Traced it to
    a real bug in the shared .tu-page rule (used by every page), not a per-page styling gap —
    already patched twice this session as one-off overrides on Lots and Funding. Fixed once at the
    source; deleted both now-redundant patches.
  2. Onboarding's "Refresh status" button had zero rate limiting. Added an explicit 10s cooldown.
  3. Removed the Transactions page entirely, per request — clean, fully-scoped removal with
    nothing else depending on it.

1. Global page-width bug

.tu-page sits in a column flex container; its own margin: 0 auto centering is a cross-axis
auto margin, which per the flexbox spec opts it out of stretch sizing — it shrinks to its content's
width unless that content happens to be wide enough to force it back open. Confirmed live at
1280px, all previously unpatched: Orders list rendered at 293px, Portfolio at 307px,
Dashboard at 510px. Fixed at the source in PageLayout.css; deleted the redundant
.tu-page.tu-lots-page/.tu-page.tu-funding-page overrides. Also fixed DashboardPage.css's
stats row (flex-wrap → repeat(auto-fit, minmax(280px, 1fr))) so the two stat cards actually
fill the now-full-width row, matching the grid idiom already used in Portfolio's components.

Live-verified: Dashboard's stat cards now span the row; Portfolio's existing
repeat(auto-fill, minmax(260px,1fr)) grid now correctly spreads all 4 model cards (no
Portfolio-specific change needed); Orders list header now uses the full row; confirmed no
horizontal overflow at 375px.

2. Onboarding refresh throttle

useIdentityStatus.js had no debounce/cooldown at all — each click can trigger a live outbound
Stripe Identity poll and shares a 30/min-per-IP bucket with RequireOnboarded's own background
calls. Added a 10s cooldown (tracked via a ref, no extra renders) with a ticking
cooldownRemainingSeconds so the button shows "Try again in Ns" and disables itself. Applies
uniformly to every refetch() caller (button, KycStep.onCompleted, ErrorState.onRetry); the
initial mount fetch is never throttled.

Live-verified with a polling test: click → immediately "Try again in 10s" (disabled) → counts down
10s→9s→8s → re-enables at 0.

3. Remove Transactions page

Deleted TransactionsPage.jsx and everything that existed only to serve it
(TransactionTable.jsx, useTransactionHistory.js, entryTypeLabels.js — each confirmed
single-importer). Removed the route, import, and nav link. valuationApi.js's unused getHistory
removed (getBalance/getReturns stay). GET /valuation/history becomes unused by the frontend
as a result — backend left alone, that's a separate decision.

Validation evidence

  • npm run lint, npm run build — clean on every commit.
  • Live Playwright verification for all three fixes (screenshots + a polling test for the
    throttle), described above.
  • grep -rn "TransactionsPage\|TransactionTable\|useTransactionHistory\|/transactions" across
    frontend/src — zero remaining references.

Security / DB / API impact

None. Frontend-only. No new endpoint, no wire-shape change. GET /valuation/history is unchanged
server-side, just no longer called by the frontend.

…ots/Funding

Investigating "Orders/Portfolio/Dashboard don't use the available
horizontal space" found the actual root cause, not a per-page styling
gap: .tu-page (used by every page) sits in .tu-app-layout__main, a
column flex container. Its own margin: 0 auto centering is a cross-axis
auto margin on a flex item -- per the flexbox spec that opts the item out
of stretch sizing entirely, so it shrinks to its own content's width
instead of the container's, unless that content happens to be wide
enough on its own to force it back open. That's exactly why Fees/Lots/
Funding/the new Orders form never showed it (wide table or filled
multi-column form) while Orders' list, Portfolio, and Dashboard did.
Confirmed live at 1280px, all previously unpatched: Orders list rendered
at 293px, Portfolio at 307px, Dashboard at 510px.

This is the identical bug already found and patched twice this session,
once per page, as each was redesigned and happened to hit it:
LotsPage.css's .tu-page.tu-lots-page and FundingPage.css's
.tu-page.tu-funding-page, both `{ width: 100%; min-width: 0; }`.

Fixed once at the source instead: moved width: 100%; min-width: 0 onto
the base .tu-page rule in PageLayout.css, and deleted both now-redundant
per-page overrides. Fixes Orders, Portfolio, and Dashboard's wasted-space
complaint at the root, plus any future page that hits the same trap --
verified live, Portfolio's existing repeat(auto-fill, minmax(260px,1fr))
grid now correctly spreads its 4 model cards across the full width with
no page-specific change needed there.

Also fixed DashboardPage.css: with .tu-page now actually full width, its
flex-wrap stats row would have just left BalanceCard/ReturnCard small and
left-aligned with the rest of the row empty. Changed to
repeat(auto-fit, minmax(280px, 1fr)), matching the same grid idiom
already used in ModelDetail.css/AssignmentPrompt.css -- verified live,
both cards now stretch to fill the row.
…t all

useIdentityStatus.js had zero client-side rate limiting, debounce, or
cooldown on refetch(). Each call hits GET /identity/status/<id>, which
for a still-pending customer triggers a live outbound Stripe Identity
poll, not a cheap read -- and shares a single 30/min-per-IP,
in-memory bucket (identity.py, key_func=get_remote_address) with
RequireOnboarded's own background calls on every protected-route
navigation. A customer clicking "Refresh status" repeatedly could burn
through that shared budget for the whole app. The only reason a
double-click didn't already double-fire was incidental: flipping status
away from 'loaded' unmounts the button along with the entire status
card, replaced by a skeleton -- not a real cooldown, and it means every
refresh flashes the whole card away rather than updating in place.

Added an explicit 10s cooldown tracked by a ref (no extra renders for
the tracking itself) plus a ticking cooldownRemainingSeconds state so the
button can show "Try again in Ns" and disable itself, verified live: a
click immediately shows "Try again in 10s" (disabled), correctly counts
down 10s -> 9s -> 8s, and re-enables once it reaches zero. Applies to
every caller of refetch() uniformly (the button, KycStep's onCompleted,
ErrorState's onRetry) -- a spammed retry deserves the same protection as
a spammed manual refresh, and centralizing it in the hook is simpler and
more robust than duplicating a cooldown at each call site. The initial
mount fetch is never throttled -- there's nothing to cool down from yet.
Deleted TransactionsPage.jsx and everything that existed only to serve
it: TransactionTable.jsx, useTransactionHistory.js, entryTypeLabels.js
(each confirmed to have exactly one importer chain rooted at the page).
Removed the /transactions route + import from App.jsx and the nav link
from AppLayout.jsx's money group; removed valuationApi.js's now-unused
getHistory (getBalance/getReturns stay -- Dashboard's).

GET /valuation/history becomes unused by the frontend as a result
(confirmed no other feature calls it) -- left the backend endpoint,
service, and tests alone; removing working, tested backend surface is a
separate decision this change doesn't make.
Copilot AI lite review requested due to automatic review settings September 7, 2026 07:33

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 ba28148 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