redux implemented
This commit is contained in:
parent
3b173b4987
commit
7039c86365
@ -18,6 +18,9 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
plugins: ['react', 'react-hooks'],
|
plugins: ['react', 'react-hooks'],
|
||||||
rules: {
|
rules: {
|
||||||
|
rules: {
|
||||||
|
'react/react-in-jsx-scope': 'off',
|
||||||
|
},
|
||||||
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
|
// 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",
|
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
|
||||||
},
|
},
|
||||||
|
@ -47,6 +47,8 @@
|
|||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.23.1",
|
||||||
"redux": "5.0.1",
|
"redux": "5.0.1",
|
||||||
|
"redux-devtools-extension": "^2.13.9",
|
||||||
|
"redux-thunk": "^3.1.0",
|
||||||
"simplebar-react": "^3.2.5",
|
"simplebar-react": "^3.2.5",
|
||||||
"sweetalert2": "^11.12.3"
|
"sweetalert2": "^11.12.3"
|
||||||
},
|
},
|
||||||
|
@ -24,9 +24,11 @@ import {
|
|||||||
cilMoon,
|
cilMoon,
|
||||||
cilSun,
|
cilSun,
|
||||||
} from '@coreui/icons'
|
} from '@coreui/icons'
|
||||||
|
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'
|
||||||
|
|
||||||
import { AppBreadcrumb } from './index'
|
import { AppBreadcrumb } from './index'
|
||||||
import { AppHeaderDropdown } from './header/index'
|
import { AppHeaderDropdown } from './header/index'
|
||||||
|
import { IconButton } from '@mui/material'
|
||||||
|
|
||||||
const AppHeader = () => {
|
const AppHeader = () => {
|
||||||
const headerRef = useRef()
|
const headerRef = useRef()
|
||||||
@ -51,6 +53,7 @@ const AppHeader = () => {
|
|||||||
>
|
>
|
||||||
<CIcon icon={cilMenu} size="lg" />
|
<CIcon icon={cilMenu} size="lg" />
|
||||||
</CHeaderToggler>
|
</CHeaderToggler>
|
||||||
|
|
||||||
{/* <CHeaderNav className="d-none d-md-flex">
|
{/* <CHeaderNav className="d-none d-md-flex">
|
||||||
<CNavItem>
|
<CNavItem>
|
||||||
<CNavLink to="/dashboard" as={NavLink}>
|
<CNavLink to="/dashboard" as={NavLink}>
|
||||||
@ -130,6 +133,12 @@ const AppHeader = () => {
|
|||||||
<div className="vr h-100 mx-2 text-body text-opacity-75"></div>
|
<div className="vr h-100 mx-2 text-body text-opacity-75"></div>
|
||||||
</li> */}
|
</li> */}
|
||||||
<AppHeaderDropdown />
|
<AppHeaderDropdown />
|
||||||
|
|
||||||
|
<CNavLink to="/cart" as={NavLink}>
|
||||||
|
<IconButton>
|
||||||
|
<ShoppingCartIcon style={{ color: 'black' }} />
|
||||||
|
</IconButton>
|
||||||
|
</CNavLink>
|
||||||
</CHeaderNav>
|
</CHeaderNav>
|
||||||
</CContainer>
|
</CContainer>
|
||||||
<CContainer className="px-4" fluid>
|
<CContainer className="px-4" fluid>
|
||||||
|
@ -56,7 +56,7 @@ const AppHeaderDropdown = () => {
|
|||||||
return (
|
return (
|
||||||
<CDropdown variant="nav-item">
|
<CDropdown variant="nav-item">
|
||||||
<CDropdownToggle placement="bottom-end" className="py-0 pe-0" caret={false}>
|
<CDropdownToggle placement="bottom-end" className="py-0 pe-0" caret={false}>
|
||||||
<div className="media d-flex align-items-center">
|
<div className="media d-flex align-items-center " style={{ marginTop: '0.5rem' }}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
style={{ fontSize: '2rem' }}
|
style={{ fontSize: '2rem' }}
|
||||||
className="user-avatar md-avatar rounded-circle "
|
className="user-avatar md-avatar rounded-circle "
|
||||||
|
16
src/index.css
Normal file
16
src/index.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* Poppins */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
|
||||||
|
/* Space Grotesk */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk&display=swap');
|
||||||
|
/* Inter */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import 'core-js'
|
import 'core-js'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
|
126
src/redux-store/CartStore/ducs.js
Normal file
126
src/redux-store/CartStore/ducs.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
const initialState = {
|
||||||
|
items: [],
|
||||||
|
}
|
||||||
|
const cartactionTypes = {
|
||||||
|
ADD_TO_CART: 'ADD_TO_CART',
|
||||||
|
REMOVE_FROM_CART: 'REMOVE_FROM_CART',
|
||||||
|
INCREASE_COUNT: 'INCREASE_COUNT',
|
||||||
|
DECREASE_COUNT: 'DECREASE_COUNT',
|
||||||
|
LOAD_CART: 'LOAD_CART',
|
||||||
|
CLEAR_CART: 'CLEAR_CART',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addToCart = (product) => (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)
|
@ -1,5 +1,8 @@
|
|||||||
|
import { element } from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import Cart from './views/pages/cart/cart'
|
||||||
|
|
||||||
const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard'))
|
const Dashboard = React.lazy(() => import('./views/dashboard/Dashboard'))
|
||||||
const Shop = React.lazy(() => import('./views/shops/Shop'))
|
const Shop = React.lazy(() => import('./views/shops/Shop'))
|
||||||
const Order = React.lazy(() => import('./views/orders/Order'))
|
const Order = React.lazy(() => import('./views/orders/Order'))
|
||||||
@ -19,6 +22,8 @@ const routes = [
|
|||||||
|
|
||||||
{ path: '/my-profile', name: 'Profile', element: MyProfile },
|
{ path: '/my-profile', name: 'Profile', element: MyProfile },
|
||||||
{ path: '/change-password', name: 'Change password', element: ChangePassword },
|
{ path: '/change-password', name: 'Change password', element: ChangePassword },
|
||||||
|
|
||||||
|
{ path: '/cart', name: 'Cart', element: Cart },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
|
12
src/store.js
12
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 = {
|
const initialState = {
|
||||||
sidebarShow: true,
|
sidebarShow: true,
|
||||||
@ -13,6 +16,9 @@ const changeState = (state = initialState, { type, ...rest }) => {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const rootReducer = combineReducers({
|
||||||
const store = createStore(changeState)
|
cart: cartReducer,
|
||||||
|
changeState: changeState,
|
||||||
|
})
|
||||||
|
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
|
||||||
export default store
|
export default store
|
||||||
|
167
src/views/pages/cart/addressAndPayment.js
Normal file
167
src/views/pages/cart/addressAndPayment.js
Normal file
@ -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 (
|
||||||
|
<div>
|
||||||
|
<Box sx={{ my: '2rem', background: '#fff', padding: '1rem', borderRadius: '0.8rem' }}>
|
||||||
|
<Typography variant="h4" textAlign={'center'} marginBottom={2}>
|
||||||
|
Select Address and Payment Mode
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={3}>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>Bill Address</Typography>
|
||||||
|
<FormControl required fullWidth error={billToError}>
|
||||||
|
<InputLabel id="bill-address-label">Bill Address</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="bill-address-label"
|
||||||
|
id="bill-address-select"
|
||||||
|
required
|
||||||
|
value={billTo}
|
||||||
|
label="Bill Address"
|
||||||
|
onChange={(e) => setBillTo(e.target.value)}
|
||||||
|
>
|
||||||
|
<MenuItem value={'123, MG Road, Bengaluru, Karnataka - 560001'}>
|
||||||
|
123, MG Road, Bengaluru, Karnataka - 560001
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={'456, Park Street, Kolkata, West Bengal - 700016'}>
|
||||||
|
456, Park Street, Kolkata, West Bengal - 700016
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={'789, Connaught Place, New Delhi - 110001'}>
|
||||||
|
789, Connaught Place, New Delhi - 110001
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
{billToError && <FormHelperText>Bill Address is required</FormHelperText>}
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>Ship Address</Typography>
|
||||||
|
<FormControl required fullWidth error={shipToError}>
|
||||||
|
<InputLabel id="ship-address-label">Ship Address</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="ship-address-label"
|
||||||
|
id="ship-address-select"
|
||||||
|
value={shipTo}
|
||||||
|
label="Ship Address"
|
||||||
|
onChange={(e) => setShipTo(e.target.value)}
|
||||||
|
>
|
||||||
|
<MenuItem value={'123, MG Road, Bengaluru, Karnataka - 560001'}>
|
||||||
|
123, MG Road, Bengaluru, Karnataka - 560001
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={'456, Park Street, Kolkata, West Bengal - 700016'}>
|
||||||
|
456, Park Street, Kolkata, West Bengal - 700016
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={'789, Connaught Place, New Delhi - 110001'}>
|
||||||
|
789, Connaught Place, New Delhi - 110001
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
{shipToError && <FormHelperText>Ship Address is required</FormHelperText>}
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
{billTo !== '' && (
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Selected Bill Address
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>{billTo}</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
{shipTo !== '' && (
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Selected Ship to Address
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>{shipTo}</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item sm={12} md={12} lg={12}>
|
||||||
|
<Typography>Payment Mode</Typography>
|
||||||
|
<FormControl component="fieldset" error={paymentModeError}>
|
||||||
|
<RadioGroup value={paymentMode} onChange={(e) => setPaymentMode(e.target.value)}>
|
||||||
|
<FormControlLabel
|
||||||
|
value="online-transfer"
|
||||||
|
control={<Radio />}
|
||||||
|
label="Online Transfer"
|
||||||
|
/>
|
||||||
|
<FormControlLabel value="cheque" control={<Radio />} label="Cheque" />
|
||||||
|
<FormControlLabel value="credit" control={<Radio />} label="Credit" />
|
||||||
|
</RadioGroup>
|
||||||
|
{paymentModeError && <FormHelperText>Payment Mode is required</FormHelperText>}
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'center', my: 4 }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
size="large"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleReviewOrderClick}
|
||||||
|
sx={{ width: '200px' }}
|
||||||
|
>
|
||||||
|
View Order
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddressAndPayment
|
248
src/views/pages/cart/cart.js
Normal file
248
src/views/pages/cart/cart.js
Normal file
@ -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 }) => (
|
||||||
|
<Box
|
||||||
|
ref={reference}
|
||||||
|
onClick={onClick}
|
||||||
|
sx={{
|
||||||
|
minWidth: '256px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
pb: '1rem',
|
||||||
|
cursor: 'pointer',
|
||||||
|
borderBottom: complete ? '2px solid #45B26B' : active ? '2px solid black' : '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
sx={{
|
||||||
|
background: complete ? '#45B26B' : active ? '#000' : '#B1B5C3',
|
||||||
|
marginRight: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{complete ? (
|
||||||
|
<CheckIcon sx={{ color: 'white' }} />
|
||||||
|
) : (
|
||||||
|
<Typography
|
||||||
|
color={active ? 'white' : complete ? '#45B26B' : '#FCFCFD'}
|
||||||
|
width={30}
|
||||||
|
height={30}
|
||||||
|
borderRadius={'50%'}
|
||||||
|
>
|
||||||
|
{stepNumber}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontFamily: 'inter',
|
||||||
|
fontSize: '1rem',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: complete ? '#45B26B' : active ? 'black' : '#B1B5C3',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Container>
|
||||||
|
<Tabs
|
||||||
|
value={value}
|
||||||
|
indicatorColor="primary"
|
||||||
|
textColor="primary"
|
||||||
|
onChange={handleTabChange}
|
||||||
|
TabIndicatorProps={{
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sx={styles.tabs}
|
||||||
|
>
|
||||||
|
<Tab label="Cart " value={0} sx={styles.tab} />
|
||||||
|
<Tab label="Address and Payment Mode" value={1} sx={styles.tab} />
|
||||||
|
<Tab label="Review Order" value={2} sx={styles.tab} />
|
||||||
|
<Tab label="Order Confirmation" value={3} sx={styles.tab} />
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
{value === 0 &&
|
||||||
|
(cartItems.length === 0 ? (
|
||||||
|
<Typography sx={{ m: '2rem ', background: '#FFF', padding: '1rem' }}>
|
||||||
|
No items available. Please add items to the cart.
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Box>
|
||||||
|
<ShopingCart cartItems={cartItems} handleTabChange={handleTabChange} />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{value === 1 && (
|
||||||
|
<Box>
|
||||||
|
<AddressAndPayment
|
||||||
|
billTo={billTo}
|
||||||
|
setBillTo={setBillTo}
|
||||||
|
shipTo={shipTo}
|
||||||
|
setShipTo={setShipTo}
|
||||||
|
paymentMode={paymentMode}
|
||||||
|
setPaymentMode={setPaymentMode}
|
||||||
|
handleTabChange={handleTabChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{value === 2 && (
|
||||||
|
<Box>
|
||||||
|
<ReviewOrder
|
||||||
|
billTo={billTo}
|
||||||
|
shipTo={shipTo}
|
||||||
|
paymentMode={paymentMode}
|
||||||
|
orderId={orderId}
|
||||||
|
cartItem={cartItems}
|
||||||
|
handleTabChange={handleTabChange}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{value === 3 && (
|
||||||
|
<Box>
|
||||||
|
<OrderConfirmation
|
||||||
|
billTo={billTo}
|
||||||
|
shipTo={shipTo}
|
||||||
|
paymentMode={paymentMode}
|
||||||
|
orderId={orderId}
|
||||||
|
cartItem={cartItems}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Cart
|
134
src/views/pages/cart/orderConfirmation.js
Normal file
134
src/views/pages/cart/orderConfirmation.js
Normal file
@ -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 (
|
||||||
|
<Box>
|
||||||
|
<Box sx={{ my: '2rem', background: '#fff', padding: '1rem', borderRadius: '0.8rem' }}>
|
||||||
|
<Typography variant="h4" textAlign={'center'} marginBottom={2}>
|
||||||
|
OrderId : {orderId}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Bill Address
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>{billTo}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Ship Address
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>{shipTo}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Payment mode : <strong>{paymentMode}</strong>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box my={8}>
|
||||||
|
<Grid container spacing={5}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Box>
|
||||||
|
<TableContainer
|
||||||
|
component={Paper}
|
||||||
|
elevation={0}
|
||||||
|
sx={{ borderBottom: '1.5px solid #CFCFD5', borderRadius: 0 }}
|
||||||
|
>
|
||||||
|
<Table sx={{ minWidth: 650 }} size="large">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Product</TableCell>
|
||||||
|
<TableCell>HSN</TableCell>
|
||||||
|
<TableCell>Quantity</TableCell>
|
||||||
|
<TableCell>Price</TableCell>
|
||||||
|
<TableCell>GST</TableCell>
|
||||||
|
<TableCell>Subtotal</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{cartItem.map((row, index) => {
|
||||||
|
const gstAmount = (row.price * row.GST.tax) / 100
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={index}
|
||||||
|
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||||
|
>
|
||||||
|
<TableCell>
|
||||||
|
<Box sx={{ display: 'flex' }}>
|
||||||
|
{/* <img
|
||||||
|
src={row.product.image[0].url}
|
||||||
|
alt={row.product.name}
|
||||||
|
style={{ width: '100px', height: '70px', marginRight: '1rem' }}
|
||||||
|
/> */}
|
||||||
|
<Box>
|
||||||
|
<Typography fontWeight={600}>{row.name}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{row.SKU}</TableCell>
|
||||||
|
<TableCell>{row.count}</TableCell>
|
||||||
|
<TableCell>₹{row.price}</TableCell>
|
||||||
|
<TableCell>₹{gstAmount}</TableCell>
|
||||||
|
<TableCell>₹{row.price * row.count}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Subtotal:<strong> ₹ {subtotal}</strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Total GST:<strong> ₹ {totalGST} </strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Grand Total: <strong> ₹ {subtotal + totalGST}</strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrderConfirmation
|
139
src/views/pages/cart/reviewOrder.js
Normal file
139
src/views/pages/cart/reviewOrder.js
Normal file
@ -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 (
|
||||||
|
<Box>
|
||||||
|
<Box sx={{ my: '2rem', background: '#fff', padding: '1rem', borderRadius: '0.8rem' }}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Bill Address
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>{billTo}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Ship Address
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ mb: '0.5rem' }}>{shipTo}</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item sm={6} md={6} lg={6}>
|
||||||
|
<Typography variant="h5" sx={{ mb: '0.5rem' }}>
|
||||||
|
Payment mode : <strong>{paymentMode}</strong>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box my={4}>
|
||||||
|
<Grid container spacing={5}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Box>
|
||||||
|
<TableContainer
|
||||||
|
component={Paper}
|
||||||
|
elevation={0}
|
||||||
|
sx={{ borderBottom: '1.5px solid #CFCFD5', borderRadius: 0 }}
|
||||||
|
>
|
||||||
|
<Table sx={{ minWidth: 650 }} size="large">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Product</TableCell>
|
||||||
|
<TableCell>HSN</TableCell>
|
||||||
|
<TableCell>Quantity</TableCell>
|
||||||
|
<TableCell>Price</TableCell>
|
||||||
|
<TableCell>GST</TableCell>
|
||||||
|
<TableCell>Subtotal</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{cartItem.map((row, index) => {
|
||||||
|
const gstAmount = (row.price * row.GST.tax) / 100
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={index}
|
||||||
|
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||||
|
>
|
||||||
|
<TableCell>
|
||||||
|
<Box sx={{ display: 'flex' }}>
|
||||||
|
{/* <img
|
||||||
|
src={row.product.image[0].url}
|
||||||
|
alt={row.product.name}
|
||||||
|
style={{ width: '100px', height: '70px', marginRight: '1rem' }}
|
||||||
|
/> */}
|
||||||
|
<Box>
|
||||||
|
<Typography fontWeight={600}>{row.name}</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{row.SKU}</TableCell>
|
||||||
|
<TableCell>{row.count}</TableCell>
|
||||||
|
<TableCell>₹{row.price}</TableCell>
|
||||||
|
<TableCell>₹{gstAmount}</TableCell>
|
||||||
|
<TableCell>₹{row.price * row.count}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Subtotal:<strong> ₹ {subtotal}</strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Total GST:<strong> ₹ {totalGST} </strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Grand Total: <strong> ₹ {subtotal + totalGST}</strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Box display={'flex'} justifyContent={'center'} mt={4} gap={10}>
|
||||||
|
<Button variant="contained" color="primary" onClick={(e) => handleTabChange(e, 0)}>
|
||||||
|
Update Order
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" color="success" onClick={(e) => handleTabChange(e, 3)}>
|
||||||
|
Confirm Order
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReviewOrder
|
191
src/views/pages/cart/shopingCart.js
Normal file
191
src/views/pages/cart/shopingCart.js
Normal file
@ -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 (
|
||||||
|
<Box>
|
||||||
|
<Container>
|
||||||
|
<Box my={8}>
|
||||||
|
<Grid container spacing={5}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Box>
|
||||||
|
<TableContainer
|
||||||
|
component={Paper}
|
||||||
|
elevation={0}
|
||||||
|
sx={{ borderBottom: '1.5px solid #CFCFD5', borderRadius: 0 }}
|
||||||
|
>
|
||||||
|
<Table sx={{ minWidth: 650 }} size="large">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Product</TableCell>
|
||||||
|
<TableCell>HSN</TableCell>
|
||||||
|
<TableCell>Quantity</TableCell>
|
||||||
|
<TableCell>Price</TableCell>
|
||||||
|
<TableCell>GST</TableCell>
|
||||||
|
<TableCell>Subtotal</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{cartItems.map((row, index) => {
|
||||||
|
const gstAmount = (row.price * row.GST.tax) / 100
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={index}
|
||||||
|
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||||
|
>
|
||||||
|
<TableCell>
|
||||||
|
<Box sx={{ display: 'flex' }}>
|
||||||
|
{/* <img
|
||||||
|
src={row.product.image[0].url}
|
||||||
|
alt={row.product.name}
|
||||||
|
style={{ width: '100px', height: '70px', marginRight: '1rem' }}
|
||||||
|
/> */}
|
||||||
|
<Box>
|
||||||
|
<Typography fontWeight={600}>{row.name}</Typography>
|
||||||
|
<Box
|
||||||
|
onClick={() => removeCartItemHandler(row._id)}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: '#6C7275',
|
||||||
|
marginTop: '0.5rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ClearIcon fontSize="small" />
|
||||||
|
<Typography>Remove</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{row.SKU}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
border: '1px solid #6C7275',
|
||||||
|
borderRadius: '4px',
|
||||||
|
width: '80px',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
onClick={() => handleDecrease(row._id)}
|
||||||
|
sx={{ cursor: 'pointer', padding: '0.5rem' }}
|
||||||
|
>
|
||||||
|
-
|
||||||
|
</Typography>
|
||||||
|
<Typography>{row.count}</Typography>
|
||||||
|
<Typography
|
||||||
|
onClick={() => handleIncrease(row._id)}
|
||||||
|
sx={{ cursor: 'pointer', padding: '0.5rem' }}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>₹{row.price}</TableCell>
|
||||||
|
<TableCell>₹{gstAmount}</TableCell>
|
||||||
|
<TableCell>₹{row.price * row.count}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Subtotal:<strong> ₹ {subtotal}</strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
<TableCell>
|
||||||
|
Total GST:<strong> ₹ {totalGST} </strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={5} />
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
Grand total: <strong> ₹ {subtotal + totalGST}</strong>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'center', my: 4 }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
size="large"
|
||||||
|
color="primary"
|
||||||
|
onClick={(e) => handleTabChange(e, 1)}
|
||||||
|
sx={{
|
||||||
|
width: '200px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Address and payment mode
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShoppingCart
|
@ -1,59 +1,138 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
import Swal from 'sweetalert2'
|
||||||
|
import Axios from '../../axios'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Container,
|
Container,
|
||||||
FormControl,
|
FormControl,
|
||||||
Grid,
|
Grid,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
|
Pagination,
|
||||||
Select,
|
Select,
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import cardData from '../../assets/data/cardjson.json'
|
|
||||||
import React, { useState } from 'react'
|
|
||||||
import ShopCard from './shopCard'
|
import ShopCard from './shopCard'
|
||||||
|
|
||||||
const Shop = () => {
|
const Shop = () => {
|
||||||
const [option, setOption] = useState('all')
|
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) => {
|
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 =
|
const getCategories = async () => {
|
||||||
option === 'all' ? cardData : cardData.filter((item) => item.categoryName === option)
|
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 (
|
return (
|
||||||
<>
|
<Container>
|
||||||
<Container>
|
<Typography sx={{ fontWeight: 'bold' }} variant="h4">
|
||||||
<Typography sx={{ fontWeight: 'bold' }} variant="h4">
|
Categories
|
||||||
Categories
|
</Typography>
|
||||||
</Typography>
|
<FormControl sx={{ width: '400px', mt: '1rem' }}>
|
||||||
<Select
|
<Select
|
||||||
sx={{ width: '400px', mt: '1rem' }}
|
labelId="category-select-label"
|
||||||
labelId="demo-simple-select-label"
|
id="category-select"
|
||||||
id="demo-simple-select"
|
|
||||||
value={option}
|
value={option}
|
||||||
label=""
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
<MenuItem value="all">All</MenuItem>
|
<MenuItem value="all">All</MenuItem>
|
||||||
<MenuItem value="camera">Camera</MenuItem>
|
{categories.map((category) => (
|
||||||
<MenuItem value="phone">Phone</MenuItem>
|
<MenuItem key={category._id} value={category.categoryName}>
|
||||||
|
{category.categoryName}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<Box mt={3}>
|
<Box mt={3}>
|
||||||
{/* Cards */}
|
{loading ? (
|
||||||
<Grid container spacing={2}>
|
<CircularProgress />
|
||||||
{filteredData.map((item, i) => (
|
) : (
|
||||||
<Grid key={i} item xs={12} sm={6} md={4} lg={4}>
|
<>
|
||||||
<ShopCard item={item} />
|
<Grid container spacing={2}>
|
||||||
</Grid>
|
{products.map((item, i) => (
|
||||||
))}
|
<Grid key={i} item xs={12} sm={6} md={4} lg={4}>
|
||||||
</Grid>
|
<ShopCard item={item} />
|
||||||
</Box>
|
</Grid>
|
||||||
</Container>
|
))}
|
||||||
</>
|
</Grid>
|
||||||
|
<Box mt={3} display="flex" justifyContent="center">
|
||||||
|
<Pagination
|
||||||
|
count={Math.ceil(totalData / itemsPerPage)}
|
||||||
|
page={currentPage}
|
||||||
|
onChange={handlePageChange}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
import { Button, Card, CardContent, CardMedia, Typography } from '@mui/material'
|
import { Button, Card, CardContent, CardMedia, Typography } from '@mui/material'
|
||||||
import React from 'react'
|
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
|
// eslint-disable-next-line react/prop-types
|
||||||
const ShopCard = ({ item }) => {
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Card>
|
<Card>
|
||||||
@ -19,10 +31,11 @@ const ShopCard = ({ item }) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
fullWidth
|
fullWidth
|
||||||
// onClick={handleAddToCart}
|
disabled={isProductInCart}
|
||||||
|
onClick={handleAddToCart}
|
||||||
sx={{ marginTop: '10px' }}
|
sx={{ marginTop: '10px' }}
|
||||||
>
|
>
|
||||||
Add to Cart
|
{isProductInCart ? 'Already in Cart' : 'Add to Cart'}
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
Loading…
Reference in New Issue
Block a user