├── src ├── App.module.css ├── assets │ ├── YourBizHere.gif │ └── YourBizHere.png ├── components │ ├── LogInForm │ │ ├── LoginForm.module.css │ │ └── LoginForm.js │ ├── UserLogOut │ │ ├── UserLogOut.module.css │ │ └── UserLogOut.js │ ├── MenuList │ │ ├── MenuList.module.css │ │ └── MenuList.js │ ├── Logo │ │ ├── Logo.js │ │ └── Logo.module.css │ ├── BarGraph │ │ ├── BarGraph.css │ │ └── BarGraph.js │ ├── NavBar │ │ ├── NavBar.module.css │ │ └── NavBar.js │ ├── OrderList │ │ ├── OrderList.module.css │ │ └── OrderList.js │ ├── CategoryList │ │ ├── CategoryList.js │ │ └── CategoryList.module.css │ ├── MenuListItem │ │ ├── MenuListItem.js │ │ └── MenuListItem.module.css │ ├── OrderListItem │ │ ├── OrderListItem.js │ │ └── OrderListItem.module.css │ ├── LineItem │ │ ├── LineItem.module.css │ │ └── LineItem.js │ ├── OrderDetail │ │ ├── OrderDetail.module.css │ │ └── OrderDetail.js │ └── SignUpForm │ │ └── SignUpForm.js ├── setupTests.js ├── utilities │ ├── items-api.js │ ├── send-request.js │ ├── order-api.js │ ├── users-service.js │ └── users-api.js ├── App.test.js ├── pages │ ├── AdminDashBoard.css │ ├── AdminDashBoard.js │ ├── NewOrderPage.module.css │ ├── OrderHistoryPage.module.css │ ├── AuthPage.module.css │ ├── AuthPage.js │ ├── OrderHistoryPage.js │ ├── NewOrderPage.js │ └── Settings.js ├── reportWebVitals.js ├── index.js ├── App.js ├── logo.svg └── index.css ├── ERD.png ├── Admindash.png ├── MainPage.png ├── YourBizHere.gif ├── login-signup.png ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── CRUD-flow-diagram.png ├── config ├── ensureLoggedIn.js ├── database.js └── checkToken.js ├── models ├── item.js ├── category.js ├── itemSchema.js ├── user.js └── order.js ├── routes └── api │ ├── items.js │ ├── users.js │ └── orders.js ├── .gitignore ├── controllers └── api │ ├── items.js │ ├── orders.js │ └── users.js ├── package.json ├── server.js ├── README.md └── seed.js /src/App.module.css: -------------------------------------------------------------------------------- 1 | .App { 2 | height: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /ERD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/ERD.png -------------------------------------------------------------------------------- /Admindash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/Admindash.png -------------------------------------------------------------------------------- /MainPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/MainPage.png -------------------------------------------------------------------------------- /YourBizHere.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/YourBizHere.gif -------------------------------------------------------------------------------- /login-signup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/login-signup.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /CRUD-flow-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/CRUD-flow-diagram.png -------------------------------------------------------------------------------- /src/assets/YourBizHere.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/src/assets/YourBizHere.gif -------------------------------------------------------------------------------- /src/assets/YourBizHere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MemeEngineer/YourBizHere/HEAD/src/assets/YourBizHere.png -------------------------------------------------------------------------------- /src/components/LogInForm/LoginForm.module.css: -------------------------------------------------------------------------------- 1 | /* .form-container{ 2 | display: "flex"; 3 | flex-direction: "column"; 4 | justify-content: "center"; 5 | align-items:"center"; 6 | } */ -------------------------------------------------------------------------------- /config/ensureLoggedIn.js: -------------------------------------------------------------------------------- 1 | module.exports = function(req, res, next) { 2 | // Status code of 401 is Unauthorized 3 | if (!req.user) return res.status(401).json('Unauthorized'); 4 | // A okay 5 | next(); 6 | }; -------------------------------------------------------------------------------- /src/components/UserLogOut/UserLogOut.module.css: -------------------------------------------------------------------------------- 1 | .UserLogOut { 2 | font-size: 1.5vmin; 3 | color: var(--text-light); 4 | text-align: center; 5 | } 6 | 7 | .UserLogOut .email { 8 | font-size: smaller; 9 | } -------------------------------------------------------------------------------- /models/item.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | // Ensure the Category model is processed by Mongoose 3 | require('./category'); 4 | 5 | const itemSchema = require('./itemSchema'); 6 | 7 | module.exports = mongoose.model('Item', itemSchema); -------------------------------------------------------------------------------- /src/components/MenuList/MenuList.module.css: -------------------------------------------------------------------------------- 1 | .MenuList { 2 | background-color: var(--tan-1); 3 | border: .1vmin solid var(--tan-3); 4 | border-radius: 2vmin; 5 | margin: 3vmin 0; 6 | padding: 3vmin; 7 | overflow-y: scroll; 8 | } -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | mongoose.connect(process.env.DATABASE_URL); 4 | 5 | const db = mongoose.connection; 6 | 7 | db.on('connected', function () { 8 | console.log(`Connected to ${db.name} at ${db.host}:${db.port}`); 9 | }); -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/utilities/items-api.js: -------------------------------------------------------------------------------- 1 | import sendRequest from './send-request'; 2 | 3 | const BASE_URL = '/api/items'; 4 | 5 | export function getAll() { 6 | return sendRequest(BASE_URL); 7 | } 8 | 9 | export function getById(id) { 10 | return sendRequest(`${BASE_URL}/${id}`); 11 | } -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/Logo/Logo.js: -------------------------------------------------------------------------------- 1 | import styles from './Logo.module.css'; 2 | import YourBizHere from '../../assets/YourBizHere.gif' 3 | 4 | export default function Logo() { 5 | return ( 6 |
7 | logo 8 |
9 | ); 10 | } -------------------------------------------------------------------------------- /src/pages/AdminDashBoard.css: -------------------------------------------------------------------------------- 1 | .dashboard{ 2 | background-color: var(--white); 3 | border-radius: 2vmin; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: space-evenly; 7 | align-items: center; 8 | align-content: center; 9 | margin: 3vmin 2vmin; 10 | } -------------------------------------------------------------------------------- /models/category.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const categorySchema = new Schema({ 5 | name: { type: String, required: true }, 6 | sortOrder: Number 7 | }, { 8 | timestamps: true 9 | }); 10 | 11 | module.exports = mongoose.model('Category', categorySchema); -------------------------------------------------------------------------------- /routes/api/items.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const itemsCtrl = require('../../controllers/api/items'); 4 | 5 | // GET /api/items 6 | router.get('/', itemsCtrl.index); 7 | // GET /api/items/:id 8 | router.get('/:id', itemsCtrl.show); 9 | 10 | module.exports = router; -------------------------------------------------------------------------------- /src/pages/AdminDashBoard.js: -------------------------------------------------------------------------------- 1 | import Bar from '../components/BarGraph/BarGraph.js' 2 | import "./AdminDashBoard.css" 3 | 4 | function adminDashBoard(){ 5 | return( 6 |
7 |

DASHBOARD

8 | 9 |
10 | ) 11 | } 12 | 13 | export default adminDashBoard; -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/components/BarGraph/BarGraph.css: -------------------------------------------------------------------------------- 1 | .table { 2 | display: inline-table; 3 | text-align: center; 4 | justify-content: center; 5 | border: 5px solid black; 6 | border-collapse: collapse; 7 | width: 80%; 8 | 9 | } 10 | thead th, td{ 11 | border-width: 2px; 12 | border-style: solid; 13 | border-color: black; 14 | background-color: #2F56A6; /* NYCC BLUE*/ 15 | color: white; 16 | font-weight: bold; 17 | } -------------------------------------------------------------------------------- /src/pages/NewOrderPage.module.css: -------------------------------------------------------------------------------- 1 | .NewOrderPage { 2 | height: 100%; 3 | display: grid; 4 | grid-template-columns: 1.6fr 3.5fr 3fr; 5 | grid-template-rows: 1fr; 6 | background-color: var(--white); 7 | border-radius: 2vmin; 8 | } 9 | 10 | .NewOrderPage aside { 11 | display: flex; 12 | flex-direction: column; 13 | justify-content: space-between; 14 | align-items: center; 15 | margin: 3vmin 2vmin; 16 | } -------------------------------------------------------------------------------- /src/components/Logo/Logo.module.css: -------------------------------------------------------------------------------- 1 | .Logo { 2 | height: 10px; 3 | width: 10px; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | border-radius: 50%; 9 | background-color: var(--blue); 10 | color: var(--tan-1); 11 | font-size: 2.7vmin; 12 | border: .6vmin solid var(--tan-2); 13 | } 14 | 15 | img{ 16 | height: 7em; 17 | width: 7em; 18 | } -------------------------------------------------------------------------------- /src/pages/OrderHistoryPage.module.css: -------------------------------------------------------------------------------- 1 | .OrderHistoryPage { 2 | height: 100%; 3 | display: grid; 4 | grid-template-columns: 1.6fr 3.5fr 3fr; 5 | grid-template-rows: 1fr; 6 | background-color: var(--white); 7 | border-radius: 2vmin; 8 | } 9 | 10 | .OrderHistoryPage .aside { 11 | display: flex; 12 | flex-direction: column; 13 | justify-content: space-between; 14 | align-items: center; 15 | margin: 3vmin 2vmin; 16 | } -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.module.css: -------------------------------------------------------------------------------- 1 | img{ 2 | height: 10em; 3 | width: 10em; 4 | } 5 | 6 | .NavBar button{ 7 | display: "flex"; 8 | flex-direction: 'row'; 9 | width: "50px"; 10 | padding: "12px"; 11 | margin: "0 6px 6px"; 12 | background: "Black"; 13 | text-decoration: "none"; 14 | color: "White"; 15 | 16 | } 17 | 18 | .NavBar button:hover{ 19 | 20 | background-color: green; 21 | padding: 10px; 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /models/itemSchema.js: -------------------------------------------------------------------------------- 1 | const item = require('./item'); 2 | 3 | const Schema = require('mongoose').Schema; 4 | 5 | const itemSchema = new Schema({ 6 | name: { type: String, required: true }, 7 | emoji: String, 8 | description: {type: String}, 9 | image: {type: String}, 10 | category: { type: Schema.Types.ObjectId, ref: 'Category' }, 11 | price: { type: Number, required: true, default: 0 } 12 | }, { 13 | timestamps: true 14 | }); 15 | 16 | module.exports = itemSchema; -------------------------------------------------------------------------------- /src/components/MenuList/MenuList.js: -------------------------------------------------------------------------------- 1 | import styles from './MenuList.module.css'; 2 | import MenuListItem from '../MenuListItem/MenuListItem'; 3 | 4 | export default function MenuList({ menuItems, handleAddToOrder }) { 5 | const items = menuItems.map(item => 6 | 11 | ); 12 | return ( 13 |
14 | {items} 15 |
16 | ); 17 | } -------------------------------------------------------------------------------- /src/components/OrderList/OrderList.module.css: -------------------------------------------------------------------------------- 1 | .OrderList { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | background-color: var(--tan-1); 6 | border: .1vmin solid var(--tan-3); 7 | border-radius: 2vmin; 8 | margin: 3vmin 0; 9 | padding: 3vmin; 10 | overflow-y: scroll; 11 | } 12 | 13 | .OrderList .noOrders { 14 | color: var(--text-light); 15 | font-size: 2vmin; 16 | position: absolute; 17 | top: calc(50vh); 18 | } -------------------------------------------------------------------------------- /src/pages/AuthPage.module.css: -------------------------------------------------------------------------------- 1 | .AuthPage { 2 | height: 100%; 3 | display: flex; 4 | justify-content: space-evenly; 5 | align-items: center; 6 | background-color: var(--white); 7 | border-radius: 2vmin; 8 | } 9 | 10 | .AuthPage h3 { 11 | margin-top: 4vmin; 12 | text-align: center; 13 | color: var(--text-light); 14 | cursor: pointer; 15 | } 16 | 17 | .AuthForm{ 18 | display:flex; 19 | align-items: center; 20 | flex-direction: column; 21 | 22 | } -------------------------------------------------------------------------------- /src/components/UserLogOut/UserLogOut.js: -------------------------------------------------------------------------------- 1 | import styles from './UserLogOut.module.css'; 2 | import { logOut } from '../../utilities/users-service'; 3 | 4 | export default function UserLogOut({ user, setUser }) { 5 | function handleLogOut() { 6 | logOut(); 7 | setUser(null); 8 | } 9 | 10 | return ( 11 |
12 |
{user.name}
13 |
{user.email}
14 | 15 |
16 | ); 17 | } -------------------------------------------------------------------------------- /routes/api/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const usersCtrl = require('../../controllers/api/users') 3 | const ensureLoggedIn = require('../../config/ensureLoggedIn.js') 4 | const router = express.Router() 5 | 6 | 7 | router.delete('/delete',usersCtrl.deleteUser) 8 | router.put('/update',ensureLoggedIn, usersCtrl.update) 9 | 10 | //POST 11 | router.post('/', usersCtrl.create) 12 | 13 | router.post('/login', usersCtrl.login) 14 | 15 | 16 | 17 | router.get('/check-token', ensureLoggedIn, usersCtrl.checkToken) 18 | 19 | 20 | module.exports = router -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/CategoryList/CategoryList.js: -------------------------------------------------------------------------------- 1 | import styles from './CategoryList.module.css'; 2 | 3 | export default function CategoryList({ categories, activeCat, setActiveCat }) { 4 | const cats = categories.map(cat => 5 |
  • setActiveCat(cat)} 11 | > 12 | {cat} 13 |
  • 14 | ); 15 | return ( 16 | 19 | ); 20 | } -------------------------------------------------------------------------------- /src/components/MenuListItem/MenuListItem.js: -------------------------------------------------------------------------------- 1 | import styles from './MenuListItem.module.css'; 2 | 3 | export default function MenuListItem({ menuItem, handleAddToOrder }) { 4 | return ( 5 |
    6 |
    {menuItem.emoji}
    7 |
    {menuItem.name}
    8 |
    9 | ${menuItem.price.toFixed(2)} 10 | 13 |
    14 |
    15 | ); 16 | } -------------------------------------------------------------------------------- /src/components/OrderList/OrderList.js: -------------------------------------------------------------------------------- 1 | import OrderListItem from '../OrderListItem/OrderListItem'; 2 | import styles from './OrderList.module.css'; 3 | 4 | export default function OrderList({ orders, activeOrder, handleSelectOrder }) { 5 | const orderItems = orders.map(o => 6 | 12 | ); 13 | 14 | return ( 15 |
    16 | {orderItems.length ? 17 | orderItems 18 | : 19 | No Previous Orders 20 | } 21 |
    22 | ); 23 | } -------------------------------------------------------------------------------- /src/components/CategoryList/CategoryList.module.css: -------------------------------------------------------------------------------- 1 | .CategoryList { 2 | color: var(--text-light); 3 | list-style: none; 4 | padding: 0; 5 | font-size: 1.7vw; 6 | } 7 | 8 | .CategoryList li { 9 | padding: .6vmin; 10 | text-align: center; 11 | border-radius: .5vmin; 12 | margin-bottom: .5vmin; 13 | } 14 | 15 | .CategoryList li:hover:not(.active) { 16 | cursor: pointer; 17 | background-color: var(--blue); 18 | color: var(--white); 19 | } 20 | 21 | .CategoryList li.active { 22 | color: var(--text-dark); 23 | background-color: var(--tan-1); 24 | border: .1vmin solid var(--tan-3); 25 | } -------------------------------------------------------------------------------- /routes/api/orders.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const ordersCtrl = require('../../controllers/api/orders'); 4 | 5 | // GET /api/orders/cart 6 | router.get('/cart', ordersCtrl.cart); 7 | // GET /api/orders/history 8 | router.get('/history', ordersCtrl.history); 9 | router.get('/historyitem',ordersCtrl.getItems); 10 | 11 | // POST /api/orders/cart/items/:id 12 | router.post('/cart/items/:id', ordersCtrl.addToCart); 13 | // POST /api/orders/cart/checkout 14 | router.post('/cart/checkout', ordersCtrl.checkout); 15 | // POST /api/orders/cart/qty 16 | router.put('/cart/qty', ordersCtrl.setItemQtyInCart); 17 | 18 | module.exports = router; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import {BrowserRouter as Router} from 'react-router-dom' 7 | 8 | const root = ReactDOM.createRoot(document.getElementById('root')); 9 | root.render( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | 17 | // If you want to start measuring performance in your app, pass a function 18 | // to log results (for example: reportWebVitals(console.log)) 19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 20 | reportWebVitals(); 21 | -------------------------------------------------------------------------------- /src/components/OrderListItem/OrderListItem.js: -------------------------------------------------------------------------------- 1 | import styles from './OrderListItem.module.css'; 2 | 3 | export default function OrderListItem({ order, isSelected, handleSelectOrder }) { 4 | return ( 5 |
    handleSelectOrder(order)}> 6 |
    7 |
    Order Id: {order.orderId}
    8 |
    {new Date(order.updatedAt).toLocaleDateString()}
    9 |
    10 |
    11 |
    ${order.orderTotal.toFixed(2)}
    12 |
    {order.totalQty} Item{order.totalQty > 1 ? 's' : ''}
    13 |
    14 |
    15 | ); 16 | } -------------------------------------------------------------------------------- /controllers/api/items.js: -------------------------------------------------------------------------------- 1 | const Item = require('../../models/item'); 2 | 3 | module.exports = { 4 | index, 5 | show 6 | }; 7 | 8 | async function index(req, res) { 9 | try{ 10 | const items = await Item.find({}).sort('name').populate('category').exec(); 11 | // re-sort based upon the sortOrder of the categories 12 | items.sort((a, b) => a.category.sortOrder - b.category.sortOrder); 13 | res.status(200).json(items); 14 | }catch(e){ 15 | res.status(400).json({ msg: e.message }); 16 | } 17 | } 18 | 19 | async function show(req, res) { 20 | try{ 21 | const item = await Item.findById(req.params.id); 22 | res.status(200).json(item); 23 | }catch(e){ 24 | res.status(400).json({ msg: e.message }); 25 | } 26 | } -------------------------------------------------------------------------------- /src/components/OrderListItem/OrderListItem.module.css: -------------------------------------------------------------------------------- 1 | .OrderListItem { 2 | width: 100%; 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | margin-bottom: 3vmin; 7 | padding: 2vmin; 8 | color: var(--text-light); 9 | background-color: var(--white); 10 | border: .2vmin solid var(--tan-3); 11 | border-radius: 1vmin; 12 | font-size: 2vmin; 13 | cursor: pointer; 14 | } 15 | 16 | .OrderListItem > div> div:first-child { 17 | margin-bottom: .5vmin; 18 | } 19 | 20 | .OrderListItem.selected { 21 | border-color: var(--blue); 22 | border-width: .2vmin; 23 | cursor: default; 24 | } 25 | 26 | .OrderListItem:not(.selected):hover { 27 | border-color: var(--blue); 28 | border-width: .2vmin; 29 | } -------------------------------------------------------------------------------- /src/components/LineItem/LineItem.module.css: -------------------------------------------------------------------------------- 1 | .LineItem { 2 | width: 100%; 3 | display: grid; 4 | grid-template-columns: 3vw 15.35vw 5.75vw 5.25vw; 5 | padding: 1vmin 0; 6 | color: var(--text-light); 7 | background-color: var(--white); 8 | border-top: .1vmin solid var(--tan-3); 9 | font-size: 1.5vw; 10 | } 11 | 12 | .LineItem:last-child { 13 | border-bottom: .1vmin solid var(--tan-3); 14 | } 15 | 16 | .LineItem .qty { 17 | display: flex; 18 | justify-content: space-between; 19 | align-items: center; 20 | font-size: 1.3vw; 21 | } 22 | 23 | .LineItem .extPrice { 24 | display: flex; 25 | justify-content: flex-end; 26 | align-items: center; 27 | font-size: 1.3vw; 28 | } 29 | 30 | .LineItem button { 31 | margin: 0; 32 | } -------------------------------------------------------------------------------- /config/checkToken.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | module.exports = function(req, res, next) { 4 | // Check for the token being sent in a header or as a query parameter 5 | let token = req.get('Authorization') || req.query.token; 6 | if (token) { 7 | // Remove the 'Bearer ' if it was included in the token header 8 | token = token.replace('Bearer ', ''); 9 | // Check if token is valid and not expired 10 | jwt.verify(token, process.env.SECRET, function(err, decoded) { 11 | // If valid token, decoded will be the token's entire payload 12 | // If invalid token, err will be set 13 | req.user = err ? null : decoded.user; 14 | // If your app cares... (optional) 15 | req.exp = err ? null : new Date(decoded.exp * 1000); 16 | return next(); 17 | }); 18 | } else { 19 | // No token was sent 20 | req.user = null; 21 | return next(); 22 | } 23 | }; -------------------------------------------------------------------------------- /src/utilities/send-request.js: -------------------------------------------------------------------------------- 1 | import { getToken } from './users-service'; 2 | 3 | export default async function sendRequest(url, method = 'GET', payload = null) { 4 | // Fetch takes an optional options object as the 2nd argument 5 | // used to include a data payload, set headers, etc. 6 | const options = { method }; 7 | if (payload) { 8 | options.headers = { 'Content-Type': 'application/json' }; 9 | options.body = JSON.stringify(payload); 10 | } 11 | const token = getToken(); 12 | if (token) { 13 | // Ensure headers object exists 14 | options.headers = options.headers || {}; 15 | // Add token to an Authorization header 16 | // Prefacing with 'Bearer' is recommended in the HTTP specification 17 | options.headers.Authorization = `Bearer ${token}`; 18 | } 19 | const res = await fetch(url, options); 20 | // res.ok will be false if the status code set to 4xx in the controller action 21 | if (res.ok) return res.json(); 22 | throw new Error('Bad Request'); 23 | } -------------------------------------------------------------------------------- /src/components/MenuListItem/MenuListItem.module.css: -------------------------------------------------------------------------------- 1 | .MenuListItem { 2 | width: 100%; 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | margin-bottom: 3vmin; 7 | padding: 2vmin; 8 | color: var(--text-light); 9 | background-color: var(--white); 10 | border: .1vmin solid var(--tan-3); 11 | border-radius: 1vmin; 12 | font-size: 4vmin; 13 | } 14 | 15 | .MenuListItem .emoji { 16 | height: 8vw; 17 | width: 8vw; 18 | font-size: 4vw; 19 | background-color: var(--tan-1); 20 | border: .1vmin solid var(--tan-3); 21 | border-radius: 1vmin; 22 | } 23 | 24 | .MenuListItem .buy { 25 | display: flex; 26 | flex-direction: column; 27 | } 28 | 29 | .MenuListItem .buy span { 30 | font-size: 1.7vw; 31 | text-align: center; 32 | color: var(--text-light); 33 | } 34 | 35 | .MenuListItem .name { 36 | font-size: 2vw; 37 | text-align: center; 38 | color: var(--text-light); 39 | } -------------------------------------------------------------------------------- /src/components/LineItem/LineItem.js: -------------------------------------------------------------------------------- 1 | import styles from './LineItem.module.css'; 2 | 3 | export default function LineItem({ lineItem, isPaid, handleChangeQty }) { 4 | return ( 5 |
    6 |
    {lineItem.item.emoji}
    7 |
    8 | {lineItem.item.name} 9 | {lineItem.item.price.toFixed(2)} 10 |
    11 |
    12 | {!isPaid && 13 | 17 | } 18 | {lineItem.qty} 19 | {!isPaid && 20 | 24 | } 25 |
    26 |
    ${lineItem.extPrice.toFixed(2)}
    27 |
    28 | ); 29 | } -------------------------------------------------------------------------------- /src/components/OrderDetail/OrderDetail.module.css: -------------------------------------------------------------------------------- 1 | .OrderDetail { 2 | flex-direction: column; 3 | justify-content: flex-start; 4 | align-items: center; 5 | padding: 3vmin; 6 | font-size: 2vmin; 7 | color: var(--text-light); 8 | } 9 | 10 | .OrderDetail .sectionHeading { 11 | width: 100% 12 | } 13 | 14 | .OrderDetail .lineItemContainer { 15 | margin-top: 3vmin; 16 | justify-content: flex-start; 17 | height: calc(100vh - 18vmin); 18 | width: 100%; 19 | } 20 | 21 | .OrderDetail .total { 22 | width: 100%; 23 | display: grid; 24 | grid-template-columns: 18.35vw 5.75vw 5.25vw; 25 | padding: 1vmin 0; 26 | color: var(--text-light); 27 | border-top: .1vmin solid var(--tan-3); 28 | } 29 | 30 | .OrderDetail .total span { 31 | display: flex; 32 | justify-content: center; 33 | align-items: center; 34 | font-size: 1.5vw; 35 | color: var(--text-dark); 36 | 37 | } 38 | 39 | .OrderDetail .total span.right { 40 | display: flex; 41 | justify-content: flex-end; 42 | margin: -50px; 43 | 44 | } 45 | 46 | .OrderDetail .hungry { 47 | position: absolute; 48 | top: 50vh; 49 | font-size: 2vmin; 50 | } -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const bcrypt = require('bcrypt') 4 | 5 | //salt round 6 | const SALT_ROUNDS = 6; 7 | 8 | const userSchema = new Schema({ 9 | name: {type: String, required: true}, 10 | email: { 11 | type: String, 12 | unique: true, 13 | trim: true, 14 | lowercase: true, 15 | required: true 16 | }, 17 | password: { 18 | type: String, 19 | trim: true, 20 | minLength: 3, 21 | required: true 22 | }, 23 | isAdmin:{type: Boolean, default: false} 24 | }, {timestamps: true, 25 | toJSON: { 26 | transform: function(doc,ret){ 27 | delete ret.password; 28 | return ret 29 | //removes password from showing up on db 30 | } 31 | } 32 | }); 33 | 34 | //pre-save hook 35 | 36 | userSchema.pre('save', async function(next){ 37 | //'this' is the user doc / if the password has not been modified 38 | if(!this.isModified('password')) return next(); 39 | 40 | // update the password with the computed hash 41 | this.password = await bcrypt.hash(this.password, SALT_ROUNDS) 42 | return next(); 43 | }) 44 | 45 | 46 | module.exports = mongoose.model('User', userSchema); -------------------------------------------------------------------------------- /src/utilities/order-api.js: -------------------------------------------------------------------------------- 1 | import sendRequest from './send-request'; 2 | 3 | const BASE_URL = '/api/orders'; 4 | 5 | // Retrieve an unpaid order for the logged in user 6 | export function getCart() { 7 | return sendRequest(`${BASE_URL}/cart`); 8 | } 9 | 10 | // Add an item to the cart 11 | export function addItemToCart(itemId) { 12 | // Just send itemId for best security (no pricing) 13 | return sendRequest(`${BASE_URL}/cart/items/${itemId}`, 'POST'); 14 | } 15 | 16 | // Update the item's qty in the cart 17 | // Will add the item to the order if not currently in the cart 18 | // Sending info via the data payload instead of a long URL 19 | export function setItemQtyInCart(itemId, newQty) { 20 | 21 | return sendRequest(`${BASE_URL}/cart/qty`, 'PUT', { itemId, newQty }); 22 | } 23 | 24 | // Updates the order's (cart's) isPaid property to true 25 | export function checkout() { 26 | // Changing data on the server, so make it a POST request 27 | return sendRequest(`${BASE_URL}/cart/checkout`, 'POST'); 28 | } 29 | 30 | // Return all paid orders for the logged in user 31 | export function getOrderHistory() { 32 | return sendRequest(`${BASE_URL}/history`); 33 | } 34 | 35 | export function getItemHistory(){ 36 | return sendRequest(`${BASE_URL}/historyitem`) 37 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import {useState} from 'react' 2 | import NewOrderPage from './pages/NewOrderPage'; 3 | import AuthPage from './pages/AuthPage'; 4 | import {Routes , Route, Navigate} from 'react-router-dom' 5 | import OrderHistoryPage from './pages/OrderHistoryPage'; 6 | import NavBar from './components/NavBar/NavBar'; 7 | import {getUser} from "./utilities/users-service" 8 | import styles from './App.module.css'; 9 | import Settings from './pages/Settings' 10 | import AdminDashBoard from './pages/AdminDashBoard'; 11 | 12 | function App() { 13 | const [user, setUser] = useState(getUser()) 14 | return ( 15 |
    16 | { 17 | user ? ( 18 | <> 19 | 20 | 21 | } /> 22 | } /> 23 | } /> 24 | }/> 25 | } /> 26 | 27 | 28 | ): () 29 | } 30 |
    31 | ); 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yourbizhere", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.11.1", 7 | "@emotion/styled": "^11.11.0", 8 | "@mui/material": "^5.14.10", 9 | "@testing-library/jest-dom": "^5.17.0", 10 | "@testing-library/react": "^13.4.0", 11 | "@testing-library/user-event": "^13.5.0", 12 | "bcrypt": "^5.1.1", 13 | "dotenv": "^16.3.1", 14 | "express": "^4.18.2", 15 | "jsonwebtoken": "^9.0.2", 16 | "mongoose": "^7.5.1", 17 | "morgan": "^1.10.0", 18 | "react": "^18.2.0", 19 | "react-dom": "^18.2.0", 20 | "react-router-dom": "^6.15.0", 21 | "react-scripts": "5.0.1", 22 | "recharts": "^2.8.0", 23 | "serve-favicon": "^2.5.0", 24 | "web-vitals": "^2.1.4" 25 | }, 26 | "scripts": { 27 | "start": "react-scripts start", 28 | "build": "react-scripts build", 29 | "test": "react-scripts test", 30 | "eject": "react-scripts eject", 31 | "seed": "node seed" 32 | }, 33 | "eslintConfig": { 34 | "extends": [ 35 | "react-app", 36 | "react-app/jest" 37 | ] 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | }, 51 | "proxy": "http://localhost:3001" 52 | } 53 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const path = require('path') 3 | const favicon = require('serve-favicon') 4 | const logger = require('morgan') 5 | require('dotenv').config() 6 | require('./config/database') 7 | 8 | const app = express() 9 | const PORT = process.env.PORT || 3001 10 | 11 | 12 | //middleware 13 | app.use(logger('dev')) 14 | app.use(express.json()) 15 | //check for a token and create an req.user prop in the request 16 | app.use(require('./config/checkToken')) 17 | 18 | // Configure both serve-favicon & static middleware 19 | // to serve from the production 'build' folder 20 | app.use(favicon(path.join(__dirname, 'build', 'favicon.ico'))); 21 | app.use(express.static(path.join(__dirname, 'build'))); 22 | 23 | 24 | 25 | // Put API routes here, before the "catch all" route 26 | 27 | app.use('/api/users', require('./routes/api/users')) 28 | 29 | // Protect the API routes below from anonymous users 30 | const ensureLoggedIn = require('./config/ensureLoggedIn'); 31 | app.use('/api/items', ensureLoggedIn, require('./routes/api/items')); 32 | app.use('/api/orders', ensureLoggedIn, require('./routes/api/orders')); 33 | 34 | 35 | // The following "catch all" route (note the *) is necessary 36 | // to return the index.html on all non-AJAX requests 37 | app.get('/*', function(req, res) { 38 | res.sendFile(path.join(__dirname, 'build', 'index.html')); 39 | }); 40 | 41 | 42 | app.listen(PORT, () => { 43 | console.log(`Express app running on port ${PORT}`) 44 | }); -------------------------------------------------------------------------------- /src/utilities/users-service.js: -------------------------------------------------------------------------------- 1 | import * as usersApi from './users-api' 2 | 3 | export async function signUp(userData){ 4 | 5 | //calling the user-api signUp function 6 | const token = await usersApi.signUp(userData) 7 | localStorage.setItem('Token', token) 8 | return getUser(); 9 | } 10 | 11 | export async function login(credentials){ 12 | const token = await usersApi.login(credentials) 13 | localStorage.setItem('Token', token) 14 | return getUser(); 15 | } 16 | 17 | export async function logOut(){ 18 | localStorage.removeItem('Token') 19 | } 20 | 21 | 22 | export async function checkToken(){ 23 | const dateStr = await usersApi.checkToken() 24 | return new Date(dateStr) 25 | } 26 | 27 | export function getToken() { 28 | // getItem returns null if there's no string 29 | const token = localStorage.getItem('Token'); 30 | if (!token) return null; 31 | // Obtain the payload of the token 32 | const payload = JSON.parse(atob(token.split('.')[1])); 33 | // A JWT's exp is expressed in seconds, not milliseconds, so convert 34 | if (payload.exp < Date.now() / 1000) { 35 | // Token has expired - remove it from localStorage 36 | localStorage.removeItem('Token'); 37 | return null; 38 | } 39 | return token; 40 | } 41 | 42 | export function getUser() { 43 | const token = getToken(); 44 | // If there's a token, return the user in the payload, otherwise return null 45 | return token ? JSON.parse(atob(token.split('.')[1])).user : null; 46 | } -------------------------------------------------------------------------------- /src/pages/AuthPage.js: -------------------------------------------------------------------------------- 1 | import SignUpForm from "../components/SignUpForm/SignUpForm"; 2 | import LoginForm from "../components/LogInForm/LoginForm"; 3 | import { useState } from "react"; 4 | import styles from "./AuthPage.module.css"; 5 | // import Logo from '../components/Logo/Logo.js'; 6 | 7 | import FormGroup from "@mui/material/FormGroup"; 8 | import FormControlLabel from "@mui/material/FormControlLabel"; 9 | import Switch from "@mui/material/Switch"; 10 | import Stack from '@mui/material/Stack'; 11 | 12 | function AuthPage({ setUser }) { 13 | const [showLogin, setShowLogin] = useState(true); 14 | 15 | function handleChange(){ 16 | setShowLogin(!showLogin) 17 | } 18 | return ( 19 |
    20 |
    21 | {/*

    setShowLogin(!showLogin)}> 22 | {" "} 23 | {showLogin ? "Sign Up" : "Log In"} 24 |

    */} 25 |

    {showLogin ? "Login Page" : "Sign Up"}

    26 | 27 | {showLogin ? ( 28 | 29 | ) : ( 30 | 31 | )} 32 | 33 | 34 |

    Sign Up

    35 | } /> 36 |

    Log In

    37 |
    38 |
    39 |
    40 |
    41 | ); 42 | } 43 | 44 | export default AuthPage; 45 | -------------------------------------------------------------------------------- /src/pages/OrderHistoryPage.js: -------------------------------------------------------------------------------- 1 | import styles from './OrderHistoryPage.module.css'; 2 | import { useState, useEffect } from 'react'; 3 | import { Link } from 'react-router-dom'; 4 | import * as ordersAPI from '../utilities/order-api'; 5 | // import Logo from '../components/Logo/Logo'; 6 | import UserLogOut from '../components/UserLogOut/UserLogOut'; 7 | import OrderList from '../components/OrderList/OrderList'; 8 | import OrderDetail from '../components/OrderDetail/OrderDetail'; 9 | 10 | export default function OrderHistoryPage({ user, setUser }) { 11 | /*--- State --- */ 12 | const [orders, setOrders] = useState([]); 13 | const [activeOrder, setActiveOrder] = useState(null); 14 | 15 | /*--- Side Effects --- */ 16 | useEffect(function () { 17 | // Load previous orders (paid) 18 | async function fetchOrderHistory() { 19 | const orders = await ordersAPI.getOrderHistory(); 20 | setOrders(orders); 21 | // If no orders, activeOrder will be set to null below 22 | setActiveOrder(orders[0] || null); 23 | } 24 | fetchOrderHistory(); 25 | }, []); 26 | 27 | /*--- Event Handlers --- */ 28 | function handleSelectOrder(order) { 29 | setActiveOrder(order); 30 | } 31 | 32 | /*--- Rendered UI --- */ 33 | return ( 34 |
    35 | 40 | 45 | 48 |
    49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /src/utilities/users-api.js: -------------------------------------------------------------------------------- 1 | // import { getToken } from "./users-service"; 2 | import sendRequest from "./send-request" 3 | const BASE_URL = '/api/users' 4 | 5 | export function signUp(userData) { 6 | return sendRequest(BASE_URL, 'POST', userData); 7 | } 8 | 9 | export function login(credentials) { 10 | return sendRequest(`${BASE_URL}/login`, 'POST', credentials); 11 | } 12 | 13 | export function updateInfo(userData){ 14 | return sendRequest(`${BASE_URL}/update`, 'PUT', userData) 15 | } 16 | 17 | export function deleteUser(){ 18 | return sendRequest(`${BASE_URL}/delete`, 'DELETE') 19 | } 20 | 21 | export function checkToken(){ 22 | return sendRequest(`${BASE_URL}/check-token`) 23 | } 24 | 25 | // /*--- Helper Functions ---*/ 26 | 27 | // async function sendRequest(url, method = 'GET', payload = null) { 28 | // // Fetch accepts an options object as the 2nd argument 29 | // // used to include a data payload, set headers, etc. 30 | // const options = { method }; 31 | // if (payload) { 32 | // options.headers = { 'Content-Type': 'application/json' }; 33 | // options.body = JSON.stringify(payload); 34 | // } 35 | // const token = getToken(); 36 | // if (token) { 37 | // // Ensure the headers object exists 38 | // options.headers = options.headers || {}; 39 | // // Add token to an Authorization header 40 | // // Prefacing with 'Bearer' is recommended in the HTTP specification 41 | // options.headers.Authorization = `Bearer ${token}`; 42 | // } 43 | 44 | // const res = await fetch(url, options); 45 | // // res.ok will be false if the status code set to 4xx in the controller action 46 | // if (res.ok) return res.json(); 47 | // throw new Error('Bad Request'); 48 | // } -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | import * as usersService from "../../utilities/users-service"; 3 | // import Logo from "../Logo/Logo.js" 4 | import styles from "./NavBar.module.css"; 5 | import YourBizHere from "../../assets/YourBizHere.gif"; 6 | import { useState } from "react"; 7 | 8 | function NavBar({ user, setUser }) { 9 | const [active, setActive] = useState(""); 10 | 11 | const handleLogOut = () => { 12 | usersService.logOut(); 13 | setUser(null); 14 | }; 15 | 16 | const handleClickNav = (e) => { 17 | 18 | setActive(e.target.text); 19 | }; 20 | return ( 21 | 58 | ); 59 | } 60 | 61 | export default NavBar; 62 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/components/OrderDetail/OrderDetail.js: -------------------------------------------------------------------------------- 1 | import styles from './OrderDetail.module.css'; 2 | import LineItem from '../LineItem/LineItem'; 3 | 4 | // Used to display the details of any order, including the cart (unpaid order) 5 | export default function OrderDetail({ order, handleChangeQty, handleCheckout }) { 6 | if (!order) return null; 7 | 8 | const lineItems = order.lineItems.map(item => 9 | 15 | ); 16 | 17 | return ( 18 |
    19 |
    20 | {order.isPaid ? 21 | ORDER {order.orderId} 22 | : 23 | NEW ORDER 24 | } 25 | {new Date(order.updatedAt).toLocaleDateString()} 26 |
    27 |
    28 | {lineItems.length ? 29 | <> 30 | {lineItems} 31 |
    32 | {order.isPaid ? 33 | TOTAL   34 | : 35 | 40 | } 41 | {order.totalQty} 42 | ${order.orderTotal.toFixed(2)} 43 |
    44 | 45 | : 46 |
    Hungry?
    47 | } 48 |
    49 |
    50 | ); 51 | } -------------------------------------------------------------------------------- /src/components/LogInForm/LoginForm.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import * as usersService from '../../utilities/users-service'; 3 | import YourBizHere from "../../assets/YourBizHere.gif" 4 | import './LoginForm.module.css' 5 | 6 | export default function LoginForm({ setUser }) { 7 | const [credentials, setCredentials] = useState({ 8 | email: '', 9 | password: '' 10 | }); 11 | const [error, setError] = useState(''); 12 | 13 | function handleChange(evt) { 14 | setCredentials({ ...credentials, [evt.target.name]: evt.target.value }); 15 | setError(''); 16 | } 17 | 18 | async function handleSubmit(evt) { 19 | // Prevent form from being submitted to the server 20 | evt.preventDefault(); 21 | try { 22 | // The promise returned by the signUp service method 23 | // will resolve to the user object included in the 24 | // payload of the JSON Web Token (JWT) 25 | const user = await usersService.login(credentials); 26 | setUser(user); 27 | } catch { 28 | setError('Log In Failed - Try Again'); 29 | } 30 | } 31 | 32 | function adminLogin(){ 33 | setCredentials({email: "admin@gmail.com", password:"admin123"}) 34 | } 35 | 36 | 37 | return ( 38 |
    39 |

     {error}

    40 |
    41 | logo 42 |
    43 | 44 | 45 | 46 | 47 | 48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | ); 55 | } -------------------------------------------------------------------------------- /controllers/api/orders.js: -------------------------------------------------------------------------------- 1 | const Order = require('../../models/order'); 2 | // const Item = require('../../models/item'); 3 | 4 | module.exports = { 5 | cart, 6 | addToCart, 7 | setItemQtyInCart, 8 | checkout, 9 | history, 10 | getItems 11 | }; 12 | 13 | // A cart is the unpaid order for a user 14 | async function cart(req, res) { 15 | try{ 16 | const cart = await Order.getCart(req.user._id); 17 | res.status(200).json(cart); 18 | }catch(e){ 19 | res.status(400).json({ msg: e.message }); 20 | } 21 | } 22 | 23 | // Add an item to the cart 24 | async function addToCart(req, res) { 25 | try{ 26 | const cart = await Order.getCart(req.user._id); 27 | await cart.addItemToCart(req.params.id); 28 | res.status(200).json(cart); 29 | }catch(e){ 30 | res.status(400).json({ msg: e.message }); 31 | } 32 | } 33 | 34 | // Updates an item's qty in the cart 35 | async function setItemQtyInCart(req, res) { 36 | try{ 37 | const cart = await Order.getCart(req.user._id); 38 | await cart.setItemQty(req.body.itemId, req.body.newQty); 39 | res.status(200).json(cart); 40 | }catch(e){ 41 | res.status(400).json({ msg: e.message }); 42 | } 43 | } 44 | 45 | // Update the cart's isPaid property to true 46 | async function checkout(req, res) { 47 | try{ 48 | const cart = await Order.getCart(req.user._id); 49 | cart.isPaid = true; 50 | await cart.save(); 51 | res.status(200).json(cart); 52 | }catch(e){ 53 | res.status(400).json({ msg: e.message }); 54 | } 55 | } 56 | 57 | // Return the logged in user's paid order history 58 | async function history(req, res) { 59 | // Sort most recent orders first 60 | try{ 61 | const orders = await Order 62 | .find({ user: req.user._id, isPaid: true }) 63 | .sort('-updatedAt').exec(); 64 | res.status(200).json(orders); 65 | }catch(e){ 66 | res.status(400).json({ msg: e.message }); 67 | } 68 | 69 | } 70 | 71 | //get items 72 | async function getItems(req, res){ 73 | try{ 74 | const orders = await Order 75 | .find({isPaid: true}) 76 | .sort('-updatedAt').exec(); 77 | 78 | res.status(200).json(orders); 79 | }catch(e){ 80 | res.status(400).json({ msg: e.message }); 81 | } 82 | } -------------------------------------------------------------------------------- /src/components/SignUpForm/SignUpForm.js: -------------------------------------------------------------------------------- 1 | import {useState} from 'react' 2 | import {signUp} from '../../utilities/users-service' 3 | 4 | 5 | 6 | // SignUpForm.jsx <--> users-service.js <--> users-api.js 7 | // <-Internet-> routes/users.js <--> controller/users.js 8 | function SignUpForm({setUser}){ 9 | const [formData, setFormData] = useState({ 10 | name: "", 11 | email: "", 12 | password:"", 13 | confirm: "", 14 | error: "" 15 | 16 | }) 17 | 18 | const disable = formData.password !== formData.confirm 19 | const handleChange = (e) => { 20 | setFormData({...formData, [e.target.name]: e.target.value, error: ''}) 21 | } 22 | 23 | const handleSubmit = async(e) => { 24 | e.preventDefault() 25 | try{ 26 | console.log(formData) 27 | //copy the formData 28 | const userFormData = {...formData} 29 | //delete the extra properties 30 | delete userFormData.confirm 31 | delete userFormData.error 32 | 33 | //calling user service signUp function 34 | const user = await signUp(userFormData) 35 | // console.log('USER', user) 36 | setUser(user) 37 | }catch(error){ 38 | console.log(error) 39 | setFormData({ 40 | ...formData, 41 | error: "Sign Up Failed - Try Again" 42 | }) 43 | } 44 | } 45 | 46 | return( 47 |
    48 |

    {formData.error}

    49 |
    50 |
    51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 67 |
    68 | ) 69 | } 70 | 71 | export default SignUpForm; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Live Demo: 2 | https://yourbizhere.onrender.com 3 | 4 | ![Alt text](YourBizHere.gif) 5 | 6 | # About 7 | Template E-commerce website for business (restaurants or retail) with administrative capabilities for statistical & logistical analysis of orders and items. 8 | 9 | # Approach 10 | Building on top of an E-commerce market application. Users are able to Create, Update, Delete (CRUD) their accounts and make orders of items on the menu. If user has admin privilege they are able to access a dashboard (C[R]UD) graphing the orders of all users and count of item quantity. Along with some revenue and item count. 11 | 12 | ## Technology Used 13 | - JavaScript 14 | - React 15 | - CSS 16 | - MaterialUI 17 | - MongoDB 18 | - Express 19 | - Node.js 20 | - Mongoose 21 | - Recharts 22 | - JWT (JSON Web Token: Authorization and Authentication) 23 | - Bcrypt (Password encrpytion, hashing and salting) 24 | 25 | ## Flow Diagram of CRUD / FULL Stack 26 | ![Alt text](CRUD-flow-diagram.png) 27 | 28 | ## Wireframes 29 | ![Alt text](login-signup.png) 30 | ![Alt text](MainPage.png) 31 | ![Alt text](Admindash.png) 32 | 33 | ## Entity Relationship Diagram (ERD) 34 | ![Alt text](ERD.png) 35 | 36 | # Commands/Dependencies 37 | 38 | ## Front 39 | `npm start` 40 | 41 | ## Back 42 | `nodemon server` 43 | 44 | ## Changes run: 45 | `npm build` 46 | 47 | ## Seed 48 | `npm run seed` 49 | 50 | ## Secret key 51 | `openssl rand -hex 32` 52 | 53 | ## Recharts 54 | `npm install recharts` 55 | 56 | ## MaterialUI 57 | `npm install @mui/material @emotion/react @emotion/styled` 58 | 59 | ## Express 60 | `npm i express` 61 | 62 | ## ENV (hiding mongoDB key) 63 | `npm i dotenv` 64 | 65 | ## Mongoose 66 | `npm i mongoose` 67 | 68 | ## React & React-DOM 69 | `npm i react react-dom` 70 | 71 | ## Bcrypt 72 | `npm i bcrypt` 73 | 74 | ## JWT (JsonWebToken) 75 | `npm install jsonwebtoken` 76 | 77 | ## Morgan (logger middleware) 78 | `npm i morgan` 79 | 80 | ## serve-favicon (middleware) 81 | `npm install serve-favicon` 82 | 83 | # Improvements 84 | - Styling 85 | - Making acutal item cards with food / real items examples for menu 86 | - Incorporate time to graphs to see busy hours 87 | - Add financial payment processing (Stripe?) 88 | - Add more statistical and logical analysis for admin access 89 | - Add more capabilities (admin access) to delete users and orders 90 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /controllers/api/users.js: -------------------------------------------------------------------------------- 1 | const User = require("../../models/user"); 2 | const jwt = require("jsonwebtoken"); 3 | const bcrypt = require("bcrypt"); 4 | 5 | async function create(req, res) { 6 | try { 7 | // Add the user to the database 8 | const user = await User.create(req.body); 9 | 10 | //create a new token 11 | const token = createJWT(user); 12 | 13 | res.json(token); 14 | } catch (error) { 15 | console.log(error); 16 | res.status(400).json(error); 17 | } 18 | } 19 | 20 | async function update(req, res){ 21 | // const {id} = req.params 22 | // console.log(req.body) 23 | try{ 24 | console.log(req.user) 25 | const dbUser = await User.findById(req.user._id) 26 | console.log('OLD password',dbUser) 27 | //matching old password with password in db 28 | const match = await bcrypt.compare(req.body.password, dbUser.password) 29 | 30 | if(!match){ 31 | console.log('USER', req.body) 32 | res.status(400).json("Password Error") 33 | return 34 | }else{ 35 | req.body.password = await bcrypt.hash(req.body.newpassword, 6) 36 | delete req.body.newpassword 37 | console.log('USER', req.body) 38 | } 39 | const user = await User.findByIdAndUpdate(req.user._id, req.body, {new: true}) 40 | 41 | res.json(user) 42 | 43 | }catch(error){ 44 | console.log(error) 45 | } 46 | } 47 | async function deleteUser(req, res){ 48 | 49 | try{ 50 | 51 | const user = await User.findByIdAndDelete(req.user._id) 52 | 53 | 54 | res.json('user deleted') 55 | }catch(e){ 56 | console.log(e) 57 | } 58 | } 59 | 60 | 61 | async function login(req, res) { 62 | try { 63 | // find a user by email 64 | const user = await User.findOne({ email: req.body.email }); 65 | if (!user) throw new Error(); 66 | 67 | // comparing password 68 | const match = await bcrypt.compare(req.body.password, user.password); 69 | if (!match) throw new Error(); 70 | 71 | // create new token 72 | const token = createJWT(user); 73 | res.json(token); 74 | 75 | } catch (error) { 76 | console.log(error); 77 | res.status(400).json(error); 78 | } 79 | } 80 | 81 | async function checkToken(req, res){ 82 | //req.user will always be there for you when a token is sent 83 | console.log('req.user', req.user); 84 | res.json(req.exp) 85 | } 86 | 87 | //helper function to create a jwt token 88 | function createJWT(user) { 89 | return jwt.sign({ user }, process.env.SECRET, { expiresIn: "24h" }); 90 | } 91 | 92 | module.exports = { 93 | create, 94 | login, 95 | checkToken, 96 | update, 97 | deleteUser, 98 | }; 99 | -------------------------------------------------------------------------------- /models/order.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | const itemSchema = require('./itemSchema'); 4 | 5 | const lineItemSchema = new Schema({ 6 | qty: { type: Number, default: 1 }, 7 | item: itemSchema 8 | }, { 9 | timestamps: true, 10 | toJSON: { virtuals: true } 11 | }); 12 | 13 | lineItemSchema.virtual('extPrice').get(function() { 14 | // 'this' is bound to the lineItem subdoc 15 | return this.qty * this.item.price; 16 | }); 17 | 18 | const orderSchema = new Schema({ 19 | user: { type: Schema.Types.ObjectId, ref: 'User' }, 20 | lineItems: [lineItemSchema], 21 | isPaid: { type: Boolean, default: false } 22 | }, { 23 | timestamps: true, 24 | toJSON: { virtuals: true } 25 | }); 26 | 27 | orderSchema.virtual('orderTotal').get(function() { 28 | return this.lineItems.reduce((total, item) => total + item.extPrice, 0); 29 | }); 30 | 31 | orderSchema.virtual('totalQty').get(function() { 32 | return this.lineItems.reduce((total, item) => total + item.qty, 0); 33 | }); 34 | 35 | orderSchema.virtual('orderId').get(function() { 36 | return this.id.slice(-6).toUpperCase(); 37 | }); 38 | 39 | orderSchema.statics.getCart = function(userId) { 40 | // 'this' is the Order model 41 | return this.findOneAndUpdate( 42 | // query 43 | { user: userId, isPaid: false }, 44 | // update 45 | { user: userId }, 46 | // upsert option will create the doc if 47 | // it doesn't exist 48 | { upsert: true, new: true } 49 | ); 50 | }; 51 | 52 | orderSchema.methods.addItemToCart = async function(itemId) { 53 | const cart = this; 54 | // Check if item already in cart 55 | const lineItem = cart.lineItems.find(lineItem => lineItem.item._id.equals(itemId)); 56 | if (lineItem) { 57 | lineItem.qty += 1; 58 | } else { 59 | const item = await mongoose.model('Item').findById(itemId); 60 | cart.lineItems.push({ item }); 61 | } 62 | return cart.save(); 63 | }; 64 | 65 | // Instance method to set an item's qty in the cart (will add item if does not exist) 66 | orderSchema.methods.setItemQty = function(itemId, newQty) { 67 | // this keyword is bound to the cart (order doc) 68 | const cart = this; 69 | // Find the line item in the cart for the menu item 70 | const lineItem = cart.lineItems.find(lineItem => lineItem.item._id.equals(itemId)); 71 | if (lineItem && newQty <= 0) { 72 | // Calling deleteOne, removes itself from the cart.lineItems array 73 | lineItem.deleteOne(); 74 | } else if (lineItem) { 75 | // Set the new qty - positive value is assured thanks to prev if 76 | lineItem.qty = newQty; 77 | } 78 | // return the save() method's promise 79 | return cart.save(); 80 | }; 81 | 82 | module.exports = mongoose.model('Order', orderSchema); -------------------------------------------------------------------------------- /src/pages/NewOrderPage.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useRef } from 'react'; 2 | import * as itemsAPI from '../utilities/items-api'; 3 | import * as ordersAPI from '../utilities/order-api'; 4 | import styles from './NewOrderPage.module.css'; 5 | import { Link, useNavigate } from 'react-router-dom'; 6 | // import Logo from '../components/Logo/Logo'; 7 | import MenuList from '../components/MenuList/MenuList'; 8 | import CategoryList from '../components/CategoryList/CategoryList'; 9 | import OrderDetail from '../components/OrderDetail/OrderDetail'; 10 | import UserLogOut from '../components/UserLogOut/UserLogOut'; 11 | 12 | export default function NewOrderPage({ user, setUser }) { 13 | const [menuItems, setMenuItems] = useState([]); 14 | const [activeCat, setActiveCat] = useState(''); 15 | const [cart, setCart] = useState(null); 16 | const categoriesRef = useRef([]); 17 | const navigate = useNavigate(); 18 | 19 | useEffect(function() { 20 | async function getItems() { 21 | const items = await itemsAPI.getAll(); 22 | categoriesRef.current = items.reduce((cats, item) => { 23 | const cat = item.category.name; 24 | return cats.includes(cat) ? cats : [...cats, cat]; 25 | }, []); 26 | setMenuItems(items); 27 | setActiveCat(categoriesRef.current[0]); 28 | } 29 | getItems(); 30 | async function getCart() { 31 | const cart = await ordersAPI.getCart(); 32 | setCart(cart); 33 | } 34 | getCart(); 35 | }, []); 36 | // Providing an empty 'dependency array' 37 | // results in the effect running after 38 | // the FIRST render only 39 | 40 | /*-- Event Handlers --*/ 41 | async function handleAddToOrder(itemId) { 42 | const updatedCart = await ordersAPI.addItemToCart(itemId); 43 | setCart(updatedCart); 44 | } 45 | 46 | async function handleChangeQty(itemId, newQty) { 47 | const updatedCart = await ordersAPI.setItemQtyInCart(itemId, newQty); 48 | setCart(updatedCart); 49 | } 50 | 51 | async function handleCheckout() { 52 | await ordersAPI.checkout(); 53 | navigate('/orders'); 54 | } 55 | 56 | return ( 57 |
    58 | 68 | item.category.name === activeCat)} 70 | handleAddToOrder={handleAddToOrder} 71 | /> 72 | 77 |
    78 | ); 79 | } -------------------------------------------------------------------------------- /src/pages/Settings.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { updateInfo, deleteUser } from "../utilities/users-api"; 3 | 4 | function Settings({ user, setUser }) { 5 | const [formData, setFormData] = useState({ 6 | name: user.name, 7 | email: user.email, 8 | password: "", 9 | newpassword: "", 10 | confirm: "", 11 | error: "", 12 | }); 13 | 14 | const disable = formData.newpassword !== formData.confirm; 15 | const handleChange = (e) => { 16 | setFormData({ ...formData, [e.target.name]: e.target.value, error: "" }); 17 | }; 18 | 19 | const handleSubmit = async (e) => { 20 | e.preventDefault(); 21 | try { 22 | console.log(formData); 23 | //copy the formData 24 | const userFormData = { ...formData }; 25 | //delete the extra properties 26 | delete userFormData.confirm; 27 | delete userFormData.error; 28 | 29 | //calling user service update function 30 | const user = await updateInfo(userFormData); 31 | console.log("USER", user); 32 | setUser(null); 33 | } catch (error) { 34 | console.log(error); 35 | setFormData({ 36 | ...formData, 37 | error: "Update Failed - Try Again", 38 | }); 39 | } 40 | }; 41 | 42 | const handleClick = () => { 43 | deleteUser() 44 | setUser(null) 45 | localStorage.removeItem('Token') 46 | } 47 | return ( 48 |
    49 |
    50 | 51 | 59 | 60 | 61 | 69 | 70 | 71 | 78 | 79 | 80 | 87 | 88 | 89 | 96 | 97 | 100 |
    101 | 102 |
    103 | ); 104 | } 105 | 106 | export default Settings; 107 | -------------------------------------------------------------------------------- /src/components/BarGraph/BarGraph.js: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | BarChart, 4 | Bar, 5 | XAxis, 6 | YAxis, 7 | CartesianGrid, 8 | Tooltip, 9 | Legend 10 | } from "recharts"; 11 | import * as ordersAPI from '../../utilities/order-api'; 12 | import React, { useState, useEffect } from 'react'; 13 | import './BarGraph.css' 14 | 15 | function BarGraph(){ 16 | const [itemCount, setItemCount]= useState([]) 17 | useEffect(function () { 18 | // Load previous orders (paid) 19 | async function fetchItemOrders() { 20 | const orders = await ordersAPI.getItemHistory(); 21 | setItemCount(orders) 22 | // console.log(orders) 23 | } 24 | fetchItemOrders(); 25 | }, []); 26 | 27 | 28 | const chartData = {} 29 | itemCount.forEach((order) =>{ 30 | order.lineItems.forEach((item)=> { 31 | 32 | chartData[item.item.name] ? chartData[item.item.name] += item.qty : chartData[item.item.name] = item.qty 33 | 34 | }) 35 | }) 36 | 37 | // console.log(Object.keys(chartData)) 38 | // console.log(Object.values(chartData)) 39 | const data = []; 40 | const name = Object.keys(chartData) 41 | // const value = Object.values(chartData) 42 | name.forEach((element) => { 43 | const dataValue = {} 44 | dataValue['name'] = element 45 | dataValue['value'] = chartData[element] 46 | data.push(dataValue) 47 | }) 48 | 49 | // console.log(data) 50 | 51 | let totalRevenue = 0; 52 | let totalItemCount =0; 53 | itemCount.forEach((order) =>{ 54 | 55 | totalRevenue += order.orderTotal 56 | totalItemCount += order.totalQty 57 | 58 | }) 59 | // console.log(totalRevenue) 60 | // console.log(totalItemCount) 61 | return ( 62 |
    63 | 64 | 76 | 77 | 78 | 79 | 80 | {/* */} 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
    Total Revenue# of Items Sold
    ${totalRevenue.toFixed(2)}{totalItemCount}
    98 | {/* 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | {data.map((key) => { 107 | return( 108 | 109 | 110 | 111 | ) 112 | })} 113 | 114 | 115 |
    ItemsAmount Sold
    {key}
    */} 116 |
    117 | ); 118 | } 119 | 120 | export default BarGraph; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | /* CSS Custom Properties */ 16 | :root { 17 | --white: #FFFFFF; 18 | --tan-1: #f6fbfb; 19 | --tan-2: #dde3e7; 20 | --tan-3: #d1dee2; 21 | --tan-4: #aed1d3; 22 | --blue: #007Ff7; 23 | --text-light: #968c84; 24 | --text-dark: #615954; 25 | } 26 | 27 | *, *:before, *:after { 28 | box-sizing: border-box; 29 | } 30 | 31 | body { 32 | margin: 0; 33 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 34 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 35 | sans-serif; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-osx-font-smoothing: grayscale; 38 | background-color: var(--tan-4); 39 | padding: 2vmin; 40 | height: 100vh; 41 | } 42 | 43 | code { 44 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 45 | monospace; 46 | } 47 | 48 | #root { 49 | height: 100%; 50 | } 51 | 52 | .align-ctr { 53 | text-align: center; 54 | } 55 | 56 | .align-rt { 57 | text-align: right; 58 | } 59 | 60 | .smaller { 61 | font-size: smaller; 62 | } 63 | 64 | .flex-ctr-ctr { 65 | display: flex; 66 | justify-content: center; 67 | align-items: center; 68 | } 69 | 70 | .flex-col { 71 | flex-direction: column; 72 | } 73 | 74 | .flex-j-end { 75 | justify-content: flex-end; 76 | } 77 | 78 | .scroll-y { 79 | overflow-y: scroll; 80 | } 81 | 82 | .section-heading { 83 | display: flex; 84 | justify-content: space-around; 85 | align-items: center; 86 | background-color: var(--tan-1); 87 | color: var(--text-dark); 88 | border: .1vmin solid var(--tan-3); 89 | border-radius: 1vmin; 90 | padding: .6vmin; 91 | text-align: center; 92 | font-size: 2vmin; 93 | } 94 | 95 | .form-container { 96 | padding: 3vmin; 97 | background-color: var(--tan-1); 98 | border: .1vmin solid var(--tan-3); 99 | border-radius: 1vmin; 100 | } 101 | 102 | p.error-message { 103 | color: var(--blue); 104 | text-align: center; 105 | } 106 | 107 | form { 108 | display: grid; 109 | grid-template-columns: 1fr 3fr; 110 | gap: 1.25vmin; 111 | color: var(--text-light); 112 | } 113 | 114 | label { 115 | font-size: 2vmin; 116 | display: flex; 117 | align-items: center; 118 | } 119 | 120 | input { 121 | padding: 1vmin; 122 | font-size: 2vmin; 123 | border: .1vmin solid var(--tan-3); 124 | border-radius: .5vmin; 125 | color: var(--text-dark); 126 | background-image: none !important; /* prevent lastpass */ 127 | outline: none; 128 | } 129 | 130 | input:focus { 131 | border-color: var(--blue); 132 | } 133 | 134 | button, a.button { 135 | margin: 1vmin; 136 | padding: 1vmin; 137 | color: var(--white); 138 | background-color: var(--blue); 139 | font-size: 2vmin; 140 | font-weight: bold; 141 | text-decoration: none; 142 | text-align: center; 143 | border: .1vmin solid var(--tan-2); 144 | border-radius: .5vmin; 145 | outline: none; 146 | cursor: pointer; 147 | } 148 | 149 | button.btn-sm { 150 | font-size: 1.5vmin; 151 | padding: .6vmin .8vmin; 152 | } 153 | 154 | button.btn-xs { 155 | font-size: 1vmin; 156 | padding: .4vmin .5vmin; 157 | } 158 | 159 | button:disabled, form:invalid button[type="submit"] { 160 | cursor: not-allowed; 161 | background-color: var(--tan-4); 162 | } 163 | 164 | button[type="submit"] { 165 | grid-column: span 2; 166 | margin: 1vmin 0 0; 167 | } -------------------------------------------------------------------------------- /seed.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | require('./config/database'); 3 | const jwt = require("jsonwebtoken"); 4 | 5 | const Category = require('./models/category'); 6 | const Item = require('./models/item'); 7 | const User = require('./models/user'); 8 | 9 | (async function() { 10 | 11 | await Category.deleteMany({}); 12 | const categories = await Category.create([ 13 | {name: 'Breakfast', sortOrder:10}, 14 | {name: 'Desserts', sortOrder: 20}, 15 | {name: 'Diner', sortOrder:30}, 16 | {name: 'Drinks', sortOrder: 40}, 17 | {name: 'Italian', sortOrder: 50}, 18 | {name: 'Mexican', sortOrder: 60}, 19 | {name: 'Sandwiches', sortOrder: 70}, 20 | {name: 'Seafood', sortOrder: 80}, 21 | {name: 'Sides', sortOrder: 90}, 22 | {name: 'Memes', sortOrder:100} 23 | ]); 24 | 25 | await Item.deleteMany({}); 26 | const items = await Item.create([ 27 | {name: 'Hamburger', emoji: '🍔', category: categories[2], price: 5.95}, 28 | {name: 'Noodles', emoji: '🍜', category: categories[2], price: 11.95}, 29 | {name: 'Fried Rice', emoji: '🍘', category: categories[2], price: 9.95}, 30 | {name: 'Jollof Rice', emoji: '🍛', category: categories[2], price: 9.95}, 31 | {name: 'Veggy Brochette', emoji: '🍢', category: categories[8], price: 3.95}, 32 | {name: 'Sushi', emoji: '🍣', category: categories[2], price: 5.95}, 33 | {name: 'Beef', emoji: '🍖', category: categories[2], price: 9.95}, 34 | {name: 'Croissant', emoji: '🥐', category: categories[0], price: 5.95}, 35 | {name: 'Fried Egg', emoji: '🍳', category: categories[0], price: 5.95}, 36 | {name: 'Doughnut', emoji: '🍩', category: categories[0], price: 5.95}, 37 | {name: 'Turkey Sandwich', emoji: '🥪', category: categories[0], price: 6.95}, 38 | {name: 'Hot Dog', emoji: '🌭', category: categories[2], price: 3.95}, 39 | {name: 'Crab Plate', emoji: '🦀', category: categories[7], price: 14.95}, 40 | {name: 'Soft drink', emoji:'🥤', category: categories[3], price: 2.95}, 41 | {name: 'Fried Shrimp', emoji: '🍤', category: categories[7], price: 13.95}, 42 | {name: 'Whole Lobster', emoji: '🦞', category: categories[7], price: 25.95}, 43 | {name: 'Taco', emoji: '🌮', category: categories[5], price: 1.95}, 44 | {name: 'Burrito', emoji: '🌯', category: categories[5], price: 4.95}, 45 | {name: 'Pizza Slice', emoji: '🍕', category: categories[4], price: 3.95}, 46 | {name: 'Spaghetti', emoji: '🍝', category: categories[4], price: 7.95}, 47 | {name: 'Garlic Bread', emoji: '🍞', category: categories[4], price: 1.95}, 48 | {name: 'French Fries', emoji: '🍟', category: categories[8], price: 2.95}, 49 | {name: 'Popcorn', emoji: '🍿', category: categories[8], price: 2.95}, 50 | {name: 'French Fries', emoji: '🥨', category: categories[2], price: 2.95}, 51 | {name: 'Sweet Potato', emoji: '🍠', category: categories[8], price: 2.95}, 52 | {name: 'Green Salad', emoji: '🥗', category: categories[4], price: 3.95}, 53 | {name: 'Ice Cream', emoji: '🍨', category: categories[1], price: 1.95}, 54 | {name: 'Cup Cake', emoji: '🧁', category: categories[1], price: 0.95}, 55 | {name: 'Custard', emoji: '🍮', category: categories[1], price: 2.95}, 56 | {name: 'Strawberry Shortcake', emoji: '🍰', category: categories[1], price: 3.95}, 57 | {name: 'Stuffed Flatbread', emoji: '🥙', category: categories[5], price: 9.95}, 58 | {name: 'Milk', emoji: '🥛', category: categories[3], price: 0.95}, 59 | {name: 'Coffee', emoji: '☕', category: categories[3], price: 0.95}, 60 | {name: 'Mai Tai', emoji: '🍹', category: categories[3], price: 8.95}, 61 | {name: 'Beer', emoji: '🍺', category: categories[3], price: 3.95}, 62 | {name: 'Wine', emoji: '🍷', category: categories[3], price: 7.95}, 63 | {name: 'Fried Chicken', emoji: '🍗', category: categories[2], price: 9.95}, 64 | {name: 'Pancakes', emoji: '🥞', category: categories[0], price: 7.95}, 65 | {name: 'Bacon', emoji: '🥓', category: categories[0], price: 3.95}, 66 | {name: 'Tea', emoji: '🍵', category: categories[3], price: 2.95}, 67 | {name: 'Fishing', emoji: '🎣', category: categories[9], price: 10000}, 68 | {name: 'GigaChad', emoji:'🗿', category: categories[9], price: 1000}, 69 | {name: 'Michael Johnny', emoji:'🏀', category: categories[9], price: 100000}, 70 | {name: 'Built Different', emoji:'💪', category: categories[9], price: 1000}, 71 | {name:'Yeehaw!', emoji:"🤠", category:categories[9], price: 10} 72 | ]); 73 | 74 | await User.findOneAndDelete({email: "admin@gmail.com"}) 75 | 76 | const user = await User.create([ 77 | {name: 'admin', 78 | email: 'admin@gmail.com', 79 | password:'admin123', 80 | isAdmin: true 81 | 82 | } 83 | ]); 84 | 85 | console.log(items) 86 | console.log(user) 87 | process.exit(); 88 | 89 | })(); --------------------------------------------------------------------------------