diff --git a/package-lock.json b/package-lock.json index f2ead90..484b00d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,6 @@ "jsonwebtoken": "^9.0.2", "lottie-react": "^2.4.0", "lucide-react": "^0.474.0", - "mohamed-mujtaba-coffee-shop": "file:", "mongodb": "^6.18.0", "mongoose": "^8.0.3", "openai": "^4.78.0", @@ -16230,10 +16229,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mohamed-mujtaba-coffee-shop": { - "resolved": "", - "link": true - }, "node_modules/mongodb": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", diff --git a/package.json b/package.json index c76d34c..4462b72 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "jsonwebtoken": "^9.0.2", "lottie-react": "^2.4.0", "lucide-react": "^0.474.0", - "mohamed-mujtaba-coffee-shop": "file:", "mongodb": "^6.18.0", "mongoose": "^8.0.3", "openai": "^4.78.0", diff --git a/src/App.js b/src/App.js index 1e09722..deada4a 100644 --- a/src/App.js +++ b/src/App.js @@ -37,9 +37,9 @@ import ExpertBaristas from './Pages/ExpertBaristas'; import CozyAmbiance from './Pages/CozyAmbiance'; import Feedback from './Pages/Feedback'; import Favorites from './Pages/favorites'; +import Wishlist from './Pages/wishlist'; import Reviews from './componets/Reviews'; import AnimatedCursor from 'react-animated-cursor'; - import Chatbot from "./componets/Chatbot"; // Styled Containers @@ -54,14 +54,11 @@ const ContentContainer = styled.div` flex: 1; `; -// ✅ Enhanced Protected Profile Route with Google Auth const SafeProfileRoute = () => { const { isAuthenticated, user, loading } = useSelector((state) => state.auth); - - // Check both new auth system and legacy localStorage const legacyUserData = localStorage.getItem('user'); const legacyUser = legacyUserData ? JSON.parse(legacyUserData) : null; - + if (loading) { return (
@@ -70,11 +67,9 @@ const SafeProfileRoute = () => { ); } - // Allow access if authenticated through Google Auth OR legacy system return (isAuthenticated && user) || legacyUser ? : ; }; -// ✅ Protected Route Component for other protected pages const ProtectedRoute = ({ children }) => { const { isAuthenticated, user, loading } = useSelector((state) => state.auth); const legacyUserData = localStorage.getItem('user'); @@ -91,10 +86,9 @@ const ProtectedRoute = ({ children }) => { return (isAuthenticated && user) || legacyUser ? children : ; }; -// ✅ Main App Content Component const AppContent = () => { const dispatch = useDispatch(); - const { token, user } = useSelector((state) => state.auth); + const { token, user } = useSelector((state) => state.auth); useEffect(() => { if (token && !user) { @@ -105,32 +99,10 @@ const AppContent = () => { return ( - - - - - + + - {/* Public Routes */} } /> } /> } /> @@ -152,58 +124,29 @@ const AppContent = () => { } /> } /> } /> - - {/* Protected Routes */} } /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> + } /> + } /> + } /> + } /> + } /> - - - {/* Add Chatbot so it floats on every page */} -
- {sectionProducts.map((product) => { - // If there's a search query, only show products that match - if ( - searchQuery && - !product.name - .toLowerCase() - .includes(searchQuery.toLowerCase()) - ) { - return null; - } - return ( + {sectionProducts.map((product) => (
toggleHeart(product.id)} + onClick={() => toggleHeart(product)} style={{ position: "absolute", top: "10px", right: "10px", cursor: "pointer", fontSize: "24px", - color: likedProducts[product.id] ? "red" : "gray", + color: wishlistItems.some(item => item.id === product.id) ? "red" : "gray", zIndex: 2, }} > item.id === product.id) ? "fas" : "far" }`} >
@@ -1354,16 +1031,10 @@ function Shop() { {product.name} ${product.price.toFixed(2)} - - +
- ); - })} + ))}
); diff --git a/src/Pages/cart.js b/src/Pages/cart.js index 43a9d5d..e2dea7c 100644 --- a/src/Pages/cart.js +++ b/src/Pages/cart.js @@ -1,131 +1,280 @@ import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import { removeFromCart, updateQuantity, clearCart } from '../Store/cartSlice'; +import { removeFromCart, updateQuantity, clearCart, moveToWishlist } from '../Store/cartSlice'; import styled from 'styled-components'; import { motion } from 'framer-motion'; import { toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; +import { useNavigate } from 'react-router-dom'; const CartContainer = styled.div` padding: 2rem; max-width: 1200px; margin: 0 auto; + background-color: #fffbeb; + min-height: 100vh; +`; + +const CartHeader = styled.h1` + text-align: center; + color: #7c2214; + margin-bottom: 2rem; + font-size: 2.5rem; + font-weight: bold; +`; + +const EmptyCartMessage = styled.div` + text-align: center; + padding: 3rem; + color: #666; + font-size: 1.2rem; `; const CartItem = styled(motion.div)` display: flex; justify-content: space-between; align-items: center; - padding: 1rem; + padding: 1.5rem; margin-bottom: 1.5rem; background-color: #fff; - border-radius: 8px; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + border-radius: 12px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; + transition: transform 0.2s ease, box-shadow 0.2s ease; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); + } `; const ItemInfo = styled.div` display: flex; align-items: center; + flex: 1; `; const ItemImage = styled.img` - width: 70px; - height: 70px; + width: 80px; + height: 80px; object-fit: cover; - margin-right: 1rem; + margin-right: 1.5rem; border-radius: 8px; + border: 2px solid #f0f0f0; `; -const ItemName = styled.span` +const ItemDetails = styled.div` + flex: 1; +`; + +const ItemName = styled.h3` font-weight: bold; - font-size: 1.1rem; + font-size: 1.2rem; color: #333; + margin-bottom: 0.5rem; `; const ItemPrice = styled.span` + font-size: 1.1rem; + color: #7c2214; + font-weight: 600; +`; + +const ItemTotal = styled.span` + font-size: 1.1rem; + color: #2e7d32; + font-weight: bold; margin-left: 1rem; - font-size: 1rem; - color: #888; +`; + +const QuantityContainer = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + margin: 0 2rem; +`; + +const QuantityButton = styled.button` + background: #7c2214; + color: white; + border: none; + width: 35px; + height: 35px; + border-radius: 50%; + cursor: pointer; + font-size: 1.2rem; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.3s ease; + + &:hover { + background-color: #5e1105; + } + + &:disabled { + background-color: #ccc; + cursor: not-allowed; + } `; const QuantityInput = styled.input` width: 60px; - height: 30px; + height: 35px; text-align: center; - border: 1px solid #ddd; - border-radius: 4px; - margin-right: 1rem; + border: 2px solid #ddd; + border-radius: 6px; font-size: 1rem; padding: 5px; + font-weight: bold; + &:focus { outline: none; - border-color: #ff5722; + border-color: #7c2214; } `; -const RemoveButton = styled(motion.button)` - background-color: #e74c3c; - color: white; +const ActionButtons = styled.div` + display: flex; + gap: 0.5rem; +`; + +const ActionButton = styled(motion.button)` + padding: 0.5rem 1rem; border: none; - padding: 0.5rem; border-radius: 6px; cursor: pointer; font-size: 0.9rem; - transition: background-color 0.3s ease; - &:hover { - background-color: #c0392b; + font-weight: 500; + transition: all 0.3s ease; + + &.remove { + background-color: #e74c3c; + color: white; + &:hover { + background-color: #c0392b; + } + } + + &.wishlist { + background-color: #f39c12; + color: white; + &:hover { + background-color: #e67e22; + } } `; +const SummaryContainer = styled.div` + background: white; + border-radius: 12px; + padding: 2rem; + margin-top: 2rem; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; +`; + const SummaryTable = styled.table` width: 100%; - margin-top: 2rem; border-collapse: collapse; + margin-bottom: 1.5rem; `; const SummaryRow = styled.tr` - border-bottom: 1px solid #ddd; + border-bottom: 1px solid #eee; + + &:last-child { + border-bottom: none; + font-weight: bold; + font-size: 1.2rem; + } `; const SummaryCell = styled.td` - padding: 0.8rem; + padding: 1rem 0; text-align: right; font-size: 1.1rem; color: #333; + + &:first-child { + text-align: left; + font-weight: 500; + } `; const ProceedButton = styled(motion.button)` - background: #7c2214; + background: linear-gradient(145deg, #7c2214, #8e2a1a); color: white; border: none; - padding: 1rem; - border-radius: 4px; + padding: 1.2rem 2rem; + border-radius: 8px; cursor: pointer; - margin-top: 1rem; width: 100%; - font-size: 1.2rem; + font-size: 1.3rem; + font-weight: bold; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(124, 34, 20, 0.3); + + &:hover { + background: linear-gradient(145deg, #5e1105, #7c2214); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(124, 34, 20, 0.4); + } + + &:disabled { + background: #ccc; + cursor: not-allowed; + transform: none; + box-shadow: none; + } +`; + +const ClearCartButton = styled(motion.button)` + background: #95a5a6; + color: white; + border: none; + padding: 0.8rem 1.5rem; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + margin-top: 1rem; transition: background-color 0.3s ease; + &:hover { - background-color: #5e1105; + background-color: #7f8c8d; } `; function Cart() { const cartItems = useSelector((state) => state.cart.items); const dispatch = useDispatch(); + const navigate = useNavigate(); const handleRemoveFromCart = (productId) => { dispatch(removeFromCart(productId)); - toast.error('Item removed from cart!'); + toast.error('Item removed from cart!', { autoClose: 2000 }); }; const handleUpdateQuantity = (productId, quantity) => { - if (quantity <= 0) { - toast.warn('Quantity must be greater than 0'); + if (quantity < 0) { + toast.warn('Quantity cannot be negative', { autoClose: 2000 }); return; } dispatch(updateQuantity({ productId, quantity: parseInt(quantity) })); - toast.info('Cart updated!'); + toast.info('Cart updated!', { autoClose: 2000 }); + }; + + const handleMoveToWishlist = (productId) => { + dispatch(moveToWishlist(productId)); + toast.success('Item moved to wishlist!', { autoClose: 2000 }); + }; + + const handleClearCart = () => { + if (window.confirm('Are you sure you want to clear your cart?')) { + dispatch(clearCart()); + toast.info('Cart cleared!', { autoClose: 2000 }); + } }; const totalPrice = cartItems.reduce( @@ -138,72 +287,142 @@ function Cart() { const finalPrice = totalPrice + SGST + CGST; const handleProceedToPayment = () => { - alert('Payment is processing...'); - dispatch(clearCart()); - toast.success('Thank you for your purchase!'); + if (cartItems.length === 0) { + toast.warn('Your cart is empty!', { autoClose: 2000 }); + return; + } + + // Navigate to checkout page + navigate('/checkout'); + toast.success('Proceeding to checkout!', { autoClose: 2000 }); }; return ( -

Your Cart

+ 🛒 Your Shopping Cart + {cartItems.length === 0 ? ( -

Your cart is empty. Please add some items!

- ) : ( - cartItems.map((item) => ( - +

Your cart is empty

+

Add some delicious items to get started!

+ navigate('/shop')} + style={{ + background: '#7c2214', + color: 'white', + border: 'none', + padding: '1rem 2rem', + borderRadius: '8px', + fontSize: '1.1rem', + cursor: 'pointer', + marginTop: '1rem' + }} > - - - {item.name} - ${item.price.toFixed(2)} - - handleUpdateQuantity(item.id, e.target.value)} - /> - + + ) : ( + <> + {cartItems.map((item) => ( + + + + + {item.name} + ${item.price.toFixed(2)} each + Total: ${(item.price * item.quantity).toFixed(2)} + + + + + handleUpdateQuantity(item.id, item.quantity - 1)} + disabled={item.quantity <= 1} + > + - + + handleUpdateQuantity(item.id, e.target.value)} + /> + handleUpdateQuantity(item.id, item.quantity + 1)} + > + + + + + + + handleMoveToWishlist(item.id)} + > + ♡ Wishlist + + handleRemoveFromCart(item.id)} + > + Remove + + + + ))} + + +

Order Summary

+ + + + Subtotal ({cartItems.length} items): + ${totalPrice.toFixed(2)} + + + SGST (9%): + ${SGST.toFixed(2)} + + + CGST (9%): + ${CGST.toFixed(2)} + + + Total Amount: + ${finalPrice.toFixed(2)} + + + + + + 🛒 Proceed to Checkout + + + handleRemoveFromCart(item.id)} + onClick={handleClearCart} > - Remove -
-
- )) + Clear Cart + + + )} - - - - Total: - ${totalPrice.toFixed(2)} - - - SGST (9%): - ${SGST.toFixed(2)} - - - CGST (9%): - ${CGST.toFixed(2)} - - - Final Price: - ${finalPrice.toFixed(2)} - - - - - Proceed to Payment -
); } diff --git a/src/Pages/wishlist.js b/src/Pages/wishlist.js new file mode 100644 index 0000000..0bc3518 --- /dev/null +++ b/src/Pages/wishlist.js @@ -0,0 +1,255 @@ +import React from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { removeFromWishlist } from '../Store/cartSlice'; +import styled from 'styled-components'; +import { motion, AnimatePresence } from 'framer-motion'; +import { toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import { useNavigate } from 'react-router-dom'; +import EnhancedAddToCartButton from '../componets/AddToCartButton'; +import "@fortawesome/fontawesome-free/css/all.min.css"; + +const WishlistContainer = styled.div` + padding: 6rem 2rem 4rem 2rem; + max-width: 1200px; + margin: 0 auto; + background-color: #fffbeb; + padding-top: 1.5rem; +`; + +const WishlistHeader = styled(motion.h1)` + font-size: clamp(1.5rem, 5vw, 2.5rem); + margin-bottom: 3rem; + margin-top: 2rem; + padding: 0 1rem; + text-align: center; + color: #78350f; + word-wrap: break-word; + overflow-wrap: break-word; +`; + +const EmptyWishlistMessage = styled.div` + text-align: center; + padding: 3rem; + color: #666; + font-size: 1.2rem; +`; + +const WishlistGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, max-content)); + gap: 2rem; + max-width: 1100px; + margin: 0 auto; + margin-top: 50px; +`; + +const WishlistItem = styled(motion.div)` + background: linear-gradient(145deg, #ffffff, #e6e6e6); + border-radius: 10px; + overflow: hidden; + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 420px; + box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.1), -2px -2px 8px rgba(255, 255, 255, 0.8); + transition: transform 0.3s ease, box-shadow 0.3s ease; + cursor: pointer; + + &:hover { + transform: translateY(-5px); + box-shadow: 6px 6px 15px rgba(0, 0, 0, 0.15), -4px -4px 12px rgba(255, 255, 255, 0.9); + } + + &:hover .overlay { + opacity: 1; + } +`; + +const ItemImage = styled(motion.img)` + width: 100%; + height: 220px; + object-fit: cover; + border: 2px solid rgb(65, 21, 5); + border-radius: 12px; + box-sizing: border-box; + background-color: #f5f5f5; + transition: transform 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease, + filter 0.4s ease; + + &:hover { + transform: scale(1.05); + border-color: #6d4c41; + box-shadow: 0 8px 20px rgba(109, 76, 65, 0.3); + filter: brightness(1.03) contrast(1.05); + } +`; + +const Overlay = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.8); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.3s ease; + padding: 1rem; + text-align: center; +`; + +const OverlayText = styled.p` + font-size: 1rem; + color: #333; + text-align: center; +`; + +const ItemName = styled.h3` + font-size: 1.4rem; + margin-bottom: 0.5rem; + font-weight: 600; + box-sizing: border-box; + color: #333; +`; + +const ItemInfo = styled.div` + padding: 1.25rem; + background: url("https://png.pngtree.com/thumb_back/fh260/background/20231205/pngtree-creamy-textured-milk-colored-background-image_13815875.png"); + background-size: cover; + display: flex; + flex-direction: column; + justify-content: space-between; + flex: 1; + min-height: 200px; +`; + +const ItemPrice = styled.p` + font-size: 1.1rem; + color: #4a2c2a; + margin-bottom: 1rem; + font-weight: 600; +`; + +function Wishlist() { + const wishlistItems = useSelector((state) => state.cart.wishlist); + const dispatch = useDispatch(); + const navigate = useNavigate(); + + const handleRemoveFromWishlist = (itemId, itemName) => { + dispatch(removeFromWishlist(itemId)); + toast.error(`${itemName} removed from wishlist!`, { autoClose: 2000 }); + }; + + console.log('Wishlist items:', wishlistItems); // Debug log + + return ( + + + 💝 Your Wishlist ({wishlistItems.length} items) + + + {wishlistItems.length === 0 ? ( + +

Your wishlist is empty

+

Start adding items to your wishlist while shopping!

+ navigate('/shop')} + style={{ + background: '#7c2214', + color: 'white', + border: 'none', + padding: '1rem 2rem', + borderRadius: '8px', + fontSize: '1.1rem', + cursor: 'pointer', + marginTop: '1rem' + }} + > + Start Shopping + +
+ ) : ( + + + {wishlistItems.map((item) => ( + +
+ + +
handleRemoveFromWishlist(item.id, item.name)} + style={{ + position: "absolute", + top: "10px", + right: "10px", + cursor: "pointer", + fontSize: "24px", + color: "#e74c3c", + zIndex: 2, + background: "rgba(255, 255, 255, 0.9)", + borderRadius: "50%", + width: "40px", + height: "40px", + display: "flex", + alignItems: "center", + justifyContent: "center", + transition: "all 0.3s ease", + }} + onMouseEnter={(e) => { + e.target.style.transform = "scale(1.1)"; + e.target.style.background = "rgba(255, 255, 255, 1)"; + }} + onMouseLeave={(e) => { + e.target.style.transform = "scale(1)"; + e.target.style.background = "rgba(255, 255, 255, 0.9)"; + }} + > + +
+ + + {item.description || 'A delicious item from our menu.'} + +
+ + + {item.name} + ${item.price.toFixed(2)} + + +
+ ))} +
+
+ )} +
+ ); +} + +export default Wishlist; \ No newline at end of file diff --git a/src/Store/cartSlice.js b/src/Store/cartSlice.js index b0c6a4e..8524be3 100644 --- a/src/Store/cartSlice.js +++ b/src/Store/cartSlice.js @@ -1,9 +1,32 @@ import { createSlice } from '@reduxjs/toolkit'; +// Load cart from localStorage if available +const loadCartFromStorage = () => { + try { + const cartData = localStorage.getItem('mscafe_cart'); + return cartData ? JSON.parse(cartData) : { items: [] }; + } catch (error) { + console.error('Error loading cart from localStorage:', error); + return { items: [] }; + } +}; + +// Load wishlist from localStorage if available +const loadWishlistFromStorage = () => { + try { + const wishlistData = localStorage.getItem('mscafe_wishlist'); + return wishlistData ? JSON.parse(wishlistData) : { items: [] }; + } catch (error) { + console.error('Error loading wishlist from localStorage:', error); + return { items: [] }; + } +}; + const cartSlice = createSlice({ name: 'cart', initialState: { - items: [], + items: loadCartFromStorage().items, + wishlist: loadWishlistFromStorage().items, }, reducers: { addToCart: (state, action) => { @@ -13,22 +36,84 @@ const cartSlice = createSlice({ } else { state.items.push({ ...action.payload, quantity: 1 }); } + // Save to localStorage + localStorage.setItem('mscafe_cart', JSON.stringify({ items: state.items })); }, removeFromCart: (state, action) => { state.items = state.items.filter(item => item.id !== action.payload); + // Save to localStorage + localStorage.setItem('mscafe_cart', JSON.stringify({ items: state.items })); }, updateQuantity: (state, action) => { const { productId, quantity } = action.payload; const item = state.items.find(item => item.id === productId); if (item) { - item.quantity = quantity; - } + if (quantity <= 0) { + // Remove item if quantity is 0 or less + state.items = state.items.filter(item => item.id !== productId); + } else { + item.quantity = quantity; + } + // Save to localStorage + localStorage.setItem('mscafe_cart', JSON.stringify({ items: state.items })); + } }, clearCart: (state) => { state.items = []; + // Save to localStorage + localStorage.setItem('mscafe_cart', JSON.stringify({ items: state.items })); + }, + // Wishlist functionality + addToWishlist: (state, action) => { + const existingItem = state.wishlist.find(item => item.id === action.payload.id); + if (!existingItem) { + state.wishlist.push({ ...action.payload, quantity: 1 }); + // Save to localStorage + localStorage.setItem('mscafe_wishlist', JSON.stringify({ items: state.wishlist })); + } + }, + removeFromWishlist: (state, action) => { + state.wishlist = state.wishlist.filter(item => item.id !== action.payload); + // Save to localStorage + localStorage.setItem('mscafe_wishlist', JSON.stringify({ items: state.wishlist })); + }, + moveToWishlist: (state, action) => { + const itemToMove = state.items.find(item => item.id === action.payload); + if (itemToMove) { + // Add to wishlist + const existingWishlistItem = state.wishlist.find(item => item.id === action.payload); + if (!existingWishlistItem) { + state.wishlist.push({ ...itemToMove, quantity: 1 }); + } + // Remove from cart + state.items = state.items.filter(item => item.id !== action.payload); + // Save both to localStorage + localStorage.setItem('mscafe_cart', JSON.stringify({ items: state.items })); + localStorage.setItem('mscafe_wishlist', JSON.stringify({ items: state.wishlist })); + } + }, + // Buy Now functionality - adds item to cart and redirects + buyNow: (state, action) => { + const existingItem = state.items.find(item => item.id === action.payload.id); + if (existingItem) { + existingItem.quantity += 1; + } else { + state.items.push({ ...action.payload, quantity: 1 }); + } + // Save to localStorage + localStorage.setItem('mscafe_cart', JSON.stringify({ items: state.items })); }, }, }); -export const { addToCart, removeFromCart, updateQuantity, clearCart } = cartSlice.actions; +export const { + addToCart, + removeFromCart, + updateQuantity, + clearCart, + addToWishlist, + removeFromWishlist, + moveToWishlist, + buyNow +} = cartSlice.actions; export default cartSlice.reducer; \ No newline at end of file diff --git a/src/componets/AddToCartButton.js b/src/componets/AddToCartButton.js new file mode 100644 index 0000000..53240a8 --- /dev/null +++ b/src/componets/AddToCartButton.js @@ -0,0 +1,201 @@ +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { addToCart, updateQuantity } from '../Store/cartSlice'; +import { toast } from 'react-toastify'; +import styled from 'styled-components'; +import { motion } from 'framer-motion'; +import { useNavigate } from 'react-router-dom'; + +const ButtonContainer = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.5rem; +`; + +const AddToCartButton = styled(motion.button)` + background: linear-gradient(145deg, rgb(51, 15, 15), rgb(46, 22, 22)); + color: white; + border: none; + padding: 0.6rem 1.2rem; + font-size: 1rem; + border-radius: 20px; + cursor: pointer; + letter-spacing: 0.6px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); + transition: background 0.3s ease, transform 0.2s ease; + font-weight: 600; + + &:hover { + background: linear-gradient(145deg, #7d5858, #8e6a6a); + transform: scale(1.05); + } + + &:active { + transform: scale(0.98); + box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3); + } + + &:disabled { + background: #ccc; + cursor: not-allowed; + transform: none; + } +`; + +const QuantitySelector = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + background: white; + border-radius: 20px; + padding: 0.3rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border: 2px solid #7c2214; +`; + +const QuantityButton = styled.button` + background: #7c2214; + color: white; + border: none; + width: 28px; + height: 28px; + border-radius: 50%; + cursor: pointer; + font-size: 1rem; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.3s ease; + + &:hover { + background-color: #5e1105; + } + + &:disabled { + background-color: #ccc; + cursor: not-allowed; + } +`; + +const QuantityDisplay = styled.span` + font-weight: bold; + color: #7c2214; + min-width: 30px; + text-align: center; + font-size: 1rem; +`; + +const BuyNowButton = styled(motion.button)` + background: linear-gradient(145deg, rgb(51, 15, 15), rgb(46, 22, 22)); + color: white; + border: none; + padding: 0.6rem 1.2rem; + font-size: 1rem; + border-radius: 20px; + cursor: pointer; + letter-spacing: 0.6px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); + transition: background 0.3s ease, transform 0.2s ease; + font-weight: 600; + margin-left: 0.5rem; + + &:hover { + background: linear-gradient(145deg, #7d5858, #8e6a6a); + transform: scale(1.05); + } + + &:active { + transform: scale(0.98); + box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3); + } +`; + +const EnhancedAddToCartButton = ({ product }) => { + const dispatch = useDispatch(); + const cartItems = useSelector((state) => state.cart.items); + const [isInCart, setIsInCart] = useState(false); + const navigate = useNavigate(); + + // Check if product is already in cart + React.useEffect(() => { + const cartItem = cartItems.find(item => item.id === product.id); + setIsInCart(!!cartItem); + }, [cartItems, product.id]); + + const handleAddToCart = () => { + dispatch(addToCart(product)); + toast.success(`${product.name} added to cart!`, { autoClose: 2000 }); + }; + + const handleQuantityChange = (newQuantity) => { + if (newQuantity <= 0) { + // Remove from cart if quantity is 0 + dispatch(updateQuantity({ productId: product.id, quantity: 0 })); + toast.info(`${product.name} removed from cart!`, { autoClose: 2000 }); + } else { + dispatch(updateQuantity({ productId: product.id, quantity: newQuantity })); + toast.info(`Quantity updated to ${newQuantity}!`, { autoClose: 2000 }); + } + }; + + const handleBuyNow = () => { + dispatch(addToCart(product)); + toast.success(`${product.name} added to cart! Redirecting to checkout...`, { autoClose: 2000 }); + // Navigate to cart page after a short delay + setTimeout(() => { + navigate('/cart'); + }, 1000); + }; + + const currentQuantity = cartItems.find(item => item.id === product.id)?.quantity || 0; + + if (!isInCart) { + return ( + + + Add to Cart + + + Buy Now + + + ); + } + + return ( + + + handleQuantityChange(currentQuantity - 1)} + > + - + + {currentQuantity} + handleQuantityChange(currentQuantity + 1)} + > + + + + + + Buy Now + + + ); +}; + +export default EnhancedAddToCartButton; \ No newline at end of file diff --git a/src/componets/FloatingCart.js b/src/componets/FloatingCart.js new file mode 100644 index 0000000..0bcb53d --- /dev/null +++ b/src/componets/FloatingCart.js @@ -0,0 +1,311 @@ +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import styled from 'styled-components'; +import { motion, AnimatePresence } from 'framer-motion'; +import { FaShoppingCart, FaHeart, FaTimes } from 'react-icons/fa'; + +const FloatingContainer = styled.div` + position: fixed; + top: 100px; + right: 20px; + z-index: 1000; + display: flex; + flex-direction: column; + gap: 1rem; +`; + +const FloatingButton = styled(motion.button)` + width: 60px; + height: 60px; + border-radius: 50%; + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + transition: all 0.3s ease; + position: relative; + + &.cart { + background: linear-gradient(145deg, #7c2214, #8e2a1a); + color: white; + + &:hover { + background: linear-gradient(145deg, #5e1105, #7c2214); + transform: scale(1.1); + } + } + + &.wishlist { + background: linear-gradient(145deg, #e91e63, #f06292); + color: white; + + &:hover { + background: linear-gradient(145deg, #c2185b, #e91e63); + transform: scale(1.1); + } + } +`; + +const Badge = styled.span` + position: absolute; + top: -5px; + right: -5px; + background: #ff5722; + color: white; + border-radius: 50%; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + font-weight: bold; + border: 2px solid white; +`; + +const MiniCart = styled(motion.div)` + position: absolute; + top: 70px; + right: 0; + width: 300px; + background: white; + border-radius: 12px; + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); + border: 1px solid #e0e0e0; + overflow: hidden; +`; + +const MiniCartHeader = styled.div` + background: linear-gradient(145deg, #7c2214, #8e2a1a); + color: white; + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +`; + +const MiniCartTitle = styled.h3` + margin: 0; + font-size: 1.1rem; + font-weight: 600; +`; + +const CloseButton = styled.button` + background: none; + border: none; + color: white; + cursor: pointer; + font-size: 1.2rem; + padding: 0; + display: flex; + align-items: center; + justify-content: center; +`; + +const MiniCartContent = styled.div` + max-height: 300px; + overflow-y: auto; + padding: 1rem; +`; + +const MiniCartItem = styled.div` + display: flex; + align-items: center; + gap: 0.8rem; + padding: 0.8rem 0; + border-bottom: 1px solid #f0f0f0; + + &:last-child { + border-bottom: none; + } +`; + +const MiniCartItemImage = styled.img` + width: 40px; + height: 40px; + object-fit: cover; + border-radius: 6px; + border: 1px solid #e0e0e0; +`; + +const MiniCartItemInfo = styled.div` + flex: 1; +`; + +const MiniCartItemName = styled.div` + font-weight: 600; + font-size: 0.9rem; + color: #333; + margin-bottom: 0.2rem; +`; + +const MiniCartItemPrice = styled.div` + font-size: 0.8rem; + color: #7c2214; + font-weight: 600; +`; + +const MiniCartItemQuantity = styled.div` + font-size: 0.8rem; + color: #666; +`; + +const MiniCartFooter = styled.div` + padding: 1rem; + border-top: 1px solid #e0e0e0; + background: #f9f9f9; +`; + +const MiniCartTotal = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + font-weight: 600; + color: #333; +`; + +const ViewCartButton = styled(motion.button)` + width: 100%; + background: linear-gradient(145deg, #7c2214, #8e2a1a); + color: white; + border: none; + padding: 0.8rem; + border-radius: 8px; + cursor: pointer; + font-weight: 600; + transition: all 0.3s ease; + + &:hover { + background: linear-gradient(145deg, #5e1105, #7c2214); + transform: translateY(-2px); + } +`; + +const EmptyCartMessage = styled.div` + text-align: center; + padding: 2rem; + color: #666; + font-size: 0.9rem; +`; + +const FloatingCart = () => { + const cartItems = useSelector((state) => state.cart.items); + const wishlistItems = useSelector((state) => state.cart.wishlist); + const navigate = useNavigate(); + const [showMiniCart, setShowMiniCart] = useState(false); + + const cartItemCount = cartItems.reduce((total, item) => total + item.quantity, 0); + const wishlistCount = wishlistItems.length; + + const totalPrice = cartItems.reduce( + (total, item) => total + item.price * item.quantity, + 0 + ); + + const handleCartClick = () => { + if (cartItems.length === 0) { + navigate('/cart'); + } else { + setShowMiniCart(!showMiniCart); + } + }; + + const handleWishlistClick = () => { + navigate('/wishlist'); + }; + + const handleViewCart = () => { + setShowMiniCart(false); + navigate('/cart'); + }; + + return ( + + + + {cartItemCount > 0 && {cartItemCount}} + + + + + {wishlistCount > 0 && {wishlistCount}} + + + + {showMiniCart && ( + + + 🛒 Mini Cart + setShowMiniCart(false)}> + + + + + + {cartItems.length === 0 ? ( + +

Your cart is empty

+

Add some items to get started!

+
+ ) : ( + cartItems.slice(0, 3).map((item) => ( + + + + {item.name} + ${item.price.toFixed(2)} + Qty: {item.quantity} + + + )) + )} + {cartItems.length > 3 && ( +
+ +{cartItems.length - 3} more items +
+ )} +
+ + {cartItems.length > 0 && ( + + + Total: + ${totalPrice.toFixed(2)} + + + View Full Cart + + + )} +
+ )} +
+
+ ); +}; + +export default FloatingCart; \ No newline at end of file diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..ea8b839 --- /dev/null +++ b/vercel.json @@ -0,0 +1,14 @@ +{ + "buildCommand": "npm run build", + "outputDirectory": "build", + "framework": "create-react-app", + "rewrites": [ + { + "source": "/(.*)", + "destination": "/index.html" + } + ], + "env": { + "REACT_APP_GOOGLE_CLIENT_ID": "@react_app_google_client_id" + } +} \ No newline at end of file