redux implemented

This commit is contained in:
ROSHAN GARG 2024-08-16 13:42:45 +05:30
parent 3b173b4987
commit 7039c86365
16 changed files with 1174 additions and 35 deletions

View File

@ -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",
}, },

View File

@ -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"
}, },

View File

@ -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>

View File

@ -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
View 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;
}

View File

@ -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'

View 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)

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View 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

View 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

View File

@ -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>
) )
} }

View File

@ -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>