Skip to content

fix(frontend): thirteen small UI, validation and a11y fixes - #1132

Merged
cristim merged 21 commits into
mainfrom
fix/frontend-xs-batch-b
Oct 19, 2026
Merged

cristim merged 21 commits into
mainfrom
fix/frontend-xs-batch-b

Conversation

@cristim

@cristim cristim commented Oct 19, 2026

Copy link
Copy Markdown
Member

Summary

Thirteen small frontend fixes, one commit per issue plus review follow-ups.

Closes #50
Closes #133
Closes #179
Closes #184
Closes #715
Closes #187
Closes #216
Closes #273
Closes #275
Closes #277
Closes #280
Closes #752
Closes #1131

Issue Fix
#50 Add Purchases refuses a start date before tomorrow (UTC), matching the server rule; the picker min uses the same rule
#133 A stored max amount of 0 is shown instead of blank; save refuses 0, negative or unparsable caps (blank stays "no limit", owner decision 2026-10-16)
#179 Active commitments show the account id once when the name already contains it
#184 Glyph-only planned purchase row actions get aria-labels
#715 The profile entry point is a real button; opening it from the mobile drawer keeps focus in the modal
#187 The sidebar highlights the item for the current URL before user and permission fetches finish
#216 Opening the mobile drawer focuses the first visible nav link
#273 apiRequest returns null only for 204 or an empty body; other 2xx parse failures reject
#275 Bulk user delete uses allSettled, reloads and deselects in every case, and names the users that survived
#277 AWS no longer offers monthly as a payment option
#280 An empty dashboard summary raises a named error instead of a TypeError
#752 The per-service chart tooltip renders for committed services without recommendations
#1131 GCP offers only monthly, matching config.ValidPaymentOptionsByProvider

Jest now runs with TZ=America/New_York through a globalSetup, so a local-date implementation of the UTC start-date rule fails even on a UTC CI runner.

Verification evidence (independent reviews, local build and tests at the head SHA) is posted as PR comments.

🤖 Generated with Claude Code

cristim and others added 18 commits October 19, 2026 18:47
The server refuses non-future dates; mirror the check inline so the
error shows before the round trip.

Closes #50

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stored max_amount of 0 rendered blank (|| vs ??), and the form saved
0, negative or unparsable amounts without the cap. Show the stored
value and refuse anything not greater than 0 on save; blank stays no
limit (owner decision 2026-10-16).

Closes #133

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skip the (account_id) suffix when the account name already contains the id.

Closes #179

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The glyph-only run, pause, resume, edit and cancel buttons only had a title; add matching aria-labels. The RI Exchange settings button already has visible text.

Closes #184

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The signed-in email was a span with a click listener, unreachable by keyboard or screen reader. Use a button reset to the same look; the placeholder aria-label is dropped once the email is shown so the name matches the visible text.

Closes #715

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
init() now marks the sidebar item for the current path active before awaiting the user and permission fetches, instead of leaving the static Home highlight until switchTab ran. Extract setActiveNav from switchTab for this.

Closes #187

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
openDrawer picked the desktop-only sidebar toggle, which is display:none below the breakpoint, so focus stayed on the hamburger. Skip the toggle so the first nav link takes focus.

Closes #216

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
apiRequest swallowed every JSON parse failure as null, so a truncated or proxy-mangled 200 looked like no data. Return null only for 204 or content-length 0 and let other parse errors reject. The backend emits no 204s and returns JSON bodies, so no caller depended on the old fallback.

Closes #273

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lete

Use allSettled so successful deletions are deselected and the list reloads in every case; the error toast names the users that survived.

Closes #275

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
paymentOptionsFor listed monthly for AWS because the AWS branch fell through to true. Add an allow-list matching config.ValidPaymentOptionsByProvider (all-upfront, partial-upfront, no-upfront).

Closes #277

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A null summary is a fulfilled result, so the non-null assertions led to a TypeError banner. Throw a named error instead and drop the assertions. Chose the explicit error over placeholder tiles since the tiles all derive from the summary.

Closes #280

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dations

The per-service chart tooltip returned an empty string when a service had no recommendation stats, before reaching the Current / Committed branch. Check that dataset first.

Closes #752

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server parses start_date as midnight UTC and requires it after now,
so tomorrow (UTC) is the first accepted day. Compare against the UTC day
instead of the local day and say so in the message.

Refs #50

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The drawer's account-action delegate closed the drawer after the profile button had opened the modal, pulling focus to the hamburger and inerting the modal's return-focus trigger. Close the drawer in the capture phase for the profile button only, so the modal records the hamburger as its trigger and keeps focus. Also hide the profile button until a user is set, since before login it was focusable and did nothing.

Refs #715

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reusing the stored-value refusal told admins to repair the API or database for something they typed. Validate Max Amount separately and say how to fix the field.

Refs #133

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Set the picker min to tomorrow UTC like the submit check, and run jest in America/New_York via globalSetup so a local-date implementation fails the UTC tests even on a UTC CI runner. Drops the obsolete local-day picker tests.

Refs #50

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oard

apiRequest no longer returns null for unparseable 2xx bodies; say what it does now.

Refs #273

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The backend accepts only monthly for GCP and rewrites other tokens to it, but the UI also offered all-upfront and upfront. The fan-out bucket dropdown and the purchase modal both read paymentOptionsFor and isPaymentSupported, so they follow.

Closes #1131

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 28 billable files and costs up to $7.00.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 48 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 88 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: LeanerCloud/cloud-commitments-platform/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 635d36c0-1abf-45e9-b38c-3d42c1697db3
📥 Commits

Reviewing files that changed from the base of the PR and between d9b2aa7 and c565be0.

📒 Files selected for processing: 28
  • frontend/package.json
  • frontend/src/__tests__/api.test.ts
  • frontend/src/__tests__/app.test.ts
  • frontend/src/__tests__/auth.test.ts
  • frontend/src/__tests__/dashboard.test.ts
  • frontend/src/__tests__/globalSetup.ts
  • frontend/src/__tests__/groups.test.ts
  • frontend/src/__tests__/html.test.ts
  • frontend/src/__tests__/inventory.test.ts
  • frontend/src/__tests__/mobile-nav.test.ts
  • frontend/src/__tests__/plans-permissions.test.ts
  • frontend/src/__tests__/plans-range-validation.test.ts
  • frontend/src/__tests__/purchase-compatibility.test.ts
  • frontend/src/__tests__/recommendations.test.ts
  • frontend/src/__tests__/users.test.ts
  • frontend/src/api/client.ts
  • frontend/src/app.ts
  • frontend/src/auth.ts
  • frontend/src/dashboard.ts
  • frontend/src/groups/groupModals.ts
  • frontend/src/index.html
  • frontend/src/inventory.ts
  • frontend/src/lib/purchase-compatibility.ts
  • frontend/src/navigation.ts
  • frontend/src/plans.ts
  • frontend/src/recommendations.ts
  • frontend/src/styles/layout.css
  • frontend/src/users/userActions.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cristim cristim added effort/s Hours effort/xs Trivial / one-liner impact/all-users Affects every user impact/few Limited audience impact/many Affects most users priority/p2 Backlog-worthy severity/high Significant harm type/bug Defect urgency/this-quarter Within the quarter urgency/this-sprint Within the current sprint labels Oct 19, 2026
Refs #216

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cristim and others added 2 commits October 19, 2026 23:52
A number input reports unparsable text as value "" with validity.badInput,
so it read as blank and saved with no cap. Treat badInput as the existing
field error and test with a real type=number control.

Refs #133

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bulk gate checked every bucket against the all-upfront toolbar default,
which GCP no longer supports, so a lone monthly GCP rec was sent to the
fan-out modal and lost Execute Now. Also accept the rec own supported
payment, which the single-bucket modal already seeds from.

Refs #1131

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristim

cristim commented Oct 19, 2026

Copy link
Copy Markdown
Member Author

Merge gate at c565be0f2f8f0a135692e282cabb64dc87f7fa68

Independent review 1 (cold brief, non-author): APPROVE c565be0f2f8f0a135692e282cabb64dc87f7fa68 (pass 1 APPROVE at d7a3748e, pass 2 on the delta)

Pass 1 checked the UTC start-date rule against handler_plans.go:575-581 (frontend minimum equals the first server-accepted date, including 23:xx and 00:00:00Z), the jest globalSetup TZ taking effect in workers (probe tests saw America/New_York, offset 300, also with --runInBand), apiRequest callers and the backend buildResponse nil-body {} behaviour (#273), and the payment allow-lists against ValidPaymentOptionsByProvider (#277/#1131). Mutations killed: local-date earliestStartDate, apiRequest catch-to-null, GCP all-upfront allowed, AWS token guard removed.

Pass 2: c565be0f widens isBucketPaymentCompatible (only caller handleBulkPurchaseClick); buckets are keyed by provider|service|term|payment|account, so mixed selections still fan out, and resolvePerRecPaymentSeed seeds only a supported own payment, so the single modal cannot receive an unsupported payment. 9290310c: validity.badInput is the right signal. Mutations killed: own-payment clause removed; badInput check replaced with false. Nit: no test for "own payment unsupported and toolbar unsupported" (a loosened gate survived); correct by reading.

Independent review 2 (cold brief, non-author): APPROVE c565be0f2f8f0a135692e282cabb64dc87f7fa68 (pass 1 REQUEST_CHANGES at d7a3748e)

Pass 1 blocking finding: a type="number" Max Amount input sanitizes unparsable text to "", which read as blank ("no limit"), so a typo silently removed the cap; the tests used type="text" and hid it. Fixed in 9290310c (validity.badInput refused; tests use real number inputs); mutation (badInput check -> false) fails a groups test. Pass 1 nit on the #216 comment fixed in fd4e3555.

Pass 1 also verified #50 against handler_plans.go:575-581, #273 void-DELETE bodies, #275 allSettled/reload/error text, #715/#184 accessible names, and killed mutations on #133 zero, #275 deselect and #273 parse rejection.

Pass 2 attacked c565be0f: mixed selections always produce more than one bucket (key provider|service|term|payment|account); AWS/Azure own-payment arm still calls isPaymentSupported (AWS monthly/upfront and RDS 3-year no-upfront still rejected); null own payment falls back to the toolbar rule as before; every modal seed branch checks isPaymentSupported. Targeted jest (5 suites, 556 tests) passes.

Local verification (macOS, exact head, worktree clean):

  • npm ci, npm run build, npm run lint (0 errors) and npx jest (108 suites, 3416 passed, 1 skipped) all pass.
  • The implementer reproduced the GCP execute-now Playwright failure and ran tests-e2e/execute-now-warning.spec.ts after the fix: 4/4 pass.

CI: all checks are green at this SHA, including Frontend E2E.

Threads: none open. CodeRabbit is waived as a merge gate.

@cristim
cristim merged commit 2d822f3 into main Oct 19, 2026
27 checks passed
@cristim
cristim deleted the fix/frontend-xs-batch-b branch October 19, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment