Fixed offer end date for repeating offers in Portal #26713
Conversation
WalkthroughBumps apps/portal package from 2.65.3 to 2.65.4. paid-account-actions now imports and uses 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ec9e2ed93
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js
Show resolved
Hide resolved
3ec9e2e to
2517c19
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/portal/src/utils/helpers.js`:
- Around line 944-946: The addMonths function currently returns the raw date
input when numberOfMonths is invalid, which can leak non-Date types; update
addMonths (check the numberOfMonths parameter) to return null for invalid input
(keeping the return type Date|null), and ensure the function always returns a
Date instance for valid inputs by cloning/parsing the incoming date before
modifying it (refer to addMonths, numberOfMonths, and the date variable to
locate the logic to change).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 78dfaf15-9e11-4646-8ec5-389f72c36add
📥 Commits
Reviewing files that changed from the base of the PR and between c144a8f and 3ec9e2ed93c6419c841824d5a8fd6f4e8673fc95.
📒 Files selected for processing (5)
apps/portal/package.jsonapps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.jsapps/portal/src/utils/fixtures-generator.jsapps/portal/src/utils/helpers.jsapps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js (1)
240-339: Add a yearly repeating-offer case to lock date math behavior.The updated assertions only cover monthly cadence. Add one
interval: 'year'repeating scenario so interval-related end-date regressions are caught early.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js` around lines 240 - 339, Add a new unit test (similar to "displays discounted price with \"Ends {date}\" for repeating offers") that uses getSubscriptionData / getNextPaymentData / getDiscountData with interval: 'year' and a repeating offer to verify the displayed "Ends {date}" uses yearly math; create a member with currentPeriodEnd and discount end dates spaced by the expected repeating duration in years, call setup({site, member}) and assert the original price, offer label, discounted price, and that the rendered "Ends {date}" matches the year-based end date (using queryByText) to prevent interval-related regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js`:
- Around line 237-242: The repeating-offer end-date math currently uses
addMonths(currentPeriodEnd, discount.duration_in_months - 1) which assumes a
1-month billing cadence; update the logic to multiply the number of billing
cycles by the plan's cadence in months (use plan.interval and
plan.interval_count or subscription.interval_count/plan.interval to compute
billingCycleMonths, e.g., billingCycleMonths = (plan.interval === 'year' ? 12 :
1) * (plan.interval_count || 1)), then compute monthsToAdd = billingCycleMonths
* (discount.duration_in_months - 1) and pass that to addMonths to produce
offerEndDate so durationLabel (and getDateString) is cadence-aware.
In
`@apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js`:
- Around line 288-290: The failing assertion checks the wrong end date for the
repeating-offer; update the expectations in paid-account-actions.test.js so the
repeating discount text matches the component's current-period–anchored date
computation: swap the assertions to expect queryByText('$4.00/month — Ends 5 May
2099') toBeInTheDocument() and expect queryByText('$4.00/month — Ends 3 May
2099') not.toBeInTheDocument(); locate the checks using queryByText in the
paid-account-actions test for the repeating offer and adjust them accordingly.
---
Nitpick comments:
In
`@apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js`:
- Around line 240-339: Add a new unit test (similar to "displays discounted
price with \"Ends {date}\" for repeating offers") that uses getSubscriptionData
/ getNextPaymentData / getDiscountData with interval: 'year' and a repeating
offer to verify the displayed "Ends {date}" uses yearly math; create a member
with currentPeriodEnd and discount end dates spaced by the expected repeating
duration in years, call setup({site, member}) and assert the original price,
offer label, discounted price, and that the rendered "Ends {date}" matches the
year-based end date (using queryByText) to prevent interval-related regressions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2241afaa-ad63-4de1-86b1-abb9e8cb70f3
📥 Commits
Reviewing files that changed from the base of the PR and between 3ec9e2ed93c6419c841824d5a8fd6f4e8673fc95 and 2517c19f5090d610d0d7401379b0ea147949294d.
📒 Files selected for processing (6)
apps/portal/package.jsonapps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.jsapps/portal/src/utils/fixtures-generator.jsapps/portal/src/utils/helpers.jsapps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.jsapps/portal/test/utils/helpers.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/portal/package.json
- apps/portal/src/utils/helpers.js
apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js
Outdated
Show resolved
Hide resolved
apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js
Show resolved
Hide resolved
2517c19 to
a0c5b11
Compare
apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js`:
- Around line 237-242: The repeating-offer end-date calculation calls
addMonths(currentPeriodEnd, ...) even when currentPeriodEnd can be null,
producing an incorrect epoch date; update the branch handling discount.duration
=== 'repeating' to first check currentPeriodEnd is truthy before calling
addMonths (use the existing currentPeriodEnd guard pattern), only compute
offerEndDate and set durationLabel (via getDateString) when currentPeriodEnd is
present and addMonths returns a valid date, and leave durationLabel unchanged
otherwise.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 371a9629-bad1-4472-b6a3-96ced7dda6db
📥 Commits
Reviewing files that changed from the base of the PR and between 2517c19f5090d610d0d7401379b0ea147949294d and a0c5b112aecc5f832cf8b57abd56f39fe678ef40.
📒 Files selected for processing (6)
apps/portal/package.jsonapps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.jsapps/portal/src/utils/fixtures-generator.jsapps/portal/src/utils/helpers.jsapps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.jsapps/portal/test/utils/helpers.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/portal/package.json
- apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js
apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js
Outdated
Show resolved
Hide resolved
closes https://linear.app/ghost/issue/BER-3413/account-page-date-mismatch-between-discount-end-billing-period - When a member redeems a 1-month free offer, Stripe sets the discount start date to now and end date to now + 1 month, i.e. anchors them to the redemption date, not the billing date - In Portal, to avoid confusion, we want to anchor the "- Ends {date}" to a billing date to avoid confusion - For example, today is 5 Mar 2026 and my subscription renews on 7 Mar 2026. I hit cancel, redeem a free-month offer that applies to my next payment. I expect to see "$0.00/month - Ends 7 Mar 2026", not "$0.00/month - Ends 5 Apr 2026"
a0c5b11 to
eaf86db
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js (1)
239-239: Consider: Defensive handling for undefinedduration_in_months.If
discount.duration_in_monthsis unexpectedlyundefined, the expressiondiscount.duration_in_months - 1evaluates toNaN, causingaddMonthsto return the originalcurrentPeriodEnd. This silently falls back to showing the current period end date as the offer end date.While this is a safe fallback, you may want to add an explicit guard or log a warning for debugging purposes if this scenario indicates a data issue upstream.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js` at line 239, The computation of offerEndDate using addMonths(currentPeriodEnd, discount.duration_in_months - 1) can produce NaN when discount.duration_in_months is undefined; update the offerEndDate calculation to defensively handle undefined/invalid duration_in_months by validating/coercing it to a safe integer (e.g., defaulting to 1 or 0) and/or logging a warning when discount.duration_in_months is missing; locate the assignment to offerEndDate and the discount object usage in this module (references: offerEndDate, discount.duration_in_months, addMonths, currentPeriodEnd) and add the guard + optional processLogger/console.warn so the code falls back deterministically while surfacing the unexpected data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js`:
- Line 239: The computation of offerEndDate using addMonths(currentPeriodEnd,
discount.duration_in_months - 1) can produce NaN when
discount.duration_in_months is undefined; update the offerEndDate calculation to
defensively handle undefined/invalid duration_in_months by validating/coercing
it to a safe integer (e.g., defaulting to 1 or 0) and/or logging a warning when
discount.duration_in_months is missing; locate the assignment to offerEndDate
and the discount object usage in this module (references: offerEndDate,
discount.duration_in_months, addMonths, currentPeriodEnd) and add the guard +
optional processLogger/console.warn so the code falls back deterministically
while surfacing the unexpected data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e3bfa86d-05c1-4886-b377-3291e58f9237
📥 Commits
Reviewing files that changed from the base of the PR and between a0c5b112aecc5f832cf8b57abd56f39fe678ef40 and eaf86db.
📒 Files selected for processing (6)
apps/portal/package.jsonapps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.jsapps/portal/src/utils/fixtures-generator.jsapps/portal/src/utils/helpers.jsapps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.jsapps/portal/test/utils/helpers.test.js
✅ Files skipped from review due to trivial changes (1)
- apps/portal/package.json
closes https://linear.app/ghost/issue/BER-3413/account-page-date-mismatch-between-discount-end-billing-period
"- Ends {date}"to a billing date to avoid confusion"$0.00/month - Ends 3 Apr 2026", not"$0.00/month - Ends 5 Apr 2026"Before:

After:
