Forráskód Böngészése

fix(admin-ui): Add custom field controls to ProductOption dialog

Fixes #382
Michael Bromley 5 éve
szülő
commit
4678360d51
20 módosított fájl, 420 hozzáadás és 127 törlés
  1. 2 0
      packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.html
  2. 6 1
      packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.ts
  3. 8 8
      packages/admin-ui/src/lib/catalog/src/components/product-variants-editor/product-variants-editor.component.ts
  4. 3 3
      packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.html
  5. 33 16
      packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.ts
  6. 13 2
      packages/admin-ui/src/lib/catalog/src/components/update-product-option-dialog/update-product-option-dialog.component.html
  7. 36 4
      packages/admin-ui/src/lib/catalog/src/components/update-product-option-dialog/update-product-option-dialog.component.ts
  8. 13 4
      packages/admin-ui/src/lib/core/src/common/base-detail.component.ts
  9. 79 30
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  10. 43 30
      packages/admin-ui/src/lib/core/src/data/definitions/product-definitions.ts
  11. 1 1
      packages/admin-ui/src/lib/core/src/data/providers/product-data.service.ts
  12. 32 6
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  13. 12 2
      packages/common/src/generated-shop-types.ts
  14. 32 6
      packages/common/src/generated-types.ts
  15. 32 6
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  16. 12 2
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  17. 31 0
      packages/dev-server/dev-config.ts
  18. 32 6
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  19. 0 0
      schema-admin.json
  20. 0 0
      schema-shop.json

+ 2 - 0
packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.html

@@ -208,6 +208,8 @@
                         [productVariantsFormArray]="detailForm.get('variants')"
                         [taxCategories]="taxCategories$ | async"
                         [customFields]="customVariantFields"
+                        [customOptionFields]="customOptionFields"
+                        [activeLanguage]="languageCode$ | async"
                         (assetChange)="variantAssetChange($event)"
                         (updateProductOption)="updateProductOption($event)"
                         (selectionChange)="selectedVariantIds = $event"

+ 6 - 1
packages/admin-ui/src/lib/catalog/src/components/product-detail/product-detail.component.ts

@@ -76,6 +76,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
     taxCategories$: Observable<TaxCategory.Fragment[]>;
     customFields: CustomFieldConfig[];
     customVariantFields: CustomFieldConfig[];
+    customOptionGroupFields: CustomFieldConfig[];
+    customOptionFields: CustomFieldConfig[];
     detailForm: FormGroup;
     assetChanges: SelectedAssets = {};
     variantAssetChanges: { [variantId: string]: SelectedAssets } = {};
@@ -101,6 +103,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
         super(route, router, serverConfigService, dataService);
         this.customFields = this.getCustomFieldConfig('Product');
         this.customVariantFields = this.getCustomFieldConfig('ProductVariant');
+        this.customOptionGroupFields = this.getCustomFieldConfig('ProductOptionGroup');
+        this.customOptionFields = this.getCustomFieldConfig('ProductOption');
         this.detailForm = this.formBuilder.group({
             product: this.formBuilder.group({
                 enabled: true,
@@ -156,7 +160,8 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
     }
 
     navigateToTab(tabName: TabName) {
-        this.router.navigate(['./', { tab: tabName }], {
+        this.router.navigate(['./', { ...this.route.snapshot.params, tab: tabName }], {
+            queryParamsHandling: 'merge',
             relativeTo: this.route,
             state: {
                 [IGNORE_CAN_DEACTIVATE_GUARD]: true,

+ 8 - 8
packages/admin-ui/src/lib/catalog/src/components/product-variants-editor/product-variants-editor.component.ts

@@ -1,19 +1,19 @@
 import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
-import { DeactivateAware } from '@vendure/admin-ui/core';
-import { NotificationService } from '@vendure/admin-ui/core';
-import { ModalService } from '@vendure/admin-ui/core';
-import { getDefaultUiLanguage } from '@vendure/admin-ui/core';
 import {
     CreateProductOptionGroup,
     CreateProductOptionInput,
     CurrencyCode,
+    DataService,
+    DeactivateAware,
+    getDefaultUiLanguage,
     GetProductVariantOptions,
     LanguageCode,
-    ProductOptionGroupFragment,
+    ModalService,
+    NotificationService,
+    ProductOptionGroupWithOptionsFragment,
 } from '@vendure/admin-ui/core';
-import { DataService } from '@vendure/admin-ui/core';
 import { normalizeString } from '@vendure/common/lib/normalize-string';
 import { generateAllCombinations, notNullOrUndefined } from '@vendure/common/lib/shared-utils';
 import { EMPTY, forkJoin, Observable, of } from 'rxjs';
@@ -290,7 +290,7 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
         }
     }
 
-    private fetchOptionGroups(groupsIds: string[]): Observable<ProductOptionGroupFragment[]> {
+    private fetchOptionGroups(groupsIds: string[]): Observable<ProductOptionGroupWithOptionsFragment[]> {
         return forkJoin(
             groupsIds.map((id) =>
                 this.dataService.product
@@ -301,7 +301,7 @@ export class ProductVariantsEditorComponent implements OnInit, DeactivateAware {
         );
     }
 
-    private createNewProductVariants(groups: ProductOptionGroupFragment[]) {
+    private createNewProductVariants(groups: ProductOptionGroupWithOptionsFragment[]) {
         const options = groups
             .filter(notNullOrUndefined)
             .map((og) => og.options)

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

@@ -2,9 +2,9 @@
     <div
         class="variant-container card"
         *ngFor="let variant of variants; let i = index"
-        [class.disabled]="!formArray.get([i, 'enabled'])!.value"
+        [class.disabled]="!formArray.get([i, 'enabled'])?.value"
     >
-        <ng-container [formGroup]="formArray.at(i)">
+        <ng-container *ngIf="formArray.at(i)" [formGroup]="formArray.at(i)">
             <div class="card-block header-row">
                 <div class="details">
                     <vdr-title-input class="sku" [readonly]="!('UpdateCatalog' | hasPermission)">
@@ -145,7 +145,7 @@
                                 [icon]="('UpdateCatalog' | hasPermission) && 'pencil'"
                             >
                                 <span class="option-group-name">{{ optionGroupName(option.groupId) }}</span>
-                                {{ option.name }}
+                                {{ optionName(option) }}
                             </vdr-chip>
                         </div>
                     </div>

+ 33 - 16
packages/admin-ui/src/lib/catalog/src/components/product-variants-list/product-variants-list.component.ts

@@ -11,13 +11,12 @@ import {
     SimpleChanges,
 } from '@angular/core';
 import { FormArray } from '@angular/forms';
-import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
-import { Subscription } from 'rxjs';
-
 import {
     CustomFieldConfig,
     FacetValue,
     FacetWithValues,
+    LanguageCode,
+    ProductOptionFragment,
     ProductVariant,
     ProductWithVariants,
     TaxCategory,
@@ -25,6 +24,9 @@ import {
 } from '@vendure/admin-ui/core';
 import { flattenFacetValues } from '@vendure/admin-ui/core';
 import { ModalService } from '@vendure/admin-ui/core';
+import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
+import { Subscription } from 'rxjs';
+
 import { AssetChange } from '../product-assets/product-assets.component';
 import { VariantFormValue } from '../product-detail/product-detail.component';
 import { UpdateProductOptionDialogComponent } from '../update-product-option-dialog/update-product-option-dialog.component';
@@ -46,6 +48,8 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
     @Input() facets: FacetWithValues.Fragment[];
     @Input() optionGroups: ProductWithVariants.OptionGroups[];
     @Input() customFields: CustomFieldConfig[];
+    @Input() customOptionFields: CustomFieldConfig[];
+    @Input() activeLanguage: LanguageCode;
     @Output() assetChange = new EventEmitter<VariantAssetChange>();
     @Output() selectionChange = new EventEmitter<string[]>();
     @Output() selectFacetValueClick = new EventEmitter<string[]>();
@@ -77,7 +81,7 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
     getTaxCategoryName(index: number): string {
         const control = this.formArray.at(index).get(['taxCategoryId']);
         if (control && this.taxCategories) {
-            const match = this.taxCategories.find(t => t.id === control.value);
+            const match = this.taxCategories.find((t) => t.id === control.value);
             return match ? match.name : '';
         }
         return '';
@@ -92,7 +96,7 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
             variantId,
             ...event,
         });
-        const index = this.variants.findIndex(v => v.id === variantId);
+        const index = this.variants.findIndex((v) => v.id === variantId);
         this.formArray.at(index).markAsDirty();
     }
 
@@ -100,7 +104,7 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
         if (this.areAllSelected()) {
             this.selectedVariantIds = [];
         } else {
-            this.selectedVariantIds = this.variants.map(v => v.id);
+            this.selectedVariantIds = this.variants.map((v) => v.id);
         }
         this.selectionChange.emit(this.selectedVariantIds);
     }
@@ -116,17 +120,28 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
     }
 
     optionGroupName(optionGroupId: string): string | undefined {
-        const group = this.optionGroups.find(g => g.id === optionGroupId);
-        return group && group.name;
+        const group = this.optionGroups.find((g) => g.id === optionGroupId);
+        if (group) {
+            const translation =
+                group?.translations.find((t) => t.languageCode === this.activeLanguage) ??
+                group.translations[0];
+            return translation.name;
+        }
+    }
+
+    optionName(option: ProductOptionFragment) {
+        const translation =
+            option.translations.find((t) => t.languageCode === this.activeLanguage) ?? option.translations[0];
+        return translation.name;
     }
 
     pendingFacetValues(index: number) {
         if (this.facets) {
             const formFacetValueIds = this.getFacetValueIds(index);
-            const variantFacetValueIds = this.variants[index].facetValues.map(fv => fv.id);
+            const variantFacetValueIds = this.variants[index].facetValues.map((fv) => fv.id);
             return formFacetValueIds
-                .filter(x => !variantFacetValueIds.includes(x))
-                .map(id => this.facetValues.find(fv => fv.id === id))
+                .filter((x) => !variantFacetValueIds.includes(x))
+                .map((id) => this.facetValues.find((fv) => fv.id === id))
                 .filter(notNullOrUndefined);
         } else {
             return [];
@@ -136,18 +151,18 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
     existingFacetValues(index: number) {
         const variant = this.variants[index];
         const formFacetValueIds = this.getFacetValueIds(index);
-        const intersection = [...formFacetValueIds].filter(x =>
-            variant.facetValues.map(fv => fv.id).includes(x),
+        const intersection = [...formFacetValueIds].filter((x) =>
+            variant.facetValues.map((fv) => fv.id).includes(x),
         );
         return intersection
-            .map(id => variant.facetValues.find(fv => fv.id === id))
+            .map((id) => variant.facetValues.find((fv) => fv.id === id))
             .filter(notNullOrUndefined);
     }
 
     removeFacetValue(index: number, facetValueId: string) {
         const formGroup = this.formArray.at(index);
         const newValue = (formGroup.value as VariantFormValue).facetValueIds.filter(
-            id => id !== facetValueId,
+            (id) => id !== facetValueId,
         );
         formGroup.patchValue({
             facetValueIds: newValue,
@@ -169,9 +184,11 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro
                 size: 'md',
                 locals: {
                     productOption: option,
+                    activeLanguage: this.activeLanguage,
+                    customFields: this.customOptionFields,
                 },
             })
-            .subscribe(result => {
+            .subscribe((result) => {
                 if (result) {
                     this.updateProductOption.emit(result);
                 }

+ 13 - 2
packages/admin-ui/src/lib/catalog/src/components/update-product-option-dialog/update-product-option-dialog.component.html

@@ -1,5 +1,4 @@
 <ng-template vdrDialogTitle>{{ 'catalog.update-product-option' | translate }}</ng-template>
-
 <vdr-form-field [label]="'catalog.option-name' | translate" for="name">
     <input
         id="name"
@@ -13,13 +12,25 @@
 <vdr-form-field [label]="'common.code' | translate" for="code">
     <input id="code" type="text" #codeInput="ngModel" required [(ngModel)]="code" pattern="[a-z0-9_-]+" />
 </vdr-form-field>
+<section *ngIf="customFields.length">
+    <label>{{ 'common.custom-fields' | translate }}</label>
+    <ng-container *ngFor="let customField of customFields">
+        <vdr-custom-field-control
+            *ngIf="customFieldsForm.get(customField.name)"
+            entityName="ProductOption"
+            [customFieldsFormGroup]="customFieldsForm"
+            [customField]="customField"
+            [readonly]="!('UpdateCatalog' | hasPermission)"
+        ></vdr-custom-field-control>
+    </ng-container>
+</section>
 
 <ng-template vdrDialogButtons>
     <button type="button" class="btn" (click)="cancel()">{{ 'common.cancel' | translate }}</button>
     <button
         type="submit"
         (click)="update()"
-        [disabled]="nameInput.invalid || codeInput.invalid || (nameInput.pristine && codeInput.pristine)"
+        [disabled]="nameInput.invalid || codeInput.invalid || (nameInput.pristine && codeInput.pristine && customFieldsForm.pristine)"
         class="btn btn-primary"
     >
         {{ 'catalog.update-product-option' | translate }}

+ 36 - 4
packages/admin-ui/src/lib/catalog/src/components/update-product-option-dialog/update-product-option-dialog.component.ts

@@ -1,5 +1,11 @@
 import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-import { ProductVariant, UpdateProductOptionInput } from '@vendure/admin-ui/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import {
+    CustomFieldConfig,
+    LanguageCode,
+    ProductVariant,
+    UpdateProductOptionInput,
+} from '@vendure/admin-ui/core';
 import { createUpdatedTranslatable } from '@vendure/admin-ui/core';
 import { Dialog } from '@vendure/admin-ui/core';
 import { normalizeString } from '@vendure/common/lib/normalize-string';
@@ -14,22 +20,48 @@ export class UpdateProductOptionDialogComponent implements Dialog<UpdateProductO
     resolveWith: (result?: UpdateProductOptionInput) => void;
     // Provided by caller
     productOption: ProductVariant.Options;
+    activeLanguage: LanguageCode;
     name: string;
     code: string;
+    customFields: CustomFieldConfig[];
     codeInputTouched = false;
+    customFieldsForm: FormGroup;
 
     ngOnInit(): void {
-        this.name = this.productOption.name;
+        const currentTranslation = this.productOption.translations.find(
+            (t) => t.languageCode === this.activeLanguage,
+        );
+        this.name = currentTranslation?.name ?? '';
         this.code = this.productOption.code;
+        this.customFieldsForm = new FormGroup({});
+        if (this.customFields) {
+            const cfCurrentTranslation =
+                (currentTranslation && (currentTranslation as any).customFields) || {};
+
+            for (const fieldDef of this.customFields) {
+                const key = fieldDef.name;
+                const value =
+                    fieldDef.type === 'localeString'
+                        ? cfCurrentTranslation[key]
+                        : (this.productOption as any).customFields[key];
+                this.customFieldsForm.addControl(fieldDef.name, new FormControl(value));
+            }
+        }
     }
 
     update() {
         const result = createUpdatedTranslatable({
             translatable: this.productOption,
-            languageCode: this.productOption.languageCode,
+            languageCode: this.activeLanguage,
             updatedFields: {
                 code: this.code,
                 name: this.name,
+                customFields: this.customFieldsForm.value,
+            },
+            customFieldConfig: this.customFields,
+            defaultTranslation: {
+                languageCode: this.activeLanguage,
+                name: '',
             },
         });
         this.resolveWith(result);
@@ -40,7 +72,7 @@ export class UpdateProductOptionDialogComponent implements Dialog<UpdateProductO
     }
 
     updateCode(nameValue: string) {
-        if (!this.codeInputTouched) {
+        if (!this.codeInputTouched && !this.productOption.code) {
             this.code = normalizeString(nameValue, '-');
         }
     }

+ 13 - 4
packages/admin-ui/src/lib/core/src/common/base-detail.component.ts

@@ -82,9 +82,18 @@ export abstract class BaseDetailComponent<Entity extends { id: string; updatedAt
     }
 
     protected setQueryParam(key: string, value: any) {
-        this.router.navigate(['./', { [key]: value }], {
-            relativeTo: this.route,
-            queryParamsHandling: 'merge',
-        });
+        this.router.navigate(
+            [
+                './',
+                {
+                    ...this.route.snapshot.params,
+                    [key]: value,
+                },
+            ],
+            {
+                relativeTo: this.route,
+                queryParamsHandling: 'merge',
+            },
+        );
     }
 }

+ 79 - 30
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -513,18 +513,26 @@ export type CreateProductInput = {
   customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type CreateProductOptionCustomFieldsInput = {
+  colorHex?: Maybe<Scalars['String']>;
+};
+
+export type CreateProductOptionGroupCustomFieldsInput = {
+  linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type CreateProductOptionGroupInput = {
   code: Scalars['String'];
   translations: Array<ProductOptionGroupTranslationInput>;
   options: Array<CreateGroupOptionInput>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<CreateProductOptionGroupCustomFieldsInput>;
 };
 
 export type CreateProductOptionInput = {
   productOptionGroupId: Scalars['ID'];
   code: Scalars['String'];
   translations: Array<ProductOptionGroupTranslationInput>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<CreateProductOptionCustomFieldsInput>;
 };
 
 export type CreateProductVariantInput = {
@@ -2687,7 +2695,12 @@ export type ProductOption = Node & {
   name: Scalars['String'];
   groupId: Scalars['ID'];
   translations: Array<ProductOptionTranslation>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+   __typename?: 'ProductOptionCustomFields';
+  colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -2700,7 +2713,12 @@ export type ProductOptionGroup = Node & {
   name: Scalars['String'];
   options: Array<ProductOption>;
   translations: Array<ProductOptionGroupTranslation>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+   __typename?: 'ProductOptionGroupCustomFields';
+  linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {
@@ -3672,18 +3690,26 @@ export type UpdateProductInput = {
   customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type UpdateProductOptionCustomFieldsInput = {
+  colorHex?: Maybe<Scalars['String']>;
+};
+
+export type UpdateProductOptionGroupCustomFieldsInput = {
+  linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type UpdateProductOptionGroupInput = {
   id: Scalars['ID'];
   code?: Maybe<Scalars['String']>;
   translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<UpdateProductOptionGroupCustomFieldsInput>;
 };
 
 export type UpdateProductOptionInput = {
   id: Scalars['ID'];
   code?: Maybe<Scalars['String']>;
   translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<UpdateProductOptionCustomFieldsInput>;
 };
 
 export type UpdateProductVariantInput = {
@@ -4956,6 +4982,24 @@ export type AssetFragment = (
   )> }
 );
 
+export type ProductOptionGroupFragment = (
+  { __typename?: 'ProductOptionGroup' }
+  & Pick<ProductOptionGroup, 'id' | 'code' | 'languageCode' | 'name'>
+  & { translations: Array<(
+    { __typename?: 'ProductOptionGroupTranslation' }
+    & Pick<ProductOptionGroupTranslation, 'id' | 'languageCode' | 'name'>
+  )> }
+);
+
+export type ProductOptionFragment = (
+  { __typename?: 'ProductOption' }
+  & Pick<ProductOption, 'id' | 'code' | 'languageCode' | 'name' | 'groupId'>
+  & { translations: Array<(
+    { __typename?: 'ProductOptionTranslation' }
+    & Pick<ProductOptionTranslation, 'id' | 'languageCode' | 'name'>
+  )> }
+);
+
 export type ProductVariantFragment = (
   { __typename?: 'ProductVariant' }
   & Pick<ProductVariant, 'id' | 'createdAt' | 'updatedAt' | 'enabled' | 'languageCode' | 'name' | 'price' | 'currencyCode' | 'priceIncludesTax' | 'priceWithTax' | 'stockOnHand' | 'trackInventory' | 'sku'>
@@ -4967,11 +5011,7 @@ export type ProductVariantFragment = (
     & Pick<TaxCategory, 'id' | 'name'>
   ), options: Array<(
     { __typename?: 'ProductOption' }
-    & Pick<ProductOption, 'id' | 'code' | 'languageCode' | 'name' | 'groupId'>
-    & { translations: Array<(
-      { __typename?: 'ProductOptionTranslation' }
-      & Pick<ProductOptionTranslation, 'id' | 'languageCode' | 'name'>
-    )> }
+    & ProductOptionFragment
   )>, facetValues: Array<(
     { __typename?: 'FacetValue' }
     & Pick<FacetValue, 'id' | 'code' | 'name'>
@@ -5005,7 +5045,7 @@ export type ProductWithVariantsFragment = (
     & Pick<ProductTranslation, 'id' | 'languageCode' | 'name' | 'slug' | 'description'>
   )>, optionGroups: Array<(
     { __typename?: 'ProductOptionGroup' }
-    & Pick<ProductOptionGroup, 'id' | 'languageCode' | 'code' | 'name'>
+    & ProductOptionGroupFragment
   )>, variants: Array<(
     { __typename?: 'ProductVariant' }
     & ProductVariantFragment
@@ -5022,7 +5062,7 @@ export type ProductWithVariantsFragment = (
   )> }
 );
 
-export type ProductOptionGroupFragment = (
+export type ProductOptionGroupWithOptionsFragment = (
   { __typename?: 'ProductOptionGroup' }
   & Pick<ProductOptionGroup, 'id' | 'createdAt' | 'updatedAt' | 'languageCode' | 'code' | 'name'>
   & { translations: Array<(
@@ -5112,7 +5152,7 @@ export type CreateProductOptionGroupMutation = (
   { __typename?: 'Mutation' }
   & { createProductOptionGroup: (
     { __typename?: 'ProductOptionGroup' }
-    & ProductOptionGroupFragment
+    & ProductOptionGroupWithOptionsFragment
   ) }
 );
 
@@ -5125,7 +5165,7 @@ export type GetProductOptionGroupQuery = (
   { __typename?: 'Query' }
   & { productOptionGroup?: Maybe<(
     { __typename?: 'ProductOptionGroup' }
-    & ProductOptionGroupFragment
+    & ProductOptionGroupWithOptionsFragment
   )> }
 );
 
@@ -5359,7 +5399,7 @@ export type UpdateProductOptionMutation = (
   { __typename?: 'Mutation' }
   & { updateProductOption: (
     { __typename?: 'ProductOption' }
-    & Pick<ProductOption, 'id' | 'createdAt' | 'updatedAt' | 'code' | 'name'>
+    & ProductOptionFragment
   ) }
 );
 
@@ -5391,7 +5431,7 @@ export type GetProductVariantOptionsQuery = (
       & Pick<ProductOptionGroup, 'id' | 'name' | 'code'>
       & { options: Array<(
         { __typename?: 'ProductOption' }
-        & Pick<ProductOption, 'id' | 'createdAt' | 'updatedAt' | 'name' | 'code'>
+        & ProductOptionFragment
       )> }
     )>, variants: Array<(
       { __typename?: 'ProductVariant' }
@@ -7220,17 +7260,26 @@ export namespace Asset {
   export type FocalPoint = (NonNullable<AssetFragment['focalPoint']>);
 }
 
+export namespace ProductOptionGroup {
+  export type Fragment = ProductOptionGroupFragment;
+  export type Translations = (NonNullable<ProductOptionGroupFragment['translations'][0]>);
+}
+
+export namespace ProductOption {
+  export type Fragment = ProductOptionFragment;
+  export type Translations = (NonNullable<ProductOptionFragment['translations'][0]>);
+}
+
 export namespace ProductVariant {
   export type Fragment = ProductVariantFragment;
   export type TaxRateApplied = ProductVariantFragment['taxRateApplied'];
   export type TaxCategory = ProductVariantFragment['taxCategory'];
-  export type Options = (NonNullable<ProductVariantFragment['options'][0]>);
-  export type Translations = (NonNullable<(NonNullable<ProductVariantFragment['options'][0]>)['translations'][0]>);
+  export type Options = ProductOptionFragment;
   export type FacetValues = (NonNullable<ProductVariantFragment['facetValues'][0]>);
   export type Facet = (NonNullable<ProductVariantFragment['facetValues'][0]>)['facet'];
   export type FeaturedAsset = AssetFragment;
   export type Assets = AssetFragment;
-  export type _Translations = (NonNullable<ProductVariantFragment['translations'][0]>);
+  export type Translations = (NonNullable<ProductVariantFragment['translations'][0]>);
 }
 
 export namespace ProductWithVariants {
@@ -7238,18 +7287,18 @@ export namespace ProductWithVariants {
   export type FeaturedAsset = AssetFragment;
   export type Assets = AssetFragment;
   export type Translations = (NonNullable<ProductWithVariantsFragment['translations'][0]>);
-  export type OptionGroups = (NonNullable<ProductWithVariantsFragment['optionGroups'][0]>);
+  export type OptionGroups = ProductOptionGroupFragment;
   export type Variants = ProductVariantFragment;
   export type FacetValues = (NonNullable<ProductWithVariantsFragment['facetValues'][0]>);
   export type Facet = (NonNullable<ProductWithVariantsFragment['facetValues'][0]>)['facet'];
   export type Channels = (NonNullable<ProductWithVariantsFragment['channels'][0]>);
 }
 
-export namespace ProductOptionGroup {
-  export type Fragment = ProductOptionGroupFragment;
-  export type Translations = (NonNullable<ProductOptionGroupFragment['translations'][0]>);
-  export type Options = (NonNullable<ProductOptionGroupFragment['options'][0]>);
-  export type _Translations = (NonNullable<(NonNullable<ProductOptionGroupFragment['options'][0]>)['translations'][0]>);
+export namespace ProductOptionGroupWithOptions {
+  export type Fragment = ProductOptionGroupWithOptionsFragment;
+  export type Translations = (NonNullable<ProductOptionGroupWithOptionsFragment['translations'][0]>);
+  export type Options = (NonNullable<ProductOptionGroupWithOptionsFragment['options'][0]>);
+  export type _Translations = (NonNullable<(NonNullable<ProductOptionGroupWithOptionsFragment['options'][0]>)['translations'][0]>);
 }
 
 export namespace UpdateProduct {
@@ -7285,13 +7334,13 @@ export namespace UpdateProductVariants {
 export namespace CreateProductOptionGroup {
   export type Variables = CreateProductOptionGroupMutationVariables;
   export type Mutation = CreateProductOptionGroupMutation;
-  export type CreateProductOptionGroup = ProductOptionGroupFragment;
+  export type CreateProductOptionGroup = ProductOptionGroupWithOptionsFragment;
 }
 
 export namespace GetProductOptionGroup {
   export type Variables = GetProductOptionGroupQueryVariables;
   export type Query = GetProductOptionGroupQuery;
-  export type ProductOptionGroup = ProductOptionGroupFragment;
+  export type ProductOptionGroup = ProductOptionGroupWithOptionsFragment;
 }
 
 export namespace AddOptionToGroup {
@@ -7385,7 +7434,7 @@ export namespace SearchProducts {
 export namespace UpdateProductOption {
   export type Variables = UpdateProductOptionMutationVariables;
   export type Mutation = UpdateProductOptionMutation;
-  export type UpdateProductOption = UpdateProductOptionMutation['updateProductOption'];
+  export type UpdateProductOption = ProductOptionFragment;
 }
 
 export namespace DeleteProductVariant {
@@ -7399,7 +7448,7 @@ export namespace GetProductVariantOptions {
   export type Query = GetProductVariantOptionsQuery;
   export type Product = (NonNullable<GetProductVariantOptionsQuery['product']>);
   export type OptionGroups = (NonNullable<(NonNullable<GetProductVariantOptionsQuery['product']>)['optionGroups'][0]>);
-  export type Options = (NonNullable<(NonNullable<(NonNullable<GetProductVariantOptionsQuery['product']>)['optionGroups'][0]>)['options'][0]>);
+  export type Options = ProductOptionFragment;
   export type Variants = (NonNullable<(NonNullable<GetProductVariantOptionsQuery['product']>)['variants'][0]>);
   export type _Options = (NonNullable<(NonNullable<(NonNullable<GetProductVariantOptionsQuery['product']>)['variants'][0]>)['options'][0]>);
 }

+ 43 - 30
packages/admin-ui/src/lib/core/src/data/definitions/product-definitions.ts

@@ -20,6 +20,35 @@ export const ASSET_FRAGMENT = gql`
     }
 `;
 
+export const PRODUCT_OPTION_GROUP_FRAGMENT = gql`
+    fragment ProductOptionGroup on ProductOptionGroup {
+        id
+        code
+        languageCode
+        name
+        translations {
+            id
+            languageCode
+            name
+        }
+    }
+`;
+
+export const PRODUCT_OPTION_FRAGMENT = gql`
+    fragment ProductOption on ProductOption {
+        id
+        code
+        languageCode
+        name
+        groupId
+        translations {
+            id
+            languageCode
+            name
+        }
+    }
+`;
+
 export const PRODUCT_VARIANT_FRAGMENT = gql`
     fragment ProductVariant on ProductVariant {
         id
@@ -45,16 +74,7 @@ export const PRODUCT_VARIANT_FRAGMENT = gql`
         }
         sku
         options {
-            id
-            code
-            languageCode
-            name
-            groupId
-            translations {
-                id
-                languageCode
-                name
-            }
+            ...ProductOption
         }
         facetValues {
             id
@@ -77,6 +97,7 @@ export const PRODUCT_VARIANT_FRAGMENT = gql`
             name
         }
     }
+    ${PRODUCT_OPTION_FRAGMENT}
     ${ASSET_FRAGMENT}
 `;
 
@@ -104,10 +125,7 @@ export const PRODUCT_WITH_VARIANTS_FRAGMENT = gql`
             description
         }
         optionGroups {
-            id
-            languageCode
-            code
-            name
+            ...ProductOptionGroup
         }
         variants {
             ...ProductVariant
@@ -126,12 +144,13 @@ export const PRODUCT_WITH_VARIANTS_FRAGMENT = gql`
             code
         }
     }
+    ${PRODUCT_OPTION_GROUP_FRAGMENT}
     ${PRODUCT_VARIANT_FRAGMENT}
     ${ASSET_FRAGMENT}
 `;
 
-export const PRODUCT_OPTION_GROUP_FRAGMENT = gql`
-    fragment ProductOptionGroup on ProductOptionGroup {
+export const PRODUCT_OPTION_GROUP_WITH_OPTIONS_FRAGMENT = gql`
+    fragment ProductOptionGroupWithOptions on ProductOptionGroup {
         id
         createdAt
         updatedAt
@@ -202,19 +221,19 @@ export const UPDATE_PRODUCT_VARIANTS = gql`
 export const CREATE_PRODUCT_OPTION_GROUP = gql`
     mutation CreateProductOptionGroup($input: CreateProductOptionGroupInput!) {
         createProductOptionGroup(input: $input) {
-            ...ProductOptionGroup
+            ...ProductOptionGroupWithOptions
         }
     }
-    ${PRODUCT_OPTION_GROUP_FRAGMENT}
+    ${PRODUCT_OPTION_GROUP_WITH_OPTIONS_FRAGMENT}
 `;
 
 export const GET_PRODUCT_OPTION_GROUP = gql`
     query GetProductOptionGroup($id: ID!) {
         productOptionGroup(id: $id) {
-            ...ProductOptionGroup
+            ...ProductOptionGroupWithOptions
         }
     }
-    ${PRODUCT_OPTION_GROUP_FRAGMENT}
+    ${PRODUCT_OPTION_GROUP_WITH_OPTIONS_FRAGMENT}
 `;
 
 export const ADD_OPTION_TO_GROUP = gql`
@@ -426,13 +445,10 @@ export const SEARCH_PRODUCTS = gql`
 export const UPDATE_PRODUCT_OPTION = gql`
     mutation UpdateProductOption($input: UpdateProductOptionInput!) {
         updateProductOption(input: $input) {
-            id
-            createdAt
-            updatedAt
-            code
-            name
+            ...ProductOption
         }
     }
+    ${PRODUCT_OPTION_FRAGMENT}
 `;
 
 export const DELETE_PRODUCT_VARIANT = gql`
@@ -456,11 +472,7 @@ export const GET_PRODUCT_VARIANT_OPTIONS = gql`
                 name
                 code
                 options {
-                    id
-                    createdAt
-                    updatedAt
-                    name
-                    code
+                    ...ProductOption
                 }
             }
             variants {
@@ -484,6 +496,7 @@ export const GET_PRODUCT_VARIANT_OPTIONS = gql`
             }
         }
     }
+    ${PRODUCT_OPTION_FRAGMENT}
 `;
 
 export const ASSIGN_PRODUCTS_TO_CHANNEL = gql`

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

@@ -240,7 +240,7 @@ export class ProductDataService {
         return this.baseDataService.mutate<UpdateProductOption.Mutation, UpdateProductOption.Variables>(
             UPDATE_PRODUCT_OPTION,
             {
-                input: pick(input, ['id', 'code', 'translations']),
+                input: pick(input, ['id', 'code', 'translations', 'customFields']),
             },
         );
     }

+ 32 - 6
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -512,18 +512,26 @@ export type CreateProductInput = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type CreateProductOptionCustomFieldsInput = {
+    colorHex?: Maybe<Scalars['String']>;
+};
+
+export type CreateProductOptionGroupCustomFieldsInput = {
+    linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type CreateProductOptionGroupInput = {
     code: Scalars['String'];
     translations: Array<ProductOptionGroupTranslationInput>;
     options: Array<CreateGroupOptionInput>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<CreateProductOptionGroupCustomFieldsInput>;
 };
 
 export type CreateProductOptionInput = {
     productOptionGroupId: Scalars['ID'];
     code: Scalars['String'];
     translations: Array<ProductOptionGroupTranslationInput>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<CreateProductOptionCustomFieldsInput>;
 };
 
 export type CreateProductVariantInput = {
@@ -2564,7 +2572,12 @@ export type ProductOption = Node & {
     name: Scalars['String'];
     groupId: Scalars['ID'];
     translations: Array<ProductOptionTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+    __typename?: 'ProductOptionCustomFields';
+    colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -2577,7 +2590,12 @@ export type ProductOptionGroup = Node & {
     name: Scalars['String'];
     options: Array<ProductOption>;
     translations: Array<ProductOptionGroupTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+    __typename?: 'ProductOptionGroupCustomFields';
+    linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {
@@ -3504,18 +3522,26 @@ export type UpdateProductInput = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type UpdateProductOptionCustomFieldsInput = {
+    colorHex?: Maybe<Scalars['String']>;
+};
+
+export type UpdateProductOptionGroupCustomFieldsInput = {
+    linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type UpdateProductOptionGroupInput = {
     id: Scalars['ID'];
     code?: Maybe<Scalars['String']>;
     translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<UpdateProductOptionGroupCustomFieldsInput>;
 };
 
 export type UpdateProductOptionInput = {
     id: Scalars['ID'];
     code?: Maybe<Scalars['String']>;
     translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<UpdateProductOptionCustomFieldsInput>;
 };
 
 export type UpdateProductVariantInput = {

+ 12 - 2
packages/common/src/generated-shop-types.ts

@@ -1768,7 +1768,12 @@ export type ProductOption = Node & {
     name: Scalars['String'];
     groupId: Scalars['ID'];
     translations: Array<ProductOptionTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+    __typename?: 'ProductOptionCustomFields';
+    colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -1781,7 +1786,12 @@ export type ProductOptionGroup = Node & {
     name: Scalars['String'];
     options: Array<ProductOption>;
     translations: Array<ProductOptionGroupTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+    __typename?: 'ProductOptionGroupCustomFields';
+    linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {

+ 32 - 6
packages/common/src/generated-types.ts

@@ -512,18 +512,26 @@ export type CreateProductInput = {
   customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type CreateProductOptionCustomFieldsInput = {
+  colorHex?: Maybe<Scalars['String']>;
+};
+
+export type CreateProductOptionGroupCustomFieldsInput = {
+  linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type CreateProductOptionGroupInput = {
   code: Scalars['String'];
   translations: Array<ProductOptionGroupTranslationInput>;
   options: Array<CreateGroupOptionInput>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<CreateProductOptionGroupCustomFieldsInput>;
 };
 
 export type CreateProductOptionInput = {
   productOptionGroupId: Scalars['ID'];
   code: Scalars['String'];
   translations: Array<ProductOptionGroupTranslationInput>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<CreateProductOptionCustomFieldsInput>;
 };
 
 export type CreateProductVariantInput = {
@@ -2647,7 +2655,12 @@ export type ProductOption = Node & {
   name: Scalars['String'];
   groupId: Scalars['ID'];
   translations: Array<ProductOptionTranslation>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+   __typename?: 'ProductOptionCustomFields';
+  colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -2660,7 +2673,12 @@ export type ProductOptionGroup = Node & {
   name: Scalars['String'];
   options: Array<ProductOption>;
   translations: Array<ProductOptionGroupTranslation>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+   __typename?: 'ProductOptionGroupCustomFields';
+  linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {
@@ -3624,18 +3642,26 @@ export type UpdateProductInput = {
   customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type UpdateProductOptionCustomFieldsInput = {
+  colorHex?: Maybe<Scalars['String']>;
+};
+
+export type UpdateProductOptionGroupCustomFieldsInput = {
+  linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type UpdateProductOptionGroupInput = {
   id: Scalars['ID'];
   code?: Maybe<Scalars['String']>;
   translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<UpdateProductOptionGroupCustomFieldsInput>;
 };
 
 export type UpdateProductOptionInput = {
   id: Scalars['ID'];
   code?: Maybe<Scalars['String']>;
   translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-  customFields?: Maybe<Scalars['JSON']>;
+  customFields?: Maybe<UpdateProductOptionCustomFieldsInput>;
 };
 
 export type UpdateProductVariantInput = {

+ 32 - 6
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -512,18 +512,26 @@ export type CreateProductInput = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type CreateProductOptionCustomFieldsInput = {
+    colorHex?: Maybe<Scalars['String']>;
+};
+
+export type CreateProductOptionGroupCustomFieldsInput = {
+    linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type CreateProductOptionGroupInput = {
     code: Scalars['String'];
     translations: Array<ProductOptionGroupTranslationInput>;
     options: Array<CreateGroupOptionInput>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<CreateProductOptionGroupCustomFieldsInput>;
 };
 
 export type CreateProductOptionInput = {
     productOptionGroupId: Scalars['ID'];
     code: Scalars['String'];
     translations: Array<ProductOptionGroupTranslationInput>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<CreateProductOptionCustomFieldsInput>;
 };
 
 export type CreateProductVariantInput = {
@@ -2564,7 +2572,12 @@ export type ProductOption = Node & {
     name: Scalars['String'];
     groupId: Scalars['ID'];
     translations: Array<ProductOptionTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+    __typename?: 'ProductOptionCustomFields';
+    colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -2577,7 +2590,12 @@ export type ProductOptionGroup = Node & {
     name: Scalars['String'];
     options: Array<ProductOption>;
     translations: Array<ProductOptionGroupTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+    __typename?: 'ProductOptionGroupCustomFields';
+    linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {
@@ -3504,18 +3522,26 @@ export type UpdateProductInput = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type UpdateProductOptionCustomFieldsInput = {
+    colorHex?: Maybe<Scalars['String']>;
+};
+
+export type UpdateProductOptionGroupCustomFieldsInput = {
+    linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type UpdateProductOptionGroupInput = {
     id: Scalars['ID'];
     code?: Maybe<Scalars['String']>;
     translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<UpdateProductOptionGroupCustomFieldsInput>;
 };
 
 export type UpdateProductOptionInput = {
     id: Scalars['ID'];
     code?: Maybe<Scalars['String']>;
     translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<UpdateProductOptionCustomFieldsInput>;
 };
 
 export type UpdateProductVariantInput = {

+ 12 - 2
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -1768,7 +1768,12 @@ export type ProductOption = Node & {
     name: Scalars['String'];
     groupId: Scalars['ID'];
     translations: Array<ProductOptionTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+    __typename?: 'ProductOptionCustomFields';
+    colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -1781,7 +1786,12 @@ export type ProductOptionGroup = Node & {
     name: Scalars['String'];
     options: Array<ProductOption>;
     translations: Array<ProductOptionGroupTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+    __typename?: 'ProductOptionGroupCustomFields';
+    linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {

+ 31 - 0
packages/dev-server/dev-config.ts

@@ -7,6 +7,7 @@ import {
     DefaultLogger,
     DefaultSearchPlugin,
     examplePaymentHandler,
+    LanguageCode,
     LogLevel,
     VendureConfig,
 } from '@vendure/core';
@@ -55,6 +56,36 @@ export const devConfig: VendureConfig = {
             { name: 'rating', type: 'float', readonly: true },
             { name: 'markup', type: 'float', internal: true },
         ],*/
+        ProductOptionGroup: [
+            {
+                name: 'linkUrl',
+                type: 'string',
+            },
+        ],
+        ProductOption: [
+            {
+                name: 'colorHex',
+                description: [
+                    {
+                        languageCode: LanguageCode.en,
+                        value: 'Color',
+                    },
+                    {
+                        languageCode: LanguageCode.pt_BR,
+                        value: 'Cor',
+                    },
+                ],
+                type: 'string',
+                nullable: true,
+                public: true,
+                pattern: '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$',
+                length: 7,
+            },
+            {
+                name: 'colorName',
+                type: 'localeString',
+            },
+        ],
     },
     logger: new DefaultLogger({ level: LogLevel.Info }),
     importExportOptions: {

+ 32 - 6
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -512,18 +512,26 @@ export type CreateProductInput = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type CreateProductOptionCustomFieldsInput = {
+    colorHex?: Maybe<Scalars['String']>;
+};
+
+export type CreateProductOptionGroupCustomFieldsInput = {
+    linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type CreateProductOptionGroupInput = {
     code: Scalars['String'];
     translations: Array<ProductOptionGroupTranslationInput>;
     options: Array<CreateGroupOptionInput>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<CreateProductOptionGroupCustomFieldsInput>;
 };
 
 export type CreateProductOptionInput = {
     productOptionGroupId: Scalars['ID'];
     code: Scalars['String'];
     translations: Array<ProductOptionGroupTranslationInput>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<CreateProductOptionCustomFieldsInput>;
 };
 
 export type CreateProductVariantInput = {
@@ -2564,7 +2572,12 @@ export type ProductOption = Node & {
     name: Scalars['String'];
     groupId: Scalars['ID'];
     translations: Array<ProductOptionTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionCustomFields>;
+};
+
+export type ProductOptionCustomFields = {
+    __typename?: 'ProductOptionCustomFields';
+    colorHex?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroup = Node & {
@@ -2577,7 +2590,12 @@ export type ProductOptionGroup = Node & {
     name: Scalars['String'];
     options: Array<ProductOption>;
     translations: Array<ProductOptionGroupTranslation>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<ProductOptionGroupCustomFields>;
+};
+
+export type ProductOptionGroupCustomFields = {
+    __typename?: 'ProductOptionGroupCustomFields';
+    linkUrl?: Maybe<Scalars['String']>;
 };
 
 export type ProductOptionGroupTranslation = {
@@ -3504,18 +3522,26 @@ export type UpdateProductInput = {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type UpdateProductOptionCustomFieldsInput = {
+    colorHex?: Maybe<Scalars['String']>;
+};
+
+export type UpdateProductOptionGroupCustomFieldsInput = {
+    linkUrl?: Maybe<Scalars['String']>;
+};
+
 export type UpdateProductOptionGroupInput = {
     id: Scalars['ID'];
     code?: Maybe<Scalars['String']>;
     translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<UpdateProductOptionGroupCustomFieldsInput>;
 };
 
 export type UpdateProductOptionInput = {
     id: Scalars['ID'];
     code?: Maybe<Scalars['String']>;
     translations?: Maybe<Array<ProductOptionGroupTranslationInput>>;
-    customFields?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<UpdateProductOptionCustomFieldsInput>;
 };
 
 export type UpdateProductVariantInput = {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
schema-admin.json


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
schema-shop.json


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott