import { Component, OnInit, OnDestroy, ViewChild, ViewContainerRef, ElementRef } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { select, Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; import { take } from 'rxjs/operators'; import { UserService, BomService, AnalyticsService, DialogService, DownloadService, QueryParams, AnalyticsEvent } from '@arrow/bom/core'; import { ConfigService } from '@arrow/bom/config'; import { FeatureFlagService } from '@arrow/bom/feature-flag'; import { Observable, Subscription } from 'rxjs'; import { LoadBoms, State as BomState, selectAll, Bom, DeleteBom } from '@arrow/bom/store'; import { LoggerService } from '@arrow/bom/logger'; declare var require: any; const pack = require('@arrow/bom/package.json'); // refers the file within /bom folder //import * as pack from '@arrow/bom/package.json'; // 'import' version did not for when loaded bom-tool lib in arrow.com/yarrow @Component({ templateUrl: 'management.component.html', styleUrls: ['management.component.scss'], }) export class ManagementComponent implements OnInit, OnDestroy { public isMyArrow = this.configService.getTarget() === 'myarrow'; region: string; searchingState: boolean; isAnonymous: boolean; partsFound: boolean; searchTerm = ''; isShowingMatchedBoms = false; subscriptions: Array = []; boms$: Observable; searchPlaceHolder: string; @ViewChild('myarrowSearch') myarrowSearchEl: ElementRef; @ViewChild('arrowcomSearch') arrowcomSearchEl: ElementRef; constructor( private router: Router, private activatedRoute: ActivatedRoute, private configService: ConfigService, private bomService: BomService, private analyticsService: AnalyticsService, private dialogService: DialogService, private downloadService: DownloadService, private store$: Store, private loggerService: LoggerService, private ffService: FeatureFlagService, private translateService: TranslateService, public userService: UserService, ) { if (ffService.maintenance) { this.router.navigate(['/bom/maintenance']); } } ngOnInit(): void { this.loggerService.info(`bomVersion: ${pack.version}`); this.subscriptions = new Array(); this.region = this.configService.region; this.userService.requestFreshAuth(); this.boms$ = this.store$.pipe(select(selectAll)); if (this.userService.isLoggedIn()) { this.store$.dispatch(new LoadBoms()); } else { this.userService .isReady() .pipe(take(1)) .subscribe(() => { this.isAnonymous = this.userService.isAnonymous; this.store$.dispatch(new LoadBoms()); }); } // WPT-10780 - resetting sockets clientId to prevent conflicting data returned // Removing next code fixed an issue with Part Deletion: // mng page -> connected to SignalR -> saved connectionId in localStorage -> bom view -> delete part [worked] -> navigate to mng page again -> localStorage key is removed -> bom view -> delete part [no longed worked] // TODO: analyze initial WPT-10780 issue more in case of errors after next line was disabled // localStorage.removeItem(this.config.clientIdLocalStorageKey); } ngOnDestroy(): void { this.subscriptions.forEach((sub: Subscription) => { if (sub) { sub.unsubscribe(); } }); this.subscriptions = null; // TODO double check why } // !Important: index was a parameter in bom-tool & Arrow.com but not MyArrow trackFn(index: number, bom: Bom): string { return bom.id; } /** * Directs the user to the Arrow.com login page, then returns them to the BOM tool on completion. * Triggered by a 'click' event in the html template. */ gotoLogin(): void { const url = encodeURIComponent(location.href); location.replace(`${this.configService.getRoute('login')}?url=${url}`); } /** * Directs the user to the Arrow.com registration page. * Triggered by a 'click' event in the html template. */ gotoRegister(): void { const url = encodeURIComponent(location.href); location.replace(`${this.configService.getRoute('register')}?gotoURL=${url}`); } /** * Tracks user interaction for the register button. * Triggered by a 'click' event in the html template. */ trackRegister($event: Event): void { $event.preventDefault(); const sub = this.analyticsService.track('register').subscribe( () => { this.gotoRegister(); sub.unsubscribe(); }, () => { this.gotoRegister(); sub.unsubscribe(); } ); } /** * Handles the API search call. * @param value Users search term. */ startGlobalPartSearch(partToSearch: any): void { partToSearch = partToSearch.trim(); if (partToSearch.length > 1 && partToSearch.length <= 100) { // TODO: If user type more than 100 characters it wouldn't see any error and it will fail. // Clear highlighted BOMs this.clearHighlight(); this.analyticsService.googleTrackEvent(AnalyticsEvent.CLICK, { category: 'BOM', action: 'Search BOMs', label: partToSearch, }); this.searchingState = true; // Call Service this.bomService.globalSearch(partToSearch).subscribe( bomIds => { this.searchingState = false; this.partsFound = !!bomIds.payload.length; this.isShowingMatchedBoms = true; this.applyHighlight(bomIds.payload); }, error => { this.searchingState = false; this.dialogService.alert( this.translateService.instant('An-error-has-occurred'), error ); } ); } } /** * Resets the part search in BOMs * Clears search text input field * Clears any highlighting of BOMs * @param searchInput */ clearSearch(): void { // Clear text this.searchTerm = ''; //accessibility feature, set focus back to the search field, specially useful for users using only keyboard. if(this.isMyArrow) { this.myarrowSearchEl?.nativeElement?.focus(); } else { this.arrowcomSearchEl?.nativeElement?.focus(); } // Clear highlight this.clearHighlight(); } /** * When the text in the part search input changes, run some checks and see if we should clear highlights */ handleSearchChange(isShowingMatchedBoms: boolean): void { // set to null to remove the message 'no results', useful for screen readers to be able to read the message every time. this.partsFound = null; if (isShowingMatchedBoms) { this.clearHighlight(); } } /** * Applies hightlight to BOM containing searched for MPN * @param bomIds Array of BOM ids reponse from API */ applyHighlight(bomIds: Array): void { this.boms$.subscribe(boms => { boms.forEach(bom => { // Applies highlight conditionally if (bomIds.find(id => bom.id === id)) bom.highlight = true; }); }); } /** * Clears all highlighted BOMs */ clearHighlight(): void { this.boms$.subscribe(boms => { boms.forEach(bom => (bom.highlight = false)); this.isShowingMatchedBoms = false; }); } onBomUpload(): void { this.router.navigate(['mapping'], { relativeTo: this.activatedRoute }); } viewBom(bomId: string): void { this.router.navigate(['view', bomId, 1, 25], { relativeTo: this.activatedRoute }); } deleteBom(event, id: string): void { event.stopPropagation(); this.store$.dispatch(new DeleteBom({ id })); } downloadBom(event, id: string): void { event.stopPropagation(); this.analyticsService.googleTrackEvent(AnalyticsEvent.CLICK, { category: 'BOM', action: 'Management Page Export', label: id, }); this.downloadService .downloadBom(id, new QueryParams({ includeCrosses: true })) .pipe(take(1)) .subscribe(() => {}); } }