소스 검색

feat(admin-ui): Add support for bulk product facet editing

Relates to #853
Michael Bromley 3 년 전
부모
커밋
0d1b592d32
23개의 변경된 파일478개의 추가작업 그리고 21개의 파일을 삭제
  1. 20 20
      packages/admin-ui/i18n-coverage.json
  2. 4 0
      packages/admin-ui/src/lib/catalog/src/catalog.module.ts
  3. 51 0
      packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.html
  4. 9 0
      packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.scss
  5. 141 0
      packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.ts
  6. 72 0
      packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.graphql.ts
  7. 41 0
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list-bulk-actions.ts
  8. 2 1
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.html
  9. 3 0
      packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.scss
  10. 109 0
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  11. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/cs.json
  12. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  13. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  14. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  15. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/fr.json
  16. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/it.json
  17. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  18. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  19. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_PT.json
  20. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/ru.json
  21. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/uk.json
  22. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  23. 2 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

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

@@ -1,69 +1,69 @@
 {
-  "generatedOn": "2022-09-26T12:56:28.131Z",
-  "lastCommit": "b9e89fd11bfdc8c5c6b492559dbda50d067b7a69",
+  "generatedOn": "2022-09-26T19:12:40.780Z",
+  "lastCommit": "6ee74e423d52d0f228e0bff5696975282d67fb25",
   "translationStatus": {
     "cs": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 593,
       "percentage": 90
     },
     "de": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 572,
       "percentage": 87
     },
     "en": {
-      "tokenCount": 657,
-      "translatedCount": 655,
+      "tokenCount": 659,
+      "translatedCount": 657,
       "percentage": 100
     },
     "es": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 624,
       "percentage": 95
     },
     "fr": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 614,
       "percentage": 93
     },
     "it": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 622,
-      "percentage": 95
+      "percentage": 94
     },
     "pl": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 407,
       "percentage": 62
     },
     "pt_BR": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 591,
       "percentage": 90
     },
     "pt_PT": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 635,
-      "percentage": 97
+      "percentage": 96
     },
     "ru": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 621,
-      "percentage": 95
+      "percentage": 94
     },
     "uk": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 621,
-      "percentage": 95
+      "percentage": 94
     },
     "zh_Hans": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 559,
       "percentage": 85
     },
     "zh_Hant": {
-      "tokenCount": 657,
+      "tokenCount": 659,
       "translatedCount": 387,
       "percentage": 59
     }

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

@@ -20,6 +20,7 @@ import { GenerateProductVariantsComponent } from './components/generate-product-
 import { OptionValueInputComponent } from './components/option-value-input/option-value-input.component';
 import { ProductDetailComponent } from './components/product-detail/product-detail.component';
 import {
+    assignFacetValuesToProductsBulkAction,
     assignProductsToChannelBulkAction,
     deleteProductsBulkAction,
 } from './components/product-list/product-list-bulk-actions';
@@ -30,6 +31,7 @@ import { ProductVariantsListComponent } from './components/product-variants-list
 import { ProductVariantsTableComponent } from './components/product-variants-table/product-variants-table.component';
 import { UpdateProductOptionDialogComponent } from './components/update-product-option-dialog/update-product-option-dialog.component';
 import { VariantPriceDetailComponent } from './components/variant-price-detail/variant-price-detail.component';
+import { BulkAddFacetValuesDialogComponent } from './components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component';
 
 const CATALOG_COMPONENTS = [
     ProductListComponent,
@@ -55,6 +57,7 @@ const CATALOG_COMPONENTS = [
     AssetDetailComponent,
     ConfirmVariantDeletionDialogComponent,
     ProductOptionsEditorComponent,
+    BulkAddFacetValuesDialogComponent,
 ];
 
 @NgModule({
@@ -64,6 +67,7 @@ const CATALOG_COMPONENTS = [
 })
 export class CatalogModule {
     constructor(private bulkActionRegistryService: BulkActionRegistryService) {
+        bulkActionRegistryService.registerBulkAction(assignFacetValuesToProductsBulkAction);
         bulkActionRegistryService.registerBulkAction(assignProductsToChannelBulkAction);
         bulkActionRegistryService.registerBulkAction(deleteProductsBulkAction);
     }

+ 51 - 0
packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.html

@@ -0,0 +1,51 @@
+<ng-template vdrDialogTitle>
+    {{ 'catalog.edit-facet-values' | translate }}
+</ng-template>
+
+<div class="flex">
+    <div class="flex center">
+        <div class="mr2">
+            {{ 'catalog.add-facet-value' | translate }}
+        </div>
+        <vdr-facet-value-selector
+            [facets]="facets"
+            (selectedValuesChange)="selectedValues = $event"
+        ></vdr-facet-value-selector>
+    </div>
+</div>
+
+<table class="table" *ngIf="state !== 'loading'; else placeholder">
+    <tbody>
+        <tr *ngFor="let item of items">
+            <td class="left align-middle">
+                <div>{{ item.name }}</div>
+                <div *ngIf="item.sku" class="sku">{{ item.sku }}</div>
+            </td>
+            <td class="left">
+                <vdr-facet-value-chip
+                    *ngFor="let facetValue of item.facetValues"
+                    [facetValue]="facetValue"
+                    (remove)="removeFacetValue(item, facetValue.id)"
+                ></vdr-facet-value-chip>
+            </td>
+        </tr>
+    </tbody>
+</table>
+
+<ng-template #placeholder>
+    <div class="loading">
+    <clr-spinner></clr-spinner>
+    </div>
+</ng-template>
+
+<ng-template vdrDialogButtons>
+    <button type="button" class="btn" (click)="cancel()">{{ 'common.cancel' | translate }}</button>
+    <button
+        type="submit"
+        (click)="addFacetValues()"
+        [disabled]="selectedValues.length === 0 && facetValuesRemoved === false"
+        class="btn btn-primary"
+    >
+        {{ 'common.update' | translate }}
+    </button>
+</ng-template>

+ 9 - 0
packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.scss

@@ -0,0 +1,9 @@
+.loading {
+    min-height: 25vh;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+.sku {
+    color: var(--color-text-300);
+}

+ 141 - 0
packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component.ts

@@ -0,0 +1,141 @@
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
+import {
+    DataService,
+    Dialog,
+    FacetWithValuesFragment,
+    GetProductsWithFacetValuesByIdsQuery,
+    GetProductsWithFacetValuesByIdsQueryVariables,
+    GetVariantsWithFacetValuesByIdsQuery,
+    UpdateProductsBulkMutation,
+    UpdateProductsBulkMutationVariables,
+    UpdateVariantsBulkMutation,
+    UpdateVariantsBulkMutationVariables,
+} from '@vendure/admin-ui/core';
+import { unique } from '@vendure/common/lib/unique';
+import { Observable, Subscription } from 'rxjs';
+import { shareReplay, switchMap } from 'rxjs/operators';
+
+import {
+    GET_PRODUCTS_WITH_FACET_VALUES_BY_IDS,
+    GET_VARIANTS_WITH_FACET_VALUES_BY_IDS,
+    UPDATE_PRODUCTS_BULK,
+    UPDATE_VARIANTS_BULK,
+} from './bulk-add-facet-values-dialog.graphql';
+
+interface ProductOrVariant {
+    id: string;
+    name: string;
+    sku?: string;
+    facetValues: Array<{
+        id: string;
+        name: string;
+        code: string;
+        facet: Array<{
+            id: string;
+            name: string;
+            code: string;
+        }>;
+    }>;
+}
+
+@Component({
+    selector: 'vdr-bulk-add-facet-values-dialog',
+    templateUrl: './bulk-add-facet-values-dialog.component.html',
+    styleUrls: ['./bulk-add-facet-values-dialog.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class BulkAddFacetValuesDialogComponent
+    implements OnInit, OnDestroy, Dialog<FacetWithValuesFragment[]>
+{
+    resolveWith: (result?: FacetWithValuesFragment[]) => void;
+    /* provided by call to ModalService */
+    mode: 'product' | 'variant' = 'product';
+    ids?: string[];
+    facets: FacetWithValuesFragment[] = [];
+    state: 'loading' | 'ready' | 'saving' = 'loading';
+
+    selectedValues: FacetWithValuesFragment[] = [];
+    items: ProductOrVariant[] = [];
+    facetValuesRemoved = false;
+    private subscription: Subscription;
+    constructor(private dataService: DataService, private changeDetectorRef: ChangeDetectorRef) {}
+
+    ngOnInit(): void {
+        const fetchData$: Observable<any> =
+            this.mode === 'product'
+                ? this.dataService
+                      .query<
+                          GetProductsWithFacetValuesByIdsQuery,
+                          GetProductsWithFacetValuesByIdsQueryVariables
+                      >(GET_PRODUCTS_WITH_FACET_VALUES_BY_IDS, {
+                          ids: this.ids ?? [],
+                      })
+                      .mapSingle(({ products }) =>
+                          products.items.map(p => ({ ...p, facetValues: [...p.facetValues] })),
+                      )
+                : this.dataService
+                      .query<
+                          GetVariantsWithFacetValuesByIdsQuery,
+                          GetProductsWithFacetValuesByIdsQueryVariables
+                      >(GET_VARIANTS_WITH_FACET_VALUES_BY_IDS, {
+                          ids: this.ids ?? [],
+                      })
+                      .mapSingle(({ productVariants }) =>
+                          productVariants.items.map(p => ({ ...p, facetValues: [...p.facetValues] })),
+                      );
+        this.subscription = fetchData$.subscribe({
+            next: items => {
+                this.items = items;
+                this.state = 'ready';
+                this.changeDetectorRef.markForCheck();
+            },
+        });
+    }
+
+    ngOnDestroy() {
+        this.subscription?.unsubscribe();
+    }
+
+    cancel() {
+        this.resolveWith();
+    }
+
+    removeFacetValue(item: ProductOrVariant, facetValueId: string) {
+        item.facetValues = item.facetValues.filter(fv => fv.id !== facetValueId);
+        this.facetValuesRemoved = true;
+    }
+
+    addFacetValues() {
+        const selectedFacetValueIds = this.selectedValues.map(sv => sv.id);
+        this.state = 'saving';
+        const save$: Observable<any> =
+            this.mode === 'product'
+                ? this.dataService.mutate<UpdateProductsBulkMutation, UpdateProductsBulkMutationVariables>(
+                      UPDATE_PRODUCTS_BULK,
+                      {
+                          input: this.items?.map(product => ({
+                              id: product.id,
+                              facetValueIds: unique([
+                                  ...product.facetValues.map(fv => fv.id),
+                                  ...selectedFacetValueIds,
+                              ]),
+                          })),
+                      },
+                  )
+                : this.dataService.mutate<UpdateVariantsBulkMutation, UpdateVariantsBulkMutationVariables>(
+                      UPDATE_VARIANTS_BULK,
+                      {
+                          input: this.items?.map(product => ({
+                              id: product.id,
+                              facetValueIds: unique([
+                                  ...product.facetValues.map(fv => fv.id),
+                                  ...selectedFacetValueIds,
+                              ]),
+                          })),
+                      },
+                  );
+        return save$.subscribe(result => {
+            this.resolveWith(this.selectedValues);
+        });
+    }
+}

+ 72 - 0
packages/admin-ui/src/lib/catalog/src/components/bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.graphql.ts

@@ -0,0 +1,72 @@
+import { gql } from 'apollo-angular';
+
+export const GET_PRODUCTS_WITH_FACET_VALUES_BY_IDS = gql`
+    query GetProductsWithFacetValuesByIds($ids: [String!]!) {
+        products(options: { filter: { id: { in: $ids } } }) {
+            items {
+                id
+                name
+                facetValues {
+                    id
+                    name
+                    code
+                    facet {
+                        id
+                        name
+                        code
+                    }
+                }
+            }
+        }
+    }
+`;
+
+export const GET_VARIANTS_WITH_FACET_VALUES_BY_IDS = gql`
+    query GetVariantsWithFacetValuesByIds($ids: [String!]!) {
+        productVariants(options: { filter: { id: { in: $ids } } }) {
+            items {
+                id
+                name
+                sku
+                facetValues {
+                    id
+                    name
+                    code
+                    facet {
+                        id
+                        name
+                        code
+                    }
+                }
+            }
+        }
+    }
+`;
+
+export const UPDATE_PRODUCTS_BULK = gql`
+    mutation UpdateProductsBulk($input: [UpdateProductInput!]!) {
+        updateProducts(input: $input) {
+            id
+            name
+            facetValues {
+                id
+                name
+                code
+            }
+        }
+    }
+`;
+
+export const UPDATE_VARIANTS_BULK = gql`
+    mutation UpdateVariantsBulk($input: [UpdateProductVariantInput!]!) {
+        updateProductVariants(input: $input) {
+            id
+            name
+            facetValues {
+                id
+                name
+                code
+            }
+        }
+    }
+`;

+ 41 - 0
packages/admin-ui/src/lib/catalog/src/components/product-list/product-list-bulk-actions.ts

@@ -12,6 +12,7 @@ import { EMPTY } from 'rxjs';
 import { switchMap } from 'rxjs/operators';
 
 import { AssignProductsToChannelDialogComponent } from '../assign-products-to-channel-dialog/assign-products-to-channel-dialog.component';
+import { BulkAddFacetValuesDialogComponent } from '../bulk-add-facet-values-dialog/bulk-add-facet-values-dialog.component';
 
 import { ProductListComponent } from './product-list.component';
 
@@ -97,3 +98,43 @@ export const assignProductsToChannelBulkAction: BulkAction<SearchProducts.Items,
             });
     },
 };
+
+export const assignFacetValuesToProductsBulkAction: BulkAction<SearchProducts.Items, ProductListComponent> = {
+    location: 'product-list',
+    label: _('catalog.edit-facet-values'),
+    icon: 'tag',
+    onClick: ({ injector, selection, hostComponent, clearSelection }) => {
+        const modalService = injector.get(ModalService);
+        const dataService = injector.get(DataService);
+        const notificationService = injector.get(NotificationService);
+        const mode: 'product' | 'variant' = hostComponent.groupByProduct ? 'product' : 'variant';
+        const ids =
+            mode === 'product'
+                ? unique(selection.map(p => p.productId))
+                : unique(selection.map(p => p.productVariantId));
+        return dataService.facet
+            .getAllFacets()
+            .mapSingle(data => data.facets.items)
+            .pipe(
+                switchMap(facets =>
+                    modalService.fromComponent(BulkAddFacetValuesDialogComponent, {
+                        size: 'xl',
+                        locals: {
+                            facets,
+                            mode,
+                            ids,
+                        },
+                    }),
+                ),
+            )
+            .subscribe(result => {
+                if (result) {
+                    notificationService.success(_('common.notify-bulk-update-success'), {
+                        count: selection.length,
+                        entity: mode === 'product' ? 'Products' : 'ProductVariants',
+                    });
+                    clearSelection();
+                }
+            });
+    },
+};

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

@@ -119,7 +119,8 @@
             </div>
         </td>
         <td class="left align-middle" [class.disabled]="!result.enabled">
-            {{ groupByProduct ? result.productName : result.productVariantName }}
+            <div>{{ groupByProduct ? result.productName : result.productVariantName }}</div>
+            <div *ngIf="!groupByProduct" class="sku">{{ result.sku }}</div>
         </td>
         <td class="align-middle" [class.disabled]="!result.enabled">
             <vdr-chip *ngIf="!result.enabled">{{ 'common.disabled' | translate }}</vdr-chip>

+ 3 - 0
packages/admin-ui/src/lib/catalog/src/components/product-list/product-list.component.scss

@@ -50,3 +50,6 @@ td.disabled {
 .edit-button {
     margin-right: 24px;
 }
+.sku {
+    color: var(--color-text-300);
+}

+ 109 - 0
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -2557,6 +2557,8 @@ export type Mutation = {
   updateProductOptionGroup: ProductOptionGroup;
   /** Update existing ProductVariants */
   updateProductVariants: Array<Maybe<ProductVariant>>;
+  /** Update multiple existing Products */
+  updateProducts: Array<Product>;
   updatePromotion: UpdatePromotionResult;
   /** Update an existing Role */
   updateRole: Role;
@@ -3158,6 +3160,11 @@ export type MutationUpdateProductVariantsArgs = {
 };
 
 
+export type MutationUpdateProductsArgs = {
+  input: Array<UpdateProductInput>;
+};
+
+
 export type MutationUpdatePromotionArgs = {
   input: UpdatePromotionInput;
 };
@@ -5492,6 +5499,76 @@ export type Zone = Node & {
   customFields?: Maybe<Scalars['JSON']>;
 };
 
+export type GetProductsWithFacetValuesByIdsQueryVariables = Exact<{
+  ids: Array<Scalars['String']> | Scalars['String'];
+}>;
+
+
+export type GetProductsWithFacetValuesByIdsQuery = { products: (
+    { __typename?: 'ProductList' }
+    & { items: Array<(
+      { __typename?: 'Product' }
+      & Pick<Product, 'id' | 'name'>
+      & { facetValues: Array<(
+        { __typename?: 'FacetValue' }
+        & Pick<FacetValue, 'id' | 'name' | 'code'>
+        & { facet: (
+          { __typename?: 'Facet' }
+          & Pick<Facet, 'id' | 'name' | 'code'>
+        ) }
+      )> }
+    )> }
+  ) };
+
+export type GetVariantsWithFacetValuesByIdsQueryVariables = Exact<{
+  ids: Array<Scalars['String']> | Scalars['String'];
+}>;
+
+
+export type GetVariantsWithFacetValuesByIdsQuery = { productVariants: (
+    { __typename?: 'ProductVariantList' }
+    & { items: Array<(
+      { __typename?: 'ProductVariant' }
+      & Pick<ProductVariant, 'id' | 'name' | 'sku'>
+      & { facetValues: Array<(
+        { __typename?: 'FacetValue' }
+        & Pick<FacetValue, 'id' | 'name' | 'code'>
+        & { facet: (
+          { __typename?: 'Facet' }
+          & Pick<Facet, 'id' | 'name' | 'code'>
+        ) }
+      )> }
+    )> }
+  ) };
+
+export type UpdateProductsBulkMutationVariables = Exact<{
+  input: Array<UpdateProductInput> | UpdateProductInput;
+}>;
+
+
+export type UpdateProductsBulkMutation = { updateProducts: Array<(
+    { __typename?: 'Product' }
+    & Pick<Product, 'id' | 'name'>
+    & { facetValues: Array<(
+      { __typename?: 'FacetValue' }
+      & Pick<FacetValue, 'id' | 'name' | 'code'>
+    )> }
+  )> };
+
+export type UpdateVariantsBulkMutationVariables = Exact<{
+  input: Array<UpdateProductVariantInput> | UpdateProductVariantInput;
+}>;
+
+
+export type UpdateVariantsBulkMutation = { updateProductVariants: Array<Maybe<(
+    { __typename?: 'ProductVariant' }
+    & Pick<ProductVariant, 'id' | 'name'>
+    & { facetValues: Array<(
+      { __typename?: 'FacetValue' }
+      & Pick<FacetValue, 'id' | 'name' | 'code'>
+    )> }
+  )>> };
+
 export type RoleFragment = (
   { __typename?: 'Role' }
   & Pick<Role, 'id' | 'createdAt' | 'updatedAt' | 'code' | 'description' | 'permissions'>
@@ -9320,6 +9397,38 @@ export type TestEligibleShippingMethodsQuery = { testEligibleShippingMethods: Ar
 
 type DiscriminateUnion<T, U> = T extends U ? T : never;
 
+export namespace GetProductsWithFacetValuesByIds {
+  export type Variables = GetProductsWithFacetValuesByIdsQueryVariables;
+  export type Query = GetProductsWithFacetValuesByIdsQuery;
+  export type Products = (NonNullable<GetProductsWithFacetValuesByIdsQuery['products']>);
+  export type Items = NonNullable<(NonNullable<(NonNullable<GetProductsWithFacetValuesByIdsQuery['products']>)['items']>)[number]>;
+  export type FacetValues = NonNullable<(NonNullable<NonNullable<(NonNullable<(NonNullable<GetProductsWithFacetValuesByIdsQuery['products']>)['items']>)[number]>['facetValues']>)[number]>;
+  export type Facet = (NonNullable<NonNullable<(NonNullable<NonNullable<(NonNullable<(NonNullable<GetProductsWithFacetValuesByIdsQuery['products']>)['items']>)[number]>['facetValues']>)[number]>['facet']>);
+}
+
+export namespace GetVariantsWithFacetValuesByIds {
+  export type Variables = GetVariantsWithFacetValuesByIdsQueryVariables;
+  export type Query = GetVariantsWithFacetValuesByIdsQuery;
+  export type ProductVariants = (NonNullable<GetVariantsWithFacetValuesByIdsQuery['productVariants']>);
+  export type Items = NonNullable<(NonNullable<(NonNullable<GetVariantsWithFacetValuesByIdsQuery['productVariants']>)['items']>)[number]>;
+  export type FacetValues = NonNullable<(NonNullable<NonNullable<(NonNullable<(NonNullable<GetVariantsWithFacetValuesByIdsQuery['productVariants']>)['items']>)[number]>['facetValues']>)[number]>;
+  export type Facet = (NonNullable<NonNullable<(NonNullable<NonNullable<(NonNullable<(NonNullable<GetVariantsWithFacetValuesByIdsQuery['productVariants']>)['items']>)[number]>['facetValues']>)[number]>['facet']>);
+}
+
+export namespace UpdateProductsBulk {
+  export type Variables = UpdateProductsBulkMutationVariables;
+  export type Mutation = UpdateProductsBulkMutation;
+  export type UpdateProducts = NonNullable<(NonNullable<UpdateProductsBulkMutation['updateProducts']>)[number]>;
+  export type FacetValues = NonNullable<(NonNullable<NonNullable<(NonNullable<UpdateProductsBulkMutation['updateProducts']>)[number]>['facetValues']>)[number]>;
+}
+
+export namespace UpdateVariantsBulk {
+  export type Variables = UpdateVariantsBulkMutationVariables;
+  export type Mutation = UpdateVariantsBulkMutation;
+  export type UpdateProductVariants = NonNullable<(NonNullable<UpdateVariantsBulkMutation['updateProductVariants']>)[number]>;
+  export type FacetValues = NonNullable<(NonNullable<NonNullable<(NonNullable<UpdateVariantsBulkMutation['updateProductVariants']>)[number]>['facetValues']>)[number]>;
+}
+
 export namespace Role {
   export type Fragment = RoleFragment;
   export type Channels = NonNullable<(NonNullable<RoleFragment['channels']>)[number]>;

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Zobrazit jako tabulku",
     "drop-files-to-upload": "Přetáhněte soubory k nahrávání",
     "duplicate-sku-warning": "",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Rozevřít všechny kolekce",
     "facet-values": "Hodnoty atributů",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Nenastaveno",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Vyskytla se chyba, nebylo vytvořeno: { entity }",
     "notify-create-success": "Vytvořeno: { entity }",
     "notify-delete-error": "Vyskytla se chyba, nebylo smazáno: { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Tabellenansicht",
     "drop-files-to-upload": "Dateien zum Hochladen ablegen",
     "duplicate-sku-warning": "",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Alle Sammlungen erweitern",
     "facet-values": "Facettenwerte",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Nicht festgelegt",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Ein Fehler ist aufgetreten, { entity } konnte nicht erstellt werden",
     "notify-create-success": "{ entity } erstellt",
     "notify-delete-error": "Ein Fehler ist aufgetreten, { entity } konnte nicht gelöscht werden",

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

@@ -95,6 +95,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-facet-values": "Edit facet values",
     "edit-options": "Edit options",
     "expand-all-collections": "Expand all collections",
     "facet-values": "Facet values",
@@ -229,6 +230,7 @@
     "not-applicable": "Not applicable",
     "not-set": "Not set",
     "notify-bulk-delete-success": "Deleted { count } { entity }",
+    "notify-bulk-update-success": "Updated { count } { entity }",
     "notify-create-error": "An error occurred, could not create { entity }",
     "notify-create-success": "Created new { entity }",
     "notify-delete-error": "An error occurred, could not delete { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Ver como tabla",
     "drop-files-to-upload": "Arrastra recursos para subirlos",
     "duplicate-sku-warning": "Por favor, asegúrese de que todos los códigos de referencia son únicos",
+    "edit-facet-values": "",
     "edit-options": "Editar opciones",
     "expand-all-collections": "Expandir todas las colecciones",
     "facet-values": "Valores de faceta",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Sin fijar",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Ha ocurrido un problema, imposible de crear { entity }",
     "notify-create-success": "Creado nuevo { entity }",
     "notify-delete-error": "Ha ocurrido un problema, imposible de eliminar { entity }",

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

@@ -95,6 +95,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-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Etendre toutes les collections",
     "facet-values": "Valeurs de composant",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Non défini",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Une erreur est survenue, création de { entity } échouée",
     "notify-create-success": "Nouveau { entity } créé",
     "notify-delete-error": "Une erreur est survenue, suppression de { entity } échouée",

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

@@ -95,6 +95,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-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Espandi le collezioni",
     "facet-values": "Valori attributo",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Non impostato",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Si è verificato un errore, impossibile creare { entity }",
     "notify-create-success": "Creato nuovo { entity }",
     "notify-delete-error": "Si è verificato un errore, impossibile cancellare { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Wyświetl jako tabele",
     "drop-files-to-upload": "Upuść pliki do uploadu",
     "duplicate-sku-warning": "",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Rozwiń wszystkie kolekcje",
     "facet-values": "Wartości faseta",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Nie ustawione",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Wystąpił błąd, nie można utworzyć { entity }",
     "notify-create-success": "Utworzono { entity }",
     "notify-delete-error": "Wystąpił błąd, nie można usunąć { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Ver como tabela",
     "drop-files-to-upload": "Soltar arquivos para envio",
     "duplicate-sku-warning": "",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Expandir todas as categorias",
     "facet-values": "Valor da Etiqueta",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Não configurado",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Ocorreu um erro, não foi possível criar { entity }",
     "notify-create-success": "Criado novo { entity }",
     "notify-delete-error": "Ocorreu um erro, não foi possível excluir { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Lista simplificada",
     "drop-files-to-upload": "Colocar ficheiros para enviar",
     "duplicate-sku-warning": "Certifique-se de que todos os SKUs sejam únicos",
+    "edit-facet-values": "",
     "edit-options": "Editar opções",
     "expand-all-collections": "Expandir todas as categorias",
     "facet-values": "Valor da Etiqueta",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Não configurado",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Ocorreu um erro. Não foi possível criar { entity }",
     "notify-create-success": "Novo(a) { entity } adicionado(a)",
     "notify-delete-error": "Ocorreu um erro, não foi possível eliminar { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Просмотр в виде таблицы",
     "drop-files-to-upload": "Перетащите файлы для загрузки",
     "duplicate-sku-warning": "Убедитесь, что все артикулы (SKU) уникальны",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Развернуть все коллекции",
     "facet-values": "Значения тега",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Не задано",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Ошибка, не удалось создать { entity }",
     "notify-create-success": "Создано новое { entity }",
     "notify-delete-error": "Ошибка, не удалось удалить { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "Перегляд у вигляді таблиці",
     "drop-files-to-upload": "Перетягніть файли для завантаження",
     "duplicate-sku-warning": "Переконайтесь, що всі артикули (SKU) унікальні",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "Розгорнути всі колекції",
     "facet-values": "Значення тегу",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "Не задано",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "Помилка, не вдалося створити { entity }",
     "notify-create-success": "Створено нове { entity }",
     "notify-delete-error": "Помилка, не вдалося видалити { entity }",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "表格显示",
     "drop-files-to-upload": "拖拽文件上传",
     "duplicate-sku-warning": "",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "展开所有系列",
     "facet-values": "特征值列表",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "未设置",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "添加{ entity }失败",
     "notify-create-success": "{ entity }已添加",
     "notify-delete-error": "删除{ entity }失败",

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

@@ -95,6 +95,7 @@
     "display-variant-table": "表格顯示",
     "drop-files-to-upload": "拖拽文件上傳",
     "duplicate-sku-warning": "",
+    "edit-facet-values": "",
     "edit-options": "",
     "expand-all-collections": "展開所有系列",
     "facet-values": "特徵值列表",
@@ -229,6 +230,7 @@
     "not-applicable": "",
     "not-set": "未設定",
     "notify-bulk-delete-success": "",
+    "notify-bulk-update-success": "",
     "notify-create-error": "新增{ entity }失敗",
     "notify-create-success": "{ entity }已新增",
     "notify-delete-error": "移除{ entity }失敗",