Skip to content

Commit b730bdf

Browse files
authored
Merge pull request #95 from xdorro/tuananh
Update product quantity
2 parents f133bce + 4507f5f commit b730bdf

File tree

8 files changed

+345
-22
lines changed

8 files changed

+345
-22
lines changed

src/main/java/com/example/multikart/common/Utils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ public static float getTotalPriceCart(List<CartDTO> carts) {
5555
return carts.stream().map(cart -> cart.getPrice() * cart.getQuantity()).reduce(total, Float::sum);
5656
}
5757

58+
public static float getItemTotalPriceCart(List<CartDTO> carts, Long id) {
59+
var total = 0.0f;
60+
return carts.stream().filter(x -> x.getProductId().equals(id)).map(cart -> cart.getPrice() * cart.getQuantity()).reduce(total, Float::sum);
61+
}
62+
5863
public static Customer getCustomerSession(HttpSession session) {
5964
var customer = (Customer) session.getAttribute("customer");
6065
if (DataUtils.isNullOrEmpty(customer)) {

src/main/java/com/example/multikart/controller/ApiController.java

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
package com.example.multikart.controller;
22

3-
import com.example.multikart.domain.dto.DistrictDTO;
4-
import com.example.multikart.domain.dto.ProvinceDTO;
5-
import com.example.multikart.domain.dto.WardDTO;
3+
import com.example.multikart.common.Const;
4+
import com.example.multikart.common.Const.DefaultStatus;
5+
import com.example.multikart.common.DataUtils;
6+
import com.example.multikart.common.Utils;
7+
import com.example.multikart.domain.dto.*;
8+
import com.example.multikart.repo.ProductRepository;
69
import com.google.gson.Gson;
710
import lombok.extern.slf4j.Slf4j;
811
import org.springframework.beans.factory.annotation.Autowired;
912
import org.springframework.cache.annotation.Cacheable;
1013
import org.springframework.core.io.ResourceLoader;
11-
import org.springframework.web.bind.annotation.GetMapping;
12-
import org.springframework.web.bind.annotation.PathVariable;
13-
import org.springframework.web.bind.annotation.RequestMapping;
14-
import org.springframework.web.bind.annotation.RestController;
14+
import org.springframework.http.HttpStatus;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.web.bind.annotation.*;
1517

18+
import javax.servlet.http.HttpSession;
19+
import javax.validation.Valid;
1620
import java.io.BufferedReader;
1721
import java.io.FileReader;
1822
import java.io.IOException;
@@ -26,6 +30,8 @@
2630
public class ApiController {
2731
@Autowired
2832
ResourceLoader resourceLoader;
33+
@Autowired
34+
private ProductRepository productRepository;
2935

3036
@GetMapping("/dvhc/provinces")
3137
@Cacheable("provinces")
@@ -82,4 +88,43 @@ public List<WardDTO> getWardList(@PathVariable("code") String code) throws IOExc
8288
wards.addAll(maps.values());
8389
return wards;
8490
}
91+
92+
@PostMapping("/cart/update")
93+
public ResponseEntity<CartResponseDTO> updateCartQuantity(@Valid @ModelAttribute("cart") AddToCartRequestDTO input, HttpSession session) {
94+
var product = productRepository.findByProductIdAndStatus(input.getProductId(), DefaultStatus.ACTIVE);
95+
if (DataUtils.isNullOrEmpty(product)) {
96+
var result = CartResponseDTO.builder()
97+
.status(false)
98+
.message("Sản phẩm không tồn tại")
99+
.build();
100+
return new ResponseEntity<>(result, HttpStatus.OK);
101+
}
102+
103+
var carts = Utils.getCartSession(session);
104+
// add or update cart
105+
if (!Utils.checkExistCart(carts, input.getProductId())) {
106+
carts.add(new CartDTO(product, input.getQuantity()));
107+
} else {
108+
carts.forEach(c -> {
109+
if (c.getProductId().equals(input.getProductId())) {
110+
c.setQuantity(input.getQuantity());
111+
}
112+
});
113+
}
114+
115+
session.setAttribute("carts", carts);
116+
var total = Utils.getTotalPriceCart(carts);
117+
118+
var itemTotal = Utils.getItemTotalPriceCart(carts, input.getProductId());
119+
120+
var result = CartResponseDTO.builder()
121+
.status(true)
122+
.message("Cập nhật thành công")
123+
.cartCount(carts.size())
124+
.cartTotal(total)
125+
.itemTotal(itemTotal)
126+
.build();
127+
128+
return new ResponseEntity<>(result, HttpStatus.OK);
129+
}
85130
}

src/main/java/com/example/multikart/domain/dto/CartDTO.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ public class CartDTO implements Serializable {
1818
private String slug;
1919
private String image;
2020

21-
private Integer quantity;
21+
private Integer amount; // product amount
22+
23+
private Integer quantity; // cart quantity
2224
private Float price;
2325

2426
public CartDTO(Product product, Integer number) {
2527
productId = product.getProductId();
2628
name = product.getName();
2729
slug = product.getSlug();
30+
amount = product.getAmount();
2831
quantity = number;
2932
price = product.getExportPrice();
3033
}
@@ -34,6 +37,7 @@ public CartDTO(ItemProductDTO product, Integer number) {
3437
name = product.getName();
3538
slug = product.getSlug();
3639
image = product.getImage();
40+
amount = product.getAmount();
3741
quantity = number;
3842
price = product.getExportPrice();
3943
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.example.multikart.domain.dto;
2+
3+
import lombok.*;
4+
5+
@Getter
6+
@Setter
7+
@Builder
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
@ToString
11+
public class CartResponseDTO {
12+
private Integer cartCount;
13+
private Float cartTotal;
14+
private Float itemTotal;
15+
private boolean status;
16+
private String message;
17+
}

src/main/resources/templates/frontend/cart.html

Lines changed: 151 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,35 @@ <h2>Giỏ hàng</h2>
4646
<tbody th:each="cart : ${carts}">
4747
<form id="removeFromCart" th:action="@{/cart/{id}/delete(id=${cart.productId})}"
4848
th:method="POST">
49-
<tr>
49+
<tr th:id="${cart.productId}">
5050
<td>
5151
<a th:href="@{/san-pham/{slug}(slug=${cart.slug})}">
52-
<img alt=""
53-
th:src="@{/{url}(url=${cart.image})}"/>
52+
<img alt=""
53+
th:src="@{/{url}(url=${cart.image})}"/>
5454
</a>
5555
</td>
5656
<td><a th:href="@{/san-pham/{slug}(slug=${cart.slug})}" th:text="${cart.name}"></a>
5757
<div class="mobile-cart-content row">
5858
<div class="col">
5959
<div class="qty-box">
6060
<div class="input-group">
61-
<input type="text" name="quantity" class="form-control input-number"
61+
<span class="input-group-prepend">
62+
<button type="button" class="btn quantity-left-minus minus1"
63+
data-type="minus"
64+
data-field="">
65+
<i class="ti-angle-left"></i>
66+
</button>
67+
</span>
68+
69+
<input type="text" name="quantity" class="form-control input-number qty1"
6270
value="1" th:value="${cart.quantity}">
71+
72+
<span class="input-group-prepend">
73+
<button type="button" class="btn quantity-right-plus plus1"
74+
data-type="plus" data-field="">
75+
<i class="ti-angle-right"></i>
76+
</button>
77+
</span>
6378
</div>
6479
</div>
6580
</div>
@@ -81,8 +96,24 @@ <h2 th:text="${#numbers.formatDecimal(cart.price, 0, 'COMMA', 0, 'POINT') + '
8196
<td>
8297
<div class="qty-box">
8398
<div class="input-group">
84-
<input type="number" name="quantity" class="form-control input-number"
99+
<span class="input-group-prepend">
100+
<button type="button" class="btn quantity-left-minus minus"
101+
data-type="minus"
102+
data-field="">
103+
<i class="ti-angle-left"></i>
104+
</button>
105+
</span>
106+
107+
<input type="text" name="quantity" class="form-control input-number qty"
85108
value="1" th:value="${cart.quantity}">
109+
<input type="hidden" class="product_amount" th:value="${cart.amount}">
110+
111+
<span class="input-group-prepend">
112+
<button type="button" class="btn quantity-right-plus plus"
113+
data-type="plus" data-field="">
114+
<i class="ti-angle-right"></i>
115+
</button>
116+
</span>
86117
</div>
87118
</div>
88119
</td>
@@ -91,7 +122,7 @@ <h2 th:text="${#numbers.formatDecimal(cart.price, 0, 'COMMA', 0, 'POINT') + '
91122
class="ti-close"></i></a>
92123
</td>
93124
<td>
94-
<h2 class="td-color"
125+
<h2 class="itemTotal td-color"
95126
th:text="${#numbers.formatDecimal(cart.price * cart.quantity, 0, 'COMMA', 0, 'POINT') + ' ₫'}"></h2>
96127
</td>
97128
</tr>
@@ -104,7 +135,7 @@ <h2 th:text="${#numbers.formatDecimal(cart.price, 0, 'COMMA', 0, 'POINT') + '
104135
<tr>
105136
<td>Thành tiền :</td>
106137
<td>
107-
<h2 th:text="${#numbers.formatDecimal(total, 0, 'COMMA', 0, 'POINT') + ' ₫'}"></h2>
138+
<h2 id="cartTotal" th:text="${#numbers.formatDecimal(total, 0, 'COMMA', 0, 'POINT') + ' ₫'}"></h2>
108139
</td>
109140
</tr>
110141
</tfoot>
@@ -129,5 +160,118 @@ <h2 th:text="${#numbers.formatDecimal(total, 0, 'COMMA', 0, 'POINT') + ' ₫'}">
129160
<div th:replace="layouts/frontend/nosidebar-footer :: footer"></div>
130161
</div>
131162

163+
164+
<div layout:fragment="customScript">
165+
<script th:inline="javascript">
166+
/*<![CDATA[*/
167+
168+
function separateComma(val) {
169+
return new Intl.NumberFormat('vi-VN', { maximumFractionDigits: 0 }).format(val);
170+
}
171+
172+
function debounce(func, wait) {
173+
var timeout;
174+
175+
return function () {
176+
var context = this,
177+
args = arguments;
178+
179+
var executeFunction = function () {
180+
func.apply(context, args);
181+
};
182+
183+
clearTimeout(timeout);
184+
timeout = setTimeout(executeFunction, wait);
185+
};
186+
}
187+
188+
$(function () {
189+
190+
$(".plus").on("click", function() {
191+
var self = this;
192+
var qty = $(self).closest("tr").find(".qty");
193+
var maxQty = $(self).closest("tr").find(".product_amount").val();
194+
195+
if (parseInt(qty.val()) <= parseInt(maxQty)) {
196+
qty.val(+parseInt(qty.val()) + 1);
197+
$(self).closest("tr").find(".minus").attr("disabled", false);
198+
//Trigger change event
199+
qty.trigger("change");
200+
} else {
201+
$(self).attr("disabled", true);
202+
}
203+
});
204+
205+
$(".minus").on("click", function() {
206+
var self = this;
207+
var qty = $(self).closest("tr").find(".qty");
208+
209+
if (parseInt(qty.val()) < 2) {
210+
$(self).attr("disabled", true);
211+
} else {
212+
qty.val(+parseInt(qty.val()) - 1);
213+
$(self).closest("tr").find(".plus").attr("disabled", false);
214+
//Trigger change event
215+
qty.trigger("change");
216+
}
217+
});
218+
219+
$(".qty").on("change", debounce(function (e) {
220+
console.log("qty change");
221+
222+
var self = this;
223+
var oldQty = $(self).val();
224+
var maxQty = $(self).closest("tr").find(".product_amount").val();
225+
226+
if (parseInt($(self).val()) <= 0 || isNaN($(self).val())) {
227+
$(self).val(1);
228+
}
229+
230+
if (parseInt($(self).val()) >= maxQty) {
231+
$(self).val(maxQty);
232+
}
233+
234+
$(self).closest("tr").find(".plus").attr("disabled", false);
235+
$(self).closest("tr").find(".minus").attr("disabled", false);
236+
237+
var id = $(self).closest("tr").attr('id');
238+
var qty = $(self).val();
239+
240+
$.ajax({
241+
"url": "/cart/update",
242+
"method": "POST",
243+
"data": {
244+
"productId": id,
245+
"quantity": qty,
246+
}
247+
}).done(function(json) {
248+
// console.log(json);
249+
250+
if (json.status === true) {
251+
// $('#itemTotal').html(json.itemTotal);
252+
$('#cartTotal').html(separateComma(json.cartTotal) + ' ₫');
253+
$('.cart_qty_cls').html(json.cartCount);
254+
$(self).closest("tr").find(".itemTotal").html(separateComma(json.itemTotal) + ' ₫');
255+
} else {
256+
$(self).val(oldQty);
257+
Swal.fire({
258+
toast: true,
259+
position: "top-end",
260+
showConfirmButton: false,
261+
timer: 3000,
262+
icon: "error",
263+
title: json.message,
264+
});
265+
}
266+
}).error(function () {
267+
$(self).val(oldQty);
268+
});
269+
}, 300));
270+
});
271+
272+
/*]]>*/
273+
</script>
274+
</div>
275+
132276
</body>
133277
</html>

0 commit comments

Comments
 (0)