Browse Source

feat(admin-ui): Allow editing ProductOptionGroup names & options

Relates to #965
Michael Bromley 4 years ago
parent
commit
55d9784ccc
24 changed files with 1152 additions and 3407 deletions
  1. 17 17
      packages/admin-ui/i18n-coverage.json
  2. 2 0
      packages/admin-ui/src/lib/catalog/src/catalog.module.ts
  3. 31 0
      packages/admin-ui/src/lib/catalog/src/catalog.routes.ts
  4. 93 0
      packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.html
  5. 4 0
      packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.scss
  6. 219 0
      packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.ts
  7. 1 0
      packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.html
  8. 1 0
      packages/admin-ui/src/lib/catalog/src/public_api.ts
  9. 543 3130
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  10. 189 256
      packages/admin-ui/src/lib/core/src/common/introspection-result.ts
  11. 15 3
      packages/admin-ui/src/lib/core/src/data/definitions/product-definitions.ts
  12. 12 0
      packages/admin-ui/src/lib/core/src/data/providers/product-data.service.ts
  13. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/cs.json
  14. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  15. 3 1
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  16. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  17. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/fr.json
  18. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/it.json
  19. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  20. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  21. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/ru.json
  22. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/uk.json
  23. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  24. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

+ 17 - 17
packages/admin-ui/i18n-coverage.json

@@ -1,64 +1,64 @@
 {
-  "generatedOn": "2021-07-21T12:03:11.085Z",
-  "lastCommit": "355488079e08c0dd119431f252f2b4135e390937",
+  "generatedOn": "2021-08-31T09:12:48.131Z",
+  "lastCommit": "7b76a7c12e4e93fc1240dd619755d40606004672",
   "translationStatus": {
     "cs": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 591,
       "percentage": 95
     },
     "de": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 570,
-      "percentage": 92
+      "percentage": 91
     },
     "en": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 621,
       "percentage": 100
     },
     "es": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 309,
       "percentage": 50
     },
     "fr": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 613,
-      "percentage": 99
+      "percentage": 98
     },
     "it": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 621,
       "percentage": 100
     },
     "pl": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 405,
       "percentage": 65
     },
     "pt_BR": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 588,
-      "percentage": 95
+      "percentage": 94
     },
     "ru": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 621,
       "percentage": 100
     },
     "uk": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 621,
       "percentage": 100
     },
     "zh_Hans": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 558,
       "percentage": 90
     },
     "zh_Hant": {
-      "tokenCount": 621,
+      "tokenCount": 623,
       "translatedCount": 385,
       "percentage": 62
     }

+ 2 - 0
packages/admin-ui/src/lib/catalog/src/catalog.module.ts

@@ -20,6 +20,7 @@ import { OptionValueInputComponent } from './components/option-value-input/optio
 import { ProductAssetsComponent } from './components/product-assets/product-assets.component';
 import { ProductDetailComponent } from './components/product-detail/product-detail.component';
 import { ProductListComponent } from './components/product-list/product-list.component';
+import { ProductOptionsEditorComponent } from './components/product-options-editor/product-options-editor.component';
 import { ProductSearchInputComponent } from './components/product-search-input/product-search-input.component';
 import { ProductVariantsEditorComponent } from './components/product-variants-editor/product-variants-editor.component';
 import { ProductVariantsListComponent } from './components/product-variants-list/product-variants-list.component';
@@ -54,6 +55,7 @@ import { VariantPriceDetailComponent } from './components/variant-price-detail/v
         AssignProductsToChannelDialogComponent,
         AssetDetailComponent,
         ConfirmVariantDeletionDialogComponent,
+        ProductOptionsEditorComponent,
     ],
 })
 export class CatalogModule {}

+ 31 - 0
packages/admin-ui/src/lib/catalog/src/catalog.routes.ts

@@ -19,6 +19,7 @@ import { FacetDetailComponent } from './components/facet-detail/facet-detail.com
 import { FacetListComponent } from './components/facet-list/facet-list.component';
 import { ProductDetailComponent } from './components/product-detail/product-detail.component';
 import { ProductListComponent } from './components/product-list/product-list.component';
+import { ProductOptionsEditorComponent } from './components/product-options-editor/product-options-editor.component';
 import { ProductVariantsEditorComponent } from './components/product-variants-editor/product-variants-editor.component';
 import { AssetResolver } from './providers/routing/asset-resolver';
 import { CollectionResolver } from './providers/routing/collection-resolver';
@@ -52,6 +53,15 @@ export const catalogRoutes: Route[] = [
             breadcrumb: productVariantEditorBreadcrumb,
         },
     },
+    {
+        path: 'products/:id/options',
+        component: ProductOptionsEditorComponent,
+        resolve: createResolveData(ProductVariantsResolver),
+        canDeactivate: [CanDeactivateDetailGuard],
+        data: {
+            breadcrumb: productOptionsEditorBreadcrumb,
+        },
+    },
     {
         path: 'facets',
         component: FacetListComponent,
@@ -132,6 +142,27 @@ export function productVariantEditorBreadcrumb(data: any, params: any) {
     );
 }
 
+export function productOptionsEditorBreadcrumb(data: any, params: any) {
+    return data.entity.pipe(
+        map((entity: any) => {
+            return [
+                {
+                    label: _('breadcrumb.products'),
+                    link: ['../', 'products'],
+                },
+                {
+                    label: `${entity.name}`,
+                    link: ['../', 'products', params.id, { tab: 'variants' }],
+                },
+                {
+                    label: _('breadcrumb.product-options'),
+                    link: ['options'],
+                },
+            ];
+        }),
+    );
+}
+
 export function facetBreadcrumb(data: any, params: any) {
     return detailBreadcrumb<FacetWithValues.Fragment>({
         entity: data.entity,

+ 93 - 0
packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.html

@@ -0,0 +1,93 @@
+<vdr-action-bar>
+    <vdr-ab-left>
+        <vdr-language-selector
+            [availableLanguageCodes]="availableLanguages$ | async"
+            [currentLanguageCode]="languageCode$ | async"
+            (languageCodeChange)="setLanguage($event)"
+        ></vdr-language-selector>
+    </vdr-ab-left>
+
+    <vdr-ab-right>
+        <div class="flex center">
+            <div class="mr2">
+                <clr-checkbox-wrapper>
+                    <input
+                        clrCheckbox
+                        type="checkbox"
+                        id="auto-update"
+                        [(ngModel)]="autoUpdateVariantNames"
+                    />
+                    <label>{{ 'catalog.auto-update-product-variant-name' | translate }}</label>
+                </clr-checkbox-wrapper>
+            </div>
+            <button
+                *vdrIfPermissions="updatePermission"
+                class="btn btn-primary"
+                (click)="save()"
+                [disabled]="detailForm.pristine || detailForm.invalid"
+            >
+                {{ 'common.update' | translate }}
+            </button>
+        </div>
+    </vdr-ab-right>
+</vdr-action-bar>
+<form class="form" [formGroup]="detailForm" *ngIf="optionGroups$ | async as optionGroups">
+    <div formGroupName="optionGroups" class="clr-row">
+        <div class="clr-col-12 clr-col-xl-6" *ngFor="let optionGroup of getOptionGroups(); index as i">
+            <section class="card" [formArrayName]="i">
+                <div class="card-header option-group-header">
+                    <vdr-entity-info [entity]="optionGroup.value"></vdr-entity-info>
+                    <div class="ml2">{{ optionGroup.value.code }}</div>
+                </div>
+                <div class="card-block">
+                    <vdr-form-field [label]="'common.name' | translate" for="name">
+                        <input
+                            [id]="'name-' + i"
+                            type="text"
+                            formControlName="name"
+                            [readonly]="!(updatePermission | hasPermission)"
+                        />
+                    </vdr-form-field>
+                    <vdr-form-field [label]="'common.code' | translate" for="code">
+                        <input
+                            [id]="'code-' + i"
+                            type="text"
+                            [readonly]="!(updatePermission | hasPermission)"
+                            formControlName="code"
+                        />
+                    </vdr-form-field>
+                </div>
+                <section class="card-block">
+                    <table class="facet-values-list table mt2 mb4" formGroupName="options">
+                        <thead>
+                            <tr>
+                                <th></th>
+                                <th>{{ 'common.name' | translate }}</th>
+                                <th>{{ 'common.code' | translate }}</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr
+                                class="facet-value"
+                                *ngFor="let option of getOptions(optionGroup); let i = index"
+                                [formGroupName]="i"
+                            >
+                                <td class="align-middle">
+                                    <vdr-entity-info [entity]="option.value"></vdr-entity-info>
+                                </td>
+                                <td class="align-middle">
+                                    <input
+                                        type="text"
+                                        formControlName="name"
+                                        [readonly]="!(updatePermission | hasPermission)"
+                                    />
+                                </td>
+                                <td class="align-middle"><input type="text" formControlName="code" /></td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </section>
+            </section>
+        </div>
+    </div>
+</form>

+ 4 - 0
packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.scss

@@ -0,0 +1,4 @@
+.option-group-header {
+    display: flex;
+    align-items: baseline;
+}

+ 219 - 0
packages/admin-ui/src/lib/catalog/src/components/product-options-editor/product-options-editor.component.ts

@@ -0,0 +1,219 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
+import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+import {
+    BaseDetailComponent,
+    CreateFacetInput,
+    createUpdatedTranslatable,
+    CustomFieldConfig,
+    DataService,
+    FacetWithValues,
+    findTranslation,
+    GetProductVariantOptions,
+    LanguageCode,
+    NotificationService,
+    Permission,
+    ProductOption,
+    ProductOptionGroup,
+    ServerConfigService,
+    UpdateFacetInput,
+    UpdateProductOptionGroupInput,
+    UpdateProductOptionInput,
+} from '@vendure/admin-ui/core';
+import { combineLatest, forkJoin, Observable } from 'rxjs';
+import { map, mergeMap, take } from 'rxjs/operators';
+
+import { ProductDetailService } from '../../providers/product-detail/product-detail.service';
+
+@Component({
+    selector: 'vdr-product-options-editor',
+    templateUrl: './product-options-editor.component.html',
+    styleUrls: ['./product-options-editor.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ProductOptionsEditorComponent
+    extends BaseDetailComponent<GetProductVariantOptions.Product>
+    implements OnInit
+{
+    detailForm: FormGroup;
+    optionGroups$: Observable<GetProductVariantOptions.OptionGroups[]>;
+    languageCode$: Observable<LanguageCode>;
+    availableLanguages$: Observable<LanguageCode[]>;
+    optionGroupCustomFields: CustomFieldConfig[];
+    optionCustomFields: CustomFieldConfig[];
+    autoUpdateVariantNames = true;
+    readonly updatePermission = [Permission.UpdateCatalog, Permission.UpdateProduct];
+
+    constructor(
+        protected route: ActivatedRoute,
+        protected router: Router,
+        protected serverConfigService: ServerConfigService,
+        protected dataService: DataService,
+        private productDetailService: ProductDetailService,
+        private formBuilder: FormBuilder,
+        private changeDetector: ChangeDetectorRef,
+        private notificationService: NotificationService,
+    ) {
+        super(route, router, serverConfigService, dataService);
+        this.optionGroupCustomFields = this.getCustomFieldConfig('ProductOptionGroup');
+        this.optionCustomFields = this.getCustomFieldConfig('ProductOption');
+    }
+
+    ngOnInit(): void {
+        this.optionGroups$ = this.route.snapshot.data.entity.pipe(
+            map((product: GetProductVariantOptions.Product) => product.optionGroups),
+        );
+        this.detailForm = new FormGroup({
+            optionGroups: new FormArray([]),
+        });
+        super.init();
+    }
+
+    getOptionGroups(): FormGroup[] {
+        const optionGroups = this.detailForm.get('optionGroups');
+        return (optionGroups as FormArray).controls as FormGroup[];
+    }
+
+    getOptions(optionGroup: FormGroup): FormGroup[] {
+        const options = optionGroup.get('options');
+        return (options as FormArray).controls as FormGroup[];
+    }
+
+    save() {
+        if (this.detailForm.invalid || this.detailForm.pristine) {
+            return;
+        }
+        // tslint:disable-next-line:no-non-null-assertion
+        const $product = this.dataService.product.getProduct(this.id).mapSingle(data => data.product!);
+        combineLatest(this.entity$, this.languageCode$, $product)
+            .pipe(
+                take(1),
+                mergeMap(([{ optionGroups }, languageCode, product]) => {
+                    const updateOperations: Array<Observable<any>> = [];
+                    for (const optionGroupForm of this.getOptionGroups()) {
+                        if (optionGroupForm.get('name')?.dirty || optionGroupForm.get('code')?.dirty) {
+                            const optionGroupEntity = optionGroups.find(
+                                og => og.id === optionGroupForm.value.id,
+                            );
+                            if (optionGroupEntity) {
+                                const input = this.getUpdatedOptionGroup(
+                                    optionGroupEntity,
+                                    optionGroupForm,
+                                    languageCode,
+                                );
+                                updateOperations.push(
+                                    this.dataService.product.updateProductOptionGroup(input),
+                                );
+                            }
+                        }
+
+                        for (const optionForm of this.getOptions(optionGroupForm)) {
+                            if (optionForm.get('name')?.dirty || optionForm.get('code')?.dirty) {
+                                const optionGroup = optionGroups
+                                    .find(og => og.id === optionGroupForm.value.id)
+                                    ?.options.find(o => o.id === optionForm.value.id);
+                                if (optionGroup) {
+                                    const input = this.getUpdatedOption(
+                                        optionGroup,
+                                        optionForm,
+                                        languageCode,
+                                    );
+                                    updateOperations.push(
+                                        this.productDetailService.updateProductOption(
+                                            { ...input, autoUpdate: this.autoUpdateVariantNames },
+                                            product,
+                                            languageCode,
+                                        ),
+                                    );
+                                }
+                            }
+                        }
+                    }
+                    return forkJoin(updateOperations);
+                }),
+            )
+            .subscribe(
+                () => {
+                    this.detailForm.markAsPristine();
+                    this.changeDetector.markForCheck();
+                    this.notificationService.success(_('common.notify-update-success'), {
+                        entity: 'ProductOptionGroup',
+                    });
+                },
+                err => {
+                    this.notificationService.error(_('common.notify-update-error'), {
+                        entity: 'ProductOptionGroup',
+                    });
+                },
+            );
+    }
+
+    private getUpdatedOptionGroup(
+        optionGroup: ProductOptionGroup.Fragment,
+        optionGroupFormGroup: FormGroup,
+        languageCode: LanguageCode,
+    ): UpdateProductOptionGroupInput {
+        const input = createUpdatedTranslatable({
+            translatable: optionGroup,
+            updatedFields: optionGroupFormGroup.value,
+            customFieldConfig: this.optionGroupCustomFields,
+            languageCode,
+            defaultTranslation: {
+                languageCode,
+                name: optionGroup.name || '',
+            },
+        });
+        return input;
+    }
+
+    private getUpdatedOption(
+        option: ProductOption.Fragment,
+        optionFormGroup: FormGroup,
+        languageCode: LanguageCode,
+    ): UpdateProductOptionInput {
+        const input = createUpdatedTranslatable({
+            translatable: option,
+            updatedFields: optionFormGroup.value,
+            customFieldConfig: this.optionGroupCustomFields,
+            languageCode,
+            defaultTranslation: {
+                languageCode,
+                name: option.name || '',
+            },
+        });
+        return input;
+    }
+
+    protected setFormValues(entity: GetProductVariantOptions.Product, languageCode: LanguageCode): void {
+        const groupsFormArray = new FormArray([]);
+        for (const optionGroup of entity.optionGroups) {
+            const groupTranslation = findTranslation(optionGroup, languageCode);
+            const group = {
+                id: optionGroup.id,
+                createdAt: optionGroup.createdAt,
+                updatedAt: optionGroup.updatedAt,
+                code: optionGroup.code,
+                name: groupTranslation ? groupTranslation.name : '',
+            };
+            const optionsFormArray = new FormArray([]);
+
+            for (const option of optionGroup.options) {
+                const optionTranslation = findTranslation(option, languageCode);
+                const optionControl = this.formBuilder.group({
+                    id: option.id,
+                    createdAt: option.createdAt,
+                    updatedAt: option.updatedAt,
+                    code: option.code,
+                    name: optionTranslation ? optionTranslation.name : '',
+                });
+                optionsFormArray.push(optionControl);
+            }
+
+            const groupControl = this.formBuilder.group(group);
+            groupControl.addControl('options', optionsFormArray);
+            groupsFormArray.push(groupControl);
+        }
+        this.detailForm.setControl('optionGroups', groupsFormArray);
+    }
+}

+ 1 - 0
packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.html

@@ -241,6 +241,7 @@
                                 <span class="option-group-name">{{ optionGroupName(option.groupId) }}</span>
                                 {{ optionName(option) }}
                             </vdr-chip>
+                            <a [routerLink]="['./', 'options']" class="btn btn-link btn-sm">{{ 'catalog.edit-options' | translate }}...</a>
                         </div>
                     </div>
                     <div class="flex-spacer"></div>

+ 1 - 0
packages/admin-ui/src/lib/catalog/src/public_api.ts

@@ -19,6 +19,7 @@ export * from './components/option-value-input/option-value-input.component';
 export * from './components/product-assets/product-assets.component';
 export * from './components/product-detail/product-detail.component';
 export * from './components/product-list/product-list.component';
+export * from './components/product-options-editor/product-options-editor.component';
 export * from './components/product-search-input/product-search-input.component';
 export * from './components/product-variants-editor/product-variants-editor.component';
 export * from './components/product-variants-list/product-variants-list.component';

File diff suppressed because it is too large
+ 543 - 3130
packages/admin-ui/src/lib/core/src/common/generated-types.ts


+ 189 - 256
packages/admin-ui/src/lib/core/src/common/introspection-result.ts

@@ -1,259 +1,192 @@
 // tslint:disable
 
-      export interface PossibleTypesResultData {
-        possibleTypes: {
-          [key: string]: string[]
-        }
-      }
-      const result: PossibleTypesResultData = {
-  "possibleTypes": {
-    "CreateAssetResult": [
-      "Asset",
-      "MimeTypeError"
-    ],
-    "NativeAuthenticationResult": [
-      "CurrentUser",
-      "InvalidCredentialsError",
-      "NativeAuthStrategyError"
-    ],
-    "AuthenticationResult": [
-      "CurrentUser",
-      "InvalidCredentialsError"
-    ],
-    "CreateChannelResult": [
-      "Channel",
-      "LanguageNotAvailableError"
-    ],
-    "UpdateChannelResult": [
-      "Channel",
-      "LanguageNotAvailableError"
-    ],
-    "CreateCustomerResult": [
-      "Customer",
-      "EmailAddressConflictError"
-    ],
-    "UpdateCustomerResult": [
-      "Customer",
-      "EmailAddressConflictError"
-    ],
-    "UpdateGlobalSettingsResult": [
-      "GlobalSettings",
-      "ChannelDefaultLanguageError"
-    ],
-    "TransitionOrderToStateResult": [
-      "Order",
-      "OrderStateTransitionError"
-    ],
-    "SettlePaymentResult": [
-      "Payment",
-      "SettlePaymentError",
-      "PaymentStateTransitionError",
-      "OrderStateTransitionError"
-    ],
-    "AddFulfillmentToOrderResult": [
-      "Fulfillment",
-      "EmptyOrderLineSelectionError",
-      "ItemsAlreadyFulfilledError",
-      "InsufficientStockOnHandError",
-      "InvalidFulfillmentHandlerError",
-      "FulfillmentStateTransitionError",
-      "CreateFulfillmentError"
-    ],
-    "CancelOrderResult": [
-      "Order",
-      "EmptyOrderLineSelectionError",
-      "QuantityTooGreatError",
-      "MultipleOrderError",
-      "CancelActiveOrderError",
-      "OrderStateTransitionError"
-    ],
-    "RefundOrderResult": [
-      "Refund",
-      "QuantityTooGreatError",
-      "NothingToRefundError",
-      "OrderStateTransitionError",
-      "MultipleOrderError",
-      "PaymentOrderMismatchError",
-      "RefundOrderStateError",
-      "AlreadyRefundedError",
-      "RefundStateTransitionError"
-    ],
-    "SettleRefundResult": [
-      "Refund",
-      "RefundStateTransitionError"
-    ],
-    "TransitionFulfillmentToStateResult": [
-      "Fulfillment",
-      "FulfillmentStateTransitionError"
-    ],
-    "TransitionPaymentToStateResult": [
-      "Payment",
-      "PaymentStateTransitionError"
-    ],
-    "ModifyOrderResult": [
-      "Order",
-      "NoChangesSpecifiedError",
-      "OrderModificationStateError",
-      "PaymentMethodMissingError",
-      "RefundPaymentIdMissingError",
-      "OrderLimitError",
-      "NegativeQuantityError",
-      "InsufficientStockError"
-    ],
-    "AddManualPaymentToOrderResult": [
-      "Order",
-      "ManualPaymentStateError"
-    ],
-    "RemoveOptionGroupFromProductResult": [
-      "Product",
-      "ProductOptionInUseError"
-    ],
-    "CreatePromotionResult": [
-      "Promotion",
-      "MissingConditionsError"
-    ],
-    "UpdatePromotionResult": [
-      "Promotion",
-      "MissingConditionsError"
-    ],
-    "StockMovement": [
-      "StockAdjustment",
-      "Allocation",
-      "Sale",
-      "Cancellation",
-      "Return",
-      "Release"
-    ],
-    "StockMovementItem": [
-      "StockAdjustment",
-      "Allocation",
-      "Sale",
-      "Cancellation",
-      "Return",
-      "Release"
-    ],
-    "PaginatedList": [
-      "AdministratorList",
-      "CustomerGroupList",
-      "JobList",
-      "PaymentMethodList",
-      "AssetList",
-      "CollectionList",
-      "ProductVariantList",
-      "CountryList",
-      "CustomerList",
-      "FacetList",
-      "HistoryEntryList",
-      "OrderList",
-      "ProductList",
-      "PromotionList",
-      "RoleList",
-      "ShippingMethodList",
-      "TagList",
-      "TaxRateList"
-    ],
-    "Node": [
-      "Administrator",
-      "Asset",
-      "Collection",
-      "Customer",
-      "Facet",
-      "HistoryEntry",
-      "Job",
-      "Order",
-      "Fulfillment",
-      "Payment",
-      "OrderModification",
-      "PaymentMethod",
-      "Product",
-      "ProductVariant",
-      "StockAdjustment",
-      "Allocation",
-      "Sale",
-      "Cancellation",
-      "Return",
-      "Release",
-      "Address",
-      "Channel",
-      "Country",
-      "CustomerGroup",
-      "FacetValue",
-      "OrderItem",
-      "OrderLine",
-      "Refund",
-      "Surcharge",
-      "ProductOptionGroup",
-      "ProductOption",
-      "Promotion",
-      "Role",
-      "ShippingMethod",
-      "Tag",
-      "TaxCategory",
-      "TaxRate",
-      "User",
-      "AuthenticationMethod",
-      "Zone"
-    ],
-    "ErrorResult": [
-      "MimeTypeError",
-      "LanguageNotAvailableError",
-      "ChannelDefaultLanguageError",
-      "SettlePaymentError",
-      "EmptyOrderLineSelectionError",
-      "ItemsAlreadyFulfilledError",
-      "InvalidFulfillmentHandlerError",
-      "CreateFulfillmentError",
-      "InsufficientStockOnHandError",
-      "MultipleOrderError",
-      "CancelActiveOrderError",
-      "PaymentOrderMismatchError",
-      "RefundOrderStateError",
-      "NothingToRefundError",
-      "AlreadyRefundedError",
-      "QuantityTooGreatError",
-      "RefundStateTransitionError",
-      "PaymentStateTransitionError",
-      "FulfillmentStateTransitionError",
-      "OrderModificationStateError",
-      "NoChangesSpecifiedError",
-      "PaymentMethodMissingError",
-      "RefundPaymentIdMissingError",
-      "ManualPaymentStateError",
-      "ProductOptionInUseError",
-      "MissingConditionsError",
-      "NativeAuthStrategyError",
-      "InvalidCredentialsError",
-      "OrderStateTransitionError",
-      "EmailAddressConflictError",
-      "OrderLimitError",
-      "NegativeQuantityError",
-      "InsufficientStockError"
-    ],
-    "CustomField": [
-      "StringCustomFieldConfig",
-      "LocaleStringCustomFieldConfig",
-      "IntCustomFieldConfig",
-      "FloatCustomFieldConfig",
-      "BooleanCustomFieldConfig",
-      "DateTimeCustomFieldConfig",
-      "RelationCustomFieldConfig",
-      "TextCustomFieldConfig"
-    ],
-    "CustomFieldConfig": [
-      "StringCustomFieldConfig",
-      "LocaleStringCustomFieldConfig",
-      "IntCustomFieldConfig",
-      "FloatCustomFieldConfig",
-      "BooleanCustomFieldConfig",
-      "DateTimeCustomFieldConfig",
-      "RelationCustomFieldConfig",
-      "TextCustomFieldConfig"
-    ],
-    "SearchResultPrice": [
-      "PriceRange",
-      "SinglePrice"
-    ]
-  }
+export interface PossibleTypesResultData {
+    possibleTypes: {
+        [key: string]: string[];
+    };
+}
+const result: PossibleTypesResultData = {
+    possibleTypes: {
+        AddFulfillmentToOrderResult: [
+            'Fulfillment',
+            'EmptyOrderLineSelectionError',
+            'ItemsAlreadyFulfilledError',
+            'InsufficientStockOnHandError',
+            'InvalidFulfillmentHandlerError',
+            'FulfillmentStateTransitionError',
+            'CreateFulfillmentError',
+        ],
+        AddManualPaymentToOrderResult: ['Order', 'ManualPaymentStateError'],
+        AuthenticationResult: ['CurrentUser', 'InvalidCredentialsError'],
+        CancelOrderResult: [
+            'Order',
+            'EmptyOrderLineSelectionError',
+            'QuantityTooGreatError',
+            'MultipleOrderError',
+            'CancelActiveOrderError',
+            'OrderStateTransitionError',
+        ],
+        CreateAssetResult: ['Asset', 'MimeTypeError'],
+        CreateChannelResult: ['Channel', 'LanguageNotAvailableError'],
+        CreateCustomerResult: ['Customer', 'EmailAddressConflictError'],
+        CreatePromotionResult: ['Promotion', 'MissingConditionsError'],
+        CustomField: [
+            'BooleanCustomFieldConfig',
+            'DateTimeCustomFieldConfig',
+            'FloatCustomFieldConfig',
+            'IntCustomFieldConfig',
+            'LocaleStringCustomFieldConfig',
+            'RelationCustomFieldConfig',
+            'StringCustomFieldConfig',
+            'TextCustomFieldConfig',
+        ],
+        CustomFieldConfig: [
+            'StringCustomFieldConfig',
+            'LocaleStringCustomFieldConfig',
+            'IntCustomFieldConfig',
+            'FloatCustomFieldConfig',
+            'BooleanCustomFieldConfig',
+            'DateTimeCustomFieldConfig',
+            'RelationCustomFieldConfig',
+            'TextCustomFieldConfig',
+        ],
+        ErrorResult: [
+            'AlreadyRefundedError',
+            'CancelActiveOrderError',
+            'ChannelDefaultLanguageError',
+            'CreateFulfillmentError',
+            'EmailAddressConflictError',
+            'EmptyOrderLineSelectionError',
+            'FulfillmentStateTransitionError',
+            'InsufficientStockError',
+            'InsufficientStockOnHandError',
+            'InvalidCredentialsError',
+            'InvalidFulfillmentHandlerError',
+            'ItemsAlreadyFulfilledError',
+            'LanguageNotAvailableError',
+            'ManualPaymentStateError',
+            'MimeTypeError',
+            'MissingConditionsError',
+            'MultipleOrderError',
+            'NativeAuthStrategyError',
+            'NegativeQuantityError',
+            'NoChangesSpecifiedError',
+            'NothingToRefundError',
+            'OrderLimitError',
+            'OrderModificationStateError',
+            'OrderStateTransitionError',
+            'PaymentMethodMissingError',
+            'PaymentOrderMismatchError',
+            'PaymentStateTransitionError',
+            'ProductOptionInUseError',
+            'QuantityTooGreatError',
+            'RefundOrderStateError',
+            'RefundPaymentIdMissingError',
+            'RefundStateTransitionError',
+            'SettlePaymentError',
+        ],
+        ModifyOrderResult: [
+            'Order',
+            'NoChangesSpecifiedError',
+            'OrderModificationStateError',
+            'PaymentMethodMissingError',
+            'RefundPaymentIdMissingError',
+            'OrderLimitError',
+            'NegativeQuantityError',
+            'InsufficientStockError',
+        ],
+        NativeAuthenticationResult: ['CurrentUser', 'InvalidCredentialsError', 'NativeAuthStrategyError'],
+        Node: [
+            'Address',
+            'Administrator',
+            'Allocation',
+            'Asset',
+            'AuthenticationMethod',
+            'Cancellation',
+            'Channel',
+            'Collection',
+            'Country',
+            'Customer',
+            'CustomerGroup',
+            'Facet',
+            'FacetValue',
+            'Fulfillment',
+            'HistoryEntry',
+            'Job',
+            'Order',
+            'OrderItem',
+            'OrderLine',
+            'OrderModification',
+            'Payment',
+            'PaymentMethod',
+            'Product',
+            'ProductOption',
+            'ProductOptionGroup',
+            'ProductVariant',
+            'Promotion',
+            'Refund',
+            'Release',
+            'Return',
+            'Role',
+            'Sale',
+            'ShippingMethod',
+            'StockAdjustment',
+            'Surcharge',
+            'Tag',
+            'TaxCategory',
+            'TaxRate',
+            'User',
+            'Zone',
+        ],
+        PaginatedList: [
+            'AdministratorList',
+            'AssetList',
+            'CollectionList',
+            'CountryList',
+            'CustomerGroupList',
+            'CustomerList',
+            'FacetList',
+            'HistoryEntryList',
+            'JobList',
+            'OrderList',
+            'PaymentMethodList',
+            'ProductList',
+            'ProductVariantList',
+            'PromotionList',
+            'RoleList',
+            'ShippingMethodList',
+            'TagList',
+            'TaxRateList',
+        ],
+        RefundOrderResult: [
+            'Refund',
+            'QuantityTooGreatError',
+            'NothingToRefundError',
+            'OrderStateTransitionError',
+            'MultipleOrderError',
+            'PaymentOrderMismatchError',
+            'RefundOrderStateError',
+            'AlreadyRefundedError',
+            'RefundStateTransitionError',
+        ],
+        RemoveOptionGroupFromProductResult: ['Product', 'ProductOptionInUseError'],
+        SearchResultPrice: ['PriceRange', 'SinglePrice'],
+        SettlePaymentResult: [
+            'Payment',
+            'SettlePaymentError',
+            'PaymentStateTransitionError',
+            'OrderStateTransitionError',
+        ],
+        SettleRefundResult: ['Refund', 'RefundStateTransitionError'],
+        StockMovement: ['Allocation', 'Cancellation', 'Release', 'Return', 'Sale', 'StockAdjustment'],
+        StockMovementItem: ['StockAdjustment', 'Allocation', 'Sale', 'Cancellation', 'Return', 'Release'],
+        TransitionFulfillmentToStateResult: ['Fulfillment', 'FulfillmentStateTransitionError'],
+        TransitionOrderToStateResult: ['Order', 'OrderStateTransitionError'],
+        TransitionPaymentToStateResult: ['Payment', 'PaymentStateTransitionError'],
+        UpdateChannelResult: ['Channel', 'LanguageNotAvailableError'],
+        UpdateCustomerResult: ['Customer', 'EmailAddressConflictError'],
+        UpdateGlobalSettingsResult: ['GlobalSettings', 'ChannelDefaultLanguageError'],
+        UpdatePromotionResult: ['Promotion', 'MissingConditionsError'],
+    },
 };
-      export default result;
-    
+export default result;

+ 15 - 3
packages/admin-ui/src/lib/core/src/data/definitions/product-definitions.ts

@@ -32,6 +32,8 @@ export const TAG_FRAGMENT = gql`
 export const PRODUCT_OPTION_GROUP_FRAGMENT = gql`
     fragment ProductOptionGroup on ProductOptionGroup {
         id
+        createdAt
+        updatedAt
         code
         languageCode
         name
@@ -46,6 +48,8 @@ export const PRODUCT_OPTION_GROUP_FRAGMENT = gql`
 export const PRODUCT_OPTION_FRAGMENT = gql`
     fragment ProductOption on ProductOption {
         id
+        createdAt
+        updatedAt
         code
         languageCode
         name
@@ -525,6 +529,15 @@ export const PRODUCT_SELECTOR_SEARCH = gql`
     }
 `;
 
+export const UPDATE_PRODUCT_OPTION_GROUP = gql`
+    mutation UpdateProductOptionGroup($input: UpdateProductOptionGroupInput!) {
+        updateProductOptionGroup(input: $input) {
+            ...ProductOptionGroup
+        }
+    }
+    ${PRODUCT_OPTION_GROUP_FRAGMENT}
+`;
+
 export const UPDATE_PRODUCT_OPTION = gql`
     mutation UpdateProductOption($input: UpdateProductOptionInput!) {
         updateProductOption(input: $input) {
@@ -551,9 +564,7 @@ export const GET_PRODUCT_VARIANT_OPTIONS = gql`
             updatedAt
             name
             optionGroups {
-                id
-                name
-                code
+                ...ProductOptionGroup
                 options {
                     ...ProductOption
                 }
@@ -579,6 +590,7 @@ export const GET_PRODUCT_VARIANT_OPTIONS = gql`
             }
         }
     }
+    ${PRODUCT_OPTION_GROUP_FRAGMENT}
     ${PRODUCT_OPTION_FRAGMENT}
 `;
 

+ 12 - 0
packages/admin-ui/src/lib/core/src/data/providers/product-data.service.ts

@@ -50,6 +50,8 @@ import {
     UpdateProduct,
     UpdateProductInput,
     UpdateProductOption,
+    UpdateProductOptionGroup,
+    UpdateProductOptionGroupInput,
     UpdateProductOptionInput,
     UpdateProductVariantInput,
     UpdateProductVariants,
@@ -90,6 +92,7 @@ import {
     UPDATE_ASSET,
     UPDATE_PRODUCT,
     UPDATE_PRODUCT_OPTION,
+    UPDATE_PRODUCT_OPTION_GROUP,
     UPDATE_PRODUCT_VARIANTS,
     UPDATE_TAG,
 } from '../definitions/product-definitions';
@@ -307,6 +310,15 @@ export class ProductDataService {
         );
     }
 
+    updateProductOptionGroup(input: UpdateProductOptionGroupInput) {
+        return this.baseDataService.mutate<
+            UpdateProductOptionGroup.Mutation,
+            UpdateProductOptionGroup.Variables
+        >(UPDATE_PRODUCT_OPTION_GROUP, {
+            input: pick(input, ['id', 'code', 'translations', 'customFields']),
+        });
+    }
+
     getProductOptionGroups(filterTerm?: string) {
         return this.baseDataService.query<GetProductOptionGroups.Query, GetProductOptionGroups.Variables>(
             GET_PRODUCT_OPTION_GROUPS,

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/cs.json

@@ -40,6 +40,7 @@
     "modifying": "Úpravy",
     "orders": "Objednávky",
     "payment-methods": "Platební metody",
+    "product-options": "",
     "products": "Produkty",
     "profile": "Profil",
     "promotions": "Propagace",
@@ -89,6 +90,7 @@
     "display-variant-table": "Zobrazit jako tabulku",
     "drop-files-to-upload": "Přetáhněte soubory k nahrávání",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "Rozevřít všechny kolekce",
     "facet-values": "Hodnoty atributů",
     "filter-by-name": "Filtrovat dle jména",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/de.json

@@ -40,6 +40,7 @@
     "modifying": "Bearbeiten",
     "orders": "Bestellungen",
     "payment-methods": "Zahlungsarten",
+    "product-options": "",
     "products": "Produkte",
     "profile": "Profil",
     "promotions": "Promotionen",
@@ -89,6 +90,7 @@
     "display-variant-table": "Tabellenansicht",
     "drop-files-to-upload": "Dateien zum Hochladen ablegen",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "Alle Sammlungen erweitern",
     "facet-values": "Facettenwerte",
     "filter-by-name": "Nach Name filtern",

+ 3 - 1
packages/admin-ui/src/lib/static/i18n-messages/en.json

@@ -40,6 +40,7 @@
     "modifying": "Modifying",
     "orders": "Orders",
     "payment-methods": "Payment methods",
+    "product-options": "Product options",
     "products": "Products",
     "profile": "Profile",
     "promotions": "Promotions",
@@ -89,6 +90,7 @@
     "display-variant-table": "View as table",
     "drop-files-to-upload": "Drop files to upload",
     "duplicate-sku-warning": "Please ensure all SKUs are unique",
+    "edit-options": "Edit options",
     "expand-all-collections": "Expand all collections",
     "facet-values": "Facet values",
     "filter-by-name": "Filter by name",
@@ -652,4 +654,4 @@
     "job-result": "Job result",
     "job-state": "Job state"
   }
-}
+}

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/es.json

@@ -40,6 +40,7 @@
     "modifying": "",
     "orders": "Pedidos",
     "payment-methods": "Métodos de pago",
+    "product-options": "",
     "products": "Productos",
     "profile": "",
     "promotions": "Promociones",
@@ -89,6 +90,7 @@
     "display-variant-table": "Ver como tabla",
     "drop-files-to-upload": "Arrastra archivos para subirlos",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "Expandir todas las colecciones",
     "facet-values": "Valores de faceta",
     "filter-by-name": "Filtrar por nombre",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/fr.json

@@ -40,6 +40,7 @@
     "modifying": "Modification",
     "orders": "Commandes",
     "payment-methods": "Modes de paiement",
+    "product-options": "",
     "products": "Produits",
     "profile": "Profil",
     "promotions": "Promotions",
@@ -89,6 +90,7 @@
     "display-variant-table": "Voir en tant que tableau",
     "drop-files-to-upload": "Déposer des fichiers pour téléverser",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "Etendre toutes les collections",
     "facet-values": "Valeurs de composant",
     "filter-by-name": "Filtrer par nom",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/it.json

@@ -40,6 +40,7 @@
     "modifying": "Modifica",
     "orders": "Ordini",
     "payment-methods": "Metodi di pagamento",
+    "product-options": "",
     "products": "Prodotti",
     "profile": "Profilo",
     "promotions": "Promozioni",
@@ -89,6 +90,7 @@
     "display-variant-table": "Visualizza come tabella",
     "drop-files-to-upload": "Trascina file da caricare",
     "duplicate-sku-warning": "Per favore assicurati che tutte le SKU siano univoche",
+    "edit-options": "",
     "expand-all-collections": "Espandi le collezioni",
     "facet-values": "Valori attributo",
     "filter-by-name": "Filtra per nome",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/pl.json

@@ -40,6 +40,7 @@
     "modifying": "",
     "orders": "Zamówienia",
     "payment-methods": "Metody płatności",
+    "product-options": "",
     "products": "Produkty",
     "profile": "",
     "promotions": "Promocje",
@@ -89,6 +90,7 @@
     "display-variant-table": "Wyświetl jako tabele",
     "drop-files-to-upload": "Upuść pliki do uploadu",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "Rozwiń wszystkie kolekcje",
     "facet-values": "Wartości faseta",
     "filter-by-name": "Filtruj po nazwie",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json

@@ -40,6 +40,7 @@
     "modifying": "Modificando",
     "orders": "Pedidos",
     "payment-methods": "Métodos de pagamentos",
+    "product-options": "",
     "products": "Produtos",
     "profile": "Perfil",
     "promotions": "Promoções",
@@ -89,6 +90,7 @@
     "display-variant-table": "Ver como tabela",
     "drop-files-to-upload": "Soltar arquivos para envio",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "Expandir todas as categorias",
     "facet-values": "Valor da Etiqueta",
     "filter-by-name": "Filtrar por nome",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/ru.json

@@ -40,6 +40,7 @@
     "modifying": "Изменение",
     "orders": "Заказы",
     "payment-methods": "Способы оплаты",
+    "product-options": "",
     "products": "Товары",
     "profile": "Профиль",
     "promotions": "Акции",
@@ -89,6 +90,7 @@
     "display-variant-table": "Просмотр в виде таблицы",
     "drop-files-to-upload": "Перетащите файлы для загрузки",
     "duplicate-sku-warning": "Убедитесь, что все артикулы (SKU) уникальны",
+    "edit-options": "",
     "expand-all-collections": "Развернуть все коллекции",
     "facet-values": "Значения тега",
     "filter-by-name": "Фильтр по имени",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/uk.json

@@ -40,6 +40,7 @@
     "modifying": "Зміна",
     "orders": "Замовлення",
     "payment-methods": "Способи оплати",
+    "product-options": "",
     "products": "Товари",
     "profile": "Профіль",
     "promotions": "Акції",
@@ -89,6 +90,7 @@
     "display-variant-table": "Перегляд у вигляді таблиці",
     "drop-files-to-upload": "Перетягніть файли для завантаження",
     "duplicate-sku-warning": "Переконайтесь, що всі артикули (SKU) унікальні",
+    "edit-options": "",
     "expand-all-collections": "Розгорнути всі колекції",
     "facet-values": "Значення тегу",
     "filter-by-name": "Фільтр по імені",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json

@@ -40,6 +40,7 @@
     "modifying": "正在修改",
     "orders": "订单管理",
     "payment-methods": "支付管理",
+    "product-options": "",
     "products": "商品列表",
     "profile": "个人资料",
     "promotions": "优惠券管理",
@@ -89,6 +90,7 @@
     "display-variant-table": "表格显示",
     "drop-files-to-upload": "拖拽文件上传",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "展开所有系列",
     "facet-values": "特征值列表",
     "filter-by-name": "按名字过滤",

+ 2 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

@@ -40,6 +40,7 @@
     "modifying": "",
     "orders": "訂單管理",
     "payment-methods": "支付方式",
+    "product-options": "",
     "products": "商品",
     "profile": "",
     "promotions": "優惠",
@@ -89,6 +90,7 @@
     "display-variant-table": "表格顯示",
     "drop-files-to-upload": "拖拽文件上傳",
     "duplicate-sku-warning": "",
+    "edit-options": "",
     "expand-all-collections": "展開所有系列",
     "facet-values": "特徵值列表",
     "filter-by-name": "按名字篩選",

Some files were not shown because too many files changed in this diff