Skip to content

Commit

Permalink
feat(admin-ui): Add product variant bulk actions (assign/delete chann…
Browse files Browse the repository at this point in the history
…el, delete) (#2238)

* fix(admin-ui): Fix product variant bulk action for deletion

* feat(admin-ui): Add bulk action for removing prod-variants from channel

* feat(admin-ui): Add bulk action for assigning product variants to channels

This also refactors the fetching of variants in the dialog component.
  • Loading branch information
DanielBiegler authored Jun 28, 2023
1 parent 60c9e86 commit b25ddcd
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 87 deletions.
8 changes: 8 additions & 0 deletions packages/admin-ui/src/lib/catalog/src/catalog.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ import { ProductListComponent } from './components/product-list/product-list.com
import { ProductOptionsEditorComponent } from './components/product-options-editor/product-options-editor.component';
import { ProductVariantDetailComponent } from './components/product-variant-detail/product-variant-detail.component';
import { ProductVariantListComponent } from './components/product-variant-list/product-variant-list.component';
import {
assignProductVariantsToChannelBulkAction,
removeProductVariantsFromChannelBulkAction,
deleteProductVariantsBulkAction,
} from './components/product-variant-list/product-variant-list-bulk-actions';
import { ProductVariantQuickJumpComponent } from './components/product-variant-quick-jump/product-variant-quick-jump.component';
import { ProductVariantsEditorComponent } from './components/product-variants-editor/product-variants-editor.component';
import { ProductVariantsTableComponent } from './components/product-variants-table/product-variants-table.component';
Expand Down Expand Up @@ -127,8 +132,11 @@ export class CatalogModule {
) {
bulkActionRegistryService.registerBulkAction(assignFacetValuesToProductsBulkAction);
bulkActionRegistryService.registerBulkAction(assignProductsToChannelBulkAction);
bulkActionRegistryService.registerBulkAction(assignProductVariantsToChannelBulkAction);
bulkActionRegistryService.registerBulkAction(removeProductsFromChannelBulkAction);
bulkActionRegistryService.registerBulkAction(removeProductVariantsFromChannelBulkAction);
bulkActionRegistryService.registerBulkAction(deleteProductsBulkAction);
bulkActionRegistryService.registerBulkAction(deleteProductVariantsBulkAction);

bulkActionRegistryService.registerBulkAction(assignFacetsToChannelBulkAction);
bulkActionRegistryService.registerBulkAction(removeFacetsFromChannelBulkAction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
Dialog,
GetChannelsQuery,
ItemOf,
LogicalOperator,
NotificationService,
ProductVariantFragment,
} from '@vendure/admin-ui/core';
import { combineLatest, from, Observable } from 'rxjs';
import { combineLatest, from, lastValueFrom, Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

type Channel = ItemOf<GetChannelsQuery, 'channels'>;
Expand Down Expand Up @@ -115,21 +115,18 @@ export class AssignProductsToChannelDialogComponent implements OnInit, Dialog<an
this.resolveWith();
}

private async getTopVariants(take: number): Promise<ProductVariantFragment[]> {
const variants: ProductVariantFragment[] = [];

for (let i = 0; i < this.productIds.length && variants.length < take; i++) {
const productVariants = await this.dataService.product
.getProduct(this.productIds[i], { take: this.isProductVariantMode ? undefined : take })
.mapSingle(({ product }) => {
const _variants = product ? product.variantList.items : [];
return _variants.filter(v =>
this.isProductVariantMode ? this.productVariantIds?.includes(v.id) : true,
);
})
.toPromise();
variants.push(...(productVariants || []));
}
return variants.slice(0, take);
private async getTopVariants(take: number) {
return (
await lastValueFrom(
this.dataService.product.getProductVariants({
filterOperator: LogicalOperator.OR,
filter: {
productId: { in: this.productIds },
id: { in: this.productVariantIds },
},
take,
}).single$,
)
).productVariants.items;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import {
BulkAction,
DataService,
DeletionResult,
GetProductVariantListQuery,
ItemOf,
ModalService,
NotificationService,
Permission,
createBulkRemoveFromChannelAction,
isMultiChannel,
} from '@vendure/admin-ui/core';
import { unique } from '@vendure/common/lib/unique';
import { EMPTY } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { ProductVariant } from 'package/core';
import { AssignProductsToChannelDialogComponent } from '../assign-products-to-channel-dialog/assign-products-to-channel-dialog.component';
import { ProductVariantListComponent } from './product-variant-list.component';

export const assignProductVariantsToChannelBulkAction: BulkAction<
ItemOf<GetProductVariantListQuery, 'productVariants'>,
ProductVariantListComponent
> = {
location: 'product-variant-list',
label: _('catalog.assign-to-channel'),
icon: 'layers',
requiresPermission: userPermissions =>
userPermissions.includes(Permission.UpdateCatalog) ||
userPermissions.includes(Permission.UpdateProduct),
isVisible: ({ injector }) => isMultiChannel(injector.get(DataService)),
onClick: ({ injector, selection, clearSelection }) => {
const modalService = injector.get(ModalService);
modalService
.fromComponent(AssignProductsToChannelDialogComponent, {
size: 'lg',
locals: {
productVariantIds: unique(selection.map(p => p.id)),
currentChannelIds: [],
},
})
.subscribe(result => {
if (result) {
clearSelection();
}
});
},
};

export const removeProductVariantsFromChannelBulkAction = createBulkRemoveFromChannelAction<
ItemOf<GetProductVariantListQuery, 'productVariants'>
>({
location: 'product-variant-list',
requiresPermission: userPermissions =>
userPermissions.includes(Permission.UpdateCatalog) ||
userPermissions.includes(Permission.UpdateProduct),
getItemName: item => item.name,
bulkRemoveFromChannel: (dataService, ids, channelId) =>
dataService.product
.removeVariantsFromChannel({
channelId: channelId,
productVariantIds: ids,
})
.pipe(map(res => res.removeProductVariantsFromChannel)),
});

export const deleteProductVariantsBulkAction: BulkAction<ProductVariant, ProductVariantListComponent> = {
location: 'product-variant-list',
label: _('common.delete'),
icon: 'trash',
iconClass: 'is-danger',
requiresPermission: userPermissions =>
userPermissions.includes(Permission.DeleteProduct) ||
userPermissions.includes(Permission.DeleteCatalog),
onClick: ({ injector, selection, hostComponent, clearSelection }) => {
const modalService = injector.get(ModalService);
const dataService = injector.get(DataService);
const notificationService = injector.get(NotificationService);
modalService
.dialog({
title: _('common.confirm-bulk-delete'),
translationVars: {
count: selection.length,
},
buttons: [
{ type: 'secondary', label: _('common.cancel') },
{ type: 'danger', label: _('common.delete'), returnValue: true },
],
})
.pipe(
switchMap(response =>
response
? dataService.product.deleteProductVariants(unique(selection.map(p => p.id)))
: EMPTY,
),
)
.subscribe(result => {
let deleted = 0;
const errors: string[] = [];
for (const item of result.deleteProductVariants) {
if (item.result === DeletionResult.DELETED) {
deleted++;
} else if (item.message) {
errors.push(item.message);
}
}
if (0 < deleted) {
notificationService.success(_('catalog.notify-bulk-delete-products-success'), {
count: deleted,
});
}
if (0 < errors.length) {
notificationService.error(errors.join('\n'));
}
hostComponent.refresh();
clearSelection();
});
},
};
2 changes: 1 addition & 1 deletion packages/admin-ui/src/lib/catalog/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export * from './components/product-list/product-list.graphql';
export * from './components/product-options-editor/product-options-editor.component';
export * from './components/product-variant-detail/product-variant-detail.component';
export * from './components/product-variant-detail/product-variant-detail.graphql';
export * from './components/product-variant-list/product-list-bulk-actions';
export * from './components/product-variant-list/product-variant-list-bulk-actions';
export * from './components/product-variant-list/product-variant-list.component';
export * from './components/product-variant-list/product-variant-list.graphql';
export * from './components/product-variant-quick-jump/product-variant-quick-jump.component';
Expand Down
Loading

0 comments on commit b25ddcd

Please sign in to comment.