Open In App

Restaurant Reservation System using MERN Stack

Last Updated : 01 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Restaurant reservation system that helps people book tables at restaurants they like. It’s built using React, Node.js, Express, and the MERN stack, along with Sass for styling. Users can search for restaurants based on their preferences, see details like location, description, price range, and contact info, and then pick a date, time, and number of people for their reservation. MongoDB stores all the important information about restaurants, reservations, and users. React makes the system easy to use with its dynamic and interactive features.

Output Preview: Let us have a look at how the final output will look like.

AtSeat-GoogleChrome2024-03-2620-59-12-ezgifcom-video-to-gif-converter

Preview of final output

Prerequisites:

Approach to create Restaurant Reservation System:

The website has the following features:

  • Authenticate & Authorization: Separate admin and customer register and login. Only admins can create restaurant details and view reservations at their restaurant and only customers can make reservations and view reservations made by them.
  • Admin Side: The admin serves the purpose of creating and viewing their restaurant and monitoring the reservations made at their restaurant.
  • Search of Restaurants: The customers are at the front of the business and they can search their favorite restaurants with the help of names or locations.
  • Restaurant Page: On clicking a restaurant, customers can view details like name, location on map, images, rating etc and make reservations.
  • Reservations: Customers can make reservations based on availability of slots on particular days and also add number of people. Once a slot is booked for a particular day, it cannot be booked by anyone again, unless cancelled.
  • Cancel: Customers can cancel reservations made by them.
  • Interactive Maps: The website also houses a map on search page depicting the restaurants and on respective restaurant page a map showing the location. This is an additional feature made to make the website interactive, it can be skipped if not necessary.
  • Security: Helmet is used in the backend to protect from database injections. Bcryptjs is used to encrypt and decrypt user password and then send it to the database.

Steps to Create the Backend of the Project:

Step 1: Start a node.js app by typing the following command:

npm init -y

Step 2: Install the following packages:

npm i bcyptjs body-parser cors dotenv express helmet jsonwebtoken mongoose morgan nodemon

package.json file should be looking like:

"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.2.1",
"morgan": "^1.10.0",
"nodemon": "^3.1.0"
}

Step 3: Create the following folder structure. We are going to use the model-view-controller architecture (MVC).

Project Structure(Backend):

Screenshot-2024-03-21-151052

Project Structure

Example: Below is the code for Backend of Restaurant Reservation System.

JavaScript
// api/index.js

import express from "express";
import dotenv from "dotenv";
import helmet from "helmet";
import morgan from "morgan";
import mongoose from "mongoose";
import cookieParser from "cookie-parser";
import cors from "cors"

import adminRoute from "./routes/admins.js"
import userRoute from "./routes/users.js"
import restRoute from "./routes/rests.js"
import reservRoute from "./routes/reservations.js"

const app = express();
dotenv.config();

const PORT = process.env.PORT || 7700;

const connect = async () => {
    try {
        await mongoose.connect(process.env.MONGO);
        console.log("Connected to mongoDB.");
    } catch (error) {
        throw error;
    }
};

mongoose.connection.on("disconnected", () => {
    console.log("mongoDB disconnected!");
});

app.get('/', (req, res) => { res.send('Hello from Express!') });

//middlewares
app.use(cookieParser())
app.use(express.json());
app.use(helmet());

app.use(cors({
    origin: "http://localhost:3000",
    credentials: true
}))

app.use(morgan("common"));

app.use("/api/admin", adminRoute);
app.use("/api/users", userRoute);
app.use("/api/restaurants", restRoute);
app.use("/api/reservations", reservRoute);

app.listen(PORT, () => {
    console.log("Listening on port 7700");
    connect();
});
JavaScript
// api/error.js

export const createError = (status, message) => {
    const err = new Error();
    err.status = status;
    err.message = message;
    return err;
}
JavaScript
// api/models/Admin.js

import mongoose from "mongoose";

const AdminSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      require: true,
    },
    password: {
      type: String,
      required: true,
      min: 6,
    },
    isAdmin: {
      type: Boolean,
      default: true,
    },
    rest: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Restaurant'
    }
  },
  { timestamps: true }
);



export default mongoose.model("Admin", AdminSchema);
JavaScript
// api/models/Reservation.js

import mongoose from "mongoose";

const ReservationSchema = new mongoose.Schema(
  {
    date: {
        type: Date,
        required: true
    },
    slot: {
        type: String,
        required: true
    },
    people: {
        type: Number,
        required: true
    },
    author: { 
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    rest: { 
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Restaurant'
    }
  },
  { timestamps: true }
);



export default mongoose.model("Reservation", ReservationSchema);
JavaScript
// api/models/Restaurant.js

import mongoose from "mongoose";

const RestaurantSchema = new mongoose.Schema(
  {
    name: {
        type: 'String',
        required: true
    },
    location: {
        type: 'String',
        required: true
    },
    admin: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Admin'
    },
    contact: {
        type: 'String',
        required: true
    }, 
    description: {
        type: 'String',
        required: true
    },
    photo: {
        type: String,
        required: true
    },
    price: {
        type: 'String',
        required: true
    },
    rating: {
        type: Number
    }

  },
  { timestamps: true }
);



export default mongoose.model("Restaurant", RestaurantSchema);
JavaScript
// api/models/User.js

import mongoose from "mongoose";

const UserSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
      unique: true,
    },
    password: {
      type: String,
      required: true,
    },
    isAdmin: {
      type: Boolean,
      default: false,
    },
    reservations: [
      {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Reservation'
      }
  ]
  },
  { timestamps: true }
);



export default mongoose.model("User", UserSchema);
JavaScript
// api/controllers/admin.js

import Admin from "../models/Admin.js";
import bcrypt from "bcryptjs"
import jwt from "jsonwebtoken"
import { createError } from "../error.js"

export const register = async (req, res, next) => {
    try {

        const salt = bcrypt.genSaltSync(10);
        const hash = bcrypt.hashSync(req.body.password, salt);

        const newAdmin = new Admin({
            ...req.body,
            password: hash
        })

        await newAdmin.save();

        res.status(201).json("Admin has been created");
    }
    catch (err) {
        next(err)
    }
}

export const login = async (req, res, next) => {
    try {
        const admin = await Admin.findOne({ username: req.body.username })
        if (!admin) return next(createError(404, "User not found"))

        const isCorrectPassword = await bcrypt.compare(req.body.password, admin.password);
        if (!isCorrectPassword) return next(createError(400, "Username or Password incorrect"));

        const token = jwt.sign({ id: admin._id }, process.env.JWT)

        const { password, ...otherDetails } = admin._doc;
        res
            .cookie("access_token", token, {
                httpOnly: true,
            })
            .status(200)
            .json({ details: { ...otherDetails }});
    }
    catch (err) {
        next(err)
    }
}
JavaScript
// api/controllers/reservation.js

import Reservation from "../models/Reservation.js";
import Restaurant from "../models/Restaurant.js";
import { slots } from "../data.js";

export const checkAvailableSlots = async(req, res, next) => {
  const date = req.params.date;
  const restId = req.params.id;
  try {
    const reservations = await Reservation.find({
      rest: restId,
      date: {$eq: new Date(date)} 
    });

    const reservedSlots = reservations.map(reservation => reservation.slot);
    const availableSlots = slots.filter(slot => !reservedSlots.includes(slot));
    res.status(200).json(availableSlots);
  }
  catch(err) {
    next(err)
  }
}

export const createReservation = async (req, res, next) => {

  const newRes = new Reservation(req.body);
  try {
    const reservation = await newRes.save();
    res.status(200).json(reservation);

  } catch (err) {
    next(err);
  }
};

// function to cancel reservation
export const deleteReservation = async (req, res, next) => {
  const resId = req.params.id;
  try {
    await Reservation.findByIdAndDelete(resId);
    res.status(200).json("Deleted Successfully");
  } catch (err) {
    next(err);
  }
};

// Function to get reservations by user ID
export const getReservationsByUserId = async (req, res, next) => {
  const userId = req.params.id;
  try {
    const reservations = await Reservation.find({ author: userId }).populate('rest', 'name');
    res.status(200).json(reservations);
  } catch (error) {
    next(err);
  }
};

// Function to get reservations by user ID
export const getReservationsByRestId = async (req, res, next) => {
  const restId = req.params.id;
  try {
    const reservations = await Reservation.find({ rest: restId }).populate('rest', 'name');
    res.status(200).json(reservations);
  } catch (error) {
    next(err);
  }
};
JavaScript
// api/controllers/rest.js

// create, update, view, get all reservations

import Restaurant from "../models/Restaurant.js";
import Admin from "../models/Admin.js";

export const createRestaurant = async (req, res, next) => {
  const adminId = req.body.admin; 
  const newRestaurant = new Restaurant(req.body);
  try {
    const savedRestaurant = await newRestaurant.save();
    await Admin.findOneAndUpdate(
      { _id: adminId },
      { $set: { rest: savedRestaurant._id } },
      { new: true }
    );
    res.status(200).json(savedRestaurant);
  } catch (err) {
    next(err);
  }
};

export const updateRestaurant = async (req, res, next) => {
  try {
    const restaurant = await Restaurant.findByIdAndUpdate(
      req.params.id,
      { $set: req.body },
      { new: true }
    );
    res.status(200).json(restaurant);
  } catch (err) {
    next(err);
  }
};


export const getRestaurant = async (req, res, next) => {
  try {
    const restaurant = await Restaurant.findById(req.params.id);
    res.status(200).json(restaurant);
  } catch (err) {
    next(err);
  }
};

export const getRestaurants = async (req, res, next) => {
  try {
    const rests = await Restaurant.find();
    res.status(200).json(rests);
  } catch (err) {
    next(err)
  }
}
JavaScript
// api/controllers/user.js

import User from "../models/User.js";
import bcrypt from "bcryptjs"
import jwt from "jsonwebtoken"
import { createError } from "../error.js"

export const register = async (req, res, next) => {
    try {

        const salt = bcrypt.genSaltSync(10);
        const hash = bcrypt.hashSync(req.body.password, salt);

        const newUser = new User({
            ...req.body,
            password: hash
        })

        await newUser.save();

        res.status(201).json("User has been created");
    }
    catch (err) {
        next(err)
    }
}

export const login = async (req, res, next) => {
    try {
        const user = await User.findOne({ username: req.body.username })
        if (!user) return next(createError(404, "User not found"))

        const isCorrectPassword = await bcrypt.compare(req.body.password, user.password);
        if (!isCorrectPassword) return next(createError(400, "Username or Password incorrect"));

        const token = jwt.sign({ id: user._id }, process.env.JWT)

        const { password, ...otherDetails } = user._doc;
        res
            .cookie("access_token", token, {
                httpOnly: true,
            })
            .status(200)
            .json({ details: { ...otherDetails }});
    }
    catch (err) {
        next(err)
    }
}
JavaScript
// api/routes/admins.js

import express from "express";
import { login, register } from "../controllers/admin.js ";

const router = express.Router();

router.post("/register", register)
router.post("/login", login)

export default router;
JavaScript
// api/routes/reservations.js

import express from "express";
import {
  createReservation,
  getReservationsByUserId,
  getReservationsByRestId,
  deleteReservation,
  checkAvailableSlots
} from "../controllers/reservation.js";

const router = express.Router();

router.get("/slots/:id/:date", checkAvailableSlots)
router.post("/", createReservation);
router.delete("/:id", deleteReservation);
router.get("/rest/:id", getReservationsByRestId);
router.get("/user/:id", getReservationsByUserId);

export default router;
JavaScript
import express from "express";
import {
  createRestaurant,
  getRestaurant,
  getRestaurants,
  updateRestaurant,
} from "../controllers/rest.js";

const router = express.Router();

router.post("/", createRestaurant);
router.put("/:id", updateRestaurant);
router.get("/:id", getRestaurant);
router.get("/", getRestaurants);

export default router;
JavaScript
// api/routes/users.js

import express from "express";
import { login, register } from "../controllers/user.js ";

const router = express.Router();

router.post("/register", register)
router.post("/login", login)

export default router;
JavaScript
// api/data.js

export const slots = [
    "11:00 AM - 12:00 PM",
    "12:00 PM - 01:00 PM",
    "01:00 PM - 02:00 PM",
    "02:00 PM - 03:00 PM",
    "03:00 PM - 04:00 PM",
    "04:00 PM - 05:00 PM",
    "05:00 PM - 06:00 PM",
    "06:00 PM - 07:00 PM",
    "07:00 PM - 08:00 PM",
    "08:00 PM - 09:00 PM",
    "09:00 PM - 10:00 PM"
];

Steps to Create Frontend:

Step 1: Create a react application by using the following command and navigate to the folder:

npx create-react-app client
cd client

Step 2: Install the following npm packages:

npm i @fortawesome/free-regular-svg-icons 
@fortawesome/free-solid-svg-icons
@fortawesome/react-fontawesome
axios react-router-dom sass dotenv
mapbox-gl react-map-gl

This is the snippet of what package.json should look like after installing the dependencies

"dependencies": {
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.7",
"dotenv": "^16.4.5",
"mapbox-gl": "^3.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-map-gl": "^7.1.7",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"sass": "^1.71.1",
"web-vitals": "^2.1.4"
}

Step 3: Create the following folder structure, separating pages, components and their styles.

Add-a-subheading(1)

Frontend Project Structure

Step 4: Create .env file in the client folder and add the Mapbox access token there:

REACT_APP_API_MAPBOX_KEY = <access_token>

Step 5: In the .gitignore file add .env.

// rest of the content in .gitignore
.env

Example: Below is the code of frontend Restaurant Reservation System:

CSS
/* client/src/index.css */

body {
  margin: 0;
  padding: 0;
  font-family: "Raleway", sans-serif;
}

:root {
  --magenta: #9F0D7F;
  --bright-pink: #EA1179;
  --black: #22092C;
  --maroon: #872341;
  --red: #BE3144;
  --orange: #F05941;
  --light-orange: #FFE4C9;
  --cream: #FFF7F1;
}
CSS
/* client/src/styles/adminLanding.css */

body,
html {
    overflow-x: hidden;
}

.createRestContainer {
    background-color: var(--cream);
    height: 100%;
}

.cpContainer {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    color: var(--maroon);
    width: 100%;
    padding: 50px;
}

.formContainer {
    width: 70%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 100px 0;
    flex-wrap: wrap;
}

.inputContainer {
    width: 80%;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    gap: 20px;
    justify-content: center;
    align-items: center;

}

.inputContainer button {
    width: 100px;
}



.star-rating-slider {
    margin: 5px 0 15px 0;
}

.star-rating-slider .star-icon{
    cursor: pointer;
    margin: 0 5px;
}

.input {
    display: flex;
    flex-direction: column;
    width: 90%;
}


.createRestContainer .column .input .formInput {
    display: flex;
    flex-direction: column;
    width: 70%;
    gap: 10px;
}

.input label {
    font-size: 1rem;
    font-weight: bold;
}

.inputContainer button {
    width: 200px;
    height: 40px;
    margin-top: 20px;
    border: none;
    background: var(--bright-pink);
    border-radius: 25px;
    font-size: 18px;
    color: white;
    font-weight: 700;
    cursor: pointer;
    outline: none;
}

.input input {
    font-size: 1rem;
    height: 40px;
    width: 100%;
    font-size: 1rem;
    outline: 0;
    border: 0;
    border-bottom: 1px solid var(--maroon);
    background: transparent;
    color: var(--orange);
}

.createRestContainer .column .input .type {
    text-align: center;
    height: 35px;
    background: white;
    border-radius: 5px;
    color: #0c4957;
    cursor: pointer;
    font-weight: bold;
}

@media screen and (max-width: 800px) {
    .inputContainer {
        width: 80%;
    }

    .picsContainer {
        width: 80%;
    }
}

@media screen and (max-width: 600px) {


    .formContainer {
        width: 90%;
    }

    .inputContainer {
        width: 100%;
    }

    .createRestContainer.column {
        width: 100%;
        margin-top: 50px;

    }

}
CSS
/* client/src/styles/home.css */

.home {
    background-color: var(--light-orange);
}

.search {
    position: relative;
    background-color: var(--orange);
    height: 300px;
    background-size: cover;
}

.searchBackground #surfer {
    background-attachment: fixed;
    background-size: cover;
    height: 800px;
    width: 100%;
    filter: brightness(60%);
}

.searchBar {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.searchBar h2 {
    color: white;
    text-align: center;
    font-size: 2.5em;
    margin-bottom: 20px;
}

.searchInput {
    background-color: white;
    padding: 5px 10px;
    border-radius: 50px;
    width: 400px;
    display: flex;
    align-items: center;
    justify-content: space-around;
}

.searchInput input {
    border: none;
    height: 40px;
    width: 70%;
    padding: 0 10px;
}

.searchInput input:focus {
    outline: none;
}

.searchInput .icon {
    cursor: pointer;
}

.searchedPosts {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    padding: 50px 0;
    gap: 50px;
}

#loading {
    width: 20px;
    height: 20px;
  }


@media screen and (max-width: 700px) {
    .searchInput {
        width: 300px;
    }

}
CSS
/* client/src/styles/landing.css */

.landing {
    background-color: var(--cream);
    height: 100vh;
    position: relative;

    .text {
        position: absolute;
        top: 50%;
        left: 50%;
        border: 7px solid var(--bright-pink);
        padding: 50px;
        transform: translate(-50%, -50%);
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 50px;
    
        p {
            font-size: 3rem;
    
    
            span {
                color: var(--magenta);
                font-weight: 800;
                font-style: italic;
            }
        }
    
        button {
            font-size: 1.5rem;
            padding: 30px 50px;
            border: none;
            outline: none;
            background-color: var(--orange);
            color: white;
            border-radius: 40px;
            cursor: pointer;
            transition: all ease-in-out 0.3s;
    
            &:hover {
                transform: translateY(-5px);
                background-color: var(--bright-pink);
            }
        }
    }
    
}
CSS
/* client/src/styles/register.css */

body,
html {
    overflow-x: hidden;
}

.register {
    background-color: var(--orange);
}

.registerCard {
    background-size: cover;
    background-repeat: no-repeat;
    height: 1000px;
    overflow: hidden;
    position: relative;
}

.registerCard .center {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 400px;
    background-color: white;
    border-radius: 10px;
}

.center h1 {
    font-size: 1.2rem;
    text-align: center;
    padding: 0 0 20px 0;
    text-transform: capitalize;
    border-bottom: 1px solid silver;
}

.center form {
    padding: 0 40px;
    box-sizing: border-box;
}

form .txt_field {
    position: relative;
    border-bottom: 2px solid #adadad;
    margin: 30px 0;
}

form .txt_field_img {
    position: relative;
    margin-top: 10px;
}

.txt_field {
    width: 100%;
    padding: 0 5px;
    height: 40px;
    font-size: 16px;
    border: none;
    background: none;
    outline: none;
}

.txt_field input {
    width: 100%;
    padding: 0 5px;
    height: 40px;
    font-size: 16px;
    border: none;
    background: none;
    outline: none;
}

.registerCard form .image {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    margin-top: 20px;
}

.register input {
    width: 100%;
    height: 50px;
    border: none;
    background: transparent;
    border-radius: 25px;
    font-size: 16px;
    color: black;
    outline: none;
}

.login_button .button {
    width: 100%;
    height: 50px;
    border: none;
    background: transparent;
    border-radius: 25px;
    font-size: 18px;
    color: white;
    font-weight: 700;
    cursor: pointer;
    outline: none;
}

.login_button {
    width: 100%;
    height: 50px;
    border: none;
    background: var(--red);
    border-radius: 25px;
    font-size: 18px;
    color: white;
    font-weight: 700;
    cursor: pointer;
    outline: none;
}

.signup_link {
    margin: 30px 0;
    text-align: center;
    font-size: 16px;
    color: #666666;
}

.signup_link a {
    color: var(--maroon);
    text-decoration: none;
}

.signup_link a:hover {
    text-decoration: underline;
}

@media screen and (max-width: 500px) {
    .registerCard .center {
        width: 300px;
        margin: 0;
    }
}
CSS
/* client/src/styles/reservation.css */

.reservation-container {
    margin-top: 100px;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 50px;
    flex-wrap: wrap;
    
}
CSS
/* client/src/styles/restaurant.css */ 

.restaurant {

    .rest-container {
        padding: 80px 0;
        align-items: center;
        justify-content: center;
        display: flex;
        gap: 50px;
        margin-top: 30px;
        width: 100%;
        flex-wrap: wrap;

        .leftContainer {
            display: flex;
            width: 40%;
            gap: 20px;
            flex-direction: column;

            @media screen and (max-width:600px){
                width: 80%;
            }

            .other-details {
                display: flex;
                flex-direction: column;
                justify-content: center;
                gap: 20px;
                padding: 20px 0;
                border-top: 2px solid var(--bright-pink);
                border-bottom: 2px solid var(--bright-pink);

                span {
                    color: var(--maroon);
                    font-weight: 700;
                }
            }

            .reservation-box {
                display: flex;
                flex-direction: column;
                justify-content: center;
                background-color: var(--cream);
                padding: 20px;
                align-items: center;
                gap: 20px;

                .form-input {
                    display: flex;
                    flex-direction: column;
                    gap: 10px;
                    
                    label {
                        font-size: 1.2rem;
                        font-weight: 700;
                    }

                    input {
                        width: 200px;
                        font-size: 1rem;
                        padding: 5px;
                    }

                    select {
                        width: 200px;
                        padding: 5px;
                    }
                }

                button {
                    width: 200px;
                    padding: 10px;
                    outline: none;
                    border: none;
                    cursor: pointer;
                    background-color: var(--magenta);
                    color: white;
                    border-radius: 20px;
                    font-size: 1rem;
                    font-weight: 700;
                }
            }
        }
    
        .rightContainer {
            display: flex;
            width: 40%;
            align-items: center;
            gap: 50px;
            flex-direction: column;

            .arrows {
                display: flex;
                gap: 10px;
                font-size: 1.7rem;
                margin-top: 20px;
                color: var(--orange);
                justify-content: center;

                .arrow {
                    cursor: pointer;
                }
            }

            @media screen and (max-width:600px){
                width: 80%;
            }
        }
    }
}
CSS
/* client/src/styles/card.css */

.cardContainer {

    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 10px;
    width: 500px;
    background-color: white;

    .picContainer{
        height: 150px;
        width: fit-content;

        img {
            height: 150px;
            width: 200px;
            object-fit: cover;
        }
    }

    .detailsContainer {
        display: flex;
        flex-direction: column;
        padding: 0 10px;
        justify-content: space-between;
        gap: 10px;

        h2 {
            color: var(--maroon);
        }

        h3 {
            color: var(--black);
            width: inherit;
        }
        
        .star-rating-slider {
            color: var(--black);
            .star-icon {
                color: var(--orange);
            }
        }
    }
}
CSS
/* client/src/styles/navbar.css */

* {
    margin: 0;
    padding: 0;
    text-decoration: none;
}

.navContainer {
    overflow: hidden;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    background-color: var(--red);
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
    padding: 0px 7%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    z-index: 100;
}


.navLogo {
    color: white;
    font-family: "Rubik Doodle Shadow", system-ui;
    font-weight: 900;
    font-size: 1.5rem;
    font-style: italic;
}

.navbar ul {
    list-style: none;
}

.navbar ul li {
    position: relative;
    float: left;
}

.profilePicture {
    height: 40px;
    width: 40px;
}

.profilePicture img {
    margin-top: 10px;
    height: 40px;
    width: 40px;
    border-radius: 50%;
    object-fit: cover;
}

#usernamename {
    display: none;
}

.navbar ul li p {
    font-size: 1rem;
    padding: 20px;
    color: white;
    display: block;
    transition: all 1s;
}

.navbar ul li p:hover {
    transform: translateY(-1px);
    border-bottom: solid 2px white;
}

#menu-bar {
    display: none;
}

.navContainer label {
    font-size: 1.5rem;
    color: white;
    cursor: pointer;
    display: none;
}

@media (max-width:800px) {
    .navContainer {
        height: 70px;
    }

    .navContainer label {
        display: initial;
    }

    .navContainer .navbar {
        position: fixed;
        top: 70px;
        left: -100%;
        text-align: center;
        background: white;
        border-top: 1px solid rgba(0, 0, 0, 0.1);
        display: block;
        transition: all 0.3s ease;
        width: 100%;
    }

    .profilePicture {
        display: none;
    }

    #usernamename {
        font-weight: bolder;
        display: block;
    }

    .navbar ul li p {
        color: black;
    }

    .navbar ul li p:hover {
        transform: translateY(-1px);

        border-bottom: none;
    }

    .navbar ul li {
        width: 100%;
    }

    #menu-bar:checked~.navbar {
        left: 0;
    }
}
CSS
/* client/src/styles/reservationCard.css */

.reservation-card {
    width: 40%;
    height: fit-content;
    background-color: var(--cream);
    display: flex;
    gap: 100px;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    padding: 50px;

    .icon {
        font-size: 30px;
        color: var(--red);
        width: 10%;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        transition: all 0.2s ease-in-out;

        &:hover {
            transform: translateY(-2px);
            color: var(--maroon);
        }
    }

    .details {
        display: flex;
        flex-direction: column;
        gap: 20px;
        width: 70%;
        .res-name {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            h1 {
                color: var(--magenta);
            }
    
            button {
                padding: 10px 20px;
                text-align: center;
                border: none;
                background-color: var(--light-orange);
                cursor: pointer;
                font-size: 1.025rem;
                transition: all  0.3s ease;
    
                &:hover {
                    background-color: var(--orange);
                    color: white;
                }
            }
    
        }
    
        .res-details {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;

            p {
                font-size: 1.2rem;
                font-weight: 800;
                color: var(--maroon);
            }
        }
    }
}
JavaScript
// client/src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { AuthContextProvider } from './authContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <AuthContextProvider>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </AuthContextProvider>
);
JavaScript
// client/src/App.js

import {
    BrowserRouter, Routes,
    Route, Navigate
} from "react-router-dom"
import { useContext } from "react";
import { AuthContext } from "./authContext";
import AdminLanding from "./pages/AdminLanding"
import Login from "./pages/Login";
import Register from "./pages/Register";
import Landing from "./pages/Landing";
import Home from "./pages/Home";
import Restaurant from "./pages/Restaurant";
import Reservations from "./pages/Reservations";

function App() {

    const { user } = useContext(AuthContext);

    const ProtectedRoute = ({ children, redirectTo }) => {
        if (!user || user.isAdmin) {
            return <Navigate to={redirectTo} />;
        } else {
            return children;
        }
    };

    const AdminProtectedRoute = ({ children, redirectTo }) => {
        if (!user || !user.isAdmin) {
            return <Navigate to={redirectTo} />;
        } else {
            return children;
        }
    };

    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<ProtectedRoute redirectTo="/userLogin">
                    <Home />
                </ProtectedRoute>} />
                <Route path="/landing" element={<Landing />} />
                <Route path="/adminLogin" element={<Login type="admin" />} />
                <Route path="/restaurant/:id"
                    element={<ProtectedRoute redirectTo="/userLogin">
                        <Restaurant />
                    </ProtectedRoute>} />
                <Route path="/reservations"
                    element={<ProtectedRoute redirectTo="/userLogin">
                        <Reservations />
                    </ProtectedRoute>} />
                <Route path="/adminRegister" element={<Register type="admin" />} />
                <Route path="/userLogin" element={<Login type="user" />} />
                <Route path="/userRegister" element={<Register type="user" />} />
                <Route path="/admin/dashboard" element={
                    <AdminProtectedRoute redirectTo="/adminLogin">
                        <AdminLanding />
                    </AdminProtectedRoute>
                } />
            </Routes>
        </BrowserRouter>
    );
}

export default App;
JavaScript
// client/src/useFetch.js

import { useEffect, useState } from "react";
import axios from "axios";

const useFetch = (url) => {
    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(false);

    useEffect(() => {
        const fetchData = async () => {
            setLoading(true);
            try {

                const res = await axios.get(url)
                
                setData(res.data);
            } catch (err) {
                setError(err);
            }
            setLoading(false);
        };
        fetchData();
    }, [url]);

    const reFetch = async () => {
        setLoading(true);
        try {
            const res = await axios.get(url)

            setData(res.data);
        } catch (err) {
            setError(err);
        }
        setLoading(false);
    };

    return { data, loading, error, reFetch };
};

export default useFetch;
JavaScript
// client/src/authContext.js

import { createContext, useReducer, useEffect } from "react"

const INITIAL_STATE = {
    user: JSON.parse(localStorage.getItem("user")) || null,
    loading: false,
    error: null,
};


export const AuthContext = createContext(INITIAL_STATE)

const AuthReducer = (state, action) => {
    switch (action.type) {
        case "LOGIN_START":
            return {
                user: null,
                loading: true,
                error: null
            };

        case "LOGIN_SUCCESS":
            return {
                user: action.payload,
                loading: false,
                error: null
            };

        case "LOGIN_FAILURE":
            return {
                user: null,
                loading: false,
                error: action.payload
            };

        case "LOGOUT":
            return {
                user: null,
                loading: false,
                error: null
            };
        default:
            return state;
    }
}

export const AuthContextProvider = ({ children }) => {
    const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE)

    useEffect(() => {
        localStorage.setItem("user", JSON.stringify(state.user))
    }, [state.user])


    return (
        <AuthContext.Provider
            value={{
                user: state.user,
                loading: state.loading,
                error: state.error,
                dispatch
            }}
        >
            {children}
        </AuthContext.Provider>
    )
}
JavaScript
// client/src/data.js

export const slots = [
    "11:00 AM - 12:00 PM",
    "12:00 PM - 01:00 PM",
    "01:00 PM - 02:00 PM",
    "02:00 PM - 03:00 PM",
    "03:00 PM - 04:00 PM",
    "04:00 PM - 05:00 PM",
    "05:00 PM - 06:00 PM",
    "06:00 PM - 07:00 PM",
    "07:00 PM - 08:00 PM",
    "08:00 PM - 09:00 PM",
    "09:00 PM - 10:00 PM"
];
JavaScript
// client/src/pages/AdminLanding.jsx

import React, { useContext, useState } from "react";
import { faStar as solidStar } from "@fortawesome/free-solid-svg-icons";
import { faStar as regularStar } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Navbar from '../components/Navbar'
import axios from "axios";
import { AuthContext } from "../authContext";
import { useNavigate } from "react-router-dom";
import { slots } from "../data";
import "../styles/adminLanding.scss"

const AdminLanding = () => {

  const [info, setInfo] = useState({});
  const [rating, setRating] = useState(0);
  const { user } = useContext(AuthContext);
  const navigate = useNavigate();

  const handleStarClick = (selectedRating) => {
    setRating(selectedRating);
  };

  const handleChange = (e) => {
    setInfo((prev) => ({ ...prev, [e.target.id]: e.target.value }));
  };

  const handleClick = async (e) => {
    e.preventDefault();
    
      const newpost = {
        ...info,
        admin: user._id,
        rating: rating,
        slots: slots
      }

      try {
        const res = await axios.post("http://localhost:7700/api/restaurants", newpost)
        console.log(res)
        navigate(`/admin/restaurant/${res.data._id}`);
      } catch (err) {
        console.log(err);
      }
    
  };

  return (
    <div className="createRestContainer">
        <Navbar />
        <div className="cpContainer">
        <div className="formContainer">

          <div className="inputContainer">

                <div className="input">
                  <label htmlFor="title">Name</label>
                  <input
                    onChange={handleChange}
                    type="text"
                    id="name"
                    placeholder="Enter Name"
                  />
                </div>

                <div className="input">
                  <label htmlFor="location">Location</label>
                  <input
                    onChange={handleChange}
                    type="text"
                    id="location"
                    placeholder="Enter location"
                  />
                </div>

                <div className="input">
                  <label htmlFor="location">Add Picture</label>
                  <input
                    onChange={handleChange}
                    type="text"
                    id="photo"
                    placeholder="Enter url of restaurant Picture"
                  />
                </div>

                <div className="input">
                  <label htmlFor="price">Price Range</label>
                  <input
                    onChange={handleChange}
                    type="text"
                    id="price"
                    placeholder="Enter price range"
                  />
                </div>

                <div className="input">
                  <label htmlFor="date">Contact Information</label>
                  <input
                    onChange={handleChange}
                    type="text"
                    id="contact"
                    placeholder="Enter the information"
                  />
                </div>

                <div className="input">
                  <div className="star-rating-slider">
                    Rating: 
                    {[1, 2, 3, 4, 5].map((star) => (
                      <FontAwesomeIcon
                        key={star}
                        icon={star <= rating ? solidStar : regularStar}
                        className={"star-icon"}
                        onClick={() => handleStarClick(star)}
                      />
                    ))}
                  </div>
                </div>

            <div className="input">
              <label htmlFor="desc">Description</label>
              <input
                onChange={handleChange}
                type="text"
                id="description"
                placeholder="A brief description"
              />
            </div>

            <button className="button" onClick={handleClick} type="submit">
              Create Restaurant
            </button>
          </div>
        </div>

      </div>
    </div>
  )
}

export default AdminLanding
JavaScript
// client/src/pages/Home.jsx

import React, { useState } from 'react'
import Navbar from '../components/Navbar'
import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import useFetch from "../useFetch"
import '../styles/home.scss'
import Card from '../components/Card';

const Home = () => {
  const [query, setQuery] = useState("");
    const {data, loading} = useFetch(`/restaurants`)

    const keys = ["name", "location"];

    const search = (data) => {
      return data.filter((item) =>
        keys.some((key) => item[key] && item[key].toLowerCase().includes(query))
      );
    };
    

  return (
    <div className='home'>
      <Navbar />
      <div className="search">
          <div className="searchBar">
              <h2>Explore</h2>
              <div className="searchInput">
                  <input
                    type="text"
                    placeholder="Search places or restaurants"
                    onChange={(e) => setQuery(e.target.value)}
                  />
                  <FontAwesomeIcon className="icon" icon={faMagnifyingGlass} />
              </div>
          </div>
      </div>

      <div className="searchedPosts">
        {loading ? (
          <>
            <div className="p" style={{color: "white", "fontFamily": "'Kaushan Script', cursive"}}>Loading...</div>
          </>
        ) : (
          <>
            {search(data)?.map((item, i) => (
              <Card
                key={i} 
                _id={item._id}
                photo={item.photo}
                name={item.name}
                location={item.location}
                rating={item.rating}
              />
            ))}
          </>
        )}
      </div>
    </div>
  )
}

export default Home
JavaScript
// client/src/pages/Landing.jsx

import React, { useContext, useEffect } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import "../styles/landing.scss"
import { AuthContext } from '../authContext'

const Landing = () => {
  const { user } = useContext(AuthContext)
  const navigate = useNavigate();

  useEffect(() => {
    if (user) {
      if (user.isAdmin) {
        navigate("/admin/dashboard");
      } else {
        navigate('/home');
      }
    }
  }, [user, navigate]);

  return (
    <div className="">
      {
        user ? null : (
          <div className='landing'>
            <div className="text">
              <p>Welcome to <span>AtSeat</span> !</p>
              <Link to="/adminLogin">
                <button>Login as Admin</button>
              </Link>
              <Link to="/userLogin">
                <button>Login as User</button>
              </Link>
            </div>
          </div>
        )
      }
    </div>
  )
}

export default Landing
JavaScript
// client/src/pages/Login.jsx

import React from "react";
import Navbar from "../components/Navbar";
import "../styles/login.scss";
import axios from "axios";
import { useContext, useState } from "react";
import { useNavigate, Link } from "react-router-dom";
import { AuthContext } from "../authContext";

function Login({type}) {
  const [credentials, setCredentials] = useState({
    username: undefined,
    password: undefined,
  });

  const urls = {
    "admin": "http://localhost:7700/api/admin/login",
    "user": "http://localhost:7700/api/users/login"
  }

  const landings = {
    "admin": "/admin/dashboard",
    "user": "/home"
  }

  const { dispatch } = useContext(AuthContext);
  const navigate = useNavigate();

  const handleChange = (e) => {
    setCredentials((prev) => ({ ...prev, [e.target.id]: e.target.value }));
  };

  const handleClick = async (e) => {
    e.preventDefault();
    dispatch({ type: "LOGIN_START" });
    try {
      const res = await axios.post(urls[type], credentials);
      dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
      navigate(landings[type]);
    } catch (err) {
      if (err.response && err.response.data) {
        // If error response and data exist, dispatch LOGIN_FAILURE with error message
        dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
      } else {
        // If no error response or data, dispatch generic error message
        dispatch({ type: "LOGIN_FAILURE", payload: "An error occurred while logging in" });
      }
    }
  };


  return (
    <div className="login">
      <Navbar type={type}/>
      <div className="loginCard">
        <div className="center">
          <h1>Welcome back {type}!</h1>
          <form>
            <div className="txt_field">
              <input
                type="text"
                placeholder="username"
                id="username"
                onChange={handleChange}
                className="lInput"
              />
            </div>
            <div className="txt_field">
              <input
                type="password"
                placeholder="password"
                id="password"
                onChange={handleChange}
                className="lInput"
              />
            </div>
            <div className="login_button">
              <button className="button" onClick={handleClick}>
                Login
              </button>
            </div>
            <div className="signup_link">
              <p>
                Not registered? <Link to={type==="admin"? "/adminRegister" : "/userRegister"}>Register</Link>
              </p>
            </div>
          </form>
 
        </div>
      </div>
    </div>
  );
}

export default Login;
JavaScript
// client/src/pages/Register.jsx

import React from "react";
import Navbar from "../components/Navbar";
import "../styles/register.scss";
import { Link } from "react-router-dom";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";

function Register({type}) {
  const navigate = useNavigate();

  const urls = {
    "admin": "/admin/register",
    "user": "/users/register"
  }

  const logins = {
    "admin": "/adminLogin",
    "user": "/userLogin"
  }
  const [info, setInfo] = useState({});

  const handleChange = (e) => {
    setInfo((prev) => ({ ...prev, [e.target.id]: e.target.value }));
  };

  const handleClick = async (e) => {
    e.preventDefault();

    try {
        await axios.post(urls[type], info, {withcredentials: false})

        navigate(logins[type]);
    } catch (err) {
        console.log(err)
    }
    
  };



  return (
    <div className="register">
      <Navbar type={type}/>
      <div className="registerCard">
        <div className="center">
          <h1>Join us dear {type}!</h1>

          <form>

            <div className="formInput">


              <div className="txt_field">
                <input
                  type="text"
                  placeholder="username"
                  name="username"
                  onChange={handleChange}
                  id="username"
                  required
                />
              </div>
              {type==="user" && <div className="txt_field">
                <input
                  type="email"
                  placeholder="email"
                  name="email"
                  onChange={handleChange}
                  id="email"
                  required
                />
              </div>}
              <div className="txt_field">
                <input
                  type="password"
                  placeholder="password"
                  name="password"
                  onChange={handleChange}
                  id="password"
                  required
                />
              </div>
            </div>
            <div className="login_button">
              <button className="button" onClick={handleClick}>
                Register
              </button>
            </div>
            <div className="signup_link">
              <p>
                Already Registered? <Link to={type==="admin"? "/adminLogin" : "/userLogin"}>Login</Link>
              </p>
            </div>
          </form>
        </div>
      </div>

    </div>
  );
}

export default Register;
JavaScript
// client/src/pages/Reservations.jsx

import React, { useContext } from 'react'
import useFetch from '../useFetch'
import { AuthContext } from '../authContext'
import ReservationCard from '../components/ReservationCard'
import Navbar from '../components/Navbar'
import "../styles/reservation.scss"

const Reservations = ({type}) => {

  const { user } = useContext(AuthContext)

  const urls = {
    "admin": `/reservations/rest/${user.rest}`,
    "user": `/reservations/user/${user._id}`
  }

  // Call useFetch unconditionally
  const {data} = useFetch(urls[type])

  return (
    <div>
        <Navbar />
        <div className="reservation-container">
        {data ? (
          data?.map((item, index) => (
            <ReservationCard key={index} props={{...item, type}} />
          ))
        ) : (
          "No Reservations Yet"
        )}
        </div>
    </div>
  )
}

export default Reservations
JavaScript
// client/src/pages/Restaurant.jsx

import React,  { useContext, useState, useEffect } from 'react'
import Navbar from '../components/Navbar'
import useFetch from '../useFetch'
import {
  faMoneyBill,
  faLocationDot,
  faThumbsUp,
  faPhone
} from "@fortawesome/free-solid-svg-icons";
import { useLocation, useNavigate } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import "../styles/restaurant.scss"
import Map, {Marker} from 'react-map-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import axios from 'axios'
import { AuthContext } from '../authContext';

const Restaurant = ({type}) => {

  const [date, setDate] = useState("");
  const location = useLocation();
  let id;
  if(type==="user")
    id = location.pathname.split("/")[2];
  else
    id = location.pathname.split("/")[3];
  const {data} = useFetch(`/restaurants/${id}`);
  const slots = useFetch(`/reservations/slots/${id}/${date}`).data


  const { user } = useContext(AuthContext);
  const [info, setInfo] = useState({});
  const navigate = useNavigate();

  // set the usestate to the data user passed 
  const handleChange = (e) => {
      setInfo((prev) => ({ ...prev, [e.target.id]: e.target.value }));
  }

  // post the usestate to database
  const handleClick = async (e) => {
    e.preventDefault();

    const newRes = {
        ...info, author: user._id, rest: id, date:date
    }
    try {
        await axios.post("http://localhost:7700/api/reservations", newRes, {
            withCredentials: false
        })

        navigate('/reservations')

    }
    catch (err) {
        console.log(err)
    }
}

  useEffect(() => {
    getPlaces();
  }, [data]);

  const [viewState, setViewState] = React.useState({
    latitude: 37.8,
    longitude: -122.4,
    zoom: 12
  });

  const getPlaces = async() => {
    const promise = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${data?.location}.json?access_token=${process.env.REACT_APP_API_MAPBOX_KEY}`)
    const placesData = await promise.json();
    if (placesData.features.length > 0) {
      const firstPlace = placesData.features[0]; // Assuming you want to use the first result
      const { center } = firstPlace;
      setViewState((prevState) => ({
        ...prevState,
        latitude: center[1],
        longitude: center[0],
      }));
    }
  }


  return (
    <div className='restaurant'>
      <Navbar />
      <div className="rest-container">
        <div className="leftContainer">
          <h1>{data.name}</h1>
          <p>{data.description}</p>


          <div className="other-details">
            <div className="location"><span><FontAwesomeIcon icon={faLocationDot} /> Location:  </span>{data.location}</div>
            <div className="rating"><span><FontAwesomeIcon icon={faThumbsUp} /> Rating:  </span>{data.rating}</div>
            <div className="price"><span><FontAwesomeIcon icon={faMoneyBill} /> Price Range:  </span>{data.price}</div>
            <div className="contact"><span><FontAwesomeIcon icon={faPhone} /> Contact:  </span>{data.contact}</div>
          </div>


          {!user.isAdmin && <div className="reservation-box">
              <div className="form-input">
                <label htmlFor="date">Date</label>
                <input type="date" onChange={(e) => setDate(e.target.value)} id='date'/>
              </div>
              {date && <div className="form-input">
                <label htmlFor="slot">Time</label>
                <select id="slot" onChange={handleChange}>
                  <option key={0} value="none">-</option> 
                  {
                    slots?.map((s, index) => (
                      <option key={index} value={s}>{s}</option>
                    ))
                  }
                </select>
              </div>}
              <div className="form-input">
                <label htmlFor="people">People</label>
                <input type="number" id='people' onChange={handleChange}/>
              </div>
              <button onClick={handleClick}>Make Reservation</button>
          </div>}
        </div>


        <div className="rightContainer">
        <div className="location-map">
          <Map
          {...viewState}
          onMove={evt => setViewState(evt.viewState)}
          style={{width: 400, height: 300}}
          mapStyle="mapbox://styles/mapbox/streets-v9"
          mapboxAccessToken={process.env.REACT_APP_API_MAPBOX_KEY}
        >
          <Marker className="marker" longitude={viewState.longitude} latitude={viewState.latitude} color="red" />
        </Map>
        </div>


          <div className="imgSlider">
            <div className="images">
              <img src={data.photo} height="300px" alt="" />
              </div> 
          </div>
        </div>
      </div>
    </div>
  )
}

export default Restaurant
JavaScript
// client/src/components/Card.jsx

import React from 'react'
import { faStar as solidStar } from "@fortawesome/free-solid-svg-icons";
import { faStar as regularStar } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import "../styles/card.scss"
import { Link } from 'react-router-dom';

const Card = (props) => {
  return (
    <div>
      <Link to={`/restaurant/${props._id}`}>
        <div className='cardContainer'>
          <div className="picContainer">
            <img src={props.photo} alt="" />
          </div>
          <div className="detailsContainer">
            <h2>{props.name}</h2>
            <h3>{props.location.substring(0, 50)}...</h3>
            <div className="star-rating-slider">
              Rating: 
                {[1, 2, 3, 4, 5].map((star) => (
                  <FontAwesomeIcon
                  key={star}
                  icon={star <= props.rating ? solidStar : regularStar}
                  className={"star-icon"}
                  />
                  ))}
            </div>
          </div>
        </div>
      </Link>
    </div>
  )
}

export default Card
JavaScript
// client/src/components/Navbar.jsx

import '../styles/navbar.scss'
import { useContext } from 'react';
import { faBars } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useNavigate } from "react-router-dom"
import { AuthContext } from "../authContext"


const Navbar = ({type}) => {

    const navigate = useNavigate()

    const { user, dispatch } = useContext(AuthContext)
    const handleClick = async (e) => {
        e.preventDefault();
        dispatch({ type: "LOGOUT" });
        navigate("/")
    }



    return (
        <div className='navContainer'>
            <Link to="/"> 
                <p className='navLogo'>AtSeat</p>
            </Link>

            <input type="checkbox" id='menu-bar' />
            <label htmlFor="menu-bar"><FontAwesomeIcon icon={faBars} className="icon" /></label>
            <nav className='navbar'>
                <ul>
                    {!user && <Link to="/landing">
                        <li><p>Landing</p></li>
                    </Link>}
                    {user && !user.isAdmin && <Link to="/home">
                        <li><p>Search Page</p></li>
                    </Link>}
                    {user && user.isAdmin && <Link to={`/admin/restaurant/${user.rest}`}>
                        <li><p>Restaurant</p></li>
                    </Link>}
                    {user && user.isAdmin && <Link to="/admin/reservations">
                        <li><p>Reservations</p></li>
                    </Link>}
                    {user && !user.isAdmin && <Link to="/reservations">
                        <li><p>Reservations</p></li>
                    </Link>}
                    {user ? (<>

                            <li onClick={handleClick} style={{ cursor: "pointer" }}><p>Logout</p></li>
                            <li><div className="profilePicture">
                                <img src={user.profilePicture || "https://i.ibb.co/MBtjqXQ/no-avatar.gif"} alt="" />
                            </div></li>
                            <li id="usernamename"><p>{user.username}</p></li>
                    </>
                    )
                        :
                        (
                            <>
                                <Link to={type === "admin"? "/adminRegister":"/userRegister"}>
                                    <li><p>Register</p></li>
                                </Link>
                                <Link to={type === "user"? "/userLogin" : "/adminLogin"}>
                                    <li><p>Login</p></li>
                                </Link>
                            </>
                        )}
                </ul>
            </nav>
        </div >
    )
}

export default Navbar
JavaScript
// client/src/ReservationCard.jsx

import React from 'react'
import "../styles/reservationCard.scss"
import { faTrash } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link } from 'react-router-dom';
import axios from 'axios';

const ReservationCard = ({props}) => {

    const handleClick = async () => {
        try{
            await axios.delete(`http://localhost:7700/api/reservations/${props._id}`, { withCredentials: false })
            window.location.reload();
        }
        catch(err) {
            console.log(err)
        }
    }

  return (
    <div className='reservation-card'>
        <div className="details">
            <div className="res-name">
                <h1>{props.rest.name}</h1>
                {props.type === "User" && 
                    <Link to={`/restaurant/${props.rest._id}`}>
                        <button>View</button>
                    </Link>
                }
            </div>
            <div className='res-details'><p>Date: </p> <span>{props.date.substring(0, 10)}</span> <p>Time: </p> <span>{props.slot}</span> <p>People: </p> <span>{props.people}</span></div>
        </div>
        {props.type === "User" && <div className="icon">
            <FontAwesomeIcon icon={faTrash} onClick={handleClick}/>
        </div>}
    </div>
  )
}

export default ReservationCard

Steps to Run the Application:

Start the frontend:

cd client
npm start

Start the backend:

cd api
npm start

Output:

  • Browser Output
ezgifcom-video-to-gif-converter-(4)

Final Output

  • Data stored in database:
Data_Cloud_MongoDBCloud-GoogleChrome2024-03-2115-12-15-ezgifcom-video-to-gif-converter

Database Records



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads