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 */}
-
);
};
-// ✅ Main App Component with Google OAuth Provider
function App() {
+ // Handle Google OAuth client ID for different environments
+ const googleClientId = process.env.REACT_APP_GOOGLE_CLIENT_ID ||
+ process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID ||
+ "placeholder_client_id";
+
return (
-
+
@@ -214,4 +157,4 @@ function App() {
);
}
-export default App;
\ No newline at end of file
+export default App;
diff --git a/src/Pages/Shop.js b/src/Pages/Shop.js
index cfc3f00..910bab6 100644
--- a/src/Pages/Shop.js
+++ b/src/Pages/Shop.js
@@ -1,9 +1,11 @@
import React, { useState, useRef } from "react"; // import useState, useRef hooks
-import { useDispatch } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import styled from "styled-components";
import { motion } from "framer-motion";
-import { addToCart } from "../Store/cartSlice";
+import { addToWishlist, removeFromWishlist } from "../Store/cartSlice";
import Button from "../componets/Button";
+import EnhancedAddToCartButton from "../componets/AddToCartButton";
+import FloatingCart from "../componets/FloatingCart";
import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import DynamicText from "./dynamicText";
@@ -126,28 +128,7 @@ const ProductPrice = styled.p`
font-weight: 600;
`;
-const StyledButton = styled.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;
-
- &: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 products = [
{
@@ -790,270 +771,10 @@ const products = [
},
];
-// Tea Section
-const product1 = [
- {
- id: 19,
- name: "Chai",
- price: 7.3,
- image:
- "https://img.freepik.com/free-photo/frappe-glass-slices-bread-with-seeds_23-2148623233.jpg?ga=GA1.1.1542821208.1727756299&semt=ais_hybrid",
- description:
- "Spiced black tea brewed with milk and sweetened, aromatic and comforting.",
- },
- {
- id: 20,
- name: "Lemon Tea",
- price: 4.1,
- image:
- "https://img.freepik.com/free-photo/cup-hot-mint-tea_144627-34462.jpg?ga=GA1.1.1542821208.1727756299&semt=ais_hybrid ",
- description:
- "Refreshing black tea infused with lemon, perfect for a soothing experience.",
- },
- {
- id: 21,
- name: "Green Tea",
- price: 3.4,
- image:
- "https://img.freepik.com/free-photo/cup-green-tea_144627-34463.jpg?ga=GA1.1.1542821208.1727756299&semt=ais_hybrid ",
- description:
- "Light and delicate, made from unfermented tea leaves, rich in antioxidants.",
- },
- {
- id: 22,
- name: "Black Tea",
- price: 4.5,
- image:
- "https://img.freepik.com/free-photo/cup-black-tea_144627-34464.jpg?ga=GA1.1.1542821208.1727756299&semt=ais_hybrid ",
- description:
- "Strong and full-bodied, made from fully oxidized tea leaves, classic and robust.",
- },
- {
- id: 23,
- name: "Herbal Tea",
- price: 5.5,
- image:
- "https://img.freepik.com/premium-photo/black-tea-cup-glass-mint-tea-leaves-white-isolated_127657-17608.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid ",
- description:
- "Caffeine-free tea made from herbs, fruits, or spices, naturally soothing.",
- },
- {
- id: 24,
- name: "Iced Tea",
- price: 5.6,
- image:
- "https://img.freepik.com/free-vector/long-island-ice-tea-cocktail-realistic_1284-3888.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid ",
- description:
- "Refreshing chilled tea, often sweetened and served with lemon, perfect for hot days.",
- },
-];
-// Milkshake and Smothiee
-const product2 = [
- {
- id: 26,
- name: "Strawberry smoothie",
- price: 6.2,
- image:
- "https://www.eatingwell.com/thmb/TBp6lbiwoYPhRP4N__4sROiUDhA=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/mixed-berry-breakfast-smoothie-7959466-1x1-e0ad2304222e49508cda7b73b21de921.jpg",
- description:
- "Creamy and sweet, made with fresh strawberries, yogurt, and a touch of honey.",
- },
- {
- id: 27,
- name: "Mango smoothie",
- price: 3.2,
- image:
- "https://cdn.loveandlemons.com/wp-content/uploads/2023/05/mango-smoothie.jpg",
- description:
- "Tropical and refreshing, blended with ripe mangoes, banana, and coconut milk.",
- },
- {
- id: 28,
- name: "Strawberry banana smoothie",
- price: 6.45,
- image:
- "https://www.purelykaylie.com/wp-content/uploads/2023/07/strawberry-banana-smoothie-bowl-5.jpg",
- description:
- "A classic combination of strawberries and bananas, creamy and naturally sweet.",
- },
- {
- id: 29,
- name: "Creamy, Nutty Coffee Smoothie",
- price: 7.2,
- image:
- "https://www.seriouseats.com/thmb/dwKjOOPQu1ki3pSf1M4eB7FGVzI=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/20240206-SEA-Coffee-Smoothie-hero-27d1864a41cc411ea7ff0c64ada77a2e.jpg",
- description:
- "A rich blend of coffee, nuts, and cream, perfect for a morning energy boost.",
- },
- {
- id: 30,
- name: "Coffee Smoothie",
- price: 6.3,
- image:
- "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT-Y9in1wQf-XCl9sdyuw5pXWT_CrYn8P5j7A&s",
- description:
- "Creamy and caffeinated, made with cold brew, banana, and almond milk.",
- },
- {
- id: 31,
- name: "Chocolate Milkshake",
- price: 5.2,
- image:
- "https://www.sharmispassions.com/wp-content/uploads/2012/07/chocolate-milkshake1.jpg",
- description:
- "Rich and indulgent, made with chocolate ice cream, milk, and whipped cream.",
- },
- {
- id: 32,
- name: "Oreo Milkshake",
- price: 5.2,
- image:
- "https://www.solara.in/cdn/shop/articles/Oreo_Milkshake.jpg?v=1715757748&width=2048",
- description:
- "Creamy and delicious, blended with Oreo cookies, ice cream, and milk.",
- },
- {
- id: 33,
- name: "Strawberry Oreo Milkshake",
- price: 2.6,
- image:
- "https://marleysmenu.com/wp-content/uploads/2021/08/Strawberry-Oreo-Milkshake-Featured-Image.jpg",
- description:
- "A sweet blend of strawberries, Oreo cookies, and ice cream, perfect for dessert lovers.",
- },
- {
- id: 34,
- name: "Mixed Nut and Fruit Milkshake",
- price: 8.2,
- image:
- "https://images.mrcook.app/recipe-image/018d50f7-344f-7744-97e3-1f89e5a3cf29",
- description:
- "A nutritious blend of mixed nuts, fruits, and milk, creamy and satisfying.",
- },
- {
- id: 35,
- name: "Peanut Butter Milkshake",
- price: 5.8,
- image:
- "https://www.julieseatsandtreats.com/wp-content/uploads/2021/08/Peanut-Butter-Milkshake-Square.jpg",
- description:
- "Rich and creamy, made with peanut butter, ice cream, and milk, a peanut butter lover's dream.",
- },
-];
-// Cake Section
-const product3 = [
- {
- id: 36,
- name: "Oreo cheese cake",
- price: 9.2,
- image:
- "https://handletheheat.com/wp-content/uploads/2015/11/oreo-cheesecake-recipe-SQUARE.jpg",
- description:
- "Creamy cheesecake with an Oreo crust and topping, rich and indulgent.",
- },
- {
- id: 37,
- name: "Chocolate cake",
- price: 7.2,
- image:
- "https://img.freepik.com/free-photo/chocolate-cake_1203-8942.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Moist and decadent, a classic chocolate cake perfect for any celebration.",
- },
- {
- id: 38,
- name: "Red velvet cake",
- price: 4.2,
- image:
- "https://img.freepik.com/free-photo/top-view-red-strawberry-cake-delicious-with-tea-table-fruit-color-cake-biscuit-sweet_140725-28319.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Rich and velvety, a moist red cake with cream cheese frosting, elegant and delicious.",
- },
- {
- id: 39,
- name: "Cheese cake",
- price: 8.2,
- image:
- "https://img.freepik.com/premium-photo/citrus-cheesecake-cake-with-kumquats_82780-1574.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Creamy and smooth, a classic cheesecake with a graham cracker crust, perfect for dessert.",
- },
- {
- id: 40,
- name: "Blueberry cake",
- price: 3.2,
- image:
- "https://img.freepik.com/premium-photo/pieces-pie-from-cottage-cheese-blueberries_116441-1516.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Moist and bursting with blueberries, a sweet and tangy cake perfect for any occasion.",
- },
- {
- id: 41,
- name: "Strawberry cake",
- price: 6,
- image:
- "https://img.freepik.com/free-photo/delicious-cake-with-strawberries_23-2150797874.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Light and fluffy, a sweet strawberry cake with creamy frosting, perfect for summer.",
- },
-];
-// Soup Section
-const product4 = [
- {
- id: 42,
- name: "Salad",
- price: 7.3,
- image:
- "https://img.freepik.com/free-photo/dietary-salad-with-tomatoes-feta-lettuce-spinach-pine-nuts_2829-20128.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Fresh and healthy, a mix of greens, vegetables, and a tangy dressing, perfect for a light meal.",
- },
- {
- id: 43,
- name: "Tomato soup",
- price: 6.7,
- image:
- "https://img.freepik.com/free-photo/portrait-shooting-tomato-soup-with-crackers-cheese-tomatoes-bread-table_141793-2858.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Warm and comforting, a classic tomato soup perfect for a cozy meal, often served with grilled cheese.",
- },
- {
- id: 44,
- name: "Chicken Noodle soup",
- price: 8.2,
- image:
- "https://img.freepik.com/free-photo/delicious-noodle-soup-with-chicken-uncooked-pasta-small-brown-bowl-spoon-garlic-dark-background_140725-140085.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Hearty and soothing, a classic soup with chicken, noodles, and vegetables, perfect for cold days.",
- },
- {
- id: 45,
- name: "Miso soup",
- price: 7.5,
- image:
- "https://img.freepik.com/free-photo/top-view-japanese-food-bowls-arrangement_23-2148809848.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Savory and umami-rich, a traditional Japanese soup made with miso paste and dashi broth.",
- },
- {
- id: 46,
- name: "Cold cucumber soup",
- price: 7.34,
- image:
- "https://img.freepik.com/free-photo/cold-cucumber-soup-with-dried-tomatoes-mozzarella_2829-14287.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Refreshing and cool, a chilled soup made with cucumbers, yogurt, and herbs, perfect for summer.",
- },
- {
- id: 47,
- name: "Tom Yum Soup",
- price: 9.2,
- image:
- "https://img.freepik.com/free-photo/tom-yum-kung-thai-hot-spicy-soup-shrimp-with-lemon-grass-lemon-galangal-chilli-wooden-table-thailand-food_1150-21078.jpg?ga=GA1.1.900909129.1729318722&semt=ais_hybrid",
- description:
- "Spicy and aromatic, a Thai soup with lemongrass, kaffir lime leaves, and chilies, often with shrimp.",
- },
-];
+
+
+
+
const SearchFilterContainer = styled.div`
display: flex;
@@ -1093,8 +814,9 @@ const SearchButton = styled.button`
function Shop() {
const dispatch = useDispatch();
+ const wishlistItems = useSelector((state) => state.cart.wishlist);
const [category, setCategory] = useState("hot");
- const [searchQuery, setSearchQuery] = useState("");
+
// Added useRef hooks to scroll to sections
const hotBeveragesRef = useRef(null);
@@ -1122,48 +844,22 @@ function Shop() {
}
};
- const handleAddToCart = (product) => {
- dispatch(addToCart(product));
- toast.success(`${product.name} added to cart!`);
- };
-
- const handleClick = (value) => {
- setCategory(value);
- };
- // Filter products based on search query or category
- const filteredProducts = products.filter((product) => {
- // Check if product matches the selected category
- const matchesCategory = category === "all" || product.type === category;
- // Check if product name matches the search query (case insensitive)
- const matchesSearchQuery = product.name
- .toLowerCase()
- .includes(searchQuery.toLowerCase());
- // If search query is provided, only show products matching the query and selected category
- return searchQuery ? matchesSearchQuery : matchesCategory;
- });
-
- const [itemsNo, setItemsNo] = useState(9);
-
- const [likedProducts, setLikedProducts] = useState({});
-
- const toggleHeart = (productId) => {
- setLikedProducts((prevState) => ({
- ...prevState,
- [productId]: !prevState[productId],
- }));
- };
-
- const handleItemsNo = () => {
- const s = products.length;
- if (s == itemsNo) {
- setItemsNo(9);
+ const toggleHeart = (product) => {
+ const isInWishlist = wishlistItems.some(item => item.id === product.id);
+
+ if (isInWishlist) {
+ dispatch(removeFromWishlist(product.id));
+ toast.info(`${product.name} removed from wishlist!`, { autoClose: 2000 });
} else {
- setItemsNo(Math.min(itemsNo + 9, s));
+ dispatch(addToWishlist(product));
+ toast.success(`${product.name} added to wishlist!`, { autoClose: 2000 });
}
};
+
+
// Group products by type (category)
const groupedProducts = products.reduce((acc, product) => {
if (!acc[product.type]) acc[product.type] = [];
@@ -1173,6 +869,7 @@ function Shop() {
return (
+
setSearchQuery(e.target.value)}
/>
console.log("Search clicked!")}>
Search
@@ -1260,15 +955,7 @@ function Shop() {
{Object.keys(groupedProducts).map((section) => {
const sectionProducts = groupedProducts[section];
- // Only display the section if it matches the search query or if the search query is empty
- const matchesSearchQuery = sectionProducts.some((product) =>
- product.name.toLowerCase().includes(searchQuery.toLowerCase())
- );
- // Skip section if no products match the search query and the search query is not empty
- if (searchQuery && !matchesSearchQuery) {
- return null;
- }
return (
@@ -1298,17 +985,7 @@ function Shop() {
- {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