diff --git a/src/app/auth/switch-org/switch-org.page.spec.ts b/src/app/auth/switch-org/switch-org.page.spec.ts index 48a1a9361f..88bd205f07 100644 --- a/src/app/auth/switch-org/switch-org.page.spec.ts +++ b/src/app/auth/switch-org/switch-org.page.spec.ts @@ -44,6 +44,11 @@ import { DeepLinkService } from 'src/app/core/services/deep-link.service'; import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; import { transformedExpenseData } from 'src/app/core/mock-data/transformed-expense.data'; import { LaunchDarklyService } from 'src/app/core/services/launch-darkly.service'; +import { OrgSettingsService } from 'src/app/core/services/org-settings.service'; +import { orgSettingsData } from 'src/app/core/test-data/org-settings.service.spec.data'; +import { SpenderOnboardingService } from 'src/app/core/services/spender-onboarding.service'; +import { onboardingStatusData } from 'src/app/core/mock-data/onboarding-status.data'; +import { OnboardingState } from 'src/app/core/models/onboarding-state.enum'; const roles = ['OWNER', 'USER', 'FYLER']; const email = 'ajain@fyle.in'; @@ -73,6 +78,8 @@ describe('SwitchOrgPage', () => { let transactionService: jasmine.SpyObj; let expensesService: jasmine.SpyObj; let deepLinkService: jasmine.SpyObj; + let orgSettingsService: jasmine.SpyObj; + let spenderOnboardingService: jasmine.SpyObj; beforeEach(waitForAsync(() => { const platformSpy = jasmine.createSpyObj('Platform', ['is']); @@ -110,6 +117,8 @@ describe('SwitchOrgPage', () => { const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const deepLinkServiceSpy = jasmine.createSpyObj('DeepLinkService', ['getExpenseRoute']); const ldSpy = jasmine.createSpyObj('LaunchDarklyService', ['initializeUser']); + const orgSettingsServiceSpy = jasmine.createSpyObj('OrgSettingsService', ['get']); + const spenderOnboardingServiceSpy = jasmine.createSpyObj('SpenderOnboardingSettings', ['getOnboardingSettings']); TestBed.configureTestingModule({ declarations: [SwitchOrgPage, ActiveOrgCardComponent, OrgCardComponent, FyZeroStateComponent], @@ -150,6 +159,14 @@ describe('SwitchOrgPage', () => { provide: LoaderService, useValue: loaderServiceSpy, }, + { + provide: OrgSettingsService, + useValue: orgSettingsServiceSpy, + }, + { + provide: SpenderOnboardingService, + useValue: spenderOnboardingServiceSpy, + }, { provide: UserService, useValue: userServiceSpy, @@ -256,11 +273,14 @@ describe('SwitchOrgPage', () => { deepLinkService = TestBed.inject(DeepLinkService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; + spenderOnboardingService = TestBed.inject(SpenderOnboardingService) as jasmine.SpyObj; + orgSettingsService = TestBed.inject(OrgSettingsService) as jasmine.SpyObj; component.searchRef = fixture.debugElement.query(By.css('#search')); component.searchOrgsInput = fixture.debugElement.query(By.css('.smartlook-show')); component.contentRef = fixture.debugElement.query(By.css('.switch-org__content-container__content-block')); fixture.detectChanges(); + spyOn(component, 'navigateToDashboard').and.callThrough(); })); it('should create', () => { @@ -642,16 +662,34 @@ describe('SwitchOrgPage', () => { }); describe('navigateBasedOnUserStatus(): ', () => { - it('should navigate to dashboard if status is active', (done) => { + 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 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(); }); }); diff --git a/src/app/auth/switch-org/switch-org.page.ts b/src/app/auth/switch-org/switch-org.page.ts index b3e7b8f189..226de20f0f 100644 --- a/src/app/auth/switch-org/switch-org.page.ts +++ b/src/app/auth/switch-org/switch-org.page.ts @@ -31,7 +31,6 @@ import { TransactionService } from 'src/app/core/services/transaction.service'; import { DeepLinkService } from 'src/app/core/services/deep-link.service'; import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { LaunchDarklyService } from 'src/app/core/services/launch-darkly.service'; -import { OrgSettings } from 'src/app/core/models/org-settings.model'; import { OrgSettingsService } from 'src/app/core/services/org-settings.service'; import { SpenderOnboardingService } from 'src/app/core/services/spender-onboarding.service'; import { OnboardingState } from 'src/app/core/models/onboarding-state.enum'; @@ -321,7 +320,12 @@ export class SwitchOrgPage implements OnInit, AfterViewChecked { navigateToDashboard(openOptInDialog?: boolean): void { forkJoin([this.orgSettingsService.get(), this.spenderOnboardingService.getOnboardingStatus()]).subscribe( ([orgSettings, onboardingStatus]) => { - if (onboardingStatus.state !== OnboardingState.COMPLETED) { + 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([ diff --git a/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html b/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html index c5a56c32ed..535957b1b2 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html +++ b/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html @@ -187,6 +187,7 @@ role="button" (click)="enrollCards()" appFormButtonValidation + [disabled]="!fg.valid" [loading]="cardsEnrolling" [loadingText]="'Continue'" > diff --git a/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts b/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts index 3724562154..1ec5ceb89b 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts +++ b/src/app/fyle/spender-onboarding/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts @@ -1,18 +1,8 @@ import { HttpErrorResponse } from '@angular/common/http'; -import { - ChangeDetectorRef, - Component, - EventEmitter, - Input, - OnChanges, - OnInit, - Output, - SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, Validators } from '@angular/forms'; import { PopoverController } from '@ionic/angular'; -import { error } from 'console'; -import { catchError, concatMap, from, map, noop, of, switchMap, tap } from 'rxjs'; +import { catchError, concatMap, from, map, of } from 'rxjs'; import { CardNetworkType } from 'src/app/core/enums/card-network-type'; import { OrgSettings } from 'src/app/core/models/org-settings.model'; import { OverlayResponse } from 'src/app/core/models/overlay-response.modal'; diff --git a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html index b8b9bfec28..0e9d22ac7f 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html +++ b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.html @@ -13,7 +13,7 @@
- Mobile number + Mobile Number
-
- +
+
+ + Go back +
+ Continue
diff --git a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss index 4e8b360a35..678310c5f0 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss +++ b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.scss @@ -11,18 +11,17 @@ height: 100%; } - &__primary-cta-container { + &__cta-container { display: flex; flex-direction: row; - justify-content: flex-end; } - &__card-number-input { - width: fit-content !important; - margin-right: 24px; - &::placeholder { - word-spacing: 24px; - } + &__with-go-back { + justify-content: space-between; + } + + &__without-go-back { + justify-content: flex-end; } &__heading { @@ -46,6 +45,7 @@ &__primary-cta { width: 108px; align-self: flex-end; + margin-bottom: 20px; } &__toolbar-title { @@ -73,7 +73,7 @@ } &__input-container { - padding: 16px 16px 8px; + padding: 16px 16px 8px 16px; border-radius: 8px; border: 1px solid $grey; @@ -269,22 +269,11 @@ } } - &__primary-cta { - margin: 16px auto; - width: 90%; - - .mat-button-base { - width: 100%; - font-weight: 700; - min-height: 47px; - } - } - &__otp-container { display: flex; - justify-content: center; + justify-content: flex-start; align-items: center; - margin-bottom: 32px; + margin-bottom: 16px; &__label { margin: 0 8px 0 0; @@ -400,15 +389,29 @@ font-size: 14px; } } - - &__help-article-icon { - margin: 4px 0px 0px 6px; - width: 14px; - height: 14px; - } } &__footer { margin-bottom: calc(env(safe-area-inset-bottom)); } + + &__cta-text { + font-size: 14px; + font-weight: 500; + } + + &__arrow-icon { + margin-right: 6px; + height: 18px; + width: 18px; + } + + &__cta-secondary { + display: flex; + align-items: center; + justify-content: center; + color: $blue-black; + flex-direction: row; + padding: 16px 0; + } } diff --git a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts index b31b1913e1..48e405f93f 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts +++ b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.spec.ts @@ -23,6 +23,8 @@ import { snackbarPropertiesRes2 } from 'src/app/core/mock-data/snackbar-properti import { ToastMessageComponent } from 'src/app/shared/components/toast-message/toast-message.component'; import { UserEventService } from 'src/app/core/services/user-event.service'; import { FormBuilder } from '@angular/forms'; +import { SpenderOnboardingService } from 'src/app/core/services/spender-onboarding.service'; +import { onboardingStatusData } from 'src/app/core/mock-data/onboarding-status.data'; describe('SpenderOnboardingOptInStepComponent', () => { let component: SpenderOnboardingOptInStepComponent; @@ -35,8 +37,8 @@ describe('SpenderOnboardingOptInStepComponent', () => { let trackingService: jasmine.SpyObj; let matSnackbar: jasmine.SpyObj; let loaderService: jasmine.SpyObj; - let platformHandlerService: jasmine.SpyObj; let userEventService: jasmine.SpyObj; + let spenderOnboardingService: jasmine.SpyObj; let fb: FormBuilder; beforeEach(waitForAsync(() => { @@ -63,6 +65,7 @@ describe('SpenderOnboardingOptInStepComponent', () => { const browserHandlerServiceSpy = jasmine.createSpyObj('BrowserHandlerService', ['openLinkWithToolbarColor']); const platformHandlerServiceSpy = jasmine.createSpyObj('PlatformHandlerService', ['registerBackButtonAction']); const userEventServiceSpy = jasmine.createSpyObj('UserEventService', ['clearTaskCache']); + const spenderOnboardingServiceSpy = jasmine.createSpyObj('SpenderOnboardingService', ['getOnboardingStatus']); TestBed.configureTestingModule({ declarations: [SpenderOnboardingOptInStepComponent], @@ -80,6 +83,7 @@ describe('SpenderOnboardingOptInStepComponent', () => { { provide: BrowserHandlerService, useValue: browserHandlerServiceSpy }, { provide: PlatformHandlerService, useValue: platformHandlerServiceSpy }, { provide: UserEventService, useValue: userEventServiceSpy }, + { provide: SpenderOnboardingService, useValue: spenderOnboardingServiceSpy }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); @@ -96,7 +100,7 @@ describe('SpenderOnboardingOptInStepComponent', () => { trackingService = TestBed.inject(TrackingService) as jasmine.SpyObj; matSnackbar = TestBed.inject(MatSnackBar) as jasmine.SpyObj; loaderService = TestBed.inject(LoaderService) as jasmine.SpyObj; - platformHandlerService = TestBed.inject(PlatformHandlerService) as jasmine.SpyObj; + spenderOnboardingService = TestBed.inject(SpenderOnboardingService) as jasmine.SpyObj; userEventService = TestBed.inject(UserEventService) as jasmine.SpyObj; fb = TestBed.inject(FormBuilder); })); @@ -108,6 +112,9 @@ describe('SpenderOnboardingOptInStepComponent', () => { describe('ngOnInit():', () => { beforeEach(() => { component.eou = cloneDeep(eouRes2); + spenderOnboardingService.getOnboardingStatus.and.returnValue( + of({ ...onboardingStatusData, step_connect_cards_is_skipped: true }) + ); }); it('should not set mobileNumberInputValue if mobile number is not present in DB', () => { diff --git a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts index ad39317fc5..9aee16bb79 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts +++ b/src/app/fyle/spender-onboarding/spender-onboarding-opt-in-step/spender-onboarding-opt-in-step.component.ts @@ -27,6 +27,7 @@ import { LoaderService } from 'src/app/core/services/loader.service'; import { MobileNumberVerificationService } from 'src/app/core/services/mobile-number-verification.service'; import { OrgUserService } from 'src/app/core/services/org-user.service'; import { SnackbarPropertiesService } from 'src/app/core/services/snackbar-properties.service'; +import { SpenderOnboardingService } from 'src/app/core/services/spender-onboarding.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { UserEventService } from 'src/app/core/services/user-event.service'; import { ToastMessageComponent } from 'src/app/shared/components/toast-message/toast-message.component'; @@ -45,6 +46,8 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { @Output() isStepComplete: EventEmitter = new EventEmitter(); + @Output() goToConnectCard: EventEmitter = new EventEmitter(); + cardForm: FormControl; isVisaRTFEnabled = false; @@ -88,6 +91,8 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { hardwareBackButtonAction: Subscription; + showGoBackCta = false; + otpConfig: NgOtpInputConfig = { allowNumbersOnly: true, length: 6, @@ -109,7 +114,8 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { private loaderService: LoaderService, private matSnackBar: MatSnackBar, private userEventService: UserEventService, - private snackbarProperties: SnackbarPropertiesService + private snackbarProperties: SnackbarPropertiesService, + private spenderOnboardingService: SpenderOnboardingService ) {} get OptInFlowState(): typeof OptInFlowState { @@ -129,6 +135,11 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { 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; + } + }); } goBack(): void { @@ -184,6 +195,10 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { } } + goBackToConnectCard(): void { + this.goToConnectCard.emit(true); + } + handleOtpSuccess(otpDetails: Partial, action: string): void { this.otpAttemptsLeft = otpDetails.attempts_left; @@ -209,7 +224,7 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { this.sendCodeLoading = false; } - handleOtpError(err: HttpErrorResponse) { + handleOtpError(err: HttpErrorResponse): void { if (err.status === 400) { const error = err.error as { message: string }; const errorMessage = error.message?.toLowerCase() || ''; @@ -308,4 +323,11 @@ export class SpenderOnboardingOptInStepComponent implements OnInit, OnChanges { } }, 1000); } + + onGotItClicked(): void { + this.trackingService.optInFlowSuccess({ + message: 'SUCCESS', + }); + this.modalController.dismiss({ action: 'SUCCESS' }); + } } diff --git a/src/app/fyle/spender-onboarding/spender-onboarding.page.html b/src/app/fyle/spender-onboarding/spender-onboarding.page.html index 1aced5d9cc..f5603f4aa5 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding.page.html +++ b/src/app/fyle/spender-onboarding/spender-onboarding.page.html @@ -37,6 +37,7 @@
diff --git a/src/app/fyle/spender-onboarding/spender-onboarding.page.scss b/src/app/fyle/spender-onboarding/spender-onboarding.page.scss index 6cc4d11768..034a34e14b 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding.page.scss +++ b/src/app/fyle/spender-onboarding/spender-onboarding.page.scss @@ -1,15 +1,12 @@ @import '../../../theme/colors.scss'; $item-border-color: #e0e0e0; -$background-color: #fff; $menu-botton-color: #000; $toolbar-border: #ababab6b; -.spender-onboarding-container { -} - .spender-onboarding { background: $border-info-lighter; height: 100%; + padding-top: 20px; &__progress-bar { width: 50px; @@ -77,12 +74,12 @@ $toolbar-border: #ababab6b; } &__menubutton { - color: $menu-botton-color; + color: $pure-black; } &__title { line-height: 1.3; - color: $menu-botton-color; + color: $pure-black; } &__zero-state { diff --git a/src/app/fyle/spender-onboarding/spender-onboarding.page.spec.ts b/src/app/fyle/spender-onboarding/spender-onboarding.page.spec.ts index 2f82c6e147..8e69123985 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding.page.spec.ts +++ b/src/app/fyle/spender-onboarding/spender-onboarding.page.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { Router } from '@angular/router'; import { of } from 'rxjs'; import { SpenderOnboardingPage } from './spender-onboarding.page'; @@ -11,6 +11,7 @@ import { OnboardingStep } from './models/onboarding-step.enum'; import { orgSettingsData } from 'src/app/core/test-data/accounts.service.spec.data'; import { onboardingStatusData } from 'src/app/core/mock-data/onboarding-status.data'; import { extendedOrgUserResponse } from 'src/app/core/test-data/tasks.service.spec.data'; +import { OnboardingStepStatus } from 'src/app/core/models/onboarding-step-status.model'; describe('SpenderOnboardingPage', () => { let component: SpenderOnboardingPage; @@ -31,12 +32,13 @@ describe('SpenderOnboardingPage', () => { 'markConnectCardsStepAsComplete', 'skipSmsOptInStep', 'markSmsOptInStepAsComplete', + 'markWelcomeModalStepAsComplete', ]); const orgSettingsServiceSpy = jasmine.createSpyObj('OrgSettingsService', ['get']); const corporateCreditCardExpenseServiceSpy = jasmine.createSpyObj('CorporateCreditCardExpenseService', [ 'getCorporateCards', ]); - const routerSpy = jasmine.createSpyObj('Router', ['navigate']); + const routerSpy = jasmine.createSpyObj('Router', ['navigate', 'navigateByUrl']); await TestBed.configureTestingModule({ declarations: [SpenderOnboardingPage], @@ -84,23 +86,41 @@ describe('SpenderOnboardingPage', () => { }); }); - it('skipOnboardingStep(): should skip the current onboarding step', () => { + it('skipOnboardingStep(): should skip the current onboarding step', fakeAsync(() => { + const onboardingRequestResponse: OnboardingStepStatus = { + is_configured: false, + is_skipped: true, + }; component.currentStep = OnboardingStep.CONNECT_CARD; + spenderOnboardingService.skipConnectCardsStep.and.returnValue(of(onboardingRequestResponse)); component.skipOnboardingStep(); + tick(); expect(spenderOnboardingService.skipConnectCardsStep).toHaveBeenCalled(); component.currentStep = OnboardingStep.OPT_IN; + spenderOnboardingService.skipSmsOptInStep.and.returnValue(of(onboardingRequestResponse)); component.skipOnboardingStep(); + tick(); expect(spenderOnboardingService.skipSmsOptInStep).toHaveBeenCalled(); - }); + })); - it('markStepAsComplete(): should mark the current step as complete', () => { + it('markStepAsComplete(): should mark the current step as complete', fakeAsync(() => { + const onboardingRequestResponse: OnboardingStepStatus = { + is_configured: true, + is_skipped: false, + }; component.currentStep = OnboardingStep.CONNECT_CARD; + spenderOnboardingService.markConnectCardsStepAsComplete.and.returnValue(of(onboardingRequestResponse)); component.markStepAsComplete(); + tick(); expect(spenderOnboardingService.markConnectCardsStepAsComplete).toHaveBeenCalled(); component.currentStep = OnboardingStep.OPT_IN; + fixture.detectChanges(); + spenderOnboardingService.markSmsOptInStepAsComplete.and.returnValue(of(onboardingRequestResponse)); + spenderOnboardingService.markWelcomeModalStepAsComplete.and.returnValue(of({ is_complete: true })); component.markStepAsComplete(); + tick(); expect(spenderOnboardingService.markSmsOptInStepAsComplete).toHaveBeenCalled(); - }); + })); }); diff --git a/src/app/fyle/spender-onboarding/spender-onboarding.page.ts b/src/app/fyle/spender-onboarding/spender-onboarding.page.ts index b5b2ffae7e..c531dfb638 100644 --- a/src/app/fyle/spender-onboarding/spender-onboarding.page.ts +++ b/src/app/fyle/spender-onboarding/spender-onboarding.page.ts @@ -46,6 +46,8 @@ export class SpenderOnboardingPage { ) {} ionViewWillEnter(): void { + this.router.navigateByUrl('/enterprise/my_dashboard', { skipLocationChange: true }); + this.router.navigate(['/', 'enterprise', 'my_dashboard']); this.isLoading = true; from(this.loaderService.showLoader()) .pipe( @@ -81,13 +83,16 @@ export class SpenderOnboardingPage { this.currentStep = OnboardingStep.CONNECT_CARD; } } - this.currentStep = OnboardingStep.OPT_IN; this.isLoading = false; }) ) .subscribe(); } + goBackToConnectCard(): void { + this.currentStep = OnboardingStep.CONNECT_CARD; + } + skipOnboardingStep(): void { if (this.currentStep === OnboardingStep.CONNECT_CARD) { this.spenderOnboardingService @@ -107,7 +112,7 @@ export class SpenderOnboardingPage { switchMap(() => this.spenderOnboardingService.markWelcomeModalStepAsComplete()), map(() => { this.onboardingComplete = true; - this.startCountdown(); + this.router.navigate(['/', 'enterprise', 'my_dashboard']); }) ) .subscribe(); @@ -116,7 +121,9 @@ export class SpenderOnboardingPage { markStepAsComplete(): void { if (this.currentStep === OnboardingStep.CONNECT_CARD) { - this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(); + this.spenderOnboardingService.markConnectCardsStepAsComplete().subscribe(() => { + this.currentStep = OnboardingStep.OPT_IN; + }); } if (this.currentStep === OnboardingStep.OPT_IN) { this.onboardingInProgress = false; @@ -134,11 +141,11 @@ export class SpenderOnboardingPage { } startCountdown(): void { - const interval = setInterval(() => { + 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); diff --git a/src/app/post-verification/invited-user/invited-user.page.ts b/src/app/post-verification/invited-user/invited-user.page.ts index c81885eb4c..427e61a4d7 100644 --- a/src/app/post-verification/invited-user/invited-user.page.ts +++ b/src/app/post-verification/invited-user/invited-user.page.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, OnInit } from '@angular/core'; -import { Observable, noop, concat, from } from 'rxjs'; +import { Observable, noop, concat, from, forkJoin } from 'rxjs'; import { NetworkService } from 'src/app/core/services/network.service'; import { FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms'; import { switchMap, finalize, tap } from 'rxjs/operators'; @@ -12,6 +12,9 @@ import { TrackingService } from '../../core/services/tracking.service'; import { ToastMessageComponent } from 'src/app/shared/components/toast-message/toast-message.component'; import { MatSnackBar } from '@angular/material/snack-bar'; import { SnackbarPropertiesService } from 'src/app/core/services/snackbar-properties.service'; +import { OrgSettingsService } from 'src/app/core/services/org-settings.service'; +import { SpenderOnboardingService } from 'src/app/core/services/spender-onboarding.service'; +import { OnboardingState } from 'src/app/core/models/onboarding-state.enum'; @Component({ selector: 'app-invited-user', @@ -58,7 +61,9 @@ export class InvitedUserPage implements OnInit { private router: Router, private trackingService: TrackingService, private matSnackBar: MatSnackBar, - private snackbarProperties: SnackbarPropertiesService + private snackbarProperties: SnackbarPropertiesService, + private orgSettingsService: OrgSettingsService, + private spenderOnboardingService: SpenderOnboardingService ) {} ngOnInit(): void { @@ -90,6 +95,23 @@ export class InvitedUserPage implements OnInit { this.showPasswordTooltip = value; } + 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']); + } + } + ); + } + async saveData(): Promise { this.isLoading = true; this.fg.markAllAsTouched(); @@ -113,8 +135,7 @@ export class InvitedUserPage implements OnInit { }) ) .subscribe(() => { - this.router.navigate(['/', 'enterprise', 'my_dashboard']); - // return $state.go('enterprise.my_dashboard'); + this.navigateToDashboard(); }); } else { const message = `Please enter a valid ${!this.fg.controls.fullName.valid ? 'name' : 'password'}`; diff --git a/src/app/shared/components/sidemenu/sidemenu.component.spec.ts b/src/app/shared/components/sidemenu/sidemenu.component.spec.ts index d057d5553c..39643e262b 100644 --- a/src/app/shared/components/sidemenu/sidemenu.component.spec.ts +++ b/src/app/shared/components/sidemenu/sidemenu.component.spec.ts @@ -34,6 +34,9 @@ import { setSideMenuRes, } from 'src/app/core/mock-data/sidemenu.data'; import { delegatorData } from 'src/app/core/mock-data/platform/v1/delegator.data'; +import { SpenderOnboardingService } from 'src/app/core/services/spender-onboarding.service'; +import { onboardingStatusData } from 'src/app/core/mock-data/onboarding-status.data'; +import { OnboardingState } from 'src/app/core/models/onboarding-state.enum'; describe('SidemenuComponent', () => { let component: SidemenuComponent; @@ -50,6 +53,7 @@ describe('SidemenuComponent', () => { let orgService: jasmine.SpyObj; let authService: jasmine.SpyObj; let orgUserSettingsService: jasmine.SpyObj; + let spenderOnboardingService: jasmine.SpyObj; @Component({ selector: 'app-sidemenu', @@ -83,6 +87,7 @@ describe('SidemenuComponent', () => { const authServiceSpy = jasmine.createSpyObj('AuthService', ['getEou']); authServiceSpy.getEou.and.resolveTo(apiEouRes); const orgUserSettingsServiceSpy = jasmine.createSpyObj('OrgUserSettingsService', ['get']); + const spenderOnboardingServiceSpy = jasmine.createSpyObj('SpenderOnboardingService', ['getOnboardingStatus']); TestBed.configureTestingModule({ declarations: [SidemenuComponent], @@ -100,6 +105,7 @@ describe('SidemenuComponent', () => { { provide: OrgService, useValue: orgServiceSpy }, { provide: AuthService, useValue: authServiceSpy }, { provide: OrgUserSettingsService, useValue: orgUserSettingsServiceSpy }, + { provide: SpenderOnboardingService, useValue: spenderOnboardingServiceSpy }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], }).compileComponents(); @@ -116,6 +122,7 @@ describe('SidemenuComponent', () => { orgService = TestBed.inject(OrgService) as jasmine.SpyObj; authService = TestBed.inject(AuthService) as jasmine.SpyObj; orgUserSettingsService = TestBed.inject(OrgUserSettingsService) as jasmine.SpyObj; + spenderOnboardingService = TestBed.inject(SpenderOnboardingService) as jasmine.SpyObj; networkService.connectivityWatcher.and.returnValue(new EventEmitter()); @@ -448,8 +455,14 @@ describe('SidemenuComponent', () => { }); }); - describe('setupSideMenu()', () => { - it('should setup the side menu', () => { + describe('setupSideMenu(): ', () => { + beforeEach(() => { + spenderOnboardingService.getOnboardingStatus.and.returnValue( + of({ ...onboardingStatusData, state: OnboardingState.COMPLETED }) + ); + }); + + it('should setup the side menu', fakeAsync(() => { const getPrimarySidemenuOptionsSpy = spyOn(component, 'getPrimarySidemenuOptions').and.returnValue( getPrimarySidemenuOptionsRes1 ); @@ -458,22 +471,24 @@ describe('SidemenuComponent', () => { ); const resData = setSideMenuRes; component.setupSideMenu(true, orgData1, true); + tick(); fixture.detectChanges(); expect(component.filteredSidemenuList).toEqual(resData); expect(getPrimarySidemenuOptionsSpy).toHaveBeenCalledOnceWith(true, false); expect(getSecondarySidemenuOptionsSpy).toHaveBeenCalledOnceWith(orgData1, true, true, false); - }); + })); - it('should only get the primary options when there is no internet connection', () => { + it('should only get the primary options when there is no internet connection', fakeAsync(() => { const getPrimarySidemenuOptionsOfflineSpy = spyOn(component, 'getPrimarySidemenuOptionsOffline').and.returnValue( sidemenuData1 ); const resData = sidemenuData1; component.setupSideMenu(false, orgData1, false); fixture.detectChanges(); + tick(); expect(component.filteredSidemenuList).toEqual(resData); expect(getPrimarySidemenuOptionsOfflineSpy).toHaveBeenCalledTimes(1); - }); + })); }); describe('goToProfile():', () => { @@ -516,6 +531,9 @@ describe('SidemenuComponent', () => { orgUserService.getCurrent.and.returnValue(of(currentEouRes)); sidemenuService.getAllowedActions.and.returnValue(of(sidemenuAllowedActions)); + spenderOnboardingService.getOnboardingStatus.and.returnValue( + of({ ...onboardingStatusData, state: OnboardingState.COMPLETED }) + ); component.showSideMenuOnline(); tick(500); diff --git a/src/app/shared/components/sidemenu/sidemenu.component.ts b/src/app/shared/components/sidemenu/sidemenu.component.ts index f0d61b693c..c6e2850cb1 100644 --- a/src/app/shared/components/sidemenu/sidemenu.component.ts +++ b/src/app/shared/components/sidemenu/sidemenu.component.ts @@ -389,6 +389,10 @@ export class SidemenuComponent implements OnInit { } } + reloadSidemenu(): void { + this.setupSideMenu(); + } + setupSideMenu(isConnected?: boolean, orgs?: Org[], isDelegatee?: boolean): void { if (isConnected) { this.spenderOnboardingService.getOnboardingStatus().subscribe((onboardingStatus) => {