From 8b4c8f931121e9ad253dc7443bd52563ddcaa1e0 Mon Sep 17 00:00:00 2001 From: nguyenvanhadncntt <35553635+nguyenvanhadncntt@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:01:55 +0700 Subject: [PATCH] #1184 Apply promotion discount on cart page of storefront (#1186) --- .../controller/PromotionController.java | 5 +- .../promotion/service/PromotionService.java | 1 + .../viewmodel/PromotionVerifyResultDto.java | 1 + .../controller/PromotionControllerTest.java | 1 + .../modules/promotion/model/Promotion.ts | 13 ++++ .../promotion/service/PromotionService.ts | 13 ++++ storefront/pages/cart/index.tsx | 70 ++++++++++++++++--- storefront/styles/globals.css | 8 +++ 8 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 storefront/modules/promotion/model/Promotion.ts create mode 100644 storefront/modules/promotion/service/PromotionService.ts diff --git a/promotion/src/main/java/com/yas/promotion/controller/PromotionController.java b/promotion/src/main/java/com/yas/promotion/controller/PromotionController.java index 2ba3dd7e4d..de6d2f890f 100644 --- a/promotion/src/main/java/com/yas/promotion/controller/PromotionController.java +++ b/promotion/src/main/java/com/yas/promotion/controller/PromotionController.java @@ -106,8 +106,9 @@ public ResponseEntity getPromotion(@PathVariable("promotionId return new ResponseEntity<>(promotionDetailVm, HttpStatus.OK); } - @GetMapping({"/storefront/promotions/verify", "/backoffice/promotions/verify"}) - public ResponseEntity verifyPromotion(PromotionVerifyVm promotionVerifyInfo) { + @PostMapping({"/storefront/promotions/verify", "/backoffice/promotions/verify"}) + public ResponseEntity verifyPromotion( + @RequestBody PromotionVerifyVm promotionVerifyInfo) { return ResponseEntity.ok(promotionService.verifyPromotion(promotionVerifyInfo)); } } diff --git a/promotion/src/main/java/com/yas/promotion/service/PromotionService.java b/promotion/src/main/java/com/yas/promotion/service/PromotionService.java index 8d98f8f6aa..08e7cb6197 100644 --- a/promotion/src/main/java/com/yas/promotion/service/PromotionService.java +++ b/promotion/src/main/java/com/yas/promotion/service/PromotionService.java @@ -231,6 +231,7 @@ public PromotionVerifyResultDto verifyPromotion(PromotionVerifyVm promotionVerif .sorted(Comparator.comparing(ProductVm::price)).toList(); return new PromotionVerifyResultDto(true, productsCanApply.getFirst().id(), + promotion.getCouponCode(), promotion.getDiscountType(), DiscountType.FIXED.equals(promotion.getDiscountType()) ? promotion.getDiscountAmount() : promotion.getDiscountPercentage()); diff --git a/promotion/src/main/java/com/yas/promotion/viewmodel/PromotionVerifyResultDto.java b/promotion/src/main/java/com/yas/promotion/viewmodel/PromotionVerifyResultDto.java index 513e35fa72..d66bc08a6c 100644 --- a/promotion/src/main/java/com/yas/promotion/viewmodel/PromotionVerifyResultDto.java +++ b/promotion/src/main/java/com/yas/promotion/viewmodel/PromotionVerifyResultDto.java @@ -5,6 +5,7 @@ public record PromotionVerifyResultDto( boolean isValid, Long productId, + String couponCode, DiscountType discountType, Long discountValue) { } diff --git a/promotion/src/test/java/com/yas/promotion/controller/PromotionControllerTest.java b/promotion/src/test/java/com/yas/promotion/controller/PromotionControllerTest.java index 1717cbca0f..4a6dcbe01e 100644 --- a/promotion/src/test/java/com/yas/promotion/controller/PromotionControllerTest.java +++ b/promotion/src/test/java/com/yas/promotion/controller/PromotionControllerTest.java @@ -345,6 +345,7 @@ void verify_promotion_with_valid_data_returns_expected_result() { PromotionVerifyResultDto expectedResult = new PromotionVerifyResultDto( true, 1L, + "coupon-code-1", DiscountType.FIXED, 10000L ); diff --git a/storefront/modules/promotion/model/Promotion.ts b/storefront/modules/promotion/model/Promotion.ts new file mode 100644 index 0000000000..b661c0da12 --- /dev/null +++ b/storefront/modules/promotion/model/Promotion.ts @@ -0,0 +1,13 @@ +export type PromotionVerifyRequest = { + couponCode: string; + orderPrice: number; + productIds: number[]; +}; + +export type PromotionVerifyResult = { + isValid: boolean; + productId: number; + couponCode: string; + discountType: string; + discountValue: number; +}; diff --git a/storefront/modules/promotion/service/PromotionService.ts b/storefront/modules/promotion/service/PromotionService.ts new file mode 100644 index 0000000000..2c9c857dc3 --- /dev/null +++ b/storefront/modules/promotion/service/PromotionService.ts @@ -0,0 +1,13 @@ +import apiClientService from '@/common/services/ApiClientService'; +import { PromotionVerifyRequest, PromotionVerifyResult } from '../model/Promotion'; + +export async function verifyPromotion( + request: PromotionVerifyRequest +): Promise { + return ( + await apiClientService.post( + '/api/promotion/storefront/promotions/verify', + JSON.stringify(request) + ) + ).json(); +} diff --git a/storefront/pages/cart/index.tsx b/storefront/pages/cart/index.tsx index 013823e8ed..f40019e028 100644 --- a/storefront/pages/cart/index.tsx +++ b/storefront/pages/cart/index.tsx @@ -6,6 +6,7 @@ import { toast } from 'react-toastify'; import ImageWithFallBack from '@/common/components/ImageWithFallback'; import ConfirmationDialog from '@/common/components/dialog/ConfirmationDialog'; import { useCartContext } from '@/context/CartContext'; +import { useUserInfoContext } from '@/context/UserInfoContext'; import { Cart as CartModel } from '@/modules/cart/models/Cart'; import { getCart, @@ -13,13 +14,14 @@ import { removeProductInCart, updateCart, } from '@/modules/cart/services/CartService'; -import { formatPrice } from 'utils/formatPrice'; +import { toastError } from '@/modules/catalog/services/ToastService'; +import { Checkout } from '@/modules/order/models/Checkout'; import { CheckoutItem } from '@/modules/order/models/CheckoutItem'; import { createCheckout } from '@/modules/order/services/OrderService'; -import { Checkout } from '@/modules/order/models/Checkout'; -import { useUserInfoContext } from '@/context/UserInfoContext'; +import { PromotionVerifyResult } from '@/modules/promotion/model/Promotion'; +import { verifyPromotion } from '@/modules/promotion/service/PromotionService'; import { useRouter } from 'next/router'; -import { toastError } from '@/modules/catalog/services/ToastService'; +import { formatPrice } from 'utils/formatPrice'; const Cart = () => { const router = useRouter(); @@ -46,6 +48,10 @@ const Cart = () => { const { email } = useUserInfoContext(); + const [couponCode, setCouponCode] = useState(''); + + const [promotionApply, setPromotionApply] = useState(); + const [cart, setCart] = useState({ id: 0, customerId: '', @@ -72,7 +78,7 @@ const Cart = () => { }; const calculateProductPrice = (item: Item) => { - return formatPrice(item.price * item.quantity); + return formatPrice(item.price * item.quantity - (promotionApply?.discountValue ?? 0)); }; const getProductThumbnails = (productIds: number[]) => { @@ -259,6 +265,20 @@ const Cart = () => { }); }; + const applyCopounCode = () => { + verifyPromotion({ + couponCode: couponCode, + orderPrice: totalPrice, + productIds: Array.from(selectedProductIds.values()), + }).then((result) => { + setPromotionApply(result); + }); + }; + + const removeCouponCode = () => { + setPromotionApply(undefined); + }; + return (
@@ -331,7 +351,17 @@ const Cart = () => {
- {formatPrice(item.price)} + + {promotionApply?.productId === item.productId && ( +
+ {formatPrice(item.price)} +
+ )} + +
+ {formatPrice(item.price - (promotionApply?.discountValue ?? 0))} +
+
@@ -394,6 +424,20 @@ const Cart = () => {
+ {promotionApply && ( +
+
+
+ Coupon code applied: +
+
+ +
+
+
+ )}
@@ -410,8 +454,18 @@ const Cart = () => {
Discount codes
- - + setCouponCode(e.target.value)} + /> +
diff --git a/storefront/styles/globals.css b/storefront/styles/globals.css index 0a6d765264..9d7df4be4b 100644 --- a/storefront/styles/globals.css +++ b/storefront/styles/globals.css @@ -312,3 +312,11 @@ a { .btn-create-address:hover { color: #0070f3; } + +.coupon-code-apply { + border-radius: 10px; + padding: 9px; + background-color: #0070f3; + color: #fff; + cursor: pointer; +}