Skip to content

Improved canceled state on Portal account page#26679

Merged
minimaluminium merged 14 commits intomainfrom
portal-canceled-state-BER-3348
Mar 5, 2026
Merged

Improved canceled state on Portal account page#26679
minimaluminium merged 14 commits intomainfrom
portal-canceled-state-BER-3348

Conversation

@minimaluminium
Copy link
Copy Markdown
Member

@minimaluminium minimaluminium commented Mar 4, 2026

ref https://linear.app/ghost/issue/BER-3348/portal-paid-section-doesnt-indicate-subscription-is-canceled

When a member cancels their subscription, the Portal account page looked nearly identical to an active subscription — the only signal was a small plain-text line that was easy to miss.

  • Replaced the plain cancellation text with an accent-colored banner containing the expiry date and "Continue subscription" button
  • Added a CANCELED badge next to the tier name in the plan row
  • Added i18n keys for the new copy
  • Added unit tests for both the banner and badge

Note

Low Risk
UI-only changes to account display plus new tests/i18n strings; no backend logic or sensitive flows modified.

Overview
Improves the Portal account UI for canceled-but-still-active paid subscriptions by replacing the easy-to-miss expiry notice with a prominent brand-accented banner showing the expiry date and a Continue subscription CTA.

Adds a Canceled badge next to the tier/plan name, introduces styling for the new banner/badge, and includes i18n keys plus new unit tests covering the banner, expiry date rendering, and badge visibility.

Written by Cursor Bugbot for commit 39b77fe. This will update automatically on new commits. Configure here.

Replaced plain cancellation text with an accent-colored banner containing
the expiry message and continue button. Added a CANCELED badge next to the
plan name in the account plan row.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds UI, translations, and tests to surface canceled subscriptions on AccountHomePage. Introduces CSS selectors for a cancellation banner and a canceled badge; refactors ContinueSubscriptionButton to always render a banner with an expiry date derived from subscription.current_period_end and a single full‑width continue button; updates PaidAccountActions to conditionally render a "Canceled" badge next to the plan name; adds i18n keys for the badge label and banner message; and adds unit tests for the banner and badge behavior. Also bumps apps/portal package version.

Possibly related PRs

Suggested reviewers

  • aileen
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description provides clear context about the problem, the specific changes made (banner, badge, i18n, tests), and directly relates to the changeset across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title 'Improved canceled state on Portal account page' directly aligns with the PR's main objective of enhancing the visual indication of a canceled subscription state on the Portal account page.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch portal-canceled-state-BER-3348

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.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/portal/test/unit/components/pages/AccountHomePage/continue-subscription-button.test.js (1)

104-135: Add one interaction test for the continue action dispatch.

Current tests validate rendering, but not that clicking the button actually calls doAction('continueSubscription', ...).

✅ Suggested test addition
+    test('clicking continue button dispatches continueSubscription action', () => {
+        const products = getProductsData({numOfProducts: 1});
+        const site = getSiteData({products, portalProducts: products.map(p => p.id)});
+        const member = getMemberData({
+            paid: true,
+            subscriptions: [
+                getSubscriptionData({
+                    status: 'active',
+                    cancelAtPeriodEnd: true,
+                    currentPeriodEnd: '2026-04-03T00:00:00.000Z',
+                    amount: 500,
+                    currency: 'USD',
+                    interval: 'month'
+                })
+            ]
+        });
+
+        const {queryByText, mockDoActionFn} = setup({site, member});
+        const button = queryByText('Continue subscription');
+        button.click();
+
+        expect(mockDoActionFn).toHaveBeenCalledWith(
+            'continueSubscription',
+            expect.objectContaining({subscriptionId: expect.any(String)})
+        );
+    });
🤖 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/continue-subscription-button.test.js`
around lines 104 - 135, Add an interaction test to the existing "continue button
renders inside the banner" case (or a new test) that verifies clicking the
"Continue subscription" button dispatches the continue action: mock or spy the
doAction function used by the component (the action name 'continueSubscription'
/ doAction) before calling setup({site, member}), find the button via
queryByText('Continue subscription'), simulate a click on it, and assert that
doAction was called with 'continueSubscription' and the expected payload (e.g.,
subscription id or member context) so the UI test covers both render and
interaction.
🤖 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/account-home-page.css`:
- Around line 79-96: The banner overlay (::before on .gh-portal-cancel-banner)
is covering controls; make the pseudo-element non-interactive and place it
beneath content by adding pointer-events: none and a lower stacking context
(e.g., z-index: 0) to .gh-portal-cancel-banner::before, and ensure the visible
content (.gh-portal-cancel-banner p and/or .gh-portal-cancel-banner itself) has
a higher z-index (e.g., z-index: 1) and position: relative so buttons and links
remain clickable and sit above the overlay.

In
`@apps/portal/src/components/pages/AccountHomePage/components/continue-subscription-button.js`:
- Around line 20-35: The loading/disabled state is tied to the wrong action
string: isRunning is checking for 'cancelSubscription:running' but the button
calls doAction('continueSubscription', ...), so change the action check to
listen for the continueSubscription running state; update the isRunning
computation (and consequently disabled) to check for
'continueSubscription:running' (or derive it from the same action variable used
elsewhere) so ActionButton receives the correct isRunning/disabled when
continueSubscription is in-flight; adjust the symbols isRunning, disabled, and
the string used in the includes(...) accordingly in
continue-subscription-button.js.

---

Nitpick comments:
In
`@apps/portal/test/unit/components/pages/AccountHomePage/continue-subscription-button.test.js`:
- Around line 104-135: Add an interaction test to the existing "continue button
renders inside the banner" case (or a new test) that verifies clicking the
"Continue subscription" button dispatches the continue action: mock or spy the
doAction function used by the component (the action name 'continueSubscription'
/ doAction) before calling setup({site, member}), find the button via
queryByText('Continue subscription'), simulate a click on it, and assert that
doAction was called with 'continueSubscription' and the expected payload (e.g.,
subscription id or member context) so the UI test covers both render and
interaction.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3cec1569-0797-4dd0-a64a-8739d77942d8

📥 Commits

Reviewing files that changed from the base of the PR and between 2bbc0bf and 696cd7c.

📒 Files selected for processing (6)
  • apps/portal/src/components/pages/AccountHomePage/account-home-page.css
  • apps/portal/src/components/pages/AccountHomePage/components/continue-subscription-button.js
  • apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js
  • apps/portal/test/unit/components/pages/AccountHomePage/continue-subscription-button.test.js
  • apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js
  • ghost/i18n/locales/en/portal.json

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/portal/src/components/pages/AccountHomePage/components/continue-subscription-button.js (1)

20-23: Simplify redundant boolean expression.

Line 22's ternary is redundant since isRunning is already a boolean.

✨ Suggested simplification
     const label = t('Continue subscription');
     const isRunning = ['continueSubscription:running'].includes(action);
-    const disabled = (isRunning) ? true : false;
+    const disabled = isRunning;
     const expiryDate = getDateString(subscription.current_period_end);
🤖 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/continue-subscription-button.js`
around lines 20 - 23, The ternary on the disabled assignment is redundant—remove
it and assign the boolean directly: set disabled to the existing isRunning
variable (which is computed from action) instead of using (isRunning) ? true :
false; update the code around the isRunning/disabled declarations (symbols:
isRunning, disabled, action) so disabled is simply assigned from isRunning.
🤖 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/continue-subscription-button.js`:
- Around line 20-23: The ternary on the disabled assignment is redundant—remove
it and assign the boolean directly: set disabled to the existing isRunning
variable (which is computed from action) instead of using (isRunning) ? true :
false; update the code around the isRunning/disabled declarations (symbols:
isRunning, disabled, action) so disabled is simply assigned from isRunning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aaeb0717-c4bc-4310-a8fd-7e75def33c98

📥 Commits

Reviewing files that changed from the base of the PR and between 8616437 and f33f8dc.

📒 Files selected for processing (2)
  • apps/portal/src/components/pages/AccountHomePage/account-home-page.css
  • apps/portal/src/components/pages/AccountHomePage/components/continue-subscription-button.js

@minimaluminium minimaluminium requested a review from sagzy March 4, 2026 08:55
Changelog for v2.64.13 -> 2.64.14:
  - Updated i18n translations
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (4)
ghost/i18n/locales/sv/portal.json (1)

30-30: Prefer adding Swedish copy for the new cancellation keys

Line 30 and Line 234 are introduced with empty values. This will likely surface English fallback text in the Swedish portal flow for canceled subscriptions.

Suggested locale update
-    "Canceled": "",
+    "Canceled": "Avbruten",
...
-    "Your subscription has been canceled and will expire on {expiryDate}.": "",
+    "Your subscription has avslutats och upphör den {expiryDate}.",

Also applies to: 234-234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/i18n/locales/sv/portal.json` at line 30, The Swedish locale is missing
translations for the new cancellation keys; update the i18n resource so the
"Canceled" key (and the other empty cancellation key introduced around line 234)
in the sv portal.json contain proper Swedish strings (e.g., "Avbruten" or the
appropriate contextual phrase) instead of empty values so the portal uses
Swedish text rather than falling back to English; locate and edit the "Canceled"
entry and the other empty cancellation key in the sv portal.json to provide
correct Swedish copy.
ghost/i18n/locales/sl/portal.json (1)

30-30: Fill the new Slovenian keys to avoid mixed-language canceled-state UI.

Both new strings are currently empty, so this flow will likely render in English for sl. Since these labels are central to the canceled-subscription state, please provide Slovenian values now.

Proposed translation patch
-    "Canceled": "",
+    "Canceled": "Preklicano",
...
-    "Your subscription has been canceled and will expire on {expiryDate}.": "",
+    "Your subscription has been canceled and will expire on {expiryDate}.": "Vaša naročnina je bila preklicana in bo potekla dne {expiryDate}.",

Also applies to: 234-234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/i18n/locales/sl/portal.json` at line 30, The Slovenian locale file has
empty translation entries (notably the "Canceled" key in portal.json and another
empty key around the later block), causing fallback to English; open
ghost/i18n/locales/sl/portal.json, locate the "Canceled" property (and the other
empty string near the later block) and replace the empty values with proper
Slovenian phrases that match the UI tone for canceled-subscription states so the
UI no longer renders mixed-language text.
ghost/i18n/locales/fi/portal.json (1)

30-30: Fill the two new Finnish translations instead of leaving them empty.

Both new cancellation strings are key UI copy for this PR; shipping them untranslated weakens the Finnish Portal experience.

Suggested translation update
-    "Canceled": "",
+    "Canceled": "Peruutettu",
...
-    "Your subscription has been canceled and will expire on {expiryDate}.": "",
+    "Your subscription has been canceled and will expire on {expiryDate}.": "Tilauksesi on peruttu ja se päättyy {expiryDate}.",

Also applies to: 234-234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/i18n/locales/fi/portal.json` at line 30, Two new cancellation keys are
left empty; fill the Finnish translations for the key "Canceled" and the other
new cancellation key in this locale file by replacing the empty strings with
correct Finnish UI copy (use the same capitalization/style as other portal
keys), e.g., provide concise Finnish equivalents for both cancellation messages
so the Portal displays translated text instead of blanks.
ghost/i18n/locales/hu/portal.json (1)

30-30: Add Hungarian translations for the new canceled-state strings.

Line 30 and Line 234 introduce new keys with empty values, so canceled-state copy won’t be localized for hu.

Proposed translation patch
-    "Canceled": "",
+    "Canceled": "Lemondva",
...
-    "Your subscription has been canceled and will expire on {expiryDate}.": "",
+    "Your subscription has been canceled and will expire on {expiryDate}.": "Az előfizetésed lemondásra került, és ekkor jár le: {expiryDate}.",

Also applies to: 234-234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/i18n/locales/hu/portal.json` at line 30, Update the Hungarian locale
entries that have empty values for the canceled-state keys by filling in an
appropriate Hungarian translation: replace the empty string for the "Canceled"
key (appearing twice in the hu portal.json) with "Törölve" (ensuring proper JSON
string quoting and commas), so the canceled-state copy is localized for
Hungarian.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ghost/i18n/locales/ar/portal.json`:
- Line 30: Fill in Arabic translations for the missing cancellation strings in
ghost/i18n/locales/ar/portal.json: provide an Arabic value for the "Canceled"
key and for the expiry banner key (the expiry/expired banner string in the same
file) so users see localized text instead of falling back to source copy; update
the two empty values with appropriate Arabic phrases for the canceled badge and
the plan-expired banner.

In `@ghost/i18n/locales/bg/portal.json`:
- Line 30: The "Canceled" translation keys in the Bulgarian locale are empty —
update both occurrences of the "Canceled" key in the portal.json Bulgarian
locale to a proper Bulgarian translation (e.g. "Отменено"), ensuring the same
translated string is used for both entries and that the JSON remains valid;
after updating, run the i18n checks/linter to confirm no missing keys or
formatting issues.

In `@ghost/i18n/locales/bn/portal.json`:
- Line 30: Add a Bengali translation for the "Canceled" plan badge by replacing
the empty value for the "Canceled" key in the bn portal translations with the
appropriate Bengali string (e.g., "বাতিল"). Update the "Canceled" entry in
portal.json so the translation is returned instead of falling back to English.
- Line 234: Replace the empty translation for the JSON key "Your subscription
has been canceled and will expire on {expiryDate}." with a Bengali localization
that preserves the {expiryDate} placeholder, e.g. use: "আপনার সাবস্ক্রিপশন বাতিল
করা হয়েছে এবং এটি {expiryDate}-এ মেয়াদ উত্তীর্ণ হবে।" so the message remains
user-facing and the placeholder is unchanged.

In `@ghost/i18n/locales/el/portal.json`:
- Line 30: Open the Greek portal locale file (ghost/i18n/locales/el/portal.json)
and replace the empty string values for the cancellation keys with their Greek
translations: set the value for "Canceled" to the appropriate Greek word/phrase
and also update the other empty cancellation key present near line 234 (the
second cancellation key in the same file) with its Greek translation so both
keys no longer fall back to the source copy.

In `@ghost/i18n/locales/es/portal.json`:
- Line 30: Add Spanish translations for the two empty localization keys: fill
the "Canceled" key with the Spanish string (e.g., "Cancelado") and update the
expiry banner key at the other empty entry (line referenced in the comment) with
an appropriate Spanish phrase (e.g., "Vencido" or "Expirado" depending on
context). Ensure the translations are plain strings (no extra markup) and match
the surrounding locale style in the same file so the canceled badge and expiry
banner appear localized.

In `@ghost/i18n/locales/gd/portal.json`:
- Line 30: Add Gaelic translations for the missing canceled-state localization
keys so the portal uses Gaelic text instead of falling back to English;
specifically fill the "Canceled" key in ghost/i18n/locales/gd/portal.json (and
the other empty canceled-state entry at the second occurrence) with the correct
Scottish Gaelic copy, ensuring the strings match the intent of the badge/banner
copy used elsewhere in the codebase.

In `@ghost/i18n/locales/hi/portal.json`:
- Line 30: Add Hindi translations for the empty keys in hi locale: replace the
empty value for the "Canceled" key and the other canceled-state key used for the
banner/badge (the string containing "{expiryDate}") with appropriate Hindi text
while preserving the placeholder {expiryDate} exactly as-is; update the values
in ghost/i18n/locales/hi/portal.json for the two canceled-related entries (the
plain "Canceled" label and the longer canceled subscription message that
includes {expiryDate}) so they are translated to Hindi and keep the placeholder
unchanged.

In `@ghost/i18n/locales/it/portal.json`:
- Line 30: The "Canceled" canceled-state keys in portal.json were left empty
causing blank UI text; open the portal.json locale object and add an Italian
translation (or an English fallback) for the "Canceled" key and any other
canceled-state keys that were added with empty values so badges/banners render
text for Italian users; ensure the key names (e.g., "Canceled") are updated with
non-empty string values and preserve JSON formatting/escaping.

In `@ghost/i18n/locales/ko/portal.json`:
- Line 30: The locale JSON has new empty values for the portal keys (notably the
"Canceled" key and the second newly added canceled-state key) so the Korean UI
is missing translations; update those keys in the Korean portal locale JSON by
replacing the empty strings with proper Korean translations for the
canceled-state copy (ensure you edit the "Canceled" key and the other newly
added canceled-related key in the same file), keep the phrasing consistent with
existing portal terminology, and run the localization/lint checks to confirm
valid JSON and coverage.

In `@ghost/i18n/locales/nn/portal.json`:
- Line 30: Add Nynorsk translations for the two empty cancellation entries in
the NN portal locale JSON: fill the value for the "Canceled" key and the other
empty cancellation key near the bottom of the file with appropriate Nynorsk
strings (e.g., "Avbrote" or the correct contextual translation). Update the
values in ghost/i18n/locales/nn/portal.json for the "Canceled" key and the
second cancellation key so the account UI will render localized Nynorsk text.

In `@ghost/i18n/locales/pl/portal.json`:
- Line 30: The Polish locale file is missing translations for the new
canceled-state strings (e.g. the "Canceled" key in
ghost/i18n/locales/pl/portal.json), causing fallbacks to English; update the
file by replacing the empty values with correct Polish translations for
"Canceled" and any other cancelled-state keys present (also the other empty
entry noted around line 234) so the portal UI shows Polish text for
account-state messages.

In `@ghost/i18n/locales/ru/portal.json`:
- Line 30: The Russian localization is missing translations for the
canceled-state strings; provide proper Russian text for the "Canceled" key and
the other empty canceled-state badge/banner entry in the ru/portal.json file so
the badge/banner strings display in Russian; update those empty string values
with appropriate Russian translations (e.g., "Отменено" or context-appropriate
variants) keeping the JSON keys unchanged.

In `@ghost/i18n/locales/sk/portal.json`:
- Line 30: The Slovak locale file is missing translations for the new
canceled-state keys ("Canceled" and the other instance introduced at the same
location), leaving their values empty; update ghost/i18n/locales/sk/portal.json
by replacing the empty string values for "Canceled" (and the duplicate key at
the other occurrence) with appropriate Slovak translations (e.g., "Zrušené" or
context-appropriate phrasing) so the canceled badge/banner displays localized
copy.

In `@ghost/i18n/locales/ta/portal.json`:
- Line 30: Fill the empty Tamil translations for the cancellation keys by
replacing the empty string values for the JSON key "Canceled" (the occurrences
shown in the diff) with the correct Tamil copy (e.g., "ரத்துசெய்தது" or the
agreed translation), preserving JSON syntax and quoting and keeping the trailing
commas as in the file so the canceled badge/banner UI uses the localized string
instead of falling back to source text.

In `@ghost/i18n/locales/uk/portal.json`:
- Line 30: The file has missing Ukrainian translations for newly introduced
portal keys—fill in the empty values for the "Canceled" key and the other empty
key at line 234 in ghost/i18n/locales/uk/portal.json by replacing the empty
strings with the correct Ukrainian translations (e.g., "Canceled": "Скасовано"
or your approved translation), keep the JSON key names unchanged, preserve
quoting/commas and file encoding, and run the i18n/JSON linter to ensure valid
JSON after editing.

---

Nitpick comments:
In `@ghost/i18n/locales/fi/portal.json`:
- Line 30: Two new cancellation keys are left empty; fill the Finnish
translations for the key "Canceled" and the other new cancellation key in this
locale file by replacing the empty strings with correct Finnish UI copy (use the
same capitalization/style as other portal keys), e.g., provide concise Finnish
equivalents for both cancellation messages so the Portal displays translated
text instead of blanks.

In `@ghost/i18n/locales/hu/portal.json`:
- Line 30: Update the Hungarian locale entries that have empty values for the
canceled-state keys by filling in an appropriate Hungarian translation: replace
the empty string for the "Canceled" key (appearing twice in the hu portal.json)
with "Törölve" (ensuring proper JSON string quoting and commas), so the
canceled-state copy is localized for Hungarian.

In `@ghost/i18n/locales/sl/portal.json`:
- Line 30: The Slovenian locale file has empty translation entries (notably the
"Canceled" key in portal.json and another empty key around the later block),
causing fallback to English; open ghost/i18n/locales/sl/portal.json, locate the
"Canceled" property (and the other empty string near the later block) and
replace the empty values with proper Slovenian phrases that match the UI tone
for canceled-subscription states so the UI no longer renders mixed-language
text.

In `@ghost/i18n/locales/sv/portal.json`:
- Line 30: The Swedish locale is missing translations for the new cancellation
keys; update the i18n resource so the "Canceled" key (and the other empty
cancellation key introduced around line 234) in the sv portal.json contain
proper Swedish strings (e.g., "Avbruten" or the appropriate contextual phrase)
instead of empty values so the portal uses Swedish text rather than falling back
to English; locate and edit the "Canceled" entry and the other empty
cancellation key in the sv portal.json to provide correct Swedish copy.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 03385c8e-3e68-457b-a55c-3200eefbdfaa

📥 Commits

Reviewing files that changed from the base of the PR and between f33f8dc and c117ce9.

📒 Files selected for processing (62)
  • ghost/i18n/locales/af/portal.json
  • ghost/i18n/locales/ar/portal.json
  • ghost/i18n/locales/bg/portal.json
  • ghost/i18n/locales/bn/portal.json
  • ghost/i18n/locales/bs/portal.json
  • ghost/i18n/locales/ca/portal.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/portal.json
  • ghost/i18n/locales/da/portal.json
  • ghost/i18n/locales/de-CH/portal.json
  • ghost/i18n/locales/de/portal.json
  • ghost/i18n/locales/el/portal.json
  • ghost/i18n/locales/eo/portal.json
  • ghost/i18n/locales/es/portal.json
  • ghost/i18n/locales/et/portal.json
  • ghost/i18n/locales/eu/portal.json
  • ghost/i18n/locales/fa/portal.json
  • ghost/i18n/locales/fi/portal.json
  • ghost/i18n/locales/fr/portal.json
  • ghost/i18n/locales/gd/portal.json
  • ghost/i18n/locales/he/portal.json
  • ghost/i18n/locales/hi/portal.json
  • ghost/i18n/locales/hr/portal.json
  • ghost/i18n/locales/hu/portal.json
  • ghost/i18n/locales/id/portal.json
  • ghost/i18n/locales/is/portal.json
  • ghost/i18n/locales/it/portal.json
  • ghost/i18n/locales/ja/portal.json
  • ghost/i18n/locales/ko/portal.json
  • ghost/i18n/locales/kz/portal.json
  • ghost/i18n/locales/lt/portal.json
  • ghost/i18n/locales/lv/portal.json
  • ghost/i18n/locales/mk/portal.json
  • ghost/i18n/locales/mn/portal.json
  • ghost/i18n/locales/ms/portal.json
  • ghost/i18n/locales/nb/portal.json
  • ghost/i18n/locales/ne/portal.json
  • ghost/i18n/locales/nl/portal.json
  • ghost/i18n/locales/nn/portal.json
  • ghost/i18n/locales/pa/portal.json
  • ghost/i18n/locales/pl/portal.json
  • ghost/i18n/locales/pt-BR/portal.json
  • ghost/i18n/locales/pt/portal.json
  • ghost/i18n/locales/ro/portal.json
  • ghost/i18n/locales/ru/portal.json
  • ghost/i18n/locales/si/portal.json
  • ghost/i18n/locales/sk/portal.json
  • ghost/i18n/locales/sl/portal.json
  • ghost/i18n/locales/sq/portal.json
  • ghost/i18n/locales/sr-Cyrl/portal.json
  • ghost/i18n/locales/sr/portal.json
  • ghost/i18n/locales/sv/portal.json
  • ghost/i18n/locales/sw/portal.json
  • ghost/i18n/locales/ta/portal.json
  • ghost/i18n/locales/th/portal.json
  • ghost/i18n/locales/tr/portal.json
  • ghost/i18n/locales/uk/portal.json
  • ghost/i18n/locales/ur/portal.json
  • ghost/i18n/locales/uz/portal.json
  • ghost/i18n/locales/vi/portal.json
  • ghost/i18n/locales/zh-Hant/portal.json
  • ghost/i18n/locales/zh/portal.json
✅ Files skipped from review due to trivial changes (3)
  • ghost/i18n/locales/hr/portal.json
  • ghost/i18n/locales/he/portal.json
  • ghost/i18n/locales/lt/portal.json

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ghost/i18n/locales/sl/portal.json`:
- Line 36: Fill in Slovenian translations for the new canceled-state keys in
portal.json by replacing the empty string values for "Canceled" and the other
newly added canceled-state keys with appropriate Slovenian text (e.g.,
"Canceled": "Preklicano"); locate these keys in the sl portal.json (look for
"Canceled" and any sibling canceled-related keys) and provide accurate
translations so the canceled-state flow is localized.

In `@ghost/i18n/locales/vi/portal.json`:
- Line 36: The "Canceled" translation entries in portal.json are empty and will
render blank UI; replace both empty values for the "Canceled" key (the two
occurrences) with a safe non-empty fallback (e.g., the English "Canceled" or
Vietnamese "Đã hủy") so the canceled-state badge/banner displays correctly until
a proper localized string is provided.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 37c54f49-a553-46bc-8761-44a66706d21e

📥 Commits

Reviewing files that changed from the base of the PR and between c117ce9 and 39b77fe.

📒 Files selected for processing (66)
  • apps/portal/package.json
  • apps/portal/src/components/pages/AccountHomePage/components/paid-account-actions.js
  • apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js
  • ghost/i18n/locales/af/portal.json
  • ghost/i18n/locales/ar/portal.json
  • ghost/i18n/locales/bg/portal.json
  • ghost/i18n/locales/bn/portal.json
  • ghost/i18n/locales/bs/portal.json
  • ghost/i18n/locales/ca/portal.json
  • ghost/i18n/locales/context.json
  • ghost/i18n/locales/cs/portal.json
  • ghost/i18n/locales/da/portal.json
  • ghost/i18n/locales/de-CH/portal.json
  • ghost/i18n/locales/de/portal.json
  • ghost/i18n/locales/el/portal.json
  • ghost/i18n/locales/en/portal.json
  • ghost/i18n/locales/eo/portal.json
  • ghost/i18n/locales/es/portal.json
  • ghost/i18n/locales/et/portal.json
  • ghost/i18n/locales/eu/portal.json
  • ghost/i18n/locales/fa/portal.json
  • ghost/i18n/locales/fi/portal.json
  • ghost/i18n/locales/fr/portal.json
  • ghost/i18n/locales/gd/portal.json
  • ghost/i18n/locales/he/portal.json
  • ghost/i18n/locales/hi/portal.json
  • ghost/i18n/locales/hr/portal.json
  • ghost/i18n/locales/hu/portal.json
  • ghost/i18n/locales/id/portal.json
  • ghost/i18n/locales/is/portal.json
  • ghost/i18n/locales/it/portal.json
  • ghost/i18n/locales/ja/portal.json
  • ghost/i18n/locales/ko/portal.json
  • ghost/i18n/locales/kz/portal.json
  • ghost/i18n/locales/lt/portal.json
  • ghost/i18n/locales/lv/portal.json
  • ghost/i18n/locales/mk/portal.json
  • ghost/i18n/locales/mn/portal.json
  • ghost/i18n/locales/ms/portal.json
  • ghost/i18n/locales/nb/portal.json
  • ghost/i18n/locales/ne/portal.json
  • ghost/i18n/locales/nl/portal.json
  • ghost/i18n/locales/nn/portal.json
  • ghost/i18n/locales/pa/portal.json
  • ghost/i18n/locales/pl/portal.json
  • ghost/i18n/locales/pt-BR/portal.json
  • ghost/i18n/locales/pt/portal.json
  • ghost/i18n/locales/ro/portal.json
  • ghost/i18n/locales/ru/portal.json
  • ghost/i18n/locales/si/portal.json
  • ghost/i18n/locales/sk/portal.json
  • ghost/i18n/locales/sl/portal.json
  • ghost/i18n/locales/sq/portal.json
  • ghost/i18n/locales/sr-Cyrl/portal.json
  • ghost/i18n/locales/sr/portal.json
  • ghost/i18n/locales/sv/portal.json
  • ghost/i18n/locales/sw/portal.json
  • ghost/i18n/locales/ta/portal.json
  • ghost/i18n/locales/th/portal.json
  • ghost/i18n/locales/tr/portal.json
  • ghost/i18n/locales/uk/portal.json
  • ghost/i18n/locales/ur/portal.json
  • ghost/i18n/locales/uz/portal.json
  • ghost/i18n/locales/vi/portal.json
  • ghost/i18n/locales/zh-Hant/portal.json
  • ghost/i18n/locales/zh/portal.json
✅ Files skipped from review due to trivial changes (1)
  • apps/portal/package.json
🚧 Files skipped from review as they are similar to previous changes (44)
  • ghost/i18n/locales/fi/portal.json
  • ghost/i18n/locales/ro/portal.json
  • ghost/i18n/locales/bs/portal.json
  • ghost/i18n/locales/ur/portal.json
  • ghost/i18n/locales/tr/portal.json
  • ghost/i18n/locales/bn/portal.json
  • ghost/i18n/locales/ko/portal.json
  • ghost/i18n/locales/da/portal.json
  • ghost/i18n/locales/is/portal.json
  • apps/portal/test/unit/components/pages/AccountHomePage/paid-account-actions.test.js
  • ghost/i18n/locales/ta/portal.json
  • ghost/i18n/locales/mn/portal.json
  • ghost/i18n/locales/nn/portal.json
  • ghost/i18n/locales/sk/portal.json
  • ghost/i18n/locales/cs/portal.json
  • ghost/i18n/locales/hu/portal.json
  • ghost/i18n/locales/hi/portal.json
  • ghost/i18n/locales/ne/portal.json
  • ghost/i18n/locales/nb/portal.json
  • ghost/i18n/locales/pt/portal.json
  • ghost/i18n/locales/sr-Cyrl/portal.json
  • ghost/i18n/locales/bg/portal.json
  • ghost/i18n/locales/uk/portal.json
  • ghost/i18n/locales/kz/portal.json
  • ghost/i18n/locales/hr/portal.json
  • ghost/i18n/locales/sq/portal.json
  • ghost/i18n/locales/fa/portal.json
  • ghost/i18n/locales/si/portal.json
  • ghost/i18n/locales/nl/portal.json
  • ghost/i18n/locales/ca/portal.json
  • ghost/i18n/locales/ms/portal.json
  • ghost/i18n/locales/sw/portal.json
  • ghost/i18n/locales/gd/portal.json
  • ghost/i18n/locales/eu/portal.json
  • ghost/i18n/locales/th/portal.json
  • ghost/i18n/locales/af/portal.json
  • ghost/i18n/locales/pl/portal.json
  • ghost/i18n/locales/eo/portal.json
  • ghost/i18n/locales/it/portal.json
  • ghost/i18n/locales/en/portal.json
  • ghost/i18n/locales/pt-BR/portal.json
  • ghost/i18n/locales/ar/portal.json
  • ghost/i18n/locales/ru/portal.json
  • ghost/i18n/locales/zh/portal.json

Copy link
Copy Markdown
Contributor

@sagzy sagzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, left one question

@ErisDS
Copy link
Copy Markdown
Member

ErisDS commented Mar 4, 2026

🤖 Velo CI Failure Analysis

Classification: 🟠 SOFT FAIL

  • Workflow: CI
  • Failed Step: Integration tests
  • Run: View failed run
    What failed: Test assertion failure: expected 332 to equal 392 in domain warming integration test
    Why: The root cause of the failure is a test assertion failure in the domain warming integration test. This is a code issue, as the test is checking the expected behavior of the application code and the failure indicates a mismatch between the expected and actual values.
    Action:
    The author should investigate the domain warming functionality and update the test to match the expected behavior or fix the underlying code to ensure the test passes.

@ErisDS
Copy link
Copy Markdown
Member

ErisDS commented Mar 4, 2026

🤖 Velo CI Failure Analysis

Classification: 🟠 SOFT FAIL

  • Workflow: CI
  • Failed Step: Integration tests
  • Run: View failed run
    What failed: Test assertion failure in domain warming integration test
    Why: The root cause is a test assertion failure in the domain warming integration test. This is a code issue, as the test is failing due to an incorrect expected value in the test case.
    Action:
    The author should investigate the domain warming integration test and fix the incorrect assertion to ensure the test passes.

@ErisDS
Copy link
Copy Markdown
Member

ErisDS commented Mar 4, 2026

🤖 Velo CI Failure Analysis

Classification: 🟠 SOFT FAIL

  • Workflow: CI
  • Failed Step: Integration tests
  • Run: View failed run
    What failed: Integration test failure due to incorrect time-based warmup limit
    Why: The root cause of the failure is a test assertion error where the expected value of 332 did not match the actual value of 392 for the domain warming progression test. This is a code issue related to the implementation of the domain warming feature, not an infrastructure problem.
    Action:
    The author should investigate the domain warming logic and update the test to ensure the expected behavior is correctly implemented.

@minimaluminium minimaluminium changed the title Portal canceled state ber 3348 Improved canceled state on Portal account page Mar 5, 2026
@cursor
Copy link
Copy Markdown

cursor bot commented Mar 5, 2026

You have run out of free Bugbot PR reviews for this billing cycle. This will reset on April 3.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@minimaluminium minimaluminium requested a review from sagzy March 5, 2026 09:09
@minimaluminium minimaluminium merged commit 8a21c7a into main Mar 5, 2026
32 checks passed
@minimaluminium minimaluminium deleted the portal-canceled-state-BER-3348 branch March 5, 2026 09:19
sagzy pushed a commit that referenced this pull request Mar 9, 2026
…6733)

ref
https://linear.app/ghost/issue/BER-3417

The `{{cancel_link}}` theme helper's default `continueLabel` was
"Continue subscription", while Portal was updated to use "Resume
subscription" in #26679.
                  
- Updated the default `continueLabel` from "Continue subscription" to
"Resume subscription"
  - Updated the corresponding unit test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants