File

projects/congarevenuecloud/elements/src/lib/quick-add/quick-add.component.ts

Description

QuickAdd component is used to show a modal window or embedded layout with the cartItems fields that can be added to the cart.

Preview

Usage

Example :
import { QuickAddModule } from '@congarevenuecloud/elements';

@NgModule({
imports: [ QuickAddModule, ...]
})
export class AppModule {}
Example :
```typescript
<app-quick-add layout='modal' productFieldType='text'>
Example :

Implements

OnInit OnChanges

Metadata

Index

Properties
Methods
Inputs

Constructor

constructor(modalService: BsModalService, exceptionService: ExceptionService, aobjectService: AObjectService, cartItemService: CartItemService, router: Router, sanitizer: DomSanitizer, plservice: PriceListService, productService: ProductService, cartService: CartService, quoteService: QuoteService, orderService: OrderService, accountService: AccountService)
Parameters :
Name Type Optional
modalService BsModalService No
exceptionService ExceptionService No
aobjectService AObjectService No
cartItemService CartItemService No
router Router No
sanitizer DomSanitizer No
plservice PriceListService No
productService ProductService No
cartService CartService No
quoteService QuoteService No
orderService OrderService No
accountService AccountService No

Inputs

actionType
Type : "quick-add" | "quick-quote" | "quick-order"
Default value : 'quick-add'

Input property that specifies the representation and behavior of an action component. It controls whether the action appears as a button (for quick quote/order) or an icon (for add-to-cart). 'quick-add': Renders a button with a dropdown menu for quick item addition, quick order creation, and quick quote generation. The dropdown allows users to select the specific action. 'quick-quote': Renders a standalone button labeled 'New Quote' for initiating the creation of a new quote. 'quick-order': Renders a standalone button labeled 'New Order' for initiating the creation of a new order.

btnClass
Type : string
Default value : 'btn-custom-primary'

Custom class or bootstrap class to set on the primary button.

businessObject
Type : Order | Quote | Cart

The field accepts the business object of instance order | cart | quote.

businessObjectFields
Type : Array<string>
Default value : []

List of businessObject fields to be shown on the layout

fields
Type : Array<string>

List of field properties to be shown on the layout

layout
Type : "modal" | "embedded"
Default value : 'embedded'

The layout specifies to show as a modal or embedded.

productFieldType
Type : "text" | "lookup"
Default value : 'lookup'

Flag to set the type of 'Product' field. when set to 'text' user can enter the valid product code. when set to 'lookup' the field displays the list of products that match the product name or product code.

value
Type : Array<CartItem>
Default value : []

Flag to set the predefined products to be displayed on the layout.

Methods

addItem
addItem(view: View)

The method is responsible to add new cartItem records to the layout on 'Add Product' action.

Parameters :
Name Type Optional Description
view View No

represents the fields to render on the layout for cartItem record.

Returns : void
changeHeaderValue
changeHeaderValue(value: any, item: Order | Quote, field: string)

The method updates the order/quote header values updated by user.

Parameters :
Name Type Optional Description
value any No

represents the value of order/quote fields that needs to be updated.

item Order | Quote No

is an instance of order/quote to be updated.

field string No

represents the field name of order/quote that needs to be updated.

Returns : void
changeProductText
changeProductText(searchQuery: string, item?: CartItem, field?: Field)

The method updates the value selected by the user for the product field of cartItem record when productFieldType set as 'text'.

Parameters :
Name Type Optional Description
searchQuery string No

is of type string to search the product based on the query.

item CartItem Yes

is an instance of cartItem to be updated.

field Field Yes

represents the product field that needs to be updated in the cartItem record.

Returns : void
changeProductValue
changeProductValue(searchQuery: string, item?: CartItem, field?: Field)

The method updates the value selected by the user for the product field of cartItem record when productFieldType set as 'lookup'.

Parameters :
Name Type Optional Description
searchQuery string No

is of type string to search the product based on the query.

item CartItem Yes

is an instance of cartItem to be updated.

field Field Yes

represents the product field that needs to be updated in the cartItem record.

Returns : void
changeValue
changeValue(value: any, item: CartItem, field: Field)

The method updates the cartItem record values updated by user.

Parameters :
Name Type Optional Description
value any No

represents the value of cartItem fields that needs to be updated.

item CartItem No

is an instance of cartItem to be updated.

field Field No

represents the field name of cartItem that needs to be updated.

Returns : void
closeModal
closeModal(record: Order | Quote)

The method is responsible to close modal popup reference for the layout 'modal'.

Parameters :
Name Type Optional Default value Description
record Order | Quote No null

instance of order /quote object.

Returns : void
handleAddToCart
handleAddToCart(view: View, bOView?: ObjectView)

The method is fired when user clicks on Add to Cart button. The selected items gets added to user's current active cart.

Parameters :
Name Type Optional Description
view View No

represents the fields to render on the layout for cartItem record.

bOView ObjectView Yes

represents the fields to render on the layout for order/quote record.

Returns : void
isItemSelected
isItemSelected(view: View)

Enables/disables the Add to cart button based on the availability of mandatory field on cart item record.

Parameters :
Name Type Optional Description
view View No

represents the fields to render on the layout for cartItem record.

Returns : boolean
openModal
openModal(view: View)

The method is responsible to open modal popup for layout 'modal'.

Parameters :
Name Type Optional Description
view View No

represents the fields to render on the layout for cartItem record.

Returns : void
removeItem
removeItem(item: CartItem, view: View)

The method is responsible to delete a cartItem record from the layout.

Parameters :
Name Type Optional Description
item CartItem No

is an instance of cartItem to be removed.

view View No

represents the fields to render on the layout for cartItem record.

Returns : void

Properties

bOView
Type : ObjectView
businessObjectView$
Type : Observable<ObjectView>
cancelDisabled
Type : boolean
confirmationModal
Type : BsModalRef

Confirmation modal is displayed on successful creation of order /quote object.

confirmationTemplate
Type : TemplateRef<any>
Decorators :
@ViewChild('confirmationTemplate')
count
Type : number
Default value : 1
disabled
Type : boolean
errorMessage
Type : string
instance
Type : AObject
isDisabled
Type : boolean
loading
Type : boolean
Default value : false
modalRef
Type : BsModalRef

Reference for modal dialog for layout 'modal'

noResult
Type : boolean
Default value : false
objectInstance
Type : AObject
Public orderService
Type : OrderService
priceList$
Type : Observable<PriceList>
Public quoteService
Type : QuoteService
searchBox
Type : ElementRef
Decorators :
@ViewChild('searchBox', {static: false})
searchQuery
Type : string

Typeahead product search query.

showTemplate
Type : string
subscription
Type : Subscription[]
Default value : []
title
Type : string
type
Default value : CartItem

By default the type is set to cartItem for add to cart action.

typeahead$
Type : Observable<Array<Product>>
Default value : new Observable<Array<Product>>()

Observable instance of type Product to show result based on search.

typeAheadItem
Type : TypeaheadDirective
Decorators :
@ViewChild('typeAheadItem', {static: false})
typeaheadLoading
Type : boolean
Default value : false

Flag to show loader for typeAhead.

<div *ngIf="view$ | async as view" [autoClose]="false">
  <div *ngIf="layout === 'modal'; else embedLayout">
    <div class="btn-group">
      <div *ngIf="actionType === 'quick-add'">
        <button type="button" placement="auto" title="{{ 'COMMON.QUICK_ADD_TOOLTIP' | translate }}"
          class="btn btn-link text-primary btn-raised dropdown-toggle" data-toggle="dropdown" aria-expanded="false"
          [ngClass]="buttonClass">
          <i class="fa fa-regular fa-cart-plus"></i>
        </button>

        <div class="dropdown-menu dropdown-menu-right border">
          <span class="dropdown-item py-2" (click)="setBusinessObjectValue('cart')">{{ "COMMON.QUICK_ADD" | translate
            }}</span>

          <span class="dropdown-item py-2" (click)="setBusinessObjectValue('order')">{{ "COMMON.QUICK_ORDER" | translate
            }}</span>

          <span class="dropdown-item py-2" (click)="setBusinessObjectValue('quote')">{{ "COMMON.QUICK_QUOTE" | translate
            }}</span>
        </div>
      </div>
      <div *ngIf="actionType === 'quick-quote'">
        <button type="button" class="btn btn-primary text-nowrap" [ngClass]="buttonClass"
          (click)="setBusinessObjectValue('quote')">{{ 'MY_ACCOUNT.QUOTE_LIST.CREATE_NEW' | translate}}
        </button>
      </div>
    </div>

    <!-- the template is rendered when layout is modal -->

    <ng-template #modalTemplate>
      <div class="modal-header align-items-center d-flex flex-row-reverse pt-0 mt-3 px-0 pb-2">
        <button type="button" class="close close-button pull-right" aria-label="Close" (click)="closeModal()">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>

      <div class="modal-body d-flex flex-column justify-content-center pb-3 pt-1 bg-white scrollbar">
        <h6 class="modal-title pull-left font-weight-bold p-2">
          {{ title | translate }}
        </h6>
        <ng-container [ngSwitch]="showTemplate">
          <ng-container *ngSwitchCase="'cart'">
            <ng-container *ngTemplateOutlet="template"></ng-container>
          </ng-container>

          <ng-container *ngSwitchCase="'quote'">
            <ng-container *ngTemplateOutlet="quoteForm"></ng-container>
          </ng-container>

          <ng-container *ngSwitchCase="'order'">
            <ng-container *ngTemplateOutlet="orderForm"></ng-container>
          </ng-container>
        </ng-container>
      </div>
    </ng-template>
  </div>

  <!-- the template is rendered when layout is embedded -->

  <ng-template #embedLayout>
    <div class="container-fluid py-4 d-flex justify-content-center">
      <div class="d-block card">
        <div class="pb-2">
          <h5 class="p-2">{{ title | translate }}</h5>
        </div>

        <ng-container [ngSwitch]="showTemplate">
          <ng-container *ngSwitchCase="'cart'">
            <ng-container *ngTemplateOutlet="template"></ng-container>
          </ng-container>

          <ng-container *ngSwitchCase="'quote'">
            <ng-container *ngTemplateOutlet="quoteForm"></ng-container>
          </ng-container>

          <ng-container *ngSwitchCase="'order'">
            <ng-container *ngTemplateOutlet="orderForm"></ng-container>
          </ng-container>
        </ng-container>
      </div>
    </div>
  </ng-template>

  <!-- the common template used to display cartitem fields-->

  <ng-template #template>
    <div class="table-responsive px-3">
      <table class="table table-borderless">
        <thead class="p-4">
          <tr>
            <th scope="col" class="pt-3 pl-1">#</th>

            <th class="pl-1" *ngFor="let field of view?.fieldList">
              {{ field?.label }}
            </th>
          </tr>
        </thead>

        <tbody>
          <tr *ngFor="let item of view?.item; let i = index">
            <th scope="row" class="pt-4 pl-1">{{ i + 1 }}</th>

            <ng-container *ngFor="let list of view.fieldList">
              <td class="col-md-3 pl-1" *ngIf="list?.referenceType === 'Product'; else inputField">
                <ng-container *ngIf="productFieldType === 'lookup'; else productText">
                  <input type="search" class="form-control" placeholder="{{
                      'HEADER.ENTER_PRODUCT_TO_QUICK_ADD' | translate
                    }}" (typeaheadLoading)="typeaheadLoading = $event" (typeaheadOnSelect)="
                      typeaheadOnSelect($event, item, list?.result)
                    " [typeaheadIsFirstItemActive]="false" [typeaheadItemTemplate]="typeAheadItem"
                    [typeahead]="typeahead$" [typeaheadOptionsLimit]="5" minlength="2" [typeaheadWaitMs]="1000"
                    [typeaheadOptionField]="Name" [(ngModel)]="item._metadata.typheaheadSelection" (ngModelChange)="
                      changeProductValue($event, item, list?.result)
                    " name="searchQuery" (typeaheadNoResults)="typeaheadNoResults($event, item)" #searchBox
                    aptAutoFocus autocomplete="off" />

                  <small class="media w-100 py-2 pl-2" *ngIf="
                      item?._metadata?.noResult &&
                      item?._metadata?.searchQuery?.length >= 3 &&
                      !typeaheadLoading
                    ">
                    {{ "COMMON.NO_PRODUCT" | translate }}</small>

                  <small class="media w-100 py-2 pl-2" *ngIf="
                      item?._metadata?.pickValue &&
                      item?._metadata?.searchQuery?.length >= 3 &&
                      !typeaheadLoading
                    ">
                    {{ "COMMON.PICKVALUE" | translate }}</small>
                </ng-container>

                <ng-template #productText>
                  <apt-input-field [entity]="item" [field]="'Name'" [showLabel]="false"
                    [(ngModel)]="item._metadata.searchQuery" (keyup)="loadDataDebounce($event, item, list?.result)"
                    [placeholder]="'HEADER.SEARCH_BY_CODE' | translate" [overrideDefaultFormValidation]="
                      item._metadata.errorMessage !== null
                    " [errorMsg]="item._metadata.errorMessage"></apt-input-field>
                </ng-template>
              </td>

              <ng-template #inputField>
                <td class="col-md-3 pl-1">
                  <apt-input-field class="show" [entity]="item" [field]="list?.result" [showLabel]="false"
                    [minVal]="list?.result === 'Quantity' ? 1 : 0" [(ngModel)]="item[list?.result]"
                    [customStyling]="list?.type === 'Picklist' ? '180px' : none"
                    (ngModelChange)="changeValue($event, item, list?.result)" [errorMsg]="
                      list?.result === 'Quantity'
                        ? 'ERROR.INVALID_QUANTITY'
                        : 'ERROR.NEGATIVE_VALUE'
                    "></apt-input-field>
                </td>
              </ng-template>
            </ng-container>

            <td>
              <button class="btn btn-link text-primary pl-0" type="button" (click)="removeItem(item, view)">
                <i class="fas fa-times-circle"></i>
              </button>
            </td>
          </tr>
        </tbody>
      </table>

      <button class="btn btn-link text-primary btn-icon pb-3" type="button" (click)="addItem(view)">
        <i class="fas fa-plus-circle mr-3"></i>

        {{ "COMMON.ADD_PRODUCT" | translate }}
      </button>
    </div>
    <div *ngIf="showTemplate === 'cart'" class="border-top d-flex justify-content-end pb-1">
      <div class="pt-3" *ngIf="instanceObject(businessObject) === 'cart'">
        <button class="btn btn-primary btn-raised px-1 m-0" [disabled]="isItemSelected(view)" [ladda]="loading"
          (click)="handleAddToCart(view)">
          {{ title | translate }}
        </button>
      </div>

      <div class="pt-3" *ngIf="instanceObject(businessObject) != 'cart'">
        <button class="btn btn-primary btn-raised px-1 m-0" [disabled]="isItemSelected(view)" [ladda]="loading"
          (click)="nextPage(view, bOView)">
          {{ title | translate }}
        </button>
      </div>
    </div>
  </ng-template>

  <!-- the common template to show quote form-->

  <ng-template #quoteForm>
    <div class="card border-0">
      <div class="card-body">
        <div class="row">
          <div class="col-md-6" *ngFor="let list of bOView?.fieldList">
            <apt-input-field [entity]="bOView?.item" [field]="list?.result" [(ngModel)]="bOView?.item[list?.result]"
              [required]="
                list?.result === 'Name'
                  ? true
                  : false || list?.result === 'PrimaryContact'
                  ? true
                  : false
              " (ngModelChange)="
                changeHeaderValue($event, bOView?.item, list?.result)
              " [showLabel]="true" [errorMsg]="
                !bOView?.item?.Name && list?.result === 'Name'
                  ? 'Quote title cannot be empty'
                  : null
              "></apt-input-field>
          </div>
        </div>
      </div>
    </div>

    <div class="border-top d-flex justify-content-end pt-3 pb-1">
      <button class="btn btn-link text-primary" *ngIf="showTemplate === 'quote'" [disabled]="cancelDisabled"
        (click)="actionType === 'quick-quote' ? closeModal() : showTemplate = 'cart'">
        {{ "COMMON.CANCEL" | translate }}
      </button>

      <button class="btn btn-primary btn-raised px-1 m-0" [disabled]="disabled" *ngIf="showTemplate === 'quote'"
        [ladda]="loading" (click)="handleAddToCart(view, bOView)">
        {{ "COMMON.REQUEST_QUOTE" | translate }}
      </button>
    </div>
  </ng-template>

  <!-- the common template to show order form-->

  <ng-template #orderForm>
    <div class="card border-0">
      <div class="card-body">
        <div class="row">
          <div class="col-md-6" *ngFor="let list of bOView?.fieldList">
            <div *ngIf="list?.result !== 'PriceList'; else showPriceList">
              <apt-input-field [entity]="bOView?.item" [field]="list?.result" [(ngModel)]="bOView?.item[list?.result]"
                [required]="
                  list?.result === 'Name'
                    ? true
                    : false || list?.result === 'PrimaryContact'
                    ? true
                    : false
                " (ngModelChange)="
                  changeHeaderValue($event, bOView?.item, list?.result)
                " [showLabel]="true" [errorMsg]="
                  !bOView?.item?.Name && list?.result === 'Name'
                    ? 'Order title cannot be empty'
                    : null
                "></apt-input-field>
            </div>

            <ng-template #showPriceList>
              <apt-output-field [record]="bOView?.item" [field]="list?.result" [editable]="false">
              </apt-output-field>
            </ng-template>
          </div>
        </div>
      </div>
    </div>

    <div class="border-top d-flex justify-content-end pt-3 pb-1">
      <button class="btn btn-link text-primary" *ngIf="showTemplate === 'order'" [disabled]="cancelDisabled"
        (click)="showTemplate = 'cart'">
        {{ "COMMON.CANCEL" | translate }}
      </button>

      <button class="btn btn-primary btn-raised px-1 m-0" [disabled]="disabled" *ngIf="showTemplate === 'order'"
        [ladda]="loading" (click)="handleAddToCart(view, bOView)">
        {{ "COMMON.PLACE_ORDER" | translate }}
      </button>
    </div>
  </ng-template>

  <!-- the common template used show list of products-->

  <ng-template #typeAheadItem let-model="item" let-index="index" let-last="last" let-match="match">
    <div class="media w-100 p-2" (click)="onTemplateMatch(match)">
      <img class="mr-3 align-self-center thumbnail" [src]="model.IconId | image : true : true : model.Id"
        [alt]="model.IconId" />

      <div class="media-body truncate">
        <h6 class="m-0">{{ model?.Name }}</h6>

        <small class="d-block">{{ model?.ProductCode }}</small>
      </div>
    </div>
  </ng-template>

  <!-- the common template used show confirmation modal after placing quote/order-->

  <ng-template #confirmationTemplate>
    <div class="modal-header align-items-center d-flex flex-row-reverse p-0 mx-3 mt-3 border-bottom border-secondary">
      <button type="button" class="close close-button pull-right pr-0" aria-label="Close"
        (click)="confirmationModal.hide()">
        <span aria-hidden="true">&times;</span>
      </button>
    </div>

    <div class="modal-body bg-white d-flex flex-column justify-content-center py-4">
      <h6 *ngIf="instanceObject(businessObject) === 'quote'; else orderRequest"
        class="modal-title pull-left font-weight-bold pb-3">
        {{ "QUOTE_CONFIRMATION_MODAL.QUOTE_REQUEST" | translate }}
      </h6>

      <ng-template #orderRequest>
        <h6 class="modal-title pull-left font-weight-bold pb-3">
          {{ "MANAGE_CART.CART_SUMMARY.CHECKOUT" | translate }}
        </h6>
      </ng-template>
      <p class="text-center">
        <strong>{{ "MODAL.THANK_YOU" | translate }}

          {{ confirmation?.CreatedBy?.Name }}!</strong>
      </p>

      <ng-container *ngIf="instanceObject(businessObject) === 'quote'; else orderName">
        <small class="text-center" [translate]="'QUOTE_CONFIRMATION_MODAL.QUOTE_CONFIRM_MSG'"
          [translateParams]="{ Sellername: confirmation?.CreatedBy?.Name }"></small>

        <small class="text-center" *ngIf="confirmation?.RFPResponseDueDate" [translate]="
            'QUOTE_CONFIRMATION_MODAL.QUOTE_CONFIRMATION_MSG_WITH_RFP'
          " [translateParams]="{
            RFP_value: confirmation.RFPResponseDueDate | date : 'yyyy-MM-dd'
          }"></small>

        <h5 class="text-center my-3">
          {{ "QUOTE_CONFIRMATION_MODAL.QUOTE_CONFIRMATION_NO" | translate }}

          {{ confirmation.Name }}
        </h5>

        <button class="btn btn-outline-primary w-25 mx-auto" (click)="closeModal(confirmation)">
          {{ "QUOTE_CONFIRMATION_MODAL.REVIEW_YOUR_QUOTE" | translate }}
        </button>
      </ng-container>

      <ng-template #orderName>
        <small class="text-center" *ngIf="confirmation?.PrimaryContact?.Email; else noEmail"
          [translate]="'CART.SUCCESSFUL_ORDER_CREATION_MESSAGE'" [translateParams]="{
            emailAddress: confirmation?.PrimaryContact?.Email
          }"></small>

        <ng-template #noEmail>
          <small class="text-center">{{
            "CART.SUCCESSFUL_ORDER_CREATION_MESSAGE_WITHOUT_EMAIL" | translate
            }}</small>
        </ng-template>

        <h5 class="text-center my-3" [translate]="'DETAILS.GENERATED_ORDER_NUMBER'"
          [translateParams]="{ orderName: confirmation?.Name }"></h5>

        <button class="btn btn-outline-primary w-25 mx-auto" (click)="closeModal(confirmation)">
          {{ "DETAILS.VIEW_YOUR_ORDER" | translate }}
        </button>
      </ng-template>
    </div>
  </ng-template>
</div>

./quick-add.component.scss

div.dropdown-menu {
  span {
    cursor: pointer;
  }
}

div.table-responsive {
  overflow-y: hidden;
}

.scrollbar{
  ::-webkit-scrollbar {
    width: 0.375rem !important;
    height: 0.375rem !important;
    background: #fff !important;
  }

  ::-webkit-scrollbar-thumb {
    background: #A8B2BB !important;
    border-radius: 0.25rem !important;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""