Skip to content

Commit

Permalink
feat: implementation - connect cards input UI (#3388)
Browse files Browse the repository at this point in the history
  • Loading branch information
bistaastha authored Dec 26, 2024
1 parent 0b4b3d7 commit 278fdff
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 0 deletions.
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 class="connect-card__primary-cta-container">
<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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
@import '../../../../theme/colors.scss';

.connect-card {
position: relative;
height: 100%;

&__body {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}

&__primary-cta-container {
display: flex;
flex-direction: row;
justify-content: flex-end;
}

&__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;
}
}
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;
}
}
4 changes: 4 additions & 0 deletions src/app/fyle/spender-onboarding/spender-onboarding.page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ $toolbar-border: #ababab6b;
padding: 20px 16px;
}

&__component-container {
height: 75%;
}

&__step-tracker {
padding: 24px;
border-radius: 32px 32px 0px 0px;
Expand Down

0 comments on commit 278fdff

Please sign in to comment.