From 7039c863653be7f0ae9c239caea197a82f24154d Mon Sep 17 00:00:00 2001 From: ROSHAN GARG Date: Fri, 16 Aug 2024 13:42:45 +0530 Subject: [PATCH] redux implemented --- .eslintrc.js | 3 + package.json | 2 + src/components/AppHeader.js | 9 + src/components/header/AppHeaderDropdown.js | 2 +- src/index.css | 16 ++ src/index.js | 1 + src/redux-store/CartStore/ducs.js | 126 +++++++++++ src/routes.js | 5 + src/store.js | 12 +- src/views/pages/cart/addressAndPayment.js | 167 ++++++++++++++ src/views/pages/cart/cart.js | 248 +++++++++++++++++++++ src/views/pages/cart/orderConfirmation.js | 134 +++++++++++ src/views/pages/cart/reviewOrder.js | 139 ++++++++++++ src/views/pages/cart/shopingCart.js | 191 ++++++++++++++++ src/views/shops/Shop.js | 137 +++++++++--- src/views/shops/shopCard.js | 17 +- 16 files changed, 1174 insertions(+), 35 deletions(-) create mode 100644 src/index.css create mode 100644 src/redux-store/CartStore/ducs.js create mode 100644 src/views/pages/cart/addressAndPayment.js create mode 100644 src/views/pages/cart/cart.js create mode 100644 src/views/pages/cart/orderConfirmation.js create mode 100644 src/views/pages/cart/reviewOrder.js create mode 100644 src/views/pages/cart/shopingCart.js diff --git a/.eslintrc.js b/.eslintrc.js index e9c55ee..3137169 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,6 +18,9 @@ module.exports = { ], plugins: ['react', 'react-hooks'], rules: { + rules: { + 'react/react-in-jsx-scope': 'off', + }, // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs // e.g. "@typescript-eslint/explicit-function-return-type": "off", }, diff --git a/package.json b/package.json index 17d997b..3787f7d 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,8 @@ "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", "redux": "5.0.1", + "redux-devtools-extension": "^2.13.9", + "redux-thunk": "^3.1.0", "simplebar-react": "^3.2.5", "sweetalert2": "^11.12.3" }, diff --git a/src/components/AppHeader.js b/src/components/AppHeader.js index 4bad2fb..60e1b30 100644 --- a/src/components/AppHeader.js +++ b/src/components/AppHeader.js @@ -24,9 +24,11 @@ import { cilMoon, cilSun, } from '@coreui/icons' +import ShoppingCartIcon from '@mui/icons-material/ShoppingCart' import { AppBreadcrumb } from './index' import { AppHeaderDropdown } from './header/index' +import { IconButton } from '@mui/material' const AppHeader = () => { const headerRef = useRef() @@ -51,6 +53,7 @@ const AppHeader = () => { > + {/* @@ -130,6 +133,12 @@ const AppHeader = () => {
*/} + + + + + +
diff --git a/src/components/header/AppHeaderDropdown.js b/src/components/header/AppHeaderDropdown.js index 6c7d71c..d5171b4 100644 --- a/src/components/header/AppHeaderDropdown.js +++ b/src/components/header/AppHeaderDropdown.js @@ -56,7 +56,7 @@ const AppHeaderDropdown = () => { return ( -
+
(dispatch, getState) => { + dispatch({ + type: cartactionTypes.ADD_TO_CART, + payload: product, + }) + localStorage.setItem('cart', JSON.stringify(getState().cart.items)) +} + +export const removeFromCart = (productId) => (dispatch, getState) => { + dispatch({ + type: cartactionTypes.REMOVE_FROM_CART, + payload: productId, + }) + localStorage.setItem('cart', JSON.stringify(getState().cart.items)) +} + +export const increaseCount = (productId) => (dispatch, getState) => { + dispatch({ + type: cartactionTypes.INCREASE_COUNT, + payload: productId, + }) + localStorage.setItem('cart', JSON.stringify(getState().cart.items)) +} + +export const decreaseCount = (productId) => (dispatch, getState) => { + dispatch({ + type: cartactionTypes.DECREASE_COUNT, + payload: productId, + }) + localStorage.setItem('cart', JSON.stringify(getState().cart.items)) +} + +export const loadCart = () => (dispatch) => { + const cartItems = JSON.parse(localStorage.getItem('cart')) || [] + dispatch({ + type: cartactionTypes.LOAD_CART, + payload: cartItems, + }) +} + +export const clearCart = () => (dispatch) => { + dispatch({ + type: cartactionTypes.CLEAR_CART, + }) + localStorage.removeItem('cart') +} + +export const cartReducer = (state = initialState, action) => { + switch (action.type) { + case cartactionTypes.LOAD_CART: + return { + ...state, + items: action.payload, + } + case cartactionTypes.ADD_TO_CART: + const existingItem = state.items.find((item) => item._id === action.payload._id) + if (existingItem) { + return { + ...state, + items: state.items.map((item) => + item._id === action.payload._id ? { ...item, count: item.count + 1 } : item, + ), + } + } else { + return { + ...state, + items: [...state.items, { ...action.payload, count: 1 }], + } + } + case cartactionTypes.REMOVE_FROM_CART: + return { + ...state, + items: state.items.filter((item) => item._id !== action.payload), + } + case cartactionTypes.INCREASE_COUNT: + return { + ...state, + items: state.items.map((item) => + item._id === action.payload ? { ...item, count: item.count + 1 } : item, + ), + } + case cartactionTypes.DECREASE_COUNT: + return { + ...state, + items: state.items.map((item) => + item._id === action.payload && item.count > 1 ? { ...item, count: item.count - 1 } : item, + ), + } + case cartactionTypes.CLEAR_CART: + return { + ...state, + items: [], + } + default: + return state + } +} + +// src/store/cart/selectors.js + +// Selector to get all cart items +export const selectCartItems = (state) => state.cart.items + +// Selector to get the total count of items in the cart +export const selectCartItemCount = (state) => + state.cart.items.reduce((total, item) => total + item.count, 0) + +// Selector to get the subtotal (sum of price * count) of items in the cart +export const selectCartSubtotal = (state) => + state.cart.items.reduce((total, item) => total + item.price * item.count, 0) + +// Selector to check if a specific product is already in the cart +export const selectIsProductInCart = (productId) => (state) => + state.cart.items.some((item) => item._id === productId) diff --git a/src/routes.js b/src/routes.js index 6976e7f..534addf 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1,5 +1,8 @@ +import { element } from 'prop-types' import React from 'react' +import Cart from './views/pages/cart/cart' + const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard')) const Shop = React.lazy(() => import('./views/shops/Shop')) const Order = React.lazy(() => import('./views/orders/Order')) @@ -19,6 +22,8 @@ const routes = [ { path: '/my-profile', name: 'Profile', element: MyProfile }, { path: '/change-password', name: 'Change password', element: ChangePassword }, + + { path: '/cart', name: 'Cart', element: Cart }, ] export default routes diff --git a/src/store.js b/src/store.js index 8ad30da..eb3c2df 100644 --- a/src/store.js +++ b/src/store.js @@ -1,4 +1,7 @@ -import { legacy_createStore as createStore } from 'redux' +import { applyMiddleware, combineReducers, legacy_createStore as createStore } from 'redux' +import { cartReducer } from './redux-store/CartStore/ducs' +import { thunk } from 'redux-thunk' +import { composeWithDevTools } from 'redux-devtools-extension' const initialState = { sidebarShow: true, @@ -13,6 +16,9 @@ const changeState = (state = initialState, { type, ...rest }) => { return state } } - -const store = createStore(changeState) +const rootReducer = combineReducers({ + cart: cartReducer, + changeState: changeState, +}) +const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk))) export default store diff --git a/src/views/pages/cart/addressAndPayment.js b/src/views/pages/cart/addressAndPayment.js new file mode 100644 index 0000000..3d69674 --- /dev/null +++ b/src/views/pages/cart/addressAndPayment.js @@ -0,0 +1,167 @@ +import { + Box, + Button, + FormControl, + FormControlLabel, + Grid, + InputLabel, + MenuItem, + Radio, + RadioGroup, + Select, + Typography, + FormHelperText, +} from '@mui/material' +import React, { useState } from 'react' + +const AddressAndPayment = ({ + billTo, + setBillTo, + shipTo, + setShipTo, + paymentMode, + setPaymentMode, + handleTabChange, +}) => { + const [billToError, setBillToError] = useState(false) + const [shipToError, setShipToError] = useState(false) + const [paymentModeError, setPaymentModeError] = useState(false) + + const handleReviewOrderClick = (e) => { + let isValid = true + + // Check if Bill Address is filled + if (billTo === '') { + setBillToError(true) + isValid = false + } else { + setBillToError(false) + } + + // Check if Ship Address is filled + if (shipTo === '') { + setShipToError(true) + isValid = false + } else { + setShipToError(false) + } + + // Check if Payment Mode is selected + if (paymentMode === '') { + setPaymentModeError(true) + isValid = false + } else { + setPaymentModeError(false) + } + + // If all fields are valid, move to the next tab + if (isValid) { + handleTabChange(e, 2) + } + } + + return ( +
+ + + Select Address and Payment Mode + + + + Bill Address + + Bill Address + + {billToError && Bill Address is required} + + + + Ship Address + + Ship Address + + {shipToError && Ship Address is required} + + + {billTo !== '' && ( + + + Selected Bill Address + + {billTo} + + )} + {shipTo !== '' && ( + + + Selected Ship to Address + + {shipTo} + + )} + + Payment Mode + + setPaymentMode(e.target.value)}> + } + label="Online Transfer" + /> + } label="Cheque" /> + } label="Credit" /> + + {paymentModeError && Payment Mode is required} + + + + + + + +
+ ) +} + +export default AddressAndPayment diff --git a/src/views/pages/cart/cart.js b/src/views/pages/cart/cart.js new file mode 100644 index 0000000..451697f --- /dev/null +++ b/src/views/pages/cart/cart.js @@ -0,0 +1,248 @@ +import React, { useEffect, useState } from 'react' +import { Box, Button, colors, Container, IconButton, Tab, Tabs, Typography } from '@mui/material' +import CheckIcon from '@mui/icons-material/Check' +// import ShoppingCart from '../../Components/ShoppingCart' +// import CheckoutDetails from '../../Components/CheckoutDetails' +// import OrderComplete from '../../Components/OrderComplete' +import PropTypes from 'prop-types' +import Checkout from './addressAndPayment' +import ShopingCart from './shopingCart' +import OrderComplete from './orderConfirmation' +import AddressAndPayment from './addressAndPayment' +import Swal from 'sweetalert2' +import ReviewOrder from './reviewOrder' +import OrderConfirmation from './orderConfirmation' +import { useDispatch, useSelector } from 'react-redux' +import { + loadCart, + selectCartItemCount, + selectCartItems, + selectCartSubtotal, +} from '../../../redux-store/CartStore/ducs' + +const TabItem = ({ label, active, complete, onClick, reference, stepNumber }) => ( + + + {complete ? ( + + ) : ( + + {stepNumber} + + )} + + + {label} + + +) + +TabItem.propTypes = { + label: PropTypes.string.isRequired, + active: PropTypes.bool.isRequired, + complete: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired, + reference: PropTypes.object.isRequired, + stepNumber: PropTypes.number.isRequired, +} +const styles = { + tab: { + backgroundColor: '#FFFFFF', + color: 'black', + padding: '0px 25px', + textAlign: 'center', + textTransform: 'none', + textDecoration: 'none', + minWidth: 50, + '&.Mui-selected': { + backgroundColor: '#007FFF', + border: 'none', + color: 'white', + textAlign: 'center', + textDecoration: 'none', + }, + }, + tabs: { + // maxWidth: 355, + borderRadius: 20, + maxHeight: 50, + '&.MuiTabs-root': { + minHeight: 0, + }, + }, +} + +const Cart = () => { + const [shipTo, setShipTo] = useState('') + const [billTo, setBillTo] = useState('') + const [paymentMode, setPaymentMode] = useState('') + const cartItems = useSelector(selectCartItems) + const totalItemCount = useSelector(selectCartItemCount) + const cartSubtotal = useSelector(selectCartSubtotal) + const [value, setValue] = useState(0) + const handleTabChange = (event, newValue) => { + console.log(newValue) + if (newValue === 1 && cartItems.length === 0) { + Swal.fire('Cart is emplty ,can not move to next screen pls add items to cart ') + return + } + + if (newValue === 2 && (shipTo === '' || billTo === '' || paymentMode === '')) { + Swal.fire('Fill all the details ,can not move to next screen ') + return + } + if (newValue === 3 && (shipTo === '' || billTo === '' || paymentMode === '')) { + Swal.fire('Fill all the details ,can not move to next screen ') + return + } + + setValue(newValue) + } + // const handleAddressAndOrderClick = () => { + // if (cartItems.length === 0) { + // Swal.fire('Cart is emplty can not move to next screen pls add items to cart ') + // return + // } + // setValue(1) + // } + const orderId = '#F5SD67G' + // const [cartItem, setCartItem] = useState([ + // { + // product: { + // _id: '1', + // name: 'Product 1', + // image: [ + // { + // url: 'https://images.pexels.com/photos/341523/pexels-photo-341523.jpeg?auto=compress&cs=tinysrgb&w=600', + // }, + // ], + // variant: { price: 100, gst_Id: { tax: 5 } }, + // hsn: '1234', + // }, + // quantity: 2, + // subtotal: 200, + // }, + // { + // product: { + // _id: '2', + // name: 'Product 2', + // image: [ + // { + // url: 'https://images.pexels.com/photos/341523/pexels-photo-341523.jpeg?auto=compress&cs=tinysrgb&w=600', + // }, + // ], + // variant: { price: 150, gst_Id: { tax: 5 } }, + // hsn: '5678', + // }, + // quantity: 1, + // subtotal: 150, + // }, + // ]) + const dispatch = useDispatch() + + // Load the cart items from local storage when the component mounts + useEffect(() => { + dispatch(loadCart()) + }, []) + + return ( + + + + + + + + + {value === 0 && + (cartItems.length === 0 ? ( + + No items available. Please add items to the cart. + + ) : ( + + + + ))} + + {value === 1 && ( + + + + )} + {value === 2 && ( + + + + )} + {value === 3 && ( + + + + )} + + ) +} + +export default Cart diff --git a/src/views/pages/cart/orderConfirmation.js b/src/views/pages/cart/orderConfirmation.js new file mode 100644 index 0000000..a43728f --- /dev/null +++ b/src/views/pages/cart/orderConfirmation.js @@ -0,0 +1,134 @@ +import { + Box, + Container, + Grid, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from '@mui/material' +import React from 'react' +import { useSelector } from 'react-redux' +import { selectCartSubtotal } from '../../../redux-store/CartStore/ducs' + +const OrderConfirmation = ({ orderId, billTo, shipTo, paymentMode, cartItem }) => { + const subtotal = useSelector(selectCartSubtotal) + + // Calculate total GST for the entire cart + const totalGST = cartItem.reduce((acc, item) => { + // console.log(item) + const gstAmount = (item.price * item.GST.tax) / 100 + return acc + gstAmount * item.count + }, 0) + return ( + + + + OrderId : {orderId} + + + + + + Bill Address + + {billTo} + + + + Ship Address + + {shipTo} + + + + Payment mode : {paymentMode} + + + + + + + + + + + + + + Product + HSN + Quantity + Price + GST + Subtotal + + + + {cartItem.map((row, index) => { + const gstAmount = (row.price * row.GST.tax) / 100 + + return ( + + + + {/* {row.product.name} */} + + {row.name} + + + + {row.SKU} + {row.count} + ₹{row.price} + ₹{gstAmount} + ₹{row.price * row.count} + + ) + })} + + + + Subtotal: ₹ {subtotal} + + + + + + Total GST: ₹ {totalGST} + + + + + + + Grand Total: ₹ {subtotal + totalGST} + + + +
+
+
+
+
+
+
+ ) +} + +export default OrderConfirmation diff --git a/src/views/pages/cart/reviewOrder.js b/src/views/pages/cart/reviewOrder.js new file mode 100644 index 0000000..30cee21 --- /dev/null +++ b/src/views/pages/cart/reviewOrder.js @@ -0,0 +1,139 @@ +import { + Box, + Button, + Container, + Grid, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from '@mui/material' +import React from 'react' +import { useSelector } from 'react-redux' +import { selectCartSubtotal } from '../../../redux-store/CartStore/ducs' + +const ReviewOrder = ({ orderId, billTo, shipTo, paymentMode, cartItem, handleTabChange }) => { + const subtotal = useSelector(selectCartSubtotal) + + // Calculate total GST for the entire cart + const totalGST = cartItem.reduce((acc, item) => { + // console.log(item) + const gstAmount = (item.price * item.GST.tax) / 100 + return acc + gstAmount * item.count + }, 0) + return ( + + + + + + Bill Address + + {billTo} + + + + Ship Address + + {shipTo} + + + + Payment mode : {paymentMode} + + + + + + + + + + + + + + Product + HSN + Quantity + Price + GST + Subtotal + + + + {cartItem.map((row, index) => { + const gstAmount = (row.price * row.GST.tax) / 100 + + return ( + + + + {/* {row.product.name} */} + + {row.name} + + + + {row.SKU} + {row.count} + ₹{row.price} + ₹{gstAmount} + ₹{row.price * row.count} + + ) + })} + + + + Subtotal: ₹ {subtotal} + + + + + + Total GST: ₹ {totalGST} + + + + + + + Grand Total: ₹ {subtotal + totalGST} + + + +
+
+
+
+
+ + + + +
+
+ ) +} + +export default ReviewOrder diff --git a/src/views/pages/cart/shopingCart.js b/src/views/pages/cart/shopingCart.js new file mode 100644 index 0000000..b88ea37 --- /dev/null +++ b/src/views/pages/cart/shopingCart.js @@ -0,0 +1,191 @@ +import React from 'react' +import { + Box, + Container, + Grid, + Paper, + Typography, + Button, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + useMediaQuery, +} from '@mui/material' +import ClearIcon from '@mui/icons-material/Clear' +import { useDispatch, useSelector } from 'react-redux' +import { + selectCartItems, + selectCartSubtotal, + increaseCount, + decreaseCount, + removeFromCart, + selectCartItemCount, +} from '../../../redux-store/CartStore/ducs' // Adjust this path as per your project structure + +const ShoppingCart = ({ handleTabChange }) => { + const matches = useMediaQuery('(min-width:1200px)') + const dispatch = useDispatch() + + // Fetching cart items and subtotal from the Redux store + const cartItems = useSelector(selectCartItems) + const subtotal = useSelector(selectCartSubtotal) + console.log(cartItems) + // Calculate total GST for the entire cart + const totalGST = cartItems.reduce((acc, item) => { + // console.log(item) + const gstAmount = (item.price * item.GST.tax) / 100 + return acc + gstAmount * item.count + }, 0) + + const handleDecrease = (productId) => { + dispatch(decreaseCount(productId)) + } + + const handleIncrease = (productId) => { + dispatch(increaseCount(productId)) + } + + const removeCartItemHandler = (productId) => { + dispatch(removeFromCart(productId)) + } + + return ( + + + + + + + + + + + Product + HSN + Quantity + Price + GST + Subtotal + + + + {cartItems.map((row, index) => { + const gstAmount = (row.price * row.GST.tax) / 100 + + return ( + + + + {/* {row.product.name} */} + + {row.name} + removeCartItemHandler(row._id)} + sx={{ + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + color: '#6C7275', + marginTop: '0.5rem', + }} + > + + Remove + + + + + {row.SKU} + + + handleDecrease(row._id)} + sx={{ cursor: 'pointer', padding: '0.5rem' }} + > + - + + {row.count} + handleIncrease(row._id)} + sx={{ cursor: 'pointer', padding: '0.5rem' }} + > + + + + + + ₹{row.price} + ₹{gstAmount} + ₹{row.price * row.count} + + ) + })} + + + + Subtotal: ₹ {subtotal} + + + + + + Total GST: ₹ {totalGST} + + + + + + + + Grand total: ₹ {subtotal + totalGST} + + + +
+
+
+
+
+
+
+ + + + +
+ ) +} + +export default ShoppingCart diff --git a/src/views/shops/Shop.js b/src/views/shops/Shop.js index eb113d5..9e11b71 100644 --- a/src/views/shops/Shop.js +++ b/src/views/shops/Shop.js @@ -1,59 +1,138 @@ +import { useEffect, useRef, useState } from 'react' +import { useDispatch } from 'react-redux' +import Swal from 'sweetalert2' +import Axios from '../../axios' + import { Box, - Card, CircularProgress, Container, FormControl, Grid, MenuItem, + Pagination, Select, Typography, } from '@mui/material' -import cardData from '../../assets/data/cardjson.json' -import React, { useState } from 'react' import ShopCard from './shopCard' const Shop = () => { const [option, setOption] = useState('all') + const [products, setProducts] = useState([]) + const [categories, setCategories] = useState([]) + const [loading, setLoading] = useState(true) + const [currentPage, setCurrentPage] = useState(1) + const [totalData, setTotalData] = useState(0) + const itemsPerPage = 10 // Adjust this according to your requirements + + const nameRef = useRef('') + const categoryRef = useRef('') + + const dispatch = useDispatch() const handleChange = (event) => { - setOption(event.target.value) + const selectedValue = event.target.value + setOption(selectedValue) + categoryRef.current = selectedValue === 'all' ? '' : selectedValue // Set to an empty string if "All" is selected + setCurrentPage(1) // Reset to first page when filter changes } - const filteredData = - option === 'all' ? cardData : cardData.filter((item) => item.categoryName === option) + const getCategories = async () => { + try { + const response = await Axios.get('/api/category/getCategories') + setCategories(response.data?.categories || []) // Assuming the response has a categories property + } catch (err) { + Swal.fire({ + title: 'Error', + text: 'Failed to fetch categories', + icon: 'error', + button: 'Retry', + dangerMode: true, + }) + } + } + + const getProductsData = async () => { + setLoading(true) + try { + const response = await Axios.get('/api/product/getAll/user/', { + params: { + page: currentPage, + show: itemsPerPage, + name: nameRef.current || '', + category: categoryRef.current || '', // Send category only if it's not empty + }, + }) + setProducts(response.data?.products || []) + setTotalData(response.data?.total_data || 0) + } catch (err) { + const msg = err?.response?.data?.msg || 'Something went wrong!' + Swal.fire({ + title: 'Error', + text: msg, + icon: 'error', + button: 'Retry', + dangerMode: true, + }) + } finally { + setLoading(false) + } + } + + useEffect(() => { + getCategories() // Fetch categories on component mount + getProductsData() + }, [currentPage, option]) + + const handlePageChange = (event, value) => { + setCurrentPage(value) + } return ( - <> - - - Categories - + + + Categories + + + - - {/* Cards */} - - {filteredData.map((item, i) => ( - - - - ))} - - - - + + {loading ? ( + + ) : ( + <> + + {products.map((item, i) => ( + + + + ))} + + + + + + )} + + ) } diff --git a/src/views/shops/shopCard.js b/src/views/shops/shopCard.js index a1a4a67..834fdc3 100644 --- a/src/views/shops/shopCard.js +++ b/src/views/shops/shopCard.js @@ -1,9 +1,21 @@ /* eslint-disable react/prop-types */ import { Button, Card, CardContent, CardMedia, Typography } from '@mui/material' import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { addToCart, selectIsProductInCart } from '../../redux-store/CartStore/ducs' +import Swal from 'sweetalert2' // eslint-disable-next-line react/prop-types const ShopCard = ({ item }) => { + const dispatch = useDispatch() + const isProductInCart = useSelector(selectIsProductInCart(item._id)) + + const handleAddToCart = () => { + if (!isProductInCart) { + dispatch(addToCart(item)) + Swal.fire('Product added to cart ') + } + } return (
@@ -19,10 +31,11 @@ const ShopCard = ({ item }) => { variant="contained" color="primary" fullWidth - // onClick={handleAddToCart} + disabled={isProductInCart} + onClick={handleAddToCart} sx={{ marginTop: '10px' }} > - Add to Cart + {isProductInCart ? 'Already in Cart' : 'Add to Cart'}