Browse Source

feat(admin-ui): Implement deletion of FacetValues

Michael Bromley 7 years ago
parent
commit
6de4f63d4d

+ 3 - 0
admin-ui/src/app/catalog/catalog.module.ts

@@ -12,6 +12,7 @@ import { AssetListComponent } from './components/asset-list/asset-list.component
 import { AssetPickerDialogComponent } from './components/asset-picker-dialog/asset-picker-dialog.component';
 import { CreateOptionGroupDialogComponent } from './components/create-option-group-dialog/create-option-group-dialog.component';
 import { CreateOptionGroupFormComponent } from './components/create-option-group-form/create-option-group-form.component';
+import { DeleteFacetValueDialogComponent } from './components/delete-facet-value-dialog/delete-facet-value-dialog.component';
 import { FacetDetailComponent } from './components/facet-detail/facet-detail.component';
 import { FacetListComponent } from './components/facet-list/facet-list.component';
 import { GenerateProductVariantsComponent } from './components/generate-product-variants/generate-product-variants.component';
@@ -57,12 +58,14 @@ import { ProductResolver } from './providers/routing/product-resolver';
         ProductCategoryDetailComponent,
         ProductCategoryTreeComponent,
         ProductCategoryTreeNodeComponent,
+        DeleteFacetValueDialogComponent,
     ],
     entryComponents: [
         AssetPickerDialogComponent,
         CreateOptionGroupDialogComponent,
         SelectOptionGroupDialogComponent,
         ApplyFacetDialogComponent,
+        DeleteFacetValueDialogComponent,
     ],
     providers: [ProductResolver, FacetResolver, ProductCategoryResolver],
 })

+ 8 - 0
admin-ui/src/app/catalog/components/delete-facet-value-dialog/delete-facet-value-dialog.component.html

@@ -0,0 +1,8 @@
+<ng-template vdrDialogTitle>{{ 'catalog.delete-facet-value' | translate }}</ng-template>
+{{ message }}
+<ng-template vdrDialogButtons>
+    <button type="button" class="btn" (click)="cancel()">{{ 'common.cancel' | translate }}</button>
+    <button type="submit" (click)="confirm()" class="btn btn-danger">
+        {{ 'common.delete' | translate }}
+    </button>
+</ng-template>

+ 0 - 0
admin-ui/src/app/catalog/components/delete-facet-value-dialog/delete-facet-value-dialog.component.scss


+ 22 - 0
admin-ui/src/app/catalog/components/delete-facet-value-dialog/delete-facet-value-dialog.component.ts

@@ -0,0 +1,22 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+
+import { Dialog } from '../../../shared/providers/modal/modal.service';
+
+@Component({
+    selector: 'vdr-delete-facet-value-dialog',
+    templateUrl: './delete-facet-value-dialog.component.html',
+    styleUrls: ['./delete-facet-value-dialog.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DeleteFacetValueDialogComponent implements Dialog<boolean> {
+    resolveWith: (result?: boolean) => void;
+    message: string;
+
+    confirm() {
+        this.resolveWith(true);
+    }
+
+    cancel() {
+        this.resolveWith();
+    }
+}

+ 20 - 1
admin-ui/src/app/catalog/components/facet-detail/facet-detail.component.html

@@ -67,11 +67,12 @@
                     <ng-container *ngFor="let customField of customValueFields">
                         <th>{{ customField.name }}</th>
                     </ng-container>
+                    <th></th>
                 </tr>
             </thead>
             <tbody>
                 <tr
-                    class="variant"
+                    class="facet-value"
                     *ngFor="let value of getValuesFormArray().controls; let i = index"
                     [formGroupName]="i"
                 >
@@ -93,6 +94,24 @@
                             ></vdr-custom-field-control>
                         </td>
                     </ng-container>
+                    <td class="actions">
+                        <clr-dropdown>
+                            <button type="button" class="btn btn-link btn-sm" clrDropdownTrigger>
+                                {{ 'common.actions' | translate }}
+                                <clr-icon shape="caret down"></clr-icon>
+                            </button>
+                            <clr-dropdown-menu *clrIfOpen clrPosition="bottom-right">
+                                <button
+                                    type="button"
+                                    class="delete-button"
+                                    (click)="deleteFacetValue(facet.values[i].id)"
+                                    clrDropdownItem
+                                >
+                                    {{ 'common.delete' | translate }}
+                                </button>
+                            </clr-dropdown-menu>
+                        </clr-dropdown>
+                    </td>
                 </tr>
             </tbody>
         </table>

+ 9 - 0
admin-ui/src/app/catalog/components/facet-detail/facet-detail.component.scss

@@ -0,0 +1,9 @@
+@import "variables";
+
+.actions button {
+    line-height: 24px;
+}
+
+.delete-button {
+    color: #e12200;
+}

+ 58 - 2
admin-ui/src/app/catalog/components/facet-detail/facet-detail.component.ts

@@ -1,10 +1,11 @@
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
 import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
 import { ActivatedRoute, Router } from '@angular/router';
-import { combineLatest, forkJoin, Observable } from 'rxjs';
-import { map, mergeMap, take } from 'rxjs/operators';
+import { combineLatest, EMPTY, forkJoin, Observable } from 'rxjs';
+import { map, mergeMap, switchMap, take } from 'rxjs/operators';
 import {
     CreateFacetValueInput,
+    DeletionResult,
     FacetWithValues,
     LanguageCode,
     UpdateFacetValueInput,
@@ -19,6 +20,8 @@ import { _ } from '../../../core/providers/i18n/mark-for-extraction';
 import { NotificationService } from '../../../core/providers/notification/notification.service';
 import { DataService } from '../../../data/providers/data.service';
 import { ServerConfigService } from '../../../data/server-config';
+import { ModalService } from '../../../shared/providers/modal/modal.service';
+import { DeleteFacetValueDialogComponent } from '../delete-facet-value-dialog/delete-facet-value-dialog.component';
 
 @Component({
     selector: 'vdr-facet-detail',
@@ -42,6 +45,7 @@ export class FacetDetailComponent extends BaseDetailComponent<FacetWithValues.Fr
         private dataService: DataService,
         private formBuilder: FormBuilder,
         private notificationService: NotificationService,
+        private modalService: ModalService,
     ) {
         super(route, router, serverConfigService);
         this.customFields = this.getCustomFieldConfig('Facet');
@@ -186,6 +190,58 @@ export class FacetDetailComponent extends BaseDetailComponent<FacetWithValues.Fr
             );
     }
 
+    deleteFacetValue(facetValueId: string) {
+        this.showDeleteFacetValueModalAndDelete(facetValueId)
+            .pipe(
+                switchMap(response => {
+                    if (response.deleteFacetValues[0].result === DeletionResult.DELETED) {
+                        return [true];
+                    } else {
+                        return this.showDeleteFacetValueModalAndDelete(
+                            facetValueId,
+                            response.deleteFacetValues[0].message || '',
+                        ).pipe(map(r => r.deleteFacetValues[0].result === DeletionResult.DELETED));
+                    }
+                }),
+                switchMap(deleted => {
+                    if (deleted) {
+                        return this.dataService.facet.getFacet(this.id).single$;
+                    } else {
+                        return [];
+                    }
+                }),
+            )
+            .subscribe(
+                () => {
+                    this.notificationService.success(_('common.notify-delete-success'), {
+                        entity: 'FacetValue',
+                    });
+                },
+                err => {
+                    this.notificationService.error(_('common.notify-delete-error'), {
+                        entity: 'FacetValue',
+                    });
+                },
+            );
+    }
+
+    private showDeleteFacetValueModalAndDelete(facetValueId: string, message?: string) {
+        return this.modalService
+            .fromComponent(DeleteFacetValueDialogComponent, {
+                locals: {
+                    message,
+                },
+            })
+            .pipe(
+                switchMap(result => {
+                    if (result) {
+                        return this.dataService.facet.deleteFacetValues([facetValueId], !!message);
+                    }
+                    return EMPTY;
+                }),
+            );
+    }
+
     /**
      * Sets the values of the form on changes to the facet or current language.
      */

+ 9 - 0
admin-ui/src/app/data/definitions/facet-definitions.ts

@@ -72,6 +72,15 @@ export const UPDATE_FACET_VALUES = gql`
     ${FACET_VALUE_FRAGMENT}
 `;
 
+export const DELETE_FACET_VALUES = gql`
+    mutation DeleteFacetValues($ids: [ID!]!, $force: Boolean) {
+        deleteFacetValues(ids: $ids, force: $force) {
+            result
+            message
+        }
+    }
+`;
+
 export const GET_FACET_LIST = gql`
     query GetFacetList($options: FacetListOptions, $languageCode: LanguageCode) {
         facets(languageCode: $languageCode, options: $options) {

+ 12 - 0
admin-ui/src/app/data/providers/facet-data.service.ts

@@ -3,6 +3,7 @@ import {
     CreateFacetInput,
     CreateFacetValueInput,
     CreateFacetValues,
+    DeleteFacetValues,
     GetFacetList,
     GetFacetWithValues,
     UpdateFacet,
@@ -16,6 +17,7 @@ import { getDefaultLanguage } from '../../common/utilities/get-default-language'
 import {
     CREATE_FACET,
     CREATE_FACET_VALUES,
+    DELETE_FACET_VALUES,
     GET_FACET_LIST,
     GET_FACET_WITH_VALUES,
     UPDATE_FACET,
@@ -80,4 +82,14 @@ export class FacetDataService {
             input,
         );
     }
+
+    deleteFacetValues(ids: string[], force: boolean) {
+        return this.baseDataService.mutate<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
+            DELETE_FACET_VALUES,
+            {
+                ids,
+                force,
+            },
+        );
+    }
 }

+ 4 - 0
admin-ui/src/app/shared/components/facet-value-chip/facet-value-chip.component.scss

@@ -1,5 +1,9 @@
 @import "variables";
 
+:host {
+    display: inline-block;
+}
+
 .facet-name {
     color: transparentize($color-grey-1, 0.5);
     text-transform: uppercase;

+ 5 - 0
admin-ui/src/i18n-messages/en.json

@@ -34,6 +34,7 @@
     "create-new-option-group": "Create new option group",
     "create-new-product": "Create new product",
     "create-new-product-category": "Create new product category",
+    "delete-facet-value": "Delete FacetValue",
     "drop-files-to-upload": "Drop files to upload",
     "facet": "Facet",
     "facet-values": "Facet values",
@@ -79,6 +80,7 @@
   },
   "common": {
     "ID": "ID",
+    "actions": "Actions",
     "available-languages": "Available languages",
     "back": "Back",
     "cancel": "Cancel",
@@ -90,6 +92,7 @@
     "created": "Created",
     "created-at": "Created at",
     "custom-fields": "Custom fields",
+    "delete": "Delete",
     "description": "Description",
     "discard-changes": "Discard changes",
     "done": "Done",
@@ -108,6 +111,8 @@
     "no-results": "No results",
     "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 }",
+    "notify-delete-success": "Deleted { entity }",
     "notify-save-changes-error": "An error occurred, could not save changes",
     "notify-saved-changes": "Saved changes",
     "notify-update-error": "An error occurred, could not update { entity }",

+ 2 - 2
server/e2e/facet.e2e-spec.ts

@@ -218,7 +218,7 @@ describe('Facet resolver', () => {
             expect(result1.deleteFacetValues).toEqual([
                 {
                     result: DeletionResult.NOT_DELETED,
-                    message: `The selected FacetValue is assigned to 1 Product, 1 ProductVariant. To delete anyway, set "force: true"`,
+                    message: `The selected FacetValue is assigned to 1 Product, 1 ProductVariant`,
                 },
             ]);
 
@@ -269,7 +269,7 @@ describe('Facet resolver', () => {
 
             expect(result1.deleteFacet).toEqual({
                 result: DeletionResult.NOT_DELETED,
-                message: `The selected Facet includes FacetValues which are assigned to 1 Product. To delete anyway, set "force: true"`,
+                message: `The selected Facet includes FacetValues which are assigned to 1 Product`,
             });
 
             expect(result2.facet).not.toBe(null);

+ 2 - 2
server/src/i18n/messages/en.json

@@ -25,9 +25,9 @@
   "message": {
     "country-used-in-addresses": "The selected Country cannot be deleted as it is used in {count, plural, one {1 Address} other {# Addresses}}",
     "facet-force-deleted": "The Facet was deleted and its FacetValues were removed from {products, plural, =0 {} one {1 Product} other {# Products}}{both, select, both { , } single {}}{variants, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}}",
-    "facet-used": "The selected Facet includes FacetValues which are assigned to {products, plural, =0 {} one {1 Product} other {# Products}}{both, select, both { , } single {}}{variants, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}}. To delete anyway, set \"force: true\"",
+    "facet-used": "The selected Facet includes FacetValues which are assigned to {products, plural, =0 {} one {1 Product} other {# Products}}{both, select, both { , } single {}}{variants, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}}",
     "facet-value-force-deleted": "The selected FacetValue was removed from {products, plural, =0 {} one {1 Product} other {# Products}}{both, select, both { , } single {}}{variants, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}} and deleted",
-    "facet-value-used": "The selected FacetValue is assigned to {products, plural, =0 {} one {1 Product} other {# Products}}{both, select, both { , } single {}}{variants, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}}. To delete anyway, set \"force: true\"",
+    "facet-value-used": "The selected FacetValue is assigned to {products, plural, =0 {} one {1 Product} other {# Products}}{both, select, both { , } single {}}{variants, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}}",
     "zone-used-in-tax-rates": "The selected Zone cannot be deleted as it is used in the following TaxRates: { taxRateNames }"
   }
 }

+ 18 - 0
shared/generated-types.ts

@@ -5708,6 +5708,24 @@ export namespace UpdateFacetValues {
     export type UpdateFacetValues = FacetValue.Fragment;
 }
 
+export namespace DeleteFacetValues {
+    export type Variables = {
+        ids: string[];
+        force?: boolean | null;
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        deleteFacetValues: DeleteFacetValues[];
+    };
+
+    export type DeleteFacetValues = {
+        __typename?: 'DeletionResponse';
+        result: DeletionResult;
+        message?: string | null;
+    };
+}
+
 export namespace GetFacetList {
     export type Variables = {
         options?: FacetListOptions | null;