-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
35c37cc
commit 6d86ba2
Showing
4 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
90 changes: 90 additions & 0 deletions
90
.../spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<div class="connect-card__body"> | ||
<div> | ||
<div class="connect-card__heading">Connect corporate card</div> | ||
<div class="connect-card__sub-heading"> | ||
This will help you bring your card transactions into Fyle as expenses instantly. | ||
</div> | ||
|
||
<div class="connect-card__input-label"> | ||
<span>Corporate card</span> | ||
</div> | ||
|
||
<div | ||
class="connect-card__input-inner-container" | ||
[ngClass]="{ 'connect-card__input-inner-container--error': cardForm.touched && cardForm.invalid }" | ||
> | ||
<input | ||
class="smartlook-show connect-card__card-number-input pl-0" | ||
inputmode="numeric" | ||
[formControl]="cardForm" | ||
mask="0000 0000 0000 0000" | ||
data-testid="card-number-input" | ||
appAutofocus | ||
[timeout]="500" | ||
required | ||
placeholder="Enter corporate card number" | ||
/> | ||
|
||
<ion-icon | ||
*ngIf="!cardType || cardType === cardNetworkTypes.OTHERS" | ||
src="../../../../assets/svg/card.svg" | ||
class="connect-card__input-default-icon" | ||
data-testid="default-icon" | ||
></ion-icon> | ||
|
||
<img | ||
*ngIf="cardType === cardNetworkTypes.VISA" | ||
src="../../../../assets/images/visa-logo.png" | ||
class="connect-card__input-visa-icon" | ||
data-testid="visa-icon" | ||
/> | ||
|
||
<img | ||
*ngIf="cardType === cardNetworkTypes.MASTERCARD" | ||
src="../../../../assets/images/mastercard-logo.png" | ||
class="connect-card__input-mastercard-icon" | ||
data-testid="mastercard-icon" | ||
/> | ||
</div> | ||
|
||
<div class="connect-card__input-error-space"></div> | ||
|
||
<div *ngIf="cardForm.touched && cardForm.invalid" class="connect-card__input-errors" data-testid="error-message"> | ||
<span *ngIf="cardForm.errors.invalidCardNumber">Please enter a valid card number.</span> | ||
|
||
<ng-container *ngIf="cardForm.errors.invalidCardNetwork"> | ||
<span *ngIf="isVisaRTFEnabled && isMastercardRTFEnabled; else visaOnlyOrg" | ||
>Enter a valid Visa or Mastercard number. If you have other cards, please contact your admin.</span | ||
> | ||
|
||
<ng-template #visaOnlyOrg> | ||
<!-- Check if only visa is enabled --> | ||
<span *ngIf="cardForm.errors.invalidCardNetwork && isVisaRTFEnabled; else mastercardOnlyOrg" | ||
>Enter a valid Visa number. If you have other cards, please contact your admin.</span | ||
> | ||
</ng-template> | ||
|
||
<ng-template #mastercardOnlyOrg> | ||
<!-- Check if only mastercard is enabled --> | ||
<span *ngIf="cardForm.errors.invalidCardNetwork && isMastercardRTFEnabled" | ||
>Enter a valid Mastercard number. If you have other cards, please contact your admin.</span | ||
> | ||
</ng-template> | ||
</ng-container> | ||
|
||
<span *ngIf="cardForm.errors.enrollmentError"> | ||
{{ enrollmentFailureMessage }} | ||
</span> | ||
</div> | ||
</div> | ||
<div> | ||
<ion-button | ||
class="btn-primary connect-card__primary-cta" | ||
fill="clear" | ||
aria-label="Navigate back to sign in page" | ||
role="button" | ||
> | ||
Continue | ||
</ion-button> | ||
</div> | ||
</div> |
180 changes: 180 additions & 0 deletions
180
.../spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
@import '../../../../theme/colors.scss'; | ||
|
||
.connect-card { | ||
position: relative; | ||
height: 100%; | ||
|
||
&__body { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
} | ||
&__heading { | ||
color: $black; | ||
font-size: 20px; | ||
font-weight: 500; | ||
line-height: normal; | ||
margin-bottom: 8px; | ||
margin-top: 32px; | ||
} | ||
|
||
&__sub-heading { | ||
color: $dark-grey; | ||
font-size: 14px; | ||
font-style: normal; | ||
font-weight: 400; | ||
line-height: 1.28; | ||
margin-bottom: 32px; | ||
} | ||
|
||
&__primary-cta { | ||
width: 108px; | ||
align-self: flex-end; | ||
} | ||
|
||
&__toolbar-title { | ||
font-size: 20px; | ||
font-weight: 500; | ||
color: $black; | ||
line-height: normal; | ||
} | ||
|
||
&__toolbar-close-btn { | ||
position: absolute; | ||
} | ||
|
||
&__body { | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
&__content { | ||
--padding-start: 16px; | ||
--padding-end: 16px; | ||
--padding-top: 16px; | ||
|
||
max-height: 50vh; | ||
} | ||
|
||
&__input-container { | ||
padding: 16px 16px 8px 16px; | ||
border-radius: 8px; | ||
border: 1px solid $grey; | ||
|
||
&:focus-within:not(&--error) { | ||
border: 1px solid $black-light; | ||
} | ||
} | ||
|
||
&__input-label { | ||
font-size: 12px; | ||
color: $black-light; | ||
margin-bottom: 6px; | ||
} | ||
|
||
&__input-inner-container { | ||
display: flex; | ||
align-items: center; | ||
border-bottom: 1px solid $grey; | ||
padding-bottom: 6px; | ||
margin-bottom: 2px; | ||
|
||
&--error { | ||
border-bottom: 1px solid $red; | ||
} | ||
|
||
&:focus-within:not(&--error) { | ||
border-bottom: 1px solid $black-light; | ||
} | ||
} | ||
|
||
&__card-number-input { | ||
border: 0; | ||
color: $blue-black; | ||
width: 100%; | ||
} | ||
|
||
&__input-default-icon { | ||
width: 20px; | ||
height: 20px; | ||
color: $grey-light; | ||
} | ||
|
||
&__input-visa-icon, | ||
&__input-mastercard-icon { | ||
width: 38px; | ||
height: 22px; | ||
} | ||
|
||
&__input-error-space { | ||
width: 0px; | ||
height: 16px; | ||
float: right; | ||
} | ||
|
||
&__input-errors { | ||
color: $red; | ||
font-size: 12px; | ||
line-height: 1.3; | ||
|
||
& > :not(:first-child) { | ||
// Only show one error message at a time | ||
display: none; | ||
} | ||
} | ||
|
||
&__view-tnc-btn { | ||
width: 100%; | ||
background: $pink-gradient; | ||
background-clip: text; | ||
color: transparent; | ||
padding: 12px 0; | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
gap: 8px; | ||
font-size: 14px; | ||
} | ||
|
||
&__view-tnc-btn-icon { | ||
width: 18px; | ||
height: 18px; | ||
color: $brand-primary; | ||
} | ||
|
||
&__tnc { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 24px; | ||
padding-top: 8px; | ||
font-size: 14px; | ||
} | ||
|
||
&__tnc-heading { | ||
font-weight: 500; | ||
color: $black; | ||
} | ||
|
||
&__tnc-list { | ||
margin: 0; | ||
padding-left: 20px; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 16px; | ||
} | ||
|
||
&__tnc-link { | ||
color: $blue-4; | ||
text-decoration: none; | ||
} | ||
|
||
&__tnc-link-icon { | ||
width: 16px; | ||
height: 16px; | ||
vertical-align: text-bottom; | ||
} | ||
|
||
&__footer-toolbar { | ||
padding: 16px; | ||
} | ||
} |
Empty file.
65 changes: 65 additions & 0 deletions
65
...ng/spender-onboarding-connect-card-step/spender-onboarding-connect-card-step.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Component, EventEmitter, Input, Output } from '@angular/core'; | ||
import { AbstractControl, FormControl, ValidationErrors } from '@angular/forms'; | ||
import { CardNetworkType } from 'src/app/core/enums/card-network-type'; | ||
import { OrgSettings } from 'src/app/core/models/org-settings.model'; | ||
import { CorporateCreditCardExpenseService } from 'src/app/core/services/corporate-credit-card-expense.service'; | ||
import { RealTimeFeedService } from 'src/app/core/services/real-time-feed.service'; | ||
|
||
@Component({ | ||
selector: 'app-spender-onboarding-connect-card-step', | ||
templateUrl: './spender-onboarding-connect-card-step.component.html', | ||
styleUrls: ['./spender-onboarding-connect-card-step.component.scss'], | ||
}) | ||
export class SpenderOnboardingConnectCardStepComponent { | ||
@Input() readOnly?: boolean = false; | ||
|
||
@Input() orgSettings: OrgSettings; | ||
|
||
@Output() isStepCompleted: EventEmitter<boolean> = new EventEmitter<boolean>(); | ||
|
||
cardForm: FormControl; | ||
|
||
isVisaRTFEnabled = false; | ||
|
||
isMastercardRTFEnabled = false; | ||
|
||
cardType = CardNetworkType; | ||
|
||
constructor( | ||
private corporateCreditCardExpensesService: CorporateCreditCardExpenseService, | ||
private realTimeFeedService: RealTimeFeedService | ||
) {} | ||
|
||
ionViewWillEnter(): void { | ||
this.cardForm = new FormControl('', [this.cardNumberValidator.bind(this), this.cardNetworkValidator.bind(this)]); | ||
} | ||
|
||
private cardNumberValidator(control: AbstractControl): ValidationErrors { | ||
// Reactive forms are not strongly typed in Angular 13, so we need to cast the value to string | ||
// TODO (Angular 14 >): Remove the type casting and directly use string type for the form control | ||
const cardNumber = control.value as string; | ||
|
||
const isValid = this.realTimeFeedService.isCardNumberValid(cardNumber); | ||
const cardType = this.realTimeFeedService.getCardTypeFromNumber(cardNumber); | ||
|
||
if (cardType === CardNetworkType.VISA || cardType === CardNetworkType.MASTERCARD) { | ||
return isValid && cardNumber.length === 16 ? null : { invalidCardNumber: true }; | ||
} | ||
|
||
return isValid ? null : { invalidCardNumber: true }; | ||
} | ||
|
||
private cardNetworkValidator(control: AbstractControl): ValidationErrors { | ||
const cardNumber = control.value as string; | ||
const cardType = this.realTimeFeedService.getCardTypeFromNumber(cardNumber); | ||
|
||
if ( | ||
(!this.isVisaRTFEnabled && cardType === CardNetworkType.VISA) || | ||
(!this.isMastercardRTFEnabled && cardType === CardNetworkType.MASTERCARD) | ||
) { | ||
return { invalidCardNetwork: true }; | ||
} | ||
|
||
return null; | ||
} | ||
} |