├── src ├── Pages │ ├── Order.jsx │ ├── Signup.jsx │ ├── PaymentOptions.jsx │ ├── AuthLayout.jsx │ ├── home.jsx │ ├── StoreLayout.jsx │ ├── Products.jsx │ ├── Product.jsx │ ├── Settings.jsx │ ├── Login.jsx │ └── Store.jsx ├── assets │ ├── emptycart.webp │ ├── react.svg │ └── styles │ │ └── styles.css ├── Components │ ├── ProductListingCard.jsx │ ├── CounterButton.jsx │ ├── ProductCard.jsx │ ├── CartCard.jsx │ ├── Sidebar.jsx │ └── PaymentsModal.jsx ├── main.jsx ├── App.jsx └── Contexts │ ├── Product.context.jsx │ ├── Order.context.jsx │ ├── Auth.context.jsx │ └── Cart.context.jsx ├── vite.config.js ├── .gitignore ├── index.html ├── README.md ├── .eslintrc.cjs ├── package.json └── public ├── vite.svg └── products.json /src/Pages/Order.jsx: -------------------------------------------------------------------------------- 1 | export default function Order() { 2 | return <>Order 3 | } -------------------------------------------------------------------------------- /src/Pages/Signup.jsx: -------------------------------------------------------------------------------- 1 | export default function Signup() { 2 | return <>Signup 3 | } -------------------------------------------------------------------------------- /src/Pages/PaymentOptions.jsx: -------------------------------------------------------------------------------- 1 | export default function PaymentOptions() { 2 | return
; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/emptycart.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaaluvishnu5146/Foody-Pos-Application/HEAD/src/assets/emptycart.webp -------------------------------------------------------------------------------- /src/Pages/AuthLayout.jsx: -------------------------------------------------------------------------------- 1 | export default function AuthLayout() { 2 | return ( 3 |
4 |

Login

5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/Pages/home.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | import StoreLayout from "./StoreLayout"; 3 | 4 | export default function Home() { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/Pages/StoreLayout.jsx: -------------------------------------------------------------------------------- 1 | import Sidebar from "../Components/Sidebar"; 2 | 3 | export default function StoreLayout({ children }) { 4 | return ( 5 |
6 | 7 | {children} 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/Components/ProductListingCard.jsx: -------------------------------------------------------------------------------- 1 | export default function ProductListingCard(props) { 2 | const { data = {}, handleClick = () => {} } = props; 3 | return ( 4 |
handleClick(data._id)} 7 | > 8 |

{data.name}

9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Foody POS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /src/Components/CounterButton.jsx: -------------------------------------------------------------------------------- 1 | export default function CounterButton(props) { 2 | const { id = '', quantity = 0, handleQuantity = () => {} } = props; 3 | return
4 | 5 |

{quantity}

6 | 7 |
8 | } -------------------------------------------------------------------------------- /src/Components/ProductCard.jsx: -------------------------------------------------------------------------------- 1 | export default function ProductCard(props) { 2 | const { data = {}, addToCart = () => {}, isAdded = false } = props; 3 | return
{} : () => addToCart(data)}> 4 |

{data.name}

5 |
6 |

Price: {data.price}

7 |

Ratings({data.ratings})

8 |
9 |
10 | } -------------------------------------------------------------------------------- /src/Components/CartCard.jsx: -------------------------------------------------------------------------------- 1 | import CounterButton from "./CounterButton"; 2 | 3 | export default function CartCard(props) { 4 | const { data = {}, handleQuantity = () => {} } = props; 5 | return
6 |
9 |

{data.name}

10 |
11 |
12 | 13 |
14 |
15 | } -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /src/Pages/Products.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | import ProductListingCard from "../Components/ProductListingCard"; 3 | import { useProducts } from "../Contexts/Product.context"; 4 | 5 | export default function Products() { 6 | const { products = [] } = useProducts(); 7 | const navigator = useNavigate(); 8 | 9 | function handleClick(id = "") { 10 | navigator(`/store/product/${id}`); 11 | } 12 | 13 | return ( 14 |
15 | {products.map((product, index) => ( 16 | 21 | ))} 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/Pages/Product.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | 4 | export default function Product() { 5 | const { id } = useParams(); 6 | const [product, setProduct] = useState({}); 7 | 8 | useEffect(() => { 9 | if (id) { 10 | fetch(`http://localhost:5000/api/food/${id}`) 11 | .then((response) => response.json()) 12 | .then((result) => { 13 | if (result && result.success) { 14 | setProduct(result.data); 15 | } 16 | }) 17 | .catch((err) => console.log(err)); 18 | } 19 | }, [id]); 20 | 21 | return ( 22 |
23 |

{product.name}

24 |

{product.price}

25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/Pages/Settings.jsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export default function Settings() { 4 | const headingRef = useRef(); 5 | const subHeadingRef = useRef(); 6 | 7 | // function changeColor() { 8 | // const textEl = document.getElementById("text"); 9 | // textEl.style.color = "red"; 10 | // } 11 | 12 | function changeColor() { 13 | headingRef.current.style.color = "red"; 14 | headingRef.current.style.fontWeight = "200"; 15 | subHeadingRef.current.style.color = "orange"; 16 | subHeadingRef.current.style.fontWeight = "100"; 17 | } 18 | 19 | return ( 20 |
21 |

Settings

22 |

Settings page for changing ap settings

23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.3", 14 | "react": "^18.2.0", 15 | "react-bootstrap": "^2.10.2", 16 | "react-dom": "^18.2.0", 17 | "react-icons": "^5.0.1", 18 | "react-jwt": "^1.2.1", 19 | "react-router-dom": "^6.22.3", 20 | "react-toastify": "^10.0.5" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.2.66", 24 | "@types/react-dom": "^18.2.22", 25 | "@vitejs/plugin-react": "^4.2.1", 26 | "eslint": "^8.57.0", 27 | "eslint-plugin-react": "^7.34.1", 28 | "eslint-plugin-react-hooks": "^4.6.0", 29 | "eslint-plugin-react-refresh": "^0.4.6", 30 | "vite": "^5.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom/client"; 2 | import App from "./App.jsx"; 3 | import { BrowserRouter as Router } from "react-router-dom"; 4 | import "./assets/styles/styles.css"; 5 | import ProductContextProvider from "./Contexts/Product.context.jsx"; 6 | import CartContextProvider from "./Contexts/Cart.context.jsx"; 7 | import OrdersContextProvider from "./Contexts/Order.context.jsx"; 8 | import AuthContextProvider from "./Contexts/Auth.context.jsx"; 9 | import { ToastContainer } from "react-toastify"; 10 | import "react-toastify/dist/ReactToastify.css"; 11 | 12 | ReactDOM.createRoot(document.getElementById("root")).render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from "react-router-dom"; 2 | import Login from "./Pages/Login"; 3 | import Signup from "./Pages/Signup"; 4 | import Store from "./Pages/Store"; 5 | import Order from "./Pages/Order"; 6 | import Home from "./Pages/home"; 7 | import Products from "./Pages/Products"; 8 | import Product from "./Pages/Product"; 9 | import Settings from "./Pages/Settings"; 10 | import "bootstrap/dist/css/bootstrap.min.css"; 11 | 12 | function App() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /src/Contexts/Product.context.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext, createContext } from "react"; 2 | import { useAuth } from "./Auth.context"; 3 | 4 | const ProductContext = createContext({ 5 | products: [], 6 | }); 7 | 8 | export const useProducts = () => useContext(ProductContext); 9 | 10 | export default function ProductContextProvider({ children }) { 11 | const [products, setProducts] = useState([]); 12 | const { isLoggedIn } = useAuth(); 13 | 14 | useEffect(() => { 15 | if (isLoggedIn) { 16 | fetch("http://localhost:5000/api/food/all?page=1&count=10") 17 | .then((response) => response.json()) 18 | .then((result) => { 19 | if (result.success) { 20 | setProducts(result.data); 21 | } 22 | }) 23 | .catch((err) => console.log(err)); 24 | } 25 | }, [isLoggedIn]); 26 | 27 | return ( 28 | 33 | {children} 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/Components/Sidebar.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | import { FiGlobe, FiGrid, FiGift } from "react-icons/fi"; 3 | import { BsFillGearFill } from "react-icons/bs"; 4 | 5 | export default function Sidebar() { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | 13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 | 21 |
22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/Contexts/Order.context.jsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext, useContext } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const OrdersContext = createContext({ 5 | orders: [], 6 | }); 7 | 8 | export const useOrder = () => useContext(OrdersContext); 9 | 10 | export default function OrdersContextProvider({ children }) { 11 | const [orders, setOrders] = useState([]); 12 | 13 | function createOrder(order = {}) { 14 | if (order) { 15 | fetch("http://localhost:5000/api/order/create", { 16 | method: "POST", 17 | headers: { 18 | "Content-Type": "application/json", 19 | }, 20 | body: JSON.stringify(order), 21 | }) 22 | .then((response) => response.json()) 23 | .then((result) => console.log(result)) 24 | .catch((error) => console.log(error)); 25 | } 26 | } 27 | 28 | const values = { 29 | orders, 30 | createOrder, 31 | }; 32 | 33 | return ( 34 | {children} 35 | ); 36 | } 37 | 38 | OrdersContextProvider.propTypes = { 39 | children: PropTypes.node, 40 | }; 41 | -------------------------------------------------------------------------------- /src/Contexts/Auth.context.jsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext, useContext, useEffect } from "react"; 2 | import { useJwt } from "react-jwt"; 3 | import PropTypes from "prop-types"; 4 | 5 | export const AuthContext = createContext({ 6 | isLoggedIn: false, 7 | userDetails: {}, 8 | setLoggedIn: () => {}, 9 | setUserDetails: () => {}, 10 | decodedToken: {}, 11 | }); 12 | 13 | export const useAuth = () => useContext(AuthContext); 14 | 15 | export default function AuthContextProvider({ children }) { 16 | const [isLoggedIn, setLoggedIn] = useState(false); 17 | const [userDetails, setUserDetails] = useState({}); 18 | const token = sessionStorage.getItem("_tk"); 19 | const { decodedToken, isExpired } = useJwt(token || ""); 20 | 21 | useEffect(() => { 22 | if (token) { 23 | setLoggedIn(true); 24 | } 25 | }, [token]); 26 | 27 | const values = { 28 | setLoggedIn, 29 | isLoggedIn, 30 | userDetails, 31 | setUserDetails, 32 | decodedToken, 33 | }; 34 | 35 | return {children}; 36 | } 37 | 38 | AuthContextProvider.propTypes = { 39 | children: PropTypes.node, 40 | }; 41 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Components/PaymentsModal.jsx: -------------------------------------------------------------------------------- 1 | import Button from "react-bootstrap/Button"; 2 | import Modal from "react-bootstrap/Modal"; 3 | import Form from "react-bootstrap/Form"; 4 | 5 | export default function PaymentsModal({ 6 | show = false, 7 | setOpen = () => {}, 8 | handleTransactionType = () => {}, 9 | handleOrderSubmit = () => {}, 10 | }) { 11 | function handleClose() { 12 | setOpen(false); 13 | } 14 | return ( 15 | <> 16 | 17 | 18 | Payment Mode 19 | 20 | 21 |
22 |
23 | handleTransactionType(e.target.id)} 30 | /> 31 | handleTransactionType(e.target.id)} 38 | /> 39 | handleTransactionType(e.target.id)} 46 | /> 47 |
48 |
49 |
50 | 51 | 54 | 55 |
56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /public/products.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "message": "Food fetched successfully", 4 | "data": [ 5 | { 6 | "_id": "661238816a51cbe33250b77c", 7 | "name": "Idli", 8 | "price": 30, 9 | "qty": "0.25", 10 | "qtyUnit": "kilograms", 11 | "ratings": 5, 12 | "createdAt": "2024-04-07T06:09:06.014Z", 13 | "updatedAt": "2024-04-07T06:09:06.014Z", 14 | "__v": 0 15 | }, 16 | { 17 | "_id": "66126d084cf6f3ad26f4430d", 18 | "name": "Dosa", 19 | "price": 50, 20 | "qty": "0.25", 21 | "qtyUnit": "kilograms", 22 | "ratings": 5, 23 | "createdAt": "2024-04-07T09:53:12.199Z", 24 | "updatedAt": "2024-04-07T09:53:12.199Z", 25 | "__v": 0 26 | }, 27 | { 28 | "_id": "66126d234cf6f3ad26f4430f", 29 | "name": "Bisi bele bath", 30 | "price": 35, 31 | "qty": "0.25", 32 | "qtyUnit": "kilograms", 33 | "ratings": 5, 34 | "createdAt": "2024-04-07T09:53:39.365Z", 35 | "updatedAt": "2024-04-07T09:53:39.365Z", 36 | "__v": 0 37 | }, 38 | { 39 | "_id": "66126d344cf6f3ad26f44311", 40 | "name": "Masala Dosa", 41 | "price": 55, 42 | "qty": "0.25", 43 | "qtyUnit": "kilograms", 44 | "ratings": 5, 45 | "createdAt": "2024-04-07T09:53:56.637Z", 46 | "updatedAt": "2024-04-07T09:53:56.637Z", 47 | "__v": 0 48 | }, 49 | { 50 | "_id": "66126d444cf6f3ad26f44313", 51 | "name": "Mysore masala Dosa", 52 | "price": 65, 53 | "qty": "0.25", 54 | "qtyUnit": "kilograms", 55 | "ratings": 5, 56 | "createdAt": "2024-04-07T09:54:12.279Z", 57 | "updatedAt": "2024-04-07T09:54:12.279Z", 58 | "__v": 0 59 | }, 60 | { 61 | "_id": "66126d514cf6f3ad26f44315", 62 | "name": "Onion Dosa", 63 | "price": 65, 64 | "qty": "0.25", 65 | "qtyUnit": "kilograms", 66 | "ratings": 5, 67 | "createdAt": "2024-04-07T09:54:25.978Z", 68 | "updatedAt": "2024-04-07T09:54:25.978Z", 69 | "__v": 0 70 | }, 71 | { 72 | "_id": "66126d604cf6f3ad26f44317", 73 | "name": "Ghee Dosa", 74 | "price": 55, 75 | "qty": "0.25", 76 | "qtyUnit": "kilograms", 77 | "ratings": 5, 78 | "createdAt": "2024-04-07T09:54:40.773Z", 79 | "updatedAt": "2024-04-07T09:54:40.773Z", 80 | "__v": 0 81 | } 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /src/Pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { useAuth } from "../Contexts/Auth.context"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { toast } from "react-toastify"; 5 | 6 | export default function Login() { 7 | const phoneRef = useRef(); 8 | const passwordRef = useRef(); 9 | const { setLoggedIn } = useAuth(); 10 | const navigation = useNavigate(); 11 | 12 | function showNotification(type = "", message = "") { 13 | return toast(message, { 14 | type: type, 15 | theme: "colored", 16 | position: "top-right", 17 | }); 18 | } 19 | 20 | function handleLogin() { 21 | const mobileNumber = phoneRef.current.value; 22 | const password = passwordRef.current.value; 23 | if (mobileNumber && password) { 24 | fetch("http://localhost:5000/api/auth/signin", { 25 | method: "POST", 26 | headers: { 27 | "Content-Type": "application/json", 28 | }, 29 | body: JSON.stringify({ 30 | mobileNumber, 31 | password, 32 | }), 33 | }) 34 | .then((response) => response.json()) 35 | .then((result) => { 36 | if (result && result.success) { 37 | showNotification("success", "Login successful"); 38 | try { 39 | window.sessionStorage.setItem("_tk", result.token); 40 | } catch (err) { 41 | showNotification; 42 | "error", "Setting token failed"; 43 | } 44 | setLoggedIn(true); 45 | setTimeout(() => { 46 | navigation("/store"); 47 | }, 2000); 48 | } 49 | if (result && !result.success) { 50 | showNotification("error", result.message); 51 | } 52 | }) 53 | .catch((error) => { 54 | showNotification("warning", error.message); 55 | }); 56 | } else { 57 | showNotification("warning", "Form is empty"); 58 | } 59 | } 60 | 61 | return ( 62 |
63 |
64 | 65 |
66 |
67 | 68 | 73 |
74 |
75 | 76 | 81 |
82 |
83 | 86 |
87 |
88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /src/Contexts/Cart.context.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useContext, createContext, useReducer } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | const taxApplicable = [ 5 | { name: "GST", value: 18, unit: "%" }, 6 | { name: "Door Delivery", value: 20, unit: "inr" }, 7 | ]; 8 | 9 | const CartContext = createContext({ 10 | cart: [], 11 | setCart: () => {}, 12 | addToCart: () => {}, 13 | findAleadyInTheCart: () => {}, 14 | handleItemQuantity: () => {}, 15 | cartValue: 0, 16 | taxApplicable: [], 17 | resetCart: () => {}, 18 | }); 19 | 20 | export const useCart = () => useContext(CartContext); 21 | 22 | function reducer(state, action = {}) { 23 | switch (action.type) { 24 | case "addToCart": 25 | var cartCopy = [...state.cart]; 26 | cartCopy.push({ ...action.payload, quantity: 1 }); 27 | return { ...state, cart: cartCopy }; 28 | case "handleQuantityChange": 29 | var copy = [...state.cart]; 30 | var matchingProduct = copy.find((item) => item._id === action.payload.id); 31 | if (matchingProduct) { 32 | matchingProduct.quantity = 33 | action.payload.type === "decrement" 34 | ? matchingProduct.quantity - 1 35 | : matchingProduct.quantity + 1; 36 | // setCart(cartCopy); 37 | } else { 38 | console.log("No Items matching", action.payload.id); 39 | } 40 | return { ...state, cart: copy }; 41 | case "resetCart": 42 | return { ...state, cart: [] }; 43 | case "calculateCartTotal": 44 | var grandTotal = 0; 45 | for (let i = 0; i < state.cart.length; i++) { 46 | grandTotal += state.cart[i].price * state.cart[i].quantity; 47 | } 48 | for (let t = 0; t < taxApplicable.length; t++) { 49 | if (taxApplicable[t].unit === "%") { 50 | grandTotal += (grandTotal / 100) * taxApplicable[t].value; 51 | } else { 52 | grandTotal += taxApplicable[t].value; 53 | } 54 | } 55 | return { ...state, cartValue: grandTotal }; 56 | default: 57 | // PROGRAM HERE 58 | break; 59 | } 60 | } 61 | 62 | const INITIAL_STATE = { 63 | cart: [], 64 | cartValue: 0, 65 | }; 66 | 67 | export default function CartContextProvider({ children }) { 68 | const [state, dispatch] = useReducer(reducer, INITIAL_STATE); 69 | 70 | useEffect(() => { 71 | if (state.cart && state.cart.length > 0 && taxApplicable) { 72 | dispatch({ type: "calculateCartTotal" }); 73 | } 74 | }, [state.cart]); 75 | 76 | function addToCart(data = {}) { 77 | dispatch({ type: "addToCart", payload: data }); 78 | } 79 | 80 | function handleItemQuantity(type = "decrement", id = "") { 81 | dispatch({ type: "handleQuantityChange", payload: { type, id } }); 82 | } 83 | 84 | function resetCart() { 85 | dispatch({ type: "resetCart" }); 86 | } 87 | 88 | function findAleadyInTheCart(data = {}) { 89 | const product = state.cart.find((d) => d._id === data._id); 90 | return product && product._id ? true : false; 91 | } 92 | 93 | return ( 94 | 105 | {children} 106 | 107 | ); 108 | } 109 | 110 | CartContextProvider.propTypes = { 111 | children: PropTypes.node, 112 | }; 113 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/styles/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary: #735cdd; 3 | --secondary: #c5bcf1; 4 | --white: #ffffff; 5 | --grey: #f6f6f6; 6 | --sidebar-width: 80px; 7 | } 8 | 9 | html, 10 | body { 11 | margin: 0px; 12 | padding: 0px; 13 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; 14 | } 15 | 16 | p, 17 | h1, 18 | h2, 19 | h3, 20 | h4, 21 | h5, 22 | h6 { 23 | margin: 0px; 24 | } 25 | 26 | button { 27 | width: 100%; 28 | height: 50px; 29 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; 30 | cursor: pointer; 31 | } 32 | 33 | .btn { 34 | background-color: var(--primary); 35 | color: var(--white); 36 | outline: none; 37 | border: none; 38 | } 39 | 40 | svg { 41 | width: 1.2rem; 42 | height: 1.2rem; 43 | } 44 | 45 | .d-flex { 46 | display: flex; 47 | } 48 | 49 | .flex-column { 50 | flex-direction: column; 51 | } 52 | 53 | .flex-wrap { 54 | flex-wrap: wrap; 55 | } 56 | 57 | .align-center { 58 | align-items: center; 59 | } 60 | 61 | .justify-center { 62 | justify-content: center; 63 | } 64 | 65 | .justify-between { 66 | justify-content: space-between; 67 | } 68 | 69 | .gap-10 { 70 | gap: 10px; 71 | } 72 | 73 | .b-r-10 { 74 | border-radius: 10px; 75 | } 76 | 77 | .m-b-5 { 78 | margin-bottom: 5px; 79 | } 80 | 81 | .m-b-10 { 82 | margin-bottom: 10px; 83 | } 84 | 85 | .truncate-text { 86 | white-space: nowrap; 87 | text-overflow: ellipsis; 88 | overflow: hidden; 89 | } 90 | 91 | .container { 92 | width: 100%; 93 | height: 100vh; 94 | padding: 10px; 95 | box-sizing: border-box; 96 | } 97 | 98 | /* SIDEBAR */ 99 | .sidebar { 100 | width: var(--sidebar-width); 101 | height: 100%; 102 | background: var(--primary); 103 | padding: 10px; 104 | box-sizing: border-box; 105 | } 106 | 107 | .product-listing-area { 108 | width: calc(100% - var(--sidebar-width) - 25%); 109 | height: 100%; 110 | border: 2px solid var(--primary); 111 | } 112 | 113 | .cart-listing-area { 114 | width: calc(100% - 75%); 115 | height: 100%; 116 | } 117 | 118 | .product-card { 119 | width: calc(100% / 4 - 10px); 120 | height: auto; 121 | border: 2px solid var(--primary); 122 | cursor: pointer; 123 | } 124 | 125 | .cart-card { 126 | width: 100%; 127 | height: auto; 128 | border: 2px solid var(--primary); 129 | cursor: pointer; 130 | } 131 | 132 | .product-card:hover, 133 | .cart-card:hover { 134 | background-color: #f5f5f5; 135 | } 136 | 137 | .counter { 138 | > p { 139 | font-size: 1.5rem; 140 | } 141 | } 142 | 143 | .cart-counter-btn { 144 | width: 35px; 145 | height: 35px; 146 | border: none; 147 | outline: none; 148 | cursor: pointer; 149 | } 150 | 151 | .bg-secondary { 152 | background-color: var(--secondary); 153 | } 154 | 155 | .cart-listing-container { 156 | height: auto; 157 | overflow-y: scroll; 158 | } 159 | 160 | .cart-value-section { 161 | > .tax-item { 162 | > ul { 163 | padding: 0px; 164 | list-style-type: none; 165 | 166 | > li { 167 | display: flex; 168 | align-items: center; 169 | justify-content: space-between; 170 | } 171 | } 172 | } 173 | } 174 | 175 | /* NAV_ITEM */ 176 | .nav_item { 177 | width: 50px; 178 | height: 50px; 179 | background: var(--white); 180 | color: var(--primary); 181 | } 182 | 183 | .products-container { 184 | width: 100%; 185 | } 186 | 187 | .auth_page_container { 188 | width: 100%; 189 | height: 100vh; 190 | background: var(--grey); 191 | display: flex; 192 | align-items: center; 193 | justify-content: center; 194 | } 195 | 196 | .auth_form_card { 197 | width: 400px; 198 | min-height: 400px; 199 | background: var(--white); 200 | border-radius: 5px; 201 | padding: 20px; 202 | box-sizing: border-box; 203 | display: flex; 204 | align-items: center; 205 | flex-direction: column; 206 | 207 | > img { 208 | width: 100px; 209 | } 210 | 211 | > .inputField { 212 | width: 100%; 213 | display: flex; 214 | flex-direction: column; 215 | margin-bottom: 10px; 216 | 217 | > label { 218 | font-size: 1.2rem; 219 | font-weight: 400; 220 | margin-bottom: 5px; 221 | } 222 | 223 | > input { 224 | height: 50px; 225 | padding-inline-start: 10px; 226 | font-size: 1rem; 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/Pages/Store.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import ProductCard from "../Components/ProductCard"; 3 | import CartCard from "../Components/CartCard"; 4 | import { useProducts } from "../Contexts/Product.context"; 5 | import { useCart } from "../Contexts/Cart.context"; 6 | import EmptyCart from "../assets/emptycart.webp"; 7 | import { useOrder } from "../Contexts/Order.context"; 8 | import PaymentsModal from "../Components/PaymentsModal"; 9 | import { useAuth } from "../Contexts/Auth.context"; 10 | 11 | export default function Store() { 12 | const { products = [] } = useProducts(); 13 | const [paymentModalOpen, setPaymentModalOpen] = useState(false); 14 | const [transactionType, setTransactionType] = useState("cod"); 15 | const { 16 | cart = [], 17 | addToCart = () => {}, 18 | findAleadyInTheCart = () => {}, 19 | handleItemQuantity = () => {}, 20 | cartValue, 21 | taxApplicable, 22 | resetCart, 23 | } = useCart(); 24 | const { decodedToken = {} } = useAuth(); 25 | 26 | const { createOrder = () => {} } = useOrder(); 27 | 28 | function handleCreateOrderClick(e) { 29 | e.preventDefault(); 30 | setPaymentModalOpen(true); 31 | } 32 | 33 | function handleOrderSubmit() { 34 | if (cart && cart.length > 0) { 35 | const order = { 36 | user: decodedToken.userId, 37 | products: cart, 38 | orderValue: cartValue, 39 | isPaid: false, 40 | transactionType, 41 | transactionId: null, 42 | }; 43 | createOrder(order); 44 | resetCart(); 45 | } 46 | } 47 | 48 | return ( 49 | <> 50 |
51 |
52 |

All Items

53 |
54 |
55 | {products.map((product, index) => ( 56 | 62 | ))} 63 |
64 |
65 |
66 | {cart && cart.length > 0 ? ( 67 | <> 68 |
69 |

Cart

70 |
71 |
75 | {cart && 76 | cart.length > 0 && 77 | cart.map((item, index) => ( 78 | 83 | ))} 84 |
85 |
86 |

Cart Value

87 |
88 |

Tax

89 |
    90 | {taxApplicable.map((tax, index) => ( 91 |
  • 92 |
    {tax.name}
    93 |

    94 | {tax.value} 95 | {tax.unit} 96 |

    97 |
  • 98 | ))} 99 |
100 |
101 |
102 |
    103 |
  • 104 |

    Grand Total

    105 |
    {cartValue}
    106 |
  • 107 |
108 |
109 | 115 | 118 |
119 | 120 | ) : ( 121 |
129 | cart 130 |
131 | )} 132 |
133 | 134 | ); 135 | } 136 | --------------------------------------------------------------------------------