Skip to content

fix(frontend): show the monthly charge and term total before purchase - #1364

Merged
IAmJSD merged 1 commit into
LeanerCloud:mainfrom
IAmJSD:ui/issue-711
Oct 24, 2026
Merged

IAmJSD merged 1 commit into
LeanerCloud:mainfrom
IAmJSD:ui/issue-711

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

#781 added the Upfront / Monthly / Total over term band to the single-purchase Execute now, Run now and RI exchange confirms, but the other decision screens still showed only the upfront. The bulk Review Purchase modal said "Total upfront: $0" for no-upfront buckets that bill monthly for 1 or 3 years, its confirm dialog showed no amounts at all, the Execute Now warning read "This will charge $0.00 upfront immediately" for a monthly-billed purchase, and the approval dialog had no monthly cost or term total. This PR shows the monthly charge and the term total on all of them, using the shared helper from #781.

Closes #711

What changed

  • frontend/src/commitmentSummary.ts: new shared helpers commitmentLine (priced rec to line, with optional bucket payment and account), formatCommitmentTotals (upfront / monthly / term total as text; an unknown monthly reads "Not reported (monthly fees apply)" instead of a partial sum), billsMonthly, and buildCommitmentBand (the stat band without the line list). buildCommitmentSummary now uses them.
  • frontend/src/recommendations.ts:
    • Bulk modal header adds "Monthly recurring" and "Total over term" lines, and each bucket's totals line adds "$X/mo recurring".
    • The single purchase modal gets a live Upfront / Monthly / Total over term band under the table (#purchase-modal-commitment-band) for the checked rows.
    • The Execute Now warning adds ", then $X a month for the term ($Y in total)" (or ", plus monthly fees the provider did not report").
  • frontend/src/app.ts: the single confirm uses commitmentLine; the bulk confirm now carries the same summary band and line list as the single confirm.
  • frontend/src/approval-details.ts: the header gains "Monthly cost" and "Total over term" stats right after "Upfront".
  • frontend/src/styles/components.css: the approval header grid is capped at four stats per row so the eight stats sit in two even rows.
  • Tests in commitmentSummary.test.ts, approval-details.test.ts, purchase-modal-submit.test.ts, purchase-execution-toast.test.ts and tests-e2e/execute-now-warning.spec.ts.

Why this approach

It builds on the helper #781 introduced instead of adding a second one, so every screen computes and words the three numbers the same way (term total = upfront + monthly x 12 x term, per line). This is the shared {upfront, monthlyRecurring, totalOverTerm} helper the issue suggests. The bulk modal keeps its existing "Label: value" lines and 0-decimal format, with two lines added. The confirm dialogs reuse the existing stat band. The approval header's upfront still comes from the backend's total_upfront_cost, but the monthly and term totals are summed from the per-rec rows because the API has no aggregate for them.

Screenshots

Before is main (a14dc8f2), after is this branch. Both are headless Chromium at 1440x900 against the repo's mocked e2e API, with two no-upfront AWS EC2 recs ($280/mo over 3 years and $150/mo over 1 year).

Before After
bulk-modal before bulk-modal after
bulk-confirm before bulk-confirm after
single-execute-now before single-execute-now after
approval before approval after
  • Bulk Review Purchase for two no-upfront buckets: only "Total upfront: $0". After: adds "Monthly recurring: $430" and "Total over term: $11,880", and each bucket line shows its $/mo recurring.
  • Bulk confirm dialog: no amounts at all. After: the Upfront on approval / Monthly / Total over term band plus the line list.
  • Execute Now on a no-upfront 3-year rec: "This will charge $0.00 upfront immediately." After: the warning names $280.00 a month and $10,080.00 in total, and a live band under the table shows the three totals.
  • Approval dialog: Upfront $0 and savings only. After: adds Monthly cost $430 and Total over term $11,880, in two even rows of four.

Verification

Run on this branch's head 121e5491 on top of main a14dc8f2:

  • Lint: 0 errors (142 pre-existing warnings). Typecheck and build pass.
  • Jest: 109 suites, 3498 passed, 1 skipped. Touched suites re-run with --randomize (138 and 31 passed). New tests: commitmentSummary (commitmentLine, formatCommitmentTotals, billsMonthly, buildCommitmentBand), approval-details (monthly and term stats, unreported monthly), and a new purchase-modal-submit block (modal band follows the checkboxes, Execute Now warning with a monthly and with an unreported monthly, bulk summary plus bucket line plus bulk confirm). Updated: approval-details stat indices (+2), and the purchase-execution-toast mock gained getAccountName.
  • Playwright via e2e-local.sh: 157 passed. execute-now-warning.spec.ts now expects the monthly wording and asserts the band and the fan-out "Monthly recurring" / "Total over term" lines.
  • git-secrets scan clean (planted key flagged); scripts/test-git-secrets-allowlist.sh: 137 pass, 0 fail.

Notes for review

  • Copy to check: "Monthly recurring" (bulk modal) vs "Monthly" (confirm band) vs "Monthly cost" (approval header), and the warning sentence ", then $280.00 a month for the term ($10,080.00 in total)".
  • The single modal's existing tfoot totals row still sums monthly_cost over only the rows that report one, so it can show a partial sum. It is left as is because the new band below it says "Not reported".
  • The bulk confirm lists at most 5 lines plus "and N more", the same as the single confirm.
  • No backend change was needed.

Overlaps

Those PRs will be opened alongside this one and merged in sequence, with conflicts resolved on the later branch.

Note for the UI owner

This changes UI code that @cristim owns. IAmJSD reviewed the before/after screenshots and asked for it to be merged; any design point you disagree with can be a follow-up.

🤖 Generated with Claude Code

…se screen

The bulk Review Purchase modal only stated "Total upfront", which reads
$0 for no-upfront and partial-upfront buckets that bill monthly for one
or three years. Its confirm dialog stated no amounts at all, the Execute
Now warning said "charge $0.00 upfront" for a monthly-billed purchase,
and the approval dialog showed the upfront and the savings but not the
monthly cost, so an approver could not judge the commitment.

commitmentSummary.ts already computed upfront, monthly and total over
term for the Execute now / Run now confirms. Add commitmentLine (rec to
line), formatCommitmentTotals (the three totals as text, never a partial
monthly sum) and buildCommitmentBand, and use them in:

- the bulk modal header (Monthly recurring, Total over term) and each
  bucket's totals line (monthly recurring),
- the bulk confirm dialog (the same stat band and line list as the
  single-purchase confirm),
- the single purchase modal (a band under the table for the checked
  rows) and its Execute Now warning,
- the approval dialog header (Monthly cost, Total over term), laid out
  four stats per row so the eight stats do not leave an orphan.

Refs LeanerCloud#711

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD IAmJSD added priority/p2 Backlog-worthy triaged Item has been triaged severity/high Significant harm urgency/this-quarter Within the quarter impact/many Affects most users effort/m Days type/bug Defect labels Oct 24, 2026
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 10 billable files and costs up to $2.50.

Or wait 59 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 61be5b6 and 121e549.

Files selected for processing: 10
  • frontend/src/__tests__/approval-details.test.ts
  • frontend/src/__tests__/commitmentSummary.test.ts
  • frontend/src/__tests__/purchase-execution-toast.test.ts
  • frontend/src/__tests__/purchase-modal-submit.test.ts
  • frontend/src/app.ts
  • frontend/src/approval-details.ts
  • frontend/src/commitmentSummary.ts
  • frontend/src/recommendations.ts
  • frontend/src/styles/components.css
  • frontend/tests-e2e/execute-now-warning.spec.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): APPROVE 121e54918fe3652a61fee44651060190a55d0fac

Blocking:

  • none
    Non-blocking:
  • frontend/src/approval-details.ts:113 and frontend/src/recommendations.ts:4546: these totals use 0 decimal places, while the confirm band uses 2. A $0.40/mo line shows "Monthly cost $0" in the approval header and "$0/mo recurring" in the bulk modal, the same kind of "$0" the issue complains about. Consider 2 digits for the monthly figure.
  • frontend/src/approval-details.ts:113: "Total over term" is summed from the per-rec upfront_cost, while "Upfront" beside it comes from the backend's total_upfront_cost. If those ever differ (scaled or adjusted executions), the two stats will not reconcile.
  • frontend/src/recommendations.ts:4716: skipped (unsubmittable) buckets keep their un-repriced recs, so their "$X/mo recurring" reflects the rec's own payment, not the bucket's.
    Attacked: Checked that bulk buckets really are repriced to b.payment variants via pricedCellVariant before commitmentLine(r, {payment: b.payment}), so the monthly and upfront figures match the stated payment. Checked that monthly_cost is the recurring charge (types.ts and Go comments), so term total = upfront + monthly x 12 x term is correct. Checked that getFanOutBuckets() only returns submittable buckets, so the confirm and the summary agree. Checked that the modal band refreshes on checkbox toggles and on term/payment reprice through updatePurchaseModalTotals. Everything is rendered with textContent or stat(), so there is no XSS. Closes fix(frontend): monthly cost and term total never shown, bulk shows upfront 0 #711 is justified.

@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification at 121e5491 merged onto current main (d4478cd1; merge-tree 963de13b; Linux, Node, headless Chromium):

  • This branch's own checks passed at this head: lint, typecheck, build, the full jest suite, the full Playwright e2e suite, and the git-secrets checks (see Verification in the PR body).
  • The combined tree of every UI PR in this merge wave (tree 89dd34d8, main plus 46 PRs) passes npm ci, lint, typecheck, build, jest (1 skipped, 3914 passed, 3915 total), and the full Playwright suite (208 passed).
  • CI is green on 121e5491 (CI Success, Run pre-commit hooks).
  • The independent review above approves this exact head.
  • Nothing was applied to any cloud.

@IAmJSD
IAmJSD marked this pull request as ready for review October 24, 2026 15:51
@IAmJSD
IAmJSD merged commit bad3df6 into LeanerCloud:main Oct 24, 2026
29 checks passed
@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Marked ready and merged by IAmJSD's agent, at IAmJSD's request (IAmJSD reviewed the before/after screenshots; cristim is traveling), after the independent review and local verification above, all at 121e5491.

IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/app.ts: main's LeanerCloud#1364 (Refs LeanerCloud#711) added commitmentLine()
  to commitmentSummary.ts and already gives the bulk confirm the same
  Upfront on approval / Monthly / Total over term summary this branch
  added. Took main's commitmentLine for both the single and the fan-out
  confirm and dropped this branch's now-duplicate toCommitmentLine helper
  (and its LocalRecommendation/CommitmentLine imports). Kept this
  branch's remaining LeanerCloud#707 behavior on top: the "Totals below are across
  all requests." intro sentence and the amount on the confirm button via
  executeConfirmLabel(false, totals.upfront) instead of main's plain
  "Send all for approval".
- frontend/src/__tests__/purchase-execution-toast.test.ts: comment on
  the getAccountName mock; merged both wordings (LeanerCloud#707/LeanerCloud#711).
- frontend/src/__tests__/purchase-modal-submit.test.ts: main split this
  suite over a shared harness (LeanerCloud#1349). Took main's file and moved this
  branch's "bulk approval confirm states the totals across all buckets
  (LeanerCloud#707)" test into purchase-modal-fanout-payment.test.ts, next to the
  fan-out payment-change test it sat beside before the split, adding the
  confirmDialog and formatCurrency imports it needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts (all in frontend/src/history.ts):
- Purchase History row: kept main's escaped count and upfrontCell()
  (LeanerCloud#1364 amortized upfront) and this branch's right-aligned `num`
  classes on count, monthly and savings.
- Approval queue placeholders: kept this branch's NO_VALUE for account,
  term and payment; dropped the branch's local `const amortize` because
  main hoisted the same declaration above the row map.
- Approval queue row: same combination as the history row.
- upfrontCell() (main, merged cleanly) now emits class `num` on both its
  plain and "(in monthly)" variants, so the upfront column stays
  right-aligned like the other money columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IAmJSD added a commit to IAmJSD/cloud-commitments-platform that referenced this pull request Oct 24, 2026
Conflicts:
- frontend/src/app.ts: both sides added an import after the closeModal
  import (this branch's ./fanout-retry, main's ./init-error from LeanerCloud#1338).
  Kept both.

The submitFanOutBuckets/handleFanOutExecute retry changes merged cleanly
with main's LeanerCloud#1352 account-labelled bucket names and LeanerCloud#1364 commitment
lines; the toast those feed is still used when retry mode does not apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/many Affects most users priority/p2 Backlog-worthy severity/high Significant harm triaged Item has been triaged type/bug Defect urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(frontend): monthly cost and term total never shown, bulk shows upfront 0

1 participant