-
Notifications
You must be signed in to change notification settings - Fork 15
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export enum OnboardingState { | ||
YET_TO_START = 'YET_TO_START', | ||
IN_PROGRESS = 'IN_PROGRESS', | ||
COMPLETED = 'COMPLETED', | ||
} | ||
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,12 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { OnboardingState } from './onboarding-state.enum'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+6
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
state: OnboardingState; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+3
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,4 @@ | ||||||||||||||||||||||||
export interface OnboardingStepStatus { | ||||||||||||||||||||||||
is_configured: boolean; | ||||||||||||||||||||||||
is_skipped: boolean; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+1
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,3 @@ | ||||||||||||||||||||
export interface OnboardingWelcomeStepStatus { | ||||||||||||||||||||
is_complete: boolean; | ||||||||||||||||||||
} | ||||||||||||||||||||
Comment on lines
+1
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,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) {} | ||||||||||||||||||||||||||||||||||
Comment on lines
+8
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! |
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
getOnboardingStatus(): Observable<OnboardingStepStatus> { | ||||||||||||||||||||||||||||||||||
return this.spenderPlatformV1ApiService | ||||||||||||||||||||||||||||||||||
.get<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding') | ||||||||||||||||||||||||||||||||||
.pipe(map((res) => res.data)); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+14
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Suggested change
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
processConnectCardsStep(data: OnboardingStepStatus): Observable<OnboardingStepStatus> { | ||||||||||||||||||||||||||||||||||
return this.spenderPlatformV1ApiService | ||||||||||||||||||||||||||||||||||
.post<PlatformApiResponse<OnboardingStepStatus>>('/spender/onboarding/process_step_connect_cards', { | ||||||||||||||||||||||||||||||||||
data, | ||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||
.pipe(map((res) => res.data)); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+20
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
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); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+49
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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);
}
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
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); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
})
) |
There was a problem hiding this comment.
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!
📝 Committable suggestion