ATM
alain tresor makemo

Default Component - Copy this React, Tailwind Component to your project

Ameliore ce design import React, { useState, useEffect } from "react"; import { FaTrash, FaMinus, FaPlus, FaTimes, FaShoppingCart, FaArrowLeft, } from "react icons/fa"; import { useNavigate } from "react router dom"; const Cart = ({ isOpen, toggleCart }) => { const [cart, setCart] = useState([]); const navigate = useNavigate(); // Fonction pour mettre à jour le panier depuis le localStorage const updateCart = () => { const cartData = JSON.parse(localStorage.getItem("cart")) || []; setCart(cartData); }; useEffect(() => { updateCart(); window.addEventListener("cartUpdated", updateCart); return () => { window.removeEventListener("cartUpdated", updateCart); }; }, []); const handleRemove = (productId, color) => { const updatedCart = cart .map((product) => { if (product.id === productId && product.variants) { const updatedVariants = product.variants.filter( (v) => v.color !== color ); return { ...product, variants: updatedVariants }; } return product; }) .filter((product) => !product.variants || product.variants.length > 0); setCart(updatedCart); localStorage.setItem("cart", JSON.stringify(updatedCart)); window.dispatchEvent(new Event("cartUpdated")); }; const handleQuantityChange = (productId, color, increment) => { const updatedCart = cart.map((product) => { if (product.id === productId && product.variants) { const updatedVariants = product.variants.map((v) => { if (v.color === color) { return { ...v, quantity: Math.max(1, v.quantity + (increment ? 1 : 1)), }; } return v; }); return { ...product, variants: updatedVariants }; } return product; }); setCart(updatedCart); localStorage.setItem("cart", JSON.stringify(updatedCart)); window.dispatchEvent(new Event("cartUpdated")); }; const calculateTotal = () => { const total = cart.reduce((sum, item) => { if (item.variants && item.variants.length > 0) { const variantTotal = item.variants.reduce((sub, variant) => { return sub + (parseFloat(variant.price) || 0) * variant.quantity; }, 0); return sum + variantTotal; } else { return sum + (parseFloat(item.price) || 0) * (item.quantity || 0); } }, 0); return total.toFixed(2); }; const handleCheckout = () => { toggleCart(); navigate("/checkout"); }; const handleContinueShopping = () => { toggleCart(); navigate("/productList"); }; if (!isOpen) return null; return ( <> {/* Overlay */} <div className="fixed inset 0 bg black bg opacity 50 z [150] transition opacity duration 300" onClick={toggleCart} /> {/* Cart Panel */} <div className="fixed inset y 0 right 0 w full sm:w 96 bg white shadow xl z [160] transform transition transform duration 300"> {/* Header */} <div className="sticky top 0 bg white z 10 px 6 py 4 border b"> <div className="flex items center justify between"> <div className="flex items center gap 3"> <FaShoppingCart className="text xl text blue 600" /> <h2 className="text xl font bold text gray 800">Mon Panier</h2> <span className="ml 2 bg blue 100 text blue 600 text sm px 2 py 1 rounded full"> {cart.length} articles </span> </div> <button onClick={toggleCart} className="p 2 hover:bg gray 100 rounded full transition colors" > <FaTimes className="text xl text gray 500" /> </button> </div> </div> {/* Cart Content */} <div className="h [calc(100vh 180px)] overflow y auto px 6 py 4"> {cart.length === 0 ? ( <div className="flex flex col items center justify center h full text center"> <FaShoppingCart className="text 6xl text gray 300 mb 4" /> <h3 className="text xl font semibold text gray 800 mb 2"> Votre panier est vide </h3> <p className="text gray 500 mb 6"> Découvrez nos produits et commencez votre shopping </p> <button onClick={handleContinueShopping} className="flex items center gap 2 text blue 600 hover:text blue 700 font medium" > <FaArrowLeft className="text sm" /> Continuer mes achats </button> </div> ) : ( <div className="space y 4"> {cart.map((item) => { const productDetails = item.variants?.map((variant, i) => ( <div key={`${item.id} ${variant.color} ${i}`} className="bg white border border gray 200 rounded xl p 4 hover:border blue 200 transition colors shadow lg" > {/* Product details */} <div className="flex gap 4"> <div className="w 20 h 20 rounded lg overflow hidden bg gray 50"> <img src={item.image || "/default image.jpg"} alt={item.designation || item.name} className="w full h full object cover" /> </div> <div className="flex 1"> <h3 className="font medium text gray 800 mb 1"> {item.designation || item.name} </h3> <div className="flex items center gap 2 mb 2"> <span className="w 4 h 4 rounded full border" style={{ backgroundColor: variant.color }} /> <span className="text sm text gray 600"> {variant.color} </span> </div> <div className="flex items center justify between"> {/* Quantity control */} <div className="flex items center gap 2 bg gray 100 rounded lg p 1"> <button onClick={() => handleQuantityChange( item.id, variant.color, false ) } className="p 1.5 hover:bg gray 200 rounded lg transition colors" > <FaMinus className="text gray 600" /> </button> <span className="w 8 text center font medium"> {variant.quantity} </span> <button onClick={() => handleQuantityChange( item.id, variant.color, true ) } className="p 1.5 hover:bg gray 200 rounded lg transition colors" > <FaPlus className="text gray 600" /> </button> </div> <button onClick={() => handleRemove(item.id, variant.color) } className="p 2 text red 500 hover:bg red 50 rounded lg transition colors" > <FaTrash /> </button> </div> </div> </div> <div className="mt 3 pt 3 border t"> <div className="flex justify between items center"> <span className="text sm text gray 500">Prix</span> <span className="font semibold text gray 900"> ${(variant.price * variant.quantity).toFixed(2)} </span> </div> </div> </div> )); // Single product case without variants if (!item.variants || item.variants.length === 0) { return ( <div key={item.id} className="bg white border border gray 200 rounded xl p 4 hover:border blue 200 transition colors shadow lg" > {/* Product details */} <div className="flex gap 4"> <div className="w 20 h 20 rounded lg overflow hidden bg gray 50"> <img src={item.image || "/default image.jpg"} alt={item.designation || item.name} className="w full h full object cover" /> </div> <div className="flex 1"> <h3 className="font medium text gray 800 mb 3"> {item.designation || item.name} </h3> <div className="flex items center justify between"> <div className="flex items center gap 2 bg gray 100 rounded lg p 1"> <button onClick={() => handleQuantityChange(item.id, null, false) } className="p 1.5 hover:bg gray 200 rounded lg transition colors" > <FaMinus className="text gray 600" /> </button> <span className="w 8 text center font medium"> {item.quantity} </span> <button onClick={() => handleQuantityChange(item.id, null, true) } className="p 1.5 hover:bg gray 200 rounded lg transition colors" > <FaPlus className="text gray 600" /> </button> </div> <button onClick={() => handleRemove(item.id, null)} className="p 2 text red 500 hover:bg red 50 rounded lg transition colors" > <FaTrash /> </button> </div> </div> </div> <div className="mt 3 pt 3 border t"> <div className="flex justify between items center"> <span className="text sm text gray 500">Prix</span> <span className="font semibold text gray 900"> ${(item.price * item.quantity).toFixed(2)} </span> </div> </div> </div> ); } return productDetails; })} </div> )} </div> {/* Footer with total and checkout button */} {cart.length > 0 && ( <div className="sticky bottom 0 bg white border t px 6 py 4 space y 4"> {/* Subtotal */} <div className="flex justify between items center text sm text gray 500"> <span>Sous total</span> <span>${calculateTotal()}</span> </div> {/* Shipping */} <div className="flex justify between items center text sm text gray 500"> <span>Frais de livraison</span> <span>Gratuit</span> </div> {/* Total */} <div className="flex justify between items center pt 4 border t"> <span className="text base font medium text gray 900">Total</span> <div className="text right"> <span className="text 2xl font bold text gray 900"> ${calculateTotal()} </span> <p className="text xs text gray 500">TVA incluse</p> </div> </div> {/* Action buttons */} <div className="space y 3"> <button onClick={handleCheckout} className="w full bg blue 600 text white py 3 rounded xl font semibold hover:bg blue 700 transition colors flex items center justify center gap 2" > Passer à la caisse </button> <button onClick={handleContinueShopping} className="w full bg gray 100 text gray 700 py 3 rounded xl font semibold hover:bg gray 200 transition colors flex items center justify center gap 2" > <FaArrowLeft className="text sm" /> Continuer mes achats </button> </div> {/* Security message */} <p className="text xs text center text gray 500 flex items center justify center gap 1"> <svg xmlns="http://www.w3.org/2000/svg" className="h 4 w 4 text green 600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} > <path strokeLinecap="round" strokeLinejoin="round" d="M12 2a9 9 0 00 9 9v8a9 9 0 0018 0V11a9 9 0 00 9 9z" /> </svg> Transactions sécurisées </p> </div> )} </div> </> ); }; export default Cart

Prompt
Component Preview

About

DefaultComponent - A responsive cart component built with React and Tailwind, featuring item management, total calculation, and secure c. Start coding now!

Share

Last updated 1 month ago