Skip to content
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

test: Fix tests related to newer imports #3412

Open
wants to merge 8 commits into
base: FYLE-86cx2t82k-tests-for-connect-card-3
Choose a base branch
from

Conversation

bistaastha
Copy link
Contributor

@bistaastha bistaastha commented Jan 8, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced onboarding flow with improved navigation and step management.
    • Added "Go Back" option in onboarding process.
    • Improved form validation for card connection step.
    • New method for navigating to the dashboard based on onboarding status.
    • New event binding for navigation between onboarding steps.
  • Bug Fixes

    • Updated navigation logic to handle different onboarding scenarios.
    • Refined button state management in onboarding forms.
  • Style Changes

    • Updated color schemes for onboarding pages.
    • Adjusted layout and spacing in onboarding components.
  • Tests

    • Expanded test coverage for onboarding and authentication flows.
    • Added mock services for comprehensive testing.
    • Enhanced asynchronous handling in tests for better reliability.

Copy link

coderabbitai bot commented Jan 8, 2025

Walkthrough

In this pull request, we enhance the onboarding process with precision, my friend! The navigation logic is sharpened, and new services are added to improve the flow. Tests are expanded to cover new functionalities, ensuring every step of the onboarding journey is seamless. New properties and methods are introduced, guaranteeing a smooth experience for users. It's a grand upgrade, indeed!

Changes

File Change Summary
src/app/auth/switch-org/switch-org.page.spec.ts Updated test suite with new service dependencies, added async testing for navigation methods.
src/app/auth/switch-org/switch-org.page.ts Enhanced navigation logic with additional organization settings checks.
src/app/fyle/spender-onboarding/* Multiple files modified to improve onboarding flow, add go-back functionality, and refine UI/UX.
src/app/shared/components/sidemenu/sidemenu.component.spec.ts Integrated SpenderOnboardingService into tests for onboarding functionality.
src/app/fyle/spender-onboarding/spender-onboarding.page.spec.ts Enhanced tests with asynchronous handling and expanded functionality for onboarding steps.

Possibly Related PRs

Suggested Reviewers

  • Chethan-Fyle
  • arjunaj5

Poem

🌟 In the world of code, we dance and play,
Enhancing onboarding, come what may!
Buttons disabled, logic refined,
With every change, a smoother grind!
CodeRabbit's magic, shining bright,
Together we soar, reaching new heights! 🚀


🪧 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 docstrings to generate docstrings for this PR. (Beta)
  • @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.

@github-actions github-actions bot added the size/M Medium PR label Jan 8, 2025
Copy link

@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: 9

🔭 Outside diff range comments (1)
src/app/fyle/spender-onboarding/spender-onboarding.page.scss (1)

Line range hint 1-182: Your SCSS structure is as powerful as my punch dialogues! 💪

Mind it! Your BEM naming conventions and organized structure make this stylesheet a blockbuster hit! But here's a style tip from the superstar:

Consider breaking this large SCSS file into smaller, focused partials:

  • _variables.scss
  • _animations.scss
  • _loader.scss

This way, maintaining the styles will be as smooth as my signature sunglasses flip!

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 73f752e and 0c79118.

📒 Files selected for processing (12)
  • src/app/auth/switch-org/switch-org.page.spec.ts (6 hunks)
  • src/app/auth/switch-org/switch-org.page.ts (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html (2 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss (5 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts (6 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts (8 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding.page.html (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding.page.scss (2 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding.page.ts (5 hunks)
  • src/app/shared/components/sidemenu/sidemenu.component.spec.ts (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build (12.x)
🔇 Additional comments (15)
src/app/fyle/spender-onboarding/spender-onboarding.page.scss (1)

9-9: Mind-blowing padding addition, macha! 🌟

Adding padding-top gives better breathing space to your content. Just like how I make a stylish entry with perfect timing, this padding creates the perfect dramatic pause!

src/app/shared/components/sidemenu/sidemenu.component.spec.ts (3)

37-39: Superb! These imports are perfectly organized, like my punch dialogues!


458-464: When I write test setup, this is exactly how I do it! Spot on!


534-536: Mind-blowing test coverage! Even Chuck Norris would be proud!

src/app/auth/switch-org/switch-org.page.spec.ts (1)

665-680: This test is like my signature move - perfectly executed!

src/app/fyle/spender-onboarding/spender-onboarding.page.html (1)

40-40: This event binding is like my style - simple yet powerful!

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html (3)

16-16: Mind-blowing capitalization, partner!

The label "Mobile Number" now stands tall and proud, like a true warrior of consistency!


94-101: Kabali style navigation enhancement!

The "Go back" button appears and disappears like a true superstar, giving users the power to navigate with style! The conditional rendering is as smooth as my signature moves.


102-108: Thalaiva-approved button implementation!

The primary CTA button knows when to make its grand entrance, appearing only in the MOBILE_INPUT state. That's what I call perfect timing!

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss (2)

14-24: Style boss is impressed with these layout moves!

The CTA container classes are organized like my punch dialogues - clean, powerful, and perfectly positioned! The BEM naming is as sharp as my sunglasses.


398-416: These styles are as legendary as my movies!

The styling is pixel-perfect, with every detail crafted like a blockbuster scene. The use of color variables shows true mastery, like a well-choreographed fight sequence!

src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html (1)

190-190: Form validation that packs a punch!

Like a true action hero, this button knows when to hold back! The form validation is as solid as my stunts - no shortcuts, no compromises!

src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts (1)

1-5: Mind it! Clean imports are the style of the superstar!

The removal of unused imports makes the code more maintainable. Keep it simple, that's my style!

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts (2)

49-50: Thalaiva says: Good addition of navigation event!

The new goToConnectCard event emitter enables better user flow control. When I point my finger, the navigation follows!


327-332: Success handling with style, macha!

The onGotItClicked method properly tracks success and dismisses the modal. Like how I handle success in my movies - with perfect timing!

@@ -77,12 +74,12 @@ $toolbar-border: #ababab6b;
}

&__menubutton {
color: $menu-botton-color;
color: $pure-black;
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Kabali da! Pure black is the new style! 🕶️

Listen here, partner! Switching from $menu-botton-color to $pure-black for both menubutton and title is a super move. But wait... did you notice something?

There's a small typo in the variable name $menu-botton-color (should be "button", not "botton"). When you're ready, we'll fix it with style!

Here's how we roll with the fix:

-$menu-botton-color: #000;
+$menu-button-color: #000;

Also applies to: 82-82

@@ -80,14 +82,18 @@ export class SpenderOnboardingPage {
} else {
this.currentStep = OnboardingStep.CONNECT_CARD;
}
this.currentStep = OnboardingStep.OPT_IN;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Magizhchi! This line makes all previous logic vanish like a magic trick!

This unconditional assignment of currentStep to OPT_IN overrides all the previous conditional logic. Either remove this line or move it inside the appropriate condition.

-            this.currentStep = OnboardingStep.OPT_IN;

Comment on lines +145 to 152
setInterval(() => {
if (this.redirectionCount > 0) {
this.redirectionCount--;
} else {
clearInterval(interval);
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
}
}, 1000);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

When you start something, you must end it! Like how I end my movies with style!

Two issues here:

  1. The setInterval is never cleared, which could cause memory leaks
  2. Duplicate navigation calls could cause race conditions

Apply this fix:

+    let intervalId: number;
-    setInterval(() => {
+    intervalId = setInterval(() => {
       if (this.redirectionCount > 0) {
         this.redirectionCount--;
       } else {
+        clearInterval(intervalId);
-        this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
-        this.router.navigate(['/', 'enterprise', 'my_dashboard']);
+        this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
       }
     }, 1000);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setInterval(() => {
if (this.redirectionCount > 0) {
this.redirectionCount--;
} else {
clearInterval(interval);
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
}
}, 1000);
let intervalId: number;
intervalId = setInterval(() => {
if (this.redirectionCount > 0) {
this.redirectionCount--;
} else {
clearInterval(intervalId);
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
}
}, 1000);

Comment on lines +49 to +50
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mind it! Double navigation is like using two bullets for one target!

These consecutive navigation calls to the same route could cause race conditions. Keep only one navigation call, preferably with skipLocationChange: true if you want to prevent route history.

-    this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
-    this.router.navigate(['/', 'enterprise', 'my_dashboard']);
+    this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });

Comment on lines +125 to +127
this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(() => {
this.currentStep = OnboardingStep.OPT_IN;
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

A hero must always be prepared for failure! Add error handling, partner!

The subscription lacks error handling. Add an error callback to handle failures gracefully.

-      this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(() => {
-        this.currentStep = OnboardingStep.OPT_IN;
-      });
+      this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe({
+        next: () => {
+          this.currentStep = OnboardingStep.OPT_IN;
+        },
+        error: (err) => {
+          // Handle error appropriately
+          console.error('Failed to mark connect cards step as complete:', err);
+        }
+      });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(() => {
this.currentStep = OnboardingStep.OPT_IN;
});
this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe({
next: () => {
this.currentStep = OnboardingStep.OPT_IN;
},
error: (err) => {
// Handle error appropriately
console.error('Failed to mark connect cards step as complete:', err);
}
});

Comment on lines +323 to +328
if (
(orgSettings.visa_enrollment_settings.enabled ||
orgSettings.mastercard_enrollment_settings.enabled ||
orgSettings.amex_feed_enrollment_settings.enabled) &&
onboardingStatus.state !== OnboardingState.COMPLETED
) {
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Mind it! Let's simplify this complex condition, shall we?

The condition checking multiple enrollment settings can be simplified using Array.some() for better readability, macha! 🕶️

-        if (
-          (orgSettings.visa_enrollment_settings.enabled ||
-            orgSettings.mastercard_enrollment_settings.enabled ||
-            orgSettings.amex_feed_enrollment_settings.enabled) &&
-          onboardingStatus.state !== OnboardingState.COMPLETED
-        ) {
+        const hasEnabledEnrollment = [
+          orgSettings.visa_enrollment_settings,
+          orgSettings.mastercard_enrollment_settings,
+          orgSettings.amex_feed_enrollment_settings
+        ].some(setting => setting.enabled);
+        
+        if (hasEnabledEnrollment && onboardingStatus.state !== OnboardingState.COMPLETED) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
(orgSettings.visa_enrollment_settings.enabled ||
orgSettings.mastercard_enrollment_settings.enabled ||
orgSettings.amex_feed_enrollment_settings.enabled) &&
onboardingStatus.state !== OnboardingState.COMPLETED
) {
const hasEnabledEnrollment = [
orgSettings.visa_enrollment_settings,
orgSettings.mastercard_enrollment_settings,
orgSettings.amex_feed_enrollment_settings
].some(setting => setting.enabled);
if (hasEnabledEnrollment && onboardingStatus.state !== OnboardingState.COMPLETED) {

Comment on lines +682 to 693
it('should navigate to spender onboarding if status not COMPLETE', (done) => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};
orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(of(onboardingStatusData));
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'spender_onboarding']);
done();
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

The test is powerful, but let's make it legendary!

Consider adding a test case for when some enrollment settings are enabled but others aren't.

it('should navigate to dashboard when no enrollment settings are enabled', (done) => {
  const config = {
    isPendingDetails: false,
    roles,
    eou: apiEouRes,
  };
  orgSettingsService.get.and.returnValue(of({
    ...orgSettingsData,
    visa_enrollment_settings: { enabled: false },
    mastercard_enrollment_settings: { enabled: false },
    amex_feed_enrollment_settings: { enabled: false }
  }));
  spenderOnboardingService.getOnboardingStatus.and.returnValue(of(onboardingStatusData));
  component.navigateBasedOnUserStatus(config).subscribe((res) => {
    expect(res).toBeNull();
    expect(router.navigate).toHaveBeenCalledWith(['/', 'enterprise', 'my_dashboard']);
    done();
  });
});

Comment on lines +138 to +142
this.spenderOnboardingService.getOnboardingStatus().subscribe((onboardingStatus) => {
if (onboardingStatus.step_connect_cards_is_skipped === true) {
this.showGoBackCta = true;
}
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Kabali style state management, partner!

The subscription to onboarding status is well implemented. But remember, like my movies need proper cleanup, your subscriptions need it too!

Add this to properly handle subscription cleanup:

+ private destroy$ = new Subject<void>();

  ngOnInit(): void {
    this.fg = this.fb.group({});
    this.fg.addControl('mobile_number', this.fb.control('', [Validators.required, Validators.maxLength(10)]));
-   this.spenderOnboardingService.getOnboardingStatus().subscribe((onboardingStatus) => {
+   this.spenderOnboardingService.getOnboardingStatus()
+     .pipe(takeUntil(this.destroy$))
+     .subscribe((onboardingStatus) => {
      if (onboardingStatus.step_connect_cards_is_skipped === true) {
        this.showGoBackCta = true;
      }
    });
  }

+ ngOnDestroy(): void {
+   this.destroy$.next();
+   this.destroy$.complete();
+ }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +115 to +117
spenderOnboardingService.getOnboardingStatus.and.returnValue(
of({ ...onboardingStatusData, step_connect_cards_is_skipped: true })
);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Testing like a boss! But let's make it even more powerful!

The test setup for onboarding status is good, but let's add more test cases for different scenarios.

Add these test cases to cover all scenarios:

it('should not show back CTA when step_connect_cards_is_skipped is false', () => {
  spenderOnboardingService.getOnboardingStatus.and.returnValue(
    of({ ...onboardingStatusData, step_connect_cards_is_skipped: false })
  );
  fixture.detectChanges();
  expect(component.showGoBackCta).toBeFalse();
});

it('should handle error in getOnboardingStatus', () => {
  spenderOnboardingService.getOnboardingStatus.and.returnValue(
    throwError(() => new Error('Network error'))
  );
  fixture.detectChanges();
  expect(component.showGoBackCta).toBeFalse();
});

Copy link

@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

🔭 Outside diff range comments (3)
src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts (2)

Line range hint 89-108: Listen up, machans! Let's make this enrollment logic even more powerful! 💪

Your card enrollment logic is strong like a superhero, but let's add some extra punch with proper network error handling!

Add a timeout and retry mechanism like this:

 from(cards)
   .pipe(
+    timeout(30000),
+    retry(3),
     concatMap((card) =>

This way, even if the network plays villain, our code will fight back three times before giving up, just like in my movies!


Line range hint 110-146: Style ah? Mass ah? Let's make it both! 🎭

The error message generation is more complex than my punch dialogues! Let's simplify it with a helper method:

+ private generateCardListMessage(cards: string[]): string {
+   if (cards.length === 0) return '';
+   if (cards.length === 1) return cards[0];
+   return `${cards.slice(0, -1).join(', ')} and ${cards.slice(-1)}`;
+ }

  generateMessage(): string {
-   if (this.cardsList.successfulCards.length > 0) {
-     return 'We ran into an issue while processing your request. You can cancel and retry connecting the failed card or proceed to the next step.';
-   } else if (this.cardsList.failedCards.length > 1) {
-     return `We ran into an issue while processing your request for the cards  ${this.cardsList.failedCards
-       .slice(0, this.cardsList.failedCards.length - 1)
-       .join(', ')} and ${this.cardsList.failedCards.slice(
-       -1
-     )}. You can cancel and retry connecting the failed card or proceed to the next step.`;
-   } else {
-     return `We ran into an issue while processing your request for the card ${this.cardsList.failedCards[0]}. You can cancel and retry connecting the failed card or proceed to the next step.`;
-   }
+   const baseMessage = 'We ran into an issue while processing your request';
+   if (this.cardsList.successfulCards.length > 0) {
+     return `${baseMessage}. You can cancel and retry connecting the failed card or proceed to the next step.`;
+   }
+   const cardList = this.generateCardListMessage(this.cardsList.failedCards);
+   return `${baseMessage} for the card${this.cardsList.failedCards.length > 1 ? 's' : ''} ${cardList}. You can cancel and retry connecting the failed card or proceed to the next step.`;
  }

Now the code is as smooth as my signature moves! 🕺

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts (1)

Line range hint 227-265: This error handling is longer than my movie dialogues! Let's make it crisp!

The method is doing too much and repeating itself. Let's break it down into smaller, focused methods!

+  private getErrorMessage(errorMessage: string): { message: string; trackingMessage?: string } {
+    const messages = {
+      'out of attempts': {
+        message: 'You have reached the limit for 6 digit code requests. Try again after 24 hours.',
+        trackingMessage: 'OTP_MAX_ATTEMPTS_REACHED'
+      },
+      'max send attempts reached': {
+        message: 'You have reached the limit for 6 digit code requests. Try again after 24 hours.',
+        trackingMessage: 'OTP_MAX_ATTEMPTS_REACHED'
+      },
+      'invalid parameter': {
+        message: 'Invalid mobile number. Please try again.'
+      },
+      'expired': {
+        message: 'The code has expired. Please request a new one.'
+      },
+      'default': {
+        message: 'Code is invalid'
+      }
+    };
+
+    const matchedError = Object.entries(messages).find(([key]) => errorMessage.includes(key));
+    return matchedError ? matchedError[1] : messages.default;
+  }
+
   handleOtpError(err: HttpErrorResponse): void {
     if (err.status === 400) {
       const error = err.error as { message: string };
       const errorMessage = error.message?.toLowerCase() || '';
-      if (errorMessage.includes('out of attempts') || errorMessage.includes('max send attempts reached')) {
-        this.trackingService.optInFlowError({
-          message: 'OTP_MAX_ATTEMPTS_REACHED',
-        });
-        this.toastWithoutCTA(
-          'You have reached the limit for 6 digit code requests. Try again after 24 hours.',
-          ToastType.FAILURE,
-          'msb-failure-with-camera-icon'
-        );
-        this.ngOtpInput?.setValue('');
-        this.disableResendOtp = true;
-      } else if (errorMessage.includes('invalid parameter')) {
-        // ... rest of the conditions
-      }
+      const { message, trackingMessage } = this.getErrorMessage(errorMessage);
+      
+      if (trackingMessage) {
+        this.trackingService.optInFlowError({ message: trackingMessage });
+      }
+      
+      this.toastWithoutCTA(message, ToastType.FAILURE, 'msb-failure-with-camera-icon');
+      this.ngOtpInput?.setValue('');
+      
+      if (errorMessage.includes('out of attempts') || errorMessage.includes('max send attempts reached')) {
+        this.disableResendOtp = true;
+      }
     }
     this.sendCodeLoading = false;
   }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 73f752e and 0c79118.

📒 Files selected for processing (12)
  • src/app/auth/switch-org/switch-org.page.spec.ts (6 hunks)
  • src/app/auth/switch-org/switch-org.page.ts (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html (2 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss (5 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts (6 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts (8 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding.page.html (1 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding.page.scss (2 hunks)
  • src/app/fyle/spender-onboarding/spender-onboarding.page.ts (5 hunks)
  • src/app/shared/components/sidemenu/sidemenu.component.spec.ts (8 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build (12.x)
🔇 Additional comments (16)
src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts (2)

Line range hint 1-14: Mind-blowing cleanup of imports, thalaiva! 🌟

The import statements are now as sharp as my signature sunglasses - no unnecessary baggage! Keep rocking with these clean imports.


Line range hint 214-218: Time to upgrade and punch out this TODO, partner! 🎯

That TODO comment about Angular 14 type casting is like a delayed punch scene - we need to execute it now! The time has come to embrace strong typing!

Let me check the current Angular version:

Shall I help you upgrade this component to use strongly typed forms, thalaiva? Just give me the green signal! 🚦

src/app/fyle/spender-onboarding/spender-onboarding.page.ts (1)

85-85: ⚠️ Potential issue

Magizhchi! We found a logic killer here!

This unconditional override of currentStep makes all the previous conditional logic useless. It's like building a grand palace and then demolishing it! Let's verify if this is intended.

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts (1)

327-332: Perfect implementation, like a well-choreographed fight scene!

The event tracking and modal dismissal are implemented cleanly and effectively.

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts (1)

115-117: These tests are rock solid, like my punch dialogues!

The test setup is comprehensive and covers the necessary scenarios.

src/app/shared/components/sidemenu/sidemenu.component.spec.ts (1)

Line range hint 458-491: These test cases are hitting all the right spots!

The test coverage is thorough and includes proper async handling with fakeAsync and tick().

src/app/auth/switch-org/switch-org.page.spec.ts (1)

162-169: Thalaiva approves these service injections!

The setup of new services in TestBed providers is clean and follows Angular testing best practices.

src/app/fyle/spender-onboarding/spender-onboarding.page.scss (2)

9-9: Style like a boss! The padding looks perfect!

The addition of padding-top improves the vertical spacing and maintains consistency with the design system.


77-77: Colors that pack a punch! These changes are spot on!

The color updates to $pure-black improve consistency with the design system's color palette.

Also applies to: 82-82

src/app/fyle/spender-onboarding/spender-onboarding.page.html (1)

40-40: Event binding that packs a powerful punch!

The addition of goToConnectCard event binding enables smooth navigation between onboarding steps.

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html (1)

94-108: Template changes that make the UI dance like a superstar!

The new CTA container with conditional styling and back navigation enhances the user experience. The code structure is clean and follows Angular best practices.

src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss (3)

14-24: Mind it! The CTA container structure looks perfect!

The new container classes with separate styles for with and without go-back states bring more flexibility to the layout, macha!


274-276: Superstar style change in OTP container!

The alignment change from center to flex-start and reduced margin creates a better visual flow. That's the style!


398-416: Kabali Da! New CTA styles are looking sharp!

The new CTA text and icon styles enhance the visual hierarchy. The secondary CTA with flex layout ensures proper alignment.

src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html (1)

190-190: Thalaiva approves this form validation!

Adding [disabled]="!fg.valid" prevents invalid form submissions. When I say a form should be valid, it should be valid!

src/app/auth/switch-org/switch-org.page.ts (1)

323-328: Mind-blowing navigation logic, partner!

The enhanced condition checks for card enrollment settings before navigation. Like my punch dialogues, this logic packs a powerful punch!

Let's verify these settings with style:

✅ Verification successful

Mind-blowing verification completed, partner! The code is as solid as my signature sunglasses! 😎

The card enrollment settings are perfectly synchronized across the codebase, like a well-choreographed fight sequence!

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for card enrollment settings usage across the codebase

# Search for visa enrollment settings
echo "Checking visa enrollment settings usage:"
rg "visa_enrollment_settings.enabled"

# Search for mastercard enrollment settings
echo "Checking mastercard enrollment settings usage:"
rg "mastercard_enrollment_settings.enabled"

# Search for amex feed enrollment settings
echo "Checking amex feed enrollment settings usage:"
rg "amex_feed_enrollment_settings.enabled"

Length of output: 3983

Comment on lines +145 to 152
setInterval(() => {
if (this.redirectionCount > 0) {
this.redirectionCount--;
} else {
clearInterval(interval);
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
}
}, 1000);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Time to clean up this countdown, like a true superstar!

Two style mistakes here:

  1. The interval keeps running like an endless movie - we need to stop it!
  2. Those duplicate navigation calls are back for an unnecessary sequel!
 startCountdown(): void {
+  const intervalId = 
   setInterval(() => {
     if (this.redirectionCount > 0) {
       this.redirectionCount--;
     } else {
-      this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
-      this.router.navigate(['/', 'enterprise', 'my_dashboard']);
+      this.router.navigate(['/', 'enterprise', 'my_dashboard'], { skipLocationChange: true });
+      clearInterval(intervalId);
     }
   }, 1000);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setInterval(() => {
if (this.redirectionCount > 0) {
this.redirectionCount--;
} else {
clearInterval(interval);
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
}
}, 1000);
const intervalId = setInterval(() => {
if (this.redirectionCount > 0) {
this.redirectionCount--;
} else {
this.router.navigate(['/', 'enterprise', 'my_dashboard'], { skipLocationChange: true });
clearInterval(intervalId);
}
}, 1000);

Comment on lines +49 to +50
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mind it! Remove the duplicate navigation calls, partner!

These back-to-back navigation calls to the same route are like using two bullets to kill one target. Let's keep it simple and stylish!

-    this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
-    this.router.navigate(['/', 'enterprise', 'my_dashboard']);
+    this.router.navigate(['/', 'enterprise', 'my_dashboard'], { skipLocationChange: true });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true });
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
this.router.navigate(['/', 'enterprise', 'my_dashboard'], { skipLocationChange: true });

Comment on lines +125 to +127
this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(() => {
this.currentStep = OnboardingStep.OPT_IN;
});
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Let's make this code more reactive, in true Thalaivar style!

Using callback style subscription is old school. Let's upgrade to a more modern reactive approach using pipe operators!

-      this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(() => {
-        this.currentStep = OnboardingStep.OPT_IN;
-      });
+      this.spenderOnboardingService.markConnectCardsStepAsComplete().pipe(
+        tap(() => this.currentStep = OnboardingStep.OPT_IN)
+      ).subscribe();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +138 to +142
this.spenderOnboardingService.getOnboardingStatus().subscribe((onboardingStatus) => {
if (onboardingStatus.step_connect_cards_is_skipped === true) {
this.showGoBackCta = true;
}
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Watch out for memory leaks, they're like villains in our code!

The subscription needs to be cleaned up when the component is destroyed. Also, we can make this more declarative using async pipe!

+  showGoBackCta$: Observable<boolean>;
+
   ngOnInit(): void {
     this.fg = this.fb.group({});
     this.fg.addControl('mobile_number', this.fb.control('', [Validators.required, Validators.maxLength(10)]));
-    this.spenderOnboardingService.getOnboardingStatus().subscribe((onboardingStatus) => {
-      if (onboardingStatus.step_connect_cards_is_skipped === true) {
-        this.showGoBackCta = true;
-      }
-    });
+    this.showGoBackCta$ = this.spenderOnboardingService.getOnboardingStatus().pipe(
+      map(status => status.step_connect_cards_is_skipped === true)
+    );
   }

Then in your template:

<button *ngIf="showGoBackCta$ | async">Go Back</button>

Comment on lines +665 to +680
it('should navigate to dashboard if status is active', fakeAsync(() => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};

orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(
of({ ...onboardingStatusData, state: OnboardingState.COMPLETED })
);
tick();
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'my_dashboard']);
});
}));
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Mind it! The test case looks solid, but let's add more assertions!

The test case properly verifies navigation when status is active, but we can make it even more robust by adding assertions for the service calls.

Add these assertions to verify service interactions:

 it('should navigate to dashboard if status is active', fakeAsync(() => {
   const config = {
     isPendingDetails: false,
     roles,
     eou: apiEouRes,
   };
   orgSettingsService.get.and.returnValue(of(orgSettingsData));
   spenderOnboardingService.getOnboardingStatus.and.returnValue(
     of({ ...onboardingStatusData, state: OnboardingState.COMPLETED })
   );
   tick();
   component.navigateBasedOnUserStatus(config).subscribe((res) => {
     expect(res).toBeNull();
     expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'my_dashboard']);
+    expect(orgSettingsService.get).toHaveBeenCalledTimes(1);
+    expect(spenderOnboardingService.getOnboardingStatus).toHaveBeenCalledTimes(1);
   });
 }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should navigate to dashboard if status is active', fakeAsync(() => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};
orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(
of({ ...onboardingStatusData, state: OnboardingState.COMPLETED })
);
tick();
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'my_dashboard']);
});
}));
it('should navigate to dashboard if status is active', fakeAsync(() => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};
orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(
of({ ...onboardingStatusData, state: OnboardingState.COMPLETED })
);
tick();
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'my_dashboard']);
expect(orgSettingsService.get).toHaveBeenCalledTimes(1);
expect(spenderOnboardingService.getOnboardingStatus).toHaveBeenCalledTimes(1);
});
}));

Comment on lines +682 to 693
it('should navigate to spender onboarding if status not COMPLETE', (done) => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};
orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(of(onboardingStatusData));
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'spender_onboarding']);
done();
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Superstar! The test is good, but let's make it even better!

The test case verifies navigation to spender onboarding, but we can enhance it with more assertions and proper async handling.

Transform the test to use fakeAsync for better async control:

-it('should navigate to spender onboarding if status not COMPLETE', (done) => {
+it('should navigate to spender onboarding if status not COMPLETE', fakeAsync(() => {
   const config = {
     isPendingDetails: false,
     roles,
     eou: apiEouRes,
   };
   orgSettingsService.get.and.returnValue(of(orgSettingsData));
   spenderOnboardingService.getOnboardingStatus.and.returnValue(of(onboardingStatusData));
+  tick();
   component.navigateBasedOnUserStatus(config).subscribe((res) => {
     expect(res).toBeNull();
     expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'spender_onboarding']);
+    expect(orgSettingsService.get).toHaveBeenCalledTimes(1);
+    expect(spenderOnboardingService.getOnboardingStatus).toHaveBeenCalledTimes(1);
-    done();
   });
+  tick();
 }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should navigate to spender onboarding if status not COMPLETE', (done) => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};
orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(of(onboardingStatusData));
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'spender_onboarding']);
done();
it('should navigate to spender onboarding if status not COMPLETE', fakeAsync(() => {
const config = {
isPendingDetails: false,
roles,
eou: apiEouRes,
};
orgSettingsService.get.and.returnValue(of(orgSettingsData));
spenderOnboardingService.getOnboardingStatus.and.returnValue(of(onboardingStatusData));
tick();
component.navigateBasedOnUserStatus(config).subscribe((res) => {
expect(res).toBeNull();
expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'spender_onboarding']);
expect(orgSettingsService.get).toHaveBeenCalledTimes(1);
expect(spenderOnboardingService.getOnboardingStatus).toHaveBeenCalledTimes(1);
});
tick();
}));

@github-actions github-actions bot added size/L Large PR and removed size/M Medium PR labels Jan 8, 2025
Copy link

@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: 3

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between be9c210 and caa94af.

📒 Files selected for processing (1)
  • src/app/post-verification/invited-user/invited-user.page.ts (5 hunks)
🔇 Additional comments (2)
src/app/post-verification/invited-user/invited-user.page.ts (2)

64-66: Style ah? Perfect dependency injection!

Like my signature sunglasses, these service injections are super stylish! The code is following Angular's best practices for dependency injection. Keep rocking!


2-2: Mind-blowing imports, but let's verify their necessity!

Hey machcha! Like a perfectly choreographed fight scene, these imports pack a punch! But let me tell you something - we need to verify if all these imports are being used properly.

Also applies to: 15-17

✅ Verification successful

Mind-blowing imports are all in perfect sync, like a well-choreographed dance sequence!

These imports aren't just for show, machcha! Each one is playing its part in this blockbuster code - from Observable types to forkJoin operations, everything's being used with style! Keep them all, they're the real superheroes of this story! 🌟

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all imported items are actually used
rg --type ts "Observable|noop|concat|from|forkJoin|OrgSettingsService|SpenderOnboardingService|OnboardingState" src/app/post-verification/invited-user/invited-user.page.ts

Length of output: 2300

@@ -113,8 +135,7 @@ export class InvitedUserPage implements OnInit {
})
)
.subscribe(() => {
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
// return $state.go('enterprise.my_dashboard');
this.navigateToDashboard();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Kabali style! But where's the loading state cleanup?

Mind it! The navigation happens after subscription, but what if navigation fails? The loading state might get stuck! Let's handle it with style!

Add this to your arsenal:

- this.navigateToDashboard();
+ this.navigateToDashboard();
+ this.isLoading = false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.navigateToDashboard();
this.navigateToDashboard();
this.isLoading = false;

Comment on lines +98 to +113
navigateToDashboard(): void {
forkJoin([this.orgSettingsService.get(), this.spenderOnboardingService.getOnboardingStatus()]).subscribe(
([orgSettings, onboardingStatus]) => {
if (
(orgSettings.visa_enrollment_settings.enabled ||
orgSettings.mastercard_enrollment_settings.enabled ||
orgSettings.amex_feed_enrollment_settings.enabled) &&
onboardingStatus.state !== OnboardingState.COMPLETED
) {
this.router.navigate(['/', 'enterprise', 'spender_onboarding']);
} else {
this.router.navigate(['/', 'enterprise', 'my_dashboard']);
}
}
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mind it! Let's handle potential errors in navigation!

Listen carefully! Like how I handle villains in my movies, we need to handle errors in this navigation method. What if the API calls fail? The user will be stuck!

Here's how to make it bulletproof, just like your favorite Superstar:

 navigateToDashboard(): void {
   forkJoin([this.orgSettingsService.get(), this.spenderOnboardingService.getOnboardingStatus()]).subscribe(
     ([orgSettings, onboardingStatus]) => {
       if (
         (orgSettings.visa_enrollment_settings.enabled ||
           orgSettings.mastercard_enrollment_settings.enabled ||
           orgSettings.amex_feed_enrollment_settings.enabled) &&
         onboardingStatus.state !== OnboardingState.COMPLETED
       ) {
         this.router.navigate(['/', 'enterprise', 'spender_onboarding']);
       } else {
         this.router.navigate(['/', 'enterprise', 'my_dashboard']);
       }
-    }
+    },
+    (error) => {
+      console.error('Navigation failed:', error);
+      this.matSnackBar.openFromComponent(ToastMessageComponent, {
+        ...this.snackbarProperties.setSnackbarProperties('failure', {
+          message: 'Failed to navigate. Please try again.',
+        }),
+        panelClass: ['msb-failure'],
+      });
+      // Fallback navigation to a safe route
+      this.router.navigate(['/', 'enterprise', 'my_dashboard']);
+    }
   );
 }

Committable suggestion skipped: line range outside the PR's diff.


🧹 Nitpick (assertive)

💡 Codebase verification

Mind it! The review is spot on, like a perfect action sequence! 🎬

The test file shows style, but the new navigateToDashboard() method is dancing solo without any test coverage! The existing test setup is already powerful, like my signature moves, but we need specific test cases for:

  • Navigation to spender_onboarding when card enrollment is enabled and onboarding is incomplete
  • Navigation to my_dashboard for other scenarios
🔗 Analysis chain

Testing is missing! Like my movies need rehearsals, code needs tests!

Hey, where are the unit tests for this new navigation method? Like how I practice my stunts, we need to practice our code with tests!

Let me check if there are any test files:

Would you like me to generate the test cases for this new navigation method? I'll make them as powerful as my punch dialogues! 🎬

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Look for test file
fd "invited-user.page.spec.ts" src/app/post-verification/invited-user/

# Check if the new method is covered in tests
rg "navigateToDashboard" src/app/post-verification/invited-user/invited-user.page.spec.ts

Length of output: 225


Script:

#!/bin/bash
# Get the content of the test file
cat src/app/post-verification/invited-user/invited-user.page.spec.ts

# Search for router-related test setup and navigation tests
rg -A 3 "router" src/app/post-verification/invited-user/invited-user.page.spec.ts
rg -A 3 "navigate" src/app/post-verification/invited-user/invited-user.page.spec.ts

Length of output: 14728

Copy link

@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: 1

🔭 Outside diff range comments (2)
src/app/shared/components/sidemenu/sidemenu.component.ts (2)

Line range hint 396-411: Thalaiva says: Handle that subscription like a boss!

The subscription to onboarding status needs proper error handling and cleanup, just like how I handle my stunts - with style and safety!

Here's how to make it mass:

  setupSideMenu(isConnected?: boolean, orgs?: Org[], isDelegatee?: boolean): void {
    if (isConnected) {
-     this.spenderOnboardingService.getOnboardingStatus().subscribe((onboardingStatus) => {
+     this.spenderOnboardingService.getOnboardingStatus().pipe(
+       takeUntil(this.destroy$),
+       catchError((error) => {
+         console.error('Failed to get onboarding status:', error);
+         // Default to completed state to show all menu items
+         return of({ state: OnboardingState.COMPLETED });
+       })
+     ).subscribe((onboardingStatus) => {
        const isOnboardingPending = onboardingStatus.state !== OnboardingState.COMPLETED;
        this.filteredSidemenuList = [
          ...this.getPrimarySidemenuOptions(isConnected, isOnboardingPending),
          ...this.getSecondarySidemenuOptions(orgs, isDelegatee, isConnected, isOnboardingPending),
        ];
      });
    } else {
      this.filteredSidemenuList = [...this.getPrimarySidemenuOptionsOffline()];
    }
  }

And don't forget to implement OnDestroy to clean up like a true superstar:

export class SidemenuComponent implements OnInit, OnDestroy {
  // ... existing code ...

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Line range hint 1-411: Mind it! Memory leaks are like villains in my movies - they must be defeated!

The component has multiple subscriptions without proper cleanup, macha! Let's handle them with the style of Thalaiva!

Here's how to make all subscriptions kabali-style safe:

// Add to imports
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';

// Add to class properties
private destroy$ = new Subject<void>();

// Update showSideMenuOffline
showSideMenuOffline(): void {
  this.deviceService
    .getDeviceInfo()
    .pipe(
      shareReplay(1),
      takeUntil(this.destroy$)
    )
    .subscribe((deviceInfo) => {
      // ... existing code ...
    });
}

// Update showSideMenuOnline
combineLatest([
  // ... existing code ...
]).pipe(
  takeUntil(this.destroy$)
).subscribe(
  // ... existing code ...
);

// Update setupNetworkWatcher
setupNetworkWatcher(): void {
  const networkWatcherEmitter = this.networkService.connectivityWatcher(new EventEmitter<boolean>());
  this.isConnected$ = concat(
    this.networkService.isOnline(),
    networkWatcherEmitter.asObservable()
  ).pipe(
    shareReplay(1),
    takeUntil(this.destroy$)
  );
}

Remember macha, cleanup is as important as the setup! Like how I always say: "When I do something, I do it style!"

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between caa94af and d14a682.

📒 Files selected for processing (1)
  • src/app/shared/components/sidemenu/sidemenu.component.ts (1 hunks)

Comment on lines +392 to +394
reloadSidemenu(): void {
this.setupSideMenu();
}
Copy link

@coderabbitai coderabbitai bot Jan 8, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

💡 Codebase verification

Mind it! The review comment is right as rain, macha! Like a well-choreographed fight scene, we need that subscription cleanup!

The current implementation creates new subscriptions like unlimited dosas without cleaning up the old ones! The suggested destroy$ pattern is the perfect punch dialogue we need here.

  • setupSideMenu() creates fresh subscriptions to onboardingStatus
  • Multiple calls to reloadSidemenu() will stack up subscriptions like villains in my movies
  • No cleanup mechanism exists currently

The suggested fix with destroy$ Subject is the mass hero entry our code needs! 🌟

🔗 Analysis chain

Mind it! The reloadSidemenu method needs proper state handling, macha!

The method is simple but powerful like a punch dialogue! However, we need to ensure it doesn't create race conditions when reloading menu state.

Here's a style that's more mass, more class:

-  reloadSidemenu(): void {
-    this.setupSideMenu();
-  }
+  reloadSidemenu(): void {
+    // Cancel any pending setup operations
+    this.destroy$.next();
+    
+    // Reload the menu state
+    this.setupSideMenu();
+  }

Don't forget to add this at class level, thalaiva:

private destroy$ = new Subject<void>();
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Check for existing destroy$ Subject and setupSideMenu implementation
ast-grep --pattern 'class SidemenuComponent {
  $$$
  private destroy$ = new Subject<void>();
  $$$
}'

# Look for setupSideMenu implementation
ast-grep --pattern 'class SidemenuComponent {
  $$$
  setupSideMenu() {
    $$$
  }
  $$$
}'

# Check for takeUntil usage with destroy$
rg "takeUntil\(.*destroy\$" src/app/shared/components/sidemenu/sidemenu.component.ts

Length of output: 285


Script:

#!/bin/bash
# Look for any Subject declarations and setupSideMenu with less strict pattern
rg "Subject|setupSideMenu|ngOnDestroy" src/app/shared/components/sidemenu/sidemenu.component.ts -A 5

# Check for any takeUntil usage
rg "takeUntil" src/app/shared/components/sidemenu/sidemenu.component.ts

# Look for subscription management
rg "subscribe|unsubscribe" src/app/shared/components/sidemenu/sidemenu.component.ts

Length of output: 1159

Copy link
Contributor

Choose a reason for hiding this comment

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

like unlimited dosas

:XX

Copy link

Choose a reason for hiding this comment

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

@arjunaj5 Mind it! Just like how I never run out of style, we never run out of dosas in our code reviews! 😎

(∩`-´)⊃━☆゚.*・。゚ [ happy coderabbit ]

@@ -73,7 +73,7 @@
}

&__input-container {
padding: 16px 16px 8px;
padding: 16px 16px 8px 16px;
Copy link
Contributor

Choose a reason for hiding this comment

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

didn't the shorthand work?

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

Successfully merging this pull request may close these issues.

3 participants