Skip to content

Add a billing tab to the settings #2811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

Arpan-206
Copy link
Contributor

@Arpan-206 Arpan-206 commented Apr 23, 2025

Checklist:

  • I've thoroughly self-reviewed my changes
  • I've added tests for my changes, unless they affect admin-only areas (or are otherwise not worth testing)
  • I've verified any visual changes using Percy (add a commit with [percy] in the message to trigger)

Summary by CodeRabbit

  • New Features

    • Introduced a dedicated Billing page under Settings, featuring sections for Membership status, Renewal details, Support contact, and Payment History.
    • Added visual indicators and detailed information for various membership states, including VIP and active/expired subscriptions.
    • Users can view and download invoices from their payment history and access support directly from the Billing page.
  • Bug Fixes

    • Updated all navigation and badge redirects to point to the new Billing page.
  • Chores

    • Removed the old Membership page and related tests.
    • Implemented a permanent redirect from /membership to /settings/billing.

Copy link

linear bot commented Apr 23, 2025

Copy link
Contributor

coderabbitai bot commented Apr 23, 2025

Walkthrough

This update introduces a new "Billing" section within the application settings, replacing the previous "Membership" page and associated routes. It adds new routes, controllers, templates, and components to manage and display billing, membership status, payment history, renewal information, and support options. The navigation and redirection logic throughout the application has been updated to reference the new billing section instead of the old membership route. Associated tests, page objects, and helper functions have been added or updated to reflect these changes, while obsolete membership-related files and tests have been removed. A permanent redirect from /membership to /settings/billing is also configured.

Changes

Files / Areas Change Summary
app/components/settings/billing-page/membership-section.*, payment-history-section.*, renewal-section.*, support-section.* Added new Ember component templates and TypeScript classes for billing page: Membership, Payment History, Renewal, and Support sections. These handle display logic for user membership status, payment history, renewal state, and support contact, with type-safe arguments and tracked properties as needed.
app/templates/settings/billing.hbs Added new billing settings page template, composing the above sections with dividers and passing user models as needed.
app/controllers/settings/billing.ts, app/routes/settings/billing.ts Added new controller and route for the billing settings page, handling user authentication and model structure for billing data.
app/controllers/settings.ts Added a new "Billing" tab to the settings navigation.
app/router.ts Added a nested "billing" route under "settings".
app/components/billing-status-badge/member-badge.hbs, app/components/header/account-dropdown.ts, app/routes/pay.ts Updated navigation and redirection logic to use the new "settings.billing" route instead of the old "membership" route.
vercel.json Added a permanent redirect from /membership to /settings/billing.
tests/acceptance/settings-page/billing-test.js, tests/pages/settings/billing-page.ts, tests/support/authentication-helpers.js Added new acceptance tests, page objects, and authentication helpers for the billing settings page, covering membership states, payment history, support, and VIP user scenarios.
tests/acceptance/course-page/view-course-stages-test.js, tests/acceptance/header-test.js, tests/acceptance/pay-test.js Updated tests to expect redirection to /settings/billing instead of /membership.
app/routes/membership.js, app/templates/membership.hbs, tests/acceptance/manage-membership-test.js, tests/pages/membership-page.js Deleted legacy membership route, template, acceptance tests, and page object.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant SettingsBillingRoute
    participant BillingController
    participant BillingPageComponents

    User->>Router: Navigate to /settings/billing
    Router->>SettingsBillingRoute: Enter billing route
    SettingsBillingRoute->>SettingsBillingRoute: Check authentication
    SettingsBillingRoute->>BillingController: Provide user model
    BillingController->>BillingPageComponents: Pass model/user to components
    BillingPageComponents->>User: Render membership, payment, renewal, support sections
Loading

Suggested labels

enhancement

Suggested reviewers

  • rohitpaulk

Poem

A bunny hopped to billing’s door,
No “membership” sign—there anymore!
Now four new sections, crisp and bright,
Track payments, status, all in sight.
If you need help, just click and send,
For billing journeys never end.
Hop along, the path is clear—
Settings’ billing lives right here! 🐇💳


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between a23e813 and 5e42545.

📒 Files selected for processing (1)
  • tests/acceptance/settings-page/billing-test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/acceptance/settings-page/billing-test.js

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Apr 23, 2025

Test Results

  1 files  ±0    1 suites  ±0   8m 35s ⏱️ +42s
625 tests +1  580 ✅ +2  45 💤 ±0  0 ❌ ±0 
625 runs  +1  580 ✅ +3  45 💤 ±0  0 ❌  - 1 

Results for commit 5e42545. ± Comparison against base commit 8f14f91.

This pull request removes 9 and adds 10 tests. Note that renamed tests count towards both.
Chrome 135.0 ‑ Acceptance | course-page | view-course-stages-test: member badge redirects to /membership
Chrome 135.0 ‑ Acceptance | header-test: member badge redirects to /membership
Chrome 135.0 ‑ Acceptance | manage-membership-test: subscriber can manage membership
Chrome 135.0 ‑ Acceptance | manage-membership-test: subscriber can update payment method
Chrome 135.0 ‑ Acceptance | manage-membership-test: subscriber can view recent payments
Chrome 135.0 ‑ Acceptance | manage-membership-test: subscriber can view upcoming payments
Chrome 135.0 ‑ Acceptance | manage-membership-test: subscriber that is a partner has correct membership plan copy
Chrome 135.0 ‑ Acceptance | manage-membership-test: subscriber that is a partner with expiry has correct membership plan copy
Chrome 135.0 ‑ Acceptance | pay-test: user should be redirected to /membership if user is authenticated and has an active subscription
Chrome 135.0 ‑ Acceptance | course-page | view-course-stages-test: member badge redirects to /settings/billing
Chrome 135.0 ‑ Acceptance | header-test: member badge redirects to /settings/billing
Chrome 135.0 ‑ Acceptance | pay-test: user should be redirected to /settings/billing if user is authenticated and has an active subscription
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: membership section shows VIP access for subscriber with VIP access
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: membership section shows correct plan for non-subscriber
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: membership section shows correct plan for subscriber with active subscription
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: payment history section shows charges after creation
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: payment history section shows empty state initially
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: payment history section shows refunded charges correctly
Chrome 135.0 ‑ Acceptance | settings-page | billing-test: support section is visible

♻️ This comment has been updated with latest results.

Copy link

codecov bot commented Apr 23, 2025

Codecov Report

Attention: Patch coverage is 54.54545% with 10 lines in your changes missing coverage. Please review.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...s/settings/billing-page/payment-history-section.ts 53.33% 7 Missing ⚠️
app/routes/settings/billing.ts 50.00% 1 Missing and 1 partial ⚠️
app/components/header/account-dropdown.ts 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link

codecov bot commented Apr 23, 2025

Bundle Report

Changes will decrease total bundle size by 177 bytes (-0.0%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
client-array-push 36.58MB -177 bytes (-0.0%) ⬇️

Affected Assets, Files, and Routes:

view changes for bundle: client-array-push

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/chunk.*.js -1.07kB 405.85kB -0.26%
assets/chunk.*.js 897 bytes 2.84MB 0.03%

Files in assets/chunk.*.js:

  • ./controllers/settings.ts → Total Size: 1.33kB

  • ./routes/pay.ts → Total Size: 1.98kB

  • ./controllers/settings/billing.ts → Total Size: 104 bytes

  • ./routes/settings/billing.ts → Total Size: 1.47kB

@Arpan-206 Arpan-206 marked this pull request as ready for review April 25, 2025 16:04
Copy link
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: 6

🧹 Nitpick comments (5)
app/templates/settings/billing.hbs (1)

20-23: Add handling for empty payment history

The PaymentHistorySection should handle the case where a user has no payment history. Consider adding a conditional to display an appropriate message when the charges array is empty.

 <Settings::FormSection @title="Payment history" @description="Details of your previous payments">
   {{#let this.model.charges as |charges|}}
-    <Settings::BillingPage::PaymentHistorySection @charges={{charges}} />
+    {{#if charges.length}}
+      <Settings::BillingPage::PaymentHistorySection @charges={{charges}} />
+    {{else}}
+      <div class="text-gray-500 py-4">
+        No payment history available.
+      </div>
+    {{/if}}
   {{/let}}
 </Settings::FormSection>
app/components/settings/billing-page/payment-history-section.hbs (1)

14-21: Consider adding a tooltip for refund information.

For partially refunded charges, consider adding a tooltip that provides additional context about when/why the refund was processed to improve user experience.

  {{#if (gt charge.amountRefunded 0)}}
    {{#if charge.isFullyRefunded}}
-     <span data-test-refund-text>(refunded)</span>
+     <span data-test-refund-text title="Fully refunded on {{date-format charge.refundedAt format='PPP'}}">(refunded)</span>
    {{else}}
-     <span data-test-refund-text>(<span class="font-semibold">{{charge.refundedAmountDisplayString}}</span>
+     <span data-test-refund-text title="Partially refunded on {{date-format charge.refundedAt format='PPP'}}">(<span class="font-semibold">{{charge.refundedAmountDisplayString}}</span>
        refunded)</span>
    {{/if}}
  {{/if}}
app/components/settings/billing-page/membership-section.hbs (1)

63-65: Consider adding tracking for membership conversions.

The "Start membership" buttons are key conversion points. Consider adding analytics tracking to measure user engagement with these buttons.

-<PrimaryLinkButton @size="small" @route="pay" class="mt-3">
+<PrimaryLinkButton @size="small" @route="pay" class="mt-3" data-test-start-membership-button {{on "click" this.trackMembershipButtonClick}}>
  Start membership →
</PrimaryLinkButton>

Also applies to: 80-82

tests/acceptance/settings-page/billing-test.js (1)

67-69: Consider mocking email client interaction.

Instead of commenting out the contact button test, consider mocking the email client interaction or testing that the correct mailto URL is generated.

-  // await billingPage.supportSection.clickContactButton();
-  // Commented out because it opens the email client, if behavior changes, uncomment
+  // Test that the correct mailto URL is used
+  const contactButton = document.querySelector('[data-test-support-contact-button]');
+  assert.ok(contactButton.href.startsWith('mailto:'), 'Contact button has mailto link');
+  assert.ok(contactButton.href.includes('subject='), 'Contact button includes subject');
mirage/models/charge.js (1)

1-14: Charge model structure looks good

The model correctly defines the user relationship and includes appropriate fields for a payment charge record.

Consider these improvements:

  1. Add more detailed documentation about valid values for the status field
  2. Consider whether 'succeeded' is the most appropriate default status (versus 'pending')
  3. Clarify the comment on line 9 to better explain the specific linting issue
import { Model, belongsTo } from 'miragejs';

export default Model.extend({
  user: belongsTo(),
  amount: 0,
  amountRefunded: 0,
  currency: 'usd',
  createdAt() {
-    return new Date(); // Done this way due to linting error and shared objects
+    return new Date(); // Returns a new Date object each time to avoid shared reference issues across instances
  },
  invoiceId: null,
-  status: 'succeeded',
+  status: 'succeeded', // Possible values: 'succeeded', 'pending', 'failed', etc.
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0813f20 and d2a4e06.

📒 Files selected for processing (17)
  • app/components/settings/billing-page/membership-section.hbs (1 hunks)
  • app/components/settings/billing-page/membership-section.ts (1 hunks)
  • app/components/settings/billing-page/payment-history-section.hbs (1 hunks)
  • app/components/settings/billing-page/payment-history-section.ts (1 hunks)
  • app/components/settings/billing-page/renewal-section.hbs (1 hunks)
  • app/components/settings/billing-page/renewal-section.ts (1 hunks)
  • app/components/settings/billing-page/support-section.hbs (1 hunks)
  • app/components/settings/billing-page/support-section.ts (1 hunks)
  • app/controllers/settings.ts (1 hunks)
  • app/controllers/settings/billing.ts (1 hunks)
  • app/router.ts (1 hunks)
  • app/routes/settings/billing.ts (1 hunks)
  • app/templates/settings/billing.hbs (1 hunks)
  • mirage/models/charge.js (1 hunks)
  • tests/acceptance/settings-page/billing-test.js (1 hunks)
  • tests/pages/settings/billing-page.ts (1 hunks)
  • tests/support/authentication-helpers.js (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
app/components/settings/billing-page/renewal-section.ts (3)
app/components/settings/billing-page/membership-section.ts (1)
  • Registry (15-17)
app/components/settings/billing-page/payment-history-section.ts (1)
  • Registry (15-17)
app/components/settings/billing-page/support-section.ts (1)
  • Registry (14-16)
tests/acceptance/settings-page/billing-test.js (1)
tests/support/authentication-helpers.js (4)
  • signInAsSubscriber (51-61)
  • signIn (1-14)
  • signInAsVipUser (63-69)
  • user (102-102)
app/components/settings/billing-page/payment-history-section.ts (3)
app/components/settings/billing-page/membership-section.ts (1)
  • Registry (15-17)
app/components/settings/billing-page/renewal-section.ts (1)
  • Registry (10-12)
app/components/settings/billing-page/support-section.ts (1)
  • Registry (14-16)
app/components/settings/billing-page/membership-section.ts (4)
app/models/user.ts (1)
  • UserModel (28-218)
app/components/settings/billing-page/payment-history-section.ts (1)
  • Registry (15-17)
app/components/settings/billing-page/renewal-section.ts (1)
  • Registry (10-12)
app/components/settings/billing-page/support-section.ts (1)
  • Registry (14-16)
app/components/settings/billing-page/support-section.ts (3)
app/components/settings/billing-page/membership-section.ts (1)
  • Registry (15-17)
app/components/settings/billing-page/payment-history-section.ts (1)
  • Registry (15-17)
app/components/settings/billing-page/renewal-section.ts (1)
  • Registry (10-12)
🔇 Additional comments (13)
app/controllers/settings.ts (1)

18-18: LGTM! The billing tab is correctly added to the settings controller.

This change properly integrates the new billing route into the settings interface navigation, maintaining consistency with the existing tab structure.

app/components/settings/billing-page/renewal-section.ts (1)

1-13: RenewalSectionComponent setup looks solid

The component class and its Glint registry declaration follow the established pattern, matching other billing‐page sections and providing correct typing for the root <div>. No changes needed here.

app/controllers/settings/billing.ts (1)

1-11: BillingController model typing is correct

The BillingController properly extends Controller and declares a strongly typed model combining SettingsModelType with charges: ChargeModel[]. This aligns with the route and ensures type safety.

app/components/settings/billing-page/payment-history-section.ts (1)

1-18: PaymentHistorySectionComponent matches the pattern

The component signature correctly types Args.charges and the registry declaration mirrors other billing‐page sections. Everything is consistent and ready for use in the template.

app/components/settings/billing-page/support-section.hbs (1)

1-12: Support section template is well-structured

The Handlebars markup cleanly renders the mailto link with dynamic @username and the styled support message. It follows accessibility and styling conventions in use elsewhere.

app/components/settings/billing-page/membership-section.ts (1)

1-18: MembershipSectionComponent conforms to conventions

The component signature properly types Args.user: UserModel and the registry entry is consistent with other billing‐page sections. No further updates required.

app/components/settings/billing-page/support-section.ts (1)

3-17: LGTM! Well-structured component signature

The component signature is well-defined with proper typing for the username argument and correct Glint registry setup for type safety.

app/components/settings/billing-page/payment-history-section.hbs (1)

1-45: Well-structured payment history section with appropriate conditional rendering.

The payment history section effectively handles different states (no charges, succeeded/failed charges, and refund scenarios) with clear visual indicators. The conditional logic for displaying "Download Invoice" links or failure messages is implemented correctly.

app/components/settings/billing-page/membership-section.hbs (1)

1-83: Complete and well-structured membership section with clear visual indicators.

The membership section effectively handles all user states (VIP, active, inactive, and no membership) with appropriate visual indicators and messaging.

tests/pages/settings/billing-page.ts (1)

1-30: Well-structured page object with appropriate test selectors.

The page object correctly implements the necessary selectors and properties for testing the billing page components.

tests/acceptance/settings-page/billing-test.js (1)

1-126: Comprehensive test coverage for billing functionality.

The tests cover all major scenarios for the billing page including different membership states and payment history display. Good use of test helpers and Mirage models.

app/router.ts (1)

84-84: LGTM: Clean addition of the billing route

The addition of the billing route under settings is well-placed and follows the existing routing pattern. This aligns perfectly with the PR objective of adding a billing tab to the settings section.

tests/support/authentication-helpers.js (1)

63-69: LGTM: Well-implemented VIP user authentication helper

The new helper function follows the same pattern as other authentication helpers in the file. It correctly sets the VIP status and expiration date, which will be useful for testing the billing features with different user states.

@Arpan-206 Arpan-206 force-pushed the arpan/cc-1703-revamp-membership-page branch from 78a7cb9 to 4586692 Compare April 25, 2025 16:51
@Arpan-206
Copy link
Contributor Author

@rohitpaulk for now the get help button just opens an email client but I assume we'd want a different action. Can you tell me what

Copy link
Member

@rohitpaulk rohitpaulk left a comment

Choose a reason for hiding this comment

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

Added one round of comments!

@@ -23,7 +23,6 @@
{{else}}
<Header::AccountDropdownLink @text="Subscribe" @icon="credit-card" {{on "click" (fn this.handleSubscribeClick dd.actions)}} />
{{/if}}

Copy link
Member

Choose a reason for hiding this comment

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

@Arpan-206 make sure to review and remove unrelated changes from a PR - we usually do keep empty lines between blocks where indentation changes for readability

Co-authored-by: Paul Kuruvilla <rohitpaulk@gmail.com>
Copy link
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: 0

♻️ Duplicate comments (2)
app/templates/settings/billing.hbs (2)

3-3: Pass auto-renewal data to the RenewalSection component

The RenewalSection component is rendered without any arguments, but it should display different UI states based on whether auto-renewal is enabled or not.

Consider passing the appropriate auto-renewal data:

-<Settings::BillingPage::RenewalSection />
+<Settings::BillingPage::RenewalSection 
+  @autoRenewEnabled={{@model.user.autoRenewEnabled}}
+  @renewalDate={{@model.user.membershipExpiresAt}} 
+/>

You'll need to update the RenewalSection component to accept these arguments.


1-7: 🛠️ Refactor suggestion

Consider moving form section wrappers into the respective components

The current structure passes only the user model to components, but lacks the form section wrappers that would provide proper titles and descriptions for each section.

For consistency with settings/profile.hbs and better component encapsulation, move the Settings::FormSection wrappers into each component:

-<Settings::BillingPage::MembershipSection @user={{@model.user}} />
-<Settings::FormDivider />
-<Settings::BillingPage::RenewalSection />
-<Settings::FormDivider />
-<Settings::BillingPage::SupportSection @user={{@model.user}} />
-<Settings::FormDivider />
-<Settings::BillingPage::PaymentHistorySection @user={{@model.user}} />
+<Settings::BillingPage::MembershipSection @user={{@model.user}} />
+<Settings::BillingPage::RenewalSection 
+  @autoRenewEnabled={{@model.user.autoRenewEnabled}}
+  @renewalDate={{@model.user.membershipExpiresAt}} 
+/>
+<Settings::BillingPage::SupportSection @user={{@model.user}} />
+<Settings::BillingPage::PaymentHistorySection @user={{@model.user}} />

This approach allows each component to:

  1. Control its own title and description
  2. Make customizations based on user state (member/free user)
  3. Provide a more intuitive component hierarchy
🧹 Nitpick comments (3)
app/components/settings/billing-page/payment-history-section.ts (3)

28-45: Add a retry mechanism for failed charge loading

The error handling is good, but users have no way to retry when loading fails except refreshing the entire page.

Consider adding a retry mechanism and exposing it to the template:

  @tracked charges: ChargeModel[] = [];
  @tracked isLoading = true;
  @tracked errorMessage: string | null = null;
+  @tracked attemptCount = 0;

  constructor(owner: unknown, args: Signature['Args']) {
    super(owner, args);
    this.loadCharges();
  }

+ get hasAttemptedMultipleTimes() {
+   return this.attemptCount > 1;
+ }

  async loadCharges() {
    this.isLoading = true;
    this.errorMessage = null;
+    this.attemptCount++;

    try {
      const result = await this.store.query('charge', {
        filter: { user_id: this.args.user.id },
      });
      this.charges = result.toArray();
    } catch (error) {
      console.error('Failed to fetch charges:', error);
-      this.errorMessage = 'Failed to load payment history. Please try again later.';
+      this.errorMessage = this.hasAttemptedMultipleTimes 
+        ? 'Failed to load payment history. Please try again later or contact support.'
+        : 'Failed to load payment history. Please try again.';
      Sentry.captureException(error);
-      this.charges = [];
    } finally {
      this.isLoading = false;
    }
  }

Then in your template, add a retry button when there's an error:

{{#if this.errorMessage}}
  <div class="error-message">
    <p>{{this.errorMessage}}</p>
    <button type="button" {{on "click" this.loadCharges}}>Retry</button>
  </div>
{{/if}}

41-41: Consider preserving charges on error

Currently, when an error occurs during loading, the charges array is cleared. This could create a confusing experience if a user had successfully loaded charges before but a subsequent refresh fails.

Remove the line that clears charges on error to preserve any previously loaded data:

    } catch (error) {
      console.error('Failed to fetch charges:', error);
      this.errorMessage = 'Failed to load payment history. Please try again later.';
      Sentry.captureException(error);
-      this.charges = [];
    } finally {

28-45: Add mechanism to handle race conditions in loadCharges

If loadCharges is called multiple times in quick succession (e.g., from a retry button), race conditions could occur where an older request completes after a newer one.

Add a request ID to track the latest request and ignore results from older requests:

  @tracked charges: ChargeModel[] = [];
  @tracked isLoading = true;
  @tracked errorMessage: string | null = null;
+  @tracked currentRequestId = 0;

  constructor(owner: unknown, args: Signature['Args']) {
    super(owner, args);
    this.loadCharges();
  }

  async loadCharges() {
    this.isLoading = true;
    this.errorMessage = null;
+    const requestId = ++this.currentRequestId;

    try {
      const result = await this.store.query('charge', {
        filter: { user_id: this.args.user.id },
      });
+      // Ignore results from older requests
+      if (requestId !== this.currentRequestId) return;
      this.charges = result.toArray();
    } catch (error) {
+      // Ignore errors from older requests
+      if (requestId !== this.currentRequestId) return;
      console.error('Failed to fetch charges:', error);
      this.errorMessage = 'Failed to load payment history. Please try again later.';
      Sentry.captureException(error);
      this.charges = [];
    } finally {
+      // Only update loading state for the current request
+      if (requestId === this.currentRequestId) {
        this.isLoading = false;
+      }
    }
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 28c49d4 and ff65f50.

📒 Files selected for processing (10)
  • app/components/settings/billing-page/membership-section.hbs (1 hunks)
  • app/components/settings/billing-page/payment-history-section.hbs (1 hunks)
  • app/components/settings/billing-page/payment-history-section.ts (1 hunks)
  • app/components/settings/billing-page/renewal-section.hbs (1 hunks)
  • app/components/settings/billing-page/support-section.hbs (1 hunks)
  • app/components/settings/billing-page/support-section.ts (1 hunks)
  • app/routes/settings/billing.ts (1 hunks)
  • app/templates/settings/billing.hbs (1 hunks)
  • tests/acceptance/settings-page/billing-test.js (1 hunks)
  • tests/pages/settings/billing-page.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/components/settings/billing-page/membership-section.hbs
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/components/settings/billing-page/support-section.hbs
  • app/components/settings/billing-page/renewal-section.hbs
  • app/routes/settings/billing.ts
  • app/components/settings/billing-page/support-section.ts
  • tests/acceptance/settings-page/billing-test.js
  • app/components/settings/billing-page/payment-history-section.hbs
  • tests/pages/settings/billing-page.ts
🔇 Additional comments (1)
app/components/settings/billing-page/payment-history-section.ts (1)

32-36: LGTM: Good practice for error handling and data fetching

The error handling, Sentry integration, and query structure follow good practices. The component properly manages loading states and provides user-friendly error messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants