Developing a comprehensive food delivery app requires multiple sections and functionalities to provide a smooth user experience. Here's a detailed breakdown of the sections and functionalities:
1. User Section
Functionalities:
-
User Registration/Login:
- Email/Phone/Google/Facebook login
-
Profile Management:
- Edit profile details
- Manage payment methods
- View order history
-
Search and Browse:
- Search for restaurants and dishes
- Filter by cuisine, rating, distance, price, etc.
-
Restaurant and Menu Details:
- View restaurant details and ratings
- Browse available menus and items
-
Order Placement:
- Add items to cart
- Apply promo codes
- Choose delivery or pickup
- Schedule orders
-
Checkout and Payment:
- Multiple payment options (Credit/Debit Card, PayPal, etc.)
- Order summary and confirmation
-
Real-Time Order Tracking:
- Track order preparation and delivery in real-time
- Rider location tracking on Google Maps
-
Notifications:
- Push notifications for order updates, promotions, and offers
-
Ratings and Reviews:
- Rate and review restaurants and delivery experience
-
Customer Support:
- In-app chat support
- FAQs and help center
2. Restaurant Section
Functionalities:
-
Restaurant Registration/Login:
- Secure login for restaurant owners
-
Profile Management:
- Edit restaurant details (name, address, contact, etc.)
- Update operating hours
-
Menu Management:
- Add/edit/delete menu items
- Upload images and descriptions
- Set prices and availability
-
Order Management:
- View and manage incoming orders
- Update order status (preparing, ready for pickup, out for delivery)
-
Promotions and Offers:
- Create and manage promotional offers
-
Analytics and Reports:
- View sales reports, order history, customer insights
-
Customer Interaction:
- Respond to reviews and feedback
3. Rider Section
Functionalities:
-
Rider Registration/Login:
- Secure login for riders
-
Profile Management:
- Edit personal details
- Manage availability status
-
Order Management:
- Accept/decline delivery requests
- View order details and pickup location
-
Real-Time Navigation:
- Integration with Google Maps for navigation
-
Order Tracking:
- Update delivery status (picked up, on the way, delivered)
-
Earnings and Payouts:
- View earnings and payment history
-
Notifications:
- Push notifications for new delivery requests and updates
-
In-App Chat:
- Messaging with customers and support
4. Admin Section
Functionalities:
-
Dashboard:
- Overview of app performance (active users, orders, etc.)
-
User Management:
- Manage user accounts (customers, restaurants, riders)
-
Restaurant Management:
- Approve/decline restaurant registrations
- Monitor restaurant performance
-
Rider Management:
- Approve/decline rider registrations
- Monitor rider performance
-
Order Management:
- View and manage all orders
-
Financial Management:
- Handle payments and transactions
-
Promotions and Marketing:
- Create and manage platform-wide promotions
-
Support and Feedback:
- Handle customer, restaurant, and rider complaints
- Monitor reviews and feedback
5. Technical Integration
Functionalities:
-
Google Maps Integration:
- Real-time location tracking
- Route optimization for riders
-
Payment Gateway Integration:
- Secure payment processing
-
Notification Service:
- Push notifications for real-time updates
-
Chat System Integration:
- In-app messaging for customer-rider interaction
Additional Considerations:
-
Security:
- Secure user data handling
- Regular security audits
-
Scalability:
- Ensure the app can handle high traffic
-
User Experience:
- Intuitive and user-friendly design
-
Performance Optimization:
- Ensure fast loading times and smooth performance
This breakdown should give you a comprehensive view of the sections and functionalities required for a full-fledged food delivery app.
Certainly! Below is the implementation of the User Registration/Login and Profile Management sections using Next.js and Tailwind CSS. We will use Firebase for authentication (email, phone, Google, and Facebook login) and Firestore for storing user profiles.
1. Setting up the Project
First, make sure you have Node.js installed, and then set up a new Next.js project:
npx create-next-app food-delivery-app
cd food-delivery-app
2. Install Required Dependencies
Install Firebase and Tailwind CSS:
npm install firebase tailwindcss postcss autoprefixer
3. Configure Tailwind CSS
Create tailwind.config.js
and postcss.config.js
files by running:
npx tailwindcss init -p
Update tailwind.config.js
to include paths to your pages and components:
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {},
},
plugins: [],
};
Add the following to styles/globals.css
:
@tailwind base;
@tailwind components;
@tailwind utilities;
4. Set up Firebase
Create a Firebase project at Firebase Console and add a web app. Copy the configuration and set up Firebase in your project.
Create a file firebase.js
in the root of your project and add:
// firebase.js
import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider, FacebookAuthProvider } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const googleProvider = new GoogleAuthProvider();
const facebookProvider = new FacebookAuthProvider();
export { auth, db, googleProvider, facebookProvider };
5. Create Authentication Components
Create a folder components
and add Login.js
and Profile.js
.
components/Login.js
// components/Login.js
import { useState } from "react";
import { auth, googleProvider, facebookProvider } from "../firebase";
import {
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
signInWithPopup
} from "firebase/auth";
import { useRouter } from "next/router";
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const router = useRouter();
const handleEmailSignIn = async () => {
try {
await signInWithEmailAndPassword(auth, email, password);
router.push("/profile");
} catch (error) {
console.error("Error signing in with email and password", error);
}
};
const handleEmailSignUp = async () => {
try {
await createUserWithEmailAndPassword(auth, email, password);
router.push("/profile");
} catch (error) {
console.error("Error signing up with email and password", error);
}
};
const handleGoogleSignIn = async () => {
try {
await signInWithPopup(auth, googleProvider);
router.push("/profile");
} catch (error) {
console.error("Error signing in with Google", error);
}
};
const handleFacebookSignIn = async () => {
try {
await signInWithPopup(auth, facebookProvider);
router.push("/profile");
} catch (error) {
console.error("Error signing in with Facebook", error);
}
};
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl mb-8">Login</h1>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="p-2 border border-gray-300 rounded mb-4"
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="p-2 border border-gray-300 rounded mb-4"
/>
<button
onClick={handleEmailSignIn}
className="p-2 bg-blue-500 text-white rounded mb-4"
>
Sign In
</button>
<button
onClick={handleEmailSignUp}
className="p-2 bg-green-500 text-white rounded mb-4"
>
Sign Up
</button>
<button
onClick={handleGoogleSignIn}
className="p-2 bg-red-500 text-white rounded mb-4"
>
Sign In with Google
</button>
<button
onClick={handleFacebookSignIn}
className="p-2 bg-blue-800 text-white rounded mb-4"
>
Sign In with Facebook
</button>
</div>
);
}
components/Profile.js
// components/Profile.js
import { useEffect, useState } from "react";
import { auth, db } from "../firebase";
import { useRouter } from "next/router";
import { doc, getDoc, setDoc } from "firebase/firestore";
import { signOut } from "firebase/auth";
export default function Profile() {
const [user, setUser] = useState(null);
const [profile, setProfile] = useState({ name: "", phone: "" });
const router = useRouter();
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (user) {
setUser(user);
const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
setProfile(docSnap.data());
}
} else {
router.push("/login");
}
});
return () => unsubscribe();
}, [router]);
const handleSaveProfile = async () => {
if (user) {
const docRef = doc(db, "users", user.uid);
await setDoc(docRef, profile);
}
};
const handleLogout = async () => {
await signOut(auth);
router.push("/login");
};
if (!user) {
return <div>Loading...</div>;
}
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<h1 className="text-4xl mb-8">Profile</h1>
<input
type="text"
placeholder="Name"
value={profile.name}
onChange={(e) => setProfile({ ...profile, name: e.target.value })}
className="p-2 border border-gray-300 rounded mb-4"
/>
<input
type="text"
placeholder="Phone"
value={profile.phone}
onChange={(e) => setProfile({ ...profile, phone: e.target.value })}
className="p-2 border border-gray-300 rounded mb-4"
/>
<button
onClick={handleSaveProfile}
className="p-2 bg-blue-500 text-white rounded mb-4"
>
Save Profile
</button>
<button
onClick={handleLogout}
className="p-2 bg-red-500 text-white rounded mb-4"
>
Logout
</button>
</div>
);
}
6. Create Pages
Create pages/login.js
and pages/profile.js
.
pages/login.js
// pages/login.js
import Login from "../components/Login";
export default function LoginPage() {
return <Login />;
}
pages/profile.js
// pages/profile.js
import Profile from "../components/Profile";
export default function ProfilePage() {
return <Profile />;
}
7. Update pages/_app.js
Ensure that your app uses the global styles by updating pages/_app.js
:
// pages/_app.js
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
8. Running the App
Finally, run your app:
npm run dev
Now you have a basic setup for user registration/login and profile management with Next.js and Tailwind CSS using Firebase for authentication and Firestore for storing user profiles. This should get you started, and you can expand upon this base to add more functionalities as needed.
Certainly! Below is the implementation of the Search and Browse and Restaurant and Menu Details sections using Next.js and Tailwind CSS. We will assume that we have a list of restaurants and menu items stored in Firestore.
1. Search and Browse Component
Create a file components/SearchAndBrowse.js
to handle the search and filter functionality.
components/SearchAndBrowse.js
// components/SearchAndBrowse.js
import { useState, useEffect } from "react";
import { db } from "../firebase";
import { collection, getDocs, query, where } from "firebase/firestore";
export default function SearchAndBrowse() {
const [restaurants, setRestaurants] = useState([]);
const [search, setSearch] = useState("");
const [filters, setFilters] = useState({
cuisine: "",
rating: 0,
distance: 0,
price: 0,
});
useEffect(() => {
const fetchRestaurants = async () => {
let q = collection(db, "restaurants");
if (search) {
q = query(q, where("name", "==", search));
}
if (filters.cuisine) {
q = query(q, where("cuisine", "==", filters.cuisine));
}
const querySnapshot = await getDocs(q);
const fetchedRestaurants = [];
querySnapshot.forEach((doc) => {
fetchedRestaurants.push({ id: doc.id, ...doc.data() });
});
setRestaurants(fetchedRestaurants);
};
fetchRestaurants();
}, [search, filters]);
const handleSearchChange = (e) => setSearch(e.target.value);
const handleFilterChange = (e) => {
const { name, value } = e.target;
setFilters({ ...filters, [name]: value });
};
return (
<div className="container mx-auto px-4">
<div className="flex flex-col items-center mb-8">
<input
type="text"
placeholder="Search for restaurants or dishes"
value={search}
onChange={handleSearchChange}
className="p-2 border border-gray-300 rounded mb-4"
/>
<select
name="cuisine"
value={filters.cuisine}
onChange={handleFilterChange}
className="p-2 border border-gray-300 rounded mb-4"
>
<option value="">All Cuisines</option>
<option value="Italian">Italian</option>
<option value="Chinese">Chinese</option>
<option value="Indian">Indian</option>
</select>
{/* Add more filters here */}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{restaurants.map((restaurant) => (
<div
key={restaurant.id}
className="border p-4 rounded hover:shadow-lg transition-shadow"
>
<h2 className="text-2xl font-bold">{restaurant.name}</h2>
<p className="text-gray-600">{restaurant.cuisine}</p>
<p className="text-gray-600">Rating: {restaurant.rating}</p>
<p className="text-gray-600">Distance: {restaurant.distance} km</p>
<p className="text-gray-600">Price: ${restaurant.price}</p>
</div>
))}
</div>
</div>
);
}
2. Restaurant and Menu Details Component
Create a file components/RestaurantDetails.js
to display restaurant details and their menu items.
components/RestaurantDetails.js
// components/RestaurantDetails.js
import { useEffect, useState } from "react";
import { db } from "../firebase";
import { doc, getDoc, collection, getDocs } from "firebase/firestore";
import { useRouter } from "next/router";
export default function RestaurantDetails() {
const [restaurant, setRestaurant] = useState(null);
const [menuItems, setMenuItems] = useState([]);
const router = useRouter();
const { id } = router.query;
useEffect(() => {
const fetchRestaurantDetails = async () => {
if (id) {
const docRef = doc(db, "restaurants", id);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
setRestaurant(docSnap.data());
}
const menuRef = collection(db, "restaurants", id, "menu");
const menuSnapshot = await getDocs(menuRef);
const fetchedMenuItems = [];
menuSnapshot.forEach((doc) => {
fetchedMenuItems.push({ id: doc.id, ...doc.data() });
});
setMenuItems(fetchedMenuItems);
}
};
fetchRestaurantDetails();
}, [id]);
if (!restaurant) {
return <div>Loading...</div>;
}
return (
<div className="container mx-auto px-4">
<h1 className="text-4xl font-bold mb-8">{restaurant.name}</h1>
<p className="text-gray-600 mb-4">{restaurant.cuisine}</p>
<p className="text-gray-600 mb-4">Rating: {restaurant.rating}</p>
<p className="text-gray-600 mb-4">Distance: {restaurant.distance} km</p>
<p className="text-gray-600 mb-4">Price: ${restaurant.price}</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-8">
{menuItems.map((item) => (
<div
key={item.id}
className="border p-4 rounded hover:shadow-lg transition-shadow"
>
<h2 className="text-2xl font-bold">{item.name}</h2>
<p className="text-gray-600">{item.description}</p>
<p className="text-gray-600">Price: ${item.price}</p>
</div>
))}
</div>
</div>
);
}
3. Create Pages
Create pages/search.js
and pages/restaurant/[id].js
.
pages/search.js
// pages/search.js
import SearchAndBrowse from "../components/SearchAndBrowse";
export default function SearchPage() {
return <SearchAndBrowse />;
}
pages/restaurant/[id].js
// pages/restaurant/[id].js
import { useRouter } from "next/router";
import RestaurantDetails from "../../components/RestaurantDetails";
export default function RestaurantPage() {
const router = useRouter();
const { id } = router.query;
return <RestaurantDetails id={id} />;
}
4. Link Components
Update the restaurant cards in SearchAndBrowse.js
to link to the restaurant details page.
Updated components/SearchAndBrowse.js
// components/SearchAndBrowse.js
import { useState, useEffect } from "react";
import { db } from "../firebase";
import { collection, getDocs, query, where } from "firebase/firestore";
import Link from "next/link";
export default function SearchAndBrowse() {
const [restaurants, setRestaurants] = useState([]);
const [search, setSearch] = useState("");
const [filters, setFilters] = useState({
cuisine: "",
rating: 0,
distance: 0,
price: 0,
});
useEffect(() => {
const fetchRestaurants = async () => {
let q = collection(db, "restaurants");
if (search) {
q = query(q, where("name", "==", search));
}
if (filters.cuisine) {
q = query(q, where("cuisine", "==", filters.cuisine));
}
const querySnapshot = await getDocs(q);
const fetchedRestaurants = [];
querySnapshot.forEach((doc) => {
fetchedRestaurants.push({ id: doc.id, ...doc.data() });
});
setRestaurants(fetchedRestaurants);
};
fetchRestaurants();
}, [search, filters]);
const handleSearchChange = (e) => setSearch(e.target.value);
const handleFilterChange = (e) => {
const { name, value } = e.target;
setFilters({ ...filters, [name]: value });
};
return (
<div className="container mx-auto px-4">
<div className="flex flex-col items-center mb-8">
<input
type="text"
placeholder="Search for restaurants or dishes"
value={search}
onChange={handleSearchChange}
className="p-2 border border-gray-300 rounded mb-4"
/>
<select
name="cuisine"
value={filters.cuisine}
onChange={handleFilterChange}
className="p-2 border border-gray-300 rounded mb-4"
>
<option value="">All Cuisines</option>
<option value="Italian">Italian</option>
<option value="Chinese">Chinese</option>
<option value="Indian">Indian</option>
</select>
{/* Add more filters here */}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{restaurants.map((restaurant) => (
<Link href={`/restaurant/${restaurant.id}`}
key={restaurant.id}>
<div className="border p-4 rounded hover:shadow-lg transition-shadow cursor-pointer">
<h2 className="text-2xl font-bold">{restaurant.name}</h2>
<p className="text-gray-600">{restaurant.cuisine}</p>
<p className="text-gray-600">Rating: {restaurant.rating}</p>
<p className="text-gray-600">Distance: {restaurant.distance} km</p>
<p className="text-gray-600">Price: ${restaurant.price}</p>
</div>
</Link>
))}
</div>
</div>
);
}
5. Update Navigation
Ensure you have a way to navigate to the search page. Update your pages/index.js
or wherever necessary:
pages/index.js
// pages/index.js
import Link from "next/link";
export default function Home() {
return (
<div className="container mx-auto px-4">
<h1 className="text-4xl font-bold mb-8">Welcome to Food Delivery App</h1>
<Link href="/search">
<a className="text-blue-500">Search for Restaurants</a>
</Link>
</div>
);
}
6. Update _app.js
Ensure your global styles are applied by checking pages/_app.js
:
// pages/_app.js
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
7. Running the App
Run your app:
npm run dev
Now you have implemented the Search and Browse and Restaurant and Menu Details sections using Next.js and Tailwind CSS. This setup assumes your Firestore database is structured with collections for restaurants and their respective menus. Adjust the structure as needed for your specific database schema.
Certainly! Below is the implementation of the Order Placement and Checkout and Payment sections using Next.js and Tailwind CSS. This example includes adding items to the cart, applying promo codes, choosing delivery or pickup, scheduling orders, and handling the checkout process.
1. Cart Context
First, we need a context to manage the cart state globally.
context/CartContext.js
// context/CartContext.js
import { createContext, useState, useContext } from 'react';
const CartContext = createContext();
export function CartProvider({ children }) {
const [cart, setCart] = useState([]);
const [promoCode, setPromoCode] = useState("");
const [deliveryOption, setDeliveryOption] = useState("delivery");
const [schedule, setSchedule] = useState(null);
const addItemToCart = (item) => {
setCart([...cart, item]);
};
const applyPromoCode = (code) => {
setPromoCode(code);
};
const chooseDeliveryOption = (option) => {
setDeliveryOption(option);
};
const scheduleOrder = (date) => {
setSchedule(date);
};
const clearCart = () => {
setCart([]);
setPromoCode("");
setDeliveryOption("delivery");
setSchedule(null);
};
return (
<CartContext.Provider value={{ cart, addItemToCart, promoCode, applyPromoCode, deliveryOption, chooseDeliveryOption, schedule, scheduleOrder, clearCart }}>
{children}
</CartContext.Provider>
);
}
export function useCart() {
return useContext(CartContext);
}
2. Add to Cart Component
components/AddToCart.js
// components/AddToCart.js
import { useCart } from "../context/CartContext";
export default function AddToCart({ item }) {
const { addItemToCart } = useCart();
return (
<button
onClick={() => addItemToCart(item)}
className="bg-blue-500 text-white px-4 py-2 rounded"
>
Add to Cart
</button>
);
}
3. Cart Component
components/Cart.js
// components/Cart.js
import { useCart } from "../context/CartContext";
import Link from "next/link";
export default function Cart() {
const { cart, promoCode, applyPromoCode, deliveryOption, chooseDeliveryOption, schedule, scheduleOrder } = useCart();
const handlePromoCodeChange = (e) => {
applyPromoCode(e.target.value);
};
const handleDeliveryOptionChange = (e) => {
chooseDeliveryOption(e.target.value);
};
const handleScheduleChange = (e) => {
scheduleOrder(e.target.value);
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Your Cart</h2>
<ul className="mb-4">
{cart.map((item, index) => (
<li key={index} className="border p-2 mb-2">
{item.name} - ${item.price}
</li>
))}
</ul>
<div className="mb-4">
<label className="block mb-2">Promo Code</label>
<input
type="text"
value={promoCode}
onChange={handlePromoCodeChange}
className="p-2 border border-gray-300 rounded"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Delivery Option</label>
<select
value={deliveryOption}
onChange={handleDeliveryOptionChange}
className="p-2 border border-gray-300 rounded"
>
<option value="delivery">Delivery</option>
<option value="pickup">Pickup</option>
</select>
</div>
<div className="mb-4">
<label className="block mb-2">Schedule Order</label>
<input
type="datetime-local"
value={schedule}
onChange={handleScheduleChange}
className="p-2 border border-gray-300 rounded"
/>
</div>
<Link href="/checkout">
<a className="bg-green-500 text-white px-4 py-2 rounded">Proceed to Checkout</a>
</Link>
</div>
);
}
4. Checkout Component
components/Checkout.js
// components/Checkout.js
import { useState } from "react";
import { useCart } from "../context/CartContext";
import { useRouter } from "next/router";
export default function Checkout() {
const { cart, promoCode, deliveryOption, schedule, clearCart } = useCart();
const [paymentMethod, setPaymentMethod] = useState("Credit Card");
const router = useRouter();
const handlePaymentMethodChange = (e) => {
setPaymentMethod(e.target.value);
};
const handleOrderSubmit = (e) => {
e.preventDefault();
// Add your payment processing logic here
clearCart();
router.push("/order-confirmation");
};
const calculateTotal = () => {
let total = cart.reduce((acc, item) => acc + item.price, 0);
// Apply promo code logic here (example: 10% off)
if (promoCode === "DISCOUNT10") {
total *= 0.9;
}
return total.toFixed(2);
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Checkout</h2>
<form onSubmit={handleOrderSubmit}>
<div className="mb-4">
<label className="block mb-2">Payment Method</label>
<select
value={paymentMethod}
onChange={handlePaymentMethodChange}
className="p-2 border border-gray-300 rounded"
>
<option value="Credit Card">Credit Card</option>
<option value="PayPal">PayPal</option>
</select>
</div>
<div className="mb-4">
<h3 className="text-xl font-bold mb-2">Order Summary</h3>
<ul className="mb-2">
{cart.map((item, index) => (
<li key={index} className="border p-2 mb-2">
{item.name} - ${item.price}
</li>
))}
</ul>
<p className="text-lg font-bold">Total: ${calculateTotal()}</p>
</div>
<button type="submit" className="bg-green-500 text-white px-4 py-2 rounded">
Confirm Order
</button>
</form>
</div>
);
}
5. Order Confirmation Component
components/OrderConfirmation.js
// components/OrderConfirmation.js
export default function OrderConfirmation() {
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Order Confirmation</h2>
<p>Your order has been placed successfully!</p>
</div>
);
}
6. Create Pages
Create the necessary pages in the pages
directory.
pages/cart.js
// pages/cart.js
import Cart from "../components/Cart";
export default function CartPage() {
return <Cart />;
}
pages/checkout.js
// pages/checkout.js
import Checkout from "../components/Checkout";
export default function CheckoutPage() {
return <Checkout />;
}
pages/order-confirmation.js
// pages/order-confirmation.js
import OrderConfirmation from "../components/OrderConfirmation";
export default function OrderConfirmationPage() {
return <OrderConfirmation />;
}
7. Navigation Updates
Ensure you have links to navigate to the cart and checkout pages, if needed.
Example: Update pages/index.js
// pages/index.js
import Link from "next/link";
export default function Home() {
return (
<div className="container mx-auto px-4">
<h1 className="text-4xl font-bold mb-8">Welcome to Food Delivery App</h1>
<Link href="/search">
<a className="text-blue-500">Search for Restaurants</a>
</Link>
<br />
<Link href="/cart">
<a className="text-blue-500">Go to Cart</a>
</Link>
</div>
);
}
8. Update _app.js
Ensure your global styles are applied by checking pages/_app.js
:
// pages/_app.js
import '../styles/globals.css'
import { CartProvider } from '../context/CartContext';
function MyApp({ Component, pageProps }) {
return (
<CartProvider>
<Component {...pageProps} />
</CartProvider>
);
}
export default MyApp;
Running the App
Run your app:
npm run dev
This setup allows users to add items to the cart, apply promo codes, choose delivery or
pickup, schedule orders, proceed to checkout, and confirm their order. Adjust and expand the code as needed for your specific requirements and payment processing integration.
Sure, I'll provide a detailed breakdown for implementing these sections using Next.js and Tailwind CSS. Here's the implementation for Real-Time Order Tracking, Notifications, Ratings and Reviews, and Customer Support.
1. Real-Time Order Tracking
components/OrderTracking.js
To track order preparation and delivery in real-time and display rider location on Google Maps, you'll need to use Google Maps API and a real-time database like Firebase Firestore.
// components/OrderTracking.js
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { GoogleMap, LoadScript, Marker } from "@react-google-maps/api";
import { doc, onSnapshot } from "firebase/firestore";
import { db } from "../firebase"; // Make sure you have firebase configured
const containerStyle = {
width: '100%',
height: '400px'
};
const center = {
lat: -3.745,
lng: -38.523
};
export default function OrderTracking() {
const [order, setOrder] = useState(null);
const [riderLocation, setRiderLocation] = useState(center);
const router = useRouter();
const { orderId } = router.query;
useEffect(() => {
if (orderId) {
const unsubscribe = onSnapshot(doc(db, "orders", orderId), (doc) => {
setOrder(doc.data());
if (doc.data().riderLocation) {
setRiderLocation(doc.data().riderLocation);
}
});
return () => unsubscribe();
}
}, [orderId]);
if (!order) return <div>Loading...</div>;
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Order Tracking</h2>
<p>Order Status: {order.status}</p>
<LoadScript googleMapsApiKey="YOUR_GOOGLE_MAPS_API_KEY">
<GoogleMap
mapContainerStyle={containerStyle}
center={riderLocation}
zoom={15}
>
<Marker position={riderLocation} />
</GoogleMap>
</LoadScript>
</div>
);
}
2. Notifications
For push notifications, you can use Firebase Cloud Messaging (FCM). Here, I'll outline how to set it up.
firebase-messaging-sw.js
Create a service worker file in your public directory.
// public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/9.6.1/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging-compat.js');
firebase.initializeApp({
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
});
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function(payload) {
console.log('Received background message ', payload);
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
firebase.js
Configure Firebase in your project.
// firebase.js
import { initializeApp } from "firebase/app";
import { getMessaging, getToken, onMessage } from "firebase/messaging";
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
};
const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);
export const requestPermission = async () => {
try {
await Notification.requestPermission();
const token = await getToken(messaging, { vapidKey: 'YOUR_VAPID_KEY' });
return token;
} catch (error) {
console.error('Permission denied', error);
}
};
export const onMessageListener = () =>
new Promise((resolve) => {
onMessage(messaging, (payload) => {
resolve(payload);
});
});
3. Ratings and Reviews
components/Reviews.js
// components/Reviews.js
import { useState } from "react";
import { collection, addDoc } from "firebase/firestore";
import { db } from "../firebase";
export default function Reviews({ restaurantId }) {
const [rating, setRating] = useState(0);
const [comment, setComment] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
try {
await addDoc(collection(db, "reviews"), {
restaurantId,
rating,
comment,
createdAt: new Date()
});
setRating(0);
setComment("");
} catch (error) {
console.error("Error adding review: ", error);
}
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Leave a Review</h2>
<form onSubmit={handleSubmit} className="mb-4">
<div className="mb-4">
<label className="block mb-2">Rating</label>
<select value={rating} onChange={(e) => setRating(e.target.value)} className="p-2 border border-gray-300 rounded">
<option value="0">Select Rating</option>
{[1, 2, 3, 4, 5].map((num) => (
<option key={num} value={num}>
{num} Star{num > 1 ? "s" : ""}
</option>
))}
</select>
</div>
<div className="mb-4">
<label className="block mb-2">Comment</label>
<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
rows="4"
/>
</div>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Submit Review</button>
</form>
</div>
);
}
4. Customer Support
components/Support.js
// components/Support.js
import { useState } from "react";
export default function Support() {
const [message, setMessage] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
// Handle support message submission logic here
setMessage("");
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Customer Support</h2>
<form onSubmit={handleSubmit} className="mb-4">
<div className="mb-4">
<label className="block mb-2">Your Message</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
rows="4"
/>
</div>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Send Message</button>
</form>
<div className="mt-8">
<h3 className="text-xl font-bold mb-4">FAQs</h3>
<div>
<h4 className="font-semibold">How can I track my order?</h4>
<p className="mb-4">You can track your order in the Order Tracking section in your account.</p>
<h4 className="font-semibold">What payment methods are accepted?</h4>
<p className="mb-4">We accept various payment methods including credit cards and PayPal.</p>
</div>
</div>
</div>
);
}
5. Create Pages
Create the necessary pages in the pages
directory.
pages/order-tracking.js
// pages/order-tracking.js
import OrderTracking from "../components/OrderTracking";
export default function OrderTrackingPage() {
return <OrderTracking />;
}
pages/reviews.js
// pages/reviews.js
import Reviews from "../components/Reviews";
export default function ReviewsPage({ query }) {
return <Reviews restaurantId={query.restaurantId} />;
}
ReviewsPage.getInitialProps = ({ query }) => {
return { query };
};
pages/support.js
// pages/support.js
import Support from "../components/Support";
export default function SupportPage() {
return <Support />;
}
Running the App
Run your app:
npm run dev
This setup provides the following functionalities:
- Real-Time Order Tracking: Track the order preparation and delivery status, and show rider location on Google Maps.
- Notifications: Setup push notifications using Firebase Cloud Messaging.
- **Ratings and
Reviews:** Allow users to rate and review restaurants.
- Customer Support: Provide in-app chat support and a FAQ section.
You will need to add your own Firebase configuration and Google Maps API key to make these components fully functional. Additionally, you might want to expand the functionality and styling as per your needs.
Sure! Below is the implementation for the Restaurant Section with functionalities for Restaurant Registration/Login, Profile Management, and Menu Management using Next.js and Tailwind CSS.
1. Restaurant Registration/Login
components/RestaurantAuth.js
// components/RestaurantAuth.js
import { useState } from "react";
import { signInWithEmailAndPassword, createUserWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase"; // Make sure you have firebase configured
export default function RestaurantAuth() {
const [isLogin, setIsLogin] = useState(true);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleAuth = async (e) => {
e.preventDefault();
try {
if (isLogin) {
await signInWithEmailAndPassword(auth, email, password);
} else {
await createUserWithEmailAndPassword(auth, email, password);
}
} catch (error) {
console.error("Authentication Error: ", error);
}
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">{isLogin ? "Login" : "Register"}</h2>
<form onSubmit={handleAuth}>
<div className="mb-4">
<label className="block mb-2">Email</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">{isLogin ? "Login" : "Register"}</button>
</form>
<button
className="mt-4 text-blue-500"
onClick={() => setIsLogin(!isLogin)}
>
{isLogin ? "Create an account" : "Login with existing account"}
</button>
</div>
);
}
2. Profile Management
components/RestaurantProfile.js
// components/RestaurantProfile.js
import { useState, useEffect } from "react";
import { doc, getDoc, setDoc } from "firebase/firestore";
import { db, auth } from "../firebase"; // Make sure you have firebase configured
import { useAuthState } from "react-firebase-hooks/auth";
export default function RestaurantProfile() {
const [user] = useAuthState(auth);
const [name, setName] = useState("");
const [address, setAddress] = useState("");
const [contact, setContact] = useState("");
const [hours, setHours] = useState("");
useEffect(() => {
if (user) {
const fetchProfile = async () => {
const docRef = doc(db, "restaurants", user.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const data = docSnap.data();
setName(data.name);
setAddress(data.address);
setContact(data.contact);
setHours(data.hours);
}
};
fetchProfile();
}
}, [user]);
const handleSave = async (e) => {
e.preventDefault();
if (user) {
try {
await setDoc(doc(db, "restaurants", user.uid), {
name,
address,
contact,
hours,
});
} catch (error) {
console.error("Error updating profile: ", error);
}
}
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Restaurant Profile</h2>
<form onSubmit={handleSave}>
<div className="mb-4">
<label className="block mb-2">Name</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Address</label>
<input
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Contact</label>
<input
type="text"
value={contact}
onChange={(e) => setContact(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Operating Hours</label>
<input
type="text"
value={hours}
onChange={(e) => setHours(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Save</button>
</form>
</div>
);
}
3. Menu Management
components/MenuManagement.js
// components/MenuManagement.js
import { useState, useEffect } from "react";
import { collection, addDoc, getDocs, doc, deleteDoc, updateDoc } from "firebase/firestore";
import { db, auth } from "../firebase"; // Make sure you have firebase configured
import { useAuthState } from "react-firebase-hooks/auth";
export default function MenuManagement() {
const [user] = useAuthState(auth);
const [menuItems, setMenuItems] = useState([]);
const [newItem, setNewItem] = useState({ name: "", description: "", price: "", imageUrl: "", availability: true });
useEffect(() => {
if (user) {
const fetchMenuItems = async () => {
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setMenuItems(items);
};
fetchMenuItems();
}
}, [user]);
const handleAddItem = async (e) => {
e.preventDefault();
if (user) {
try {
await addDoc(collection(db, "restaurants", user.uid, "menu"), newItem);
setNewItem({ name: "", description: "", price: "", imageUrl: "", availability: true });
// Fetch menu items again to update the list
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setMenuItems(items);
} catch (error) {
console.error("Error adding menu item: ", error);
}
}
};
const handleDeleteItem = async (id) => {
if (user) {
try {
await deleteDoc(doc(db, "restaurants", user.uid, "menu", id));
// Fetch menu items again to update the list
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setMenuItems(items);
} catch (error) {
console.error("Error deleting menu item: ", error);
}
}
};
const handleUpdateItem = async (id, updatedItem) => {
if (user) {
try {
await updateDoc(doc(db, "restaurants", user.uid, "menu", id), updatedItem);
// Fetch menu items again to update the list
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setMenuItems(items);
} catch (error) {
console.error("Error updating menu item: ", error);
}
}
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Menu Management</h2>
<form onSubmit={handleAddItem} className="mb-4">
<div className="mb-4">
<label className="block mb-2">Name</label>
<input
type="text"
value={newItem.name}
onChange={(e) => setNewItem({ ...newItem, name: e.target.value })}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2
">Description</label>
<textarea
value={newItem.description}
onChange={(e) => setNewItem({ ...newItem, description: e.target.value })}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Price</label>
<input
type="text"
value={newItem.price}
onChange={(e) => setNewItem({ ...newItem, price: e.target.value })}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Image URL</label>
<input
type="text"
value={newItem.imageUrl}
onChange={(e) => setNewItem({ ...newItem, imageUrl: e.target.value })}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Availability</label>
<input
type="checkbox"
checked={newItem.availability}
onChange={(e) => setNewItem({ ...newItem, availability: e.target.checked })}
className="mr-2"
/>
Available
</div>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">Add Item</button>
</form>
<div>
{menuItems.map(item => (
<div key={item.id} className="mb-4 p-4 border border-gray-300 rounded">
<h3 className="text-xl font-bold mb-2">{item.name}</h3>
<p>{item.description}</p>
<p className="mb-2">${item.price}</p>
<img src={item.imageUrl} alt={item.name} className="mb-2 w-32 h-32 object-cover" />
<p className="mb-2">Availability: {item.availability ? "Yes" : "No"}</p>
<button onClick={() => handleDeleteItem(item.id)} className="bg-red-500 text-white px-4 py-2 rounded mr-2">Delete</button>
<button onClick={() => handleUpdateItem(item.id, { ...item, availability: !item.availability })} className="bg-green-500 text-white px-4 py-2 rounded">Toggle Availability</button>
</div>
))}
</div>
</div>
);
}
Create Pages
Create the necessary pages in the pages
directory.
pages/restaurant-auth.js
// pages/restaurant-auth.js
import RestaurantAuth from "../components/RestaurantAuth";
export default function RestaurantAuthPage() {
return <RestaurantAuth />;
}
pages/restaurant-profile.js
// pages/restaurant-profile.js
import RestaurantProfile from "../components/RestaurantProfile";
export default function RestaurantProfilePage() {
return <RestaurantProfile />;
}
pages/menu-management.js
// pages/menu-management.js
import MenuManagement from "../components/MenuManagement";
export default function MenuManagementPage() {
return <MenuManagement />;
}
Firebase Configuration
Make sure to configure Firebase in your project. Create a firebase.js
file in your project root:
// firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
Running the App
Run your app:
npm run dev
This setup provides the following functionalities for the Restaurant Section:
- Restaurant Registration/Login: Secure login for restaurant owners.
- Profile Management: Edit restaurant details such as name, address, contact, and operating hours.
- Menu Management: Add, edit, delete menu items, upload images and descriptions, and set prices and availability.
Make sure to add your own Firebase configuration to make these components fully functional. Additionally, you might want to expand the functionality and styling as per your needs.
<label className="block mb-2">Hours of Operation</label>
<input
type="text"
value={hours}
onChange={(e) => setHours(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<button type="submit" className="bg-green-500 text-white px-4 py-2 rounded">Save Profile</button>
</form>
</div>
);
}
3. Menu Management
components/MenuManagement.js
// components/MenuManagement.js
import { useState, useEffect } from "react";
import { collection, addDoc, getDocs, doc, deleteDoc } from "firebase/firestore";
import { db, auth } from "../firebase"; // Make sure you have firebase configured
import { useAuthState } from "react-firebase-hooks/auth";
export default function MenuManagement() {
const [user] = useAuthState(auth);
const [menuItems, setMenuItems] = useState([]);
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [price, setPrice] = useState("");
useEffect(() => {
if (user) {
const fetchMenuItems = async () => {
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = [];
querySnapshot.forEach((doc) => {
items.push({ id: doc.id, ...doc.data() });
});
setMenuItems(items);
};
fetchMenuItems();
}
}, [user]);
const handleAddMenuItem = async (e) => {
e.preventDefault();
if (user) {
try {
await addDoc(collection(db, "restaurants", user.uid, "menu"), {
name,
description,
price: parseFloat(price),
});
setName("");
setDescription("");
setPrice("");
// Refresh menu items
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = [];
querySnapshot.forEach((doc) => {
items.push({ id: doc.id, ...doc.data() });
});
setMenuItems(items);
} catch (error) {
console.error("Error adding menu item: ", error);
}
}
};
const handleDeleteMenuItem = async (id) => {
if (user) {
try {
await deleteDoc(doc(db, "restaurants", user.uid, "menu", id));
// Refresh menu items
const querySnapshot = await getDocs(collection(db, "restaurants", user.uid, "menu"));
const items = [];
querySnapshot.forEach((doc) => {
items.push({ id: doc.id, ...doc.data() });
});
setMenuItems(items);
} catch (error) {
console.error("Error deleting menu item: ", error);
}
}
};
return (
<div className="container mx-auto px-4">
<h2 className="text-2xl font-bold mb-4">Menu Management</h2>
<form onSubmit={handleAddMenuItem} className="mb-4">
<div className="mb-4">
<label className="block mb-2">Name</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Description</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<div className="mb-4">
<label className="block mb-2">Price</label>
<input
type="text"
value={price}
onChange={(e) => setPrice(e.target.value)}
className="p-2 border border-gray-300 rounded w-full"
/>
</div>
<button type="submit" className="bg-green-500 text-white px-4 py-2 rounded">Add Menu Item</button>
</form>
<h3 className="text-xl font-bold mb-4">Current Menu</h3>
<ul>
{menuItems.map((item) => (
<li key={item.id} className="border p-2 mb-2">
<div className="flex justify-between items-center">
<div>
<p className="font-bold">{item.name}</p>
<p>{item.description}</p>
<p>${item.price.toFixed(2)}</p>
</div>
<button
onClick={() => handleDeleteMenuItem(item.id)}
className="bg-red-500 text-white px-4 py-2 rounded"
>
Delete
</button>
</div>
</li>
))}
</ul>
</div>
);
}
4. Create Pages
Create the necessary pages in the pages
directory.
pages/restaurant-auth.js
// pages/restaurant-auth.js
import RestaurantAuth from "../components/RestaurantAuth";
export default function RestaurantAuthPage() {
return <RestaurantAuth />;
}
pages/restaurant-profile.js
// pages/restaurant-profile.js
import RestaurantProfile from "../components/RestaurantProfile";
export default function RestaurantProfilePage() {
return <RestaurantProfile />;
}
pages/menu-management.js
// pages/menu-management.js
import MenuManagement from "../components/MenuManagement";
export default function MenuManagementPage() {
return <MenuManagement />;
}
Running the App
Run your app:
npm run dev
This setup provides the following functionalities:
- Restaurant Registration/Login: Allows restaurants to register and log in using Firebase Authentication.
- Profile Management: Allows restaurants to manage their profile information.
- Menu Management: Allows restaurants to add, view, and delete menu items.
You will need to add your own Firebase configuration to make these components fully functional. Additionally, you might want to expand the functionality and styling as per your needs.
Let me know if you need further customization or additional features!
Sure, let's outline the frontend code for the Rider Section using Next.js and Tailwind CSS.
1. Setup
First, make sure you have a Next.js project setup. You can set up a new project using:
npx create-next-app@latest
cd your-app-name
npm install tailwindcss postcss autoprefixer
npx tailwindcss init -p
Configure your tailwind.config.js
and globals.css
as per the Tailwind CSS documentation.
2. Rider Registration/Login
pages/rider/login.js
import { useState } from 'react';
import axios from 'axios';
export default function RiderLogin() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('/api/auth/login', { email, password });
// Handle successful login
} catch (error) {
console.error('Login error', error);
}
};
return (
<div className="flex items-center justify-center h-screen bg-gray-100">
<form onSubmit={handleSubmit} className="bg-white p-8 rounded shadow-md">
<h2 className="text-2xl mb-6 text-center">Rider Login</h2>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
className="w-full p-2 mb-4 border rounded"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
className="w-full p-2 mb-4 border rounded"
/>
<button type="submit" className="w-full bg-blue-500 text-white py-2 rounded">Login</button>
</form>
</div>
);
}
3. Profile Management
pages/rider/profile.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function RiderProfile() {
const [rider, setRider] = useState({ name: '', email: '', availability: false });
useEffect(() => {
// Fetch rider data from the API
const fetchRider = async () => {
try {
const response = await axios.get('/api/rider/profile');
setRider(response.data);
} catch (error) {
console.error('Error fetching rider data', error);
}
};
fetchRider();
}, []);
const handleSave = async () => {
try {
await axios.put('/api/rider/profile', rider);
// Handle successful save
} catch (error) {
console.error('Error saving rider data', error);
}
};
return (
<div className="flex items-center justify-center h-screen bg-gray-100">
<div className="bg-white p-8 rounded shadow-md">
<h2 className="text-2xl mb-6 text-center">Profile Management</h2>
<input
type="text"
value={rider.name}
onChange={(e) => setRider({ ...rider, name: e.target.value })}
placeholder="Name"
className="w-full p-2 mb-4 border rounded"
/>
<input
type="email"
value={rider.email}
onChange={(e) => setRider({ ...rider, email: e.target.value })}
placeholder="Email"
className="w-full p-2 mb-4 border rounded"
/>
<label className="flex items-center mb-4">
<input
type="checkbox"
checked={rider.availability}
onChange={(e) => setRider({ ...rider, availability: e.target.checked })}
className="mr-2"
/>
Available for deliveries
</label>
<button onClick={handleSave} className="w-full bg-blue-500 text-white py-2 rounded">Save</button>
</div>
</div>
);
}
4. Order Management
pages/rider/orders.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function RiderOrders() {
const [orders, setOrders] = useState([]);
useEffect(() => {
// Fetch orders from the API
const fetchOrders = async () => {
try {
const response = await axios.get('/api/rider/orders');
setOrders(response.data);
} catch (error) {
console.error('Error fetching orders', error);
}
};
fetchOrders();
}, []);
const handleAccept = async (orderId) => {
try {
await axios.post(`/api/rider/orders/${orderId}/accept`);
// Update order status in the UI
} catch (error) {
console.error('Error accepting order', error);
}
};
const handleDecline = async (orderId) => {
try {
await axios.post(`/api/rider/orders/${orderId}/decline`);
// Update order status in the UI
} catch (error) {
console.error('Error declining order', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Order Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-2xl">
{orders.map(order => (
<div key={order.id} className="mb-4 border-b pb-4">
<p className="mb-2"><strong>Order ID:</strong> {order.id}</p>
<p className="mb-2"><strong>Pickup Location:</strong> {order.pickupLocation}</p>
<button onClick={() => handleAccept(order.id)} className="mr-2 bg-green-500 text-white py-1 px-4 rounded">Accept</button>
<button onClick={() => handleDecline(order.id)} className="bg-red-500 text-white py-1 px-4 rounded">Decline</button>
</div>
))}
</div>
</div>
);
}
API Routes
You need to define API routes to handle these requests. For simplicity, I'm assuming your API endpoints are similar to what we've used in the above code. You can define them in pages/api
.
pages/api/auth/login.js
import { NextApiRequest, NextApiResponse } from 'next';
import { loginRider } from '../../../services/authService'; // Assume you have this service implemented
export default async (req = NextApiRequest, res = NextApiResponse) => {
const { email, password } = req.body;
try {
const rider = await loginRider(email, password);
res.status(200).json(rider);
} catch (error) {
res.status(400).json({ error: 'Login failed' });
}
};
pages/api/rider/profile.js
import { NextApiRequest, NextApiResponse } from 'next';
import { getRiderProfile, updateRiderProfile } from '../../../services/riderService'; // Assume you have this service implemented
export default async (req = NextApiRequest, res = NextApiResponse) => {
if (req.method === 'GET') {
try {
const rider = await getRiderProfile(req.user.id); // Assume you have middleware to attach user to req
res.status(200).json(rider);
} catch (error) {
res.status(400).json({ error: 'Failed to fetch profile' });
}
} else if (req.method === 'PUT') {
try {
const updatedRider = await updateRiderProfile(req.user.id, req.body);
res.status(200).json(updatedRider);
} catch (error) {
res.status(400).json({ error: 'Failed to update profile' });
}
}
};
pages/api/rider/orders.js
import { NextApiRequest, NextApiResponse } from 'next';
import { getRiderOrders } from '../../../services/orderService'; // Assume you have this service implemented
export default async (req = NextApiRequest, res = NextApiResponse) => {
try {
const orders = await getRiderOrders(req.user.id); // Assume you have middleware to attach user to req
res.status(200).json(orders);
} catch (error) {
res.status(400).json({ error: 'Failed to fetch orders' });
}
};
Conclusion
This code outlines the essential parts for the rider section of your food delivery app. Make sure to implement the necessary backend services and authentication middleware to handle requests properly. This code can serve as a good starting point for building out more complex features and ensuring a secure and functional rider interface.
Sure! Here is an extended implementation for the additional functionalities of Real-Time Navigation, Order Tracking, Earnings and Payouts, Notifications, and In-App Chat using Next.js and Tailwind CSS.
1. Real-Time Navigation
pages/rider/navigation.js
For Google Maps integration, you need to install @react-google-maps/api
.
npm install @react-google-maps/api
import { GoogleMap, useJsApiLoader, Marker } from '@react-google-maps/api';
import { useState } from 'react';
const containerStyle = {
width: '100%',
height: '400px'
};
const center = {
lat: -3.745,
lng: -38.523
};
export default function Navigation() {
const { isLoaded } = useJsApiLoader({
id: 'google-map-script',
googleMapsApiKey: 'YOUR_GOOGLE_MAPS_API_KEY'
});
const [map, setMap] = useState(null);
const onLoad = (map) => {
setMap(map);
};
const onUnmount = () => {
setMap(null);
};
return isLoaded ? (
<div className="flex items-center justify-center h-screen bg-gray-100">
<div className="w-full max-w-2xl p-4 bg-white rounded shadow-md">
<h2 className="text-2xl mb-4 text-center">Real-Time Navigation</h2>
<GoogleMap
mapContainerStyle={containerStyle}
center={center}
zoom={10}
onLoad={onLoad}
onUnmount={onUnmount}
>
<Marker position={center} />
</GoogleMap>
</div>
</div>
) : <></>;
}
2. Order Tracking
pages/rider/orderTracking.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function OrderTracking() {
const [orders, setOrders] = useState([]);
useEffect(() => {
const fetchOrders = async () => {
try {
const response = await axios.get('/api/rider/orders');
setOrders(response.data);
} catch (error) {
console.error('Error fetching orders', error);
}
};
fetchOrders();
}, []);
const updateStatus = async (orderId, status) => {
try {
await axios.put(`/api/rider/orders/${orderId}/status`, { status });
setOrders(orders.map(order => order.id === orderId ? { ...order, status } : order));
} catch (error) {
console.error('Error updating status', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Order Tracking</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-2xl">
{orders.map(order => (
<div key={order.id} className="mb-4 border-b pb-4">
<p className="mb-2"><strong>Order ID:</strong> {order.id}</p>
<p className="mb-2"><strong>Status:</strong> {order.status}</p>
<button onClick={() => updateStatus(order.id, 'Picked Up')} className="mr-2 bg-green-500 text-white py-1 px-4 rounded">Picked Up</button>
<button onClick={() => updateStatus(order.id, 'On the Way')} className="mr-2 bg-blue-500 text-white py-1 px-4 rounded">On the Way</button>
<button onClick={() => updateStatus(order.id, 'Delivered')} className="bg-gray-500 text-white py-1 px-4 rounded">Delivered</button>
</div>
))}
</div>
</div>
);
}
3. Earnings and Payouts
pages/rider/earnings.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function Earnings() {
const [earnings, setEarnings] = useState([]);
useEffect(() => {
const fetchEarnings = async () => {
try {
const response = await axios.get('/api/rider/earnings');
setEarnings(response.data);
} catch (error) {
console.error('Error fetching earnings', error);
}
};
fetchEarnings();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Earnings and Payouts</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-2xl">
{earnings.map(earning => (
<div key={earning.id} className="mb-4 border-b pb-4">
<p className="mb-2"><strong>Date:</strong> {earning.date}</p>
<p className="mb-2"><strong>Amount:</strong> ${earning.amount}</p>
</div>
))}
</div>
</div>
);
}
4. Notifications
pages/rider/notifications.js
For push notifications, you might use a service like Firebase Cloud Messaging (FCM). However, for simplicity, we will mock the notifications.
import { useState, useEffect } from 'react';
export default function Notifications() {
const [notifications, setNotifications] = useState([]);
useEffect(() => {
// Mocking fetching notifications from an API
const mockNotifications = [
{ id: 1, message: 'New delivery request available' },
{ id: 2, message: 'Order #1234 has been updated' },
];
setNotifications(mockNotifications);
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Notifications</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-2xl">
{notifications.map(notification => (
<div key={notification.id} className="mb-4 border-b pb-4">
<p className="mb-2">{notification.message}</p>
</div>
))}
</div>
</div>
);
}
5. In-App Chat
For in-app chat, you can use libraries like Socket.IO or Firebase. Here we use a simple mock chat with a state.
pages/rider/chat.js
import { useState } from 'react';
export default function Chat() {
const [messages, setMessages] = useState([
{ id: 1, sender: 'Customer', text: 'Hello, where is my order?' },
{ id: 2, sender: 'Rider', text: 'I am on the way.' },
]);
const [newMessage, setNewMessage] = useState('');
const handleSend = () => {
setMessages([...messages, { id: Date.now(), sender: 'Rider', text: newMessage }]);
setNewMessage('');
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">In-App Chat</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-2xl">
<div className="mb-4">
{messages.map(message => (
<div key={message.id} className={`mb-2 ${message.sender === 'Rider' ? 'text-right' : 'text-left'}`}>
<p className="px-4 py-2 inline-block rounded bg-gray-200">{message.text}</p>
</div>
))}
</div>
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Type a message..."
className="w-full p-2 mb-4 border rounded"
/>
<button onClick={handleSend} className="w-full bg-blue-500 text-white py-2 rounded">Send</button>
</div>
</div>
);
}
Conclusion
The above code snippets cover the essential parts for additional functionalities in your rider section. Make sure to implement the necessary backend services to handle requests properly. This code can serve as a starting point for building out more complex features and ensuring a secure and functional rider interface. For real-time features like notifications and chat, you may want to integrate WebSocket or Firebase for real-time communication.
Sure! Below is the implementation of the Admin section with functionalities for the Dashboard, User Management, and Restaurant Management using Next.js and Tailwind CSS.
1. Dashboard
pages/admin/dashboard.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function Dashboard() {
const [stats, setStats] = useState({
activeUsers: 0,
totalOrders: 0,
totalRevenue: 0,
});
useEffect(() => {
const fetchStats = async () => {
try {
const response = await axios.get('/api/admin/stats');
setStats(response.data);
} catch (error) {
console.error('Error fetching stats', error);
}
};
fetchStats();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Admin Dashboard</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-blue-100 p-4 rounded">
<h3 className="text-xl">Active Users</h3>
<p className="text-3xl">{stats.activeUsers}</p>
</div>
<div className="bg-green-100 p-4 rounded">
<h3 className="text-xl">Total Orders</h3>
<p className="text-3xl">{stats.totalOrders}</p>
</div>
<div className="bg-yellow-100 p-4 rounded">
<h3 className="text-xl">Total Revenue</h3>
<p className="text-3xl">${stats.totalRevenue}</p>
</div>
</div>
</div>
</div>
);
}
2. User Management
pages/admin/users.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function UserManagement() {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('/api/admin/users');
setUsers(response.data);
} catch (error) {
console.error('Error fetching users', error);
}
};
fetchUsers();
}, []);
const deleteUser = async (userId) => {
try {
await axios.delete(`/api/admin/users/${userId}`);
setUsers(users.filter(user => user.id !== userId));
} catch (error) {
console.error('Error deleting user', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">User Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
<th className="px-6 py-3"></th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{users.map(user => (
<tr key={user.id}>
<td className="px-6 py-4 whitespace-nowrap">{user.name}</td>
<td className="px-6 py-4 whitespace-nowrap">{user.email}</td>
<td className="px-6 py-4 whitespace-nowrap">{user.role}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button onClick={() => deleteUser(user.id)} className="text-red-600 hover:text-red-900">Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
3. Restaurant Management
pages/admin/restaurants.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function RestaurantManagement() {
const [restaurants, setRestaurants] = useState([]);
useEffect(() => {
const fetchRestaurants = async () => {
try {
const response = await axios.get('/api/admin/restaurants');
setRestaurants(response.data);
} catch (error) {
console.error('Error fetching restaurants', error);
}
};
fetchRestaurants();
}, []);
const approveRestaurant = async (restaurantId) => {
try {
await axios.put(`/api/admin/restaurants/${restaurantId}/approve`);
setRestaurants(restaurants.map(restaurant => restaurant.id === restaurantId ? { ...restaurant, status: 'Approved' } : restaurant));
} catch (error) {
console.error('Error approving restaurant', error);
}
};
const declineRestaurant = async (restaurantId) => {
try {
await axios.put(`/api/admin/restaurants/${restaurantId}/decline`);
setRestaurants(restaurants.map(restaurant => restaurant.id === restaurantId ? { ...restaurant, status: 'Declined' } : restaurant));
} catch (error) {
console.error('Error declining restaurant', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Restaurant Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Owner</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th className="px-6 py-3"></th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{restaurants.map(restaurant => (
<tr key={restaurant.id}>
<td className="px-6 py-4 whitespace-nowrap">{restaurant.name}</td>
<td className="px-6 py-4 whitespace-nowrap">{restaurant.owner}</td>
<td className="px-6 py-4 whitespace-nowrap">{restaurant.status}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
{restaurant.status === 'Pending' && (
<>
<button onClick={() => approveRestaurant(restaurant.id)} className="text-green-600 hover:text-green-900 mr-4">Approve</button>
<button onClick={() => declineRestaurant(restaurant.id)} className="text-red-600 hover:text-red-900">Decline</button>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Conclusion
The above code snippets cover the essential parts for the Admin section including the Dashboard, User Management, and Restaurant Management. Make sure to implement the necessary backend services to handle requests properly. This code can serve as a starting point for building out more complex features and ensuring a secure and functional admin interface.
Rider Management
pages/admin/riders.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function RiderManagement() {
const [riders, setRiders] = useState([]);
useEffect(() => {
const fetchRiders = async () => {
try {
const response = await axios.get('/api/admin/riders');
setRiders(response.data);
} catch (error) {
console.error('Error fetching riders', error);
}
};
fetchRiders();
}, []);
const approveRider = async (riderId) => {
try {
await axios.put(`/api/admin/riders/${riderId}/approve`);
setRiders(riders.map(rider => rider.id === riderId ? { ...rider, status: 'Approved' } : rider));
} catch (error) {
console.error('Error approving rider', error);
}
};
const declineRider = async (riderId) => {
try {
await axios.put(`/api/admin/riders/${riderId}/decline`);
setRiders(riders.map(rider => rider.id === riderId ? { ...rider, status: 'Declined' } : rider));
} catch (error) {
console.error('Error declining rider', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Rider Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th className="px-6 py-3"></th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{riders.map(rider => (
<tr key={rider.id}>
<td className="px-6 py-4 whitespace-nowrap">{rider.name}</td>
<td className="px-6 py-4 whitespace-nowrap">{rider.email}</td>
<td className="px-6 py-4 whitespace-nowrap">{rider.status}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
{rider.status === 'Pending' && (
<>
<button onClick={() => approveRider(rider.id)} className="text-green-600 hover:text-green-900 mr-4">Approve</button>
<button onClick={() => declineRider(rider.id)} className="text-red-600 hover:text-red-900">Decline</button>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Order Management
pages/admin/orders.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function OrderManagement() {
const [orders, setOrders] = useState([]);
useEffect(() => {
const fetchOrders = async () => {
try {
const response = await axios.get('/api/admin/orders');
setOrders(response.data);
} catch (error) {
console.error('Error fetching orders', error);
}
};
fetchOrders();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Order Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Order ID</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Customer</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Restaurant</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{orders.map(order => (
<tr key={order.id}>
<td className="px-6 py-4 whitespace-nowrap">{order.id}</td>
<td className="px-6 py-4 whitespace-nowrap">{order.customer}</td>
<td className="px-6 py-4 whitespace-nowrap">{order.restaurant}</td>
<td className="px-6 py-4 whitespace-nowrap">{order.status}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Financial Management
pages/admin/financials.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function FinancialManagement() {
const [financials, setFinancials] = useState([]);
useEffect(() => {
const fetchFinancials = async () => {
try {
const response = await axios.get('/api/admin/financials');
setFinancials(response.data);
} catch (error) {
console.error('Error fetching financials', error);
}
};
fetchFinancials();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Financial Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Transaction ID</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{financials.map(financial => (
<tr key={financial.id}>
<td className="px-6 py-4 whitespace-nowrap">{financial.id}</td>
<td className="px-6 py-4 whitespace-nowrap">{financial.user}</td>
<td className="px-6 py-4 whitespace-nowrap">{financial.amount}</td>
<td className="px-6 py-4 whitespace-nowrap">{financial.status}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Promotions and Marketing
pages/admin/promotions.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function PromotionsManagement() {
const [promotions, setPromotions] = useState([]);
useEffect(() => {
const fetchPromotions = async () => {
try {
const response = await axios.get('/api/admin/promotions');
setPromotions(response.data);
} catch (error) {
console.error('Error fetching promotions', error);
}
};
fetchPromotions();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Promotions Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Promotion ID</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{promotions.map(promotion => (
<tr key={promotion.id}>
<td className="px-6 py-
4 whitespace-nowrap">{promotion.id}</td>
<td className="px-6 py-4 whitespace-nowrap">{promotion.description}</td>
<td className="px-6 py-4 whitespace-nowrap">{promotion.status}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Support and Feedback
pages/admin/support.js
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function SupportFeedbackManagement() {
const [complaints, setComplaints] = useState([]);
useEffect(() => {
const fetchComplaints = async () => {
try {
const response = await axios.get('/api/admin/complaints');
setComplaints(response.data);
} catch (error) {
console.error('Error fetching complaints', error);
}
};
fetchComplaints();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Support and Feedback Management</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-4xl">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Complaint ID</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{complaints.map(complaint => (
<tr key={complaint.id}>
<td className="px-6 py-4 whitespace-nowrap">{complaint.id}</td>
<td className="px-6 py-4 whitespace-nowrap">{complaint.user}</td>
<td className="px-6 py-4 whitespace-nowrap">{complaint.description}</td>
<td className="px-6 py-4 whitespace-nowrap">{complaint.status}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
Conclusion
The above code snippets cover essential functionalities for Rider Management, Order Management, Financial Management, Promotions and Marketing, and Support and Feedback within the Admin section. Each component handles displaying data fetched from the backend and provides basic functionality such as approving/declining riders and managing orders. Ensure your backend services are properly set up to handle these requests for a fully functional admin interface.
Technical Integration
Google Maps Integration
pages/rider/map.js
import { useEffect, useRef } from 'react';
export default function RiderMap() {
const mapRef = useRef(null);
useEffect(() => {
const loadGoogleMaps = async () => {
if (window.google) return;
const googleMapsScript = document.createElement('script');
googleMapsScript.src = `https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_MAPS_API_KEY&libraries=places`;
window.document.head.appendChild(googleMapsScript);
googleMapsScript.addEventListener('load', () => {
const map = new window.google.maps.Map(mapRef.current, {
center: { lat: 23.8103, lng: 90.4125 }, // Example coordinates (Dhaka, Bangladesh)
zoom: 12,
});
const marker = new window.google.maps.Marker({
position: { lat: 23.8103, lng: 90.4125 },
map: map,
title: 'Rider Location',
});
});
};
loadGoogleMaps();
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Rider Real-Time Location</h2>
<div ref={mapRef} className="w-full h-full max-w-4xl bg-gray-200" style={{ height: '500px' }}></div>
</div>
);
}
Payment Gateway Integration
pages/payment.js
import { useState } from 'react';
import axios from 'axios';
export default function Payment() {
const [amount, setAmount] = useState('');
const handlePayment = async () => {
try {
const response = await axios.post('/api/payment', { amount });
// Handle response for payment processing
console.log(response.data);
} catch (error) {
console.error('Error processing payment', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Payment Gateway</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-md">
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
className="w-full px-3 py-2 mb-4 border border-gray-300 rounded"
placeholder="Enter amount"
/>
<button onClick={handlePayment} className="w-full px-4 py-2 bg-blue-500 text-white rounded">
Pay Now
</button>
</div>
</div>
);
}
Notification Service
pages/notifications.js
import { useEffect } from 'react';
import { initFirebase, onMessageListener } from '../utils/firebase'; // Assume you have Firebase setup
export default function Notifications() {
useEffect(() => {
initFirebase();
onMessageListener()
.then(payload => {
console.log('Notification received: ', payload);
alert(payload.notification.title);
})
.catch(err => console.log('Failed to receive notifications: ', err));
}, []);
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">Notification Service</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-md">
<p>Real-time notifications will appear here.</p>
</div>
</div>
);
}
Chat System Integration
pages/chat.js
import { useState } from 'react';
import axios from 'axios';
export default function Chat() {
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
const sendMessage = async () => {
try {
const response = await axios.post('/api/chat', { message });
setMessages([...messages, response.data]);
setMessage('');
} catch (error) {
console.error('Error sending message', error);
}
};
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h2 className="text-2xl mb-6">In-App Chat</h2>
<div className="bg-white p-8 rounded shadow-md w-full max-w-md">
<div className="mb-4">
{messages.map((msg, index) => (
<div key={index} className="mb-2">
<span className="font-semibold">{msg.sender}:</span> {msg.text}
</div>
))}
</div>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="w-full px-3 py-2 mb-4 border border-gray-300 rounded"
placeholder="Type your message"
/>
<button onClick={sendMessage} className="w-full px-4 py-2 bg-blue-500 text-white rounded">
Send
</button>
</div>
</div>
);
}
Conclusion
The above code snippets provide a basic implementation of Google Maps integration, payment gateway integration, notification service, and chat system integration using Next.js and Tailwind CSS. Ensure you have the corresponding backend endpoints and services set up for full functionality. For the Google Maps API, replace YOUR_GOOGLE_MAPS_API_KEY
with your actual API key. For the notification service, configure Firebase accordingly.
To create the User Section functionalities with NestJS and MongoDB, follow these steps:
- Set up the NestJS project:
npm install -g @nestjs/cli
nest new food-delivery-app
cd food-delivery-app
npm install @nestjs/mongoose mongoose
- Create User Module:
nest g module user
nest g service user
nest g controller user
- Define User Schema:
Create a file user.schema.ts
in the src/user
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type UserDocument = User & Document;
@Schema()
export class User {
@Prop({ required: true })
email: string;
@Prop({ required: true })
password: string;
@Prop()
name: string;
@Prop()
phone: string;
@Prop([String])
paymentMethods: string[];
@Prop([String])
orderHistory: string[];
}
export const UserSchema = SchemaFactory.createForClass(User);
- Create User DTOs:
Create a file create-user.dto.ts
in the src/user
directory:
export class CreateUserDto {
readonly email: string;
readonly password: string;
readonly name?: string;
readonly phone?: string;
}
Create a file update-user.dto.ts
in the src/user
directory:
export class UpdateUserDto {
readonly email?: string;
readonly password?: string;
readonly name?: string;
readonly phone?: string;
readonly paymentMethods?: string[];
readonly orderHistory?: string[];
}
- Implement User Service:
Modify user.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from './user.schema';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UserService {
constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const createdUser = new this.userModel(createUserDto);
return createdUser.save();
}
async findAll(): Promise<User[]> {
return this.userModel.find().exec();
}
async findOne(id: string): Promise<User> {
return this.userModel.findById(id).exec();
}
async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
return this.userModel.findByIdAndUpdate(id, updateUserDto, { new: true }).exec();
}
async delete(id: string): Promise<User> {
return this.userModel.findByIdAndDelete(id).exec();
}
}
- Implement User Controller:
Modify user.controller.ts
:
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
async create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get()
async findAll() {
return this.userService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.userService.findOne(id);
}
@Put(':id')
async update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(id, updateUserDto);
}
@Delete(':id')
async delete(@Param('id') id: string) {
return this.userService.delete(id);
}
}
- Integrate Mongoose with NestJS:
Modify app.module.ts
to include Mongoose and the User module:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserModule } from './user/user.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
UserModule,
],
})
export class AppModule {}
Update user.module.ts
to include Mongoose schema:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User, UserSchema } from './user.schema';
@Module({
imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
providers: [UserService],
controllers: [UserController],
})
export class UserModule {}
This setup covers the basic backend for user registration, profile management, and browsing functionalities. You can further extend this by adding authentication (e.g., JWT), enhancing error handling, and implementing other functionalities as needed.
Let's extend the backend functionalities for filtering restaurants, viewing restaurant details, placing orders, and handling checkout and payment in the food delivery app.
1. Restaurant Module
First, create the Restaurant module:
nest g module restaurant
nest g service restaurant
nest g controller restaurant
Restaurant Schema
Create restaurant.schema.ts
in the src/restaurant
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type RestaurantDocument = Restaurant & Document;
@Schema()
export class Restaurant {
@Prop({ required: true })
name: string;
@Prop({ required: true })
cuisine: string;
@Prop()
rating: number;
@Prop()
distance: number;
@Prop()
priceRange: string;
@Prop([String])
menu: string[];
@Prop()
details: string;
}
export const RestaurantSchema = SchemaFactory.createForClass(Restaurant);
Restaurant DTOs
Create create-restaurant.dto.ts
in the src/restaurant
directory:
export class CreateRestaurantDto {
readonly name: string;
readonly cuisine: string;
readonly rating?: number;
readonly distance?: number;
readonly priceRange?: string;
readonly menu?: string[];
readonly details?: string;
}
Restaurant Service
Modify restaurant.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Restaurant, RestaurantDocument } from './restaurant.schema';
import { CreateRestaurantDto } from './dto/create-restaurant.dto';
@Injectable()
export class RestaurantService {
constructor(@InjectModel(Restaurant.name) private restaurantModel: Model<RestaurantDocument>) {}
async create(createRestaurantDto: CreateRestaurantDto): Promise<Restaurant> {
const createdRestaurant = new this.restaurantModel(createRestaurantDto);
return createdRestaurant.save();
}
async findAll(): Promise<Restaurant[]> {
return this.restaurantModel.find().exec();
}
async findOne(id: string): Promise<Restaurant> {
return this.restaurantModel.findById(id).exec();
}
async filterBy(criteria: any): Promise<Restaurant[]> {
return this.restaurantModel.find(criteria).exec();
}
}
Restaurant Controller
Modify restaurant.controller.ts
:
import { Controller, Get, Post, Body, Param, Query } from '@nestjs/common';
import { RestaurantService } from './restaurant.service';
import { CreateRestaurantDto } from './dto/create-restaurant.dto';
@Controller('restaurants')
export class RestaurantController {
constructor(private readonly restaurantService: RestaurantService) {}
@Post()
async create(@Body() createRestaurantDto: CreateRestaurantDto) {
return this.restaurantService.create(createRestaurantDto);
}
@Get()
async findAll() {
return this.restaurantService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.restaurantService.findOne(id);
}
@Get('filter')
async filterBy(@Query() query: any) {
return this.restaurantService.filterBy(query);
}
}
2. Order Module
Next, create the Order module:
nest g module order
nest g service order
nest g controller order
Order Schema
Create order.schema.ts
in the src/order
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type OrderDocument = Order & Document;
@Schema()
export class Order {
@Prop({ required: true })
userId: string;
@Prop({ required: true })
restaurantId: string;
@Prop([{ itemId: String, quantity: Number }])
items: { itemId: string, quantity: number }[];
@Prop({ required: true })
totalPrice: number;
@Prop()
promoCode?: string;
@Prop({ required: true })
deliveryType: string;
@Prop()
scheduleTime?: Date;
@Prop({ default: 'Pending' })
status: string;
}
export const OrderSchema = SchemaFactory.createForClass(Order);
Order DTOs
Create create-order.dto.ts
in the src/order
directory:
export class CreateOrderDto {
readonly userId: string;
readonly restaurantId: string;
readonly items: { itemId: string, quantity: number }[];
readonly totalPrice: number;
readonly promoCode?: string;
readonly deliveryType: string;
readonly scheduleTime?: Date;
}
Order Service
Modify order.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Order, OrderDocument } from './order.schema';
import { CreateOrderDto } from './dto/create-order.dto';
@Injectable()
export class OrderService {
constructor(@InjectModel(Order.name) private orderModel: Model<OrderDocument>) {}
async create(createOrderDto: CreateOrderDto): Promise<Order> {
const createdOrder = new this.orderModel(createOrderDto);
return createdOrder.save();
}
async findAll(): Promise<Order[]> {
return this.orderModel.find().exec();
}
async findOne(id: string): Promise<Order> {
return this.orderModel.findById(id).exec();
}
async updateStatus(id: string, status: string): Promise<Order> {
return this.orderModel.findByIdAndUpdate(id, { status }, { new: true }).exec();
}
}
Order Controller
Modify order.controller.ts
:
import { Controller, Get, Post, Body, Param, Put } from '@nestjs/common';
import { OrderService } from './order.service';
import { CreateOrderDto } from './dto/create-order.dto';
@Controller('orders')
export class OrderController {
constructor(private readonly orderService: OrderService) {}
@Post()
async create(@Body() createOrderDto: CreateOrderDto) {
return this.orderService.create(createOrderDto);
}
@Get()
async findAll() {
return this.orderService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.orderService.findOne(id);
}
@Put(':id/status')
async updateStatus(@Param('id') id: string, @Body('status') status: string) {
return this.orderService.updateStatus(id, status);
}
}
3. Payment Integration
Implementing a payment gateway requires integrating with a third-party payment provider like Stripe, PayPal, etc. Here, we'll provide a basic structure to handle payments. For real-world applications, refer to the provider's official documentation.
Install Stripe SDK
npm install stripe
Payment Service
Create a payment.service.ts
in the src/payment
directory:
import { Injectable } from '@nestjs/common';
import Stripe from 'stripe';
@Injectable()
export class PaymentService {
private stripe: Stripe;
constructor() {
this.stripe = new Stripe('your-stripe-secret-key', {
apiVersion: '2020-08-27',
});
}
async createPaymentIntent(amount: number, currency: string): Promise<Stripe.PaymentIntent> {
return this.stripe.paymentIntents.create({
amount,
currency,
});
}
}
Payment Controller
Create a payment.controller.ts
in the src/payment
directory:
import { Controller, Post, Body } from '@nestjs/common';
import { PaymentService } from './payment.service';
@Controller('payment')
export class PaymentController {
constructor(private readonly paymentService: PaymentService) {}
@Post('create-intent')
async createPaymentIntent(@Body('amount') amount: number, @Body('currency') currency: string) {
return this.paymentService.createPaymentIntent(amount, currency);
}
}
4. Integrate Modules in App Module
Modify app.module.ts
to include the newly created modules:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserModule } from './user/user.module';
import { RestaurantModule } from './restaurant/restaurant.module';
import { OrderModule } from './order/order.module';
import { PaymentModule } from './payment/payment.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
UserModule,
RestaurantModule,
OrderModule,
PaymentModule,
],
})
export class AppModule {}
This code provides a comprehensive backend setup for the key functionalities of a food delivery app, including user management, restaurant filtering, order placement, and payment handling using NestJS and MongoDB. Remember to handle authentication, error handling, and validations as needed in a production environment.
To implement real-time order tracking, notifications, ratings and reviews, and customer support functionalities with NestJS and MongoDB, we need to integrate WebSockets for real-time tracking, a push notification service, and a chat system. Here’s how you can achieve these functionalities:
Real-Time Order Tracking
Install Required Packages
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io mongoose
Create WebSocket Gateway
Create a file order.gateway.ts
in the src/order
directory:
import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway()
export class OrderGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('updateOrderStatus')
handleUpdateOrderStatus(@MessageBody() data: { orderId: string, status: string }) {
this.server.emit('orderStatusUpdated', data);
}
@SubscribeMessage('updateRiderLocation')
handleUpdateRiderLocation(@MessageBody() data: { orderId: string, location: { lat: number, lng: number } }) {
this.server.emit('riderLocationUpdated', data);
}
}
Integrate Gateway in Order Module
Modify order.module.ts
:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { OrderService } from './order.service';
import { OrderController } from './order.controller';
import { Order, OrderSchema } from './order.schema';
import { OrderGateway } from './order.gateway';
@Module({
imports: [MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }])],
providers: [OrderService, OrderGateway],
controllers: [OrderController],
})
export class OrderModule {}
Notifications
To handle push notifications, you can use Firebase Cloud Messaging (FCM). Here’s a basic implementation:
Install Firebase Admin SDK
npm install firebase-admin
Initialize Firebase
Create a file firebase.config.ts
in the src/common
directory:
import * as admin from 'firebase-admin';
import * as serviceAccount from './path-to-service-account-file.json';
admin.initializeApp({
credential: admin.credential.cert(serviceAccount as admin.ServiceAccount),
});
export const messaging = admin.messaging();
Notification Service
Create a file notification.service.ts
in the src/notification
directory:
import { Injectable } from '@nestjs/common';
import { messaging } from '../common/firebase.config';
@Injectable()
export class NotificationService {
async sendNotification(token: string, title: string, body: string): Promise<void> {
const message = {
notification: {
title,
body,
},
token,
};
await messaging.send(message);
}
}
Ratings and Reviews
Create Rating Schema
Create a file rating.schema.ts
in the src/rating
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type RatingDocument = Rating & Document;
@Schema()
export class Rating {
@Prop({ required: true })
userId: string;
@Prop({ required: true })
restaurantId: string;
@Prop({ required: true })
rating: number;
@Prop()
review: string;
}
export const RatingSchema = SchemaFactory.createForClass(Rating);
Create Rating DTOs
Create create-rating.dto.ts
in the src/rating
directory:
export class CreateRatingDto {
readonly userId: string;
readonly restaurantId: string;
readonly rating: number;
readonly review?: string;
}
Rating Service
Create rating.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Rating, RatingDocument } from './rating.schema';
import { CreateRatingDto } from './dto/create-rating.dto';
@Injectable()
export class RatingService {
constructor(@InjectModel(Rating.name) private ratingModel: Model<RatingDocument>) {}
async create(createRatingDto: CreateRatingDto): Promise<Rating> {
const createdRating = new this.ratingModel(createRatingDto);
return createdRating.save();
}
async findAll(): Promise<Rating[]> {
return this.ratingModel.find().exec();
}
async findByRestaurant(restaurantId: string): Promise<Rating[]> {
return this.ratingModel.find({ restaurantId }).exec();
}
}
Rating Controller
Create rating.controller.ts
:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { RatingService } from './rating.service';
import { CreateRatingDto } from './dto/create-rating.dto';
@Controller('ratings')
export class RatingController {
constructor(private readonly ratingService: RatingService) {}
@Post()
async create(@Body() createRatingDto: CreateRatingDto) {
return this.ratingService.create(createRatingDto);
}
@Get()
async findAll() {
return this.ratingService.findAll();
}
@Get('restaurant/:id')
async findByRestaurant(@Param('id') restaurantId: string) {
return this.ratingService.findByRestaurant(restaurantId);
}
}
Customer Support
Create Chat Schema
Create chat.schema.ts
in the src/chat
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type ChatDocument = Chat & Document;
@Schema()
export class Chat {
@Prop({ required: true })
senderId: string;
@Prop({ required: true })
receiverId: string;
@Prop({ required: true })
message: string;
@Prop({ default: Date.now })
timestamp: Date;
}
export const ChatSchema = SchemaFactory.createForClass(Chat);
Create Chat DTOs
Create create-chat.dto.ts
in the src/chat
directory:
export class CreateChatDto {
readonly senderId: string;
readonly receiverId: string;
readonly message: string;
}
Chat Service
Create chat.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Chat, ChatDocument } from './chat.schema';
import { CreateChatDto } from './dto/create-chat.dto';
@Injectable()
export class ChatService {
constructor(@InjectModel(Chat.name) private chatModel: Model<ChatDocument>) {}
async create(createChatDto: CreateChatDto): Promise<Chat> {
const createdChat = new this.chatModel(createChatDto);
return createdChat.save();
}
async findAll(): Promise<Chat[]> {
return this.chatModel.find().exec();
}
async findByUser(userId: string): Promise<Chat[]> {
return this.chatModel.find({ $or: [{ senderId: userId }, { receiverId: userId }] }).exec();
}
}
Chat Controller
Create chat.controller.ts
:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ChatService } from './chat.service';
import { CreateChatDto } from './dto/create-chat.dto';
@Controller('chats')
export class ChatController {
constructor(private readonly chatService: ChatService) {}
@Post()
async create(@Body() createChatDto: CreateChatDto) {
return this.chatService.create(createChatDto);
}
@Get()
async findAll() {
return this.chatService.findAll();
}
@Get('user/:id')
async findByUser(@Param('id') userId: string) {
return this.chatService.findByUser(userId);
}
}
Integrate Modules in App Module
Modify app.module.ts
to include the newly created modules:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserModule } from './user/user.module';
import { RestaurantModule } from './restaurant/restaurant.module';
import { OrderModule } from './order/order.module';
import { PaymentModule } from './payment/payment.module';
import { NotificationModule } from './notification/notification.module';
import { RatingModule } from './rating/rating.module';
import { ChatModule } from './chat/chat.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
UserModule,
RestaurantModule,
OrderModule,
PaymentModule,
NotificationModule,
RatingModule,
ChatModule,
],
})
export class AppModule {}
This code provides a comprehensive backend setup for the functionalities you requested, including real-time order tracking, notifications, ratings and reviews, and customer support using NestJS and MongoDB.
To implement the Restaurant Section functionalities, we will create a module that handles restaurant registration, login, profile management, and menu management. We'll use NestJS with MongoDB.
Install Required Packages
If you haven't already, install the necessary packages:
npm install @nestjs/mongoose mongoose bcryptjs @nestjs/jwt
Create the Restaurant Module
First, generate the module, service, and controller:
nest g module restaurant
nest g service restaurant
nest g controller restaurant
Restaurant Schema
Create restaurant.schema.ts
in the src/restaurant
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type RestaurantDocument = Restaurant & Document;
@Schema()
export class Restaurant {
@Prop({ required: true })
name: string;
@Prop({ required: true })
address: string;
@Prop({ required: true })
contact: string;
@Prop()
operatingHours: string;
@Prop({ required: true, unique: true })
email: string;
@Prop({ required: true })
password: string;
@Prop([{ name: String, description: String, price: Number, availability: Boolean, image: String }])
menu: { name: string, description: string, price: number, availability: boolean, image: string }[];
}
export const RestaurantSchema = SchemaFactory.createForClass(Restaurant);
Restaurant DTOs
Create the DTOs for restaurant registration and profile management:
create-restaurant.dto.ts
export class CreateRestaurantDto {
readonly name: string;
readonly address: string;
readonly contact: string;
readonly email: string;
readonly password: string;
}
update-restaurant.dto.ts
export class UpdateRestaurantDto {
readonly name?: string;
readonly address?: string;
readonly contact?: string;
readonly operatingHours?: string;
}
create-menu-item.dto.ts
export class CreateMenuItemDto {
readonly name: string;
readonly description: string;
readonly price: number;
readonly availability: boolean;
readonly image: string;
}
Restaurant Service
Modify restaurant.service.ts
to include methods for registration, login, profile management, and menu management:
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Restaurant, RestaurantDocument } from './restaurant.schema';
import { CreateRestaurantDto } from './dto/create-restaurant.dto';
import { UpdateRestaurantDto } from './dto/update-restaurant.dto';
import { CreateMenuItemDto } from './dto/create-menu-item.dto';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class RestaurantService {
constructor(
@InjectModel(Restaurant.name) private restaurantModel: Model<RestaurantDocument>,
private jwtService: JwtService,
) {}
async register(createRestaurantDto: CreateRestaurantDto): Promise<Restaurant> {
const hashedPassword = await bcrypt.hash(createRestaurantDto.password, 10);
const createdRestaurant = new this.restaurantModel({
...createRestaurantDto,
password: hashedPassword,
});
return createdRestaurant.save();
}
async login(email: string, password: string): Promise<{ accessToken: string }> {
const restaurant = await this.restaurantModel.findOne({ email });
if (!restaurant) {
throw new NotFoundException('Restaurant not found');
}
const isPasswordValid = await bcrypt.compare(password, restaurant.password);
if (!isPasswordValid) {
throw new BadRequestException('Invalid credentials');
}
const payload = { email: restaurant.email, sub: restaurant._id };
const accessToken = this.jwtService.sign(payload);
return { accessToken };
}
async updateProfile(id: string, updateRestaurantDto: UpdateRestaurantDto): Promise<Restaurant> {
const updatedRestaurant = await this.restaurantModel.findByIdAndUpdate(id, updateRestaurantDto, { new: true });
if (!updatedRestaurant) {
throw new NotFoundException('Restaurant not found');
}
return updatedRestaurant;
}
async addMenuItem(id: string, createMenuItemDto: CreateMenuItemDto): Promise<Restaurant> {
const restaurant = await this.restaurantModel.findById(id);
if (!restaurant) {
throw new NotFoundException('Restaurant not found');
}
restaurant.menu.push(createMenuItemDto);
return restaurant.save();
}
async updateMenuItem(restaurantId: string, itemId: string, createMenuItemDto: CreateMenuItemDto): Promise<Restaurant> {
const restaurant = await this.restaurantModel.findById(restaurantId);
if (!restaurant) {
throw new NotFoundException('Restaurant not found');
}
const itemIndex = restaurant.menu.findIndex(item => item._id == itemId);
if (itemIndex === -1) {
throw new NotFoundException('Menu item not found');
}
restaurant.menu[itemIndex] = { ...restaurant.menu[itemIndex], ...createMenuItemDto };
return restaurant.save();
}
async deleteMenuItem(restaurantId: string, itemId: string): Promise<Restaurant> {
const restaurant = await this.restaurantModel.findById(restaurantId);
if (!restaurant) {
throw new NotFoundException('Restaurant not found');
}
restaurant.menu = restaurant.menu.filter(item => item._id != itemId);
return restaurant.save();
}
}
Restaurant Controller
Modify restaurant.controller.ts
to include endpoints for registration, login, profile management, and menu management:
import { Controller, Post, Body, Put, Param, UseGuards, Request } from '@nestjs/common';
import { RestaurantService } from './restaurant.service';
import { CreateRestaurantDto } from './dto/create-restaurant.dto';
import { UpdateRestaurantDto } from './dto/update-restaurant.dto';
import { CreateMenuItemDto } from './dto/create-menu-item.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
@Controller('restaurants')
export class RestaurantController {
constructor(private readonly restaurantService: RestaurantService) {}
@Post('register')
async register(@Body() createRestaurantDto: CreateRestaurantDto) {
return this.restaurantService.register(createRestaurantDto);
}
@Post('login')
async login(@Body() { email, password }: { email: string, password: string }) {
return this.restaurantService.login(email, password);
}
@UseGuards(JwtAuthGuard)
@Put('profile')
async updateProfile(@Request() req, @Body() updateRestaurantDto: UpdateRestaurantDto) {
return this.restaurantService.updateProfile(req.user.userId, updateRestaurantDto);
}
@UseGuards(JwtAuthGuard)
@Post('menu')
async addMenuItem(@Request() req, @Body() createMenuItemDto: CreateMenuItemDto) {
return this.restaurantService.addMenuItem(req.user.userId, createMenuItemDto);
}
@UseGuards(JwtAuthGuard)
@Put('menu/:itemId')
async updateMenuItem(@Request() req, @Param('itemId') itemId: string, @Body() createMenuItemDto: CreateMenuItemDto) {
return this.restaurantService.updateMenuItem(req.user.userId, itemId, createMenuItemDto);
}
@UseGuards(JwtAuthGuard)
@Delete('menu/:itemId')
async deleteMenuItem(@Request() req, @Param('itemId') itemId: string) {
return this.restaurantService.deleteMenuItem(req.user.userId, itemId);
}
}
JWT Authentication
Create the JWT authentication guard and strategy:
JwtAuthGuard
Create jwt-auth.guard.ts
in the src/auth
directory:
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
}
JwtStrategy
Create jwt.strategy.ts
in the src/auth
directory:
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'your-secret-key',
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}
Auth Module
Create auth.module.ts
:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: 'your-secret-key',
signOptions: { expiresIn: '60m' },
}),
],
providers: [JwtStrategy],
exports: [JwtModule],
})
export class AuthModule {}
Integrate Modules in App Module
Modify app.module.ts
to include the newly created modules:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import {
RestaurantModule } from './restaurant/restaurant.module';
import { AuthModule } from './auth/auth.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
RestaurantModule,
AuthModule,
],
})
export class AppModule {}
Run the Application
Start the NestJS application:
npm run start
This setup provides the backend code for restaurant registration, login, profile management, and menu management functionalities using NestJS and MongoDB. Make sure to replace 'your-secret-key'
with a secure key for JWT authentication.
To implement order management, promotions and offers, analytics and reports, and customer interaction functionalities, we'll continue building upon the existing NestJS application with MongoDB.
Order Management
Create Order Schema
Create order.schema.ts
in the src/order
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type OrderDocument = Order & Document;
@Schema()
export class Order {
@Prop({ required: true })
customerId: string;
@Prop({ required: true })
restaurantId: string;
@Prop([{ itemId: String, quantity: Number, price: Number }])
items: { itemId: string, quantity: number, price: number }[];
@Prop({ required: true })
totalAmount: number;
@Prop({ required: true })
status: string; // e.g., "preparing", "ready for pickup", "out for delivery"
@Prop({ default: Date.now })
createdAt: Date;
}
export const OrderSchema = SchemaFactory.createForClass(Order);
Create Order DTOs
Create the DTOs for order management:
create-order.dto.ts
export class CreateOrderDto {
readonly customerId: string;
readonly restaurantId: string;
readonly items: { itemId: string, quantity: number, price: number }[];
readonly totalAmount: number;
}
update-order-status.dto.ts
export class UpdateOrderStatusDto {
readonly status: string;
}
Order Service
Create order.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Order, OrderDocument } from './order.schema';
import { CreateOrderDto } from './dto/create-order.dto';
import { UpdateOrderStatusDto } from './dto/update-order-status.dto';
@Injectable()
export class OrderService {
constructor(@InjectModel(Order.name) private orderModel: Model<OrderDocument>) {}
async create(createOrderDto: CreateOrderDto): Promise<Order> {
const createdOrder = new this.orderModel(createOrderDto);
return createdOrder.save();
}
async findAllByRestaurant(restaurantId: string): Promise<Order[]> {
return this.orderModel.find({ restaurantId }).exec();
}
async updateStatus(orderId: string, updateOrderStatusDto: UpdateOrderStatusDto): Promise<Order> {
const updatedOrder = await this.orderModel.findByIdAndUpdate(orderId, updateOrderStatusDto, { new: true });
if (!updatedOrder) {
throw new NotFoundException('Order not found');
}
return updatedOrder;
}
}
Order Controller
Create order.controller.ts
:
import { Controller, Post, Body, Get, Param, Put, UseGuards, Request } from '@nestjs/common';
import { OrderService } from './order.service';
import { CreateOrderDto } from './dto/create-order.dto';
import { UpdateOrderStatusDto } from './dto/update-order-status.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
@Controller('orders')
export class OrderController {
constructor(private readonly orderService: OrderService) {}
@UseGuards(JwtAuthGuard)
@Post()
async create(@Body() createOrderDto: CreateOrderDto) {
return this.orderService.create(createOrderDto);
}
@UseGuards(JwtAuthGuard)
@Get('restaurant/:id')
async findAllByRestaurant(@Param('id') restaurantId: string) {
return this.orderService.findAllByRestaurant(restaurantId);
}
@UseGuards(JwtAuthGuard)
@Put('status/:id')
async updateStatus(@Param('id') orderId: string, @Body() updateOrderStatusDto: UpdateOrderStatusDto) {
return this.orderService.updateStatus(orderId, updateOrderStatusDto);
}
}
Promotions and Offers
Create Promotion Schema
Create promotion.schema.ts
in the src/promotion
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type PromotionDocument = Promotion & Document;
@Schema()
export class Promotion {
@Prop({ required: true })
restaurantId: string;
@Prop({ required: true })
title: string;
@Prop({ required: true })
description: string;
@Prop({ required: true })
discountPercentage: number;
@Prop({ required: true })
startDate: Date;
@Prop({ required: true })
endDate: Date;
}
export const PromotionSchema = SchemaFactory.createForClass(Promotion);
Create Promotion DTOs
Create the DTOs for promotions:
create-promotion.dto.ts
export class CreatePromotionDto {
readonly restaurantId: string;
readonly title: string;
readonly description: string;
readonly discountPercentage: number;
readonly startDate: Date;
readonly endDate: Date;
}
Promotion Service
Create promotion.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Promotion, PromotionDocument } from './promotion.schema';
import { CreatePromotionDto } from './dto/create-promotion.dto';
@Injectable()
export class PromotionService {
constructor(@InjectModel(Promotion.name) private promotionModel: Model<PromotionDocument>) {}
async create(createPromotionDto: CreatePromotionDto): Promise<Promotion> {
const createdPromotion = new this.promotionModel(createPromotionDto);
return createdPromotion.save();
}
async findAllByRestaurant(restaurantId: string): Promise<Promotion[]> {
return this.promotionModel.find({ restaurantId }).exec();
}
async delete(promotionId: string): Promise<void> {
const result = await this.promotionModel.findByIdAndDelete(promotionId);
if (!result) {
throw new NotFoundException('Promotion not found');
}
}
}
Promotion Controller
Create promotion.controller.ts
:
import { Controller, Post, Body, Get, Param, Delete, UseGuards, Request } from '@nestjs/common';
import { PromotionService } from './promotion.service';
import { CreatePromotionDto } from './dto/create-promotion.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
@Controller('promotions')
export class PromotionController {
constructor(private readonly promotionService: PromotionService) {}
@UseGuards(JwtAuthGuard)
@Post()
async create(@Body() createPromotionDto: CreatePromotionDto) {
return this.promotionService.create(createPromotionDto);
}
@UseGuards(JwtAuthGuard)
@Get('restaurant/:id')
async findAllByRestaurant(@Param('id') restaurantId: string) {
return this.promotionService.findAllByRestaurant(restaurantId);
}
@UseGuards(JwtAuthGuard)
@Delete(':id')
async delete(@Param('id') promotionId: string) {
return this.promotionService.delete(promotionId);
}
}
Analytics and Reports
For analytics and reports, you will typically aggregate data from your MongoDB collections. Here’s a simple example:
Order Service Method for Reports
Add a method in order.service.ts
for generating reports:
import { Aggregate } from 'mongoose';
@Injectable()
export class OrderService {
// ... existing methods
async getSalesReport(restaurantId: string): Promise<any> {
return this.orderModel.aggregate([
{ $match: { restaurantId } },
{
$group: {
_id: null,
totalSales: { $sum: "$totalAmount" },
totalOrders: { $sum: 1 },
averageOrderValue: { $avg: "$totalAmount" },
},
},
]).exec();
}
}
Order Controller Endpoint for Reports
Add an endpoint in order.controller.ts
for generating reports:
import { Get, Param } from '@nestjs/common';
@Controller('orders')
export class OrderController {
// ... existing methods
@UseGuards(JwtAuthGuard)
@Get('reports/sales/:id')
async getSalesReport(@Param('id') restaurantId: string) {
return this.orderService.getSalesReport(restaurantId);
}
}
Customer Interaction
Restaurant Service Method for Responding to Reviews
Add a method in restaurant.service.ts
for responding to reviews:
@Injectable()
export class RestaurantService {
// ... existing methods
async respondToReview(reviewId: string, response: string): Promise<Review> {
const review = await this.reviewModel.findById(reviewId);
if (!review) {
throw new NotFoundException('Review not found');
}
review.response = response;
return review.save();
}
}
Restaurant Controller Endpoint for Responding to Reviews
Add an endpoint in restaurant.controller.ts
for responding to reviews:
import { Put, Body } from '@nestjs/common';
@Controller('restaurants')
export class RestaurantController {
// ... existing methods
@UseGuards(JwtAuthGuard)
@Put('reviews/:id/respond')
async respondToReview(@Param('id') reviewId: string, @Body() response: string) {
return this.restaurantService.respondToReview(reviewId, response);
}
}
Integrate New Modules
Update app.module.ts
to include the new modules:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { RestaurantModule } from './restaurant/restaurant.module';
import { AuthModule } from './auth/auth.module';
import { OrderModule } from './order/order.module';
import { PromotionModule } from './promotion/promotion.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
RestaurantModule,
AuthModule,
OrderModule,
PromotionModule,
],
})
export class AppModule {}
Run the Application
Start the NestJS application:
npm run start
This setup provides backend code for order management, promotions and offers, analytics and reports, and customer interaction functionalities using NestJS and MongoDB. Make sure to secure sensitive data, handle edge cases, and properly validate inputs for a production-ready application.
To implement the rider section functionalities for registration/login, profile management, and order management, we'll build on the existing NestJS application with MongoDB.
Rider Registration/Login
Rider Schema
Create rider.schema.ts
in the src/rider
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type RiderDocument = Rider & Document;
@Schema()
export class Rider {
@Prop({ required: true })
name: string;
@Prop({ required: true, unique: true })
email: string;
@Prop({ required: true })
password: string;
@Prop({ required: true })
phoneNumber: string;
@Prop({ default: false })
isAvailable: boolean;
}
export const RiderSchema = SchemaFactory.createForClass(Rider);
Rider DTOs
Create the DTOs for rider functionalities:
create-rider.dto.ts
export class CreateRiderDto {
readonly name: string;
readonly email: string;
readonly password: string;
readonly phoneNumber: string;
}
update-rider.dto.ts
export class UpdateRiderDto {
readonly name?: string;
readonly phoneNumber?: string;
readonly isAvailable?: boolean;
}
Rider Service
Create rider.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Rider, RiderDocument } from './rider.schema';
import { CreateRiderDto } from './dto/create-rider.dto';
import { UpdateRiderDto } from './dto/update-rider.dto';
import * as bcrypt from 'bcrypt';
@Injectable()
export class RiderService {
constructor(@InjectModel(Rider.name) private riderModel: Model<RiderDocument>) {}
async create(createRiderDto: CreateRiderDto): Promise<Rider> {
const hashedPassword = await bcrypt.hash(createRiderDto.password, 10);
const createdRider = new this.riderModel({
...createRiderDto,
password: hashedPassword,
});
return createdRider.save();
}
async findOneByEmail(email: string): Promise<Rider | undefined> {
return this.riderModel.findOne({ email }).exec();
}
async findOneById(riderId: string): Promise<Rider> {
const rider = await this.riderModel.findById(riderId).exec();
if (!rider) {
throw new NotFoundException('Rider not found');
}
return rider;
}
async update(riderId: string, updateRiderDto: UpdateRiderDto): Promise<Rider> {
const updatedRider = await this.riderModel.findByIdAndUpdate(riderId, updateRiderDto, { new: true });
if (!updatedRider) {
throw new NotFoundException('Rider not found');
}
return updatedRider;
}
}
Rider Controller
Create rider.controller.ts
:
import { Controller, Post, Body, Put, Param, Get, UseGuards, Request } from '@nestjs/common';
import { RiderService } from './rider.service';
import { CreateRiderDto } from './dto/create-rider.dto';
import { UpdateRiderDto } from './dto/update-rider.dto';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
@Controller('riders')
export class RiderController {
constructor(private readonly riderService: RiderService) {}
@Post('register')
async create(@Body() createRiderDto: CreateRiderDto) {
return this.riderService.create(createRiderDto);
}
@UseGuards(JwtAuthGuard)
@Get(':id')
async findOne(@Param('id') riderId: string) {
return this.riderService.findOneById(riderId);
}
@UseGuards(JwtAuthGuard)
@Put(':id')
async update(@Param('id') riderId: string, @Body() updateRiderDto: UpdateRiderDto) {
return this.riderService.update(riderId, updateRiderDto);
}
}
Rider Authentication
Add the necessary authentication methods for riders using JWT. You can reuse the auth
module with some adjustments for riders.
Auth Module Adjustments
Update auth.service.ts
to handle rider login:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { RiderService } from '../rider/rider.service';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(
private readonly riderService: RiderService,
private readonly jwtService: JwtService,
) {}
async validateRider(email: string, pass: string): Promise<any> {
const rider = await this.riderService.findOneByEmail(email);
if (rider && await bcrypt.compare(pass, rider.password)) {
const { password, ...result } = rider.toObject();
return result;
}
return null;
}
async loginRider(rider: any) {
const payload = { email: rider.email, sub: rider._id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
Auth Controller Adjustments
Update auth.controller.ts
to handle rider login:
import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './local-auth.guard';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@UseGuards(LocalAuthGuard)
@Post('rider/login')
async loginRider(@Request() req) {
return this.authService.loginRider(req.user);
}
}
Order Management for Riders
Rider Order DTO
Create accept-order.dto.ts
in the src/rider/dto
directory:
export class AcceptOrderDto {
readonly riderId: string;
readonly orderId: string;
}
Update Order Schema for Rider Assignment
Update order.schema.ts
to include riderId:
@Schema()
export class Order {
// ... existing properties
@Prop({ required: false })
riderId?: string;
}
Order Service Method for Accepting Orders
Add a method in order.service.ts
for riders to accept orders:
@Injectable()
export class OrderService {
// ... existing methods
async acceptOrder(riderId: string, orderId: string): Promise<Order> {
const order = await this.orderModel.findById(orderId);
if (!order) {
throw new NotFoundException('Order not found');
}
order.riderId = riderId;
return order.save();
}
}
Rider Controller Endpoint for Accepting Orders
Add an endpoint in rider.controller.ts
for accepting orders:
import { Put } from '@nestjs/common';
import { AcceptOrderDto } from './dto/accept-order.dto';
@Controller('riders')
export class RiderController {
// ... existing methods
@UseGuards(JwtAuthGuard)
@Put('accept-order')
async acceptOrder(@Body() acceptOrderDto: AcceptOrderDto) {
return this.orderService.acceptOrder(acceptOrderDto.riderId, acceptOrderDto.orderId);
}
}
Integration and Running the Application
Ensure all new modules and services are included in app.module.ts
:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { RestaurantModule } from './restaurant/restaurant.module';
import { AuthModule } from './auth/auth.module';
import { OrderModule } from './order/order.module';
import { PromotionModule } from './promotion/promotion.module';
import { RiderModule } from './rider/rider.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
RestaurantModule,
AuthModule,
OrderModule,
PromotionModule,
RiderModule,
],
})
export class AppModule {}
Finally, start the NestJS application:
npm run start
This setup provides backend code for rider registration/login, profile management, and order management functionalities using NestJS and MongoDB. Make sure to secure sensitive data, handle edge cases, and properly validate inputs for a production-ready application.
To implement real-time navigation, order tracking, earnings and payouts, notifications, and in-app chat for the rider section, we'll build upon the existing NestJS application with MongoDB.
Real-Time Navigation
Integration with Google Maps for Navigation
To integrate Google Maps, you'll need to use the Google Maps API. First, install the required package:
npm install @googlemaps/google-maps-services-js
Create Google Maps Service
Create google-maps.service.ts
:
import { Injectable } from '@nestjs/common';
import { Client } from '@googlemaps/google-maps-services-js';
@Injectable()
export class GoogleMapsService {
private client: Client;
constructor() {
this.client = new Client({});
}
async getDirections(origin: string, destination: string): Promise<any> {
const response = await this.client.directions({
params: {
origin,
destination,
key: 'YOUR_GOOGLE_MAPS_API_KEY',
},
});
return response.data;
}
}
Rider Controller for Navigation
Add a method in rider.controller.ts
to get navigation directions:
import { Get, Query } from '@nestjs/common';
import { GoogleMapsService } from './google-maps.service';
@Controller('riders')
export class RiderController {
constructor(
private readonly riderService: RiderService,
private readonly googleMapsService: GoogleMapsService,
) {}
// ... existing methods
@UseGuards(JwtAuthGuard)
@Get('navigation')
async getNavigation(@Query('origin') origin: string, @Query('destination') destination: string) {
return this.googleMapsService.getDirections(origin, destination);
}
}
Order Tracking
Update Order Status DTO
Create update-order-status.dto.ts
:
export class UpdateOrderStatusDto {
readonly status: string; // e.g., "picked up", "on the way", "delivered"
}
Order Service Method for Updating Status
Add a method in order.service.ts
for updating the order status:
@Injectable()
export class OrderService {
// ... existing methods
async updateOrderStatus(orderId: string, status: string): Promise<Order> {
const order = await this.orderModel.findById(orderId);
if (!order) {
throw new NotFoundException('Order not found');
}
order.status = status;
return order.save();
}
}
Rider Controller Endpoint for Updating Order Status
Add an endpoint in rider.controller.ts
for updating the order status:
import { Put, Body, Param } from '@nestjs/common';
@Controller('riders')
export class RiderController {
// ... existing methods
@UseGuards(JwtAuthGuard)
@Put('order-status/:orderId')
async updateOrderStatus(@Param('orderId') orderId: string, @Body() updateOrderStatusDto: UpdateOrderStatusDto) {
return this.orderService.updateOrderStatus(orderId, updateOrderStatusDto.status);
}
}
Earnings and Payouts
Earnings Schema
Create earning.schema.ts
in the src/earning
directory:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type EarningDocument = Earning & Document;
@Schema()
export class Earning {
@Prop({ required: true })
riderId: string;
@Prop({ required: true })
amount: number;
@Prop({ default: Date.now })
date: Date;
}
export const EarningSchema = SchemaFactory.createForClass(Earning);
Earning Service
Create earning.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Earning, EarningDocument } from './earning.schema';
@Injectable()
export class EarningService {
constructor(@InjectModel(Earning.name) private earningModel: Model<EarningDocument>) {}
async getEarningsByRider(riderId: string): Promise<Earning[]> {
return this.earningModel.find({ riderId }).exec();
}
}
Earning Controller
Create earning.controller.ts
:
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
import { EarningService } from './earning.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
@Controller('earnings')
export class EarningController {
constructor(private readonly earningService: EarningService) {}
@UseGuards(JwtAuthGuard)
@Get(':riderId')
async getEarningsByRider(@Param('riderId') riderId: string) {
return this.earningService.getEarningsByRider(riderId);
}
}
Notifications
For push notifications, you can use a service like Firebase Cloud Messaging (FCM).
Install Firebase Admin SDK
Install the Firebase Admin SDK:
npm install firebase-admin
Create Firebase Service
Create firebase.service.ts
:
import { Injectable } from '@nestjs/common';
import * as admin from 'firebase-admin';
@Injectable()
export class FirebaseService {
constructor() {
admin.initializeApp({
credential: admin.credential.applicationDefault(),
});
}
async sendNotification(token: string, message: any): Promise<any> {
return admin.messaging().sendToDevice(token, message);
}
}
Notification Controller
Create notification.controller.ts
:
import { Controller, Post, Body } from '@nestjs/common';
import { FirebaseService } from './firebase.service';
@Controller('notifications')
export class NotificationController {
constructor(private readonly firebaseService: FirebaseService) {}
@Post('send')
async sendNotification(@Body('token') token: string, @Body('message') message: any) {
return this.firebaseService.sendNotification(token, message);
}
}
In-App Chat
For in-app chat, you can use WebSockets. NestJS provides built-in support for WebSockets.
Install WebSocket Package
Install the WebSocket package:
npm install @nestjs/websockets @nestjs/platform-socket.io
Create Chat Gateway
Create chat.gateway.ts
:
import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway()
export class ChatGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('sendMessage')
handleMessage(client: Socket, payload: { sender: string, recipient: string, message: string }): void {
this.server.to(payload.recipient).emit('receiveMessage', payload);
}
handleConnection(client: Socket) {
console.log(`Client connected: ${client.id}`);
}
handleDisconnect(client: Socket) {
console.log(`Client disconnected: ${client.id}`);
}
}
Update App Module
Ensure all new modules and services are included in app.module.ts
:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { RestaurantModule } from './restaurant/restaurant.module';
import { AuthModule } from './auth/auth.module';
import { OrderModule } from './order/order.module';
import { PromotionModule } from './promotion/promotion.module';
import { RiderModule } from './rider/rider.module';
import { EarningModule } from './earning/earning.module';
import { NotificationModule } from './notification/notification.module';
import { ChatGateway } from './chat.gateway';
import { GoogleMapsService } from './google-maps.service';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
RestaurantModule,
AuthModule,
OrderModule,
PromotionModule,
RiderModule,
EarningModule,
NotificationModule,
],
providers: [ChatGateway, GoogleMapsService],
})
export class AppModule {}
Run the Application
Start the NestJS application:
npm run start
This setup provides backend code for real-time navigation, order tracking, earnings and payouts, notifications, and in-app chat functionalities using NestJS and MongoDB. Make sure to secure sensitive data, handle edge cases, and properly validate inputs for a production-ready application.
To implement the admin section with functionalities like dashboard, user management, restaurant management, and rider management using NestJS and MongoDB, follow these steps:
Admin Dashboard
Create Dashboard Service
Create dashboard.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from '../user/user.schema';
import { Restaurant } from '../restaurant/restaurant.schema';
import { Rider } from '../rider/rider.schema';
import { Order } from '../order/order.schema';
@Injectable()
export class DashboardService {
constructor(
@InjectModel(User.name) private userModel: Model<User>,
@InjectModel(Restaurant.name) private restaurantModel: Model<Restaurant>,
@InjectModel(Rider.name) private riderModel: Model<Rider>,
@InjectModel(Order.name) private orderModel: Model<Order>,
) {}
async getOverview() {
const activeUsers = await this.userModel.countDocuments({ isActive: true }).exec();
const totalOrders = await this.orderModel.countDocuments().exec();
const activeRestaurants = await this.restaurantModel.countDocuments({ isActive: true }).exec();
const activeRiders = await this.riderModel.countDocuments({ isAvailable: true }).exec();
return {
activeUsers,
totalOrders,
activeRestaurants,
activeRiders,
};
}
}
Create Dashboard Controller
Create dashboard.controller.ts
:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { DashboardService } from './dashboard.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/dashboard')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class DashboardController {
constructor(private readonly dashboardService: DashboardService) {}
@Get('overview')
async getOverview() {
return this.dashboardService.getOverview();
}
}
User Management
Create User Management Service
Create user-management.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from '../user/user.schema';
@Injectable()
export class UserManagementService {
constructor(@InjectModel(User.name) private userModel: Model<User>) {}
async getAllUsers() {
return this.userModel.find().exec();
}
async updateUserStatus(userId: string, isActive: boolean) {
const user = await this.userModel.findByIdAndUpdate(userId, { isActive }, { new: true }).exec();
if (!user) {
throw new NotFoundException('User not found');
}
return user;
}
}
Create User Management Controller
Create user-management.controller.ts
:
import { Controller, Get, Put, Param, Body, UseGuards } from '@nestjs/common';
import { UserManagementService } from './user-management.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/users')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class UserManagementController {
constructor(private readonly userManagementService: UserManagementService) {}
@Get()
async getAllUsers() {
return this.userManagementService.getAllUsers();
}
@Put(':id/status')
async updateUserStatus(@Param('id') userId: string, @Body('isActive') isActive: boolean) {
return this.userManagementService.updateUserStatus(userId, isActive);
}
}
Restaurant Management
Create Restaurant Management Service
Create restaurant-management.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Restaurant } from '../restaurant/restaurant.schema';
@Injectable()
export class RestaurantManagementService {
constructor(@InjectModel(Restaurant.name) private restaurantModel: Model<Restaurant>) {}
async getAllRestaurants() {
return this.restaurantModel.find().exec();
}
async updateRestaurantStatus(restaurantId: string, isActive: boolean) {
const restaurant = await this.restaurantModel.findByIdAndUpdate(restaurantId, { isActive }, { new: true }).exec();
if (!restaurant) {
throw new NotFoundException('Restaurant not found');
}
return restaurant;
}
}
Create Restaurant Management Controller
Create restaurant-management.controller.ts
:
import { Controller, Get, Put, Param, Body, UseGuards } from '@nestjs/common';
import { RestaurantManagementService } from './restaurant-management.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/restaurants')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class RestaurantManagementController {
constructor(private readonly restaurantManagementService: RestaurantManagementService) {}
@Get()
async getAllRestaurants() {
return this.restaurantManagementService.getAllRestaurants();
}
@Put(':id/status')
async updateRestaurantStatus(@Param('id') restaurantId: string, @Body('isActive') isActive: boolean) {
return this.restaurantManagementService.updateRestaurantStatus(restaurantId, isActive);
}
}
Rider Management
Create Rider Management Service
Create rider-management.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Rider } from '../rider/rider.schema';
@Injectable()
export class RiderManagementService {
constructor(@InjectModel(Rider.name) private riderModel: Model<Rider>) {}
async getAllRiders() {
return this.riderModel.find().exec();
}
async updateRiderStatus(riderId: string, isAvailable: boolean) {
const rider = await this.riderModel.findByIdAndUpdate(riderId, { isAvailable }, { new: true }).exec();
if (!rider) {
throw new NotFoundException('Rider not found');
}
return rider;
}
}
Create Rider Management Controller
Create rider-management.controller.ts
:
import { Controller, Get, Put, Param, Body, UseGuards } from '@nestjs/common';
import { RiderManagementService } from './rider-management.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/riders')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class RiderManagementController {
constructor(private readonly riderManagementService: RiderManagementService) {}
@Get()
async getAllRiders() {
return this.riderManagementService.getAllRiders();
}
@Put(':id/status')
async updateRiderStatus(@Param('id') riderId: string, @Body('isAvailable') isAvailable: boolean) {
return this.riderManagementService.updateRiderStatus(riderId, isAvailable);
}
}
Update App Module
Ensure all new modules and services are included in app.module.ts
:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { RestaurantModule } from './restaurant/restaurant.module';
import { AuthModule } from './auth/auth.module';
import { OrderModule } from './order/order.module';
import { PromotionModule } from './promotion/promotion.module';
import { RiderModule } from './rider/rider.module';
import { EarningModule } from './earning/earning.module';
import { NotificationModule } from './notification/notification.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { UserManagementModule } from './user-management/user-management.module';
import { RestaurantManagementModule } from './restaurant-management/restaurant-management.module';
import { RiderManagementModule } from './rider-management/rider-management.module';
import { ChatGateway } from './chat.gateway';
import { GoogleMapsService } from './google-maps.service';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
RestaurantModule,
AuthModule,
OrderModule,
PromotionModule,
RiderModule,
EarningModule,
NotificationModule,
DashboardModule,
UserManagementModule,
RestaurantManagementModule,
RiderManagementModule,
],
providers: [ChatGateway, GoogleMapsService],
})
export class AppModule {}
Run the Application
Start the NestJS application:
npm run start
This setup provides backend code for the admin section functionalities like dashboard, user management, restaurant management, and rider management using NestJS and MongoDB. Ensure to handle edge cases, validate inputs, and secure sensitive data for a production-ready application.
To implement the Order Management, Financial Management, Promotions and Marketing, and Support and Feedback functionalities for the admin section using NestJS and MongoDB, follow these steps:
Order Management
Order Schema
Create order.schema.ts
:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type OrderDocument = Order & Document;
@Schema()
export class Order {
@Prop({ required: true })
customerId: string;
@Prop({ required: true })
restaurantId: string;
@Prop({ required: true })
items: { name: string, quantity: number }[];
@Prop({ required: true, default: 'pending' })
status: string; // pending, preparing, ready for pickup, out for delivery, delivered
@Prop({ required: true })
totalAmount: number;
@Prop({ type: Date, default: Date.now })
createdAt: Date;
}
export const OrderSchema = SchemaFactory.createForClass(Order);
Order Service
Create order.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Order, OrderDocument } from './order.schema';
@Injectable()
export class OrderService {
constructor(@InjectModel(Order.name) private orderModel: Model<OrderDocument>) {}
async getAllOrders(): Promise<Order[]> {
return this.orderModel.find().exec();
}
async getOrderById(orderId: string): Promise<Order> {
const order = await this.orderModel.findById(orderId).exec();
if (!order) {
throw new NotFoundException('Order not found');
}
return order;
}
async updateOrderStatus(orderId: string, status: string): Promise<Order> {
const order = await this.orderModel.findByIdAndUpdate(orderId, { status }, { new: true }).exec();
if (!order) {
throw new NotFoundException('Order not found');
}
return order;
}
}
Order Controller
Create order.controller.ts
:
import { Controller, Get, Put, Param, Body, UseGuards } from '@nestjs/common';
import { OrderService } from './order.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/orders')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class OrderController {
constructor(private readonly orderService: OrderService) {}
@Get()
async getAllOrders() {
return this.orderService.getAllOrders();
}
@Get(':id')
async getOrderById(@Param('id') orderId: string) {
return this.orderService.getOrderById(orderId);
}
@Put(':id/status')
async updateOrderStatus(@Param('id') orderId: string, @Body('status') status: string) {
return this.orderService.updateOrderStatus(orderId, status);
}
}
Financial Management
Financial management often involves integration with payment gateways. Here's a basic outline:
Payment Schema
Create payment.schema.ts
:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type PaymentDocument = Payment & Document;
@Schema()
export class Payment {
@Prop({ required: true })
orderId: string;
@Prop({ required: true })
amount: number;
@Prop({ required: true })
paymentMethod: string; // Credit Card, PayPal, etc.
@Prop({ default: Date.now })
createdAt: Date;
}
export const PaymentSchema = SchemaFactory.createForClass(Payment);
Payment Service
Create payment.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Payment, PaymentDocument } from './payment.schema';
@Injectable()
export class PaymentService {
constructor(@InjectModel(Payment.name) private paymentModel: Model<PaymentDocument>) {}
async getAllPayments(): Promise<Payment[]> {
return this.paymentModel.find().exec();
}
async getPaymentsByOrderId(orderId: string): Promise<Payment[]> {
return this.paymentModel.find({ orderId }).exec();
}
async createPayment(orderId: string, amount: number, paymentMethod: string): Promise<Payment> {
const newPayment = new this.paymentModel({ orderId, amount, paymentMethod });
return newPayment.save();
}
}
Payment Controller
Create payment.controller.ts
:
import { Controller, Get, Post, Param, Body, UseGuards } from '@nestjs/common';
import { PaymentService } from './payment.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/payments')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class PaymentController {
constructor(private readonly paymentService: PaymentService) {}
@Get()
async getAllPayments() {
return this.paymentService.getAllPayments();
}
@Get('order/:orderId')
async getPaymentsByOrderId(@Param('orderId') orderId: string) {
return this.paymentService.getPaymentsByOrderId(orderId);
}
@Post()
async createPayment(@Body() createPaymentDto: { orderId: string, amount: number, paymentMethod: string }) {
return this.paymentService.createPayment(createPaymentDto.orderId, createPaymentDto.amount, createPaymentDto.paymentMethod);
}
}
Promotions and Marketing
Promotion Schema
Create promotion.schema.ts
:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type PromotionDocument = Promotion & Document;
@Schema()
export class Promotion {
@Prop({ required: true })
title: string;
@Prop()
description: string;
@Prop({ type: Date, required: true })
startDate: Date;
@Prop({ type: Date, required: true })
endDate: Date;
@Prop({ required: true })
discount: number; // in percentage
@Prop({ default: Date.now })
createdAt: Date;
}
export const PromotionSchema = SchemaFactory.createForClass(Promotion);
Promotion Service
Create promotion.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Promotion, PromotionDocument } from './promotion.schema';
@Injectable()
export class PromotionService {
constructor(@InjectModel(Promotion.name) private promotionModel: Model<PromotionDocument>) {}
async getAllPromotions(): Promise<Promotion[]> {
return this.promotionModel.find().exec();
}
async createPromotion(promotion: Promotion): Promise<Promotion> {
const newPromotion = new this.promotionModel(promotion);
return newPromotion.save();
}
async deletePromotion(promotionId: string): Promise<void> {
const result = await this.promotionModel.deleteOne({ _id: promotionId }).exec();
if (result.deletedCount === 0) {
throw new NotFoundException('Promotion not found');
}
}
}
Promotion Controller
Create promotion.controller.ts
:
import { Controller, Get, Post, Delete, Param, Body, UseGuards } from '@nestjs/common';
import { PromotionService } from './promotion.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/promotions')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class PromotionController {
constructor(private readonly promotionService: PromotionService) {}
@Get()
async getAllPromotions() {
return this.promotionService.getAllPromotions();
}
@Post()
async createPromotion(@Body() promotion: Promotion) {
return this.promotionService.createPromotion(promotion);
}
@Delete(':id')
async deletePromotion(@Param('id') promotionId: string) {
return this.promotionService.deletePromotion(promotionId);
}
}
Support and Feedback
Complaint Schema
Create complaint.schema.ts
:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type ComplaintDocument = Complaint & Document;
@Schema()
export class Complaint {
@Prop({ required: true })
userId: string;
@Prop({ required: true })
type: string; // customer, restaurant, rider
@Prop({ required: true })
description: string;
@Prop({ default: Date.now })
createdAt: Date;
}
export const ComplaintSchema = SchemaFactory.createForClass(Complaint);
Complaint Service
Create complaint.service.ts
:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Complaint, ComplaintDocument } from './complaint.schema';
@Injectable()
export class ComplaintService {
constructor(@InjectModel(Complaint.name) private complaintModel: Model<ComplaintDocument>) {}
async getAllComplaints(): Promise<Com
plaint[]> {
return this.complaintModel.find().exec();
}
async createComplaint(complaint: Complaint): Promise<Complaint> {
const newComplaint = new this.complaintModel(complaint);
return newComplaint.save();
}
async deleteComplaint(complaintId: string): Promise<void> {
const result = await this.complaintModel.deleteOne({ _id: complaintId }).exec();
if (result.deletedCount === 0) {
throw new NotFoundException('Complaint not found');
}
}
}
Complaint Controller
Create complaint.controller.ts
:
import { Controller, Get, Post, Delete, Param, Body, UseGuards } from '@nestjs/common';
import { ComplaintService } from './complaint.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin/complaints')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
export class ComplaintController {
constructor(private readonly complaintService: ComplaintService) {}
@Get()
async getAllComplaints() {
return this.complaintService.getAllComplaints();
}
@Post()
async createComplaint(@Body() complaint: Complaint) {
return this.complaintService.createComplaint(complaint);
}
@Delete(':id')
async deleteComplaint(@Param('id') complaintId: string) {
return this.complaintService.deleteComplaint(complaintId);
}
}
Update App Module
Ensure all new modules and services are included in app.module.ts
:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { OrderModule } from './order/order.module';
import { PaymentModule } from './payment/payment.module';
import { PromotionModule } from './promotion/promotion.module';
import { ComplaintModule } from './complaint/complaint.module';
import { AuthModule } from './auth/auth.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { UserManagementModule } from './user-management/user-management.module';
import { RestaurantManagementModule } from './restaurant-management/restaurant-management.module';
import { RiderManagementModule } from './rider-management/rider-management.module';
import { ChatGateway } from './chat.gateway';
import { GoogleMapsService } from './google-maps.service';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
OrderModule,
PaymentModule,
PromotionModule,
ComplaintModule,
AuthModule,
DashboardModule,
UserManagementModule,
RestaurantManagementModule,
RiderManagementModule,
],
providers: [ChatGateway, GoogleMapsService],
})
export class AppModule {}
Run the Application
Start the NestJS application:
npm run start
This setup provides backend code for the admin section functionalities like order management, financial management, promotions and marketing, and support and feedback handling using NestJS and MongoDB. Adjust schemas, services, and controllers as per your specific application requirements and business logic.
Integrating technical functionalities like Google Maps, Payment Gateway, Notification Service, and Chat System into a NestJS application requires setting up services, handling APIs, and managing data appropriately. Here's a structured approach to implement these integrations:
Google Maps Integration
For real-time location tracking and route optimization:
Google Maps Service
Create google-maps.service.ts
:
import { Injectable } from '@nestjs/common';
import * as googleMaps from '@googlemaps/google-maps-services-js';
@Injectable()
export class GoogleMapsService {
private readonly client: googleMaps.Client;
constructor() {
this.client = new googleMaps.Client({});
}
async getRoute(origin: string, destination: string): Promise<any> {
const response = await this.client.directions({
params: {
origin,
destination,
mode: 'driving',
optimize: true,
key: 'YOUR_GOOGLE_MAPS_API_KEY', // Replace with your Google Maps API Key
},
});
return response.data;
}
async geocodeAddress(address: string): Promise<any> {
const response = await this.client.geocode({
params: {
address,
key: 'YOUR_GOOGLE_MAPS_API_KEY', // Replace with your Google Maps API Key
},
});
return response.data;
}
}
Payment Gateway Integration
For secure payment processing:
Payment Gateway Service
Create payment-gateway.service.ts
:
import { Injectable } from '@nestjs/common';
@Injectable()
export class PaymentGatewayService {
async processPayment(amount: number, cardDetails: any): Promise<any> {
// Integrate with your payment gateway API here
// Example:
// const response = await axios.post('PAYMENT_GATEWAY_API_URL', {
// amount,
// cardDetails,
// apiKey: 'YOUR_PAYMENT_GATEWAY_API_KEY',
// });
// return response.data;
throw new Error('Payment gateway integration not implemented');
}
}
Notification Service
For push notifications:
Notification Service
Create notification.service.ts
:
import { Injectable } from '@nestjs/common';
@Injectable()
export class NotificationService {
async sendPushNotification(deviceToken: string, message: string): Promise<any> {
// Integrate with your push notification service provider here
// Example:
// const response = await axios.post('PUSH_NOTIFICATION_API_URL', {
// deviceToken,
// message,
// apiKey: 'YOUR_PUSH_NOTIFICATION_API_KEY',
// });
// return response.data;
throw new Error('Push notification service integration not implemented');
}
}
Chat System Integration
For in-app messaging:
Chat Gateway
Create chat.gateway.ts
:
import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
@WebSocketGateway()
export class ChatGateway {
@WebSocketServer() server;
private logger: Logger = new Logger('ChatGateway');
@SubscribeMessage('messageToServer')
handleMessage(@MessageBody() data: any): string {
this.logger.log(data);
return 'Message received and sent to clients!';
}
}
Update AppModule
Ensure all services are included in app.module.ts
:
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { GoogleMapsService } from './google-maps.service';
import { PaymentGatewayService } from './payment-gateway.service';
import { NotificationService } from './notification.service';
import { ChatGateway } from './chat.gateway';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [MongooseModule.forRoot('mongodb://localhost/nest')],
controllers: [AppController],
providers: [AppService, GoogleMapsService, PaymentGatewayService, NotificationService, ChatGateway],
})
export class AppModule {}
Notes:
-
Google Maps Integration: Replace
'YOUR_GOOGLE_MAPS_API_KEY'
with your actual Google Maps API Key ingoogle-maps.service.ts
. -
Payment Gateway Integration: Implement integration with your preferred payment gateway API in
payment-gateway.service.ts
. -
Notification Service: Integrate with your chosen push notification service provider in
notification.service.ts
. -
Chat System Integration: Use
chat.gateway.ts
as a starting point for WebSocket communication; adjust as per your application's needs.
These examples provide a foundation for integrating essential technical functionalities into your NestJS application using MongoDB. Adjust each service to fit your specific API requirements and ensure proper security measures are in place, especially for sensitive operations like payment processing and user notifications.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content is generated by AI.
Top comments (6)
To develop a comprehensive food delivery web app, start by conducting market research to understand user needs and preferences, then create a detailed plan outlining essential features such as user registration, restaurant listings, real-time tracking, secure payment gateways, and customer reviews. Utilize modern web development frameworks and ensure a responsive design for both web and mobile users. Integrate APIs for location services and payment processing. Regularly update menus and prices by collaborating with a platform like menusprice to provide accurate and up-to-date information on restaurant offerings, ensuring a seamless and user-friendly experience for your customers.
To develop a comprehensive food delivery app, you need to cover user registration, profile management, real-time tracking, and more.
For implementation, consider using Next.js, Tailwind CSS, and Firebase for authentication. Additionally, integrating features like displaying real-time menu prices, such as McDonald's menu prices, can enhance user experience.
I develope UX/UI Please check and reviews my work Dunkin Donuts Menu
great
UI Develpoed one check menuspriceph
great work