All files / src/app/shared/components/basket/basket-cost-center-selection basket-cost-center-selection.component.ts

88.57% Statements 31/35
75% Branches 21/28
83.33% Functions 10/12
88.57% Lines 31/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 1062x 2x 2x   2x 2x   2x 2x   2x 2x                   2x 5x           5x     5x 5x 5x       5x   3x     5x       6x       4x 4x         5x   5x     5x     5x           4x 2x         5x                     4x 1x   4x                              
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, OnInit, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { UntypedFormGroup } from '@angular/forms';
import { FormlyFieldConfig } from '@ngx-formly/core';
import { Observable, combineLatest } from 'rxjs';
import { distinctUntilChanged, map, switchMap, take, withLatestFrom } from 'rxjs/operators';
 
import { AccountFacade } from 'ish-core/facades/account.facade';
import { CheckoutFacade } from 'ish-core/facades/checkout.facade';
import { SelectOption } from 'ish-core/models/select-option/select-option.model';
import { whenTruthy } from 'ish-core/utils/operators';
import { markAsDirtyRecursive } from 'ish-shared/forms/utils/form-utils';
 
/**
 * Assign a cost center for to the basket for business customers
 */
@Component({
  selector: 'ish-basket-cost-center-selection',
  templateUrl: './basket-cost-center-selection.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BasketCostCenterSelectionComponent implements OnInit {
  form = new UntypedFormGroup({});
  fields$: Observable<FormlyFieldConfig[]>;
  model: { costCenter: string };
 
  private costCenterOptions$: Observable<SelectOption[]>;
 
  private destroyRef = inject(DestroyRef);
 
  constructor(
    private checkoutFacade: CheckoutFacade,
    private accountFacade: AccountFacade,
    private cd: ChangeDetectorRef
  ) {}
 
  ngOnInit() {
    this.costCenterOptions$ = this.accountFacade.isBusinessCustomer$.pipe(
      whenTruthy(),
      switchMap(() => this.checkoutFacade.eligibleCostCenterSelectOptions$())
    );
 
    this.fields$ = combineLatest([
      this.costCenterOptions$,
      // retrigger field render when cost center is updated (maybe we don't need the placeholder anymore)
      this.checkoutFacade.basket$.pipe(
        map(basket => basket?.costCenter),
        distinctUntilChanged()
      ),
    ]).pipe(
      map(([options]) => options),
      map(options => (options.length ? this.getFields(options) : undefined)),
      whenTruthy()
    );
 
    // initialize model with the basket costCenter
    this.checkoutFacade.basket$
      .pipe(whenTruthy(), take(1), takeUntilDestroyed(this.destroyRef))
      .subscribe(basket => (this.model = { costCenter: basket.costCenter }));
 
    // save changes after form value changed
    this.form.valueChanges
      .pipe(
        whenTruthy(),
        map(val => val.costCenter),
        distinctUntilChanged(),
        withLatestFrom(this.checkoutFacade.basket$),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(([costCenter, basket]) => {
        if (costCenter !== basket.costCenter && !!costCenter) {
          this.checkoutFacade.updateBasketCostCenter(costCenter);
        }
      });
 
    // mark form as dirty to display validation errors
    this.checkoutFacade.basketValidationResults$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(results => {
      Iif (
        !results?.valid &&
        results?.errors?.find(error => error.code === 'basket.validation.cost_center_missing.error')
      ) {
        markAsDirtyRecursive(this.form);
        this.cd.markForCheck();
      }
    });
  }
  private getFields(options: SelectOption[]): FormlyFieldConfig[] {
    if (options.length === 1 && options[0].value && !this.model?.costCenter) {
      this.model = { ...this.model, costCenter: options[0].value };
    }
    return [
      {
        key: 'costCenter',
        type: 'ish-select-field',
        props: {
          label: 'checkout.cost_center.select.label',
          required: true,
          hideRequiredMarker: true,
          options,
          placeholder: options.length > 1 && !this.model?.costCenter ? 'account.option.select.text' : undefined,
        },
      },
    ];
  }
}