import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { debounceTime } from "rxjs/operators"; import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; import { CountryListItem, TaxInformation } from "@bitwarden/common/billing/models/domain"; @Component({ selector: "app-manage-tax-information", templateUrl: "./manage-tax-information.component.html", }) export class ManageTaxInformationComponent implements OnInit, OnDestroy { @Input() startWith: TaxInformation; @Input() onSubmit?: (taxInformation: TaxInformation) => Promise; /** * Emits when the tax information has changed. */ @Output() taxInformationChanged = new EventEmitter(); /** * Emits when the tax information has been updated. */ @Output() taxInformationUpdated = new EventEmitter(); private taxInformation: TaxInformation; protected formGroup = this.formBuilder.group({ country: ["", Validators.required], postalCode: ["", Validators.required], taxId: "", line1: "", line2: "", city: "", state: "", }); protected isTaxSupported: boolean; private destroy$ = new Subject(); protected readonly countries: CountryListItem[] = this.taxService.getCountries(); constructor( private formBuilder: FormBuilder, private taxService: TaxServiceAbstraction, ) {} getTaxInformation(): TaxInformation { return this.taxInformation; } submit = async () => { this.formGroup.markAllAsTouched(); if (this.formGroup.invalid) { return; } await this.onSubmit?.(this.taxInformation); this.taxInformationUpdated.emit(); }; validate = (): boolean => { this.formGroup.markAllAsTouched(); return this.formGroup.valid; }; async ngOnInit() { if (this.startWith) { this.formGroup.controls.country.setValue(this.startWith.country); this.formGroup.controls.postalCode.setValue(this.startWith.postalCode); this.isTaxSupported = this.startWith && this.startWith.country ? await this.taxService.isCountrySupported(this.startWith.country) : false; if (this.isTaxSupported) { this.formGroup.controls.taxId.setValue(this.startWith.taxId); this.formGroup.controls.line1.setValue(this.startWith.line1); this.formGroup.controls.line2.setValue(this.startWith.line2); this.formGroup.controls.city.setValue(this.startWith.city); this.formGroup.controls.state.setValue(this.startWith.state); } } this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((values) => { this.taxInformation = { country: values.country, postalCode: values.postalCode, taxId: values.taxId, line1: values.line1, line2: values.line2, city: values.city, state: values.state, }; }); this.formGroup.controls.country.valueChanges .pipe(debounceTime(1000), takeUntil(this.destroy$)) .subscribe((country: string) => { this.taxService .isCountrySupported(country) .then((isSupported) => (this.isTaxSupported = isSupported)) .catch(() => (this.isTaxSupported = false)) .finally(() => { if (!this.isTaxSupported) { this.formGroup.controls.taxId.setValue(null); this.formGroup.controls.line1.setValue(null); this.formGroup.controls.line2.setValue(null); this.formGroup.controls.city.setValue(null); this.formGroup.controls.state.setValue(null); } if (this.taxInformationChanged) { this.taxInformationChanged.emit(this.taxInformation); } }); }); this.formGroup.controls.postalCode.valueChanges .pipe(debounceTime(1000), takeUntil(this.destroy$)) .subscribe(() => { if (this.taxInformationChanged) { this.taxInformationChanged.emit(this.taxInformation); } }); this.formGroup.controls.taxId.valueChanges .pipe(debounceTime(1000), takeUntil(this.destroy$)) .subscribe(() => { if (this.taxInformationChanged) { this.taxInformationChanged.emit(this.taxInformation); } }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }