├── 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 |
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 |
101 |
102 |
103 | -
104 |
Grand Total
105 | {cartValue}
106 |
107 |
108 |
109 |
115 |
118 |
119 | >
120 | ) : (
121 |
129 |

130 |
131 | )}
132 |
133 | >
134 | );
135 | }
136 |
--------------------------------------------------------------------------------