@vendasta/store
Version:
Components and data for Store
780 lines (764 loc) • 380 kB
JavaScript
import { Component, Input, NgModule, EventEmitter, Output, Injectable, ChangeDetectorRef } from '@angular/core';
import { formatDisplayPrice, formatBillingFrequency, Addon } from '@vendasta/core/shared';
import { CommonModule } from '@angular/common';
import { ImageTransformationService, ImageTransformationModule } from '@vendasta/core/image-transformation';
import { MatButtonModule, MatCardModule, MatIconModule, MatMenuModule, MatChipsModule, MatTooltipModule, MatCheckboxModule, MatExpansionModule, MatInputModule, MatOptionModule, MatProgressSpinnerModule, MatSelectModule, MatDividerModule, MatListModule } from '@angular/material';
import { VaIconModule, VaSafeHtmlModule, VaBreadcrumbsModule, VaImageGalleryModule, VaListModule, UIKitModule } from '@vendasta/uikit';
import { FormControl, FormGroup, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BillingModel, BillingService, buildBilledProductFromProductPricingAndAddon, BillingModule } from '@vendasta/core/billing';
import { VaFormsModule } from '@vendasta/forms';
import { MatProgressSpinnerModule as MatProgressSpinnerModule$1 } from '@angular/material/progress-spinner';
import { isNullOrUndefined } from 'util';
import { BehaviorSubject, ReplaySubject, combineLatest, merge, zip } from 'rxjs';
import { skipWhile, shareReplay, map, take, filter, startWith } from 'rxjs/operators';
import { trigger, transition, animate, style, state } from '@angular/animations';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class VaPricingComponent {
constructor() {
this.pricing = null;
this.wrapFrequency = false;
this.isAddon = false;
this.hasVerifiedContract = false;
this.highlightPrice = true;
this.loaded = true;
}
/**
* @return {?}
*/
get isFree() {
/** @type {?} */
const isFreeHelper = (prices) => {
return prices
? prices.some(p => p.price === 0 || p.price === undefined)
: false;
};
if (this.hasVerifiedContract && this.billedProduct) {
return this.billedProduct.pricingTiers.length === 1 ? isFreeHelper(this.billedProduct.pricingTiers) : false;
}
return this.pricing && isFreeHelper(this.pricing.prices) ? true : false;
}
/**
* @return {?}
*/
get shouldContactSales() {
/** @type {?} */
const nestedPricesIsContactSales = (prices) => {
return prices && prices.length > 0
? prices.some(p => p.price === null || p.price < 0)
: true;
};
if (this.hasVerifiedContract && this.billedProduct) {
return nestedPricesIsContactSales(this.billedProduct.pricingTiers) ? true : false;
}
if (!this.pricing) {
return true;
}
return nestedPricesIsContactSales(this.pricing.prices);
}
/**
* @param {?} billedProduct
* @return {?}
*/
isFlatPrice(billedProduct) {
return billedProduct.pricingTiers.length === 1;
}
/**
* @param {?} tier
* @return {?}
*/
buildPricingTierString(tier) {
/** @type {?} */
let max = String(tier.rangeMax);
if (tier.rangeMax === -1) {
max = '∞';
if (tier.rangeMin === 0 || tier.rangeMin === 1) {
return '';
}
}
return String(tier.rangeMin) + ' to ' + max;
}
/**
* @param {?} tier
* @param {?=} frequency
* @return {?}
*/
buildPricingTierForProduct(tier, frequency) {
/** @type {?} */
const pricingTier = this.buildPricingTierString(tier);
/** @type {?} */
const formattedFrequency = formatBillingFrequency(frequency);
return pricingTier ? pricingTier + ' accounts ' + formattedFrequency : formattedFrequency;
}
/**
* @param {?} tier
* @return {?}
*/
buildPricingTierForAddon(tier) {
/** @type {?} */
const pricingRange = this.buildPricingTierString(tier);
return pricingRange ? pricingRange : '';
}
/**
* @param {?} price
* @param {?} currency
* @param {?=} excludeFrequency
* @return {?}
*/
buildPriceStringForPricing(price, currency, excludeFrequency) {
return formatDisplayPrice(price.price, currency, (/** @type {?} */ ((excludeFrequency ? '' : price.frequency))), true, true, true, price.isStartingPrice);
}
/**
* @param {?} tier
* @param {?=} frequency
* @param {?=} isStartingPrice
* @return {?}
*/
buildPriceStringForTier(tier, frequency, isStartingPrice) {
return formatDisplayPrice(tier.price, this.billedProduct.currency, (/** @type {?} */ (frequency)), undefined, undefined, undefined, isStartingPrice);
}
/**
* @param {?=} frequency
* @return {?}
*/
buildFrequencyString(frequency) {
return formatBillingFrequency(frequency);
}
/**
* @return {?}
*/
buildCommitmentMessage() {
/** @type {?} */
const frequency = this.billedProduct ? this.billedProduct.billingFrequency.toString().toLowerCase() :
this.pricing && this.pricing.prices.length > 0 ? this.pricing.prices[0].frequency.toLowerCase() : '';
/** @type {?} */
let frequencyString = '';
if (frequency === 'yearly') {
frequencyString = 'year';
}
else if (frequency === 'monthly') {
frequencyString = 'month';
}
if (!frequencyString || this.isFree || this.shouldContactSales) {
return '';
}
if (this.billedProduct && this.billedProduct.commitment) {
/** @type {?} */
const initial = this.billedProduct.commitment.initial;
/** @type {?} */
const recurring = this.billedProduct.commitment.recurring;
if (this.highlightPrice) {
return `*${initial} ${frequencyString} minimum, renews for ${recurring} ${frequencyString} periods`;
}
else {
return `${initial} ${frequencyString} commitment`;
}
}
return '';
}
}
VaPricingComponent.decorators = [
{ type: Component, args: [{
selector: 'va-pricing',
template: "<ng-container *ngIf=\"highlightPrice\">\n <highlight-pricing [pricing]=\"pricing\"\n [billedProduct]=\"billedProduct\"\n [highlightPrice]=\"highlightPrice\"\n [isAddon]=\"isAddon\"\n [hasVerifiedContract]=\"hasVerifiedContract\"\n [wrapFrequency]=\"wrapFrequency\"\n [loaded]=\"loaded\">\n </highlight-pricing>\n</ng-container>\n<ng-container *ngIf=\"!highlightPrice\">\n <table-pricing [pricing]=\"pricing\"\n [highlightPrice]=\"highlightPrice\"\n [billedProduct]=\"billedProduct\"\n [hasVerifiedContract]=\"hasVerifiedContract\"\n [loaded]=\"loaded\">\n </table-pricing>\n</ng-container>\n",
styles: [""]
}] }
];
VaPricingComponent.propDecorators = {
pricing: [{ type: Input }],
billedProduct: [{ type: Input }],
wrapFrequency: [{ type: Input }],
isAddon: [{ type: Input }],
hasVerifiedContract: [{ type: Input }],
highlightPrice: [{ type: Input }],
loaded: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class HighlightPricingComponent extends VaPricingComponent {
}
HighlightPricingComponent.decorators = [
{ type: Component, args: [{
selector: 'highlight-pricing',
template: "<ng-container *ngIf=\"loaded; else loading\">\n <div class=\"no-price\" *ngIf=\"shouldContactSales; else dontContactSales\">\n Contact Sales\n </div>\n <ng-template #dontContactSales>\n <div class=\"no-price\" *ngIf=\"isFree; else notFree\">\n <b>Free</b>\n </div>\n <ng-template #notFree>\n <ng-container *ngIf=\"!isAddon\">\n <ng-container *ngIf=\"hasVerifiedContract && billedProduct; else usePricing\">\n <div *ngFor=\"let tier of billedProduct.pricingTiers\" class=\"price-box\">\n <span>\n <span *ngIf=\"tier.isStartingPrice\" class=\"starting-at\">Starting at</span>\n <b class=\"price-number\">{{buildPriceStringForTier(tier)}}</b>\n </span>\n <span class=\"tier-billing-freq\" [ngClass]=\"{'va-wrap': wrapFrequency}\">\n {{buildPricingTierForProduct(tier, billedProduct.billingFrequency)}}\n </span>\n </div>\n </ng-container>\n <ng-template #usePricing>\n <ng-container *ngIf=\"pricing?.prices\">\n <div *ngFor=\"let price of pricing.prices; let i = index\" class=\"price-box\">\n <span>\n <span *ngIf=\"price?.isStartingPrice\" class=\"starting-at\">Starting at</span>\n <b class=\"price-number\">\n <ng-container *ngIf=\"i == 1\">+</ng-container>\n {{buildPriceStringForPricing(price, pricing?.currency, true)}}\n </b>\n </span>\n <span *ngIf=\"price.frequency as frequency\" class=\"billing-freq\" [ngClass]=\"{'va-wrap': wrapFrequency}\">\n {{buildFrequencyString(frequency)}}\n </span>\n </div>\n </ng-container>\n </ng-template>\n </ng-container>\n <ng-container *ngIf=\"isAddon\">\n <div *ngIf=\"hasVerifiedContract && billedProduct; else useAddonPricing\">\n <div *ngFor=\"let tier of billedProduct.pricingTiers\" class=\"price-box\">\n <span class=\"addon-billing-freq\">\n {{buildPricingTierForAddon(tier)}}\n </span>\n <span>\n <span *ngIf=\"tier.isStartingPrice\" class=\"starting-at\">Starting at</span>\n <b class=\"price-number\">\n {{buildPriceStringForTier(tier, billedProduct.billingFrequency)}}\n </b>\n </span>\n\n </div>\n </div>\n <ng-template #useAddonPricing>\n <div class=\"price-box\">\n <span *ngIf=\"pricing?.prices[0].frequency\" class=\"addon-billing-freq\">\n {{buildFrequencyString(pricing.prices[0].frequency)}}\n </span>\n <span>\n <span *ngIf=\"pricing?.prices[0]?.isStartingPrice\" class=\"starting-at\">Starting at</span>\n <b class=\"price-number\">\n {{buildPriceStringForPricing(pricing?.prices[0], pricing?.currency, true)}}\n </b>\n </span>\n </div>\n </ng-template>\n </ng-container>\n <div class=\"commitment\" *ngIf=\"billedProduct && billedProduct.commitment && billedProduct.commitment.initial > 1\">\n {{ buildCommitmentMessage() }}\n </div>\n </ng-template>\n </ng-template>\n</ng-container>\n<ng-template #loading>\n <div class=\"stencil-pricing stencil-shimmer\"></div>\n</ng-template>\n",
styles: [":host{color:#9e9e9e}.starting-at{margin-right:5px}.price-box{display:flex;align-items:baseline;flex-direction:row-reverse}.no-price{text-align:center}b{color:#212121;font-size:20px}@media screen and (max-width:600px){.no-price,.price-box,b{font-size:16px}}.billing-freq{text-transform:capitalize;flex:1}.tier-billing-freq{flex:1}.addon-billing-freq{padding-left:5px}.va-wrap{display:block}.stencil-pricing{height:1em}.commitment{color:#9e9e9e;font-size:12px;font-style:italic}"]
}] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class TablePricingComponent extends VaPricingComponent {
}
TablePricingComponent.decorators = [
{ type: Component, args: [{
selector: 'table-pricing',
template: "<ng-container *ngIf=\"loaded; else loading\">\n <div *ngIf=\"hasVerifiedContract && billedProduct; else usePricing\">\n <div class=\"price-container\" *ngIf=\"isFlatPrice(billedProduct); else notFlatPrice\">\n <div class=\"price\">\n {{buildPriceStringForTier(billedProduct.pricingTiers[0], billedProduct.billingFrequency, billedProduct.pricingTiers[0].isStartingPrice)}}\n </div>\n </div>\n <ng-template #notFlatPrice>\n <div class=\"price-container--tiered\" *ngIf=\"!isFlatPrice(billedProduct)\">\n <div class=\"pricing-tier\" *ngFor=\"let tier of billedProduct.pricingTiers\">\n <div class=\"pricing-tier__range\">{{buildPricingTierString(tier)}}</div>\n <div class=\"pricing-tier__price\">\n {{buildPriceStringForTier(tier, billedProduct.billingFrequency, tier.isStartingPrice)}}\n </div>\n </div>\n </div>\n </ng-template>\n <div class=\"commitment\" *ngIf=\"billedProduct && billedProduct.commitment && billedProduct.commitment.initial > 1\">\n {{ buildCommitmentMessage() }}\n </div>\n </div>\n <ng-template #usePricing>\n <div class=\"gray-font\">\n <ng-container *ngIf=\"shouldContactSales; else dontContactSales\">\n Contact Sales\n </ng-container>\n <ng-template #dontContactSales>\n <ng-container *ngIf=\"isFree; else notFree\">\n Free\n </ng-container>\n <ng-template #notFree>\n {{buildPriceStringForPricing(pricing?.prices[0], pricing?.currency)}}\n <div class=\"commitment\" *ngIf=\"billedProduct && billedProduct.commitment && billedProduct.commitment.initial > 1\">\n {{ buildCommitmentMessage() }}\n </div>\n </ng-template>\n </ng-template>\n </div>\n </ng-template>\n</ng-container>\n<ng-template #loading>\n <div class=\"stencil-pricing stencil-shimmer\"></div>\n</ng-template>\n",
styles: [":host{color:#616161}.price-container--tiered{font-size:12px;flex-direction:column;display:flex;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.price-container--tiered .pricing-tier{border-bottom:1px solid #e0e0e0;padding:5px 0;display:flex}.price-container--tiered .pricing-tier:first-child{padding-top:0}.price-container--tiered .pricing-tier:last-child{padding-bottom:0;border-bottom:none}.price-container--tiered .pricing-tier .pricing-tier__range{padding-right:20px;flex:1}.stencil-pricing{height:1em}.commitment{color:#9e9e9e;font-size:12px;font-style:italic}"]
}] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class VaPricingModule {
}
VaPricingModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule],
declarations: [VaPricingComponent, HighlightPricingComponent, TablePricingComponent],
exports: [VaPricingComponent]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class StoreCardComponent {
/**
* @param {?} imageTransformationService
*/
constructor(imageTransformationService) {
this.imageTransformationService = imageTransformationService;
this.purchasedClicked = new EventEmitter();
this.cardClicked = new EventEmitter();
this.descriptionClicked = new EventEmitter();
this.bannerClicked = new EventEmitter();
}
/**
* @return {?}
*/
getBannerColorForName() {
// determine an icon color for a product with no icon by using the product name
/** @type {?} */
const COLOR_CODES = [
'#EF5350', '#42A5F5', '#66BB6A', '#FFA726', '#AB47BC', '#FFCA28', '#EC407A', '#26C6DA',
'#FF7B57'
];
/** @type {?} */
let nameSum = 0;
/** @type {?} */
const defaultColor = '#808080';
if (!this.item.name) {
return defaultColor;
}
for (let i = 0; i < this.item.name.length; i++) {
nameSum += this.item.name[i].charCodeAt(0);
}
/** @type {?} */
const index = nameSum % COLOR_CODES.length;
return COLOR_CODES[index];
}
/**
* @param {?} imageUrl
* @return {?}
*/
getSrcsetHeaderUrls(imageUrl) {
return this.imageTransformationService.getSrcSetForImage(imageUrl, [680, 1360, 2720]);
}
}
StoreCardComponent.decorators = [
{ type: Component, args: [{
selector: 'va-store-card',
template: `
<mat-card class="hover-card" *ngIf="item" (click)="cardClicked.emit(item)">
<div class="hover-area">
<div class="product-banner" (click)="bannerClicked.emit(item)"
[style.background-color]="item.headerImageUrl ? '': getBannerColorForName()">
<img class="img-container" *ngIf="item.headerImageUrl"
[src]="item.headerImageUrl" [srcset]="getSrcsetHeaderUrls(item.headerImageUrl)">
</div>
<div class="description" (click)="descriptionClicked.emit(item)">
<mat-card-header>
<va-icon mat-card-avatar [diameter]="40" [name]="item.name" [iconUrl]="item.iconUrl"></va-icon>
<mat-card-title>{{item.name}}</mat-card-title>
<mat-card-subtitle>
<ng-container *ngIf="item.pricing; else formatted">
<va-pricing [pricing]="item.pricing"
[highlightPrice]="false"></va-pricing>
</ng-container>
<ng-template #formatted>{{item.formattedPrice}}</ng-template>
</mat-card-subtitle>
</mat-card-header>
<mat-card-content> {{ item.tagline }}
</mat-card-content>
</div>
<button *ngIf="item.purchased !== undefined"
mat-button color="primary" class="enabled-indicator" (click)="purchasedClicked.emit(item)" [disabled]="item.purchased">
<i *ngIf="!item.purchased" class="material-icons">add</i>
<i *ngIf="item.purchased" class="material-icons" style="color: #39B74A">check</i>
<span>{{(item.purchased) ? 'ENABLED' : 'ENABLE'}}</span>
</button>
</div>
</mat-card>
`,
styles: [":host{position:relative}::ng-deep .mat-card-header-text{width:100%}mat-card.hover-card{overflow:hidden;padding:0;margin-bottom:0}mat-card.hover-card mat-card-subtitle,mat-card.hover-card mat-card-title{margin:0 80px 0 0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}mat-card.hover-card mat-card-subtitle{font-size:12px;margin-top:4px}.hover-area{position:relative;cursor:pointer}.product-banner{max-height:133px;overflow:hidden;display:flex;width:100%;height:100px;align-items:center;justify-content:center}.product-banner img{max-width:100%}@media screen and (min-width:480px){.hover-area{padding-top:55%}.hover-area:hover .description{top:0}.hover-area:hover .enabled-indicator span{width:70px}.product-banner{max-height:none;position:absolute;height:60%;top:0}}.description{box-sizing:border-box;padding:10px 16px 0;overflow:hidden;background:#fff;color:#212121;transition:.3s}.description:after{content:'';display:block;width:100%;height:40%;position:absolute;bottom:0;left:0;background:linear-gradient(rgba(255,255,255,0) 40%,#fff 70%)}mat-card-header{box-sizing:border-box;padding-bottom:16px;margin:0;height:40%;display:flex;align-items:center}mat-card-content{min-height:40px;max-height:80px;padding:16px;margin:0 -16px;border-top:1px solid #e0e0e0;font-size:12px}.enabled-indicator{position:absolute;right:8px;bottom:8px}.enabled-indicator span{display:inline-block;width:70px;overflow:hidden;transition:.3s}@media screen and (min-width:480px){.description{position:absolute;top:60%;width:100%;height:100%}.description:after{height:70%}mat-card-content{min-height:0;max-height:none}.enabled-indicator span{width:0}}button[mat-button]{padding:0 8px;min-width:0}mat-card-actions{position:relative;padding:8px!important;margin:0!important;text-align:right;border-top:1px solid #e0e0e0;background-color:#fff;color:#212121}.status{font-size:14px}.img-container{height:100%;width:100%}"]
}] }
];
/** @nocollapse */
StoreCardComponent.ctorParameters = () => [
{ type: ImageTransformationService }
];
StoreCardComponent.propDecorators = {
item: [{ type: Input }],
purchasedClicked: [{ type: Output }],
cardClicked: [{ type: Output }],
descriptionClicked: [{ type: Output }],
bannerClicked: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class StoreCardModule {
}
StoreCardModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule,
VaIconModule,
MatCardModule,
MatButtonModule,
MatIconModule,
MatMenuModule,
ImageTransformationModule,
VaPricingModule
],
declarations: [StoreCardComponent],
exports: [StoreCardComponent]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class StoreComponent {
constructor() {
this.items = [];
this.searchable = true;
this.itemClicked = new EventEmitter();
}
/**
* @return {?}
*/
get filteredItems() {
if (this.filterTerm) {
return this.items.filter(pkg => pkg.name.toLowerCase().indexOf(this.filterTerm.toLowerCase()) !== -1);
}
return this.items;
}
}
StoreComponent.decorators = [
{ type: Component, args: [{
selector: 'va-store',
template: `
<div class="toolbar">
<div *ngIf="searchable" class="table-controls-row">
<va-search-box (update)="filterTerm = $event"></va-search-box>
</div>
</div>
<div class="row row-gutters">
<div *ngFor="let item of filteredItems" class="col-flex">
<va-store-card [item]="item" (cardClicked)="this.itemClicked.emit(item)"></va-store-card>
</div>
</div>
`,
styles: [".flex-row{display:flex;flex-direction:row}.toolbar{padding:0;background-color:#fff;color:#616161}.toolbar .disabled{cursor:default}.toolbar .disabled mat-icon{cursor:default;color:#9e9e9e}.toolbar va-search-box{margin-right:10px;width:350px}.toolbar .table-controls-row{padding:0 10px 10px;display:flex;flex-direction:row;align-items:center}.toolbar .table-controls-row:first-of-type{padding-top:10px}.top-border{border-top:1px solid #fff}.selected{background-color:#fff}.row{display:flex;flex-wrap:wrap}.row+.row-gutters{margin-top:0}.row-gutters{margin-top:-20px;margin-left:-20px}.row-gutters>.col-flex{padding-top:20px;padding-left:20px}.col-flex{position:relative;max-width:100%;box-sizing:border-box;flex:0 0 auto;width:100%}@media screen and (min-width:480px){.col-flex{width:50%}}@media screen and (min-width:1200px){.col-flex{width:33.333333%}}"]
}] }
];
StoreComponent.propDecorators = {
items: [{ type: Input }],
searchable: [{ type: Input }],
itemClicked: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class DropDownFormSectionComponent {
constructor() {
this.startOpen = false;
this.displayAutoTitle = false;
this.titleText = '';
this.displayAutoDescription = true;
this.descriptionText = '';
this.editingHint = '';
this.expandable = true;
this.autoDescriptionText = '';
this.iconName = 'help_outline';
this.subscriptions = [];
this.uniqueIndex = 1;
this.autoTitleText = '';
}
/**
* @return {?}
*/
ngOnInit() {
while (this.parentForm.controls.hasOwnProperty(this.titleText + this.uniqueIndex)) {
this.uniqueIndex += 1;
}
this.parentForm.addControl(this.titleText + this.uniqueIndex, this.toFormGroup(this.fields));
/** @type {?} */
const form = (/** @type {?} */ (this.parentForm.controls[this.titleText + this.uniqueIndex]));
for (const key in form.controls) {
if (form.controls.hasOwnProperty(key)) {
if (this.prepopulatedData != null && this.prepopulatedData.hasOwnProperty(key)) {
form.controls[key].setValue(this.prepopulatedData[key]);
}
}
}
this.subscriptions.push(form.statusChanges.subscribe(change => {
/** @type {?} */
let missingFields = false;
for (const control in form.controls) {
if (!form.controls[control].value || (form.controls[control].value.constructor === Array && !form.controls[control].value[0])) {
this.iconName = 'help_outline';
missingFields = true;
if (change === 'INVALID') {
this.autoTitleText = '* Please fill out all required fields';
if (form.controls[control].dirty || form.controls[control].touched) {
this.iconName = 'warning';
break;
}
}
else {
this.autoTitleText = 'Optional fields unanswered';
}
}
}
if (!missingFields) {
this.iconName = 'check_circle';
this.autoTitleText = 'Complete';
}
}));
if (this.displayAutoDescription) {
this.subscriptions.push(form.valueChanges.subscribe(changes => {
/** @type {?} */
let description = '';
for (const key in changes) {
if (changes.hasOwnProperty(key) && changes[key] !== null && changes[key].length > 0) {
if (changes[key][0].name) {
for (const fileKey in changes[key]) {
if (changes[key][fileKey] != null) {
description += changes[key][fileKey].name;
description += ', ';
}
}
}
else {
description += changes[key];
description += ', ';
}
}
}
this.autoDescriptionText = description.substring(0, description.length - 2);
}));
}
form.updateValueAndValidity({ onlySelf: false, emitEvent: true });
}
/**
* @param {?} formFields
* @return {?}
*/
toFormGroup(formFields) {
/** @type {?} */
const group = {};
formFields.forEach(field => {
/** @type {?} */
let formControl;
if (field.controlType === 'checkbox') {
formControl = new FormControl(field.value);
}
else {
formControl = field.required ? new FormControl(field.value, Validators.required) : new FormControl(field.value);
}
group[field.id] = formControl;
this.subscriptions.push(formControl.valueChanges.subscribe(value => (field.value = value)));
});
return new FormGroup(group);
}
/**
* @return {?}
*/
ngOnDestroy() {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
}
DropDownFormSectionComponent.decorators = [
{ type: Component, args: [{
selector: 'va-dropdown-form-section',
template: `
<mat-card *ngIf="!expandable && !(expandable == undefined)" class="not-expandable-card">
<mat-icon class="not-expandable-icon valid"> check_circle</mat-icon>
<mat-card-header *ngIf="!displayAutoTitle" class="not-expandable-header">
{{ titleText }}
</mat-card-header>
<mat-card-header *ngIf="displayAutoTitle" class="not-expandable-header valid">
<div class="title"> {{ titleText }}</div>
<div *ngIf="titleText && autoTitleText"> </div>
<ng-container><i> Complete </i></ng-container>
</mat-card-header>
<mat-panel-description class="not-expandable-description">
{{ descriptionText }}
</mat-panel-description>
</mat-card>
<mat-expansion-panel *ngIf="expandable || expandable == undefined" [expanded]="startOpen">
<mat-expansion-panel-header>
<div class="dropdown-form-header">
<mat-icon
[ngClass]="{invalid: iconName=='warning', valid: iconName=='check_circle', question: iconName=='help_outline'}">
{{ iconName }}
</mat-icon>
<mat-panel-title *ngIf="!displayAutoTitle">
{{ titleText }}
</mat-panel-title>
<mat-panel-title *ngIf="displayAutoTitle"
[ngClass]="{valid: iconName=='check_circle', invalid: iconName=='warning'}">
<div class="title"> {{ titleText }}</div>
<div *ngIf="titleText && autoTitleText"> </div>
<ng-container><i> {{autoTitleText}} </i></ng-container>
</mat-panel-title>
<mat-panel-description *ngIf="displayAutoDescription" [ngClass]="{invalid: iconName=='warning'}">
{{ autoDescriptionText }}
</mat-panel-description>
<mat-panel-description *ngIf="!displayAutoDescription" [ngClass]="{invalid: iconName=='warning'}">
<i> {{descriptionText}} </i>
</mat-panel-description>
</div>
</mat-expansion-panel-header>
<div class="expansion-panel-body">
<va-field *ngFor="let field of fields" [field]="field"
[form]="parentForm.controls[titleText + uniqueIndex]"></va-field>
<p *ngIf="editingHint != ''" class="editing-hint"><i>{{ editingHint }}</i></p>
</div>
</mat-expansion-panel>
`,
styles: [":host-context(va-dropdown-form-section){font-size:14px}:host-context(va-dropdown-form-section) .expansion-panel-body{margin-top:-10px;display:block;width:60%}.mat-expanded,.mat-expansion-panel{transition:margin .4s}.dropdown-form-header{width:100%;display:flex}mat-panel-description{align-self:center;flex:inherit;display:initial;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}mat-panel-description.invalid{color:#c62828}mat-icon{margin-right:7px;-ms-grid-row-align:center;align-self:center}mat-icon.valid{color:#4caf50}mat-icon.invalid{color:#c62828}mat-icon.question{color:#9e9e9e}mat-panel-title{-ms-grid-row-align:center;align-self:center;flex:none;margin-right:20px}mat-panel-title.valid{color:#4caf50}mat-panel-title.invalid{color:#c62828}.editing-hint{color:#9e9e9e}.title{color:#212121}.not-expandable-card{background:#fff;cursor:default;font-size:15px;display:flex;height:48px;align-items:center;box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12)}.not-expandable-card .not-expandable-icon{margin-left:24px}.not-expandable-card .not-expandable-header{display:flex}.not-expandable-card .not-expandable-description{margin-left:22px}.not-expandable-card .valid{color:#4caf50}"]
}] }
];
/** @nocollapse */
DropDownFormSectionComponent.ctorParameters = () => [];
DropDownFormSectionComponent.propDecorators = {
prepopulatedData: [{ type: Input }],
startOpen: [{ type: Input }],
parentForm: [{ type: Input }],
displayAutoTitle: [{ type: Input }],
titleText: [{ type: Input }],
displayAutoDescription: [{ type: Input }],
descriptionText: [{ type: Input }],
fields: [{ type: Input }],
editingHint: [{ type: Input }],
expandable: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class FieldComponent {
/**
* @return {?}
*/
get isValid() {
return this.form.controls[this.field.id].valid || this.form.controls[this.field.id].pristine;
}
}
FieldComponent.decorators = [
{ type: Component, args: [{
selector: 'va-field',
template: "<ng-container [ngSwitch]=\"field.controlType\" [formGroup]=\"form\">\n\n <ng-container *ngSwitchCase=\"'dropdown'\">\n <mat-form-field class=\"form-field-container\">\n <mat-select matInput [formControlName]=\"field.id\" [id]=\"field.id\" [placeholder]=\"field.getLabel()\" [disabled]=\"field.disabled\">\n <mat-option *ngFor=\"let option of field.options\" [value]=\"option.value\" [disabled]=\"option.disabled\">{{ option.label }}</mat-option>\n </mat-select>\n <mat-hint *ngIf=\"field.description\"> {{ field.description }} </mat-hint>\n </mat-form-field>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'textbox'\">\n <mat-form-field class=\"form-field-container\">\n <span matPrefix *ngIf=\"field.prefix\"> {{ field.prefix}} </span>\n <input matInput [type]=\"field.textboxType\" [id]=\"field.id\" [formControlName]=\"field.id\" [placeholder]=\"field.getLabel()\" [pattern]=\"field.regexValidator || ''\" [readonly]=\"field.disabled\">\n <span matSuffix *ngIf=\"field.suffix\"> {{ field.suffix }} </span>\n <mat-hint *ngIf=\"field.description\"> {{ field.description }} </mat-hint>\n <mat-error *ngIf=\"form.controls[field.id].hasError('pattern')\"> {{field.regexErrorMessage}} </mat-error>\n </mat-form-field>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'checkbox'\">\n <div class=\"form-field-container checkbox\">\n <mat-checkbox [formControlName]=\"field.id\" [id]=\"field.id\" ngControlDefault [disabled]=\"field.disabled\"></mat-checkbox>\n <div class=\"checkbox-placeholder\"> {{field.label}}</div>\n </div>\n <div class=\"checkbox-description\" *ngIf=\"field.description\">{{ field.description }}</div>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'textarea'\">\n <mat-form-field class=\"form-field-container\">\n <textarea matInput [formControlName]=\"field.id\" [placeholder]=\"field.getLabel()\" [id]=\"field.id\" [readonly]=\"field.disabled\"></textarea>\n <mat-hint *ngIf=\"field.description\"> {{ field.description }} </mat-hint>\n </mat-form-field>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'file'\">\n <div class=\"form-field-container\">\n <file-group-uploader [uploadUrl]=\"field.uploadUrl\" [label]=\"field.getLabel()\" [description]=\"field.description\" [formGroup]=\"form\" [formControlName]=\"field.id\" [disabled]=\"field.disabled\"\n [numFiles]=\"field.numFiles\" class=\"form-field-container\"></file-group-uploader>\n </div>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'vbcuser'\">\n <mat-form-field class=\"form-field-container\">\n <mat-select matInput [formControlName]=\"field.id\" [id]=\"field.id\" [placeholder]=\"field.getLabel()\" [disabled]=\"field.disabled\">\n <mat-option *ngFor=\"let option of field.options\" [value]=\"option.value\" [disabled]=\"option.disabled\">{{ option.label }}</mat-option>\n </mat-select>\n <mat-hint *ngIf=\"field.description\"> {{ field.description }} </mat-hint>\n </mat-form-field>\n </ng-container>\n\n</ng-container>\n",
styles: [".form-field-container{font-size:16px;width:100%;margin:10px 0}.checkbox{display:flex;padding:5px 0;color:rgba(0,0,0,.54);margin-top:24px}.checkbox .checkbox-placeholder{margin-left:10px}.checkbox-description{margin-top:-14px;margin-left:32px;color:rgba(0,0,0,.54);font-size:12px}"]
}] }
];
FieldComponent.propDecorators = {
field: [{ type: Input }],
form: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class FieldService {
/**
* @param {?} field
* @return {?}
*/
createFormControl(field) {
if (field.required) {
return new FormControl(field.value, Validators.required);
}
else {
return new FormControl(field.value);
}
}
/**
* @param {?} formFields
* @param {?} subscriptions
* @return {?}
*/
toFormGroup(formFields, subscriptions) {
/** @type {?} */
const group = {};
formFields.forEach(field => {
/** @type {?} */
let formControl;
if (field.controlType === 'checkbox') {
formControl = new FormControl(field.value);
}
else {
formControl = this.createFormControl(field);
}
group[field.id] = formControl;
if (subscriptions) {
subscriptions.push(formControl.valueChanges.subscribe(value => (field.value = value)));
}
});
return new FormGroup(group);
}
}
FieldService.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class VaHeaderContainerComponent {
constructor() {
this.loaded = true;
this.showEnableAddon = false;
this.prerequisiteSelected = new EventEmitter();
this.actionSelected = new EventEmitter();
this.pricingActionSelected = new EventEmitter();
}
/**
* @return {?}
*/
onActionSelected() {
this.actionSelected.emit();
}
/**
* @return {?}
*/
onPrerequisiteSelected() {
this.prerequisiteSelected.emit();
}
/**
* @param {?} billedProduct
* @return {?}
*/
showPricingModel(billedProduct) {
return billedProduct && [BillingModel.Tiered, BillingModel.Stairstep].includes(billedProduct.billingModel);
}
/**
* @param {?} billingModel
* @return {?}
*/
getPricingModelTooltips(billingModel) {
switch (billingModel) {
case BillingModel.Stairstep:
return 'Price applies per unit within a range';
case BillingModel.Tiered:
return 'Price applies to all units once range is met';
default:
return '';
}
}
}
VaHeaderContainerComponent.decorators = [
{ type: Component, args: [{
selector: 'va-header-container',
template: "<div class=\"header-container\">\n <div class=\"product-overview\">\n <div class=\"product-id\">\n\n <va-icon [iconUrl]=\"iconUrl\" [name]=\"title\" [diameter]=\"120\"></va-icon>\n\n <div class=\"product-id-text\">\n\n <h1 [ngClass]=\"{'stencil-title stencil-shimmer': !title, 'product-title': title}\">\n {{ title }}\n </h1>\n\n <span [ngClass]=\"{'stencil-tagline stencil-shimmer': !title && !tagline, 'tagline': tagline}\">\n {{ tagline }}\n </span>\n\n <div *ngIf=\"prerequisite\" class=\"prerequisite\">\n <span class=\"requires-text\">Requires </span>\n <span>{{ prerequisite }}</span>\n </div>\n\n <mat-chip-list>\n <mat-chip *ngFor=\"let chip of chipLabels\">{{ chip }}</mat-chip>\n </mat-chip-list>\n\n </div>\n </div>\n\n <div class=\"pricing\">\n\n <button *ngIf=\"showAction && !showEnableAddon\" mat-raised-button class=\"app-enable-button\" [disabled]=\"!actionEnabled\" (click)=\"onActionSelected()\">\n {{ actionLabel }}\n </button>\n\n <button *ngIf=\"showEnableAddon && !showAction\" mat-raised-button class=\"app-enable-button\" [disabled]=\"!showEnableAddon\" (click)=\"onActionSelected()\">\n {{ actionLabel }}\n </button>\n\n <div *ngIf=\"prerequisiteLabel && !showEnableAddon\" class=\"addon-enable\" (click)=\"onPrerequisiteSelected()\">\n Enabled with {{prerequisiteLabel}}\n </div>\n\n <div *ngIf=\"showPricing\" class=\"price-box\">\n <div *ngIf=\"pricing?.prices?.length\" class=\"wholesale-price\">{{ pricingLabel }}\n <div *ngIf=\"showPricingModel(billedProduct) && hasVerifiedContract\"\n class=\"pricing-model\"\n matTooltip=\"{{getPricingModelTooltips(billedProduct.billingModel)}}\">({{ billedProduct.billingModel }})\n </div>\n </div>\n <va-pricing [pricing]=\"pricing\"\n [billedProduct]=\"billedProduct\"\n [hasVerifiedContract]=\"hasVerifiedContract\"\n [loaded]=\"loaded\"></va-pricing>\n </div>\n\n <p *ngIf=\"pricingActionEnabled\" class=\"pricing-action-container\">\n <a (click)=\"pricingActionSelected.emit()\">{{ pricingActionLabel }}</a>\n </p>\n\n </div>\n\n </div>\n</div>\n",
styles: [":host{font-size:16px}.product-title{font-size:24px}.product-overview{display:flex;flex-wrap:wrap;border-bottom:1px solid #e0e0e0}.product-overview h1{margin:0;font-weight:400;line-height:1.2}.product-overview mat-chip{margin-bottom:3px}.product-id{display:flex;width:100%;padding:24px;flex-grow:1}.product-id va-icon{margin-right:20px}@media screen and (max-width:600px){.product-id va-icon ::ng-deep .va-icon-container{width:40px!important;height:40px!important}.product-id va-icon ::ng-deep .va-icon-container span{line-height:40px!important;font-size:15px!important}}.product-id .product-id-text{display:flex;flex-direction:column;justify-content:center}.product-id span{overflow:hidden}.product-id .tagline{color:#616161;margin:.5em 0 1em;font-size:16px}@media screen and (min-width:600px){.product-title{font-size:32px}.product-id .tagline{font-size:20px}.product-id{width:66%;padding-right:20px}}.product-id .prerequisite{margin:.5em 0 1em;font-size:14px}.product-id .requires-text{color:#9e9e9e}.inline-link{display:inline-flex!important;color:#1e88e5;cursor:pointer;padding-top:5px}.pricing{display:flex;flex-direction:column;width:100%;padding:0 24px 24px;color:#616161}@media screen and (min-width:600px){.pricing{width:34%;padding-top:24px;padding-left:20px}}.pricing span{display:block}.pricing span:nth-child(2){margin:.5em 0 1em}.pricing .price{font-size:24px;font-weight:700;line-height:1;color:#4caf50}.pricing .price-box{padding-top:10px}.pricing .pricing-action-container{margin:8px 0}.pricing .pricing-action-container mat-icon{vertical-align:middle;margin-right:6px}.pricing .pricing-action-container a{cursor:pointer}.pricing .pricing-model{display:inline-block;font-size:.8rem;color:#9e9e9e;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.app-enable-button{height:inherit;color:#fff;background-color:#4caf50;font-size:20px;padding:14px 16px;line-height:1}.addon-enable{height:inherit;background-color:#e0e0e0;font-size:14px;padding:14px 16px;line-height:1;color:#1e88e5;cursor:pointer;text-align:center}.wholesale-price{margin-top:0;padding-bottom:8px;margin-bottom:4px;border-bottom:1px solid #e0e0e0}.stencil-title{height:32px;width:200px;margin-bottom:5px!important}.stencil-tagline{height:32px;width:250px}"]
}] }
];
VaHeaderContainerComponent.propDecorators = {
iconUrl: [{ type: Input }],
title: [{ type: Input }],
tagline: [{ type: Input }],
prerequisite: [{ type: Input }],
chipLabels: [{ type: Input }],
pricing: [{ type: Input }],
billedProduct: [{ type: Input }],
loaded: [{ type: Input }],
hasVerifiedContract: [{ type: Input }],
pricingLabel: [{ type: Input }],
pricingActionLabel: [{ type: Input }],
pricingActionEnabled: [{ type: Input }],
actionEnabled: [{ type: Input }],
actionLabel: [{ type: Input }],
showAction: [{ type: Input }],
showPricing: [{ type: Input }],
showEnableAddon: [{ type: Input }],
prerequisiteLabel: [{ type: Input }],
prerequisiteSelected: [{ type: Output }],
actionSelected: [{ type: Output }],
pricingActionSelected: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class VaHeaderContainerModule {
}
VaHeaderContainerModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule,
MatIconModule,
VaPricingModule,
VaIconModule,
MatChipsModule,
MatButtonModule,
MatTooltipModule,
],
declarations: [VaHeaderContainerComponent],
exports: [VaHeaderContainerComponent]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class OrderFormSectionData {
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class OrderFormSectionComponent {
constructor() { }
/**
* @return {?}
*/
ngOnInit() {
// The first section should always be expanded.
/** @type {?} */
const topSection = this.getTopSection();
if (topSection) {
topSection.startOpen = true;
}
}
/**
* @return {?}
*/
getTopSection() {
if (!this.data) {
return null;
}
if (this.data.primarySection) {
return this.data.primarySection;
}
if (this.data.subsections && this.data.subsections.length > 0) {
return this.data.subsections[0];
}
return null;
}
}
OrderFormSectionComponent.decorators = [
{ type: Component, args: [{
selector: 'va-order-form-section',
template: "<div>\n <mat-card class=\"va-order-form-section-header mat-card\">\n <div class=\"product-overview\">\n <div class=\"product-id\">\n <va-icon [iconUrl]=\"data.iconUrl\" [name]=\"data.titleText\" [diameter]=\"45\"></va-icon>\n <div class=\"product-id-text\">\n <h1 class=\"product-title\">{{ data.titleText }}</h1>\n <span *ngIf=\"data.subtitleText\" class=\"tagline\">{{ data.subtitleText }}</span>\n </div>\n </div>\n <div class=\"description\" *ngIf=\"data.descriptionText\">\n <span>{{ data.descriptionText }}</span>\n </div>\n </div>\n </mat-card>\n <va-dropdown-form-section class=\"va-primary-form-section\" *ngIf=\"data.primarySection\"\n [titleText]=\"data.primarySection.titleText\"\n [editingHint]=\"data.primarySection.editingHint\"\n [displayAutoDescription]=\"data.primarySection.displayAutoDescription\"\n [descriptionText]=\"data.primarySection.descriptionText\"\n [displayAutoTitle]=\"data.primarySection.displayAutoTitle\"\n [prepopulatedData]=\"data.primarySection.prepopulatedData\"\n [parentForm]=\"data.parentForm\"\n [fields]=\"data.primarySection.fields\"\n [startOpen]=\"data.primarySection.startOpen\"\n [expandable]=\"data.primarySection.expandable\">\n </va-dropdown-form-section>\n <ng-container *ngIf=\"data.subsections?.length > 0\">\n <va-dropdown-form-section class=\"va-secondary-form-section\" *ngFor=\"let addon of data.subsections\"\n [titleText]=\"addon.titleText\"\n [editingHint]=\"addon.editingHint\"\n [displayAutoDescription]=\"addon.displayAutoDescription\"\n [descriptionText]=\"addon.descriptionText\"\n [displayAutoTitle]=\"addon.displayAutoTitle\"\n [prepopulatedData]=\"addon.prepopulatedData\"\n [parentForm]=\"data.parentForm\"\n [fields]=\"addon.fields\"\n [startOpen]=\"addon.startOpen\"\n [expandable]=\"addon.expandable\">\n </va-dropdown-form-section>\n </ng-container>\n <ng-container *ngIf=\"!data.primarySection && !(data.subsections?.len