import { Component, ElementRef, EventEmitter, NgZone, Output, ViewChild } from '@angular/core'; import { ConfigService } from '@arrow/bom/config'; import { BomService, SocketsService, MessageBusService, AnalyticsService, DialogService, AnalyticsEvent, ApiSocketListenAction, ApiSocketEmitAction } from '@arrow/bom/core'; import { TranslateService } from '@ngx-translate/core'; import { Observable, of } from 'rxjs'; import { catchError, first } from 'rxjs/operators'; @Component({ selector: 'app-quick-input', templateUrl: 'quick-input.component.html', styleUrls: ['quick-input.component.scss'] }) export class PartNumbersComponent { @Output() partNumberSubmit: EventEmitter = new EventEmitter(); @ViewChild('quickPartInput') quickPartInputElement: ElementRef; public loading = false; public partNumbers: string; public quickInputEnabled = true; public isMyArrow = this.configService.getTarget() === 'myarrow'; private defaultBomName = this.translateService.instant('New-BOM'); constructor( private analyticsService: AnalyticsService, private bomService: BomService, private socketsService: SocketsService, private messageBusService: MessageBusService, private dialogService: DialogService, private translateService: TranslateService, private configService: ConfigService, private zone: NgZone ) { } /** * Handles part numbers submit. If no parts, resets partNumbers and returns without creating bom. */ onSubmit(partNumbers: string): void { this.analyticsService.isBomNew = true; if (!partNumbers || partNumbers.trim().length === 0) { this.partNumbers = ''; return; } const parsedPartNumbers: string[] = this.parsePartsList(partNumbers); // Track BOM creation via parts list this.analyticsService.googleTrackEvent(AnalyticsEvent.CLICK, { category: 'BOM', action: 'Quick Input', label: parsedPartNumbers.length, value: parsedPartNumbers.length }); this.createBom(parsedPartNumbers); } /* this function uses two regex to control the max lines allowed in the bom part quick input - the regex in the replace() function finds all empty lines and its used to 'ignore' those lines because the blank lines will not be added to the BOM and do not count in the total. - the regex in the match() function its used to count all the lines with text that the partNumbers var has, when that count reaches the 3000 lines the submit button will be disabled. */ public checkCount() { var partCount = 0; if (this.partNumbers){ partCount = (this.partNumbers.replace(/^\s*[\r\n]/gm, '').trim().match(/\n/g) || []).length + 1; } this.quickInputEnabled = partCount <= 3000; } setQuickPartInputFocus() { this.quickPartInputElement?.nativeElement?.classList.add('bom-element-focused'); } unsetQuickPartInputFocus() { this.quickPartInputElement?.nativeElement?.classList.remove('bom-element-focused'); } /** * Parse the user submitted parts list string into an Array of part numbers; * @param parts - user submitted part numbers. * @returns array of part numbers */ private parsePartsList(parts: string): Array { return parts .split(/\r?\n/) .map((part: string) => part.trim()) .filter(part => part !== ''); } /** * creates a blank bom and then proceeds to add parts to that BOM * @param partsList - part numbers as FormData */ private createBom(partsList: string[]): void { this.messageBusService.to('ch:loader', { payload: true }); this.loading = true; this.bomService.createBom(this.defaultBomName).subscribe( response => { this.addPartsToBom(response.payload.bomId, partsList); sessionStorage.setItem('newBom', '1'); }, error => { this.dialogService.alert( this.translateService.instant('An-error-has-occurred'), error ); this.messageBusService.to('ch:loader', { payload: false }); } ); } /** * Submits a list of parts to add to the newly created BOM * @param bomId * @param parts */ private addPartsToBom(bomId: string, parts: string[]): void { this.listenToBomCreateSuccessOnce(bomId); this.socketsService .emitAction(ApiSocketEmitAction.ADD_MULTI_ROW, { bomId, quickInput: true, partNumbers: parts }) .pipe( catchError((err: any, caught: Observable) => { this.loading = false; this.messageBusService.to('ch:loader', { payload: false }); this.bomService.deleteBom(bomId).subscribe(); this.dialogService.alert( this.translateService.instant('BOM.BOM-Creation-Error'), this.translateService.instant('BOM.Something-went-wrong-when-creating-a-new-BOM.') + ' ' + this.translateService.instant('BOM.Please-try-again-later.') + ' ' + this.translateService.instant('BOM.If-the-problem-persists-please-contact-us.') ); return of('addMultiRow failed'); }), first() ) .subscribe(); } /** * Subscribes to a single sockets message of "bomCreateSuccess" so that as soon as * it's received, we know that it's safe to go to the first page of the BOM */ private listenToBomCreateSuccessOnce(bomId : string): void { this.socketsService .observeAction(ApiSocketListenAction.BOM_CREATE_SUCCESS) .pipe(first()) .subscribe(data => { this.zone.run(() => { if (data.result === true && bomId === data.id) { this.partNumberSubmit.emit(data.id); } else { this.dialogService.alert( this.translateService.instant('An-error-has-occurred') ); } this.loading = false; this.messageBusService.to('ch:loader', { payload: false }); }) }, () => { this.zone.run(() => { this.loading = false; this.messageBusService.to('ch:loader', { payload: false }); this.dialogService.alert(this.translateService.instant('An-error-has-occurred')); }) }); } }