Untitled
unknown
plain_text
a month ago
18 kB
2
Indexable
import { useState, useEffect } from "react"; import Main from "../ultils/container"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTrash } from "@fortawesome/free-solid-svg-icons"; import "../assets/styles/ShoppingCart.scss"; import { ConfigProvider, Table, Checkbox, Button, Input } from "antd"; import { createStyles } from "antd-style"; import { getCart, deleteCart, updateCart } from "../api/ShoppingCart"; import OptionCart from "../components/UpdateOptionCart/OptionCart"; const useStyle = createStyles(({ css, token }) => { const { antCls } = token; return { customTable: css` ${antCls}-table-container { max-height: 500px; /* Đặt chiều cao tối đa cho bảng */ overflow-y: auto; /* Cuộn theo chiều dọc */ overflow-x: auto; /* Cuộn theo chiều ngang nếu cần */ ${antCls}-table-body { max-height: 500px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #eaeaea transparent; scrollbar-gutter: stable; } /* Scrollbar tùy chỉnh */ &::-webkit-scrollbar { width: 8px; /* Chiều rộng của thanh cuộn */ height: 8px; /* Chiều cao của thanh cuộn ngang */ } &::-webkit-scrollbar-track { background: transparent; /* Màu nền thanh cuộn */ } &::-webkit-scrollbar-thumb { background-color: #eaeaea; /* Màu thanh cuộn */ border-radius: 4px; /* Bo góc */ } } `, }; }); const ShoppingCart = () => { const { styles } = useStyle(); const [cartItems, setCartItems] = useState([]); const [selectedItems, setSelectedItems] = useState([]); const [isSystemLocked, setIsSystemLocked] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showSizePopup, setShowSizePopup] = useState(false); const [currentItemId, setCurrentItemId] = useState(null); // Categories để cập nhật size/color const categories = [ { name: "Size", tags: ["S", "M", "L", "XL"] }, { name: "Color", tags: ["Red", "Black", "White"] }, ]; const [selectedTags, setSelectedTags] = useState({}); // Hàm gọi API const fetchCartItems = async () => { const token = localStorage.getItem("token"); try { setLoading(true); const data = await getCart(token); // Gọi API từ file ShoppingCart.js const formattedData = data.map((item) => ({ id: item.cartItemId, name: item.productName || "Unknown Product", size: item.optionValue1 || "N/A", color: item.optionValue2 || "N/A", price: item.price || 0, // Giá mặc định nếu không có giá trị quantity: item.quantity || 1, // Số lượng mặc định maxQuantity: item.maxQuantity || 50, // Giá trị mặc định của maxQuantity image: item.productImages?.find((img) => img.isFeatured)?.urlImage || // Tìm ảnh nổi bật item.productImages?.[0]?.urlImage || // Nếu không có ảnh nổi bật, lấy ảnh đầu tiên "/img/default-image.jpg", // Ảnh mặc định nếu không có ảnh selected: false, })); setCartItems(formattedData); setLoading(false); } catch (error) { setError(error.message); setLoading(false); } }; useEffect(() => { fetchCartItems(); }, []); const handleSelectItem = (id) => { setSelectedItems((prevSelected) => prevSelected.includes(id) ? prevSelected.filter((item) => item !== id) : [...prevSelected, id] ); }; const handleRemoveSelectedItems = async () => { const token = localStorage.getItem("token"); // Lấy token từ localStorage const confirmRemove = window.confirm( "Are you sure you want to remove the selected items from your cart?" ); if (!confirmRemove) return; try { setIsSystemLocked(true); // Khóa hệ thống trong khi xóa const promises = selectedItems.map((id) => deleteCart(id, token) // Gọi API xóa từng mục ); await Promise.all(promises); // Chờ tất cả API hoàn thành // Cập nhật lại giỏ hàng sau khi xóa setCartItems((prevItems) => prevItems.filter((item) => !selectedItems.includes(item.id)) ); setSelectedItems([]); // Reset danh sách đã chọn } catch (error) { alert(`Error: ${error.message}`); // Hiển thị lỗi nếu có } finally { setIsSystemLocked(false); // Mở khóa hệ thống } }; const handleSelectAll = () => { if (selectedItems.length === cartItems.length) { setSelectedItems([]); // Bỏ chọn tất cả } else { setSelectedItems(cartItems.map((item) => item.id)); // Chọn tất cả } }; const renderHeader = () => { if (selectedItems.length > 0) { return ( <div className="selected-header"> <Checkbox indeterminate={ selectedItems.length > 0 && selectedItems.length < cartItems.length } checked={selectedItems.length === cartItems.length} onChange={handleSelectAll} className="custom-checkbox" disabled={isSystemLocked} // Không cho phép chọn khi hệ thống bị khóa /> <span className="selected-text"> Selected ({selectedItems.length}) </span> <Button danger onClick={handleRemoveSelectedItems} // Gọi hàm xóa các mục đã chọn className="delete-selected-btn" disabled={isSystemLocked} // Không cho phép xóa khi hệ thống bị khóa > <FontAwesomeIcon icon={faTrash} style={{ fontSize: "15px", }} /> </Button> </div> ); } return null; }; const handleDecreaseQuantityWithConfirm = (id) => { const token = localStorage.getItem("token"); // Lấy token từ localStorage let isConfirmed = false; setCartItems((prevItems) => { const item = prevItems.find((item) => item.id === id); if (!item) return prevItems; const newQuantity = item.quantity - 1; if (newQuantity <= 0 && !isConfirmed) { isConfirmed = true; setTimeout(() => { const confirmRemove = window.confirm( "The number of products has reached 0. Do you want to remove this product from your cart?" ); if (confirmRemove) { // Xóa sản phẩm khỏi giỏ hàng setCartItems((currentItems) => currentItems.filter((item) => item.id !== id) ); // Gọi API để xóa sản phẩm deleteCart(id, token).catch((error) => { alert(`Error: ${error.message}`); }); } else { // Đặt lại số lượng thành 1 setCartItems((currentItems) => currentItems.map((item) => item.id === id ? { ...item, quantity: 1 } : item ) ); // Gọi API để cập nhật số lượng thành 1 updateCart(id, 1, token).catch((error) => { alert(`Error: ${error.message}`); }); } }, 0); } else { // Gọi API để cập nhật số lượng updateCart(id, newQuantity, token).catch((error) => { alert(`Error: ${error.message}`); }); } return prevItems.map((item) => item.id === id ? { ...item, quantity: newQuantity } : item ); }); }; const handleQuantityInputChange = (id, value) => { // Cập nhật trực tiếp giá trị người dùng đang nhập setCartItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, quantity: value } : item ) ); }; const handleBlurQuantity = (id) => { const token = localStorage.getItem("token"); // Lấy token từ localStorage const currentItem = cartItems.find((item) => item.id === id); if (!currentItem) return; let newQuantity = parseInt(currentItem.quantity, 10); // Kiểm tra logic chỉ khi người dùng blur (rời khỏi ô nhập liệu) if (isNaN(newQuantity) || newQuantity < 1) { // Nếu giá trị không hợp lệ hoặc nhỏ hơn 1, đặt thành 1 alert("Invalid value! Quantity must be greater than or equal to 1."); newQuantity = 1; } else if (newQuantity > 10) { // Nếu giá trị vượt quá 10, đặt thành 10 và hiển thị thông báo alert("Product quantity limit is 10. If you want to buy more, please contact us."); newQuantity = 10; } // Gọi API để cập nhật số lượng sau khi kiểm tra updateCart(id, newQuantity, token) .then(() => { // Thành công, cập nhật lại state setCartItems((prevItems) => prevItems.map((item) => item.id === id ? { ...item, quantity: newQuantity.toString() } : item ) ); }) .catch((error) => { alert(`Error: ${error.message}`); }); }; const handleQuantityChange = (id, type) => { const token = localStorage.getItem("token"); setCartItems((prevItems) => { return prevItems.map((item) => { if (item.id === id) { const newQuantity = type === "increase" ? item.quantity + 1 : item.quantity - 1; if (newQuantity < 0) return item; // Gọi API cập nhật số lượng updateCart(id, newQuantity, token).catch((error) => { alert(`Error: ${error.message}`); }); return { ...item, quantity: newQuantity }; } return item; }); }); }; const handleEditSize = (id) => { const item = cartItems.find((item) => item.id === id); setCurrentItemId(id); setSelectedTags({ Size: item.size, Color: item.color, }); setShowSizePopup(true); }; const handleSaveSize = async () => { const token = localStorage.getItem("token"); const currentItem = cartItems.find((item) => item.id === currentItemId); if (!currentItem) return; try { const requestBody = { cartItemId: currentItemId, variantId: currentItem.variantId, quantity: currentItem.quantity, options: { ...selectedTags, // Dynamically include the selected options }, }; // Call the API await updateCart(requestBody, token); // Update the cart state setCartItems((prevItems) => prevItems.map((item) => item.id === currentItemId ? { ...item, options: { ...item.options, ...selectedTags, // Update selected options }, } : item ) ); alert("Options updated successfully!"); } catch (error) { alert(`Error: ${error.message}`); } finally { setShowSizePopup(false); } }; const handleRemoveItem = async (cartItemId) => { const token = localStorage.getItem("token"); const confirmRemove = window.confirm( "Are you sure you want to remove this item from your cart?" ); if (!confirmRemove) return; try { setIsSystemLocked(true); await deleteCart(cartItemId, token); // Gọi hàm xóa setCartItems((prevItems) => prevItems.filter((item) => item.id !== cartItemId)); // Cập nhật lại giỏ hàng } catch (error) { alert(`Error: ${error.message}`); // Hiển thị lỗi nếu có } finally { setIsSystemLocked(false); } }; const columns = [ { title: selectedItems.length === 0 ? ( <Checkbox indeterminate={ selectedItems.length > 0 && selectedItems.length < cartItems.length } checked={selectedItems.length === cartItems.length} onChange={handleSelectAll} disabled={isSystemLocked} > Select All </Checkbox> ) : null, dataIndex: "selected", width: 105, render: (_, record) => ( <Checkbox checked={selectedItems.includes(record.id)} onChange={() => handleSelectItem(record.id)} disabled={isSystemLocked} /> ), }, { title: "Product", dataIndex: "name", width: 350, render: (_, record) => { console.log("Image URL:", record.image); // Log đường dẫn image vào console return ( <div className="product-info"> <img src={record.image} alt={record.name} className="product-info-image" style={{ width: "50px", height: "50px", objectFit: "cover", marginRight: "10px", }} /> <div className="product-info-details"> <p className="product-info-name">{record.name}</p> <p className="product-info-size"> <Button type="link" onClick={() => handleEditSize(record.id)} disabled={isSystemLocked} > {record.size}, {record.color} </Button> </p> </div> </div> ); }, }, { title: "Price", dataIndex: "price", width: 135, align: "center", render: (price) => `${price.toLocaleString()} $`, }, { title: "Quantity", dataIndex: "quantity", align: "center", width: 140, render: (_, record) => ( <div className="quantity-controls"> {/* Nút giảm số lượng */} <Button onClick={() => handleDecreaseQuantityWithConfirm(record.id)} disabled={isSystemLocked} // Không khóa giảm số lượng > - </Button> {/* Ô nhập số lượng */} <Input type="number" value={record.quantity} // Hiển thị số lượng hiện tại onChange={(e) => handleQuantityInputChange(record.id, e.target.value)} // Cập nhật khi nhập onBlur={() => handleBlurQuantity(record.id)} // Kiểm tra logic khi mất tiêu điểm className="quantity-input" style={{ textAlign: "center" }} /> {/* Nút tăng số lượng */} <Button onClick={() => handleQuantityChange(record.id, "increase")} disabled={isSystemLocked} // Cho phép tăng mà không giới hạn > + </Button> </div> ), }, { title: "Into Money", dataIndex: "intoMoney", width: 135, align: "center", render: (_, record) => { const price = record.price || 0; // Giá trị mặc định nếu null hoặc undefined const quantity = record.quantity || 0; // Giá trị mặc định nếu null hoặc undefined const intoMoney = price * quantity; return ( <span style={{ color: "#ff385c", fontWeight: "bold" }}> {intoMoney.toLocaleString()} $ </span> ); }, }, { title: "Remove", dataIndex: "actions", width: 70, align: "center", render: (_, record) => ( <Button danger onClick={() => handleRemoveItem(record.id)}> <FontAwesomeIcon icon={faTrash} /> </Button> ), }, ]; if (loading) { return <div>Loading...</div>; } if (error) { return <div>Error: {error}</div>; } return ( <ConfigProvider theme={{ token: { colorPrimary: "#ff385c", colorPrimaryHover: "#e0314b", }, }} > <Main> <div className="shopping-cart"> <h2 className="shopping-cart-title">User Shopping Cart</h2> <p className="shopping-cart-subtitle">Final Step Before Your Payment</p> <div> {renderHeader()} <Table className={styles.customTable} columns={columns} dataSource={cartItems.map((item) => ({ ...item, key: item.id }))} pagination={false} locale={{ emptyText: ( <div className="empty-cart-table"> <img src="/img/Empty-Cart.jpg" // Đường dẫn ảnh giỏ hàng trống alt="Empty Cart" style={{ width: "200px", marginBottom: "10px" }} /> <p style={{ fontSize: "18px", fontWeight: "bold", color: "#555" }}> Your cart is empty. Start shopping now! </p> </div> ), }} /> </div> </div> {showSizePopup && ( <OptionCart categories={categories} // Truyền danh mục option từ API hoặc static selectedTags={selectedTags} // Trạng thái option đã chọn onChange={(category, tag) => setSelectedTags({ ...selectedTags, [category]: tag }) } // Cập nhật khi chọn option onSave={handleSaveSize} // Hàm lưu và gọi API onCancel={() => setShowSizePopup(false)} // Đóng popup /> )} </Main> </ConfigProvider> ); }; export default ShoppingCart;
Editor is loading...
Leave a Comment