diff --git a/admin-ui/src/app/catalog/components/product-detail/product-detail.component.html b/admin-ui/src/app/catalog/components/product-detail/product-detail.component.html index 55fc3ad914..fa1eeee06e 100644 --- a/admin-ui/src/app/catalog/components/product-detail/product-detail.component.html +++ b/admin-ui/src/app/catalog/components/product-detail/product-detail.component.html @@ -155,6 +155,7 @@

{{ 'catalog.product-variants' | translate }}

(updateProductOption)="updateProductOption($event)" (selectionChange)="selectedVariantIds = $event" (selectFacetValueClick)="selectVariantFacetValue($event)" + (deleteVariant)="deleteVariant($event)" > diff --git a/admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts b/admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts index b8d40dd471..d86262a847 100644 --- a/admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts +++ b/admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts @@ -2,8 +2,8 @@ import { Location } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { combineLatest, merge, Observable } from 'rxjs'; -import { distinctUntilChanged, map, mergeMap, take, withLatestFrom } from 'rxjs/operators'; +import { combineLatest, EMPTY, merge, Observable } from 'rxjs'; +import { distinctUntilChanged, map, mergeMap, switchMap, take, withLatestFrom } from 'rxjs/operators'; import { normalizeString } from 'shared/normalize-string'; import { CustomFieldConfig } from 'shared/shared-types'; import { notNullOrUndefined } from 'shared/shared-utils'; @@ -253,6 +253,34 @@ export class ProductDetailComponent extends BaseDetailComponent + response ? this.productDetailService.deleteProductVariant(id, this.id) : EMPTY, + ), + ) + .subscribe( + () => { + this.notificationService.success(_('common.notify-delete-success'), { + entity: 'ProductVariant', + }); + }, + err => { + this.notificationService.error(_('common.notify-delete-error'), { + entity: 'ProductVariant', + }); + }, + ); + } + private displayFacetValueModal(): Observable { return this.productDetailService.getFacets().pipe( mergeMap(facets => diff --git a/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.html b/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.html index 7602794361..efdf2627d2 100644 --- a/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.html +++ b/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.html @@ -28,11 +28,27 @@ -
+
+ + + + + +
diff --git a/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.scss b/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.scss index 203ac6a9a9..d8a2cb7bf2 100644 --- a/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.scss +++ b/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.scss @@ -59,6 +59,10 @@ } } + .right-controls { + display: flex; + } + .variant-form-input-row { display: flex; flex-wrap: wrap; diff --git a/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.ts b/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.ts index 33dc8a0606..5d4cc7c61a 100644 --- a/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.ts +++ b/admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.ts @@ -48,6 +48,7 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro @Output() selectionChange = new EventEmitter(); @Output() selectFacetValueClick = new EventEmitter(); @Output() updateProductOption = new EventEmitter(); + @Output() deleteVariant = new EventEmitter(); selectedVariantIds: string[] = []; private facetValues: FacetValue.Fragment[]; private formSubscription: Subscription; @@ -163,6 +164,10 @@ export class ProductVariantsListComponent implements OnChanges, OnInit, OnDestro }); } + deleteVariantClick(id: string) { + this.deleteVariant.emit(id); + } + private getFacetValueIds(index: number): string[] { const formValue: VariantFormValue = this.formArray.at(index).value; return formValue.facetValueIds; diff --git a/admin-ui/src/app/catalog/providers/product-detail.service.ts b/admin-ui/src/app/catalog/providers/product-detail.service.ts index 45508a37d3..32a987cfd3 100644 --- a/admin-ui/src/app/catalog/providers/product-detail.service.ts +++ b/admin-ui/src/app/catalog/providers/product-detail.service.ts @@ -1,11 +1,12 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject, forkJoin, Observable, of } from 'rxjs'; -import { map, mergeMap, shareReplay, skip } from 'rxjs/operators'; +import { BehaviorSubject, forkJoin, Observable, of, throwError } from 'rxjs'; +import { map, mergeMap, shareReplay, skip, switchMap } from 'rxjs/operators'; import { normalizeString } from 'shared/normalize-string'; import { CreateProductInput, CreateProductVariantInput, + DeletionResult, FacetWithValues, LanguageCode, UpdateProductInput, @@ -145,4 +146,16 @@ export class ProductDetailService { updateProductOption(input: UpdateProductOptionInput) { return this.dataService.product.updateProductOption(input); } + + deleteProductVariant(id: string, productId: string) { + return this.dataService.product.deleteProductVariant(id).pipe( + switchMap(result => { + if (result.deleteProductVariant.result === DeletionResult.DELETED) { + return this.dataService.product.getProduct(productId).single$; + } else { + return throwError(result.deleteProductVariant.message); + } + }), + ); + } } diff --git a/admin-ui/src/app/common/generated-types.ts b/admin-ui/src/app/common/generated-types.ts index 4293619352..a3ef6e1903 100644 --- a/admin-ui/src/app/common/generated-types.ts +++ b/admin-ui/src/app/common/generated-types.ts @@ -1553,32 +1553,18 @@ export type Mutation = { assignRoleToAdministrator: Administrator, /** Create a new Asset */ createAssets: Array, - login: LoginResult, - logout: Scalars['Boolean'], - /** Create a new Channel */ - createChannel: Channel, - /** Update an existing Channel */ - updateChannel: Channel, /** Create a new Collection */ createCollection: Collection, /** Update an existing Collection */ updateCollection: Collection, /** Move a Collection to a different parent or index */ moveCollection: Collection, - /** Create a new Country */ - createCountry: Country, - /** Update an existing Country */ - updateCountry: Country, - /** Delete a Country */ - deleteCountry: DeletionResponse, - /** Create a new CustomerGroup */ - createCustomerGroup: CustomerGroup, - /** Update an existing CustomerGroup */ - updateCustomerGroup: CustomerGroup, - /** Add Customers to a CustomerGroup */ - addCustomersToGroup: CustomerGroup, - /** Remove Customers from a CustomerGroup */ - removeCustomersFromGroup: CustomerGroup, + /** Create a new Channel */ + createChannel: Channel, + /** Update an existing Channel */ + updateChannel: Channel, + login: LoginResult, + logout: Scalars['Boolean'], /** Create a new Customer. If a password is provided, a new User will also be created an linked to the Customer. */ createCustomer: Customer, /** Update an existing Customer */ @@ -1591,6 +1577,12 @@ export type Mutation = { updateCustomerAddress: Address, /** Update an existing Address */ deleteCustomerAddress: Scalars['Boolean'], + /** Create a new Country */ + createCountry: Country, + /** Update an existing Country */ + updateCountry: Country, + /** Delete a Country */ + deleteCountry: DeletionResponse, /** Create a new Facet */ createFacet: Facet, /** Update an existing Facet */ @@ -1603,14 +1595,16 @@ export type Mutation = { updateFacetValues: Array, /** Delete one or more FacetValues */ deleteFacetValues: Array, - importProducts?: Maybe, + /** Create a new CustomerGroup */ + createCustomerGroup: CustomerGroup, + /** Update an existing CustomerGroup */ + updateCustomerGroup: CustomerGroup, + /** Add Customers to a CustomerGroup */ + addCustomersToGroup: CustomerGroup, + /** Remove Customers from a CustomerGroup */ + removeCustomersFromGroup: CustomerGroup, updateGlobalSettings: GlobalSettings, - settlePayment: Payment, - fulfillOrder: Fulfillment, - cancelOrder: Order, - refundOrder: Refund, - settleRefund: Refund, - addNoteToOrder: Order, + importProducts?: Maybe, /** Update an existing PaymentMethod */ updatePaymentMethod: PaymentMethod, /** Create a new ProductOptionGroup */ @@ -1621,7 +1615,12 @@ export type Mutation = { createProductOption: ProductOption, /** Create a new ProductOption within a ProductOptionGroup */ updateProductOption: ProductOption, - reindex: JobInfo, + settlePayment: Payment, + fulfillOrder: Fulfillment, + cancelOrder: Order, + refundOrder: Refund, + settleRefund: Refund, + addNoteToOrder: Order, /** Create a new Product */ createProduct: Product, /** Update an existing Product */ @@ -1641,22 +1640,23 @@ export type Mutation = { createPromotion: Promotion, updatePromotion: Promotion, deletePromotion: DeletionResponse, - /** Create a new Role */ - createRole: Role, - /** Update an existing Role */ - updateRole: Role, + reindex: JobInfo, /** Create a new ShippingMethod */ createShippingMethod: ShippingMethod, /** Update an existing ShippingMethod */ updateShippingMethod: ShippingMethod, - /** Create a new TaxCategory */ - createTaxCategory: TaxCategory, - /** Update an existing TaxCategory */ - updateTaxCategory: TaxCategory, /** Create a new TaxRate */ createTaxRate: TaxRate, /** Update an existing TaxRate */ updateTaxRate: TaxRate, + /** Create a new Role */ + createRole: Role, + /** Update an existing Role */ + updateRole: Role, + /** Create a new TaxCategory */ + createTaxCategory: TaxCategory, + /** Update an existing TaxCategory */ + updateTaxCategory: TaxCategory, /** Create a new Zone */ createZone: Zone, /** Update an existing Zone */ @@ -1696,23 +1696,6 @@ export type MutationCreateAssetsArgs = { }; -export type MutationLoginArgs = { - username: Scalars['String'], - password: Scalars['String'], - rememberMe?: Maybe -}; - - -export type MutationCreateChannelArgs = { - input: CreateChannelInput -}; - - -export type MutationUpdateChannelArgs = { - input: UpdateChannelInput -}; - - export type MutationCreateCollectionArgs = { input: CreateCollectionInput }; @@ -1728,40 +1711,20 @@ export type MutationMoveCollectionArgs = { }; -export type MutationCreateCountryArgs = { - input: CreateCountryInput -}; - - -export type MutationUpdateCountryArgs = { - input: UpdateCountryInput -}; - - -export type MutationDeleteCountryArgs = { - id: Scalars['ID'] -}; - - -export type MutationCreateCustomerGroupArgs = { - input: CreateCustomerGroupInput -}; - - -export type MutationUpdateCustomerGroupArgs = { - input: UpdateCustomerGroupInput +export type MutationCreateChannelArgs = { + input: CreateChannelInput }; -export type MutationAddCustomersToGroupArgs = { - customerGroupId: Scalars['ID'], - customerIds: Array +export type MutationUpdateChannelArgs = { + input: UpdateChannelInput }; -export type MutationRemoveCustomersFromGroupArgs = { - customerGroupId: Scalars['ID'], - customerIds: Array +export type MutationLoginArgs = { + username: Scalars['String'], + password: Scalars['String'], + rememberMe?: Maybe }; @@ -1797,6 +1760,21 @@ export type MutationDeleteCustomerAddressArgs = { }; +export type MutationCreateCountryArgs = { + input: CreateCountryInput +}; + + +export type MutationUpdateCountryArgs = { + input: UpdateCountryInput +}; + + +export type MutationDeleteCountryArgs = { + id: Scalars['ID'] +}; + + export type MutationCreateFacetArgs = { input: CreateFacetInput }; @@ -1829,43 +1807,35 @@ export type MutationDeleteFacetValuesArgs = { }; -export type MutationImportProductsArgs = { - csvFile: Scalars['Upload'] -}; - - -export type MutationUpdateGlobalSettingsArgs = { - input: UpdateGlobalSettingsInput -}; - - -export type MutationSettlePaymentArgs = { - id: Scalars['ID'] +export type MutationCreateCustomerGroupArgs = { + input: CreateCustomerGroupInput }; -export type MutationFulfillOrderArgs = { - input: FulfillOrderInput +export type MutationUpdateCustomerGroupArgs = { + input: UpdateCustomerGroupInput }; -export type MutationCancelOrderArgs = { - input: CancelOrderInput +export type MutationAddCustomersToGroupArgs = { + customerGroupId: Scalars['ID'], + customerIds: Array }; -export type MutationRefundOrderArgs = { - input: RefundOrderInput +export type MutationRemoveCustomersFromGroupArgs = { + customerGroupId: Scalars['ID'], + customerIds: Array }; -export type MutationSettleRefundArgs = { - input: SettleRefundInput +export type MutationUpdateGlobalSettingsArgs = { + input: UpdateGlobalSettingsInput }; -export type MutationAddNoteToOrderArgs = { - input: AddNoteToOrderInput +export type MutationImportProductsArgs = { + csvFile: Scalars['Upload'] }; @@ -1894,6 +1864,36 @@ export type MutationUpdateProductOptionArgs = { }; +export type MutationSettlePaymentArgs = { + id: Scalars['ID'] +}; + + +export type MutationFulfillOrderArgs = { + input: FulfillOrderInput +}; + + +export type MutationCancelOrderArgs = { + input: CancelOrderInput +}; + + +export type MutationRefundOrderArgs = { + input: RefundOrderInput +}; + + +export type MutationSettleRefundArgs = { + input: SettleRefundInput +}; + + +export type MutationAddNoteToOrderArgs = { + input: AddNoteToOrderInput +}; + + export type MutationCreateProductArgs = { input: CreateProductInput }; @@ -1951,43 +1951,43 @@ export type MutationDeletePromotionArgs = { }; -export type MutationCreateRoleArgs = { - input: CreateRoleInput +export type MutationCreateShippingMethodArgs = { + input: CreateShippingMethodInput }; -export type MutationUpdateRoleArgs = { - input: UpdateRoleInput +export type MutationUpdateShippingMethodArgs = { + input: UpdateShippingMethodInput }; -export type MutationCreateShippingMethodArgs = { - input: CreateShippingMethodInput +export type MutationCreateTaxRateArgs = { + input: CreateTaxRateInput }; -export type MutationUpdateShippingMethodArgs = { - input: UpdateShippingMethodInput +export type MutationUpdateTaxRateArgs = { + input: UpdateTaxRateInput }; -export type MutationCreateTaxCategoryArgs = { - input: CreateTaxCategoryInput +export type MutationCreateRoleArgs = { + input: CreateRoleInput }; -export type MutationUpdateTaxCategoryArgs = { - input: UpdateTaxCategoryInput +export type MutationUpdateRoleArgs = { + input: UpdateRoleInput }; -export type MutationCreateTaxRateArgs = { - input: CreateTaxRateInput +export type MutationCreateTaxCategoryArgs = { + input: CreateTaxCategoryInput }; -export type MutationUpdateTaxRateArgs = { - input: UpdateTaxRateInput +export type MutationUpdateTaxCategoryArgs = { + input: UpdateTaxCategoryInput }; @@ -2533,47 +2533,47 @@ export type Query = { administrator?: Maybe, assets: AssetList, asset?: Maybe, - me?: Maybe, - channels: Array, - channel?: Maybe, - activeChannel: Channel, collections: CollectionList, collection?: Maybe, collectionFilters: Array, - countries: CountryList, - country?: Maybe, - customerGroups: Array, - customerGroup?: Maybe, + channels: Array, + channel?: Maybe, + activeChannel: Channel, + me?: Maybe, customers: CustomerList, customer?: Maybe, + countries: CountryList, + country?: Maybe, facets: FacetList, facet?: Maybe, + customerGroups: Array, + customerGroup?: Maybe, globalSettings: GlobalSettings, job?: Maybe, jobs: Array, - order?: Maybe, - orders: OrderList, paymentMethods: PaymentMethodList, paymentMethod?: Maybe, productOptionGroups: Array, productOptionGroup?: Maybe, - search: SearchResponse, + order?: Maybe, + orders: OrderList, products: ProductList, /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */ product?: Maybe, promotion?: Maybe, promotions: PromotionList, adjustmentOperations: AdjustmentOperations, - roles: RoleList, - role?: Maybe, + search: SearchResponse, shippingMethods: ShippingMethodList, shippingMethod?: Maybe, shippingEligibilityCheckers: Array, shippingCalculators: Array, - taxCategories: Array, - taxCategory?: Maybe, taxRates: TaxRateList, taxRate?: Maybe, + roles: RoleList, + role?: Maybe, + taxCategories: Array, + taxCategory?: Maybe, zones: Array, zone?: Maybe, networkStatus: NetworkStatus, @@ -2602,11 +2602,6 @@ export type QueryAssetArgs = { }; -export type QueryChannelArgs = { - id: Scalars['ID'] -}; - - export type QueryCollectionsArgs = { languageCode?: Maybe, options?: Maybe @@ -2619,27 +2614,27 @@ export type QueryCollectionArgs = { }; -export type QueryCountriesArgs = { - options?: Maybe +export type QueryChannelArgs = { + id: Scalars['ID'] }; -export type QueryCountryArgs = { - id: Scalars['ID'] +export type QueryCustomersArgs = { + options?: Maybe }; -export type QueryCustomerGroupArgs = { +export type QueryCustomerArgs = { id: Scalars['ID'] }; -export type QueryCustomersArgs = { - options?: Maybe +export type QueryCountriesArgs = { + options?: Maybe }; -export type QueryCustomerArgs = { +export type QueryCountryArgs = { id: Scalars['ID'] }; @@ -2656,6 +2651,11 @@ export type QueryFacetArgs = { }; +export type QueryCustomerGroupArgs = { + id: Scalars['ID'] +}; + + export type QueryJobArgs = { jobId: Scalars['String'] }; @@ -2666,16 +2666,6 @@ export type QueryJobsArgs = { }; -export type QueryOrderArgs = { - id: Scalars['ID'] -}; - - -export type QueryOrdersArgs = { - options?: Maybe -}; - - export type QueryPaymentMethodsArgs = { options?: Maybe }; @@ -2698,8 +2688,13 @@ export type QueryProductOptionGroupArgs = { }; -export type QuerySearchArgs = { - input: SearchInput +export type QueryOrderArgs = { + id: Scalars['ID'] +}; + + +export type QueryOrdersArgs = { + options?: Maybe }; @@ -2726,13 +2721,8 @@ export type QueryPromotionsArgs = { }; -export type QueryRolesArgs = { - options?: Maybe -}; - - -export type QueryRoleArgs = { - id: Scalars['ID'] +export type QuerySearchArgs = { + input: SearchInput }; @@ -2746,17 +2736,27 @@ export type QueryShippingMethodArgs = { }; -export type QueryTaxCategoryArgs = { +export type QueryTaxRatesArgs = { + options?: Maybe +}; + + +export type QueryTaxRateArgs = { id: Scalars['ID'] }; -export type QueryTaxRatesArgs = { - options?: Maybe +export type QueryRolesArgs = { + options?: Maybe }; -export type QueryTaxRateArgs = { +export type QueryRoleArgs = { + id: Scalars['ID'] +}; + + +export type QueryTaxCategoryArgs = { id: Scalars['ID'] }; @@ -3776,6 +3776,13 @@ export type UpdateProductOptionMutationVariables = { export type UpdateProductOptionMutation = ({ __typename?: 'Mutation' } & { updateProductOption: ({ __typename?: 'ProductOption' } & Pick) }); +export type DeleteProductVariantMutationVariables = { + id: Scalars['ID'] +}; + + +export type DeleteProductVariantMutation = ({ __typename?: 'Mutation' } & { deleteProductVariant: ({ __typename?: 'DeletionResponse' } & Pick) }); + export type ConfigurableOperationFragment = ({ __typename?: 'ConfigurableOperation' } & Pick & { args: Array<({ __typename?: 'ConfigArg' } & Pick)> }); export type PromotionFragment = ({ __typename?: 'Promotion' } & Pick & { conditions: Array<({ __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment)>, actions: Array<({ __typename?: 'ConfigurableOperation' } & ConfigurableOperationFragment)> }); @@ -4623,6 +4630,12 @@ export namespace UpdateProductOption { export type UpdateProductOption = UpdateProductOptionMutation['updateProductOption']; } +export namespace DeleteProductVariant { + export type Variables = DeleteProductVariantMutationVariables; + export type Mutation = DeleteProductVariantMutation; + export type DeleteProductVariant = DeleteProductVariantMutation['deleteProductVariant']; +} + export namespace ConfigurableOperation { export type Fragment = ConfigurableOperationFragment; export type Args = (NonNullable); diff --git a/admin-ui/src/app/data/definitions/product-definitions.ts b/admin-ui/src/app/data/definitions/product-definitions.ts index 2fc08b8ba4..8174229709 100644 --- a/admin-ui/src/app/data/definitions/product-definitions.ts +++ b/admin-ui/src/app/data/definitions/product-definitions.ts @@ -325,3 +325,12 @@ export const UPDATE_PRODUCT_OPTION = gql` } } `; + +export const DELETE_PRODUCT_VARIANT = gql` + mutation DeleteProductVariant($id: ID!) { + deleteProductVariant(id: $id) { + result + message + } + } +`; diff --git a/admin-ui/src/app/data/providers/product-data.service.ts b/admin-ui/src/app/data/providers/product-data.service.ts index b147f1dd75..ed199cf247 100644 --- a/admin-ui/src/app/data/providers/product-data.service.ts +++ b/admin-ui/src/app/data/providers/product-data.service.ts @@ -10,6 +10,7 @@ import { CreateProductVariantInput, CreateProductVariants, DeleteProduct, + DeleteProductVariant, GetAssetList, GetProductList, GetProductOptionGroups, @@ -33,6 +34,7 @@ import { CREATE_PRODUCT_OPTION_GROUP, CREATE_PRODUCT_VARIANTS, DELETE_PRODUCT, + DELETE_PRODUCT_VARIANT, GET_ASSET_LIST, GET_PRODUCT_LIST, GET_PRODUCT_OPTION_GROUPS, @@ -158,6 +160,15 @@ export class ProductDataService { ); } + deleteProductVariant(id: string) { + return this.baseDataService.mutate( + DELETE_PRODUCT_VARIANT, + { + id, + }, + ); + } + createProductOptionGroups(productOptionGroup: CreateProductOptionGroupInput) { const input: CreateProductOptionGroup.Variables = { input: productOptionGroup, diff --git a/admin-ui/src/i18n-messages/en.json b/admin-ui/src/i18n-messages/en.json index c100378429..4b4b8fc943 100644 --- a/admin-ui/src/i18n-messages/en.json +++ b/admin-ui/src/i18n-messages/en.json @@ -33,6 +33,7 @@ "confirm-delete-facet": "Delete facet?", "confirm-delete-facet-value": "Delete facet value?", "confirm-delete-product": "Delete product?", + "confirm-delete-product-variant": "Delete product variant?", "create-new-collection": "Create new collection", "create-new-facet": "Create new facet", "create-new-product": "New product",