Skip to content

Commit

Permalink
#1184 Apply promotion discount on cart page of storefront (#1186)
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenvanhadncntt authored Oct 15, 2024
1 parent eeaf29a commit 8b4c8f9
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ public ResponseEntity<PromotionDetailVm> getPromotion(@PathVariable("promotionId
return new ResponseEntity<>(promotionDetailVm, HttpStatus.OK);
}

@GetMapping({"/storefront/promotions/verify", "/backoffice/promotions/verify"})
public ResponseEntity<PromotionVerifyResultDto> verifyPromotion(PromotionVerifyVm promotionVerifyInfo) {
@PostMapping({"/storefront/promotions/verify", "/backoffice/promotions/verify"})
public ResponseEntity<PromotionVerifyResultDto> verifyPromotion(
@RequestBody PromotionVerifyVm promotionVerifyInfo) {
return ResponseEntity.ok(promotionService.verifyPromotion(promotionVerifyInfo));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public record PromotionVerifyResultDto(
boolean isValid,
Long productId,
String couponCode,
DiscountType discountType,
Long discountValue) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
Expand Down
13 changes: 13 additions & 0 deletions storefront/modules/promotion/model/Promotion.ts
Original file line number Diff line number Diff line change
@@ -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;
};
13 changes: 13 additions & 0 deletions storefront/modules/promotion/service/PromotionService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import apiClientService from '@/common/services/ApiClientService';
import { PromotionVerifyRequest, PromotionVerifyResult } from '../model/Promotion';

export async function verifyPromotion(
request: PromotionVerifyRequest
): Promise<PromotionVerifyResult> {
return (
await apiClientService.post(
'/api/promotion/storefront/promotions/verify',
JSON.stringify(request)
)
).json();
}
70 changes: 62 additions & 8 deletions storefront/pages/cart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ 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,
getCartProductThumbnail,
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();
Expand All @@ -46,6 +48,10 @@ const Cart = () => {

const { email } = useUserInfoContext();

const [couponCode, setCouponCode] = useState<string>('');

const [promotionApply, setPromotionApply] = useState<PromotionVerifyResult>();

const [cart, setCart] = useState<CartModel>({
id: 0,
customerId: '',
Expand All @@ -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[]) => {
Expand Down Expand Up @@ -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 (
<section className="shop-cart spad">
<div className="container">
Expand Down Expand Up @@ -331,7 +351,17 @@ const Cart = () => {
</Link>
</div>
</td>
<td className="cart__price">{formatPrice(item.price)}</td>
<td className="cart__price">
{promotionApply?.productId === item.productId && (
<div style={{ textDecorationLine: 'line-through' }}>
{formatPrice(item.price)}
</div>
)}

<div>
{formatPrice(item.price - (promotionApply?.discountValue ?? 0))}
</div>
</td>
<td className="cart__quantity">
<div className="pro-qty">
<div className="quantity buttons_added">
Expand Down Expand Up @@ -394,6 +424,20 @@ const Cart = () => {
</div>
</div>
</div>
{promotionApply && (
<div className="mt-5 mb-5">
<div className="row">
<div style={{ width: '188px' }}>
<i className="bi bi-receipt"></i> Coupon code applied:
</div>
<div className="col">
<span className="coupon-code-apply" aria-hidden="true" onClick={removeCouponCode}>
{promotionApply.couponCode}
</span>
</div>
</div>
</div>
)}
<div className="row">
<div className="col-lg-6 col-md-6 col-sm-6">
<div>
Expand All @@ -410,8 +454,18 @@ const Cart = () => {
<div className="discount__content">
<h6>Discount codes</h6>
<form action="#">
<input type="text" placeholder="Enter your coupon code" />
<button className="site-btn">Apply</button>
<input
type="text"
placeholder="Enter your coupon code"
onChange={(e) => setCouponCode(e.target.value)}
/>
<button
className="site-btn primary-btn btn btn-primary"
disabled={selectedProductIds.size === 0}
onClick={applyCopounCode}
>
Apply
</button>
</form>
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions storefront/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit 8b4c8f9

Please sign in to comment.