File

feature-libs/cart/saved-cart/components/saved-cart-form-dialog/saved-cart-form-dialog.component.ts

Implements

OnInit OnDestroy

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector cx-saved-cart-form-dialog
templateUrl ./saved-cart-form-dialog.component.html

Index

Properties
Methods
HostListeners
Accessors

Constructor

constructor(launchDialogService: LaunchDialogService, el: ElementRef, savedCartService: SavedCartFacade, eventService: EventService, routingService: RoutingService, globalMessageService: GlobalMessageService)
Parameters :
Name Type Optional
launchDialogService LaunchDialogService No
el ElementRef No
savedCartService SavedCartFacade No
eventService EventService No
routingService RoutingService No
globalMessageService GlobalMessageService No

HostListeners

click
Arguments : '$event'
click(event: UIEvent)

Methods

Protected build
build(cart?: Cart)
Parameters :
Name Type Optional
cart Cart Yes
Returns : void
close
close(reason: string)
Parameters :
Name Type Optional
reason string No
Returns : void
deleteCart
deleteCart(cartId: string)
Parameters :
Name Type Optional
cartId string No
Returns : void
handleClick
handleClick(event: UIEvent)
Decorators :
@HostListener('click', ['$event'])
Parameters :
Name Type Optional
event UIEvent No
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onComplete
onComplete(success: boolean)
Parameters :
Name Type Optional
success boolean No
Returns : void
Protected patchData
patchData(item?: any)
Parameters :
Name Type Optional
item any Yes
Returns : void
Private resetSavedCartStates
resetSavedCartStates()
Returns : void
restoreSavedCart
restoreSavedCart(cartId: string)
Parameters :
Name Type Optional
cartId string No
Returns : void
saveOrEditCart
saveOrEditCart(cartId: string)
Parameters :
Name Type Optional
cartId string No
Returns : void
toggleIsCloneSavedCart
toggleIsCloneSavedCart()
Returns : boolean

Properties

cart
Type : Cart
descriptionMaxLength
Type : number
Default value : 250
focusConfig
Type : FocusConfig
Default value : { trap: true, block: true, autofocus: 'button', focusOnEscape: true, }
form
Type : FormGroup
iconTypes
Default value : ICON_TYPE
isCloneSavedCart
Default value : false
isDisableDeleteButton$
Type : Observable<boolean>
isDisableRestoreButton$
Type : Observable<boolean>
isLoading$
Type : Observable<boolean>
layoutOption
Type : string | undefined
nameMaxLength
Type : number
Default value : 50
savedCartFormType
Default value : SavedCartFormType
Private subscription
Default value : new Subscription()

Accessors

descriptionsCharacterLeft
getdescriptionsCharacterLeft()
import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  HostListener,
  OnDestroy,
  OnInit,
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import {
  DeleteSavedCartEvent,
  DeleteSavedCartFailEvent,
  DeleteSavedCartSuccessEvent,
  SavedCartFacade,
  SavedCartFormType,
} from '@spartacus/cart/saved-cart/root';
import {
  Cart,
  EventService,
  GlobalMessageService,
  GlobalMessageType,
  RoutingService,
} from '@spartacus/core';
import {
  FocusConfig,
  ICON_TYPE,
  LaunchDialogService,
} from '@spartacus/storefront';
import { combineLatest, merge, Observable, Subscription } from 'rxjs';
import { map, mapTo, take } from 'rxjs/operators';

export interface SavedCartFormDialogOptions {
  cart: Cart;
  layoutOption?: string;
}

@Component({
  selector: 'cx-saved-cart-form-dialog',
  templateUrl: './saved-cart-form-dialog.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SavedCartFormDialogComponent implements OnInit, OnDestroy {
  private subscription = new Subscription();
  savedCartFormType = SavedCartFormType;
  form: FormGroup;
  iconTypes = ICON_TYPE;
  cart: Cart;
  layoutOption: string | undefined;

  descriptionMaxLength: number = 250;
  nameMaxLength: number = 50;
  isCloneSavedCart = false;

  focusConfig: FocusConfig = {
    trap: true,
    block: true,
    autofocus: 'button',
    focusOnEscape: true,
  };

  isLoading$: Observable<boolean>;
  isDisableDeleteButton$: Observable<boolean>;
  isDisableRestoreButton$: Observable<boolean>;

  get descriptionsCharacterLeft(): number {
    return (
      this.descriptionMaxLength -
      (this.form.get('description')?.value?.length || 0)
    );
  }

  @HostListener('click', ['$event'])
  handleClick(event: UIEvent): void {
    // Close on click outside the dialog window
    if ((event.target as any).tagName === this.el.nativeElement.tagName) {
      this.close('Cross click');
    }
  }

  constructor(
    protected launchDialogService: LaunchDialogService,
    protected el: ElementRef,
    protected savedCartService: SavedCartFacade,
    protected eventService: EventService,
    protected routingService: RoutingService,
    protected globalMessageService: GlobalMessageService
  ) {}

  ngOnInit(): void {
    this.resetSavedCartStates();

    this.isLoading$ = this.savedCartService.getSaveCartProcessLoading();

    this.isDisableDeleteButton$ = merge(
      this.eventService.get(DeleteSavedCartEvent).pipe(take(1), mapTo(true)),
      this.eventService
        .get(DeleteSavedCartFailEvent)
        .pipe(take(1), mapTo(false))
    );

    this.isDisableRestoreButton$ = combineLatest([
      this.savedCartService.getCloneSavedCartProcessLoading(),
      this.savedCartService.getRestoreSavedCartProcessLoading(),
    ]).pipe(
      map(
        ([isCloneLoading, isRestoreLoading]) =>
          isCloneLoading || isRestoreLoading
      )
    );

    this.subscription.add(
      this.launchDialogService.data$.subscribe(
        (data: SavedCartFormDialogOptions) => {
          this.cart = data.cart;
          this.layoutOption = data.layoutOption;

          this.build(this.cart);
        }
      )
    );

    this.subscription.add(
      this.savedCartService
        .getSaveCartProcessSuccess()
        .subscribe((success) => this.onComplete(success))
    );

    this.subscription.add(
      this.eventService
        .get(DeleteSavedCartSuccessEvent)
        .pipe(take(1), mapTo(true))
        .subscribe((success) => this.onComplete(success))
    );

    this.subscription.add(
      this.savedCartService
        .getRestoreSavedCartProcessSuccess()
        .subscribe((success) => this.onComplete(success))
    );
  }

  saveOrEditCart(cartId: string): void {
    const name = this.form.get('name')?.value;
    // TODO(#12660): Remove default value once backend is updated
    const description = this.form.get('description')?.value || '-';

    switch (this.layoutOption) {
      case SavedCartFormType.SAVE: {
        this.savedCartService.saveCart({
          cartId,
          saveCartName: name,
          saveCartDescription: description,
        });

        break;
      }

      case SavedCartFormType.EDIT: {
        this.savedCartService.editSavedCart({
          cartId,
          saveCartName: name,
          saveCartDescription: description,
        });

        break;
      }
    }
  }

  deleteCart(cartId: string): void {
    this.savedCartService.deleteSavedCart(cartId);
  }

  restoreSavedCart(cartId: string): void {
    if (this.isCloneSavedCart) {
      this.savedCartService.cloneSavedCart(
        cartId,
        this.form.get('cloneName')?.value
      );
    } else {
      this.savedCartService.restoreSavedCart(cartId);
    }
  }

  close(reason: string): void {
    this.launchDialogService.closeDialog(reason);
  }

  onComplete(success: boolean): void {
    if (success) {
      switch (this.layoutOption) {
        case SavedCartFormType.DELETE: {
          this.routingService.go({ cxRoute: 'savedCarts' });
          this.globalMessageService.add(
            {
              key: 'savedCartDialog.deleteCartSuccess',
            },
            GlobalMessageType.MSG_TYPE_CONFIRMATION
          );
          this.close('Successfully deleted a saved cart');

          break;
        }

        case SavedCartFormType.SAVE: {
          this.close('Successfully saved cart');
          this.savedCartService.clearSaveCart();

          this.globalMessageService.add(
            {
              key: 'savedCartCartPage.messages.cartSaved',
              params: {
                cartName: this.form.get('name')?.value || this.cart?.code,
              },
            },
            GlobalMessageType.MSG_TYPE_CONFIRMATION
          );

          break;
        }

        case SavedCartFormType.EDIT: {
          this.close('Successfully edited saved cart');
          this.savedCartService.clearSaveCart();
          this.globalMessageService.add(
            {
              key: 'savedCartDialog.editCartSuccess',
              params: {
                cartName: this.form.get('name')?.value || this.cart?.code,
              },
            },

            GlobalMessageType.MSG_TYPE_CONFIRMATION
          );

          break;
        }

        case SavedCartFormType.RESTORE: {
          this.close('Successfully restored saved cart');
          this.routingService.go({ cxRoute: 'savedCarts' });

          this.resetSavedCartStates();

          break;
        }
      }
    }
  }

  toggleIsCloneSavedCart() {
    return (this.isCloneSavedCart = !this.isCloneSavedCart);
  }

  protected build(cart?: Cart) {
    const form = new FormGroup({});
    form.setControl(
      'name',
      new FormControl('', [
        Validators.required,
        Validators.maxLength(this.nameMaxLength),
      ])
    );
    form.setControl(
      'description',
      new FormControl('', [Validators.maxLength(this.descriptionMaxLength)])
    );
    form.setControl('isCloneSavedCart', new FormControl(''));
    form.setControl('cloneName', new FormControl(''));
    this.form = form;
    this.patchData(cart);
  }

  protected patchData(item?: any): void {
    this.form.patchValue({ ...item });
  }

  private resetSavedCartStates(): void {
    this.savedCartService.clearCloneSavedCart();
    this.savedCartService.clearSaveCart();
    this.savedCartService.clearRestoreSavedCart();
  }

  ngOnDestroy(): void {
    this.subscription?.unsubscribe();
    this.close('close dialog');
  }
}
<ng-container *ngIf="cart">
  <div
    [cxFocus]="focusConfig"
    (esc)="close('Escape clicked')"
    class="cx-saved-cart-form-dialog"
  >
    <form [formGroup]="form" class="cx-saved-cart-form-container">
      <!-- Modal Header -->
      <div class="modal-header cx-saved-cart-form-header">
        <ng-container [ngSwitch]="layoutOption">
          <div class="cx-saved-cart-form-title modal-title">
            <ng-container *ngSwitchCase="savedCartFormType.EDIT">
              {{ 'savedCartDialog.editSavedCart' | cxTranslate }}
            </ng-container>
            <ng-container *ngSwitchCase="savedCartFormType.DELETE">
              {{ 'savedCartDialog.deleteSavedCart' | cxTranslate }}
            </ng-container>
            <ng-container *ngSwitchCase="savedCartFormType.SAVE">
              {{ 'savedCartDialog.saveForLater' | cxTranslate }}
            </ng-container>
            <ng-container *ngSwitchCase="savedCartFormType.RESTORE">
              {{ 'savedCartDialog.restoreSavedCart' | cxTranslate }}
            </ng-container>
          </div>
        </ng-container>

        <button
          (click)="close('Close Save Cart Dialog')"
          [disabled]="isLoading$ | async"
          [attr.aria-label]="'common.close' | cxTranslate"
          class="cx-saved-cart-form-close close"
          type="button"
        >
          <span aria-hidden="true">
            <cx-icon [type]="iconTypes.CLOSE"></cx-icon>
          </span>
        </button>
      </div>

      <!-- Modal Body -->
      <div class="cx-saved-cart-form-body">
        <!-- start DELETE and RESTORE form -->
        <ng-container
          *ngIf="
            layoutOption === savedCartFormType.DELETE ||
              layoutOption === savedCartFormType.RESTORE;
            else saveAndEditCart
          "
        >
          <p class="cx-saved-cart-form-subtitle">
            {{
              (layoutOption === savedCartFormType.DELETE
                ? 'savedCartDialog.followingCartDelete'
                : 'savedCartDialog.followingCartRestore'
              ) | cxTranslate
            }}
          </p>

          <div class="cx-saved-cart-form-row">
            <div class="cx-saved-cart-values-container">
              <div class="cx-saved-cart-label">
                {{ 'savedCartDialog.name' | cxTranslate }}
              </div>
              <div class="cx-saved-cart-value">
                {{ cart?.name }}
              </div>
            </div>

            <div class="cx-saved-cart-values-container">
              <div class="cx-saved-cart-label">
                {{ 'savedCartDialog.id' | cxTranslate }}
              </div>
              <div class="cx-saved-cart-value">
                {{ cart?.code }}
              </div>
            </div>

            <div class="cx-saved-cart-values-container">
              <div class="cx-saved-cart-label">
                {{ 'savedCartDialog.description' | cxTranslate }}
              </div>
              <div class="cx-saved-cart-value">
                {{ cart?.description }}
              </div>
            </div>

            <div class="cx-saved-cart-values-container">
              <div class="cx-saved-cart-label">
                {{ 'savedCartDialog.quantity' | cxTranslate }}
              </div>
              <div class="cx-saved-cart-value">
                {{ cart?.totalItems }}
              </div>
            </div>

            <div class="cx-saved-cart-values-container">
              <div class="cx-saved-cart-label">
                {{ 'savedCartDialog.total' | cxTranslate }}
              </div>
              <div class="cx-saved-cart-value">
                {{ cart?.totalPriceWithTax?.formattedValue }}
              </div>
            </div>
          </div>

          <ng-container *ngIf="layoutOption === savedCartFormType.RESTORE">
            <div class="cx-copy-saved-cart-row form-check">
              <input
                id="cx-copy-saved-cart"
                type="checkbox"
                class="cx-copy-saved-cart-input"
                [checked]="isCloneSavedCart"
                (change)="toggleIsCloneSavedCart()"
              />
              <label
                for="cx-copy-saved-cart"
                class="cx-copy-saved-cart-label"
                >{{ 'savedCartDialog.keepCopySavedCart' | cxTranslate }}</label
              >
            </div>

            <div *ngIf="isCloneSavedCart" class="cx-copy-saved-cart-row">
              <label>
                <span class="label-content">
                  {{ 'savedCartDialog.nameOfCloneCart' | cxTranslate }}
                </span>

                <input
                  [maxLength]="nameMaxLength"
                  class="form-control"
                  formControlName="cloneName"
                  type="text"
                  placeholder="{{
                    'savedCartDialog.defaultCloneCartName'
                      | cxTranslate: { name: form.get('name')?.value }
                  }}"
                />
              </label>
            </div>
          </ng-container>

          <div class="cx-saved-cart-form-footer">
            <button
              (click)="close('Close Save Cart Dialog')"
              [attr.aria-label]="'common.close' | cxTranslate"
              class="mr-2 btn btn-action"
              type="button"
            >
              {{ 'savedCartDialog.cancel' | cxTranslate }}
            </button>

            <ng-container
              *ngIf="
                layoutOption === savedCartFormType.DELETE;
                else isRestoreSavedCart
              "
            >
              <button
                *ngIf="cart.code"
                (click)="deleteCart(cart.code)"
                [attr.aria-label]="'common.delete' | cxTranslate"
                [disabled]="isDisableDeleteButton$ | async"
                class="ml-2 btn btn-primary"
                type="button"
              >
                {{ 'savedCartDialog.delete' | cxTranslate }}
              </button>
            </ng-container>

            <ng-template #isRestoreSavedCart>
              <button
                *ngIf="cart.code"
                (click)="restoreSavedCart(cart.code)"
                [disabled]="isDisableRestoreButton$ | async"
                [attr.aria-label]="'common.restore' | cxTranslate"
                class="ml-2 btn btn-primary"
                type="button"
              >
                {{ 'savedCartDialog.restore' | cxTranslate }}
              </button>
            </ng-template>
          </div>
        </ng-container>
        <!-- end DELETE form -->

        <!-- start SAVE and EDIT form -->
        <ng-template #saveAndEditCart>
          <ng-container *ngIf="layoutOption === savedCartFormType.SAVE">
            <p class="cx-saved-cart-form-subtitle">
              {{ 'savedCartDialog.itemsSavedForLater' | cxTranslate }}
            </p>
          </ng-container>

          <div class="cx-saved-cart-form-row">
            <ng-container>
              <label>
                <span class="cx-saved-carts-label label-content">{{
                  'savedCartDialog.savedCartName' | cxTranslate
                }}</span>
                <input
                  aria-required="true"
                  [maxLength]="nameMaxLength"
                  class="form-control"
                  formControlName="name"
                  required
                  type="text"
                />
                <cx-form-errors
                  aria-live="assertive"
                  aria-atomic="true"
                  [control]="form.get('name')"
                ></cx-form-errors>
              </label>
            </ng-container>
          </div>

          <div class="cx-saved-cart-form-row">
            <label>
              <span class="cx-saved-carts-label label-content"
                >{{ 'savedCartDialog.savedCartDescription' | cxTranslate }}
                <span class="cx-saved-carts-label-optional">
                  ({{ 'savedCartDialog.optional' | cxTranslate }})
                </span></span
              >
              <textarea
                [maxLength]="descriptionMaxLength"
                class="form-control"
                formControlName="description"
                rows="5"
              ></textarea>
              <cx-form-errors
                aria-live="assertive"
                aria-atomic="true"
                [control]="form.get('description')"
              ></cx-form-errors>

              <p class="cx-saved-carts-input-hint">
                {{
                  'savedCartDialog.charactersLeft'
                    | cxTranslate: { count: descriptionsCharacterLeft }
                }}
              </p>
            </label>
          </div>
          <div class="cx-saved-cart-form-footer">
            <button
              (click)="close('Close Save Cart Dialog')"
              [attr.aria-label]="'common.close' | cxTranslate"
              [disabled]="isLoading$ | async"
              class="btn btn-action"
              type="button"
            >
              {{ 'savedCartDialog.cancel' | cxTranslate }}
            </button>
            <button
              (click)="saveOrEditCart(cart?.code)"
              [attr.aria-label]="'common.save' | cxTranslate"
              [disabled]="form.invalid || (isLoading$ | async)"
              class="btn btn-primary"
              type="button"
            >
              {{ 'savedCartDialog.save' | cxTranslate }}
            </button>
          </div>
        </ng-template>
        <!-- end SAVE and EDIT form -->
      </div>
    </form>
  </div>
</ng-container>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""