product-review-admin.resolver.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
  2. import {
  3. Allow,
  4. Ctx,
  5. ListQueryBuilder,
  6. patchEntity,
  7. Permission,
  8. Product,
  9. RequestContext,
  10. Transaction,
  11. TransactionalConnection,
  12. } from '@vendure/core';
  13. import { ProductReview } from '../entities/product-review.entity';
  14. import {
  15. MutationApproveProductReviewArgs,
  16. MutationRejectProductReviewArgs,
  17. MutationUpdateProductReviewArgs,
  18. QueryProductReviewArgs,
  19. QueryProductReviewsArgs,
  20. } from '../generated-admin-types';
  21. @Resolver()
  22. export class ProductReviewAdminResolver {
  23. constructor(private connection: TransactionalConnection, private listQueryBuilder: ListQueryBuilder) {}
  24. @Query()
  25. @Allow(Permission.ReadCatalog)
  26. async productReviews(@Ctx() ctx: RequestContext, @Args() args: QueryProductReviewsArgs) {
  27. return this.listQueryBuilder
  28. .build(ProductReview, args.options || undefined, {
  29. relations: ['product'],
  30. ctx,
  31. })
  32. .getManyAndCount()
  33. .then(([items, totalItems]) => ({
  34. items,
  35. totalItems,
  36. }));
  37. }
  38. @Query()
  39. @Allow(Permission.ReadCatalog)
  40. async productReview(@Ctx() ctx: RequestContext, @Args() args: QueryProductReviewArgs) {
  41. return this.connection.getRepository(ctx, ProductReview).findOne({
  42. where: { id: args.id },
  43. relations: {
  44. author: true,
  45. product: true,
  46. productVariant: true,
  47. },
  48. });
  49. }
  50. @Transaction()
  51. @Mutation()
  52. @Allow(Permission.UpdateCatalog)
  53. async updateProductReview(
  54. @Ctx() ctx: RequestContext,
  55. @Args() { input }: MutationUpdateProductReviewArgs,
  56. ) {
  57. const review = await this.connection.getEntityOrThrow(ctx, ProductReview, input.id);
  58. const originalResponse = review.response;
  59. const updatedProductReview = patchEntity(review, input);
  60. if (input.response !== originalResponse) {
  61. updatedProductReview.responseCreatedAt = new Date();
  62. }
  63. return this.connection.getRepository(ctx, ProductReview).save(updatedProductReview);
  64. }
  65. @Transaction()
  66. @Mutation()
  67. @Allow(Permission.UpdateCatalog)
  68. async approveProductReview(@Ctx() ctx: RequestContext, @Args() { id }: MutationApproveProductReviewArgs) {
  69. const review = await this.connection.getEntityOrThrow(ctx, ProductReview, id, {
  70. relations: ['product'],
  71. });
  72. if (review.state !== 'new') {
  73. return review;
  74. }
  75. const { product } = review;
  76. const newRating = this.calculateNewReviewAverage(review.rating, product);
  77. product.customFields.reviewCount++;
  78. product.customFields.reviewRating = newRating;
  79. await this.connection.getRepository(ctx, Product).save(product);
  80. review.state = 'approved';
  81. return this.connection.getRepository(ctx, ProductReview).save(review);
  82. }
  83. @Transaction()
  84. @Mutation()
  85. @Allow(Permission.UpdateCatalog)
  86. async rejectProductReview(@Ctx() ctx: RequestContext, @Args() { id }: MutationRejectProductReviewArgs) {
  87. const review = await this.connection.getEntityOrThrow(ctx, ProductReview, id);
  88. if (review.state !== 'new') {
  89. return review;
  90. }
  91. review.state = 'rejected';
  92. return this.connection.getRepository(ctx, ProductReview).save(review);
  93. }
  94. private calculateNewReviewAverage(rating: number, product: Product): number {
  95. const count = product.customFields.reviewCount;
  96. const currentRating = product.customFields.reviewRating || 0;
  97. const newRating = (currentRating * count + rating) / (count + 1);
  98. return newRating;
  99. }
  100. }