SIGN IN SIGN UP

AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.

0 0 83 Python

feat(frontend): split switch-to-yearly dialog copy into lines + surface charged-today amount (#13099)

### Why / What / How

**Why:** [SECRT-2348](https://linear.app/autogpt/issue/SECRT-2348) — the
Settings → Subscription cycle-switch confirmation popup is too wordy and
packs four different numbers into a single run-on sentence, so users
can't quickly tell what they're being asked to pay. The ticket also asks
us to show the exact prorated amount the user will be charged today when
switching from monthly to yearly on Pro/Max.

**What:** Rewrites the body of `SwitchCycleDialog` (monthly→yearly *and*
yearly→monthly) so each fact lives on its own line, and surfaces the
real charged-today number computed from the backend's
`proration_credit_cents`.

Before (one paragraph):
> Save 15% with yearly billing. Pro is $42.50/month when billed yearly,
charged as $510.00/year instead of $600.00/year monthly. You'll be
charged the prorated difference immediately. After this period, your
plan renews yearly at $510.00.

<img width="662" height="342" alt="Screenshot 2026-05-13 at 12 58 15 PM"
src="https://github.com/user-attachments/assets/b0924746-eded-4420-a828-d2642d9e72c8"
/>

<img width="724" height="364" alt="Screenshot 2026-05-13 at 1 00 23 PM"
src="https://github.com/user-attachments/assets/bd3ff846-6741-4f1b-ab3b-078586ddff3b"
/>

After (four scannable lines):
> Save 15% with yearly billing.
> Pro yearly: $510.00/year ($42.50/month).
> Charged today: $485.00 (prorated from your monthly plan).
> Renews $510.00/year on May 14, 2030.

**How:**
- `useYourPlanCard.ts` — `getDialogBody()` now returns `string[]`. For
monthly→yearly with both prices available, it builds four lines: savings
%, consolidated yearly + monthly-equivalent price, exact charged-today
amount, renewal line with the period-end date. For yearly→monthly it
returns three lines (date of switch, new monthly price, "No charge
today"). Falls back to a shorter two-line variant when prices are
missing (e.g. BUSINESS tier).
- Charged-today math reuses what the backend already returns: `max(0,
currentYearlyCents - proration_credit_cents)`. `proration_credit_cents`
is already computed server-side via `get_proration_credit_cents` and
matches what Stripe credits under `proration_behavior="always_invoice"`.
When the backend returns `0` (admin-granted plans / no Stripe customer),
we fall back to the generic "prorated difference today" line instead of
showing `$0`.
- Renewal-date line uses `current_period_end` (already on
`SubscriptionStatusResponse`) — no new backend fields.
- `SwitchCycleDialog.tsx` — `body` prop is now `string[]`; renders each
line as its own `<Text>` row with `gap-2` spacing.
- Tier-upgrade and tier-downgrade dialogs are intentionally left alone —
out of scope for this ticket.

### Changes 🏗️

- `useYourPlanCard.ts`: `getDialogBody()` returns `string[]`; reads
`proration_credit_cents` and `current_period_end` from the subscription
status response; computes savings %, monthly equivalent, net charge
today, and renewal date.
- `SwitchCycleDialog.tsx`: `body: string` → `body: string[]`; iterates
and renders each line as a stacked `<Text>` row.
- `billing-cards.test.tsx`: per-line anchored regex assertions for both
directions; added `proration_credit_cents: 2500` + `current_period_end`
to the Pro monthly→yearly fixture so the exact-amount path is exercised;
the Max test keeps the fallback path covered.

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Pro monthly user clicks Yearly toggle → dialog title reads "Switch
Pro to yearly billing?" and body shows four separate lines including
"Charged today: $X.XX (prorated from your monthly plan)."
- [x] Max monthly user clicks Yearly toggle → dialog title reads "Switch
Max to yearly billing?" with Max pricing on its own line.
- [x] Pro yearly user clicks Monthly toggle → dialog shows three lines
including "No charge today." on the last line.
- [x] BUSINESS user (no tier_costs) → dialog falls back to two-line
generic copy (no $0 leak).
- [x] Confirm flow still POSTs `billing_cycle` correctly in both
directions (existing tests cover this).
  - [x] `pnpm test:unit` passes for `billing-cards.test.tsx`.

#### For configuration changes:
- [x] `.env.default` is updated or already compatible with my changes
- [x] `docker-compose.yml` is updated or already compatible with my
changes
- [x] I have included a list of my configuration changes in the PR
description (under **Changes**)
A
Abhimanyu Yadav committed
16f94ed90f1dbe910e36163bc8e76034f758d0c7
Parent: 71e582d
Committed by GitHub <noreply@github.com> on 5/13/2026, 7:52:55 AM