Project Structure(Frontend)
Frontend dependencies:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: Create the required files and add the following code.
/* App.css */
.container {
font-family: 'Arial, sans-serif';
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.header {
font-size: 24px;
margin-bottom: 10px;
border-radius: 15px;
}
/* Styles to Resturant List */
/* RestaurantList.css */
.container {
font-family: 'Arial, sans-serif';
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.header {
font-size: 32px;
margin-bottom: 20px;
background-color: #fc0671;
color: white;
padding: 10px;
}
.filter-container {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin-bottom: 20px;
}
.filter-container label {
font-size: 18px;
color: #555;
}
.filter-input {
padding: 8px;
font-size: 16px;
}
.restaurant-card-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
}
/* RestaurantCard.css */
.card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 12px;
margin: 10px;
width: 200px;
cursor: pointer;
transition: transform 0.3s ease-in-out;
}
.card:hover {
transform: scale(1.05);
}
.image-container {
overflow: hidden;
border-radius: 8px;
margin-bottom: 10px;
height: 150px;
/* Set a fixed height for the image container */
}
.restaurant-image {
width: 100%;
height: 100%;
object-fit: cover;
/* Maintain the aspect ratio and cover the container */
border-radius: 8px;
}
/* Responsive Styles */
@media screen and (max-width: 600px) {
.card {
width: 100%;
}
}
/* Dish Card */
.dish-card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 12px;
margin: 10px;
width: 200px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
transition: transform 0.3s ease-in-out;
}
.dish-card:hover {
transform: scale(1.05);
}
img {
width: 100%;
/* Set the width to fill the container */
height: 100px;
/* Set the fixed height for the image */
object-fit: cover;
/* Maintain the aspect ratio and cover the container */
border-radius: 8px;
margin-bottom: 10px;
}
h3 {
margin-bottom: 8px;
}
p {
margin-bottom: 8px;
}
button {
margin-top: 8px;
padding: 8px;
cursor: pointer;
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
}
/* Responsive Styles */
@media screen and (max-width: 600px) {
.dish-card {
width: 100%;
}
}
/* CART */
.cart-container {
position: fixed;
top: 10px;
right: 10px;
width: 200px;
border: 2px solid #fc0671;
border-radius: 10px;
height: fit-content;
padding: 5px 10px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
overflow-y: auto;
z-index: 1000;
}
.cart-container button {
background-color: #fc0671;
color: white;
border: none;
border-radius: 10px;
}
h2 {
margin-bottom: 10px;
margin-left: 20px;
}
.cart-content {
padding: 16px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
#pre-orders {
background-color: #fc0671;
color: aliceblue;
font-size: 20px;
padding: 5px 10px;
border-radius: 10px;
cursor: pointer;
}
/* Previous orders */
/* PreviousOrders.css */
.previous-orders-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
position: absolute;
background-color: #fc0671;
color: white;
border-radius: 10px;
}
.orders-list {
list-style: none;
padding: 0;
}
.order-card {
background-color: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease-in-out;
}
.order-card:hover {
transform: scale(1.02);
}
.order-card h3 {
color: #333;
margin-bottom: 10px;
}
.order-details {
display: flex;
justify-content: space-between;
font-size: 14px;
color: #666;
}
//index.js (update previous index.js)
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { RestaurantProvider } from './contexts/RestaurantContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<RestaurantProvider>
<App />
</RestaurantProvider>
);
reportWebVitals();
// App.js
import React, { useContext } from "react";
import RestaurantList from "./components/RestaurantList";
import DishesMenu from "./components/DishesMenu";
import Cart from "./components/Cart";
import { RestaurantContext } from "./contexts/RestaurantContext";
import "./App.css"; // Import the CSS file
const App = () => {
const { selectedRestaurant } = useContext(RestaurantContext);
return (
<>
<div className="container">
<h1 className="header">GFG Food Delivery App</h1>
<Cart
style={{ position: "absolute", right: "20px", top: "20px" }}
/>
<RestaurantList />
{selectedRestaurant && <DishesMenu />}
</div>
</>
);
};
export default App;
//RestaurantContext.js
import React, { createContext, useState, useEffect } from "react";
import axios from "axios";
const RestaurantContext = createContext();
const RestaurantProvider = ({ children }) => {
const [restaurants, setRestaurants] = useState([]);
const [selectedRestaurant, setSelectedRestaurant] = useState(null);
const [cartItems, setCartItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);
useEffect(() => {
const fetchRestaurants = async () => {
try {
const response = await axios.get(
"http://localhost:5000/restaurants"
);
setRestaurants(response.data);
} catch (error) {
console.error("Error fetching restaurants:", error.message);
}
};
fetchRestaurants();
}, []);
const handleAddItems = (dish) => {
console.log("Dish:", dish);
// Check if the dish already exists in the cart
const existingItemIndex = cartItems.findIndex(
(item) => item._id === dish._id
);
if (existingItemIndex !== -1) {
// If the dish already exists, update
// the quantity or any other logic
console.log(
"Dish already exists in the cart.
You may want to update the quantity."
);
// Example: Increment the quantity
const updatedCartItems = [...cartItems];
updatedCartItems[existingItemIndex] = {
...updatedCartItems[existingItemIndex],
quantity: updatedCartItems[existingItemIndex].quantity + 1,
};
// console.log('cart',cartItems.length);
// setTotalPrice(prev=>prev-dish.price)
setCartItems(updatedCartItems);
} else {
// If the dish is not in the cart, add it
console.log("Dish does not exist in the cart. Adding to the cart.");
console.log("cart", cartItems.length);
// setTotalPrice(prev=>prev-dish.price)
setCartItems([...cartItems, { ...dish, quantity: 1 }]);
}
setTotalPrice((prev) => prev + dish.price);
};
const handleRemoveItems = (dish) => {
console.log("Dish ID to remove:", dish);
// Check if the dish exists in the cart
const existingItemIndex = cartItems.findIndex(
(item) => item._id === dish._id
);
if (existingItemIndex !== -1) {
// If the dish exists, decrement the
// quantity or remove it from the cart
console.log(
"Dish exists in the cart. You may
want to decrease the quantity or remove it."
);
const updatedCartItems = [...cartItems];
if (updatedCartItems[existingItemIndex].quantity > 1) {
// If the quantity is greater than 1, decrement the quantity
updatedCartItems[existingItemIndex] = {
...updatedCartItems[existingItemIndex],
quantity: updatedCartItems[existingItemIndex].quantity - 1,
};
setTotalPrice(totalPrice - cartItems[existingItemIndex].price);
} else {
// If the quantity is 1, remove the dish from the cart
updatedCartItems.splice(existingItemIndex, 1);
setTotalPrice(totalPrice - cartItems[existingItemIndex].price);
}
setCartItems(updatedCartItems);
} else {
// If the dish is not in the cart,
// log a message or handle accordingly
console.log("Dish does not exist in the cart.");
}
};
const emptyCart = () => {
setCartItems([]);
setTotalPrice(0);
};
const value = {
restaurants,
selectedRestaurant,
setSelectedRestaurant,
handleAddItems,
handleRemoveItems,
totalPrice,
emptyCart,
};
return (
<RestaurantContext.Provider value={value}>
{children}
</RestaurantContext.Provider>
);
};
export { RestaurantContext, RestaurantProvider };
//Cart.js
import React, { useContext, useState } from "react";
import axios from "axios";
import { RestaurantContext } from "../contexts/RestaurantContext";
const Cart = () => {
const { totalPrice, emptyCart } = useContext(RestaurantContext);
const [isCheckingOut, setIsCheckingOut] = useState(false);
const generateOrderId = () => {
// Generate a unique order ID
// (you can use a library like uuid for a more robust solution)
return `${Math.floor(Math.random() * 1000)}`;
};
const handleCheckout = async () => {
try {
setIsCheckingOut(true);
const orderId = generateOrderId();
// Assuming you have a backend endpoint to handle the checkout
const response = await axios.post(
"http://localhost:5000/previousOrders",
{
orderId,
dateOfOrder: new Date(),
amount: totalPrice,
}
);
console.log(response.data);
emptyCart();
} catch (error) {
console.error("Error during checkout:", error.message);
} finally {
setIsCheckingOut(false);
}
};
return (
<div className="cart-container">
<h2>Cart</h2>
<div className="cart-content">
<span style={{ color: "brown" }}>Total Price: </span> $
{totalPrice}
<button onClick={handleCheckout} disabled={isCheckingOut}>
{isCheckingOut ? "Checking out..." : "Checkout"}
</button>
</div>
</div>
);
};
export default Cart;
//DishCard.js
import React, { useContext } from "react";
import { RestaurantContext } from "../contexts/RestaurantContext";
const DishCard = ({ dish }) => {
const { handleAddItems, handleRemoveItems } = useContext(RestaurantContext);
const handleAdd = () => {
handleAddItems(dish);
};
const handleRemove = () => {
handleRemoveItems(dish);
};
return (
<div className="dish-card">
<h3>{dish.name}</h3>
<img src={dish.image} alt="" />
<p>Price: ${dish.price}</p>
<div
style={{
width: "40%",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<button onClick={handleAdd}>+</button>
<button onClick={handleRemove}>-</button>
</div>
</div>
);
};
export default DishCard;
//DishesMenu.js
import React, { useContext } from 'react';
import DishCard from './DishCard';
import { RestaurantContext } from '../contexts/RestaurantContext';
const DishesMenu = () => {
const { selectedRestaurant } = useContext(RestaurantContext);
return (
<div>
<h2>Menu</h2>
{selectedRestaurant && (
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
{selectedRestaurant.menu.map((dish) => (
<DishCard key={dish.name} dish={dish} />
))}
</div>
)}
</div>
);
};
export default DishesMenu;
//PreviousOrders.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const PreviousOrders = ({ handleShow }) => {
const [orders, setOrders] = useState([]);
useEffect(() => {
const fetchOrders = async () => {
try {
const response = await axios.get('http://localhost:5000/previousOrders');
setOrders(response.data);
} catch (error) {
console.error('Error fetching orders:', error.message);
}
};
fetchOrders();
}, []);
return (
<div className="previous-orders-container">
<h2>Your Previous Orders</h2>
<button style={{ backgroundColor: "white", color: "red" }} onClick={handleShow}>Close</button>
<ul className="orders-list">
{orders.map(order => (
<li key={order.orderId} className="order-card">
<h3>Order #{order.orderId}</h3>
<div className="order-details">
<div>Items: 1</div>
<div>Total Amount: ${order.amount.toFixed(2)}</div>
</div>
<div>Ordered on: {new Date(order.dateOfOrder).toLocaleDateString()}</div>
</li>
))}
</ul>
</div>
);
};
export default PreviousOrders;
//RestaurantCard.js
import React from 'react';
const RestaurantCard = ({ restaurant, onClick }) => {
return (
<div className="card" onClick={onClick}>
<h3>{restaurant.name}</h3>
<div className="image-container">
<img className="restaurant-image" src={restaurant.image} alt={restaurant.name} />
</div>
<p>Rating: {restaurant.rating}</p>
</div>
);
};
export default RestaurantCard;
//RestaurantList.js
import React, { useContext, useState, useEffect } from 'react';
import RestaurantCard from './RestaurantCard';
import { RestaurantContext } from '../contexts/RestaurantContext';
import PreviousOrders from './PreviousOders';
const RestaurantList = () => {
const { restaurants, setSelectedRestaurant } = useContext(RestaurantContext);
const [filteredRestaurants, setFilteredRestaurants] = useState([...restaurants]);
const [ratingFilter, setRatingFilter] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const [showOrder, setShowOrder] = useState(false)
useEffect(() => {
filterRestaurants();
}, [ratingFilter, searchTerm, restaurants]);
const handleRestaurantClick = (restaurantId) => {
setSelectedRestaurant(restaurants.find((restaurant) => restaurant._id === restaurantId));
};
const handleRatingChange = (e) => {
setRatingFilter(e.target.value);
};
const handleSearchChange = (e) => {
setSearchTerm(e.target.value);
};
const filterRestaurants = () => {
let filtered = restaurants;
if (ratingFilter) {
filtered = filtered.filter((restaurant) => restaurant.rating >= parseFloat(ratingFilter));
}
if (searchTerm) {
const searchLower = searchTerm.toLowerCase();
filtered = filtered.filter((restaurant) =>
restaurant.name.toLowerCase().includes(searchLower)
);
}
setFilteredRestaurants(filtered);
};
const handleShow = () => {
setShowOrder(!showOrder)
}
return (
<div className="container">
<h2 className="header">Restaurant List</h2>
<div className="filter-container">
<label htmlFor="rating" className="filter-label">
Filter by Rating:
</label>
<input
type="number"
id="rating"
value={ratingFilter}
onChange={handleRatingChange}
className="filter-input"
/>
<label htmlFor="search" className="filter-label">
Search by Name:
</label>
<input
type="text"
id="search"
value={searchTerm}
onChange={handleSearchChange}
className="filter-input"
/>
<p id='pre-orders' onClick={handleShow}>
Previous Orders
</p>
</div>
<div className="restaurant-card-container">
{filteredRestaurants.map((restaurant) => (
<RestaurantCard
key={restaurant._id}
restaurant={restaurant}
onClick={() => handleRestaurantClick(restaurant._id)}
/>
))}
</div>
{showOrder && <PreviousOrders handleShow={handleShow} />}
</div>
);
};
export default RestaurantList;
Step 7: To start the frontend run the following command.
npm start
Output:
Food Delivery Application Project in Software Development
Food Delivery Application is one of the most common software development projects to date. In this article, we are going to make the Food Delivery Application software development project, from scratch, for final-year students. We will be covering all the steps you have to do while developing this project.
Table of Content
- Step 1- Team Formation Phase: Creating a Dynamic Team
- Step 2- Topic Selection
- Step 3- Project Synopsys for Food Delivery Application
- Step 4- Requirement Gathering (Creating SRS for Food Delivery Application)
- Software Requirement Specification (SRS) Document | Food Delivery Application
- 4.1 SRS (Food Delivery Application) | Introduction:
- 4.2 SRS (Food Delivery Application) | Overall Description:
- 4.3 SRS (Food Delivery Application) | Designing Food Delivery Application :
- Use case Diagram for Food Delivery Application:
- ER Model of Food Delivery Application:
- Data Flow Diagram of Food Delivery Application:
- 4.4 Functional Requirements | SRS (Food Delivery Application)
- 4.5 Non Functional Requirements | SRS (Food Delivery Application)
- 4.6 SRS (Food Delivery Application) | Appendices:
- 5. Coding or Implementation of Food Delivery Application
- Prerequisites:
- Approach to create Restaurant App using MERN:
- Steps to create Application:
- Project Structure(Backend):
- Project Structure(Frontend):
- Step 6- Testing Food Delivery Application
- Step 7- Creating Project Presentation on Food Delivery Application:
- Step 8- Writing a Research Paper on Food Delivery Application:
Project Development is a multiphase process in which every process is equally important. Here in this post, we are also going to develop our Food Delivery Application Project in multiple phases, such as:
- Team Formation
- Topic Selection
- Creating Project Synopsys
- Requirement Gathering
- Coding or Implementation
- Testing
- Project Presentation
- Writing a Research Paper
Let us look into the steps one by one.