├── 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 |

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 |

42 |
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 |
65 |
66 |
67 |
68 | )
69 | }
70 |
71 | export default SignUpForm;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Live Demo:
2 | https://yourbizhere.onrender.com
3 |
4 | 
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 | 
27 |
28 | ## Wireframes
29 | 
30 | 
31 | 
32 |
33 | ## Entity Relationship Diagram (ERD)
34 | 
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 |
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 | | Total Revenue |
88 | # of Items Sold |
89 |
90 |
91 |
92 |
93 | | ${totalRevenue.toFixed(2)} |
94 | {totalItemCount} |
95 |
96 |
97 |
98 | {/*
99 |
100 |
101 | | Items |
102 | Amount Sold |
103 |
104 |
105 |
106 | {data.map((key) => {
107 | return(
108 |
109 | | {key} |
110 |
111 | )
112 | })}
113 |
114 |
115 |
*/}
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 | })();
--------------------------------------------------------------------------------