@vendure/admin-ui 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/bundles/vendure-admin-ui-catalog.umd.js +53 -101
  2. package/bundles/vendure-admin-ui-catalog.umd.js.map +1 -1
  3. package/bundles/vendure-admin-ui-core.umd.js +115 -54
  4. package/bundles/vendure-admin-ui-core.umd.js.map +1 -1
  5. package/bundles/vendure-admin-ui-customer.umd.js +52 -2
  6. package/bundles/vendure-admin-ui-customer.umd.js.map +1 -1
  7. package/bundles/vendure-admin-ui-login.umd.js +40 -8
  8. package/bundles/vendure-admin-ui-login.umd.js.map +1 -1
  9. package/bundles/vendure-admin-ui-order.umd.js +52 -3
  10. package/bundles/vendure-admin-ui-order.umd.js.map +1 -1
  11. package/catalog/components/apply-facet-dialog/apply-facet-dialog.component.d.ts +1 -2
  12. package/catalog/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.d.ts +0 -1
  13. package/catalog/components/product-detail/product-detail.component.d.ts +2 -7
  14. package/catalog/components/product-variants-list/product-variants-list.component.d.ts +11 -11
  15. package/catalog/providers/product-detail/product-detail.service.d.ts +1 -2
  16. package/catalog/vendure-admin-ui-catalog.metadata.json +1 -1
  17. package/core/common/generated-types.d.ts +59 -0
  18. package/core/common/version.d.ts +1 -1
  19. package/core/data/definitions/facet-definitions.d.ts +1 -0
  20. package/core/data/providers/facet-data.service.d.ts +5 -1
  21. package/core/providers/custom-history-entry-component/history-entry-component-types.d.ts +81 -0
  22. package/core/providers/custom-history-entry-component/history-entry-component.service.d.ts +16 -0
  23. package/core/public_api.d.ts +2 -1
  24. package/core/shared/components/facet-value-selector/facet-value-selector.component.d.ts +17 -17
  25. package/core/shared/dynamic-form-inputs/facet-value-form-input/facet-value-form-input.component.d.ts +3 -11
  26. package/core/shared/dynamic-form-inputs/register-dynamic-input-components.d.ts +1 -2
  27. package/core/vendure-admin-ui-core.metadata.json +1 -1
  28. package/customer/components/customer-detail/customer-detail.component.d.ts +3 -3
  29. package/customer/components/customer-history/customer-history-entry-host.component.d.ts +16 -0
  30. package/customer/components/customer-history/customer-history.component.d.ts +19 -3
  31. package/customer/public_api.d.ts +1 -0
  32. package/customer/vendure-admin-ui-customer.metadata.json +1 -1
  33. package/esm2015/catalog/components/apply-facet-dialog/apply-facet-dialog.component.js +2 -2
  34. package/esm2015/catalog/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.js +2 -3
  35. package/esm2015/catalog/components/product-detail/product-detail.component.js +16 -47
  36. package/esm2015/catalog/components/product-list/product-list-bulk-actions.js +4 -7
  37. package/esm2015/catalog/components/product-variants-list/product-variants-list.component.js +38 -30
  38. package/esm2015/catalog/providers/product-detail/product-detail.service.js +1 -4
  39. package/esm2015/core/common/generated-types.js +1 -1
  40. package/esm2015/core/common/introspection-result.js +2 -1
  41. package/esm2015/core/common/version.js +2 -2
  42. package/esm2015/core/data/definitions/facet-definitions.js +12 -1
  43. package/esm2015/core/data/providers/facet-data.service.js +5 -2
  44. package/esm2015/core/providers/custom-history-entry-component/history-entry-component-types.js +2 -0
  45. package/esm2015/core/providers/custom-history-entry-component/history-entry-component.service.js +39 -0
  46. package/esm2015/core/public_api.js +3 -2
  47. package/esm2015/core/shared/components/facet-value-selector/facet-value-selector.component.js +52 -22
  48. package/esm2015/core/shared/components/form-field/form-field.component.js +2 -2
  49. package/esm2015/core/shared/components/timeline-entry/timeline-entry.component.js +2 -2
  50. package/esm2015/core/shared/dynamic-form-inputs/facet-value-form-input/facet-value-form-input.component.js +3 -15
  51. package/esm2015/customer/components/customer-detail/customer-detail.component.js +1 -1
  52. package/esm2015/customer/components/customer-history/customer-history-entry-host.component.js +51 -0
  53. package/esm2015/customer/components/customer-history/customer-history.component.js +12 -4
  54. package/esm2015/customer/customer.module.js +3 -1
  55. package/esm2015/customer/public_api.js +2 -1
  56. package/esm2015/login/components/login/login.component.js +37 -5
  57. package/esm2015/order/components/order-detail/order-detail.component.js +1 -1
  58. package/esm2015/order/components/order-history/order-history-entry-host.component.js +51 -0
  59. package/esm2015/order/components/order-history/order-history.component.js +12 -5
  60. package/esm2015/order/order.module.js +6 -4
  61. package/esm2015/order/public_api.js +2 -1
  62. package/fesm2015/vendure-admin-ui-catalog.js +54 -85
  63. package/fesm2015/vendure-admin-ui-catalog.js.map +1 -1
  64. package/fesm2015/vendure-admin-ui-core.js +106 -39
  65. package/fesm2015/vendure-admin-ui-core.js.map +1 -1
  66. package/fesm2015/vendure-admin-ui-customer.js +63 -5
  67. package/fesm2015/vendure-admin-ui-customer.js.map +1 -1
  68. package/fesm2015/vendure-admin-ui-login.js +36 -4
  69. package/fesm2015/vendure-admin-ui-login.js.map +1 -1
  70. package/fesm2015/vendure-admin-ui-order.js +63 -6
  71. package/fesm2015/vendure-admin-ui-order.js.map +1 -1
  72. package/login/components/login/login.component.d.ts +11 -1
  73. package/login/vendure-admin-ui-login.metadata.json +1 -1
  74. package/order/components/order-detail/order-detail.component.d.ts +3 -3
  75. package/order/components/order-history/order-history-entry-host.component.d.ts +16 -0
  76. package/order/components/order-history/order-history.component.d.ts +18 -3
  77. package/order/public_api.d.ts +1 -0
  78. package/order/vendure-admin-ui-order.metadata.json +1 -1
  79. package/package.json +2 -2
  80. package/static/i18n-messages/de.json +3 -1
  81. package/static/i18n-messages/en.json +3 -1
  82. package/static/styles/_variables.scss +1 -0
  83. package/core/common/utilities/flatten-facet-values.d.ts +0 -2
  84. package/esm2015/core/common/utilities/flatten-facet-values.js +0 -4
@@ -3,11 +3,11 @@ import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, Injec
3
3
  import * as i1 from '@angular/router';
4
4
  import { Router, ActivatedRoute, RouterModule } from '@angular/router';
5
5
  import * as i2 from '@vendure/admin-ui/core';
6
- import { BaseDetailComponent, ServerConfigService, NotificationService, DataService, BaseListComponent, SortOrder, LogicalOperator, DeletionResult, ModalService, Permission, unicodePatternValidator, findTranslation, getConfigArgValue, createUpdatedTranslatable, encodeConfigArgValue, LocalStorageService, SelectionManager, FacetValueSelectorComponent, flattenFacetValues, getChannelCodeFromUserStatus, JobState, JobQueueService, getDefaultUiLanguage, BaseEntityResolver, AssetType, createResolveData, CanDeactivateDetailGuard, detailBreadcrumb, AssetPickerDialogComponent, AssetPreviewDialogComponent, isMultiChannel, currentChannelIsNotDefault, GlobalFlag, SharedModule, BulkActionRegistryService } from '@vendure/admin-ui/core';
6
+ import { BaseDetailComponent, ServerConfigService, NotificationService, DataService, BaseListComponent, SortOrder, LogicalOperator, DeletionResult, ModalService, Permission, unicodePatternValidator, findTranslation, getConfigArgValue, createUpdatedTranslatable, encodeConfigArgValue, LocalStorageService, SelectionManager, FacetValueSelectorComponent, getChannelCodeFromUserStatus, JobState, JobQueueService, getDefaultUiLanguage, BaseEntityResolver, AssetType, createResolveData, CanDeactivateDetailGuard, detailBreadcrumb, AssetPickerDialogComponent, AssetPreviewDialogComponent, isMultiChannel, currentChannelIsNotDefault, GlobalFlag, SharedModule, BulkActionRegistryService } from '@vendure/admin-ui/core';
7
7
  import { marker } from '@biesbjerg/ngx-translate-extract-marker';
8
- import { map, debounceTime, takeUntil, finalize, switchMap, filter, take, mergeMap, tap, shareReplay, distinctUntilChanged, mapTo, startWith, skipUntil, skip, withLatestFrom, delay, defaultIfEmpty, catchError } from 'rxjs/operators';
8
+ import { map, debounceTime, takeUntil, finalize, switchMap, filter, take, mergeMap, tap, shareReplay, distinctUntilChanged, mapTo, startWith, skipUntil, skip, switchMapTo, delay, withLatestFrom, defaultIfEmpty, catchError } from 'rxjs/operators';
9
9
  import { FormGroup, FormControl, FormBuilder, Validators, FormArray, NG_VALUE_ACCESSOR } from '@angular/forms';
10
- import { BehaviorSubject, combineLatest, EMPTY, Subject, merge, of, forkJoin, throwError, from } from 'rxjs';
10
+ import { BehaviorSubject, combineLatest, EMPTY, Subject, merge, of, forkJoin, throwError, from, concat } from 'rxjs';
11
11
  import { normalizeString } from '@vendure/common/lib/normalize-string';
12
12
  import { notNullOrUndefined, generateAllCombinations } from '@vendure/common/lib/shared-utils';
13
13
  import { SortOrder as SortOrder$1 } from '@vendure/common/lib/generated-shop-types';
@@ -1039,9 +1039,6 @@ class ProductDetailService {
1039
1039
  constructor(dataService) {
1040
1040
  this.dataService = dataService;
1041
1041
  }
1042
- getFacets() {
1043
- return this.dataService.facet.getAllFacets().mapSingle(data => data.facets.items);
1044
- }
1045
1042
  getTaxCategories() {
1046
1043
  return this.dataService.settings
1047
1044
  .getTaxCategories()
@@ -1264,7 +1261,7 @@ class ApplyFacetDialogComponent {
1264
1261
  ApplyFacetDialogComponent.decorators = [
1265
1262
  { type: Component, args: [{
1266
1263
  selector: 'vdr-apply-facet-dialog',
1267
- template: "<ng-template vdrDialogTitle>{{ 'catalog.add-facets' | translate }}</ng-template>\r\n\r\n<vdr-facet-value-selector\r\n [facets]=\"facets\"\r\n (selectedValuesChange)=\"selectedValues = $event\"\r\n></vdr-facet-value-selector>\r\n\r\n<ng-template vdrDialogButtons>\r\n <button type=\"button\" class=\"btn\" (click)=\"cancel()\">{{ 'common.cancel' | translate }}</button>\r\n <button\r\n type=\"submit\"\r\n (click)=\"selectValues()\"\r\n [disabled]=\"selectedValues.length === 0\"\r\n class=\"btn btn-primary\"\r\n >\r\n {{ 'catalog.add-facets' | translate }}\r\n </button>\r\n</ng-template>\r\n",
1264
+ template: "<ng-template vdrDialogTitle>{{ 'catalog.add-facets' | translate }}</ng-template>\r\n\r\n<vdr-facet-value-selector\r\n (selectedValuesChange)=\"selectedValues = $event\"\r\n></vdr-facet-value-selector>\r\n\r\n<ng-template vdrDialogButtons>\r\n <button type=\"button\" class=\"btn\" (click)=\"cancel()\">{{ 'common.cancel' | translate }}</button>\r\n <button\r\n type=\"submit\"\r\n (click)=\"selectValues()\"\r\n [disabled]=\"selectedValues.length === 0\"\r\n class=\"btn btn-primary\"\r\n >\r\n {{ 'catalog.add-facets' | translate }}\r\n </button>\r\n</ng-template>\r\n",
1268
1265
  changeDetection: ChangeDetectionStrategy.OnPush,
1269
1266
  styles: [""]
1270
1267
  },] }
@@ -1451,20 +1448,13 @@ class ProductDetailComponent extends BaseDetailComponent {
1451
1448
  .subscribe(([variants, languageCode]) => {
1452
1449
  this.buildVariantFormArray(variants, languageCode);
1453
1450
  });
1454
- // FacetValues are provided initially by the nested array of the
1455
- // Product entity, but once a fetch to get all Facets is made (as when
1456
- // opening the FacetValue selector modal), then these additional values
1457
- // are concatenated onto the initial array.
1458
- this.facets$ = this.productDetailService.getFacets();
1459
1451
  const productFacetValues$ = this.product$.pipe(map(product => product.facetValues));
1460
- const allFacetValues$ = this.facets$.pipe(map(flattenFacetValues));
1461
1452
  const productGroup = this.getProductFormGroup();
1462
- const formFacetValueIdChanges$ = productGroup.valueChanges.pipe(map(val => val.facetValueIds), distinctUntilChanged());
1463
- const formChangeFacetValues$ = combineLatest(formFacetValueIdChanges$, productFacetValues$, allFacetValues$).pipe(map(([ids, productFacetValues, allFacetValues]) => {
1464
- const combined = [...productFacetValues, ...allFacetValues];
1465
- return ids.map(id => combined.find(fv => fv.id === id)).filter(notNullOrUndefined);
1466
- }));
1467
- this.facetValues$ = merge(productFacetValues$, formChangeFacetValues$);
1453
+ // tslint:disable-next-line:no-non-null-assertion
1454
+ const formFacetValueIdChanges$ = productGroup.get('facetValueIds').valueChanges.pipe(skip(1), distinctUntilChanged(), switchMap(ids => this.dataService.facet
1455
+ .getFacetValues({ filter: { id: { in: ids } } })
1456
+ .mapSingle(({ facetValues }) => facetValues.items)), shareReplay(1));
1457
+ this.facetValues$ = concat(productFacetValues$.pipe(take(1)), productFacetValues$.pipe(switchMapTo(formFacetValueIdChanges$)));
1468
1458
  this.productChannels$ = this.product$.pipe(map(p => p.channels));
1469
1459
  this.channelPriceIncludesTax$ = this.dataService.settings
1470
1460
  .getActiveChannel('cache-first')
@@ -1629,32 +1619,6 @@ class ProductDetailComponent extends BaseDetailComponent {
1629
1619
  });
1630
1620
  productGroup.markAsDirty();
1631
1621
  }
1632
- /**
1633
- * Opens a dialog to select FacetValues to apply to the select ProductVariants.
1634
- */
1635
- selectVariantFacetValue(selectedVariantIds) {
1636
- this.displayFacetValueModal()
1637
- .pipe(withLatestFrom(this.variants$))
1638
- .subscribe(([facetValueIds, variants]) => {
1639
- if (facetValueIds) {
1640
- for (const variantId of selectedVariantIds) {
1641
- const index = variants.findIndex(v => v.id === variantId);
1642
- const variant = variants[index];
1643
- const existingFacetValueIds = variant ? variant.facetValues.map(fv => fv.id) : [];
1644
- const variantFormGroup = this.detailForm.get('variants').controls.find(c => c.value.id === variantId);
1645
- if (variantFormGroup) {
1646
- const uniqueFacetValueIds = unique([...existingFacetValueIds, ...facetValueIds]);
1647
- variantFormGroup.patchValue({
1648
- facetValueIds: uniqueFacetValueIds,
1649
- });
1650
- variantFormGroup.markAsDirty();
1651
- this.variantFacetValueChanges[variantId] = uniqueFacetValueIds;
1652
- }
1653
- }
1654
- this.changeDetector.markForCheck();
1655
- }
1656
- });
1657
- }
1658
1622
  variantsToCreateAreValid() {
1659
1623
  return (0 < this.createVariantsConfig.variants.length &&
1660
1624
  this.createVariantsConfig.variants.every(v => {
@@ -1662,11 +1626,12 @@ class ProductDetailComponent extends BaseDetailComponent {
1662
1626
  }));
1663
1627
  }
1664
1628
  displayFacetValueModal() {
1665
- return this.productDetailService.getFacets().pipe(mergeMap(facets => this.modalService.fromComponent(ApplyFacetDialogComponent, {
1629
+ return this.modalService
1630
+ .fromComponent(ApplyFacetDialogComponent, {
1666
1631
  size: 'md',
1667
1632
  closable: true,
1668
- locals: { facets },
1669
- })), map(facetValues => facetValues && facetValues.map(v => v.id)));
1633
+ })
1634
+ .pipe(map(facetValues => facetValues && facetValues.map(v => v.id)));
1670
1635
  }
1671
1636
  create() {
1672
1637
  const productGroup = this.getProductFormGroup();
@@ -1721,6 +1686,7 @@ class ProductDetailComponent extends BaseDetailComponent {
1721
1686
  this.detailForm.markAsPristine();
1722
1687
  this.assetChanges = {};
1723
1688
  this.variantAssetChanges = {};
1689
+ this.variantFacetValueChanges = {};
1724
1690
  this.notificationService.success(marker('common.notify-update-success'), {
1725
1691
  entity: 'Product',
1726
1692
  });
@@ -1759,7 +1725,7 @@ class ProductDetailComponent extends BaseDetailComponent {
1759
1725
  const variantTranslation = findTranslation(variant, languageCode);
1760
1726
  const pendingFacetValueChanges = this.variantFacetValueChanges[variant.id];
1761
1727
  const facetValueIds = pendingFacetValueChanges
1762
- ? pendingFacetValueChanges
1728
+ ? pendingFacetValueChanges.map(fv => fv.id)
1763
1729
  : variant.facetValues.map(fv => fv.id);
1764
1730
  const group = {
1765
1731
  id: variant.id,
@@ -1874,7 +1840,7 @@ class ProductDetailComponent extends BaseDetailComponent {
1874
1840
  ProductDetailComponent.decorators = [
1875
1841
  { type: Component, args: [{
1876
1842
  selector: 'vdr-product-detail',
1877
- template: "<vdr-action-bar>\r\n <vdr-ab-left>\r\n <div class=\"flex clr-flex-row\">\r\n <vdr-entity-info [entity]=\"entity$ | async\"></vdr-entity-info>\r\n <clr-toggle-wrapper *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\">\r\n <input\r\n type=\"checkbox\"\r\n clrToggle\r\n name=\"enabled\"\r\n [formControl]=\"detailForm.get(['product', 'enabled'])\"\r\n />\r\n <label>{{ 'common.enabled' | translate }}</label>\r\n </clr-toggle-wrapper>\r\n </div>\r\n <vdr-language-selector\r\n [disabled]=\"isNew$ | async\"\r\n [availableLanguageCodes]=\"availableLanguages$ | async\"\r\n [currentLanguageCode]=\"languageCode$ | async\"\r\n (languageCodeChange)=\"setLanguage($event)\"\r\n ></vdr-language-selector>\r\n </vdr-ab-left>\r\n\r\n <vdr-ab-right>\r\n <vdr-action-bar-items locationId=\"product-detail\"></vdr-action-bar-items>\r\n <button\r\n class=\"btn btn-primary\"\r\n *ngIf=\"isNew$ | async; else updateButton\"\r\n (click)=\"create()\"\r\n [disabled]=\"detailForm.invalid || detailForm.pristine || !variantsToCreateAreValid()\"\r\n >\r\n {{ 'common.create' | translate }}\r\n </button>\r\n <ng-template #updateButton>\r\n <button\r\n *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\"\r\n class=\"btn btn-primary\"\r\n (click)=\"save()\"\r\n [disabled]=\"\r\n (detailForm.invalid || detailForm.pristine) && !assetsChanged() && !variantAssetsChanged()\r\n \"\r\n >\r\n {{ 'common.update' | translate }}\r\n </button>\r\n </ng-template>\r\n </vdr-ab-right>\r\n</vdr-action-bar>\r\n\r\n<form class=\"form\" [formGroup]=\"detailForm\" *ngIf=\"product$ | async as product\">\r\n <button type=\"submit\" hidden x-data=\"prevents enter key from triggering other buttons\"></button>\r\n <clr-tabs>\r\n <clr-tab>\r\n <button clrTabLink (click)=\"navigateToTab('details')\">\r\n {{ 'catalog.product-details' | translate }}\r\n </button>\r\n <clr-tab-content *clrIfActive=\"(activeTab$ | async) === 'details'\">\r\n <div class=\"clr-row\">\r\n <div class=\"clr-col\">\r\n <section class=\"form-block\" formGroupName=\"product\">\r\n <ng-container *ngIf=\"!(isNew$ | async)\">\r\n <ng-container *vdrIfMultichannel>\r\n <vdr-form-item\r\n [label]=\"'common.channels' | translate\"\r\n *vdrIfDefaultChannelActive\r\n >\r\n <div class=\"flex channel-assignment\">\r\n <ng-container *ngFor=\"let channel of productChannels$ | async\">\r\n <vdr-chip\r\n *ngIf=\"!isDefaultChannel(channel.code)\"\r\n icon=\"times-circle\"\r\n (iconClick)=\"removeFromChannel(channel.id)\"\r\n >\r\n <vdr-channel-badge\r\n [channelCode]=\"channel.code\"\r\n ></vdr-channel-badge>\r\n {{ channel.code | channelCodeToLabel }}\r\n </vdr-chip>\r\n </ng-container>\r\n <button class=\"btn btn-sm\" (click)=\"assignToChannel()\">\r\n <clr-icon shape=\"layers\"></clr-icon>\r\n {{ 'catalog.assign-to-channel' | translate }}\r\n </button>\r\n </div>\r\n </vdr-form-item>\r\n </ng-container>\r\n </ng-container>\r\n <vdr-form-field [label]=\"'catalog.product-name' | translate\" for=\"name\">\r\n <input\r\n id=\"name\"\r\n type=\"text\"\r\n formControlName=\"name\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n (input)=\"updateSlug($event.target.value)\"\r\n />\r\n </vdr-form-field>\r\n <div\r\n class=\"auto-rename-wrapper\"\r\n [class.visible]=\"\r\n (isNew$ | async) === false && detailForm.get(['product', 'name'])?.dirty\r\n \"\r\n >\r\n <clr-checkbox-wrapper>\r\n <input\r\n clrCheckbox\r\n type=\"checkbox\"\r\n id=\"auto-update\"\r\n formControlName=\"autoUpdateVariantNames\"\r\n />\r\n <label>{{\r\n 'catalog.auto-update-product-variant-name' | translate\r\n }}</label>\r\n </clr-checkbox-wrapper>\r\n </div>\r\n <vdr-form-field\r\n [label]=\"'catalog.slug' | translate\"\r\n for=\"slug\"\r\n [errors]=\"{ pattern: 'catalog.slug-pattern-error' | translate }\"\r\n >\r\n <input\r\n id=\"slug\"\r\n type=\"text\"\r\n formControlName=\"slug\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n />\r\n </vdr-form-field>\r\n <vdr-rich-text-editor\r\n formControlName=\"description\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n [label]=\"'common.description' | translate\"\r\n ></vdr-rich-text-editor>\r\n\r\n <section formGroupName=\"customFields\" *ngIf=\"customFields.length\">\r\n <label>{{ 'common.custom-fields' | translate }}</label>\r\n <vdr-tabbed-custom-fields\r\n entityName=\"Product\"\r\n [customFields]=\"customFields\"\r\n [customFieldsFormGroup]=\"detailForm.get(['product', 'customFields'])\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n ></vdr-tabbed-custom-fields>\r\n </section>\r\n <vdr-custom-detail-component-host\r\n locationId=\"product-detail\"\r\n [entity$]=\"entity$\"\r\n [detailForm]=\"detailForm\"\r\n ></vdr-custom-detail-component-host>\r\n </section>\r\n </div>\r\n <div class=\"clr-col-md-auto\">\r\n <vdr-assets\r\n [assets]=\"assetChanges.assets || product.assets\"\r\n [featuredAsset]=\"assetChanges.featuredAsset || product.featuredAsset\"\r\n [updatePermissions]=\"updatePermissions\"\r\n (change)=\"assetChanges = $event\"\r\n ></vdr-assets>\r\n <div class=\"facets\">\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of facetValues$ | async\"\r\n [facetValue]=\"facetValue\"\r\n [removable]=\"['UpdateCatalog', 'UpdateProduct'] | hasPermission\"\r\n (remove)=\"removeProductFacetValue(facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n <button\r\n class=\"btn btn-sm btn-secondary\"\r\n *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\"\r\n (click)=\"selectProductFacetValue()\"\r\n >\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-facets' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"isNew$ | async\">\r\n <h4>{{ 'catalog.product-variants' | translate }}</h4>\r\n <vdr-generate-product-variants\r\n (variantsChange)=\"createVariantsConfig = $event\"\r\n ></vdr-generate-product-variants>\r\n </div>\r\n </clr-tab-content>\r\n </clr-tab>\r\n <clr-tab *ngIf=\"!(isNew$ | async)\">\r\n <button clrTabLink (click)=\"navigateToTab('variants')\">\r\n {{ 'catalog.product-variants' | translate }}\r\n </button>\r\n <clr-tab-content *clrIfActive=\"(activeTab$ | async) === 'variants'\">\r\n <section class=\"form-block\">\r\n <div class=\"view-mode\">\r\n <div class=\"btn-group\">\r\n <button\r\n class=\"btn btn-secondary-outline\"\r\n (click)=\"variantDisplayMode = 'card'\"\r\n [class.btn-primary]=\"variantDisplayMode === 'card'\"\r\n >\r\n <clr-icon shape=\"list\"></clr-icon>\r\n <span class=\"full-label\">{{ 'catalog.display-variant-cards' | translate }}</span>\r\n </button>\r\n <button\r\n class=\"btn\"\r\n (click)=\"variantDisplayMode = 'table'\"\r\n [class.btn-primary]=\"variantDisplayMode === 'table'\"\r\n >\r\n <clr-icon shape=\"table\"></clr-icon>\r\n <span class=\"full-label\">{{ 'catalog.display-variant-table' | translate }}</span>\r\n </button>\r\n </div>\r\n <div class=\"variant-filter\">\r\n <input\r\n [formControl]=\"filterInput\"\r\n [placeholder]=\"'catalog.filter-by-name-or-sku' | translate\"\r\n />\r\n <button class=\"icon-button\" (click)=\"filterInput.setValue('')\">\r\n <clr-icon shape=\"times\"></clr-icon>\r\n </button>\r\n </div>\r\n <div class=\"flex-spacer\"></div>\r\n <a\r\n *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\"\r\n [routerLink]=\"['./', 'manage-variants']\"\r\n class=\"btn btn-secondary edit-variants-btn mb0 mr0\"\r\n >\r\n <clr-icon shape=\"add-text\"></clr-icon>\r\n {{ 'catalog.manage-variants' | translate }}\r\n </a>\r\n </div>\r\n\r\n <div class=\"pagination-row mt4\" *ngIf=\"10 < (paginationConfig$ | async)?.totalItems\">\r\n <vdr-items-per-page-controls\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (itemsPerPageChange)=\"setItemsPerPage($event)\"\r\n ></vdr-items-per-page-controls>\r\n\r\n <vdr-pagination-controls\r\n [id]=\"(paginationConfig$ | async)?.id\"\r\n [currentPage]=\"currentPage$ | async\"\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (pageChange)=\"setPage($event)\"\r\n ></vdr-pagination-controls>\r\n </div>\r\n\r\n <vdr-product-variants-table\r\n *ngIf=\"variantDisplayMode === 'table'\"\r\n [variants]=\"variants$ | async\"\r\n [paginationConfig]=\"paginationConfig$ | async\"\r\n [optionGroups]=\"product.optionGroups\"\r\n [channelPriceIncludesTax]=\"channelPriceIncludesTax$ | async\"\r\n [productVariantsFormArray]=\"detailForm.get('variants')\"\r\n [pendingAssetChanges]=\"variantAssetChanges\"\r\n ></vdr-product-variants-table>\r\n <vdr-product-variants-list\r\n *ngIf=\"variantDisplayMode === 'card'\"\r\n [variants]=\"variants$ | async\"\r\n [paginationConfig]=\"paginationConfig$ | async\"\r\n [channelPriceIncludesTax]=\"channelPriceIncludesTax$ | async\"\r\n [facets]=\"facets$ | async\"\r\n [optionGroups]=\"product.optionGroups\"\r\n [productVariantsFormArray]=\"detailForm.get('variants')\"\r\n [taxCategories]=\"taxCategories$ | async\"\r\n [customFields]=\"customVariantFields\"\r\n [customOptionFields]=\"customOptionFields\"\r\n [activeLanguage]=\"languageCode$ | async\"\r\n [pendingAssetChanges]=\"variantAssetChanges\"\r\n (assignToChannel)=\"assignVariantToChannel($event)\"\r\n (removeFromChannel)=\"removeVariantFromChannel($event)\"\r\n (assetChange)=\"variantAssetChange($event)\"\r\n (updateProductOption)=\"updateProductOption($event)\"\r\n (selectionChange)=\"selectedVariantIds = $event\"\r\n (selectFacetValueClick)=\"selectVariantFacetValue($event)\"\r\n ></vdr-product-variants-list>\r\n </section>\r\n <div class=\"pagination-row mt4\" *ngIf=\"10 < (paginationConfig$ | async)?.totalItems\">\r\n <vdr-items-per-page-controls\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (itemsPerPageChange)=\"setItemsPerPage($event)\"\r\n ></vdr-items-per-page-controls>\r\n\r\n <vdr-pagination-controls\r\n [id]=\"(paginationConfig$ | async)?.id\"\r\n [currentPage]=\"currentPage$ | async\"\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (pageChange)=\"setPage($event)\"\r\n ></vdr-pagination-controls>\r\n </div>\r\n </clr-tab-content>\r\n </clr-tab>\r\n </clr-tabs>\r\n</form>\r\n",
1843
+ template: "<vdr-action-bar>\r\n <vdr-ab-left>\r\n <div class=\"flex clr-flex-row\">\r\n <vdr-entity-info [entity]=\"entity$ | async\"></vdr-entity-info>\r\n <clr-toggle-wrapper *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\">\r\n <input\r\n type=\"checkbox\"\r\n clrToggle\r\n name=\"enabled\"\r\n [formControl]=\"detailForm.get(['product', 'enabled'])\"\r\n />\r\n <label>{{ 'common.enabled' | translate }}</label>\r\n </clr-toggle-wrapper>\r\n </div>\r\n <vdr-language-selector\r\n [disabled]=\"isNew$ | async\"\r\n [availableLanguageCodes]=\"availableLanguages$ | async\"\r\n [currentLanguageCode]=\"languageCode$ | async\"\r\n (languageCodeChange)=\"setLanguage($event)\"\r\n ></vdr-language-selector>\r\n </vdr-ab-left>\r\n\r\n <vdr-ab-right>\r\n <vdr-action-bar-items locationId=\"product-detail\"></vdr-action-bar-items>\r\n <button\r\n class=\"btn btn-primary\"\r\n *ngIf=\"isNew$ | async; else updateButton\"\r\n (click)=\"create()\"\r\n [disabled]=\"detailForm.invalid || detailForm.pristine || !variantsToCreateAreValid()\"\r\n >\r\n {{ 'common.create' | translate }}\r\n </button>\r\n <ng-template #updateButton>\r\n <button\r\n *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\"\r\n class=\"btn btn-primary\"\r\n (click)=\"save()\"\r\n [disabled]=\"\r\n (detailForm.invalid || detailForm.pristine) && !assetsChanged() && !variantAssetsChanged()\r\n \"\r\n >\r\n {{ 'common.update' | translate }}\r\n </button>\r\n </ng-template>\r\n </vdr-ab-right>\r\n</vdr-action-bar>\r\n\r\n<form class=\"form\" [formGroup]=\"detailForm\" *ngIf=\"product$ | async as product\">\r\n <button type=\"submit\" hidden x-data=\"prevents enter key from triggering other buttons\"></button>\r\n <clr-tabs>\r\n <clr-tab>\r\n <button clrTabLink (click)=\"navigateToTab('details')\">\r\n {{ 'catalog.product-details' | translate }}\r\n </button>\r\n <clr-tab-content *clrIfActive=\"(activeTab$ | async) === 'details'\">\r\n <div class=\"clr-row\">\r\n <div class=\"clr-col\">\r\n <section class=\"form-block\" formGroupName=\"product\">\r\n <ng-container *ngIf=\"!(isNew$ | async)\">\r\n <ng-container *vdrIfMultichannel>\r\n <vdr-form-item\r\n [label]=\"'common.channels' | translate\"\r\n *vdrIfDefaultChannelActive\r\n >\r\n <div class=\"flex channel-assignment\">\r\n <ng-container *ngFor=\"let channel of productChannels$ | async\">\r\n <vdr-chip\r\n *ngIf=\"!isDefaultChannel(channel.code)\"\r\n icon=\"times-circle\"\r\n (iconClick)=\"removeFromChannel(channel.id)\"\r\n >\r\n <vdr-channel-badge\r\n [channelCode]=\"channel.code\"\r\n ></vdr-channel-badge>\r\n {{ channel.code | channelCodeToLabel }}\r\n </vdr-chip>\r\n </ng-container>\r\n <button class=\"btn btn-sm\" (click)=\"assignToChannel()\">\r\n <clr-icon shape=\"layers\"></clr-icon>\r\n {{ 'catalog.assign-to-channel' | translate }}\r\n </button>\r\n </div>\r\n </vdr-form-item>\r\n </ng-container>\r\n </ng-container>\r\n <vdr-form-field [label]=\"'catalog.product-name' | translate\" for=\"name\">\r\n <input\r\n id=\"name\"\r\n type=\"text\"\r\n formControlName=\"name\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n (input)=\"updateSlug($event.target.value)\"\r\n />\r\n </vdr-form-field>\r\n <div\r\n class=\"auto-rename-wrapper\"\r\n [class.visible]=\"\r\n (isNew$ | async) === false && detailForm.get(['product', 'name'])?.dirty\r\n \"\r\n >\r\n <clr-checkbox-wrapper>\r\n <input\r\n clrCheckbox\r\n type=\"checkbox\"\r\n id=\"auto-update\"\r\n formControlName=\"autoUpdateVariantNames\"\r\n />\r\n <label>{{\r\n 'catalog.auto-update-product-variant-name' | translate\r\n }}</label>\r\n </clr-checkbox-wrapper>\r\n </div>\r\n <vdr-form-field\r\n [label]=\"'catalog.slug' | translate\"\r\n for=\"slug\"\r\n [errors]=\"{ pattern: 'catalog.slug-pattern-error' | translate }\"\r\n >\r\n <input\r\n id=\"slug\"\r\n type=\"text\"\r\n formControlName=\"slug\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n />\r\n </vdr-form-field>\r\n <vdr-rich-text-editor\r\n formControlName=\"description\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n [label]=\"'common.description' | translate\"\r\n ></vdr-rich-text-editor>\r\n\r\n <section formGroupName=\"customFields\" *ngIf=\"customFields.length\">\r\n <label>{{ 'common.custom-fields' | translate }}</label>\r\n <vdr-tabbed-custom-fields\r\n entityName=\"Product\"\r\n [customFields]=\"customFields\"\r\n [customFieldsFormGroup]=\"detailForm.get(['product', 'customFields'])\"\r\n [readonly]=\"!(['UpdateCatalog', 'UpdateProduct'] | hasPermission)\"\r\n ></vdr-tabbed-custom-fields>\r\n </section>\r\n <vdr-custom-detail-component-host\r\n locationId=\"product-detail\"\r\n [entity$]=\"entity$\"\r\n [detailForm]=\"detailForm\"\r\n ></vdr-custom-detail-component-host>\r\n </section>\r\n </div>\r\n <div class=\"clr-col-md-auto\">\r\n <vdr-assets\r\n [assets]=\"assetChanges.assets || product.assets\"\r\n [featuredAsset]=\"assetChanges.featuredAsset || product.featuredAsset\"\r\n [updatePermissions]=\"updatePermissions\"\r\n (change)=\"assetChanges = $event\"\r\n ></vdr-assets>\r\n <div class=\"facets\">\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of facetValues$ | async\"\r\n [facetValue]=\"facetValue\"\r\n [removable]=\"['UpdateCatalog', 'UpdateProduct'] | hasPermission\"\r\n (remove)=\"removeProductFacetValue(facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n <button\r\n class=\"btn btn-sm btn-secondary\"\r\n *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\"\r\n (click)=\"selectProductFacetValue()\"\r\n >\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-facets' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"isNew$ | async\">\r\n <h4>{{ 'catalog.product-variants' | translate }}</h4>\r\n <vdr-generate-product-variants\r\n (variantsChange)=\"createVariantsConfig = $event\"\r\n ></vdr-generate-product-variants>\r\n </div>\r\n </clr-tab-content>\r\n </clr-tab>\r\n <clr-tab *ngIf=\"!(isNew$ | async)\">\r\n <button clrTabLink (click)=\"navigateToTab('variants')\">\r\n {{ 'catalog.product-variants' | translate }}\r\n </button>\r\n <clr-tab-content *clrIfActive=\"(activeTab$ | async) === 'variants'\">\r\n <section class=\"form-block\">\r\n <div class=\"view-mode\">\r\n <div class=\"btn-group\">\r\n <button\r\n class=\"btn btn-secondary-outline\"\r\n (click)=\"variantDisplayMode = 'card'\"\r\n [class.btn-primary]=\"variantDisplayMode === 'card'\"\r\n >\r\n <clr-icon shape=\"list\"></clr-icon>\r\n <span class=\"full-label\">{{ 'catalog.display-variant-cards' | translate }}</span>\r\n </button>\r\n <button\r\n class=\"btn\"\r\n (click)=\"variantDisplayMode = 'table'\"\r\n [class.btn-primary]=\"variantDisplayMode === 'table'\"\r\n >\r\n <clr-icon shape=\"table\"></clr-icon>\r\n <span class=\"full-label\">{{ 'catalog.display-variant-table' | translate }}</span>\r\n </button>\r\n </div>\r\n <div class=\"variant-filter\">\r\n <input\r\n [formControl]=\"filterInput\"\r\n [placeholder]=\"'catalog.filter-by-name-or-sku' | translate\"\r\n />\r\n <button class=\"icon-button\" (click)=\"filterInput.setValue('')\">\r\n <clr-icon shape=\"times\"></clr-icon>\r\n </button>\r\n </div>\r\n <div class=\"flex-spacer\"></div>\r\n <a\r\n *vdrIfPermissions=\"['UpdateCatalog', 'UpdateProduct']\"\r\n [routerLink]=\"['./', 'manage-variants']\"\r\n class=\"btn btn-secondary edit-variants-btn mb0 mr0\"\r\n >\r\n <clr-icon shape=\"add-text\"></clr-icon>\r\n {{ 'catalog.manage-variants' | translate }}\r\n </a>\r\n </div>\r\n\r\n <div class=\"pagination-row mt4\" *ngIf=\"10 < (paginationConfig$ | async)?.totalItems\">\r\n <vdr-items-per-page-controls\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (itemsPerPageChange)=\"setItemsPerPage($event)\"\r\n ></vdr-items-per-page-controls>\r\n\r\n <vdr-pagination-controls\r\n [id]=\"(paginationConfig$ | async)?.id\"\r\n [currentPage]=\"currentPage$ | async\"\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (pageChange)=\"setPage($event)\"\r\n ></vdr-pagination-controls>\r\n </div>\r\n\r\n <vdr-product-variants-table\r\n *ngIf=\"variantDisplayMode === 'table'\"\r\n [variants]=\"variants$ | async\"\r\n [paginationConfig]=\"paginationConfig$ | async\"\r\n [optionGroups]=\"product.optionGroups\"\r\n [channelPriceIncludesTax]=\"channelPriceIncludesTax$ | async\"\r\n [productVariantsFormArray]=\"detailForm.get('variants')\"\r\n [pendingAssetChanges]=\"variantAssetChanges\"\r\n ></vdr-product-variants-table>\r\n <vdr-product-variants-list\r\n *ngIf=\"variantDisplayMode === 'card'\"\r\n [variants]=\"variants$ | async\"\r\n [paginationConfig]=\"paginationConfig$ | async\"\r\n [channelPriceIncludesTax]=\"channelPriceIncludesTax$ | async\"\r\n [pendingFacetValueChanges]=\"variantFacetValueChanges\"\r\n [optionGroups]=\"product.optionGroups\"\r\n [productVariantsFormArray]=\"detailForm.get('variants')\"\r\n [taxCategories]=\"taxCategories$ | async\"\r\n [customFields]=\"customVariantFields\"\r\n [customOptionFields]=\"customOptionFields\"\r\n [activeLanguage]=\"languageCode$ | async\"\r\n [pendingAssetChanges]=\"variantAssetChanges\"\r\n (assignToChannel)=\"assignVariantToChannel($event)\"\r\n (removeFromChannel)=\"removeVariantFromChannel($event)\"\r\n (assetChange)=\"variantAssetChange($event)\"\r\n (updateProductOption)=\"updateProductOption($event)\"\r\n (selectionChange)=\"selectedVariantIds = $event\"\r\n ></vdr-product-variants-list>\r\n </section>\r\n <div class=\"pagination-row mt4\" *ngIf=\"10 < (paginationConfig$ | async)?.totalItems\">\r\n <vdr-items-per-page-controls\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (itemsPerPageChange)=\"setItemsPerPage($event)\"\r\n ></vdr-items-per-page-controls>\r\n\r\n <vdr-pagination-controls\r\n [id]=\"(paginationConfig$ | async)?.id\"\r\n [currentPage]=\"currentPage$ | async\"\r\n [itemsPerPage]=\"itemsPerPage$ | async\"\r\n (pageChange)=\"setPage($event)\"\r\n ></vdr-pagination-controls>\r\n </div>\r\n </clr-tab-content>\r\n </clr-tab>\r\n </clr-tabs>\r\n</form>\r\n",
1878
1844
  changeDetection: ChangeDetectionStrategy.OnPush,
1879
1845
  styles: [":host ::ng-deep trix-toolbar{top:24px}.facets{margin-top:12px;display:flex;flex-wrap:wrap;align-items:center}@media screen and (min-width: 768px){.facets{max-width:340px}}vdr-action-bar clr-toggle-wrapper{margin-top:12px}.variant-filter{flex:1;display:flex}.variant-filter input{flex:1;max-width:initial;border-radius:3px 0 0 3px!important}.variant-filter .icon-button{border:1px solid var(--color-component-border-300);background-color:var(--color-component-bg-100);border-radius:0 3px 3px 0;border-left:none}.group-name{padding-right:6px}.view-mode{display:flex;flex-direction:column;justify-content:space-between}@media screen and (min-width: 768px){.view-mode{flex-direction:row}}.edit-variants-btn{margin-top:0}.channel-assignment{flex-wrap:wrap;max-height:144px;overflow-y:auto}.auto-rename-wrapper{overflow:hidden;max-height:0;padding-left:9.5rem;margin-bottom:0;transition:max-height .2s,margin-bottom .2s}.auto-rename-wrapper.visible{max-height:24px;margin-bottom:12px}.pagination-row{display:flex;align-items:baseline;justify-content:space-between}\n"]
1880
1846
  },] }
@@ -3147,7 +3113,6 @@ class BulkAddFacetValuesDialogComponent {
3147
3113
  this.changeDetectorRef = changeDetectorRef;
3148
3114
  /* provided by call to ModalService */
3149
3115
  this.mode = 'product';
3150
- this.facets = [];
3151
3116
  this.state = 'loading';
3152
3117
  this.selectedValues = [];
3153
3118
  this.items = [];
@@ -3216,7 +3181,7 @@ class BulkAddFacetValuesDialogComponent {
3216
3181
  BulkAddFacetValuesDialogComponent.decorators = [
3217
3182
  { type: Component, args: [{
3218
3183
  selector: 'vdr-bulk-add-facet-values-dialog',
3219
- template: "<ng-template vdrDialogTitle>\r\n {{ 'catalog.edit-facet-values' | translate }}\r\n</ng-template>\r\n\r\n<div class=\"flex\">\r\n <div class=\"flex center\">\r\n <div class=\"mr2\">\r\n {{ 'catalog.add-facet-value' | translate }}\r\n </div>\r\n <vdr-facet-value-selector\r\n [facets]=\"facets\"\r\n (selectedValuesChange)=\"selectedValues = $event\"\r\n ></vdr-facet-value-selector>\r\n </div>\r\n</div>\r\n\r\n<table class=\"table\" *ngIf=\"state !== 'loading'; else placeholder\">\r\n <tbody>\r\n <tr *ngFor=\"let item of items\">\r\n <td class=\"left align-middle\">\r\n <div>{{ item.name }}</div>\r\n <div *ngIf=\"item.sku\" class=\"sku\">{{ item.sku }}</div>\r\n </td>\r\n <td class=\"left\">\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of item.facetValues\"\r\n [facetValue]=\"facetValue\"\r\n (remove)=\"removeFacetValue(item, facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n </td>\r\n </tr>\r\n </tbody>\r\n</table>\r\n\r\n<ng-template #placeholder>\r\n <div class=\"loading\">\r\n <clr-spinner></clr-spinner>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template vdrDialogButtons>\r\n <button type=\"button\" class=\"btn\" (click)=\"cancel()\">{{ 'common.cancel' | translate }}</button>\r\n <button\r\n type=\"submit\"\r\n (click)=\"addFacetValues()\"\r\n [disabled]=\"selectedValues.length === 0 && facetValuesRemoved === false\"\r\n class=\"btn btn-primary\"\r\n >\r\n {{ 'common.update' | translate }}\r\n </button>\r\n</ng-template>\r\n",
3184
+ template: "<ng-template vdrDialogTitle>\r\n {{ 'catalog.edit-facet-values' | translate }}\r\n</ng-template>\r\n\r\n<div class=\"flex\">\r\n <div class=\"flex center\">\r\n <div class=\"mr2\">\r\n {{ 'catalog.add-facet-value' | translate }}\r\n </div>\r\n <vdr-facet-value-selector\r\n (selectedValuesChange)=\"selectedValues = $event\"\r\n ></vdr-facet-value-selector>\r\n </div>\r\n</div>\r\n\r\n<table class=\"table\" *ngIf=\"state !== 'loading'; else placeholder\">\r\n <tbody>\r\n <tr *ngFor=\"let item of items\">\r\n <td class=\"left align-middle\">\r\n <div>{{ item.name }}</div>\r\n <div *ngIf=\"item.sku\" class=\"sku\">{{ item.sku }}</div>\r\n </td>\r\n <td class=\"left\">\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of item.facetValues\"\r\n [facetValue]=\"facetValue\"\r\n (remove)=\"removeFacetValue(item, facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n </td>\r\n </tr>\r\n </tbody>\r\n</table>\r\n\r\n<ng-template #placeholder>\r\n <div class=\"loading\">\r\n <clr-spinner></clr-spinner>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template vdrDialogButtons>\r\n <button type=\"button\" class=\"btn\" (click)=\"cancel()\">{{ 'common.cancel' | translate }}</button>\r\n <button\r\n type=\"submit\"\r\n (click)=\"addFacetValues()\"\r\n [disabled]=\"selectedValues.length === 0 && facetValuesRemoved === false\"\r\n class=\"btn btn-primary\"\r\n >\r\n {{ 'common.update' | translate }}\r\n </button>\r\n</ng-template>\r\n",
3220
3185
  changeDetection: ChangeDetectionStrategy.OnPush,
3221
3186
  styles: [".loading{min-height:25vh;display:flex;justify-content:center;align-items:center}.sku{color:var(--color-text-300)}\n"]
3222
3187
  },] }
@@ -4297,17 +4262,14 @@ const ɵ9 = userPermissions => userPermissions.includes(Permission.UpdateCatalog
4297
4262
  const ids = mode === 'product'
4298
4263
  ? unique(selection.map(p => p.productId))
4299
4264
  : unique(selection.map(p => p.productVariantId));
4300
- return dataService.facet
4301
- .getAllFacets()
4302
- .mapSingle(data => data.facets.items)
4303
- .pipe(switchMap(facets => modalService.fromComponent(BulkAddFacetValuesDialogComponent, {
4265
+ return modalService
4266
+ .fromComponent(BulkAddFacetValuesDialogComponent, {
4304
4267
  size: 'xl',
4305
4268
  locals: {
4306
- facets,
4307
4269
  mode,
4308
4270
  ids,
4309
4271
  },
4310
- })))
4272
+ })
4311
4273
  .subscribe(result => {
4312
4274
  if (result) {
4313
4275
  notificationService.success(marker('common.notify-bulk-update-success'), {
@@ -4392,7 +4354,6 @@ class ProductVariantsListComponent {
4392
4354
  this.removeFromChannel = new EventEmitter();
4393
4355
  this.assetChange = new EventEmitter();
4394
4356
  this.selectionChange = new EventEmitter();
4395
- this.selectFacetValueClick = new EventEmitter();
4396
4357
  this.updateProductOption = new EventEmitter();
4397
4358
  this.selectedVariantIds = [];
4398
4359
  this.formGroupMap = new Map();
@@ -4413,11 +4374,6 @@ class ProductVariantsListComponent {
4413
4374
  }));
4414
4375
  this.buildFormGroupMap();
4415
4376
  }
4416
- ngOnChanges(changes) {
4417
- if ('facets' in changes && !!changes['facets'].currentValue) {
4418
- this.facetValues = flattenFacetValues(this.facets);
4419
- }
4420
- }
4421
4377
  ngOnDestroy() {
4422
4378
  if (this.subscription) {
4423
4379
  this.subscription.unsubscribe();
@@ -4496,25 +4452,35 @@ class ProductVariantsListComponent {
4496
4452
  const translation = (_a = option.translations.find(t => t.languageCode === this.activeLanguage)) !== null && _a !== void 0 ? _a : option.translations[0];
4497
4453
  return translation.name;
4498
4454
  }
4499
- pendingFacetValues(variant) {
4500
- if (this.facets) {
4501
- const formFacetValueIds = this.getFacetValueIds(variant.id);
4502
- const variantFacetValueIds = variant.facetValues.map(fv => fv.id);
4503
- return formFacetValueIds
4504
- .filter(x => !variantFacetValueIds.includes(x))
4505
- .map(id => this.facetValues.find(fv => fv.id === id))
4506
- .filter(notNullOrUndefined);
4507
- }
4508
- else {
4509
- return [];
4510
- }
4455
+ currentOrPendingFacetValues(variant) {
4456
+ var _a;
4457
+ return (_a = this.pendingFacetValueChanges[variant.id]) !== null && _a !== void 0 ? _a : variant.facetValues;
4511
4458
  }
4512
- existingFacetValues(variant) {
4513
- const formFacetValueIds = this.getFacetValueIds(variant.id);
4514
- const intersection = [...formFacetValueIds].filter(x => variant.facetValues.map(fv => fv.id).includes(x));
4515
- return intersection
4516
- .map(id => variant.facetValues.find(fv => fv.id === id))
4517
- .filter(notNullOrUndefined);
4459
+ selectFacetValue(variant) {
4460
+ return this.modalService
4461
+ .fromComponent(ApplyFacetDialogComponent, {
4462
+ size: 'md',
4463
+ closable: true,
4464
+ })
4465
+ .subscribe(facetValues => {
4466
+ if (facetValues) {
4467
+ const existingFacetValueIds = variant ? variant.facetValues.map(fv => fv.id) : [];
4468
+ const variantFormGroup = this.formArray.controls.find(c => c.value.id === variant.id);
4469
+ if (variantFormGroup) {
4470
+ const uniqueFacetValueIds = unique([
4471
+ ...existingFacetValueIds,
4472
+ ...facetValues.map(fv => fv.id),
4473
+ ]);
4474
+ variantFormGroup.patchValue({ facetValueIds: uniqueFacetValueIds });
4475
+ variantFormGroup.markAsDirty();
4476
+ if (!this.pendingFacetValueChanges[variant.id]) {
4477
+ this.pendingFacetValueChanges[variant.id] = variant.facetValues.slice(0);
4478
+ }
4479
+ this.pendingFacetValueChanges[variant.id].push(...facetValues);
4480
+ }
4481
+ this.changeDetector.markForCheck();
4482
+ }
4483
+ });
4518
4484
  }
4519
4485
  removeFacetValue(variant, facetValueId) {
4520
4486
  const formGroup = this.formGroupMap.get(variant.id);
@@ -4524,6 +4490,10 @@ class ProductVariantsListComponent {
4524
4490
  facetValueIds: newValue,
4525
4491
  });
4526
4492
  formGroup.markAsDirty();
4493
+ if (!this.pendingFacetValueChanges[variant.id]) {
4494
+ this.pendingFacetValueChanges[variant.id] = variant.facetValues.slice(0);
4495
+ }
4496
+ this.pendingFacetValueChanges[variant.id] = this.pendingFacetValueChanges[variant.id].filter(fv => fv.id !== facetValueId);
4527
4497
  }
4528
4498
  }
4529
4499
  isVariantSelected(variantId) {
@@ -4561,7 +4531,7 @@ class ProductVariantsListComponent {
4561
4531
  ProductVariantsListComponent.decorators = [
4562
4532
  { type: Component, args: [{
4563
4533
  selector: 'vdr-product-variants-list',
4564
- template: "<div class=\"variants-list\">\r\n <div\r\n class=\"variant-container card\"\r\n *ngFor=\"\r\n let variant of variants | paginate: paginationConfig || { itemsPerPage: 10, currentPage: 1 };\r\n trackBy: trackById;\r\n let i = index\r\n \"\r\n [class.disabled]=\"!formGroupMap.get(variant.id)?.get('enabled')?.value\"\r\n >\r\n <ng-container *ngIf=\"formGroupMap.get(variant.id) as formGroup\" [formGroup]=\"formGroup\">\r\n <div class=\"card-block header-row\">\r\n <div class=\"details\">\r\n <vdr-title-input class=\"sku\" [readonly]=\"!(updatePermission | hasPermission)\">\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"text\"\r\n formControlName=\"sku\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [placeholder]=\"'catalog.sku' | translate\"\r\n />\r\n </clr-input-container>\r\n </vdr-title-input>\r\n <vdr-title-input class=\"name\" [readonly]=\"!(updatePermission | hasPermission)\">\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"text\"\r\n formControlName=\"name\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [placeholder]=\"'common.name' | translate\"\r\n />\r\n </clr-input-container>\r\n </vdr-title-input>\r\n </div>\r\n <div class=\"right-controls\">\r\n <clr-toggle-wrapper *vdrIfPermissions=\"updatePermission\">\r\n <input type=\"checkbox\" clrToggle name=\"enabled\" formControlName=\"enabled\" />\r\n <label>{{ 'common.enabled' | translate }}</label>\r\n </clr-toggle-wrapper>\r\n </div>\r\n </div>\r\n <div class=\"card-block\">\r\n <div class=\"variant-body\">\r\n <div class=\"assets\">\r\n <vdr-assets\r\n [compact]=\"true\"\r\n [assets]=\"pendingAssetChanges[variant.id]?.assets || variant.assets\"\r\n [featuredAsset]=\"\r\n pendingAssetChanges[variant.id]?.featuredAsset || variant.featuredAsset\r\n \"\r\n [updatePermissions]=\"updatePermission\"\r\n (change)=\"onAssetChange(variant.id, $event)\"\r\n ></vdr-assets>\r\n </div>\r\n <div class=\"variant-form-inputs\">\r\n <div class=\"standard-fields\">\r\n <div class=\"variant-form-input-row\">\r\n <div class=\"tax-category\">\r\n <clr-select-container\r\n *vdrIfPermissions=\"updatePermission; else taxCategoryLabel\"\r\n >\r\n <label>{{ 'catalog.tax-category' | translate }}</label>\r\n <select clrSelect name=\"options\" formControlName=\"taxCategoryId\">\r\n <option\r\n *ngFor=\"let taxCategory of taxCategories\"\r\n [value]=\"taxCategory.id\"\r\n >\r\n {{ taxCategory.name }}\r\n </option>\r\n </select>\r\n </clr-select-container>\r\n <ng-template #taxCategoryLabel>\r\n <label class=\"clr-control-label\">{{\r\n 'catalog.tax-category' | translate\r\n }}</label>\r\n <div class=\"tax-category-label\">\r\n {{ getTaxCategoryName(formGroup) }}\r\n </div>\r\n </ng-template>\r\n </div>\r\n <div class=\"price\">\r\n <clr-input-container>\r\n <label>{{ 'catalog.price' | translate }}</label>\r\n <vdr-currency-input\r\n *ngIf=\"!channelPriceIncludesTax\"\r\n clrInput\r\n [currencyCode]=\"variant.currencyCode\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n formControlName=\"price\"\r\n ></vdr-currency-input>\r\n <vdr-currency-input\r\n *ngIf=\"channelPriceIncludesTax\"\r\n clrInput\r\n [currencyCode]=\"variant.currencyCode\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n formControlName=\"priceWithTax\"\r\n ></vdr-currency-input>\r\n </clr-input-container>\r\n </div>\r\n <vdr-variant-price-detail\r\n [price]=\"formGroup.get('price')!.value\"\r\n [currencyCode]=\"variant.currencyCode\"\r\n [priceIncludesTax]=\"channelPriceIncludesTax\"\r\n [taxCategoryId]=\"formGroup.get('taxCategoryId')!.value\"\r\n ></vdr-variant-price-detail>\r\n </div>\r\n <div class=\"variant-form-input-row\">\r\n <clr-select-container *vdrIfPermissions=\"updatePermission\">\r\n <label\r\n >{{ 'catalog.track-inventory' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.track-inventory-tooltip' | translate\"\r\n ></vdr-help-tooltip>\r\n </label>\r\n <select clrSelect name=\"options\" formControlName=\"trackInventory\">\r\n <option [value]=\"GlobalFlag.TRUE\">\r\n {{ 'catalog.track-inventory-true' | translate }}\r\n </option>\r\n <option [value]=\"GlobalFlag.FALSE\">\r\n {{ 'catalog.track-inventory-false' | translate }}\r\n </option>\r\n <option [value]=\"GlobalFlag.INHERIT\">\r\n {{ 'catalog.track-inventory-inherit' | translate }}\r\n </option>\r\n </select>\r\n </clr-select-container>\r\n <clr-input-container>\r\n <label\r\n >{{ 'catalog.stock-on-hand' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.stock-on-hand-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <input\r\n [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\"\r\n clrInput\r\n type=\"number\"\r\n [min]=\"getStockOnHandMinValue(formGroup)\"\r\n step=\"1\"\r\n formControlName=\"stockOnHand\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [vdrDisabled]=\"inventoryIsNotTracked(formGroup)\"\r\n />\r\n </clr-input-container>\r\n <div [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\">\r\n <label class=\"clr-control-label\"\r\n >{{ 'catalog.stock-allocated' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.stock-allocated-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <div class=\"value\">\r\n {{ variant.stockAllocated }}\r\n </div>\r\n </div>\r\n <div [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\">\r\n <label class=\"clr-control-label\"\r\n >{{ 'catalog.stock-saleable' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.stock-saleable-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <div class=\"value\">\r\n {{ getSaleableStockLevel(variant) }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"variant-form-input-row\">\r\n <div\r\n class=\"out-of-stock-threshold-wrapper\"\r\n [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\"\r\n >\r\n <label class=\"clr-control-label\"\r\n >{{ 'catalog.out-of-stock-threshold' | translate\r\n }}<vdr-help-tooltip\r\n [content]=\"'catalog.out-of-stock-threshold-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <div class=\"flex\">\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"number\"\r\n [formControl]=\"formGroup.get('outOfStockThreshold')\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [vdrDisabled]=\"\r\n formGroup.get('useGlobalOutOfStockThreshold')?.value !==\r\n false || inventoryIsNotTracked(formGroup)\r\n \"\r\n />\r\n </clr-input-container>\r\n <clr-toggle-wrapper>\r\n <input\r\n type=\"checkbox\"\r\n clrToggle\r\n name=\"useGlobalOutOfStockThreshold\"\r\n formControlName=\"useGlobalOutOfStockThreshold\"\r\n [vdrDisabled]=\"\r\n !(updatePermission | hasPermission) ||\r\n inventoryIsNotTracked(formGroup)\r\n \"\r\n />\r\n <label\r\n >{{ 'catalog.use-global-value' | translate }} ({{\r\n globalOutOfStockThreshold\r\n }})</label\r\n >\r\n </clr-toggle-wrapper>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"custom-fields\">\r\n <div class=\"variant-form-input-row\">\r\n <section formGroupName=\"customFields\" *ngIf=\"customFields.length\">\r\n <vdr-tabbed-custom-fields\r\n entityName=\"ProductVariant\"\r\n [customFields]=\"customFields\"\r\n [compact]=\"true\"\r\n [customFieldsFormGroup]=\"formGroup.get('customFields')\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n ></vdr-tabbed-custom-fields>\r\n </section>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"card-block\">\r\n <div class=\"options-facets\">\r\n <vdr-entity-info [entity]=\"variant\"></vdr-entity-info>\r\n <div *ngIf=\"variant.options.length\">\r\n <div class=\"options\">\r\n <vdr-chip\r\n *ngFor=\"let option of variant.options | sort: 'groupId'\"\r\n [colorFrom]=\"optionGroupName(option.groupId)\"\r\n [invert]=\"true\"\r\n (iconClick)=\"editOption(option)\"\r\n [icon]=\"(updatePermission | hasPermission) && 'pencil'\"\r\n >\r\n <span class=\"option-group-name\">{{ optionGroupName(option.groupId) }}</span>\r\n {{ optionName(option) }}\r\n </vdr-chip>\r\n <a [routerLink]=\"['./', 'options']\" class=\"btn btn-link btn-sm\"\r\n >{{ 'catalog.edit-options' | translate }}...</a\r\n >\r\n </div>\r\n </div>\r\n <div class=\"flex-spacer\"></div>\r\n <div class=\"facets\">\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of existingFacetValues(variant)\"\r\n [facetValue]=\"facetValue\"\r\n [removable]=\"updatePermission | hasPermission\"\r\n (remove)=\"removeFacetValue(variant, facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of pendingFacetValues(variant)\"\r\n [facetValue]=\"facetValue\"\r\n [removable]=\"updatePermission | hasPermission\"\r\n (remove)=\"removeFacetValue(variant, facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n <button\r\n *vdrIfPermissions=\"updatePermission\"\r\n class=\"btn btn-sm btn-secondary\"\r\n (click)=\"selectFacetValueClick.emit([variant.id])\"\r\n >\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-facets' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-container *vdrIfMultichannel>\r\n <div class=\"card-block\" *vdrIfDefaultChannelActive>\r\n <div class=\"flex channel-assignment\">\r\n <ng-container *ngFor=\"let channel of variant.channels\">\r\n <vdr-chip\r\n *ngIf=\"!isDefaultChannel(channel.code)\"\r\n icon=\"times-circle\"\r\n [title]=\"'catalog.remove-from-channel' | translate: { channelCode: channel.code }\"\r\n (iconClick)=\"\r\n removeFromChannel.emit({ channelId: channel.id, variant: variant })\r\n \"\r\n >\r\n <vdr-channel-badge [channelCode]=\"channel.code\"></vdr-channel-badge>\r\n {{ channel.code | channelCodeToLabel }}\r\n </vdr-chip>\r\n </ng-container>\r\n <button class=\"btn btn-sm\" (click)=\"assignToChannel.emit(variant)\">\r\n <clr-icon shape=\"layers\"></clr-icon>\r\n {{ 'catalog.assign-to-channel' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n</div>\r\n",
4534
+ template: "<div class=\"variants-list\">\r\n <div\r\n class=\"variant-container card\"\r\n *ngFor=\"\r\n let variant of variants | paginate: paginationConfig || { itemsPerPage: 10, currentPage: 1 };\r\n trackBy: trackById;\r\n let i = index\r\n \"\r\n [class.disabled]=\"!formGroupMap.get(variant.id)?.get('enabled')?.value\"\r\n >\r\n <ng-container *ngIf=\"formGroupMap.get(variant.id) as formGroup\" [formGroup]=\"formGroup\">\r\n <div class=\"card-block header-row\">\r\n <div class=\"details\">\r\n <vdr-title-input class=\"sku\" [readonly]=\"!(updatePermission | hasPermission)\">\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"text\"\r\n formControlName=\"sku\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [placeholder]=\"'catalog.sku' | translate\"\r\n />\r\n </clr-input-container>\r\n </vdr-title-input>\r\n <vdr-title-input class=\"name\" [readonly]=\"!(updatePermission | hasPermission)\">\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"text\"\r\n formControlName=\"name\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [placeholder]=\"'common.name' | translate\"\r\n />\r\n </clr-input-container>\r\n </vdr-title-input>\r\n </div>\r\n <div class=\"right-controls\">\r\n <clr-toggle-wrapper *vdrIfPermissions=\"updatePermission\">\r\n <input type=\"checkbox\" clrToggle name=\"enabled\" formControlName=\"enabled\" />\r\n <label>{{ 'common.enabled' | translate }}</label>\r\n </clr-toggle-wrapper>\r\n </div>\r\n </div>\r\n <div class=\"card-block\">\r\n <div class=\"variant-body\">\r\n <div class=\"assets\">\r\n <vdr-assets\r\n [compact]=\"true\"\r\n [assets]=\"pendingAssetChanges[variant.id]?.assets || variant.assets\"\r\n [featuredAsset]=\"\r\n pendingAssetChanges[variant.id]?.featuredAsset || variant.featuredAsset\r\n \"\r\n [updatePermissions]=\"updatePermission\"\r\n (change)=\"onAssetChange(variant.id, $event)\"\r\n ></vdr-assets>\r\n </div>\r\n <div class=\"variant-form-inputs\">\r\n <div class=\"standard-fields\">\r\n <div class=\"variant-form-input-row\">\r\n <div class=\"tax-category\">\r\n <clr-select-container\r\n *vdrIfPermissions=\"updatePermission; else taxCategoryLabel\"\r\n >\r\n <label>{{ 'catalog.tax-category' | translate }}</label>\r\n <select clrSelect name=\"options\" formControlName=\"taxCategoryId\">\r\n <option\r\n *ngFor=\"let taxCategory of taxCategories\"\r\n [value]=\"taxCategory.id\"\r\n >\r\n {{ taxCategory.name }}\r\n </option>\r\n </select>\r\n </clr-select-container>\r\n <ng-template #taxCategoryLabel>\r\n <label class=\"clr-control-label\">{{\r\n 'catalog.tax-category' | translate\r\n }}</label>\r\n <div class=\"tax-category-label\">\r\n {{ getTaxCategoryName(formGroup) }}\r\n </div>\r\n </ng-template>\r\n </div>\r\n <div class=\"price\">\r\n <clr-input-container>\r\n <label>{{ 'catalog.price' | translate }}</label>\r\n <vdr-currency-input\r\n *ngIf=\"!channelPriceIncludesTax\"\r\n clrInput\r\n [currencyCode]=\"variant.currencyCode\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n formControlName=\"price\"\r\n ></vdr-currency-input>\r\n <vdr-currency-input\r\n *ngIf=\"channelPriceIncludesTax\"\r\n clrInput\r\n [currencyCode]=\"variant.currencyCode\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n formControlName=\"priceWithTax\"\r\n ></vdr-currency-input>\r\n </clr-input-container>\r\n </div>\r\n <vdr-variant-price-detail\r\n [price]=\"formGroup.get('price')!.value\"\r\n [currencyCode]=\"variant.currencyCode\"\r\n [priceIncludesTax]=\"channelPriceIncludesTax\"\r\n [taxCategoryId]=\"formGroup.get('taxCategoryId')!.value\"\r\n ></vdr-variant-price-detail>\r\n </div>\r\n <div class=\"variant-form-input-row\">\r\n <clr-select-container *vdrIfPermissions=\"updatePermission\">\r\n <label\r\n >{{ 'catalog.track-inventory' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.track-inventory-tooltip' | translate\"\r\n ></vdr-help-tooltip>\r\n </label>\r\n <select clrSelect name=\"options\" formControlName=\"trackInventory\">\r\n <option [value]=\"GlobalFlag.TRUE\">\r\n {{ 'catalog.track-inventory-true' | translate }}\r\n </option>\r\n <option [value]=\"GlobalFlag.FALSE\">\r\n {{ 'catalog.track-inventory-false' | translate }}\r\n </option>\r\n <option [value]=\"GlobalFlag.INHERIT\">\r\n {{ 'catalog.track-inventory-inherit' | translate }}\r\n </option>\r\n </select>\r\n </clr-select-container>\r\n <clr-input-container>\r\n <label\r\n >{{ 'catalog.stock-on-hand' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.stock-on-hand-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <input\r\n [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\"\r\n clrInput\r\n type=\"number\"\r\n [min]=\"getStockOnHandMinValue(formGroup)\"\r\n step=\"1\"\r\n formControlName=\"stockOnHand\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [vdrDisabled]=\"inventoryIsNotTracked(formGroup)\"\r\n />\r\n </clr-input-container>\r\n <div [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\">\r\n <label class=\"clr-control-label\"\r\n >{{ 'catalog.stock-allocated' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.stock-allocated-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <div class=\"value\">\r\n {{ variant.stockAllocated }}\r\n </div>\r\n </div>\r\n <div [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\">\r\n <label class=\"clr-control-label\"\r\n >{{ 'catalog.stock-saleable' | translate }}\r\n <vdr-help-tooltip\r\n [content]=\"'catalog.stock-saleable-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <div class=\"value\">\r\n {{ getSaleableStockLevel(variant) }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"variant-form-input-row\">\r\n <div\r\n class=\"out-of-stock-threshold-wrapper\"\r\n [class.inventory-untracked]=\"inventoryIsNotTracked(formGroup)\"\r\n >\r\n <label class=\"clr-control-label\"\r\n >{{ 'catalog.out-of-stock-threshold' | translate\r\n }}<vdr-help-tooltip\r\n [content]=\"'catalog.out-of-stock-threshold-tooltip' | translate\"\r\n ></vdr-help-tooltip\r\n ></label>\r\n <div class=\"flex\">\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"number\"\r\n [formControl]=\"formGroup.get('outOfStockThreshold')\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n [vdrDisabled]=\"\r\n formGroup.get('useGlobalOutOfStockThreshold')?.value !==\r\n false || inventoryIsNotTracked(formGroup)\r\n \"\r\n />\r\n </clr-input-container>\r\n <clr-toggle-wrapper>\r\n <input\r\n type=\"checkbox\"\r\n clrToggle\r\n name=\"useGlobalOutOfStockThreshold\"\r\n formControlName=\"useGlobalOutOfStockThreshold\"\r\n [vdrDisabled]=\"\r\n !(updatePermission | hasPermission) ||\r\n inventoryIsNotTracked(formGroup)\r\n \"\r\n />\r\n <label\r\n >{{ 'catalog.use-global-value' | translate }} ({{\r\n globalOutOfStockThreshold\r\n }})</label\r\n >\r\n </clr-toggle-wrapper>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"custom-fields\">\r\n <div class=\"variant-form-input-row\">\r\n <section formGroupName=\"customFields\" *ngIf=\"customFields.length\">\r\n <vdr-tabbed-custom-fields\r\n entityName=\"ProductVariant\"\r\n [customFields]=\"customFields\"\r\n [compact]=\"true\"\r\n [customFieldsFormGroup]=\"formGroup.get('customFields')\"\r\n [readonly]=\"!(updatePermission | hasPermission)\"\r\n ></vdr-tabbed-custom-fields>\r\n </section>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"card-block\">\r\n <div class=\"options-facets\">\r\n <vdr-entity-info [entity]=\"variant\"></vdr-entity-info>\r\n <div *ngIf=\"variant.options.length\">\r\n <div class=\"options\">\r\n <vdr-chip\r\n *ngFor=\"let option of variant.options | sort: 'groupId'\"\r\n [colorFrom]=\"optionGroupName(option.groupId)\"\r\n [invert]=\"true\"\r\n (iconClick)=\"editOption(option)\"\r\n [icon]=\"(updatePermission | hasPermission) && 'pencil'\"\r\n >\r\n <span class=\"option-group-name\">{{ optionGroupName(option.groupId) }}</span>\r\n {{ optionName(option) }}\r\n </vdr-chip>\r\n <a [routerLink]=\"['./', 'options']\" class=\"btn btn-link btn-sm\"\r\n >{{ 'catalog.edit-options' | translate }}...</a\r\n >\r\n </div>\r\n </div>\r\n <div class=\"flex-spacer\"></div>\r\n <div class=\"facets\">\r\n <vdr-facet-value-chip\r\n *ngFor=\"let facetValue of currentOrPendingFacetValues(variant)\"\r\n [facetValue]=\"facetValue\"\r\n [removable]=\"updatePermission | hasPermission\"\r\n (remove)=\"removeFacetValue(variant, facetValue.id)\"\r\n ></vdr-facet-value-chip>\r\n <button\r\n *vdrIfPermissions=\"updatePermission\"\r\n class=\"btn btn-sm btn-secondary\"\r\n (click)=\"selectFacetValue(variant)\"\r\n >\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-facets' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-container *vdrIfMultichannel>\r\n <div class=\"card-block\" *vdrIfDefaultChannelActive>\r\n <div class=\"flex channel-assignment\">\r\n <ng-container *ngFor=\"let channel of variant.channels\">\r\n <vdr-chip\r\n *ngIf=\"!isDefaultChannel(channel.code)\"\r\n icon=\"times-circle\"\r\n [title]=\"'catalog.remove-from-channel' | translate: { channelCode: channel.code }\"\r\n (iconClick)=\"\r\n removeFromChannel.emit({ channelId: channel.id, variant: variant })\r\n \"\r\n >\r\n <vdr-channel-badge [channelCode]=\"channel.code\"></vdr-channel-badge>\r\n {{ channel.code | channelCodeToLabel }}\r\n </vdr-chip>\r\n </ng-container>\r\n <button class=\"btn btn-sm\" (click)=\"assignToChannel.emit(variant)\">\r\n <clr-icon shape=\"layers\"></clr-icon>\r\n {{ 'catalog.assign-to-channel' | translate }}\r\n </button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n</div>\r\n",
4565
4535
  changeDetection: ChangeDetectionStrategy.OnPush,
4566
4536
  styles: [".with-selected{display:flex;min-height:52px;align-items:center;border:1px solid var(--color-component-border-100);border-radius:3px;padding:6px 18px}.with-selected vdr-select-toggle{margin-right:12px}.with-selected>label{margin-right:12px}.variant-container{transition:background-color .2s;min-height:330px}.variant-container.disabled{background-color:var(--color-component-bg-200)}.variant-container .header-row{display:flex;align-items:center;flex-wrap:wrap}.variant-container .variant-body{display:flex;flex-direction:column}@media screen and (min-width: 768px){.variant-container .variant-body{flex-direction:row}}.variant-container .details{display:flex;flex-direction:column;flex:1;margin-right:12px}@media screen and (min-width: 768px){.variant-container .details{flex-direction:row;height:36px}}.variant-container .details .name{flex:1}.variant-container .details .name ::ng-deep .clr-control-container{width:100%}.variant-container .details .name ::ng-deep .clr-control-container input.clr-input{min-width:100%}.variant-container .details .sku{width:160px;margin-right:20px;flex:0}.variant-container .details ::ng-deep .name input{min-width:300px}.variant-container .right-controls{display:flex}.variant-container .tax-category-label{margin-top:3px}.variant-container .variant-form-inputs{flex:1;display:flex;flex-direction:column}@media screen and (min-width: 768px){.variant-container .variant-form-inputs{flex-direction:row}}.variant-container .variant-form-input-row{display:flex;flex-wrap:wrap}@media screen and (min-width: 768px){.variant-container .variant-form-input-row{margin:0 6px 8px 24px}}.variant-container .variant-form-input-row>*{margin-right:24px;margin-bottom:24px}.variant-container .track-inventory-toggle{margin-top:22px}.variant-container .clr-form-control{margin-top:0}.variant-container .facets{display:flex;flex-wrap:wrap;align-items:center}.variant-container .pricing{display:flex}.variant-container .pricing>div{margin-right:12px}.variant-container .option-group-name{color:var(--color-text-200);text-transform:uppercase;font-size:10px;margin-right:3px;height:11px}.variant-container .options-facets{display:flex;color:var(--color-grey-400)}.variant-container ::ng-deep .clr-control-container{width:100%}.channel-assignment{justify-content:flex-end;flex-wrap:wrap;max-height:110px;overflow-y:auto}.channel-assignment .btn{margin:6px 12px 6px 0}.out-of-stock-threshold-wrapper{display:flex;flex-direction:column}.out-of-stock-threshold-wrapper clr-toggle-wrapper{margin-left:24px}.inventory-untracked{opacity:.5}\n"]
4567
4537
  },] }
@@ -4577,17 +4547,16 @@ ProductVariantsListComponent.propDecorators = {
4577
4547
  paginationConfig: [{ type: Input }],
4578
4548
  channelPriceIncludesTax: [{ type: Input }],
4579
4549
  taxCategories: [{ type: Input }],
4580
- facets: [{ type: Input }],
4581
4550
  optionGroups: [{ type: Input }],
4582
4551
  customFields: [{ type: Input }],
4583
4552
  customOptionFields: [{ type: Input }],
4584
4553
  activeLanguage: [{ type: Input }],
4585
4554
  pendingAssetChanges: [{ type: Input }],
4555
+ pendingFacetValueChanges: [{ type: Input }],
4586
4556
  assignToChannel: [{ type: Output }],
4587
4557
  removeFromChannel: [{ type: Output }],
4588
4558
  assetChange: [{ type: Output }],
4589
4559
  selectionChange: [{ type: Output }],
4590
- selectFacetValueClick: [{ type: Output }],
4591
4560
  updateProductOption: [{ type: Output }]
4592
4561
  };
4593
4562