├── .postcssrc ├── .babelrc ├── src ├── Images │ ├── heart.png │ ├── heartt.png │ ├── Contact-Us.png │ └── working-on.jpg ├── assets │ └── foodie.jpg ├── utils │ ├── store.js │ ├── helper.js │ ├── cartSlice.js │ ├── useOnline.js │ ├── Constant.js │ └── useRestaurant.js ├── components │ ├── Error.js │ ├── FoodItem.js │ ├── Footer.js │ ├── Shimmer.js │ ├── RestaurantCard.js │ ├── About.js │ ├── Cart.js │ ├── Contact.js │ ├── Instamart.js │ ├── Header.js │ ├── Body.js │ └── RestrauntMenu.js └── App.js ├── .gitignore ├── tailwind.config.js ├── .vscode ├── settings.json └── launch.json ├── index.css ├── index.html ├── package.json └── README.md /.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "tailwindcss": {} 4 | } 5 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | // "plugins": [["transform-remove-console", 3 | // { "exclude": ["error", "warn"] }]] 4 | } -------------------------------------------------------------------------------- /src/Images/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RohitSharma50/food-orderinf-website/HEAD/src/Images/heart.png -------------------------------------------------------------------------------- /src/Images/heartt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RohitSharma50/food-orderinf-website/HEAD/src/Images/heartt.png -------------------------------------------------------------------------------- /src/assets/foodie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RohitSharma50/food-orderinf-website/HEAD/src/assets/foodie.jpg -------------------------------------------------------------------------------- /src/Images/Contact-Us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RohitSharma50/food-orderinf-website/HEAD/src/Images/Contact-Us.png -------------------------------------------------------------------------------- /src/Images/working-on.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RohitSharma50/food-orderinf-website/HEAD/src/Images/working-on.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # to learn more about gitignore 3 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 4 | 5 | 6 | node_modules/ 7 | dist/ 8 | .parcel-cache 9 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/**/*.{html,js}"], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /src/utils/store.js: -------------------------------------------------------------------------------- 1 | import {configureStore} from "@reduxjs/toolkit" 2 | import cartSlice from "./cartSlice"; 3 | 4 | 5 | const store = configureStore({ 6 | 7 | reducer:{ 8 | cart: cartSlice, 9 | }, 10 | 11 | }); 12 | export default store; -------------------------------------------------------------------------------- /src/utils/helper.js: -------------------------------------------------------------------------------- 1 | 2 | export function filterData(searchText, allRestaurant) { 3 | const filterData = allRestaurant.filter((restaurant) => 4 | restaurant?.info?.name.toUpperCase().includes(searchText.toUpperCase())); 5 | 6 | return filterData; 7 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "css.lint.unknownAtRules": "ignore", 3 | "files.exclude": { 4 | "**/.DS_Store": false, 5 | "**/.git": false, 6 | "**/.hg": false, 7 | "**/.svn": false, 8 | "**/CVS": false, 9 | "**/Thumbs.db": false 10 | }, 11 | "liveServer.settings.port": 5501 12 | } -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | 6 | 7 | /* 8 | @import "tailwindcss/base"; 9 | 10 | @import "tailwindcss/components"; 11 | 12 | @import "tailwindcss/utilities"; */ 13 | /* .cuisines { 14 | text-overflow: clip; 15 | display: flex; 16 | flex-wrap: wrap; 17 | word-break: break-all; 18 | 19 | } */ 20 | 21 | /* // font-light text-ellipsis whitespace-pre-wrap */ -------------------------------------------------------------------------------- /src/components/Error.js: -------------------------------------------------------------------------------- 1 | import { useRouteError } from "react-router-dom" 2 | 3 | const Error = () => { 4 | const err = useRouteError(); 5 | return ( 6 |
7 |

oops!

8 |

Something went wrong!!

9 |

{err.status + ":" + err.statusText}

10 |
11 | ); 12 | }; 13 | export default Error; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Foodie 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\index.html" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/utils/cartSlice.js: -------------------------------------------------------------------------------- 1 | import {createSlice } from "@reduxjs/toolkit"; 2 | 3 | const cartSlice= createSlice({ 4 | name: 'cart', 5 | initialState: { 6 | items:[], 7 | }, 8 | reducers:{ 9 | addItem:(state,action)=>{ 10 | 11 | state.items.push(action.payload); 12 | }, 13 | removeItem:(state, action) =>{ 14 | state.items.pop(); 15 | }, 16 | clearCart:(state)=>{ 17 | state.items=[]; 18 | }, 19 | }, 20 | }); 21 | 22 | 23 | 24 | export const {addItem, removeItem, clearCart} =cartSlice.actions; 25 | export default cartSlice.reducer; -------------------------------------------------------------------------------- /src/components/FoodItem.js: -------------------------------------------------------------------------------- 1 | import { IMG_CDN_URL } from "../utils/Constant"; 2 | 3 | const foodItem= ({ 4 | 5 | name, 6 | defaultPrice, 7 | imageId, 8 | price, 9 | }) => { 10 | return ( 11 | 12 | <> 13 | 14 |
15 | 16 | 17 |

{name}

18 |

{ (price? price:defaultPrice)/100}

19 | 20 | 21 |
22 | 23 | ) 24 | } 25 | export default foodItem; 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/utils/useOnline.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | const useOnline = () => { 4 | 5 | const [isOnline, setIsOnline] = useState(true); 6 | 7 | useEffect(() => { 8 | 9 | const handleOnline = () => { 10 | setIsOnline(true); 11 | }; 12 | const handleOffline = () => { 13 | setIsOnline(false); 14 | }; 15 | window.addEventListener("online", handleOnline); 16 | window.addEventListener("offline", handleOffline); 17 | 18 | return () => { 19 | window.removeEventListener("online", handleOnline); 20 | window.removeEventListener("offline", handleOffline); 21 | } 22 | 23 | }, []); 24 | 25 | // returning boolean value 26 | return isOnline; 27 | }; 28 | export default useOnline; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "namastey-react", 3 | "version": "1.0.0", 4 | "description": "this is live course", 5 | "scripts": { 6 | "start": "parcel index.html", 7 | "build": "parcel build index.html", 8 | "test": "jest" 9 | }, 10 | "author": "Rohit Sharma", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@babel/core": "^7.23.7", 14 | "babel-plugin-transform-remove-console": "^6.9.4", 15 | "parcel": "^2.8.3", 16 | "postcss": "^8.4.24", 17 | "process": "^0.11.10", 18 | "tailwindcss": "^3.3.2" 19 | }, 20 | "dependencies": { 21 | "@reduxjs/toolkit": "^1.9.7", 22 | "cors": "^2.8.5", 23 | "react": "^18.2.0", 24 | "react-dom": "^18.2.0", 25 | "react-redux": "^8.1.3", 26 | "react-router-dom": "^6.12.0" 27 | }, 28 | "browserslist": [ 29 | "last 2 versions" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/Constant.js: -------------------------------------------------------------------------------- 1 | 2 | export const IMG_CDN_URL = "https://media-assets.swiggy.com/swiggy/image/upload/fl_lossy,f_auto,q_auto,w_508,h_320,c_fill/"; 3 | 4 | 5 | 6 | export const MENU_API ="https://foodfire.onrender.com/api/restaurants?lat=21.1702401&lng=72.83106070000001&page_type=DESKTOP_WEB_LISTING"; 7 | 8 | export const swiggy_menu_api_URL = 9 | "https://foodfire.onrender.com/api/menu?page-type=REGULAR_MENU&complete-menu=true&lat=21.1702401&lng=72.83106070000001&&submitAction=ENTER&restaurantId="; 10 | 11 | 12 | export const options = { 13 | method: "GET", 14 | headers: { 15 | Authorization: "", 16 | }, 17 | }; 18 | // menu items api card type key 19 | 20 | export const MENU_ITEM_TYPE_KEY = 21 | "type.googleapis.com/swiggy.presentation.food.v2.ItemCategory"; 22 | export const RESTAURANT_TYPE_KEY = 23 | "type.googleapis.com/swiggy.presentation.food.v2.Restaurant"; 24 | 25 | -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | 2 | import heartt from "../Images/heartt.png"; 3 | export const Footer = () => { 4 | 5 | 6 | return ( 7 |
8 | 9 | 10 | Foodie 11 | 12 | Created By 13 | 14 | 15 | 20 | Rohit Sharma 21 |
with
22 | 23 | 24 | 25 |
heart crossed
26 | React 31 |
32 | ); 33 | 34 | }; 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/components/Shimmer.js: -------------------------------------------------------------------------------- 1 | 2 | //import RestrauntCard from "../components/RestrauntMenu"; 3 | const Shimmer = () => { 4 | return ( 5 | <> 6 |
7 | {Array(15) 8 | .fill("").map((e, index) => ( 9 |
11 |
12 |
13 |
14 |
15 | 16 |
17 | 18 | ))} 19 | 20 | 21 |
22 | 23 | ); 24 | } 25 | export default Shimmer; 26 | //shadow-stone-500 -------------------------------------------------------------------------------- /src/components/RestaurantCard.js: -------------------------------------------------------------------------------- 1 | import { IMG_CDN_URL } from "../utils/Constant"; 2 | 3 | const RestrauntCard = ({ 4 | name, 5 | cuisines, 6 | cloudinaryImageId, 7 | avgRating, 8 | costForTwo, 9 | 10 | 11 | }) => { 12 | return ( 13 | 14 |
15 | 16 |

{name}

17 |
{cuisines.join(", ")}
18 | 19 |
{ 20 | avgRating < 4.2 21 | ?

⭐{avgRating}

22 | :

⭐{avgRating}

23 | } 24 |
25 |

26 |

{costForTwo ?? '₹200 for two'}

27 |
28 |
29 | 30 | 31 | ) 32 | } 33 | export default RestrauntCard; 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | const About = () => { 2 | return ( 3 |
4 |

Some important information about project

5 |
6 | 18 | 27 |
28 | ) 29 | } 30 | export default About; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Namaste React Series 🚀 2 | 3 | ### [Namaste React Live Course](https://learn.namastedev.com/courses/namaste-react-live) from Zero to Hero 🚀 by [Akshay Saini](https://www.linkedin.com/in/akshaymarch7/) Founder of [NamasteDev](https://courses.namastedev.com/learn/Namaste-React). 4 | 5 | - I made [🚀 foodie-apps 😍](https://foodie-apps.netlify.app//) from scratch using React.js and Parcel.js, which is the part of this course. 6 | 7 | 8 | ## To Clone this Repository 9 | 10 | You need to write the following commands on the terminal screen(in vscode) so that you can run this project locally. 11 | 12 | ```bash 13 | git clone "https://github.com/RohitSharma50/food-orderinf-website" 14 | ``` 15 | 16 | Go to the project directory 17 | 18 | ```bash 19 | cd food-orderinf-website 20 | ``` 21 | 22 | Install dependencies 23 | 24 | ```bash 25 | npm install 26 | ``` 27 | 28 | Start the server 29 | 30 | ```bash 31 | npm run start 32 | ``` 33 | 34 | After doing this this application should now be running on `localhost`. If you want to Fork repository and want to run locally, follow this guidelines [Fork and Clone Github Repository](https://docs.github.com/en/get-started/quickstart/fork-a-repo) 35 | 36 | # 37 | ## Key Features 38 | ◉ Multi Select Cuisines Based Restaurant Filter. 39 | ◉ Search Based Restaurants Filter. 40 | ◉ Shimmer UI 41 | ◉ CORS Extension For Fetching Swiggy Live API Data from Swiggy Public APIs. 42 | ◉ Tailwind CSS 43 | ◉ Class Based Components. 44 | ◉ React Router DOM for routing & navigation 45 | ◉ Lazy Loading 46 | ◉ Context API 47 | ◉ Lifting The State Up 48 | 49 | 50 | 51 | 52 | ## 🔗 Let's Connect 53 | 54 | [![linkedin](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/rohit-sharma50/) 55 | -------------------------------------------------------------------------------- /src/components/Cart.js: -------------------------------------------------------------------------------- 1 | import {useSelector} from "react-redux"; 2 | import FoodItem from "./FoodItem"; 3 | import { useState } from 'react'; 4 | import {total} from "./FoodItem"; 5 | 6 | 7 | const Cart = () => { 8 | 9 | const cartItems = useSelector(store =>store.cart.items); 10 | 11 | 12 | const [isOrder, setIsOrder] = useState(false); 13 | const handleOrderClick = () => { 14 | if(!cartItems.length){ 15 | alert("Please add Value to cart"); 16 | return; 17 | } 18 | setIsOrder(true); 19 | }; 20 | const total = cartItems.reduce((sum, item) => { 21 | return sum + (item.price ? item.price : item.defaultPrice) / 100; 22 | }, 0); 23 | 24 | 25 | return ( 26 |
27 | 28 | { 29 | cartItems.map((item) =>( 30 | 31 | 32 | // {total +=(item.price ? item.price : item.defaultPrice)/100} 33 | )) 34 | } 35 |
36 |
37 |

Total in INR - {total}

39 |
40 | 44 | {isOrder && ( 45 |
46 | Your order has been placed successfully! 47 |
)} 48 |
49 | ) 50 | } 51 | export default Cart; -------------------------------------------------------------------------------- /src/components/Contact.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import contact from "../Images/Contact-Us.png" 3 | 4 | const Contact = () => { 5 | const [message, setMessage] = useState(false); 6 | const handleSubmit = (e) => { 7 | e.preventDefault(); 8 | setMessage(true); 9 | } 10 | return ( 11 |
12 |
13 | 14 |
15 |
16 |

Contact us

17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | {message && 🙏 Thanks for contacting Us, We will reply ASAP 👨‍🍳.} 25 |
26 |
27 |
28 | ); 29 | }; 30 | export default Contact; -------------------------------------------------------------------------------- /src/components/Instamart.js: -------------------------------------------------------------------------------- 1 | 2 | // import { useState } from "react"; 3 | 4 | // const Section = ({ title, description, isVisible, setIsVisible }) => { 5 | // return ( 6 | //
7 | //

{title}

8 | // {isVisible ? ( 9 | // 15 | // ) : ( 16 | // 21 | // )} 22 | // {isVisible &&

{description}

} 23 | //
24 | 25 | // ) 26 | // }; 27 | // const Instamart = () => { 28 | // const [visibleSection, setIsVisibleSection] = useState("Team"); 29 | // return ( 30 | //
31 | //

InstaMart

32 | //
setIsVisibleSection("about")} 38 | // /> 39 | //
setIsVisibleSection("Career")} 45 | // /> 46 | //
47 | // ); 48 | // }; 49 | // export default Instamart; -------------------------------------------------------------------------------- /src/utils/useRestaurant.js: -------------------------------------------------------------------------------- 1 | 2 | import { useEffect, useState } from "react"; 3 | 4 | const useRestaurant = ( 5 | swiggy_menu_api_URL, 6 | resId, 7 | RESTAURANT_TYPE_KEY, 8 | MENU_ITEM_TYPE_KEY 9 | ) => { 10 | const [restaurant, setRestaurant] = useState(null); // use useState to store restaurant data 11 | const [menuItems, setMenuItems] = useState([]); // use useState to store restaurant Menu Item data 12 | 13 | useEffect(() => { 14 | getRestaurantInfo(); // call getRestaurantInfo function so it fetch api data and set data in restaurant state variable 15 | }, []); 16 | 17 | async function getRestaurantInfo() { 18 | try { 19 | const response = await fetch(swiggy_menu_api_URL + resId); 20 | if (!response.ok) { 21 | const err = response.status; 22 | throw new Error(err); 23 | } else { 24 | const json = await response.json(); 25 | 26 | // Set restaurant data 27 | const restaurantData = 28 | json?.data?.cards 29 | ?.map((x) => x.card) 30 | ?.find((x) => x && x.card["@type"] === RESTAURANT_TYPE_KEY)?.card 31 | ?.info || null; 32 | setRestaurant(restaurantData); 33 | 34 | // Set menu item data 35 | const menuItemsData = 36 | json?.data?.cards 37 | .find((x) => x.groupedCard) 38 | ?.groupedCard?.cardGroupMap?.REGULAR?.cards?.map( 39 | (x) => x.card?.card 40 | ) 41 | ?.filter((x) => x["@type"] == MENU_ITEM_TYPE_KEY) 42 | ?.map((x) => x.itemCards) 43 | .flat() 44 | .map((x) => x.card?.info) || []; 45 | 46 | const uniqueMenuItems = []; 47 | menuItemsData.forEach((item) => { 48 | if (!uniqueMenuItems.find((x) => x.id === item.id)) { 49 | uniqueMenuItems.push(item); 50 | } 51 | }); 52 | setMenuItems(uniqueMenuItems); 53 | } 54 | } catch (err) { 55 | setMenuItems([]); 56 | setRestaurant(null); 57 | console.error(err); 58 | } 59 | } 60 | return [restaurant, menuItems]; 61 | }; 62 | 63 | export default useRestaurant; -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | 2 | import { useState } from "react"; 3 | import logo from "../assets/foodie.jpg"; 4 | import { Link } from "react-router-dom"; 5 | import useOnline from "../utils/useOnline"; 6 | import React from "react"; 7 | import { useSelector } from "react-redux"; 8 | 9 | const Title = () => { 10 | return ( 11 | 12 | Logo 16 | 17 | ); 18 | } 19 | 20 | 21 | const Header = () => { 22 | const [isLoggedIn, setIsLoggedIn] = useState(true); 23 | 24 | const isOnline = useOnline(); 25 | const cartItems=useSelector(store=>store.cart.items); 26 | // console.log(cartItems); 27 | return ( 28 |
29 | 30 | <div > 31 | <ul className="flex py-2 pr-2 text-2xl "> 32 | <li className="px-2 hover:text-white hover:text-xl rounded-md"> 33 | <Link to="/home ">Home</Link> 34 | </li> 35 | <li className="px-2 hover:text-white hover:text-m rounded-md"> 36 | <Link to="/contact">Contact</Link> 37 | </li> 38 | <li className="px-2 hover:text-white hover:text-m rounded-md"> 39 | <Link to="/About">About</Link> 40 | </li> 41 | <li className="px-2 hover:text-white hover:text-m rounded-md"> 42 | <Link to="/cart">Cart</Link> 43 | </li> 44 | <li>{cartItems.length? cartItems.length : " "}</li> 45 | 46 | </ul> 47 | <div className="grid justify-items-start"> 48 | <ul className="status px-1" > 49 | {/* <li> 50 | {isLoggedIn ? ( 51 | <button onClick={() => setIsLoggedIn(false)}>Logout</button> 52 | ) : ( 53 | <button onClick={() => setIsLoggedIn(true)}>Login</button>) 54 | 55 | } 56 | </li> */} 57 | 58 | <li > 59 | <h3 >{isOnline ? "✅" : "🔴"} </h3> 60 | 61 | </li> 62 | </ul> 63 | </div> 64 | 65 | 66 | 67 | </div> 68 | </div> 69 | 70 | ) 71 | 72 | }; 73 | export default Header; 74 | -------------------------------------------------------------------------------- /src/components/Body.js: -------------------------------------------------------------------------------- 1 | import RestrauntCard from "./RestaurantCard"; 2 | import { useEffect, useState } from "react"; 3 | import Shimmer from "./Shimmer"; 4 | import { Link } from "react-router-dom"; 5 | import { filterData } from "../utils/helper"; 6 | 7 | 8 | 9 | 10 | 11 | const Body = () => { 12 | const [allRestaurant, setAllRestaurant] = useState(""); 13 | const [searchText, setSearchText] = useState(""); 14 | const [filterdRestaurant, setFilterdRestaurant] = useState([]); 15 | 16 | useEffect(() => { 17 | getRestaurants(); 18 | }, []); 19 | 20 | const getRestaurants = async () => { 21 | try { 22 | const data = await fetch( 23 | "https://foodfire.onrender.com/api/restaurants?lat=21.1702401&lng=72.83106070000001&page_type=DESKTOP_WEB_LISTING"); 24 | 25 | const json = await data.json(); 26 | 27 | 28 | // was showing an error of data fatching because sometime data coming from cards[1] sometime cards[2] and different on other times so me make a function and check which value of i gives data in cards[i] 29 | async function checkJsonData(jsonData) { 30 | 31 | for (let i = 0; i < jsonData?.data?.cards.length; i++) { 32 | 33 | // initialize checkData for Swiggy Restaurant data 34 | let checkData = json?.data?.cards[i]?.card?.card?.gridElements?.infoWithStyle?.restaurants; 35 | 36 | // if checkData is not undefined then return it 37 | if (checkData !== undefined) { 38 | return checkData; 39 | } 40 | } 41 | } 42 | // call the checkJsonData() function which return Swiggy Restaurant data 43 | const resData = await checkJsonData(json); 44 | 45 | // update the state variable restaurants with Swiggy API data 46 | setAllRestaurant(resData); 47 | setFilterdRestaurant(resData); 48 | } catch (error) { 49 | console.log(error); 50 | } 51 | 52 | } 53 | 54 | 55 | 56 | 57 | 58 | return (allRestaurant?.length === 0) ? ( 59 | <Shimmer /> 60 | 61 | ) : ( 62 | <> 63 | <div className="p-5 bg-pink-50 my-1 "> 64 | <input 65 | type="text" 66 | className="Search-input" 67 | placeholder="Search" 68 | value={searchText} 69 | onChange={(e) => { 70 | setSearchText(e.target.value); 71 | }} 72 | /> 73 | 74 | 75 | <button 76 | className="p-3 m-2 bg-purple-600 text-white rounded-lg hover:bg-green-800" 77 | onClick={() => { 78 | const data = filterData(searchText, allRestaurant); 79 | setFilterdRestaurant(data); 80 | 81 | }} 82 | >Search</button> 83 | </div> 84 | 85 | <div className="resturant-list flex flex-wrap justify-center"> 86 | {(filterdRestaurant?.length === 0 ? <h1>No data match your filter</h1> : 87 | filterdRestaurant?.map((restaurant) => { 88 | return ( 89 | <Link 90 | key={restaurant?.info.id} 91 | to={"/restaurant/" + restaurant?.info.id} 92 | 93 | > 94 | <RestrauntCard {...restaurant?.info} /> 95 | </Link> 96 | ); 97 | }) 98 | )} 99 | 100 | 101 | 102 | </div> 103 | 104 | </> 105 | 106 | ) 107 | }; 108 | 109 | export default Body; 110 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * <---***Thinge bundle doing for us***---> 4 | * Create a server 5 | * HRM- HOt Module Reloading 6 | * File Watcher algorithum -C++ 7 | * Building 8 | * MInify 9 | * Cleaning our Code 10 | * Dev and Production Build 11 | * Super fast build Algorithum 12 | * Image Optimization 13 | * Caching while development (image should in project ) 14 | * Compression 15 | * Compatible with older version of browser 16 | * HTTPS on dev 17 | * parcel manages port number 18 | * Consistent hashing Algorithum 19 | * Zero configuration 20 | * 21 | * 22 | */ 23 | 24 | import React, { Suspense, lazy } from "react"; 25 | import ReactDOM from "react-dom/client"; 26 | import Header from "./components/Header"; 27 | import Body from "./components/Body"; 28 | import { Footer } from "./components/Footer"; 29 | import Error from "./components/Error"; 30 | import { createBrowserRouter, RouterProvider, Outlet } from "react-router-dom"; 31 | import Contact from "./components/Contact"; 32 | import RestaurantMenu from "./components/RestrauntMenu"; 33 | import Cart from "./components/Cart"; 34 | import { Profiler } from "react"; 35 | import {Provider} from "react-redux"; 36 | import store from "./utils/store" 37 | const About = lazy(() => import("./components/About")); 38 | //const Instamart = lazy(() => import("./components/Instamart")) 39 | 40 | function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) { 41 | // Aggregate or log render timings... 42 | } 43 | 44 | const AppLayout = () => { 45 | return ( 46 | 47 | <Provider store= {store}> 48 | <Profiler id="Sidebar" onRender={onRender}> 49 | <Header /> 50 | </Profiler> 51 | <Profiler id="Sidebar" onRender={onRender}> 52 | <Outlet /> 53 | </Profiler> 54 | <Profiler id="Sidebar" onRender={onRender}> 55 | <Footer /> 56 | </Profiler> 57 | 58 | </Provider> 59 | 60 | ); 61 | }; 62 | 63 | const appRouter = createBrowserRouter([ 64 | { 65 | path: "/", 66 | element: <AppLayout />, 67 | errorElement: <Error />, 68 | children: [ 69 | { 70 | path: "/", 71 | element: 72 | <Suspense> 73 | <Body className="flex flex-wrap m-2 p-1 max-w-screen-2xl justify-center" /> 74 | </Suspense> 75 | 76 | }, 77 | { 78 | path: "/About", 79 | element: 80 | <Suspense> 81 | <About /> 82 | </Suspense> 83 | }, 84 | { 85 | path: "/contact", 86 | element: 87 | <Suspense> <Contact /> </Suspense> 88 | }, 89 | { 90 | path: "/home", 91 | element: <Body /> 92 | }, 93 | { 94 | path: "/restaurant/:resId", 95 | element: 96 | <Suspense> 97 | <RestaurantMenu /> 98 | </Suspense> 99 | }, 100 | { 101 | path: "/cart", 102 | element: 103 | <Suspense> 104 | <Cart /> 105 | </Suspense> 106 | 107 | }, 108 | // { 109 | // path: "/instamart", 110 | // element: 111 | // <Suspense> 112 | // <Instamart /> 113 | // </Suspense> 114 | // }, 115 | ] 116 | }, 117 | 118 | ] 119 | ); 120 | const root = ReactDOM.createRoot(document.getElementById("root")); 121 | 122 | root.render(<RouterProvider router={appRouter} />); -------------------------------------------------------------------------------- /src/components/RestrauntMenu.js: -------------------------------------------------------------------------------- 1 | import { useParams } from "react-router-dom"; 2 | import { 3 | swiggy_menu_api_URL, 4 | IMG_CDN_URL, 5 | MENU_ITEM_TYPE_KEY, 6 | RESTAURANT_TYPE_KEY, 7 | } from "../utils/Constant"; 8 | import Shimmer from "./Shimmer"; 9 | import useRestaurant from "../utils/useRestaurant"; 10 | import { addItem } from "../utils/cartSlice"; 11 | import { useDispatch } from "react-redux"; 12 | 13 | const RestaurantMenu = () => { 14 | const { resId } = useParams(); 15 | const [restaurant, menuItems] = useRestaurant( 16 | swiggy_menu_api_URL, 17 | resId, 18 | RESTAURANT_TYPE_KEY, 19 | MENU_ITEM_TYPE_KEY 20 | ); 21 | const dispatch = useDispatch(); 22 | 23 | const addFoodItem = (item) => { 24 | dispatch(addItem(item)); 25 | console.log(item); 26 | 27 | }; 28 | 29 | 30 | return !restaurant ? ( 31 | <Shimmer /> 32 | ) : ( 33 | <div className="restaurant-menu mx-auto min-h-screen w-auto p-2"> 34 | <div className="restaurant-summary flex h-52 justify-center align-middle overflow-y-hidden bg-slate-800 text-cyan-50"> 35 | <img 36 | className="restaurant-img w-64 h-44 border-r-4 mt-4" 37 | src={IMG_CDN_URL + restaurant?.cloudinaryImageId} 38 | alt={restaurant?.name} 39 | /> 40 | <div className="restaurant-summary-details flex flex-col m-5"> 41 | <h2 className="restaurant-title text-2xl max-w-lg text-opacity-70"> 42 | {restaurant?.name} 43 | </h2> 44 | <p className="restaurant-tags flex-nowrap opacity-70 text-base max-w-lg"> 45 | {restaurant?.cuisines?.join(", ")} 46 | </p> 47 | </div> 48 | </div> 49 | 50 | <div className="restaurant-menu-content flex justify-center p-3"> 51 | <div className="menu-items-container mt-2 max-w-3xl"> 52 | <div className="menu-title-wrap p-5"> 53 | <h3 className="menu-title text-zinc-600">Recommended</h3> 54 | <p className="menu-count mt-2">{menuItems?.length} ITEMS</p> 55 | </div> 56 | <div className="menu-items-list flex justify-between flex-col"> 57 | {menuItems.map((item) => ( 58 | <div className="menu-item flex flex-col" key={item?.id}> 59 | <div className="menu-item-details"> 60 | <h3 className="item-title flex flex-initial overflow-hidden text-2xl"> 61 | {item?.name} 62 | </h3> 63 | <p className="item-cost"> 64 | { (item.price? item.price : item.defaultPrice)/100 > 0 65 | ? new Intl.NumberFormat("en-IN", { 66 | style: "currency", 67 | currency: "INR", 68 | }).format((item.price? item.price : item.defaultPrice)/100) 69 | : " "} 70 | </p> 71 | </div> 72 | <div> 73 | <div className="flex justify-start text-gray-500"> 74 | {item?.description} 75 | </div> 76 | <div className="text-right"> 77 | <button 78 | className="add-btn justify-center pr-1.5 w-16 h-8 bg-orange-700 text-end rounded-lg" 79 | onClick={() => addFoodItem(item)} 80 | > 81 | ADD+ 82 | </button> 83 | </div> 84 | </div> 85 | <hr className="h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"></hr> 86 | </div> 87 | ))} 88 | </div> 89 | </div> 90 | </div> 91 | </div> 92 | ); 93 | }; 94 | 95 | export default RestaurantMenu; 96 | --------------------------------------------------------------------------------