Skip to content

Commit

Permalink
feat: QBD direct main connection page business logic
Browse files Browse the repository at this point in the history
  • Loading branch information
DhaaraniCIT committed Nov 11, 2024
1 parent 20665aa commit c2ecefc
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ export type QBDPrerequisiteObject = {
}

export type SyncDataType = {
[key: string]: number | null;
attribute_type: string;
count: null | number
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { TestBed } from '@angular/core/testing';

import { QbdDirectConnectorService } from './qbd-direct-connector.service';

xdescribe('QbdDirectConnectorService', () => {
describe('QbdDirectConnectorServiceTsService', () => {
let service: QbdDirectConnectorService;

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Injectable } from '@angular/core';
import { ApiService } from '../../common/api.service';
import { WorkspaceService } from '../../common/workspace.service';
import { Observable } from 'rxjs';
import { QbdConnectorPost, QbdConnectorGet, SyncDataType } from 'src/app/core/models/qbd-direct/qbd-direct-configuration/qbd-direct-connector.model';

@Injectable({
providedIn: 'root'
})
export class QbdDirectConnectorService {

constructor(
private apiService: ApiService,
private workspaceService: WorkspaceService
) { }

postQbdDirectConntion(payload: QbdConnectorPost): Observable<QbdConnectorGet> {
return this.apiService.post(`/workspaces/${this.workspaceService.getWorkspaceId()}/connector_settings/`, payload);
}

syncAttribuites(): Observable<SyncDataType[]> {
return this.apiService.get(`/workspaces/${this.workspaceService.getWorkspaceId()}/qbd/attribute_stats/`, {});
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,48 @@
<!-- <app-qbd-direct-download-file></app-qbd-direct-download-file> -->
<!-- <app-qbd-direct-setup-connection></app-qbd-direct-setup-connection> -->
<!-- <app-qbd-direct-data-sync></app-qbd-direct-data-sync> -->
<div class="tw-pb-48-px">
<app-onboarding-steppers [onboardingSteps]="onboardingSteps"></app-onboarding-steppers>
<div>
<div *ngIf="isLoading" class="tw-flex tw-justify-center tw-items-center tw-pt-80-px">
<app-loader></app-loader>
</div>
<div *ngIf="!isLoading" class="configuration--contents tw-border-border-tertiary tw-mt-24-px" [ngClass]="{'tw-mx-120-px tw-shadow-app-card': brandingConfig.brandId === 'fyle', 'tw-mx-60-px tw-shadow-shadow-level-1': brandingConfig.brandId === 'co'}">
<div>
<app-configuration-step-header
[headerText]="brandingContent.configurationHeaderText"
[contentText]="brandingContent.configurationSubHeaderText"
[redirectLink]="redirectLink">
</app-configuration-step-header>
</div>
<div class="tw-px-24-px">
<div class="tw-py-24-px">
<app-qbd-direct-download-file
[isLoading]="isdownloadfileLoading"
[showDownloadLink]="showDownloadLink"
[isStepCompleted]="isDownloadStepCompleted"
[isCompanyPathInvalid]="isCompanyPathInvalid"
(nextStep)="proceedToConnection()"
(downloadClick)="triggerDownload($event)"
(retryClick)="retry()"
(manualDownload)="triggerManualDownload()"></app-qbd-direct-download-file>
</div>
<div class="tw-py-24-px">
<app-qbd-direct-setup-connection
[showSection]="isDownloadStepCompleted"
[password]="password"
[isLoading]="isconnectionLoading"
[connectionStatus]="connectionStatus"
[isStepCompleted]="isConnectionStepCompleted"
[isCTAEnabled]="isConnectionCTAEnabled"
(doneClick)="onConnectionDone($event)"
(nextClick)="proceedToSyncData()"></app-qbd-direct-setup-connection>
</div>
<div class="tw-py-24-px">
<app-qbd-direct-data-sync
[qbdFields]="qbdFields"
[isCTAEnabled]="isDataSyncCTADisabled"
[showSection]="isConnectionStepCompleted"
(continueClick)="proceedToExportSetting()"></app-qbd-direct-data-sync>
</div>
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,14 +1,192 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { QbdDirectSharedModule } from '../../qbd-direct-shared/qbd-direct-shared.module';
import { SharedModule } from 'src/app/shared/shared.module';
import { brandingConfig, brandingContent, brandingKbArticles } from 'src/app/branding/branding-config';
import { BrandingConfiguration } from 'src/app/core/models/branding/branding-configuration.model';
import { AppName, ConfigurationCta, QBDConnectionStatus, QbdDirectOnboardingState, QBDOnboardingState } from 'src/app/core/models/enum/enum.model';
import { OnboardingStepper } from 'src/app/core/models/misc/onboarding-stepper.model';
import { QbdDirectOnboardingModel } from 'src/app/core/models/qbd-direct/qbd-direct-configuration/qbd-direct-onboarding.model';
import { Router } from '@angular/router';
import { WorkspaceService } from 'src/app/core/services/common/workspace.service';
import { CommonModule } from '@angular/common';
import { QbdConnectorGet, SyncDataType } from 'src/app/core/models/qbd-direct/qbd-direct-configuration/qbd-direct-connector.model';
import { StorageService } from 'src/app/core/services/common/storage.service';
import { MinimalUser } from 'src/app/core/models/db/user.model';
import { QbdDirectWorkspace } from 'src/app/core/models/qbd-direct/db/qbd-direct-workspaces.model';
import { QbdDirectConnectorService } from 'src/app/core/services/qbd-direct/qbd-direct-configuration/qbd-direct-connector.service';
import { checkBoxEmit } from 'src/app/core/models/common/helper.model';
import { interval, switchMap, from, takeWhile } from 'rxjs';
import { QbdDirectTaskResponse } from 'src/app/core/models/qbd-direct/db/qbd-direct-task-log.model';

@Component({
selector: 'app-qbd-direct-onboarding-connector',
standalone: true,
imports: [QbdDirectSharedModule, SharedModule],
imports: [QbdDirectSharedModule, SharedModule, CommonModule],
templateUrl: './qbd-direct-onboarding-connector.component.html',
styleUrl: './qbd-direct-onboarding-connector.component.scss'
})
export class QbdDirectOnboardingConnectorComponent {
export class QbdDirectOnboardingConnectorComponent implements OnInit {

brandingContent = brandingContent.qbd_direct.configuration.connector;

onboardingSteps: OnboardingStepper[] = new QbdDirectOnboardingModel().getOnboardingSteps(this.brandingContent.stepName, this.workspaceService.getOnboardingState());

isLoading: boolean = true;

redirectLink: string = brandingKbArticles.onboardingArticles.QBD_DIRECT.CONNECTOR;

brandingConfig: BrandingConfiguration = brandingConfig;

ConfigurationCtaText = ConfigurationCta;

showDownloadLink: boolean;

isdownloadfileLoading: boolean;

isconnectionLoading: boolean;

isDownloadStepCompleted: boolean;

isConnectionStepCompleted: boolean;

xmlFileContent: string;

isCompanyPathInvalid: boolean = true;

password: string;

connectionStatus: QBDConnectionStatus;

isConnectionCTAEnabled: boolean;

isDialogVisible: boolean;

qbdFields: SyncDataType[];

isDataSyncCTADisabled: boolean;

user:MinimalUser = this.storageService.get('user');

constructor(
private router: Router,
private workspaceService: WorkspaceService,
private storageService: StorageService,
private qbdDirectConntorService: QbdDirectConnectorService
) { }

triggerDownload(filePath: string) {
const normalizedPath = filePath.replace(/\\\\/g, "\\");
const filePathRegex = /^(\/?|\.?\/?|[a-zA-Z]:\\)([a-zA-Z0-9_-]+[\\/])*[a-zA-Z0-9 _-]+\.qbw$/;
this.isCompanyPathInvalid = filePathRegex.test(normalizedPath);
if (this.isCompanyPathInvalid) {
this.isdownloadfileLoading = true;
this.qbdDirectConntorService.postQbdDirectConntion({file_location: normalizedPath}).subscribe((connectionResponse: QbdConnectorGet) => {
this.password = connectionResponse.password;
this.xmlFileContent = connectionResponse.qwc;
this.triggerManualDownload();
this.showDownloadLink = true;
});
this.isdownloadfileLoading = false;
}
}

triggerManualDownload() {
const blob = new Blob([this.xmlFileContent], { type: 'text/xml' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
const objectUrl = URL.createObjectURL(blob);
a.href = objectUrl;
a.download = 'fyle_quickbooks.qwc';
a.click();
URL.revokeObjectURL(objectUrl);
}

proceedToConnection() {
this.isdownloadfileLoading = true;
this.workspaceService.updateWorkspaceOnboardingState({onboarding_state: QbdDirectOnboardingState.PENDING_QWC_UPLOAD}).subscribe((workspaceResponse: QbdDirectWorkspace) => {
this.isDownloadStepCompleted = true;
this.isdownloadfileLoading = false;
});
}

retry() {
this.showDownloadLink = false;
}

onConnectionDone(event: checkBoxEmit) {
if (event.value) {
this.qbdDirectConntorService.syncAttribuites().subscribe((sd: SyncDataType[]) => {
this.qbdFields = sd;
this.isConnectionCTAEnabled = true;
// Interval(3000).pipe(
// SwitchMap(() => this.workspaceService.getWorkspace(this.user.org_id)), // Make HTTP request
// TakeWhile((status: any) => !this.isTerminalStatus(status.onboarding_state as QbdDirectOnboardingState), true) // Stop if terminal status is reached
// )
// .subscribe(
// (status) => this.handleStatus(status),
// (error) => console.error('Error polling workspace status:', error)
// );
});
}
}

handleStatus(status: QbdDirectWorkspace): void {
const onboardingState = status.onboarding_state;

if (onboardingState === QbdDirectOnboardingState.INCORRECT_COMPANY_PATH) {
// Set connection status, open dialog, and stop polling
this.connectionStatus = QBDConnectionStatus.INCORRECT_COMPANY_PATH;
this.isDialogVisible = true;
} else if (onboardingState === QbdDirectOnboardingState.IN_CORRECT_PASSWORD) {
// Set connection status, open dialog, and stop polling
this.connectionStatus = QBDConnectionStatus.IN_CORRECT_PASSWORD;
this.isDialogVisible = true;
} else if (onboardingState === QbdDirectOnboardingState.DESTINATION_SYNC_IN_PROGRESS || onboardingState === QbdDirectOnboardingState.DESTINATION_SYNC_COMPLETE) {
// Set success status, enable connection CTA, and stop polling
this.connectionStatus = QBDConnectionStatus.SUCCESS;
this.isConnectionCTAEnabled = true;
}
}

isTerminalStatus(status: QbdDirectOnboardingState): boolean {
return [QbdDirectOnboardingState.DESTINATION_SYNC_IN_PROGRESS, QbdDirectOnboardingState.IN_CORRECT_PASSWORD, QbdDirectOnboardingState.INCORRECT_COMPANY_PATH, QbdDirectOnboardingState.DESTINATION_SYNC_COMPLETE].includes(status);
}

proceedToSyncData() {
this.isConnectionStepCompleted = true;
this.isDataSyncCTADisabled= true;

// If DESTINATION_SYNC_IN_PROGRESS -> start polling workspaces/
// Set value to qbdFields when getting API response
// If DESTINATION_SYNC_COMPLETE => stop polling, set isDataSyncCTADisabled to false
}


proceedToExportSetting() {
this.isLoading = true;
this.workspaceService.updateWorkspaceOnboardingState({onboarding_state: QbdDirectOnboardingState.EXPORT_SETTINGS}).subscribe((workspaceResponse: QbdDirectWorkspace) => {
this.router.navigate([`/integrations/qbd_direct/onboarding/export_settings`]);
this.isLoading = false;
});
}


setupPage() {
this.workspaceService.getWorkspace(this.user.org_id).subscribe((workspaceResponse: QbdDirectWorkspace) => {
if (workspaceResponse.onboarding_state === QbdDirectOnboardingState.PENDING_QWC_UPLOAD) {
this.isDownloadStepCompleted = true;
this.isdownloadfileLoading = false;
} else if (workspaceResponse.onboarding_state === QbdDirectOnboardingState.DESTINATION_SYNC_IN_PROGRESS) {
this.isDownloadStepCompleted = true;
this.isConnectionStepCompleted = true;
this.isconnectionLoading = false;
this.isdownloadfileLoading = false;
}
this.isLoading = false;
});
}

ngOnInit(): void {
this.setupPage();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="tw-border tw-border-border-info-lighter">
<div class="tw-border tw-border-border-info-lighter" [ngClass]="{'tw-bg-border-info-lighter tw-opacity-60': !showSection}">
<div class="tw-p-24-px">
<div class="tw-flex tw-items-center tw-justify-between">
<div class="tw-flex tw-items-center tw-justify-start">
Expand All @@ -10,24 +10,24 @@
</div>
</div>
</div>
<div class="tw-py-24-px">
<div *ngFor="let field of getKeys(qbdFields); let i = index" class="tw-flex tw-items-center tw-justify-between tw-p-14-px" [ngClass]=" i < fieldLength-1 ? 'tw-border-b tw-border-b-divider-border-color' : '' ">
<div class="tw-py-24-px" *ngIf="showSection">
<div *ngFor="let field of qbdFields; let i = index" class="tw-flex tw-items-center tw-justify-between tw-p-14-px" [ngClass]=" i < fieldLength-1 ? 'tw-border-b tw-border-b-divider-border-color' : '' ">
<div>
<span class="tw-text-text-tertiary tw-font-400 tw-text-14-px">{{field}}</span>
<span class="tw-text-text-tertiary tw-font-400 tw-text-14-px">{{field.attribute_type | snakeCaseToSpaceCase | titlecase}}</span>
</div>
<div>
<span *ngIf="qbdFields[field] !== null" class="tw-text-text-tertiary tw-font-400 tw-text-14-px">{{qbdFields[field]}}</span>
<span *ngIf="field.count !== null" class="tw-text-text-tertiary tw-font-400 tw-text-14-px">{{field.count}}</span>
<div class="spin-icon tw-text-text-tertiary tw-font-400 tw-text-14-px">
<app-svg-icon *ngIf="qbdFields[field] === null" [svgSource]="'arrow-rotate-sync'" [width]="'14px'" [height]="'14px'" [isTextColorAllowed]="true" [styleClasses]="'tw-text-icon-tertiary'"></app-svg-icon>
<app-svg-icon *ngIf="field.count === null" [svgSource]="'arrow-rotate-sync'" [width]="'14px'" [height]="'14px'" [isTextColorAllowed]="true" [styleClasses]="'tw-text-icon-tertiary'"></app-svg-icon>
</div>
</div>
</div>
</div>
<div>
<div *ngIf="showSection">
<app-configuration-info-label [infoText]="'The values displayed here are synced from QuickBooks Desktop to Fyle. If you notice discrepancies in the synced values, you can continue onboarding; the sync will refresh automatically in 5 minutes to capture any missed values.'" [showIcon]="false"></app-configuration-info-label>
</div>
</div>
<div>
<div *ngIf="showSection">
<app-configuration-step-footer [ctaText]="ConfigurationCtaText.CONTINUE" [isButtonDisabled]="!isCTAEnabled" (save)="onContinueClick()"></app-configuration-step-footer>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { SharedModule } from 'src/app/shared/shared.module';
})
export class QbdDirectDataSyncComponent implements OnInit {

@Input({required: true}) qbdFields: SyncDataType;
@Input({required: true}) qbdFields: SyncDataType[];

@Input({required: true}) isCTAEnabled: boolean;

@Input({required: true}) showSection: boolean;

@Output() continueClick = new EventEmitter();

fieldLength: number;
Expand All @@ -27,12 +29,8 @@ export class QbdDirectDataSyncComponent implements OnInit {
this.continueClick.emit();
}

getKeys(obj: any): string[] {
return Object.keys(obj);
}

ngOnInit() {
this.fieldLength = Object.keys(this.qbdFields).length;
this.fieldLength = this.qbdFields.length;
}

}
Loading

0 comments on commit c2ecefc

Please sign in to comment.