@vendure/admin-ui 1.6.3 → 1.7.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 (91) hide show
  1. package/bundles/vendure-admin-ui-catalog.umd.js +196 -20
  2. package/bundles/vendure-admin-ui-catalog.umd.js.map +1 -1
  3. package/bundles/vendure-admin-ui-core.umd.js +309 -327
  4. package/bundles/vendure-admin-ui-core.umd.js.map +1 -1
  5. package/bundles/vendure-admin-ui-customer.umd.js +39 -18
  6. package/bundles/vendure-admin-ui-customer.umd.js.map +1 -1
  7. package/bundles/vendure-admin-ui-order.umd.js +179 -137
  8. package/bundles/vendure-admin-ui-order.umd.js.map +1 -1
  9. package/catalog/components/generate-product-variants/generate-product-variants.component.d.ts +3 -3
  10. package/catalog/components/option-value-input/option-value-input.component.d.ts +20 -8
  11. package/catalog/components/product-variants-editor/product-variants-editor.component.d.ts +10 -3
  12. package/catalog/vendure-admin-ui-catalog.metadata.json +1 -1
  13. package/core/common/generated-types.d.ts +91 -19
  14. package/core/common/utilities/configurable-operation-utils.d.ts +4 -2
  15. package/core/common/version.d.ts +1 -1
  16. package/core/data/definitions/order-definitions.d.ts +2 -0
  17. package/core/data/definitions/product-definitions.d.ts +1 -0
  18. package/core/data/providers/order-data.service.d.ts +1 -0
  19. package/core/data/providers/product-data.service.d.ts +1 -0
  20. package/core/shared/components/custom-field-control/custom-field-control.component.d.ts +6 -2
  21. package/core/shared/components/data-table/data-table.component.d.ts +5 -2
  22. package/core/shared/components/facet-value-selector/facet-value-selector.component.d.ts +2 -1
  23. package/core/shared/dynamic-form-inputs/facet-value-form-input/facet-value-form-input.component.d.ts +2 -0
  24. package/core/shared/dynamic-form-inputs/register-dynamic-input-components.d.ts +2 -1
  25. package/core/shared/dynamic-form-inputs/select-form-input/select-form-input.component.d.ts +10 -2
  26. package/core/shared/pipes/custom-field-label.pipe.d.ts +4 -10
  27. package/core/vendure-admin-ui-core.metadata.json +1 -1
  28. package/customer/components/customer-group-list/customer-group-list.component.d.ts +6 -4
  29. package/customer/vendure-admin-ui-customer.metadata.json +1 -1
  30. package/esm2015/catalog/components/generate-product-variants/generate-product-variants.component.js +12 -5
  31. package/esm2015/catalog/components/option-value-input/option-value-input.component.js +62 -12
  32. package/esm2015/catalog/components/product-variants-editor/product-variants-editor.component.js +109 -11
  33. package/esm2015/core/common/generated-types.js +2 -1
  34. package/esm2015/core/common/introspection-result.js +191 -255
  35. package/esm2015/core/common/utilities/configurable-operation-utils.js +13 -10
  36. package/esm2015/core/common/version.js +2 -2
  37. package/esm2015/core/components/app-shell/app-shell.component.js +1 -1
  38. package/esm2015/core/components/main-nav/main-nav.component.js +1 -1
  39. package/esm2015/core/data/definitions/order-definitions.js +445 -431
  40. package/esm2015/core/data/definitions/product-definitions.js +9 -1
  41. package/esm2015/core/data/providers/order-data.service.js +7 -2
  42. package/esm2015/core/data/providers/product-data.service.js +5 -2
  43. package/esm2015/core/shared/components/custom-field-control/custom-field-control.component.js +9 -3
  44. package/esm2015/core/shared/components/data-table/data-table.component.js +9 -2
  45. package/esm2015/core/shared/components/facet-value-selector/facet-value-selector.component.js +5 -2
  46. package/esm2015/core/shared/dynamic-form-inputs/facet-value-form-input/facet-value-form-input.component.js +11 -2
  47. package/esm2015/core/shared/dynamic-form-inputs/select-form-input/select-form-input.component.js +15 -2
  48. package/esm2015/core/shared/pipes/custom-field-label.pipe.js +4 -19
  49. package/esm2015/customer/components/customer-group-list/customer-group-list.component.js +25 -12
  50. package/esm2015/order/components/fulfill-order-dialog/fulfill-order-dialog.component.js +3 -2
  51. package/esm2015/order/components/fulfillment-detail/fulfillment-detail.component.js +9 -18
  52. package/esm2015/order/components/line-fulfillment/line-fulfillment.component.js +10 -21
  53. package/esm2015/order/components/order-custom-fields-card/order-custom-fields-card.component.js +30 -4
  54. package/esm2015/order/components/order-detail/order-detail.component.js +79 -38
  55. package/esm2015/order/components/order-list/order-list.component.js +22 -15
  56. package/esm2015/order/components/order-payment-card/order-payment-card.component.js +2 -2
  57. package/esm2015/order/components/order-table/order-table.component.js +10 -2
  58. package/fesm2015/vendure-admin-ui-catalog.js +177 -22
  59. package/fesm2015/vendure-admin-ui-catalog.js.map +1 -1
  60. package/fesm2015/vendure-admin-ui-core.js +725 -733
  61. package/fesm2015/vendure-admin-ui-core.js.map +1 -1
  62. package/fesm2015/vendure-admin-ui-customer.js +23 -11
  63. package/fesm2015/vendure-admin-ui-customer.js.map +1 -1
  64. package/fesm2015/vendure-admin-ui-order.js +155 -94
  65. package/fesm2015/vendure-admin-ui-order.js.map +1 -1
  66. package/order/components/line-fulfillment/line-fulfillment.component.d.ts +2 -2
  67. package/order/components/modification-detail/modification-detail.component.d.ts +1 -1
  68. package/order/components/order-custom-fields-card/order-custom-fields-card.component.d.ts +4 -2
  69. package/order/components/order-list/order-list.component.d.ts +1 -0
  70. package/order/components/order-table/order-table.component.d.ts +1 -0
  71. package/order/vendure-admin-ui-order.metadata.json +1 -1
  72. package/package.json +2 -2
  73. package/static/i18n-messages/cs.json +6 -2
  74. package/static/i18n-messages/de.json +6 -2
  75. package/static/i18n-messages/en.json +8 -3
  76. package/static/i18n-messages/es.json +6 -2
  77. package/static/i18n-messages/fr.json +6 -2
  78. package/static/i18n-messages/it.json +6 -2
  79. package/static/i18n-messages/pl.json +6 -2
  80. package/static/i18n-messages/pt_BR.json +6 -2
  81. package/static/i18n-messages/pt_PT.json +6 -2
  82. package/static/i18n-messages/ru.json +6 -2
  83. package/static/i18n-messages/uk.json +6 -2
  84. package/static/i18n-messages/zh_Hans.json +6 -2
  85. package/static/i18n-messages/zh_Hant.json +6 -2
  86. package/static/styles/_variables.scss +3 -0
  87. package/static/styles/global/_sass-overrides.scss +3 -0
  88. package/static/styles/global/_utilities.scss +1 -0
  89. package/static/styles/styles.scss +1 -0
  90. package/static/styles/ui-extension-theme.scss +1 -0
  91. package/static/theme.min.css +1 -1
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, Injectable, EventEmitter, Input, HostBinding, Output, ContentChild, TemplateRef, SkipSelf, Optional, forwardRef, NgModule } from '@angular/core';
2
+ import { Component, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, Injectable, EventEmitter, Input, HostBinding, Output, ContentChild, TemplateRef, SkipSelf, Optional, ViewChildren, ElementRef, forwardRef, NgModule } from '@angular/core';
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
6
  import { BaseDetailComponent, ServerConfigService, NotificationService, DataService, BaseListComponent, SortOrder, LogicalOperator, DeletionResult, ModalService, Permission, unicodePatternValidator, findTranslation, getConfigArgValue, createUpdatedTranslatable, encodeConfigArgValue, LocalStorageService, FacetValueSelectorComponent, flattenFacetValues, JobState, JobQueueService, getDefaultUiLanguage, BaseEntityResolver, AssetType, createResolveData, CanDeactivateDetailGuard, detailBreadcrumb, AssetPickerDialogComponent, AssetPreviewDialogComponent, GlobalFlag, SharedModule } 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, shareReplay, distinctUntilChanged, tap, mapTo, startWith, skipUntil, skip, withLatestFrom, delay, catchError } from 'rxjs/operators';
8
+ import { map, debounceTime, takeUntil, finalize, switchMap, filter, take, mergeMap, shareReplay, distinctUntilChanged, tap, mapTo, startWith, skipUntil, skip, withLatestFrom, delay, defaultIfEmpty, catchError } from 'rxjs/operators';
9
9
  import { FormGroup, FormControl, FormBuilder, Validators, FormArray, NG_VALUE_ACCESSOR } from '@angular/forms';
10
10
  import { BehaviorSubject, combineLatest, EMPTY, Subject, merge, of, forkJoin, throwError, from } from 'rxjs';
11
11
  import { normalizeString } from '@vendure/common/lib/normalize-string';
@@ -2184,6 +2184,7 @@ class ProductVariantsEditorComponent {
2184
2184
  this.notificationService = notificationService;
2185
2185
  this.modalService = modalService;
2186
2186
  this.formValueChanged = false;
2187
+ this.optionsChanged = false;
2187
2188
  this.generatedVariants = [];
2188
2189
  }
2189
2190
  ngOnInit() {
@@ -2209,16 +2210,105 @@ class ProductVariantsEditorComponent {
2209
2210
  ? marker('catalog.default-variant')
2210
2211
  : variant.options.map(o => o.name).join(' ');
2211
2212
  }
2212
- addOption() {
2213
+ addOptionGroup() {
2213
2214
  this.optionGroups.push({
2214
2215
  isNew: true,
2216
+ locked: false,
2215
2217
  name: '',
2216
2218
  values: [],
2217
2219
  });
2220
+ this.optionsChanged = true;
2218
2221
  }
2219
- removeOption(optionGroup) {
2220
- this.optionGroups = this.optionGroups.filter(og => og !== optionGroup);
2222
+ removeOptionGroup(optionGroup) {
2223
+ const id = optionGroup.id;
2224
+ if (optionGroup.isNew) {
2225
+ this.optionGroups = this.optionGroups.filter(og => og !== optionGroup);
2226
+ this.generateVariants();
2227
+ this.optionsChanged = true;
2228
+ }
2229
+ else if (id) {
2230
+ this.modalService
2231
+ .dialog({
2232
+ title: marker('catalog.confirm-delete-product-option-group'),
2233
+ translationVars: { name: optionGroup.name },
2234
+ buttons: [
2235
+ { type: 'secondary', label: marker('common.cancel') },
2236
+ { type: 'danger', label: marker('common.delete'), returnValue: true },
2237
+ ],
2238
+ })
2239
+ .pipe(switchMap(val => {
2240
+ if (val) {
2241
+ return this.dataService.product.removeOptionGroupFromProduct({
2242
+ optionGroupId: id,
2243
+ productId: this.product.id,
2244
+ });
2245
+ }
2246
+ else {
2247
+ return EMPTY;
2248
+ }
2249
+ }))
2250
+ .subscribe(({ removeOptionGroupFromProduct }) => {
2251
+ var _a;
2252
+ if (removeOptionGroupFromProduct.__typename === 'Product') {
2253
+ this.notificationService.success(marker('common.notify-delete-success'), {
2254
+ entity: 'ProductOptionGroup',
2255
+ });
2256
+ this.initOptionsAndVariants();
2257
+ this.optionsChanged = true;
2258
+ }
2259
+ else if (removeOptionGroupFromProduct.__typename === 'ProductOptionInUseError') {
2260
+ this.notificationService.error((_a = removeOptionGroupFromProduct.message) !== null && _a !== void 0 ? _a : '');
2261
+ }
2262
+ });
2263
+ }
2264
+ }
2265
+ addOption(groupId, optionName) {
2266
+ var _a;
2267
+ (_a = this.optionGroups.find(g => g.id === groupId)) === null || _a === void 0 ? void 0 : _a.values.push({ name: optionName, locked: false });
2221
2268
  this.generateVariants();
2269
+ this.optionsChanged = true;
2270
+ }
2271
+ removeOption(groupId, { id, name }) {
2272
+ const optionGroup = this.optionGroups.find(g => g.id === groupId);
2273
+ if (optionGroup) {
2274
+ if (!id) {
2275
+ optionGroup.values = optionGroup.values.filter(v => v.name !== name);
2276
+ this.generateVariants();
2277
+ }
2278
+ else {
2279
+ this.modalService
2280
+ .dialog({
2281
+ title: marker('catalog.confirm-delete-product-option'),
2282
+ translationVars: { name },
2283
+ buttons: [
2284
+ { type: 'secondary', label: marker('common.cancel') },
2285
+ { type: 'danger', label: marker('common.delete'), returnValue: true },
2286
+ ],
2287
+ })
2288
+ .pipe(switchMap(val => {
2289
+ if (val) {
2290
+ return this.dataService.product.deleteProductOption(id);
2291
+ }
2292
+ else {
2293
+ return EMPTY;
2294
+ }
2295
+ }))
2296
+ .subscribe(({ deleteProductOption }) => {
2297
+ var _a;
2298
+ if (deleteProductOption.result === DeletionResult.DELETED) {
2299
+ this.notificationService.success(marker('common.notify-delete-success'), {
2300
+ entity: 'ProductOption',
2301
+ });
2302
+ optionGroup.values = optionGroup.values.filter(v => v.id !== id);
2303
+ this.generateVariants();
2304
+ this.optionsChanged = true;
2305
+ }
2306
+ else {
2307
+ this.notificationService.error((_a = deleteProductOption.message) !== null && _a !== void 0 ? _a : '');
2308
+ }
2309
+ });
2310
+ }
2311
+ }
2222
2312
  }
2223
2313
  generateVariants() {
2224
2314
  const groups = this.optionGroups.map(g => g.values);
@@ -2260,10 +2350,11 @@ class ProductVariantsEditorComponent {
2260
2350
  stock: 0,
2261
2351
  };
2262
2352
  }
2263
- deleteVariant(id) {
2353
+ deleteVariant(id, options) {
2264
2354
  this.modalService
2265
2355
  .dialog({
2266
2356
  title: marker('catalog.confirm-delete-product-variant'),
2357
+ translationVars: { name: options.map(o => o.name).join(' ') },
2267
2358
  buttons: [
2268
2359
  { type: 'secondary', label: marker('common.cancel') },
2269
2360
  { type: 'danger', label: marker('common.delete'), returnValue: true },
@@ -2298,12 +2389,14 @@ class ProductVariantsEditorComponent {
2298
2389
  count: variants.length,
2299
2390
  });
2300
2391
  this.initOptionsAndVariants();
2392
+ this.optionsChanged = false;
2301
2393
  },
2302
2394
  });
2303
2395
  }
2304
2396
  checkUniqueSkus() {
2305
2397
  const withDuplicateSkus = this.generatedVariants.filter((variant, index) => {
2306
- return this.generatedVariants.find(gv => gv.sku.trim() === variant.sku.trim() && gv !== variant);
2398
+ return (variant.enabled &&
2399
+ this.generatedVariants.find(gv => gv.sku.trim() === variant.sku.trim() && gv !== variant));
2307
2400
  });
2308
2401
  if (withDuplicateSkus.length) {
2309
2402
  return this.modalService
@@ -2386,7 +2479,7 @@ class ProductVariantsEditorComponent {
2386
2479
  return forkJoin(groupsIds.map(id => this.dataService.product
2387
2480
  .getProductOptionGroup(id)
2388
2481
  .mapSingle(data => data.productOptionGroup)
2389
- .pipe(filter(notNullOrUndefined))));
2482
+ .pipe(filter(notNullOrUndefined)))).pipe(defaultIfEmpty([]));
2390
2483
  }
2391
2484
  createNewProductVariants(groups) {
2392
2485
  const options = groups
@@ -2434,15 +2527,20 @@ class ProductVariantsEditorComponent {
2434
2527
  .mapSingle(({ product }) => product)
2435
2528
  .subscribe(p => {
2436
2529
  this.product = p;
2530
+ const allUsedOptionIds = p.variants.map(v => v.options.map(option => option.id)).flat();
2531
+ const allUsedOptionGroupIds = p.variants
2532
+ .map(v => v.options.map(option => option.groupId))
2533
+ .flat();
2437
2534
  this.optionGroups = p.optionGroups.map(og => {
2438
2535
  return {
2439
2536
  id: og.id,
2440
2537
  isNew: false,
2441
2538
  name: og.name,
2539
+ locked: allUsedOptionGroupIds.includes(og.id),
2442
2540
  values: og.options.map(o => ({
2443
2541
  id: o.id,
2444
2542
  name: o.name,
2445
- locked: true,
2543
+ locked: allUsedOptionIds.includes(o.id),
2446
2544
  })),
2447
2545
  };
2448
2546
  });
@@ -2465,7 +2563,7 @@ class ProductVariantsEditorComponent {
2465
2563
  ProductVariantsEditorComponent.decorators = [
2466
2564
  { type: Component, args: [{
2467
2565
  selector: 'vdr-product-variants-editor',
2468
- template: "<vdr-action-bar>\r\n <vdr-ab-right>\r\n <button\r\n class=\"btn btn-primary\"\r\n (click)=\"save()\"\r\n [disabled]=\"!formValueChanged || getVariantsToAdd().length === 0\"\r\n >\r\n {{ 'common.add-new-variants' | translate: { count: getVariantsToAdd().length } }}\r\n </button>\r\n </vdr-ab-right>\r\n</vdr-action-bar>\r\n\r\n<div *ngFor=\"let group of optionGroups\" class=\"option-groups\">\r\n <div class=\"name\">\r\n <label>{{ 'catalog.option' | translate }}</label>\r\n <input clrInput [(ngModel)]=\"group.name\" name=\"name\" [readonly]=\"!group.isNew\" />\r\n </div>\r\n <div class=\"values\">\r\n <label>{{ 'catalog.option-values' | translate }}</label>\r\n <vdr-option-value-input\r\n #optionValueInputComponent\r\n [(ngModel)]=\"group.values\"\r\n (ngModelChange)=\"generateVariants()\"\r\n [groupName]=\"group.name\"\r\n [disabled]=\"group.name === ''\"\r\n ></vdr-option-value-input>\r\n </div>\r\n <div>\r\n <button *ngIf=\"group.isNew\" class=\"btn btn-icon btn-danger-outline mt5\" (click)=\"removeOption(group)\">\r\n <clr-icon shape=\"trash\"></clr-icon>\r\n </button>\r\n </div>\r\n</div>\r\n<button\r\n class=\"btn btn-primary-outline btn-sm\"\r\n (click)=\"addOption()\"\r\n>\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-option' | translate }}\r\n</button>\r\n\r\n<div class=\"variants-preview\">\r\n <table class=\"table\">\r\n <thead>\r\n <tr>\r\n <th>{{ 'common.create' | translate }}</th>\r\n <th>{{ 'catalog.variant' | translate }}</th>\r\n <th>{{ 'catalog.sku' | translate }}</th>\r\n <th>{{ 'catalog.price' | translate }}</th>\r\n <th>{{ 'catalog.stock-on-hand' | translate }}</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tr *ngFor=\"let variant of generatedVariants\" [class.disabled]=\"!variant.enabled || variant.existing\">\r\n <td>\r\n <input\r\n type=\"checkbox\"\r\n *ngIf=\"!variant.existing\"\r\n [(ngModel)]=\"variant.enabled\"\r\n name=\"enabled\"\r\n clrCheckbox\r\n (ngModelChange)=\"formValueChanged = true\"\r\n />\r\n </td>\r\n <td>\r\n {{ getVariantName(variant) | translate }}\r\n </td>\r\n <td>\r\n <clr-input-container *ngIf=\"!variant.existing\">\r\n <input\r\n clrInput\r\n type=\"text\"\r\n [(ngModel)]=\"variant.sku\"\r\n [placeholder]=\"'catalog.sku' | translate\"\r\n name=\"sku\"\r\n required\r\n (ngModelChange)=\"onFormChanged(variant)\"\r\n />\r\n </clr-input-container>\r\n <span *ngIf=\"variant.existing\">{{ variant.sku }}</span>\r\n </td>\r\n <td>\r\n <clr-input-container *ngIf=\"!variant.existing\">\r\n <vdr-currency-input\r\n clrInput\r\n [(ngModel)]=\"variant.price\"\r\n name=\"price\"\r\n [currencyCode]=\"currencyCode\"\r\n (ngModelChange)=\"onFormChanged(variant)\"\r\n ></vdr-currency-input>\r\n </clr-input-container>\r\n <span *ngIf=\"variant.existing\">{{ variant.price | localeCurrency: currencyCode }}</span>\r\n </td>\r\n <td>\r\n <clr-input-container *ngIf=\"!variant.existing\">\r\n <input\r\n clrInput\r\n type=\"number\"\r\n [(ngModel)]=\"variant.stock\"\r\n name=\"stock\"\r\n min=\"0\"\r\n step=\"1\"\r\n (ngModelChange)=\"onFormChanged(variant)\"\r\n />\r\n </clr-input-container>\r\n <span *ngIf=\"variant.existing\">{{ variant.stock }}</span>\r\n </td>\r\n <td>\r\n <vdr-dropdown *ngIf=\"variant.productVariantId as productVariantId\">\r\n <button class=\"icon-button\" vdrDropdownTrigger>\r\n <clr-icon shape=\"ellipsis-vertical\"></clr-icon>\r\n </button>\r\n <vdr-dropdown-menu vdrPosition=\"bottom-right\">\r\n <button\r\n type=\"button\"\r\n class=\"delete-button\"\r\n (click)=\"deleteVariant(productVariantId)\"\r\n vdrDropdownItem\r\n >\r\n <clr-icon shape=\"trash\" class=\"is-danger\"></clr-icon>\r\n {{ 'common.delete' | translate }}\r\n </button>\r\n </vdr-dropdown-menu>\r\n </vdr-dropdown>\r\n </td>\r\n </tr>\r\n </table>\r\n</div>\r\n",
2566
+ template: "<vdr-action-bar>\r\n <vdr-ab-right>\r\n <button\r\n class=\"btn btn-primary\"\r\n (click)=\"save()\"\r\n [disabled]=\"(!formValueChanged && !optionsChanged) || getVariantsToAdd().length === 0\"\r\n >\r\n {{ 'common.add-new-variants' | translate: { count: getVariantsToAdd().length } }}\r\n </button>\r\n </vdr-ab-right>\r\n</vdr-action-bar>\r\n\r\n<div *ngFor=\"let group of optionGroups\" class=\"option-groups\">\r\n <div class=\"name\">\r\n <label>{{ 'catalog.option' | translate }}</label>\r\n <input clrInput [(ngModel)]=\"group.name\" name=\"name\" [readonly]=\"!group.isNew\" />\r\n </div>\r\n <div class=\"values\">\r\n <label>{{ 'catalog.option-values' | translate }}</label>\r\n <vdr-option-value-input\r\n #optionValueInputComponent\r\n [options]=\"group.values\"\r\n [groupName]=\"group.name\"\r\n [disabled]=\"group.name === ''\"\r\n (add)=\"addOption(group.id, $event.name)\"\r\n (remove)=\"removeOption(group.id, $event)\"\r\n ></vdr-option-value-input>\r\n </div>\r\n <div>\r\n <button\r\n [disabled]=\"group.locked\"\r\n class=\"btn btn-icon btn-danger-outline mt5\" (click)=\"removeOptionGroup(group)\">\r\n <clr-icon shape=\"trash\"></clr-icon>\r\n </button>\r\n </div>\r\n</div>\r\n<button class=\"btn btn-primary-outline btn-sm\" (click)=\"addOptionGroup()\">\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-option' | translate }}\r\n</button>\r\n\r\n<div class=\"variants-preview\">\r\n <table class=\"table\">\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>{{ 'catalog.variant' | translate }}</th>\r\n <th>{{ 'catalog.sku' | translate }}</th>\r\n <th>{{ 'catalog.price' | translate }}</th>\r\n <th>{{ 'catalog.stock-on-hand' | translate }}</th>\r\n <th></th>\r\n </tr>\r\n </thead>\r\n <tr *ngFor=\"let variant of generatedVariants\" [class.disabled]=\"!variant.enabled || variant.existing\">\r\n <td class=\"left\">\r\n <clr-checkbox-wrapper *ngIf=\"!variant.existing\">\r\n <input\r\n type=\"checkbox\"\r\n [(ngModel)]=\"variant.enabled\"\r\n name=\"enabled\"\r\n clrCheckbox\r\n (ngModelChange)=\"formValueChanged = true\"\r\n />\r\n <label>{{ 'common.create' | translate }}</label>\r\n </clr-checkbox-wrapper>\r\n </td>\r\n <td>\r\n {{ getVariantName(variant) | translate }}\r\n </td>\r\n <td>\r\n <div class=\"flex center\">\r\n <clr-input-container *ngIf=\"!variant.existing\">\r\n <input\r\n clrInput\r\n type=\"text\"\r\n [(ngModel)]=\"variant.sku\"\r\n [placeholder]=\"'catalog.sku' | translate\"\r\n name=\"sku\"\r\n required\r\n (ngModelChange)=\"onFormChanged(variant)\"\r\n />\r\n </clr-input-container>\r\n <span *ngIf=\"variant.existing\">{{ variant.sku }}</span>\r\n </div>\r\n </td>\r\n <td>\r\n <div class=\"flex center\">\r\n <clr-input-container *ngIf=\"!variant.existing\">\r\n <vdr-currency-input\r\n clrInput\r\n [(ngModel)]=\"variant.price\"\r\n name=\"price\"\r\n [currencyCode]=\"currencyCode\"\r\n (ngModelChange)=\"onFormChanged(variant)\"\r\n ></vdr-currency-input>\r\n </clr-input-container>\r\n <span *ngIf=\"variant.existing\">{{ variant.price | localeCurrency: currencyCode }}</span>\r\n </div>\r\n </td>\r\n <td>\r\n <div class=\"flex center\">\r\n <clr-input-container *ngIf=\"!variant.existing\">\r\n <input\r\n clrInput\r\n type=\"number\"\r\n [(ngModel)]=\"variant.stock\"\r\n name=\"stock\"\r\n min=\"0\"\r\n step=\"1\"\r\n (ngModelChange)=\"onFormChanged(variant)\"\r\n />\r\n </clr-input-container>\r\n <span *ngIf=\"variant.existing\">{{ variant.stock }}</span>\r\n </div>\r\n </td>\r\n <td>\r\n <vdr-dropdown *ngIf=\"variant.productVariantId as productVariantId\">\r\n <button class=\"icon-button\" vdrDropdownTrigger>\r\n <clr-icon shape=\"ellipsis-vertical\"></clr-icon>\r\n </button>\r\n <vdr-dropdown-menu vdrPosition=\"bottom-right\">\r\n <button\r\n type=\"button\"\r\n class=\"delete-button\"\r\n (click)=\"deleteVariant(productVariantId, variant.options)\"\r\n vdrDropdownItem\r\n >\r\n <clr-icon shape=\"trash\" class=\"is-danger\"></clr-icon>\r\n {{ 'common.delete' | translate }}\r\n </button>\r\n </vdr-dropdown-menu>\r\n </vdr-dropdown>\r\n </td>\r\n </tr>\r\n </table>\r\n</div>\r\n",
2469
2567
  changeDetection: ChangeDetectionStrategy.Default,
2470
2568
  styles: [".option-groups{display:flex}.option-groups:first-of-type{margin-top:24px}.values{flex:1;margin:0 6px}.variants-preview tr.disabled td{background-color:var(--color-component-bg-100);color:var(--color-grey-400)}\n"]
2471
2569
  },] }
@@ -3240,6 +3338,12 @@ class GenerateProductVariantsComponent {
3240
3338
  }
3241
3339
  addOption() {
3242
3340
  this.optionGroups.push({ name: '', values: [] });
3341
+ const index = this.optionGroups.length - 1;
3342
+ setTimeout(() => {
3343
+ var _a;
3344
+ const input = (_a = this.groupNameInputs.get(index)) === null || _a === void 0 ? void 0 : _a.nativeElement;
3345
+ input === null || input === void 0 ? void 0 : input.focus();
3346
+ });
3243
3347
  }
3244
3348
  removeOption(name) {
3245
3349
  this.optionGroups = this.optionGroups.filter(g => g.name !== name);
@@ -3288,7 +3392,7 @@ class GenerateProductVariantsComponent {
3288
3392
  GenerateProductVariantsComponent.decorators = [
3289
3393
  { type: Component, args: [{
3290
3394
  selector: 'vdr-generate-product-variants',
3291
- template: "<div *ngFor=\"let group of optionGroups\" class=\"option-groups\">\r\n <div class=\"name\">\r\n <label>{{ 'catalog.option' | translate }}</label>\r\n <input\r\n placeholder=\"e.g. Size\"\r\n clrInput\r\n [(ngModel)]=\"group.name\"\r\n name=\"name\"\r\n required\r\n (keydown.enter)=\"handleEnter($event, optionValueInputComponent)\"\r\n />\r\n </div>\r\n <div class=\"values\">\r\n <label>{{ 'catalog.option-values' | translate }}</label>\r\n <vdr-option-value-input\r\n #optionValueInputComponent\r\n [(ngModel)]=\"group.values\"\r\n (ngModelChange)=\"generateVariants()\"\r\n [groupName]=\"group.name\"\r\n [disabled]=\"group.name === ''\"\r\n ></vdr-option-value-input>\r\n </div>\r\n <div class=\"remove-group\">\r\n <button\r\n class=\"btn btn-icon btn-warning-outline\"\r\n [title]=\"'catalog.remove-option' | translate\"\r\n (click)=\"removeOption(group.name)\"\r\n >\r\n <clr-icon shape=\"trash\"></clr-icon>\r\n </button>\r\n </div>\r\n</div>\r\n<button class=\"btn btn-primary-outline btn-sm\" (click)=\"addOption()\">\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-option' | translate }}\r\n</button>\r\n\r\n<div class=\"variants-preview\">\r\n <table class=\"table\">\r\n <thead>\r\n <tr>\r\n <th *ngIf=\"1 < variants.length\">{{ 'common.create' | translate }}</th>\r\n <th *ngIf=\"1 < variants.length\">{{ 'catalog.variant' | translate }}</th>\r\n <th>{{ 'catalog.sku' | translate }}</th>\r\n <th>{{ 'catalog.price' | translate }}</th>\r\n <th>{{ 'catalog.stock-on-hand' | translate }}</th>\r\n </tr>\r\n </thead>\r\n <tr\r\n *ngFor=\"let variant of variants; trackBy: trackByFn\"\r\n [class.disabled]=\"!variantFormValues[variant.id].enabled\"\r\n >\r\n <td *ngIf=\"1 < variants.length\">\r\n <input\r\n type=\"checkbox\"\r\n (change)=\"onFormChange()\"\r\n [(ngModel)]=\"variantFormValues[variant.id].enabled\"\r\n clrCheckbox\r\n />\r\n </td>\r\n <td *ngIf=\"1 < variants.length\">\r\n {{ variant.values.join(' ') }}\r\n </td>\r\n <td>\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"text\"\r\n (change)=\"onFormChange()\"\r\n [(ngModel)]=\"variantFormValues[variant.id].sku\"\r\n [placeholder]=\"'catalog.sku' | translate\"\r\n />\r\n </clr-input-container>\r\n </td>\r\n <td>\r\n <clr-input-container>\r\n <vdr-currency-input\r\n clrInput\r\n [(ngModel)]=\"variantFormValues[variant.id].price\"\r\n (ngModelChange)=\"onFormChange()\"\r\n [currencyCode]=\"currencyCode\"\r\n ></vdr-currency-input>\r\n </clr-input-container>\r\n </td>\r\n <td>\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"number\"\r\n [(ngModel)]=\"variantFormValues[variant.id].stock\"\r\n (change)=\"onFormChange()\"\r\n min=\"0\"\r\n step=\"1\"\r\n />\r\n </clr-input-container>\r\n </td>\r\n </tr>\r\n </table>\r\n</div>\r\n",
3395
+ template: "<div *ngFor=\"let group of optionGroups\" class=\"option-groups\">\r\n <div class=\"name\">\r\n <label>{{ 'catalog.option' | translate }}</label>\r\n <input\r\n #optionGroupName\r\n placeholder=\"e.g. Size\"\r\n clrInput\r\n [(ngModel)]=\"group.name\"\r\n name=\"name\"\r\n required\r\n (keydown.enter)=\"handleEnter($event, optionValueInputComponent)\"\r\n />\r\n </div>\r\n <div class=\"values\">\r\n <label>{{ 'catalog.option-values' | translate }}</label>\r\n <vdr-option-value-input\r\n #optionValueInputComponent\r\n [(ngModel)]=\"group.values\"\r\n (ngModelChange)=\"generateVariants()\"\r\n (edit)=\"generateVariants()\"\r\n [groupName]=\"group.name\"\r\n [disabled]=\"group.name === ''\"\r\n ></vdr-option-value-input>\r\n </div>\r\n <div class=\"remove-group\">\r\n <button\r\n class=\"btn btn-icon btn-warning-outline\"\r\n [title]=\"'catalog.remove-option' | translate\"\r\n (click)=\"removeOption(group.name)\"\r\n >\r\n <clr-icon shape=\"trash\"></clr-icon>\r\n </button>\r\n </div>\r\n</div>\r\n<button class=\"btn btn-primary-outline btn-sm\" (click)=\"addOption()\">\r\n <clr-icon shape=\"plus\"></clr-icon>\r\n {{ 'catalog.add-option' | translate }}\r\n</button>\r\n\r\n<div class=\"variants-preview\">\r\n <table class=\"table\">\r\n <thead>\r\n <tr>\r\n <th *ngIf=\"1 < variants.length\">{{ 'common.create' | translate }}</th>\r\n <th *ngIf=\"1 < variants.length\">{{ 'catalog.variant' | translate }}</th>\r\n <th>{{ 'catalog.sku' | translate }}</th>\r\n <th>{{ 'catalog.price' | translate }}</th>\r\n <th>{{ 'catalog.stock-on-hand' | translate }}</th>\r\n </tr>\r\n </thead>\r\n <tr\r\n *ngFor=\"let variant of variants; trackBy: trackByFn\"\r\n [class.disabled]=\"!variantFormValues[variant.id].enabled\"\r\n >\r\n <td *ngIf=\"1 < variants.length\">\r\n <input\r\n type=\"checkbox\"\r\n (change)=\"onFormChange()\"\r\n [(ngModel)]=\"variantFormValues[variant.id].enabled\"\r\n clrCheckbox\r\n />\r\n </td>\r\n <td *ngIf=\"1 < variants.length\">\r\n {{ variant.values.join(' ') }}\r\n </td>\r\n <td>\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"text\"\r\n (change)=\"onFormChange()\"\r\n [(ngModel)]=\"variantFormValues[variant.id].sku\"\r\n [placeholder]=\"'catalog.sku' | translate\"\r\n />\r\n </clr-input-container>\r\n </td>\r\n <td>\r\n <clr-input-container>\r\n <vdr-currency-input\r\n clrInput\r\n [(ngModel)]=\"variantFormValues[variant.id].price\"\r\n (ngModelChange)=\"onFormChange()\"\r\n [currencyCode]=\"currencyCode\"\r\n ></vdr-currency-input>\r\n </clr-input-container>\r\n </td>\r\n <td>\r\n <clr-input-container>\r\n <input\r\n clrInput\r\n type=\"number\"\r\n [(ngModel)]=\"variantFormValues[variant.id].stock\"\r\n (change)=\"onFormChange()\"\r\n min=\"0\"\r\n step=\"1\"\r\n />\r\n </clr-input-container>\r\n </td>\r\n </tr>\r\n </table>\r\n</div>\r\n",
3292
3396
  styles: [":host{display:block;margin-bottom:120px}.option-groups{display:flex}.values{flex:1;margin:0 6px}.remove-group{padding-top:18px}.variants-preview tr.disabled td{background-color:var(--color-component-bg-100);color:var(--color-grey-400)}\n"]
3293
3397
  },] }
3294
3398
  ];
@@ -3296,7 +3400,8 @@ GenerateProductVariantsComponent.ctorParameters = () => [
3296
3400
  { type: DataService }
3297
3401
  ];
3298
3402
  GenerateProductVariantsComponent.propDecorators = {
3299
- variantsChange: [{ type: Output }]
3403
+ variantsChange: [{ type: Output }],
3404
+ groupNameInputs: [{ type: ViewChildren, args: ['optionGroupName', { read: ElementRef },] }]
3300
3405
  };
3301
3406
 
3302
3407
  const OPTION_VALUE_INPUT_VALUE_ACCESSOR = {
@@ -3308,10 +3413,18 @@ class OptionValueInputComponent {
3308
3413
  constructor(changeDetector) {
3309
3414
  this.changeDetector = changeDetector;
3310
3415
  this.groupName = '';
3416
+ this.add = new EventEmitter();
3417
+ this.remove = new EventEmitter();
3418
+ this.edit = new EventEmitter();
3311
3419
  this.disabled = false;
3312
3420
  this.input = '';
3313
3421
  this.isFocussed = false;
3314
3422
  this.lastSelected = false;
3423
+ this.editingIndex = -1;
3424
+ }
3425
+ get optionValues() {
3426
+ var _a, _b;
3427
+ return (_b = (_a = this.formValue) !== null && _a !== void 0 ? _a : this.options) !== null && _b !== void 0 ? _b : [];
3315
3428
  }
3316
3429
  registerOnChange(fn) {
3317
3430
  this.onChangeFn = fn;
@@ -3324,15 +3437,42 @@ class OptionValueInputComponent {
3324
3437
  this.changeDetector.markForCheck();
3325
3438
  }
3326
3439
  writeValue(obj) {
3327
- this.options = obj || [];
3440
+ this.formValue = obj || [];
3328
3441
  }
3329
3442
  focus() {
3330
3443
  this.textArea.nativeElement.focus();
3331
3444
  }
3445
+ editName(index, event) {
3446
+ var _a;
3447
+ const optionValue = this.optionValues[index];
3448
+ if (!optionValue.locked && !optionValue.id) {
3449
+ event.cancelBubble = true;
3450
+ this.editingIndex = index;
3451
+ const input = (_a = this.nameInputs.get(index)) === null || _a === void 0 ? void 0 : _a.nativeElement;
3452
+ setTimeout(() => input === null || input === void 0 ? void 0 : input.focus());
3453
+ }
3454
+ }
3455
+ updateOption(index, event) {
3456
+ const optionValue = this.optionValues[index];
3457
+ const newName = event.target.value;
3458
+ if (optionValue) {
3459
+ if (newName) {
3460
+ optionValue.name = newName;
3461
+ this.edit.emit({ index, option: optionValue });
3462
+ }
3463
+ this.editingIndex = -1;
3464
+ }
3465
+ }
3332
3466
  removeOption(option) {
3467
+ var _a;
3333
3468
  if (!option.locked) {
3334
- this.options = this.options.filter(o => o.name !== option.name);
3335
- this.onChangeFn(this.options);
3469
+ if (this.formValue) {
3470
+ this.formValue = (_a = this.formValue) === null || _a === void 0 ? void 0 : _a.filter(o => o.name !== option.name);
3471
+ this.onChangeFn(this.formValue);
3472
+ }
3473
+ else {
3474
+ this.remove.emit(option);
3475
+ }
3336
3476
  }
3337
3477
  }
3338
3478
  handleKey(event) {
@@ -3360,9 +3500,17 @@ class OptionValueInputComponent {
3360
3500
  this.addOptionValue();
3361
3501
  }
3362
3502
  addOptionValue() {
3363
- this.options = unique([...this.options, ...this.parseInputIntoOptions(this.input)]);
3503
+ const options = this.parseInputIntoOptions(this.input);
3504
+ if (!this.formValue && this.options) {
3505
+ for (const option of options) {
3506
+ this.add.emit(option);
3507
+ }
3508
+ }
3509
+ else {
3510
+ this.formValue = unique([...this.formValue, ...options]);
3511
+ this.onChangeFn(this.formValue);
3512
+ }
3364
3513
  this.input = '';
3365
- this.onChangeFn(this.options);
3366
3514
  }
3367
3515
  parseInputIntoOptions(input) {
3368
3516
  return input
@@ -3372,18 +3520,19 @@ class OptionValueInputComponent {
3372
3520
  .map(s => ({ name: s, locked: false }));
3373
3521
  }
3374
3522
  removeLastOption() {
3375
- if (!this.options[this.options.length - 1].locked) {
3376
- this.options = this.options.slice(0, this.options.length - 1);
3523
+ if (this.optionValues.length) {
3524
+ const option = this.optionValues[this.optionValues.length - 1];
3525
+ this.removeOption(option);
3377
3526
  }
3378
3527
  }
3379
3528
  }
3380
3529
  OptionValueInputComponent.decorators = [
3381
3530
  { type: Component, args: [{
3382
3531
  selector: 'vdr-option-value-input',
3383
- template: "<div class=\"input-wrapper\" [class.focus]=\"isFocussed\" (click)=\"textArea.focus()\">\r\n <div class=\"chips\" *ngIf=\"0 < options.length\">\r\n <vdr-chip\r\n *ngFor=\"let option of options; last as isLast\"\r\n [icon]=\"option.locked ? 'lock' : 'times'\"\r\n [class.selected]=\"isLast && lastSelected\"\r\n [class.locked]=\"option.locked\"\r\n [colorFrom]=\"groupName\"\r\n (iconClick)=\"removeOption(option)\"\r\n >\r\n {{ option.name }}\r\n </vdr-chip>\r\n </div>\r\n <textarea\r\n #textArea\r\n (keyup)=\"handleKey($event)\"\r\n (focus)=\"isFocussed = true\"\r\n (blur)=\"handleBlur()\"\r\n [(ngModel)]=\"input\"\r\n [disabled]=\"disabled\"\r\n ></textarea>\r\n</div>\r\n",
3532
+ template: "<div class=\"input-wrapper\" [class.focus]=\"isFocussed\" (click)=\"textArea.focus()\">\r\n <div class=\"chips\" *ngIf=\"0 < optionValues.length\">\r\n <vdr-chip\r\n *ngFor=\"let option of optionValues; last as isLast; index as i\"\r\n [icon]=\"option.locked ? 'lock' : 'times'\"\r\n [class.selected]=\"isLast && lastSelected\"\r\n [class.locked]=\"option.locked\"\r\n [colorFrom]=\"groupName\"\r\n (iconClick)=\"removeOption(option)\"\r\n >\r\n <span [hidden]=\"editingIndex !== i\">\r\n <input\r\n #editNameInput\r\n type=\"text\"\r\n [ngModel]=\"option.name\"\r\n (blur)=\"updateOption(i, $event)\"\r\n (click)=\"$event.cancelBubble = true\"\r\n />\r\n </span>\r\n <span\r\n class=\"option-name\"\r\n [class.editable]=\"!option.locked && !option.id\"\r\n (click)=\"editName(i, $event)\" [hidden]=\"editingIndex === i\">{{ option.name }}</span>\r\n </vdr-chip>\r\n </div>\r\n <textarea\r\n #textArea\r\n (keyup)=\"handleKey($event)\"\r\n (focus)=\"isFocussed = true\"\r\n (blur)=\"handleBlur()\"\r\n [(ngModel)]=\"input\"\r\n [disabled]=\"disabled\"\r\n ></textarea>\r\n</div>\r\n",
3384
3533
  changeDetection: ChangeDetectionStrategy.Default,
3385
3534
  providers: [OPTION_VALUE_INPUT_VALUE_ACCESSOR],
3386
- styles: [".input-wrapper{background-color:#fff;border-radius:3px!important;border:1px solid var(--color-grey-300)!important;cursor:text}.input-wrapper.focus{border-color:var(--color-primary-500)!important;box-shadow:0 0 1px 1px var(--color-primary-100)}.input-wrapper .chips{padding:5px}.input-wrapper textarea{border:none;width:100%;height:24px;margin-top:3px;padding:0 6px}.input-wrapper textarea:focus{outline:none}.input-wrapper textarea:disabled{background-color:var(--color-component-bg-100)}vdr-chip ::ng-deep .wrapper{margin:0 3px}vdr-chip.locked{opacity:.8}vdr-chip.selected ::ng-deep .wrapper{border-color:var(--color-warning-500)!important;box-shadow:0 0 1px 1px var(--color-warning-400);opacity:.6}\n"]
3535
+ styles: [".input-wrapper{background-color:#fff;border-radius:3px!important;border:1px solid var(--color-grey-300)!important;cursor:text}.input-wrapper.focus{border-color:var(--color-primary-500)!important;box-shadow:0 0 1px 1px var(--color-primary-100)}.input-wrapper .chips{padding:5px}.input-wrapper textarea{border:none;width:100%;height:24px;margin-top:3px;padding:0 6px}.input-wrapper textarea:focus{outline:none}.input-wrapper textarea:disabled{background-color:var(--color-component-bg-100)}vdr-chip ::ng-deep .wrapper{margin:0 3px}vdr-chip.locked{opacity:.8}vdr-chip.selected ::ng-deep .wrapper{border-color:var(--color-warning-500)!important;box-shadow:0 0 1px 1px var(--color-warning-400);opacity:.6}vdr-chip .option-name.editable:hover{outline:1px solid var(--color-component-bg-300);outline-offset:1px;border-radius:1px}vdr-chip input{padding:0!important;margin-top:-2px;margin-bottom:-2px}\n"]
3387
3536
  },] }
3388
3537
  ];
3389
3538
  OptionValueInputComponent.ctorParameters = () => [
@@ -3391,7 +3540,13 @@ OptionValueInputComponent.ctorParameters = () => [
3391
3540
  ];
3392
3541
  OptionValueInputComponent.propDecorators = {
3393
3542
  groupName: [{ type: Input }],
3394
- textArea: [{ type: ViewChild, args: ['textArea', { static: true },] }]
3543
+ textArea: [{ type: ViewChild, args: ['textArea', { static: true },] }],
3544
+ nameInputs: [{ type: ViewChildren, args: ['editNameInput', { read: ElementRef },] }],
3545
+ options: [{ type: Input }],
3546
+ add: [{ type: Output }],
3547
+ remove: [{ type: Output }],
3548
+ edit: [{ type: Output }],
3549
+ disabled: [{ type: Input }]
3395
3550
  };
3396
3551
 
3397
3552
  class UpdateProductOptionDialogComponent {