File
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
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)
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
businessObjectView$
|
Type : Observable<ObjectView>
|
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
|
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
|
priceList$
|
Type : Observable<PriceList>
|
searchBox
|
Type : ElementRef
|
Decorators :
@ViewChild('searchBox', {static: false})
|
searchQuery
|
Type : string
|
Typeahead product search query.
|
subscription
|
Type : Subscription[]
|
Default value : []
|
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">×</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">×</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>
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 with directive