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

feat: Add service for spender onboarding #3378

Merged
merged 2 commits into from
Dec 19, 2024

Conversation

bistaastha
Copy link
Contributor

@bistaastha bistaastha commented Dec 19, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a new onboarding state enumeration with statuses: YET_TO_START, IN_PROGRESS, and COMPLETED.
    • Added interfaces to represent onboarding status and step statuses, enhancing the structure for tracking onboarding progress.
    • Launched a new onboarding service to manage and process onboarding steps, including methods for retrieving status and marking steps as complete or skipped.
  • Documentation

    • Updated documentation to reflect the new onboarding-related structures and services.

Copy link

coderabbitai bot commented Dec 19, 2024

Walkthrough

In a superstar style, this pull request introduces a comprehensive onboarding system for the application! We've crafted a robust set of models and a service that tracks user onboarding with precision. The changes include an enumeration for onboarding states, interfaces for tracking onboarding status, and a dedicated service to manage the entire onboarding process with multiple steps and configurations.

Changes

File Change Summary
src/app/core/models/onboarding-*.ts Added multiple model files defining enums and interfaces for onboarding process
src/app/core/services/spender-onboarding.service.ts Created OnboardingService with methods to manage onboarding steps, retrieve status, and process user onboarding

Sequence Diagram

sequenceDiagram
    participant User
    participant OnboardingService
    participant API
    
    User->>OnboardingService: Start Onboarding
    OnboardingService->>API: Get Onboarding Status
    API-->>OnboardingService: Return Current Status
    OnboardingService->>User: Display Onboarding Steps
    User->>OnboardingService: Complete/Skip Steps
    OnboardingService->>API: Update Step Status
    API-->>OnboardingService: Confirm Update
Loading

Possibly related PRs

Suggested Labels

size/L

Suggested Reviewers

  • Chethan-Fyle
  • arjunaj5

Poem

Onboarding's dance, a digital stage 🚀
Steps configured with technological rage
From yet to start to completion's light
Our service guides with algorithmic might
Rajinikanth of code, breaking every cage! 💻✨


🪧 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 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 Dec 19, 2024
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: 10

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1fbec62 and 3980083.

📒 Files selected for processing (5)
  • src/app/core/models/onboarding-state.enum.ts (1 hunks)
  • src/app/core/models/onboarding-status.model.ts (1 hunks)
  • src/app/core/models/onboarding-step-status.model.ts (1 hunks)
  • src/app/core/models/onboarding-welcome-step-status.model.ts (1 hunks)
  • src/app/core/services/spender-onboarding.service.ts (1 hunks)

Comment on lines +1 to +3
export interface OnboardingWelcomeStepStatus {
is_complete: boolean;
}
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 interface looks perfect, but let's add some swagger with JSDoc!

The interface is clean and purposeful like my signature moves! But let me show you how to make it even more powerful, partner!

+/**
+ * Interface to track the completion status of the welcome step in user onboarding.
+ */
 export interface OnboardingWelcomeStepStatus {
   is_complete: boolean;
 }
📝 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
export interface OnboardingWelcomeStepStatus {
is_complete: boolean;
}
/**
* Interface to track the completion status of the welcome step in user onboarding.
*/
export interface OnboardingWelcomeStepStatus {
is_complete: boolean;
}

Comment on lines +1 to +4
export interface OnboardingStepStatus {
is_configured: boolean;
is_skipped: boolean;
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Magizhchi! Let's make this interface bulletproof like Thalaiva!

The interface is good, but we can make it legendary! When a step is skipped, it can't be configured, and vice versa. Let's add some type magic!

+/**
+ * Represents the status of an onboarding step with mutually exclusive states
+ */
-export interface OnboardingStepStatus {
-  is_configured: boolean;
-  is_skipped: boolean;
-}
+export type OnboardingStepStatus =
+  | { is_configured: true; is_skipped: false }
+  | { is_configured: false; is_skipped: true }
+  | { is_configured: false; is_skipped: 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
export interface OnboardingStepStatus {
is_configured: boolean;
is_skipped: boolean;
}
/**
* Represents the status of an onboarding step with mutually exclusive states
*/
export type OnboardingStepStatus =
| { is_configured: true; is_skipped: false }
| { is_configured: false; is_skipped: true }
| { is_configured: false; is_skipped: false };

Comment on lines +1 to +5
export enum OnboardingState {
YET_TO_START = 'YET_TO_START',
IN_PROGRESS = 'IN_PROGRESS',
COMPLETED = '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)

Kabali da! This enum is powerful, but let's give it some style!

The enum is perfect like a well-choreographed fight scene! But every superstar needs an introduction, so let's add some documentation!

+/**
+ * Represents the various states of the user onboarding process.
+ * 
+ * @enum {string}
+ * @property {string} YET_TO_START - Initial state before onboarding begins
+ * @property {string} IN_PROGRESS - User is actively going through onboarding
+ * @property {string} COMPLETED - User has finished the onboarding process
+ */
 export enum OnboardingState {
   YET_TO_START = 'YET_TO_START',
   IN_PROGRESS = 'IN_PROGRESS',
   COMPLETED = '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
export enum OnboardingState {
YET_TO_START = 'YET_TO_START',
IN_PROGRESS = 'IN_PROGRESS',
COMPLETED = 'COMPLETED',
}
/**
* Represents the various states of the user onboarding process.
*
* @enum {string}
* @property {string} YET_TO_START - Initial state before onboarding begins
* @property {string} IN_PROGRESS - User is actively going through onboarding
* @property {string} COMPLETED - User has finished the onboarding process
*/
export enum OnboardingState {
YET_TO_START = 'YET_TO_START',
IN_PROGRESS = 'IN_PROGRESS',
COMPLETED = 'COMPLETED',
}

Comment on lines +6 to +10
step_connect_cards_is_configured: boolean;
step_connect_cards_is_skipped: boolean;
step_sms_opt_in_is_configured: boolean;
step_sms_opt_in_is_skipped: boolean;
step_show_welcome_modal_is_complete: boolean;
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Style! Let's ensure our steps can't be both configured and skipped at the same time!

Like how I can't be both the hero and villain in the same scene, a step can't be both configured and skipped! Consider adding runtime validation.

Consider implementing a validation decorator or method to ensure that is_configured and is_skipped flags are not both true for the same step.

Comment on lines +3 to +12
export interface OnboardingStatus {
user_id: string;
org_id: string;
step_connect_cards_is_configured: boolean;
step_connect_cards_is_skipped: boolean;
step_sms_opt_in_is_configured: boolean;
step_sms_opt_in_is_skipped: boolean;
step_show_welcome_modal_is_complete: boolean;
state: OnboardingState;
}
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 add some powerful documentation to this interface, shall we?

Like a well-choreographed fight scene, every piece should know its role! Add JSDoc comments to explain the purpose of this interface and its properties.

+/**
+ * Represents the user's onboarding status and progress through various steps
+ */
 export interface OnboardingStatus {
+  /** Unique identifier of the user */
   user_id: string;
+  /** Organization identifier the user belongs to */
   org_id: string;
+  /** Indicates if cards connection step is configured */
   step_connect_cards_is_configured: boolean;
+  /** Indicates if cards connection step is skipped */
   step_connect_cards_is_skipped: boolean;
+  /** Indicates if SMS opt-in step is configured */
   step_sms_opt_in_is_configured: boolean;
+  /** Indicates if SMS opt-in step is skipped */
   step_sms_opt_in_is_skipped: boolean;
+  /** Indicates if welcome modal step is completed */
   step_show_welcome_modal_is_complete: boolean;
+  /** Current state of the onboarding process */
   state: OnboardingState;
 }
📝 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
export interface OnboardingStatus {
user_id: string;
org_id: string;
step_connect_cards_is_configured: boolean;
step_connect_cards_is_skipped: boolean;
step_sms_opt_in_is_configured: boolean;
step_sms_opt_in_is_skipped: boolean;
step_show_welcome_modal_is_complete: boolean;
state: OnboardingState;
}
/**
* Represents the user's onboarding status and progress through various steps
*/
export interface OnboardingStatus {
/** Unique identifier of the user */
user_id: string;
/** Organization identifier the user belongs to */
org_id: string;
/** Indicates if cards connection step is configured */
step_connect_cards_is_configured: boolean;
/** Indicates if cards connection step is skipped */
step_connect_cards_is_skipped: boolean;
/** Indicates if SMS opt-in step is configured */
step_sms_opt_in_is_configured: boolean;
/** Indicates if SMS opt-in step is skipped */
step_sms_opt_in_is_skipped: boolean;
/** Indicates if welcome modal step is completed */
step_show_welcome_modal_is_complete: boolean;
/** Current state of the onboarding process */
state: OnboardingState;
}

Comment on lines +20 to +26
processConnectCardsStep(data: OnboardingStepStatus): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.post<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding/process_step_connect_cards', {
data,
})
.pipe(map((res) => res.data));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Mind it! Let's avoid repeating ourselves like a broken record!

These process methods are more similar than my punch dialogues! Let's create a reusable helper method.

+private processStep<T>(endpoint: string, data: T): Observable<T> {
+  return this.spenderPlatformV1ApiService
+    .post<PlatformApiResponse<T>>(`/spender/onboarding/${endpoint}`, { data })
+    .pipe(
+      map((res) => res.data),
+      catchError((error) => {
+        console.error(`Failed to process step ${endpoint}:`, error);
+        return throwError(() => new Error(`Failed to process step ${endpoint}`));
+      })
+    );
+}

 processConnectCardsStep(data: OnboardingStepStatus): Observable<OnboardingStepStatus> {
-  return this.spenderPlatformV1ApiService
-    .post<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding/process_step_connect_cards', {
-      data,
-    })
-    .pipe(map((res) => res.data));
+  return this.processStep('process_step_connect_cards', data);
 }

Also applies to: 28-32, 34-40

Comment on lines +1 to +80
import { Injectable } from '@angular/core';
import { map, Observable } from 'rxjs';
import { SpenderPlatformV1ApiService } from './spender-platform-v1-api.service';
import { PlatformApiResponse } from '../models/platform/platform-api-response.model';
import { OnboardingWelcomeStepStatus } from '../models/onboarding-welcome-step-status.model';
import { OnboardingStepStatus } from '../models/onboarding-step-status.model';

@Injectable({
providedIn: 'root',
})
export class OnboardingService {
constructor(private spenderPlatformV1ApiService: SpenderPlatformV1ApiService) {}

getOnboardingStatus(): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.get<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding')
.pipe(map((res) => res.data));
}

processConnectCardsStep(data: OnboardingStepStatus): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.post<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding/process_step_connect_cards', {
data,
})
.pipe(map((res) => res.data));
}

processSmsOptInStep(data: OnboardingStepStatus): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.post<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding/process_step_sms_opt_in', { data })
.pipe(map((res) => res.data));
}

processWelcomeModalStep(data: OnboardingWelcomeStepStatus): Observable<OnboardingWelcomeStepStatus> {
return this.spenderPlatformV1ApiService
.post<PlatformApiResponse<OnboardingWelcomeStepStatus>>('/spender/onboarding/process_step_show_welcome_modal', {
data,
})
.pipe(map((res) => res.data));
}

markWelcomeModalStepAsComplete(): Observable<OnboardingWelcomeStepStatus> {
const data: OnboardingWelcomeStepStatus = {
is_complete: true,
};
return this.processWelcomeModalStep(data);
}

markConnectCardsStepAsComplete(): Observable<OnboardingStepStatus> {
const data: OnboardingStepStatus = {
is_configured: true,
is_skipped: false,
};
return this.processConnectCardsStep(data);
}

skipConnectCardsStep(): Observable<OnboardingStepStatus> {
const data: OnboardingStepStatus = {
is_configured: false,
is_skipped: true,
};
return this.processConnectCardsStep(data);
}

markSmsOptInStepAsComplete(): Observable<OnboardingStepStatus> {
const data: OnboardingStepStatus = {
is_configured: true,
is_skipped: false,
};
return this.processSmsOptInStep(data);
}

skipSmsOptInStep(): Observable<OnboardingStepStatus> {
const data: OnboardingStepStatus = {
is_configured: false,
is_skipped: true,
};
return this.processSmsOptInStep(data);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Time for some epic retry logic, thalaiva style!

Like how I never give up, let's add retry logic to our API calls!

Add the following import and apply retry operators to all API calls:

import { retry, retryWhen, delay, take } from 'rxjs/operators';

// Apply to all API calls
.pipe(
  retry({
    count: 3,
    delay: 1000,
    resetOnSuccess: true
  })
)

Comment on lines +8 to +12
@Injectable({
providedIn: 'root',
})
export class OnboardingService {
constructor(private spenderPlatformV1ApiService: SpenderPlatformV1ApiService) {}
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 is like my stunts - it needs to be perfect!

Don't forget to add unit tests for this service, covering all the epic scenarios!

Would you like me to generate comprehensive unit tests for this service? Just say the word!

Comment on lines +14 to +18
getOnboardingStatus(): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.get<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding')
.pipe(map((res) => res.data));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Magizhchi! But let's handle errors like a boss!

The API call needs proper error handling, partner! When things go wrong, we should handle it with style!

 getOnboardingStatus(): Observable<OnboardingStepStatus> {
   return this.spenderPlatformV1ApiService
     .get<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding')
-    .pipe(map((res) => res.data));
+    .pipe(
+      map((res) => res.data),
+      catchError((error) => {
+        console.error('Failed to fetch onboarding status:', error);
+        return throwError(() => new Error('Failed to fetch onboarding status'));
+      })
+    );
 }
📝 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
getOnboardingStatus(): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.get<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding')
.pipe(map((res) => res.data));
}
getOnboardingStatus(): Observable<OnboardingStepStatus> {
return this.spenderPlatformV1ApiService
.get<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding')
.pipe(
map((res) => res.data),
catchError((error) => {
console.error('Failed to fetch onboarding status:', error);
return throwError(() => new Error('Failed to fetch onboarding status'));
})
);
}

Comment on lines +49 to +63
markConnectCardsStepAsComplete(): Observable<OnboardingStepStatus> {
const data: OnboardingStepStatus = {
is_configured: true,
is_skipped: false,
};
return this.processConnectCardsStep(data);
}

skipConnectCardsStep(): Observable<OnboardingStepStatus> {
const data: OnboardingStepStatus = {
is_configured: false,
is_skipped: true,
};
return this.processConnectCardsStep(data);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Like my signature moves, let's make our step processing more robust!

The step completion methods could use some validation to prevent invalid states.

 markConnectCardsStepAsComplete(): Observable<OnboardingStepStatus> {
+  if (this.currentStep !== 'connect_cards') {
+    return throwError(() => new Error('Invalid step sequence'));
+  }
   const data: OnboardingStepStatus = {
     is_configured: true,
     is_skipped: false,
   };
   return this.processConnectCardsStep(data);
 }

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

Copy link

Unit Test Coverage % values
Statements 95.99% ( 19302 / 20108 )
Branches 91.12% ( 10664 / 11703 )
Functions 94.25% ( 5740 / 6090 )
Lines 96.03% ( 18432 / 19194 )

@bistaastha bistaastha merged commit d4fd372 into FYLE-86cx2t82k-base-feature-branch Dec 19, 2024
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size/M Medium PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants