├── client ├── src │ ├── App.css │ ├── components │ │ ├── Spinner.jsx │ │ ├── Product.jsx │ │ ├── layout.css │ │ └── Layout.jsx │ ├── index.js │ ├── redux │ │ ├── store.js │ │ └── rootReducer.js │ ├── pages │ │ ├── customers │ │ │ └── Customers.jsx │ │ ├── login │ │ │ └── Login.jsx │ │ ├── register │ │ │ └── Register.jsx │ │ ├── home │ │ │ └── Home.jsx │ │ ├── products │ │ │ └── Products.jsx │ │ ├── cart │ │ │ └── Cart.jsx │ │ └── bills │ │ │ └── Bills.jsx │ ├── App.js │ └── index.css ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── .gitignore ├── package.json └── README.md ├── .gitignore ├── routes ├── userRoutes.js ├── billsRoutes.js └── productsRoutes.js ├── models ├── userModel.js ├── productModel.js └── billsModel.js ├── controllers ├── billsController.js ├── userController.js └── productController.js ├── package.json ├── README.md ├── server.js └── utils └── data.js /client/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | dotenv 4 | env 5 | data.json -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amPhilip/POS-System/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amPhilip/POS-System/HEAD/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amPhilip/POS-System/HEAD/client/public/logo512.png -------------------------------------------------------------------------------- /client/src/components/Spinner.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Spinner = () => { 4 | return ( 5 |
6 |

Loading...

7 |
8 | ) 9 | } 10 | 11 | export default Spinner 12 | -------------------------------------------------------------------------------- /routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { loginController, registerController } from "../controllers/userController.js"; 3 | 4 | const userRouter = express.Router(); 5 | 6 | userRouter.post("/login", loginController); 7 | 8 | userRouter.post("/register", registerController); 9 | 10 | export default userRouter; -------------------------------------------------------------------------------- /routes/billsRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { addBillsController, getBillsController } from "../controllers/billsController.js"; 3 | 4 | const billsRouter = express.Router(); 5 | 6 | billsRouter.post("/addbills", addBillsController); 7 | 8 | billsRouter.get("/getbills", getBillsController); 9 | 10 | export default billsRouter; -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import {Provider} from 'react-redux'; 4 | import './index.css'; 5 | import App from './App'; 6 | import store from './redux/store'; 7 | 8 | const root = ReactDOM.createRoot(document.getElementById('root')); 9 | root.render( 10 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /models/userModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | //for create table into db 4 | const userSchema = new mongoose.Schema({ 5 | 6 | name: { type: String, required: true }, 7 | userId: {type: String, required: true}, 8 | password: {type: String, required: true}, 9 | verified: {type: Boolean} 10 | 11 | }, { 12 | //for date 13 | timestamps: true 14 | }); 15 | 16 | const User = mongoose.model("User", userSchema); 17 | export default User; -------------------------------------------------------------------------------- /models/productModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | //for create table into db 4 | const productSchema = new mongoose.Schema({ 5 | 6 | name: { type: String, required: true }, 7 | category: { type: String, required: true }, 8 | price: { type: Number, required: true }, 9 | image: { type: String, required: true } 10 | 11 | }, { 12 | //for date 13 | timestamps: true 14 | }); 15 | 16 | const Product = mongoose.model("Product", productSchema); 17 | export default Product; -------------------------------------------------------------------------------- /routes/productsRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { getProductController, addProductController, updateProductController, deleteProductController } from "../controllers/productController.js"; 3 | 4 | const productRouter = express.Router(); 5 | 6 | productRouter.get("/getproducts", getProductController); 7 | 8 | productRouter.post("/addproducts", addProductController); 9 | 10 | productRouter.put("/updateproducts", updateProductController); 11 | 12 | productRouter.post("/deleteproducts", deleteProductController); 13 | 14 | export default productRouter; -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /controllers/billsController.js: -------------------------------------------------------------------------------- 1 | import Bills from "../models/billsModel.js"; 2 | 3 | //for add or fetch 4 | export const getBillsController = async (req, res) => { 5 | try { 6 | 7 | const bills = await Bills.find(); 8 | res.send(bills); 9 | 10 | } catch(error) { 11 | console.log(error); 12 | } 13 | } 14 | 15 | //for add 16 | export const addBillsController = async (req, res) => { 17 | 18 | try { 19 | 20 | const newBills = new Bills(req.body); 21 | await newBills.save(); 22 | res.send("Bill Created Successfully!"); 23 | 24 | } catch(error) { 25 | console.log(error); 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /client/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from 'redux'; 2 | import thunk from 'redux-thunk' 3 | import { composeWithDevTools } from 'redux-devtools-extension'; 4 | import {rootReducer} from './rootReducer'; 5 | 6 | const finalReducer = combineReducers({ 7 | rootReducer, 8 | }); 9 | 10 | const initialState = { 11 | rootReducer: { 12 | cartItems: localStorage.getItem("cartItems") ? JSON.parse(localStorage.getItem("cartItems")) : [], 13 | }, 14 | }; 15 | 16 | const middleware = [thunk]; 17 | 18 | const store = createStore(finalReducer, initialState, composeWithDevTools(applyMiddleware(...middleware))); 19 | 20 | export default store; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pos-system", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "server.js", 7 | "scripts": { 8 | "server": "nodemon server.js", 9 | "client": "npm start --prefix client", 10 | "dev": "concurrently \"npm run server\" \"npm run client\"" 11 | }, 12 | "author": "amPhilip", 13 | "license": "ISC", 14 | "dependencies": { 15 | "body-parser": "^1.20.0", 16 | "color": "^4.2.3", 17 | "concurrently": "^7.2.1", 18 | "cors": "^2.8.5", 19 | "dotenv": "^16.0.1", 20 | "express": "^4.18.1", 21 | "mongoose": "^6.3.4", 22 | "morgan": "^1.10.0" 23 | }, 24 | "devDependencies": { 25 | "nodemon": "^2.0.16" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /models/billsModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | //for create table into db 4 | const billsSchema = new mongoose.Schema({ 5 | 6 | customerName: { type: String, required: true }, 7 | customerPhone: { type: Number, required: true }, 8 | customerAddress: { type: String, required: true }, 9 | subTotal: { type: Number, required: true }, 10 | totalAmount: { type: Number, required: true }, 11 | tax: { type: Number, required: true }, 12 | paymentMethod: { type: String, required: true }, 13 | cartItems: { type: Array, required: true } 14 | 15 | }, { 16 | //for date 17 | timestamps: true 18 | }); 19 | 20 | const Bills = mongoose.model("Bills", billsSchema); 21 | export default Bills; -------------------------------------------------------------------------------- /client/src/components/Product.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Card } from 'antd'; 3 | import { useDispatch } from 'react-redux'; 4 | 5 | const Product = ({product}) => { 6 | const dispatch = useDispatch(); 7 | 8 | const handlerToCart = () => { 9 | dispatch({ 10 | type: "ADD_TO_CART", 11 | payload: { ...product, quantity: 1 } 12 | }) 13 | } 14 | 15 | const { Meta } = Card; 16 | 17 | return ( 18 | } 22 | > 23 | 24 |
25 | 26 |
27 |
28 | ) 29 | } 30 | 31 | export default Product 32 | -------------------------------------------------------------------------------- /controllers/userController.js: -------------------------------------------------------------------------------- 1 | import User from "../models/userModel.js"; 2 | 3 | 4 | //for login 5 | export const loginController = async (req, res) => { 6 | try { 7 | 8 | const {userId, password} = req.body; 9 | const user = await User.findOne({userId, password}); 10 | if(user) { 11 | res.status(200).send(user); 12 | } else { 13 | res.json({ 14 | message: "Login Fail", 15 | user, 16 | }); 17 | } 18 | 19 | } catch(error) { 20 | console.log(error); 21 | } 22 | } 23 | 24 | //for register 25 | export const registerController = async (req, res) => { 26 | 27 | try { 28 | 29 | const newUser = new User({...req.body, verified: true}); 30 | await newUser.save(); 31 | res.status(200).send("New User Added Successfully!"); 32 | 33 | } catch(error) { 34 | console.log(error); 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /client/src/redux/rootReducer.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | loading: false, 3 | cartItems: [] 4 | } 5 | 6 | export const rootReducer = (state = initialState, action) => { 7 | switch(action.type) { 8 | case "SHOW_LOADING": 9 | return { 10 | ...state, 11 | loading: true, 12 | }; 13 | case "HIDE_LOADING": 14 | return { 15 | ...state, 16 | loading: false, 17 | }; 18 | case "ADD_TO_CART": 19 | return { 20 | ...state, 21 | cartItems: [...state.cartItems, action.payload] 22 | }; 23 | case "UPDATE_CART": 24 | return { 25 | ...state, 26 | cartItems: state.cartItems.map(product => product._id === action.payload._id ? {...product, quantity: action.payload.quantity} : product), 27 | }; 28 | case "DELETE_FROM_CART": 29 | return { 30 | ...state, 31 | cartItems: state.cartItems.filter((product) => product._id !== action.payload._id), 32 | }; 33 | default: return state; 34 | } 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | POS SYSTEM 2 | === 3 | ## MERN STACK Application
4 | ![Group 1](https://user-images.githubusercontent.com/40393613/178875220-72d94698-0fc2-4b1f-b8c2-51d10cb42ff1.png) 5 | 6 | ## Usage Guide 7 | 8 | If you are a total beginner and want to use this repository to Elevate your Skills, Start here! 9 | 10 | 1. Open VSCODE or any code editor of your choice. 11 | 12 | 2. In the Terminal Run this command: 13 | ```javascript=16 14 | git clone 15 | ``` 16 | 3. Once the prject is in the Code Editor, Run: 17 | ```javascript=16 18 | npm install /npm i 19 | ``` 20 | 4. cd into Client folder and repeat step 3 and run: 21 | ```javascript=16 22 | yarn start 23 | ``` 24 | 5. Open another Terminal and in the root folder run: 25 | ```javascript=16 26 | npm run server 27 | ``` 28 |
29 | 6.Add your own ENV Config Files 30 | 31 | 32 | ## NB: 33 | Anyone can use to repository as they deem it Fit. I accept anyone to fork this repository and submit their own pull request.
34 | 35 | 36 | ## Appendix and FAQ 37 | 38 | :::info 39 | **Find this Project incomplete? Or Having Some Errors** Kindly Leave a comment! 40 | ::: 41 | 42 | ###### tags: `Templates` `Documentation`
43 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import bodyParser from 'body-parser'; 3 | import cors from 'cors'; 4 | import morgan from 'morgan'; 5 | import dotenv from 'dotenv'; 6 | import mongoose from 'mongoose'; 7 | import productRouter from './routes/productsRoutes.js'; 8 | import userRouter from './routes/userRoutes.js'; 9 | import billsRouter from './routes/billsRoutes.js'; 10 | //require('colors'); 11 | 12 | dotenv.config(); 13 | 14 | //Connect with MongoDB 15 | mongoose.connect(process.env.MONGODB_URI).then(() => { 16 | console.log("Connected to DB"); 17 | }).catch((err) => { 18 | console.log(err.message); 19 | }); 20 | 21 | const app = express(); 22 | 23 | //middlewares 24 | app.use(cors()); 25 | app.use(express.json()); 26 | 27 | app.use(bodyParser.json()); 28 | app.use(bodyParser.urlencoded({extended: false})) 29 | app.use(morgan("dev")); 30 | 31 | //routes 32 | app.use('/api/products/', productRouter); 33 | app.use('/api/users/', userRouter); 34 | app.use('/api/bills/', billsRouter); 35 | 36 | //Create Port 37 | const PORT = process.env.PORT || 5000; 38 | 39 | //Listen 40 | app.listen(PORT, () => { 41 | console.log(`Serve at running on the port: http://localhost:${PORT}`); 42 | } ) -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "proxy": "http://localhost:5000", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.14.1", 8 | "@testing-library/react": "^13.0.0", 9 | "@testing-library/user-event": "^13.2.1", 10 | "antd": "^4.20.6", 11 | "axios": "^0.27.2", 12 | "react": "^18.2.0", 13 | "react-redux": "^8.0.2", 14 | "react-router-dom": "^6.3.0", 15 | "react-dom": "^18.2.0", 16 | "react-scripts": "5.0.1", 17 | "react-to-print": "^2.14.7", 18 | "redux": "^4.2.0", 19 | "redux-devtools-extension": "^2.13.9", 20 | "redux-thunk": "^2.4.1", 21 | "web-vitals": "^2.1.0" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/src/components/layout.css: -------------------------------------------------------------------------------- 1 | .ant-layout-sider { 2 | background: #001e28; 3 | } 4 | 5 | .ant-menu-dark .ant-menu-sub, .ant-menu.ant-menu-dark, .ant-menu.ant-menu-dark .ant-menu-sub { 6 | background: #001e28; 7 | } 8 | 9 | .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-selected { 10 | background-color: #ff7f50; 11 | } 12 | 13 | #components-layout-demo-custom-trigger .trigger { 14 | padding: 0 24px; 15 | font-size: 18px; 16 | line-height: 64px; 17 | cursor: pointer; 18 | transition: color 0.3s; 19 | } 20 | 21 | #components-layout-demo-custom-trigger .trigger:hover { 22 | color: #1890ff; 23 | } 24 | 25 | #components-layout-demo-custom-trigger .logo { 26 | height: 32px; 27 | margin: 16px; 28 | background: rgba(255, 255, 255, 0.3); 29 | } 30 | 31 | .site-layout .site-layout-background { 32 | background: #fff; 33 | } 34 | 35 | .ant-layout.ant-layout-has-sider { 36 | min-height: 100vh; 37 | } 38 | 39 | .anticon svg { 40 | font-size: 25px; 41 | } 42 | 43 | .ant-layout-header .anticon svg { 44 | margin-left: 10px; 45 | } 46 | 47 | .ant-menu-title-content { 48 | font-size: 18px; 49 | } 50 | 51 | .logo { 52 | text-align: center; 53 | margin: 15px 0; 54 | } 55 | 56 | .logo-title { 57 | letter-spacing: 1.3px; 58 | color: #ffffff; 59 | } -------------------------------------------------------------------------------- /controllers/productController.js: -------------------------------------------------------------------------------- 1 | import Product from "../models/productModel.js"; 2 | 3 | //for add or fetch 4 | export const getProductController = async (req, res) => { 5 | try { 6 | 7 | const products = await Product.find(); 8 | res.status(200).send(products); 9 | 10 | } catch(error) { 11 | console.log(error); 12 | } 13 | } 14 | 15 | //for add 16 | export const addProductController = async (req, res) => { 17 | 18 | try { 19 | 20 | const newProducts = new Product(req.body); 21 | await newProducts.save(); 22 | res.status(200).send("Products Created Successfully!"); 23 | 24 | } catch(error) { 25 | console.log(error); 26 | } 27 | 28 | } 29 | 30 | //for update 31 | export const updateProductController = async (req, res) => { 32 | try { 33 | 34 | await Product.findOneAndUpdate({_id: req.body.productId}, req.body, {new: true}) 35 | res.status(201).json("Product Updated!"); 36 | } catch(error) { 37 | res.status(400).send(error); 38 | console.log(error); 39 | } 40 | } 41 | 42 | //for delete 43 | export const deleteProductController = async (req, res) => { 44 | try { 45 | 46 | await Product.findOneAndDelete({_id: req.body.productId}) 47 | res.status(200).json("Product Deleted!"); 48 | } catch(error) { 49 | res.status(400).send(error); 50 | console.log(error); 51 | } 52 | } -------------------------------------------------------------------------------- /client/src/pages/customers/Customers.jsx: -------------------------------------------------------------------------------- 1 | import { Table } from 'antd'; 2 | import axios from 'axios'; 3 | import React, { useEffect, useState } from 'react' 4 | import { useDispatch } from 'react-redux'; 5 | import Layout from '../../components/Layout' 6 | 7 | const Customers = () => { 8 | 9 | const dispatch = useDispatch(); 10 | const [billsData, setBillsData] = useState([]); 11 | 12 | const getAllBills = async () => { 13 | try { 14 | dispatch({ 15 | type: "SHOW_LOADING", 16 | }); 17 | const {data} = await axios.get('/api/bills/getbills'); 18 | setBillsData(data); 19 | dispatch({ 20 | type: "HIDE_LOADING", 21 | }); 22 | console.log(data); 23 | 24 | } catch(error) { 25 | dispatch({ 26 | type: "HIDE_LOADING", 27 | }); 28 | console.log(error); 29 | } 30 | }; 31 | 32 | useEffect(() => { 33 | getAllBills(); 34 | }, []); 35 | 36 | const columns = [ 37 | { 38 | title: "ID", 39 | dataIndex: "_id" 40 | }, 41 | { 42 | title: "Customer Name", 43 | dataIndex: "customerName", 44 | }, 45 | { 46 | title: "Contact Number", 47 | dataIndex: "customerPhone", 48 | } 49 | , 50 | { 51 | title: "Customer Address", 52 | dataIndex: "customerAddress", 53 | } 54 | ] 55 | 56 | return ( 57 | 58 |

All Customers

59 | 60 | 61 | ) 62 | } 63 | 64 | export default Customers 65 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import {BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom' 2 | import 'antd/dist/antd.min.css'; 3 | import './App.css'; 4 | import Home from './pages/home/Home'; 5 | import Products from './pages/products/Products'; 6 | import Cart from './pages/cart/Cart'; 7 | import Login from './pages/login/Login'; 8 | import Register from './pages/register/Register'; 9 | import Bills from './pages/bills/Bills'; 10 | import Customers from './pages/customers/Customers'; 11 | 12 | function App() { 13 | return ( 14 | <> 15 | 16 | 17 | 19 | 20 | 21 | } /> 22 | 24 | 25 | 26 | } /> 27 | 29 | 30 | 31 | } /> 32 | 34 | 35 | 36 | } /> 37 | 39 | 40 | 41 | } /> 42 | } /> 43 | } /> 44 | 45 | 46 | 47 | ); 48 | } 49 | 50 | export default App; 51 | 52 | export function ProtectedRouter({children}) { 53 | if(localStorage.getItem("auth")) { 54 | return children; 55 | } else { 56 | return 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /client/src/pages/login/Login.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Form, Input, message } from 'antd'; 2 | import FormItem from 'antd/lib/form/FormItem'; 3 | import axios from 'axios'; 4 | import React, { useEffect } from 'react' 5 | import { useDispatch } from 'react-redux'; 6 | import { Link, useNavigate } from 'react-router-dom'; 7 | 8 | const Login = () => { 9 | 10 | const dispatch = useDispatch(); 11 | const navigate = useNavigate(); 12 | 13 | const handlerSubmit = async (value) => { 14 | //console.log(value); 15 | try { 16 | dispatch({ 17 | type: "SHOW_LOADING", 18 | }); 19 | const res = await axios.post('/api/users/login', value); 20 | dispatch({ 21 | type: "HIDE_LOADING", 22 | }); 23 | message.success("User Login Successfully!"); 24 | localStorage.setItem("auth", JSON.stringify(res.data)); 25 | navigate("/"); 26 | 27 | 28 | } catch(error) { 29 | dispatch({ 30 | type: "HIDE_LOADING", 31 | }); 32 | message.error("Error!") 33 | console.log(error); 34 | } 35 | } 36 | 37 | useEffect(() => { 38 | if(localStorage.getItem("auth")) { 39 | localStorage.getItem("auth"); 40 | navigate("/"); 41 | } 42 | 43 | }, [navigate]); 44 | 45 | return ( 46 |
47 |

POS SYSTEM

48 |

Login

49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 | 59 | Register Here! 60 |
61 | 62 |
63 |
64 | ) 65 | } 66 | 67 | export default Login 68 | -------------------------------------------------------------------------------- /client/src/pages/register/Register.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Form, Input, message } from 'antd' 2 | import FormItem from 'antd/lib/form/FormItem' 3 | import axios from 'axios' 4 | import React, { useEffect } from 'react' 5 | import { useDispatch } from 'react-redux' 6 | import { Link, useNavigate } from 'react-router-dom' 7 | 8 | const Register = () => { 9 | 10 | const dispatch = useDispatch(); 11 | const navigate = useNavigate(); 12 | 13 | const handlerSubmit = async (value) => { 14 | try { 15 | dispatch({ 16 | type: "SHOW_LOADING", 17 | }); 18 | await axios.post('/api/users/register', value); 19 | message.success("Register Successfully!"); 20 | navigate("/login"); 21 | dispatch({ 22 | type: "HIDE_LOADING", 23 | }); 24 | 25 | 26 | } catch(error) { 27 | dispatch({ 28 | type: "HIDE_LOADING", 29 | }); 30 | message.error("Error!") 31 | console.log(error); 32 | } 33 | } 34 | 35 | useEffect(() => { 36 | if(localStorage.getItem("auth")) { 37 | localStorage.getItem("auth"); 38 | navigate("/"); 39 | } 40 | }, [navigate]); 41 | 42 | 43 | return ( 44 |
45 |

POS SYSTEM

46 |

Register Account

47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | 60 | Login Here! 61 |
62 | 63 |
64 |
65 | ) 66 | } 67 | 68 | export default Register 69 | -------------------------------------------------------------------------------- /client/src/pages/home/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import axios from 'axios' 3 | import LayoutApp from '../../components/Layout' 4 | import { Row, Col } from 'antd'; 5 | import Product from '../../components/Product'; 6 | import { useDispatch } from 'react-redux'; 7 | 8 | const Home = () => { 9 | 10 | const dispatch = useDispatch(); 11 | 12 | const [productData, setProductData] = useState([]); 13 | const [selectedCategory, setSelectedCategory] = useState('pizzas'); 14 | const categories = [ 15 | { 16 | name: "pizzas", 17 | imageUrl: "https://creazilla-store.fra1.digitaloceanspaces.com/cliparts/27954/pizza-pepperoni-clipart-xl.png", 18 | }, 19 | { 20 | name: "burgers", 21 | imageUrl: "https://cdn.pixabay.com/photo/2022/01/04/23/00/fast-food-6916101_960_720.png", 22 | }, 23 | { 24 | name: "drinks", 25 | imageUrl: "https://images.vexels.com/media/users/3/246333/isolated/preview/9626dce3278f72220ea2736de64e6233-pink-cocktail-color-stroke.png", 26 | }, 27 | 28 | ] 29 | 30 | useEffect(() => { 31 | const getAllProducts = async () => { 32 | try { 33 | dispatch({ 34 | type: "SHOW_LOADING", 35 | }); 36 | const {data} = await axios.get('/api/products/getproducts'); 37 | setProductData(data); 38 | dispatch({ 39 | type: "HIDE_LOADING", 40 | }); 41 | console.log(data); 42 | 43 | } catch(error) { 44 | console.log(error); 45 | } 46 | }; 47 | 48 | getAllProducts(); 49 | }, [dispatch]); 50 | 51 | 52 | return ( 53 | 54 |
55 | {categories.map((category) => ( 56 |
setSelectedCategory(category.name)}> 57 |

{category.name}

58 | {category.name} 59 |
60 | ))} 61 |
62 | 63 | {productData.filter((i) => i.category === selectedCategory).map((product) => ( 64 |
65 | 66 | 67 | ))} 68 | 69 | 70 | ) 71 | } 72 | 73 | export default Home 74 | -------------------------------------------------------------------------------- /client/src/components/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Layout, Menu } from 'antd'; 3 | import { 4 | MenuUnfoldOutlined, 5 | MenuFoldOutlined, 6 | HomeOutlined, 7 | UserSwitchOutlined, 8 | MoneyCollectOutlined, 9 | LogoutOutlined, 10 | ShoppingCartOutlined 11 | } from '@ant-design/icons'; 12 | import './layout.css'; 13 | import { Link, useNavigate } from 'react-router-dom'; 14 | import { useSelector } from 'react-redux'; 15 | import Spinner from './Spinner'; 16 | 17 | const { Header, Sider, Content } = Layout; 18 | 19 | const LayoutApp = ({children}) => { 20 | const {cartItems, loading} = useSelector(state => state.rootReducer); 21 | 22 | const [collapsed, setCollapsed] = useState(false); 23 | const navigate = useNavigate(); 24 | 25 | const toggle = () => { 26 | setCollapsed(!collapsed); 27 | }; 28 | 29 | useEffect(() => { 30 | localStorage.setItem('cartItems', JSON.stringify(cartItems)) 31 | }, [cartItems]); 32 | 33 | return ( 34 | 35 | {loading && } 36 | 37 |
38 |

POS SYSTEM

39 |
40 | 41 | }> 42 | Home 43 | 44 | }> 45 | Bills 46 | 47 | }> 48 | Products 49 | 50 | }> 51 | Customers 52 | 53 | } onClick={() => {localStorage.removeItem("auth"); navigate("/login");}}> 54 | LogOut 55 | 56 | 57 |
58 | 59 |
60 | {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, { 61 | className: 'trigger', 62 | onClick: toggle, 63 | })} 64 |
navigate('/cart')}> 65 | 66 | {cartItems.length} 67 |
68 |
69 | 77 | {children} 78 | 79 |
80 |
81 | ); 82 | }; 83 | 84 | export default LayoutApp; -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `yarn build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /utils/data.js: -------------------------------------------------------------------------------- 1 | //Now i create data file for products, because it's easier 2 | //This time I will show you how to manually enter a file in DB, I will not create a seed file 3 | const products = [ 4 | { 5 | name: "Product 1", 6 | category: "pizzas", 7 | price: 2.2, 8 | image: "/images/1.png", 9 | }, 10 | { 11 | name: "Product 6", 12 | category: "burgers", 13 | price: 1.75, 14 | image: "/images/6.png", 15 | }, 16 | { 17 | name: "Product 2", 18 | category: "pizzas", 19 | price: 1.85, 20 | image: "/images/2.png", 21 | }, 22 | { 23 | name: "Product 3", 24 | category: "pizzas", 25 | price: 1.95, 26 | image: "/images/3.png", 27 | }, 28 | { 29 | name: "Product 7", 30 | category: "burgers", 31 | price: 2.1, 32 | image: "/images/7.png", 33 | }, 34 | { 35 | name: "Product 12", 36 | category: "drinks", 37 | price: 2.25, 38 | image: "/images/12.png", 39 | }, 40 | { 41 | name: "Product 4", 42 | category: "pizzas", 43 | price: 2.5, 44 | image: "/images/4.png", 45 | }, 46 | { 47 | name: "Product 5", 48 | category: "pizzas", 49 | price: 1.8, 50 | image: "/images/5.png", 51 | }, 52 | { 53 | name: "Product 8", 54 | category: "burgers", 55 | price: 1.95, 56 | image: "/images/8.png", 57 | }, 58 | { 59 | name: "Product 9", 60 | category: "burgers", 61 | price: 2, 62 | image: "/images/9.png", 63 | }, 64 | , 65 | { 66 | name: "Product 13", 67 | category: "drinks", 68 | price: 1.5, 69 | image: "/images/13.png", 70 | }, 71 | , 72 | { 73 | name: "Product 10", 74 | category: "burgers", 75 | price: 2, 76 | image: "/images/10.png", 77 | }, 78 | , 79 | { 80 | name: "Product 11", 81 | category: "burgers", 82 | price: 2.25, 83 | image: "/images/11.png", 84 | }, 85 | , 86 | { 87 | name: "Product 14", 88 | category: "drinks", 89 | price: 1.8, 90 | image: "/images/14.png", 91 | }, 92 | , 93 | { 94 | name: "Product 15", 95 | category: "drinks", 96 | price: 1.7, 97 | image: "/images/15.png", 98 | }, 99 | , 100 | { 101 | name: "Product 16", 102 | category: "drinks", 103 | price: 2, 104 | image: "/images/16.png", 105 | }, 106 | , 107 | { 108 | name: "Product 17", 109 | category: "drinks", 110 | price: 2.8, 111 | image: "/images/17.png", 112 | }, 113 | , 114 | { 115 | name: "Product 18", 116 | category: "drinks", 117 | price: 2.4, 118 | image: "/images/18.png", 119 | }, 120 | , 121 | { 122 | name: "Product 19", 123 | category: "drinks", 124 | price: 2.5, 125 | image: "/images/19.png", 126 | }, 127 | , 128 | { 129 | name: "Product 20", 130 | category: "drinks", 131 | price: 1.9, 132 | image: "/images/20.png", 133 | }, 134 | , 135 | { 136 | name: "Product 21", 137 | category: "drinks", 138 | price: 2.2, 139 | image: "/images/21.png", 140 | } 141 | ] 142 | 143 | export default products; -------------------------------------------------------------------------------- /client/src/pages/products/Products.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import React, {useEffect, useState} from 'react' 3 | import { useDispatch } from 'react-redux'; 4 | import LayoutApp from '../../components/Layout' 5 | import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; 6 | import { Button, Form, Input, Modal, Select, Table, message } from 'antd'; 7 | import FormItem from 'antd/lib/form/FormItem'; 8 | 9 | const Products = () => { 10 | 11 | const dispatch = useDispatch(); 12 | const [productData, setProductData] = useState([]); 13 | const [popModal, setPopModal] = useState(false); 14 | const [editProduct, setEditProduct] = useState(false); 15 | 16 | const getAllProducts = async () => { 17 | try { 18 | dispatch({ 19 | type: "SHOW_LOADING", 20 | }); 21 | const {data} = await axios.get('/api/products/getproducts'); 22 | setProductData(data); 23 | dispatch({ 24 | type: "HIDE_LOADING", 25 | }); 26 | console.log(data); 27 | 28 | } catch(error) { 29 | dispatch({ 30 | type: "HIDE_LOADING", 31 | }); 32 | console.log(error); 33 | } 34 | }; 35 | 36 | useEffect(() => { 37 | getAllProducts(); 38 | }, []); 39 | 40 | const handlerDelete = async (record) => { 41 | try { 42 | dispatch({ 43 | type: "SHOW_LOADING", 44 | }); 45 | await axios.post('/api/products/deleteproducts', {productId:record._id}); 46 | message.success("Product Deleted Successfully!") 47 | getAllProducts(); 48 | setPopModal(false); 49 | dispatch({ 50 | type: "HIDE_LOADING", 51 | }); 52 | 53 | 54 | } catch(error) { 55 | dispatch({ 56 | type: "HIDE_LOADING", 57 | }); 58 | message.error("Error!") 59 | console.log(error); 60 | } 61 | } 62 | 63 | const columns = [ 64 | { 65 | title: "Name", 66 | dataIndex: "name" 67 | }, 68 | { 69 | title: "Image", 70 | dataIndex: "image", 71 | render:(image, record) => {record.name} 72 | }, 73 | { 74 | title: "Price", 75 | dataIndex: "price", 76 | }, 77 | { 78 | title: "Action", 79 | dataIndex: "_id", 80 | render:(id, record) => 81 |
82 | handlerDelete(record)}/> 83 | {setEditProduct(record); setPopModal(true)}} /> 84 |
85 | 86 | } 87 | ] 88 | 89 | const handlerSubmit = async (value) => { 90 | //console.log(value); 91 | if(editProduct === null) { 92 | try { 93 | dispatch({ 94 | type: "SHOW_LOADING", 95 | }); 96 | const res = await axios.post('/api/products/addproducts', value); 97 | message.success("Product Added Successfully!") 98 | getAllProducts(); 99 | setPopModal(false); 100 | dispatch({ 101 | type: "HIDE_LOADING", 102 | }); 103 | 104 | 105 | } catch(error) { 106 | dispatch({ 107 | type: "HIDE_LOADING", 108 | }); 109 | message.error("Error!") 110 | console.log(error); 111 | } 112 | } else { 113 | try { 114 | dispatch({ 115 | type: "SHOW_LOADING", 116 | }); 117 | await axios.put('/api/products/updateproducts', {...value, productId:editProduct._id}); 118 | message.success("Product Updated Successfully!") 119 | getAllProducts(); 120 | setPopModal(false); 121 | dispatch({ 122 | type: "HIDE_LOADING", 123 | }); 124 | 125 | 126 | } catch(error) { 127 | dispatch({ 128 | type: "HIDE_LOADING", 129 | }); 130 | message.error("Error!") 131 | console.log(error); 132 | } 133 | } 134 | } 135 | 136 | return ( 137 | 138 |

All Products

139 | 140 |
141 | 142 | { 143 | popModal && 144 | {setEditProduct(null); setPopModal(false);}} footer={false}> 145 |
146 | 147 | 148 | 149 | 150 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 |
163 | 164 |
165 | 166 |
167 | } 168 | 169 | 170 | ) 171 | } 172 | 173 | export default Products 174 | -------------------------------------------------------------------------------- /client/src/pages/cart/Cart.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import Layout from '../../components/Layout' 4 | import { DeleteOutlined, PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons'; 5 | import { Button, Form, Input, message, Modal, Select, Table } from 'antd'; 6 | import FormItem from 'antd/lib/form/FormItem'; 7 | import { useNavigate } from 'react-router-dom'; 8 | import axios from 'axios'; 9 | 10 | const Cart = () => { 11 | 12 | const [subTotal, setSubTotal] = useState(0); 13 | const [billPopUp, setBillPopUp] = useState(false); 14 | 15 | const dispatch = useDispatch(); 16 | const navigate = useNavigate(); 17 | 18 | const {cartItems} = useSelector(state => state.rootReducer); 19 | 20 | const handlerIncrement = (record) => { 21 | dispatch({ 22 | type: "UPDATE_CART", 23 | payload: {...record, quantity: record.quantity + 1} 24 | }); 25 | }; 26 | 27 | const handlerDecrement = (record) => { 28 | if(record.quantity !== 1){ 29 | dispatch({ 30 | type: "UPDATE_CART", 31 | payload: {...record, quantity: record.quantity - 1} 32 | }); 33 | } 34 | }; 35 | 36 | const handlerDelete = (record) => { 37 | dispatch({ 38 | type: "DELETE_FROM_CART", 39 | payload: record 40 | }); 41 | } 42 | 43 | const columns = [ 44 | { 45 | title: "Name", 46 | dataIndex: "name" 47 | }, 48 | { 49 | title: "Image", 50 | dataIndex: "image", 51 | render:(image, record) => {record.name} 52 | }, 53 | { 54 | title: "Price", 55 | dataIndex: "price", 56 | } 57 | , 58 | { 59 | title: "Quantity", 60 | dataIndex: "_id", 61 | render:(id, record) => 62 |
63 | handlerDecrement(record)}/> 64 | {record.quantity} 65 | handlerIncrement(record)} /> 66 |
67 | } 68 | , 69 | { 70 | title: "Action", 71 | dataIndex: "_id", 72 | render:(id, record) => handlerDelete(record)} /> 73 | } 74 | ] 75 | 76 | useEffect(() => { 77 | 78 | let temp = 0; 79 | cartItems.forEach((product) => (temp = temp + product.price * product.quantity)); 80 | setSubTotal(temp); 81 | 82 | }, [cartItems]); 83 | 84 | const handlerSubmit = async (value) => { 85 | //console.log(value); 86 | try { 87 | const newObject = { 88 | ...value, 89 | cartItems, 90 | subTotal, 91 | tax: Number(((subTotal / 100) * 10).toFixed(2)), 92 | totalAmount: Number((Number(subTotal) + Number(((subTotal / 100) * 10).toFixed(2))).toFixed(2)), 93 | userId: JSON.parse(localStorage.getItem("auth"))._id 94 | } 95 | await axios.post("/api/bills/addbills", newObject); 96 | message.success("Bill Generated!"); 97 | navigate("/bills"); 98 | } catch(error) { 99 | message.error("Error!") 100 | console.log(error); 101 | } 102 | } 103 | return ( 104 | 105 |

Cart

106 |
107 |
108 |

Sub Total: $ {(subTotal).toFixed(2)}

109 | 110 |
111 | setBillPopUp(false)} footer={false}> 112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 128 | 129 |
130 | SubTotal: ${(subTotal.toFixed(2))}
131 | Tax: ${((subTotal / 100) * 10).toFixed(2)} 132 |

Total: ${(Number(subTotal) + Number(((subTotal / 100) * 10).toFixed(2))).toFixed(2)}

133 |
134 |
135 | 136 |
137 | 138 |
139 | 140 | ) 141 | } 142 | 143 | export default Cart 144 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | body { 10 | font-family: 'Montserrat', sans-serif; 11 | } 12 | 13 | .product-btn { 14 | margin-top: 25px; 15 | width: 100%; 16 | } 17 | 18 | .product-btn button { 19 | display: block; 20 | margin: 0 auto; 21 | background-color: #001e28; 22 | color: #ffffff; 23 | box-shadow: 0 0 5px #001e28; 24 | transition: all 0.5s; 25 | border: none; 26 | } 27 | 28 | .product-btn button:hover { 29 | background-color: #ffffff; 30 | color: #001e28; 31 | } 32 | 33 | .ant-layout-header { 34 | display: flex; 35 | justify-content: space-between; 36 | align-items: center; 37 | } 38 | 39 | .cart-items { 40 | position: relative; 41 | margin-right: 25px; 42 | cursor: pointer; 43 | } 44 | 45 | .ant-layout-header .anticon svg { 46 | font-size: 30px; 47 | vertical-align: middle; 48 | } 49 | 50 | .cart-items span { 51 | font-size: 18px; 52 | } 53 | 54 | .cart-badge { 55 | padding: 5px; 56 | top: -10px; 57 | right: -25px; 58 | background-color: #001e28; 59 | color: #ffffff; 60 | border-radius: 50px; 61 | } 62 | 63 | .cart-minus { 64 | margin-right: 10px; 65 | cursor: pointer; 66 | transition: all 0.5s; 67 | } 68 | 69 | .cart-minus:hover { 70 | filter: drop-shadow(0 0 2px #001e28); 71 | color: #ffffff; 72 | } 73 | 74 | .cart-plus { 75 | margin-left: 10px; 76 | cursor: pointer; 77 | transition: all 0.5s; 78 | } 79 | 80 | .cart-plus:hover { 81 | filter: drop-shadow(0 0 2px #001e28); 82 | color: #ffffff; 83 | } 84 | 85 | .cart-quantity { 86 | font-size: 20px; 87 | vertical-align: bottom; 88 | } 89 | 90 | .cart-action { 91 | cursor: pointer; 92 | transition: all 0.5s; 93 | } 94 | 95 | .cart-action:hover { 96 | filter: drop-shadow(0 0 2px #ff0000); 97 | color: #ffffff; 98 | } 99 | 100 | .loading { 101 | position: absolute; 102 | left: 50%; 103 | top: 25%; 104 | text-align: center; 105 | letter-spacing: 1.3px; 106 | color: #001e28; 107 | } 108 | 109 | .cart-edit { 110 | cursor: pointer; 111 | transition: all 0.5s; 112 | margin-left: 30px; 113 | } 114 | 115 | .cart-edit.eye { 116 | margin-left: unset; 117 | } 118 | 119 | .cart-edit:hover { 120 | filter: drop-shadow(0 0 2px #0000ff); 121 | color: #ffffff; 122 | } 123 | 124 | .add-new { 125 | display: block !important; 126 | margin: 0 0 15px auto !important; 127 | background-color: #001e28 !important; 128 | color: #ffffff !important; 129 | box-shadow: 0 0 5px #001e28 !important; 130 | transition: all 0.5s; 131 | border: none !important; 132 | } 133 | 134 | .add-new:hover { 135 | background-color: #ffffff !important; 136 | color: #001e28 !important; 137 | } 138 | 139 | .category { 140 | display: flex; 141 | justify-content: center; 142 | margin-bottom: 50px; 143 | } 144 | 145 | .categoryFlex { 146 | display: flex; 147 | justify-content: center; 148 | align-items: center; 149 | flex-direction: column; 150 | margin: 0 30px; 151 | cursor: pointer; 152 | padding: 5px; 153 | } 154 | 155 | .category-active { 156 | box-shadow: 0 0 5px #afafaf; 157 | border-radius: 5px; 158 | } 159 | 160 | .categoryName { 161 | text-transform: capitalize; 162 | letter-spacing: 1.3px; 163 | } 164 | 165 | .form { 166 | display: flex; 167 | flex-direction: column; 168 | justify-content: center; 169 | align-items: center; 170 | min-height: 100vh; 171 | } 172 | 173 | .form h2 { 174 | letter-spacing: 2px; 175 | color: #001e28; 176 | font-size: 1.6rem; 177 | } 178 | 179 | .form p { 180 | letter-spacing: 1.3px; 181 | color: #001e28; 182 | font-size: 1.1rem; 183 | } 184 | 185 | .form-group { 186 | box-shadow: 0 0 2px #001e28; 187 | padding: 10px 15px; 188 | } 189 | 190 | .form-group input { 191 | border: none; 192 | outline: none !important; 193 | border-bottom: 1px solid #001e28; 194 | width: 400px; 195 | height: 40px; 196 | padding: 5px 15px; 197 | } 198 | 199 | .form-group input:focus, 200 | .form-group input:hover { 201 | border-color: #001e28; 202 | } 203 | 204 | .form-other { 205 | color: #001e28; 206 | letter-spacing: 1.3px; 207 | transition: all .1s ease-in-out; 208 | } 209 | 210 | .form-other:hover { 211 | color: #03141a; 212 | text-decoration: underline; 213 | font-size: 1rem; 214 | } 215 | 216 | .subTotal { 217 | margin-top: 40px; 218 | text-align: right; 219 | } 220 | 221 | .cardHeader h2 { 222 | letter-spacing: 1.3px; 223 | color: #001e28; 224 | } 225 | 226 | .cardHeader span { 227 | display: flex; 228 | justify-content: center; 229 | color: #001e28; 230 | letter-spacing: 1.3px; 231 | } 232 | 233 | .cardBody { 234 | margin-top: 30px; 235 | } 236 | 237 | .cardBody .group { 238 | display: flex; 239 | justify-content: space-between; 240 | border-bottom: 1px dashed #001e28; 241 | padding-top: 10px; 242 | } 243 | 244 | .footerCard { 245 | margin: 15px 0; 246 | box-shadow: 0 0 15px #afafaf; 247 | padding: 5px 15px; 248 | border-radius: 5px; 249 | } 250 | 251 | .cardFooter h4 { 252 | margin-top: 25px; 253 | text-align: center; 254 | text-transform: uppercase; 255 | letter-spacing: 2px; 256 | } 257 | 258 | .footerCard .group { 259 | display: flex; 260 | justify-content: space-between; 261 | } 262 | 263 | .footerCardTotal { 264 | width: 100px; 265 | margin: 25px 0 0 auto; 266 | } 267 | 268 | .footerCardTotal .group { 269 | display: flex; 270 | justify-content: space-between; 271 | align-items: center; 272 | } 273 | 274 | .footerThanks { 275 | text-align: center; 276 | margin-top: 10px; 277 | color: #001e28; 278 | } 279 | 280 | .bills-btn-add { 281 | margin-top: 15px; 282 | } 283 | 284 | .bills-btn-add .add-new { 285 | font-size: 12px !important; 286 | padding: 5px !important; 287 | } -------------------------------------------------------------------------------- /client/src/pages/bills/Bills.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Modal, Table } from 'antd'; 2 | import axios from 'axios'; 3 | import React, { useEffect, useState, useRef } from 'react' 4 | import ReactToPrint from 'react-to-print'; 5 | import { useReactToPrint } from 'react-to-print'; 6 | import { EyeOutlined } from '@ant-design/icons'; 7 | import { useDispatch } from 'react-redux'; 8 | import Layout from '../../components/Layout' 9 | 10 | const Bills = () => { 11 | const componentRef = useRef(); 12 | const dispatch = useDispatch(); 13 | const [billsData, setBillsData] = useState([]); 14 | const [popModal, setPopModal] = useState(false); 15 | const [selectedBill, setSelectedBill] = useState(null); 16 | 17 | const getAllBills = async () => { 18 | try { 19 | dispatch({ 20 | type: "SHOW_LOADING", 21 | }); 22 | const {data} = await axios.get('/api/bills/getbills'); 23 | setBillsData(data); 24 | dispatch({ 25 | type: "HIDE_LOADING", 26 | }); 27 | console.log(data); 28 | 29 | } catch(error) { 30 | dispatch({ 31 | type: "HIDE_LOADING", 32 | }); 33 | console.log(error); 34 | } 35 | }; 36 | 37 | useEffect(() => { 38 | getAllBills(); 39 | }, []); 40 | 41 | 42 | 43 | const columns = [ 44 | { 45 | title: "ID", 46 | dataIndex: "_id" 47 | }, 48 | { 49 | title: "Customer Name", 50 | dataIndex: "customerName", 51 | }, 52 | { 53 | title: "Contact Number", 54 | dataIndex: "customerPhone", 55 | } 56 | , 57 | { 58 | title: "Customer Address", 59 | dataIndex: "customerAddress", 60 | }, 61 | { 62 | title: "Sub Total", 63 | dataIndex: "subTotal", 64 | }, 65 | { 66 | title: "Tax", 67 | dataIndex: "tax", 68 | }, 69 | { 70 | title: "Total Amount", 71 | dataIndex: "totalAmount", 72 | }, 73 | { 74 | title: "Action", 75 | dataIndex: "_id", 76 | render:(id, record) => 77 |
78 | {setSelectedBill(record); setPopModal(true);}} /> 79 |
80 | 81 | } 82 | ] 83 | 84 | const handlePrint = useReactToPrint({ 85 | content: () => componentRef.current, 86 | }); 87 | 88 | return ( 89 | 90 |

All Invoice

91 |
92 | 93 | { 94 | popModal && 95 | setPopModal(false)} footer={false}> 96 |
97 |
98 |

MP POS

99 | Number: +381/0000000 100 | Address: 34000 Kragujevac, Serbia 101 |
102 |
103 |
104 | Customer Name: 105 | {selectedBill.customerName} 106 |
107 |
108 | Customer Phone: 109 | {selectedBill.customerPhone} 110 |
111 |
112 | Customer Address: 113 | {selectedBill.customerAddress} 114 |
115 |
116 | Date Order: 117 | {selectedBill.createdAt.toString().substring(0, 10)} 118 |
119 |
120 | Total Amount: 121 | ${selectedBill.totalAmount} 122 |
123 |
124 |
125 |

Your Order

126 | {selectedBill.cartItems.map((product) => ( 127 | <> 128 |
129 |
130 | Product: 131 | {product.name} 132 |
133 |
134 | Qty: 135 | {product.quantity} 136 |
137 |
138 | Price: 139 | ${product.price} 140 |
141 |
142 | 143 | ))} 144 |
145 |
146 |

Total:

147 |

${selectedBill.totalAmount}

148 |
149 |
150 |
151 | Thank You for buying from us 152 |
153 |
154 |
155 |
156 | 157 |
158 |
159 | } 160 | 161 | ) 162 | } 163 | 164 | export default Bills 165 | --------------------------------------------------------------------------------