Skip to content

Commit 78dbc1e

Browse files
committed
Added the code to plan meals based on user Input
1 parent 43056b3 commit 78dbc1e

File tree

9 files changed

+958
-0
lines changed

9 files changed

+958
-0
lines changed

app.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// const dotenv = require("dotenv");
2+
// dotenv.config({ path: "./config.env" });
3+
const express = require("express");
4+
const bodyParser = require("body-parser");
5+
const striptags = require("striptags");
6+
const cheerio = require("cheerio");
7+
8+
const ejs = require("ejs");
9+
10+
const app = express();
11+
12+
app.set("view engine", "ejs");
13+
app.use(bodyParser.urlencoded({ extended: true }));
14+
app.use(express.static("public"));
15+
16+
const apiKey = "c7d40af634094a53a02a542268b9f073";
17+
18+
// All the get request to render differrent page.
19+
20+
app.get("/", (req, res) => {
21+
res.render("home");
22+
});
23+
24+
app.get("/inputWeekPlanMeals", (req, res) => {
25+
res.render("inputWeekPlanMeals");
26+
});
27+
28+
app.get("/inputIngredient", (req, res) => {
29+
res.render("inputIngredient");
30+
});
31+
32+
// POST request to render menus for a week based on user input
33+
34+
app.post("/mealList", async (req, res) => {
35+
// Variables for meal planning based on user input
36+
const targetCalories = req.body.targetCalories;
37+
let diets = req.body.diets;
38+
if (typeof diets === "string") {
39+
diets = req.body.diets.split(",");
40+
}
41+
let cuisines = req.body.cusines;
42+
if (typeof cuisines === "string") {
43+
cuisines = req.body.cuisines.split(",");
44+
}
45+
let excludeIngredients = req.body.excludeIngredients;
46+
if (typeof excludeIngredients === "string") {
47+
excludeIngredients = req.body.excludeIngredients.split(",");
48+
}
49+
let includeIngredients = req.body.includeIngredients;
50+
if (typeof includeIngredients === "string") {
51+
includeIngredients = req.body.includeIngredients.split(",");
52+
}
53+
let preferences = req.body.preferences;
54+
if (typeof preferences === "string") {
55+
preferences = req.body.preferences.split(",");
56+
}
57+
58+
try {
59+
const { default: fetch } = await import("node-fetch");
60+
61+
// Week meal plan using user input
62+
const weekPlan = await fetch(
63+
`https://api.spoonacular.com/mealplanner/generate?apiKey=${apiKey}&timeFrame=week&targetCalories=${targetCalories}&diet=${diets}&excludeIngredients=${excludeIngredients}&includeIngredients=${includeIngredients}&cuisine=${cuisines}&preferences=${preferences}`
64+
);
65+
66+
const dataOfMeal = await weekPlan.json();
67+
const days = await dataOfMeal.week;
68+
69+
res.render("planMeals", { meals: days });
70+
} catch (error) {
71+
res.send("Sorry! Some error occurred. Please try again!");
72+
console.log(error);
73+
}
74+
});
75+
76+
//POST request to generate recipe based on ingredients provided by user
77+
78+
app.post("/ingredientList", async (req, res) => {
79+
// Storing the input provided by user in a variable in backend
80+
81+
let ingredients = req.body.ingredients;
82+
if (typeof ingredients === "string") {
83+
ingredients = req.body.ingredients.split(",");
84+
}
85+
86+
let number = req.body.number;
87+
if (typeof number === "string") {
88+
number = req.body.number.split(",");
89+
}
90+
91+
try {
92+
const { default: fetch } = await import("node-fetch");
93+
94+
//Generate recipes by ingredients
95+
const generateRecipesByIngredients = await fetch(
96+
`https://api.spoonacular.com/recipes/findByIngredients?ingredients=${ingredients}&number=${number}&apiKey=${apiKey}`
97+
);
98+
const recipesData = await generateRecipesByIngredients.json();
99+
100+
// Generate recipe information
101+
102+
const recipeInfo = await fetch(
103+
`https://api.spoonacular.com/recipes/complexSearch?apiKey=c7d40af634094a53a02a542268b9f073`
104+
);
105+
const results = recipeInfo.json();
106+
107+
res.render("planMealsByIngredients", {
108+
recipes: recipesData,
109+
results: results,
110+
});
111+
112+
// res.send(recipesData);
113+
} catch (error) {
114+
res.send(error);
115+
console.log(error);
116+
}
117+
});
118+
119+
app.get("/recipe/:mealId", async (req, res) => {
120+
const mealId = req.params.mealId;
121+
122+
const { default: fetch } = await import("node-fetch");
123+
124+
//Recipe info based on meal id
125+
const recipeInfo =
126+
await fetch(`https://api.spoonacular.com/recipes/${mealId}/information?apiKey=${apiKey}&includeNutrition=true
127+
`);
128+
const info = await recipeInfo.json();
129+
130+
// Summary of the recipe based on meal id
131+
132+
const summaryInfo = await fetch(
133+
`https://api.spoonacular.com/recipes/${mealId}/summary?apiKey=${apiKey}`
134+
);
135+
const summary = await summaryInfo.json();
136+
137+
//Ingredient info based on meal id
138+
const ingredientsInfo =
139+
await fetch(`https://api.spoonacular.com/recipes/${mealId}/ingredientWidget.json?apiKey=${apiKey}
140+
`);
141+
const ingredientsData = await ingredientsInfo.json();
142+
143+
res.render("recipe", {
144+
title: info.title,
145+
readyInMinutes: info.readyInMinutes,
146+
image: info.image,
147+
nutrition: info.nutrition,
148+
summary: striptags(summary.summary),
149+
analyzedInstructions: info.analyzedInstructions,
150+
ingredients: ingredientsData.ingredients,
151+
});
152+
// res.send(summary.summary);
153+
});
154+
155+
module.exports.main = app;

public/css/styles.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
body * {
2+
font-family: 'Montserrat', sans-serif;
3+
}

public/images/food.png

151 KB
Loading

views/home.ejs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Meal Planning App</title>
8+
<link rel="preconnect" href="https://fonts.googleapis.com">
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10+
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
11+
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
12+
<link rel="stylesheet" href="/css/styles.css">
13+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
14+
</head>
15+
<body>
16+
17+
18+
<!--nav-->
19+
<nav x-data="{show:false}" class="flex items-center justify-between flex-wrap bg-orange-600 p-6">
20+
<div class="flex items-center flex-shrink-0 text-white mr-6">
21+
<span class="font-semibold text-xl tracking-tight">Plan Your Meal</span>
22+
</div>
23+
<div class="block md:hidden">
24+
<button @click="show=!show" class="flex items-center px-3 py-2 border rounded text-gray-100 border-gray-200 hover:text-white hover:border-white">
25+
<svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
26+
</button>
27+
</div>
28+
</nav>
29+
30+
<!--Body-->
31+
<!-- component -->
32+
<body class="font-mono bg-gray-400">
33+
<!-- Container -->
34+
<div class="container mx-auto">
35+
<div class="flex justify-center px-6 my-12">
36+
<!-- Row -->
37+
<div class="w-full xl:w-3/4 lg:w-11/12 flex">
38+
<!-- Col -->
39+
<div
40+
class="w-full h-auto bg-gray-400 hidden lg:block lg:w-5/12 bg-cover rounded-l-lg"
41+
style="background-image: url('images/food.png')"
42+
></div>
43+
<!-- Col -->
44+
<div class="w-full lg:w-7/12 bg-white p-5 rounded-lg lg:rounded-l-none">
45+
<h3 class="pt-4 text-2xl text-center">Welcome - Let's plan some meals!</h3>
46+
<form class="px-8 pt-6 pb-8 mb-4 bg-white rounded">
47+
<a href="/inputWeekPlanMeals">
48+
<button
49+
class="w-full px-4 py-2 font-bold text-white bg-yellow-500 rounded-full hover:bg-yellow-700 focus:outline-none focus:shadow-outline"
50+
type="button"> Create your weekly menu
51+
</button>
52+
</a>
53+
<br> <br>
54+
<a href="/inputIngredient">
55+
<button
56+
class="w-full px-4 py-2 font-bold text-white bg-indigo-500 rounded-full hover:bg-indigo-700 focus:outline-none focus:shadow-outline"
57+
type="button">What's in your fridge
58+
</button>
59+
</a>
60+
<br> <br>
61+
<button
62+
class="w-full px-4 py-2 font-bold text-white bg-blue-500 rounded-full hover:bg-blue-700 focus:outline-none focus:shadow-outline"
63+
type="button">Try something new today
64+
</button>
65+
<br> <br>
66+
67+
68+
</div>
69+
70+
</form>
71+
72+
</div>
73+
74+
</div>
75+
76+
</div>
77+
78+
</div>
79+
</body>
80+
</body>
81+
</html>

views/inputIngredient.ejs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Meal Planning App</title>
8+
<link
9+
href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
10+
rel="stylesheet"
11+
/>
12+
<link href="css/styles.css" rel="stylesheet" />
13+
</head>
14+
15+
<body class="font-mono bg-gray-400">
16+
<!--nav-->
17+
<nav
18+
x-data="{show:false}"
19+
class="flex items-center justify-between flex-wrap bg-orange-600 p-6"
20+
>
21+
<div class="flex items-center flex-shrink-0 text-white mr-6">
22+
<span class="font-semibold text-xl tracking-tight">Plan Your Meal</span>
23+
</div>
24+
<div class="block md:hidden">
25+
<button
26+
@click="show=!show"
27+
class="flex items-center px-3 py-2 border rounded text-gray-100 border-gray-200 hover:text-white hover:border-white"
28+
>
29+
<svg
30+
class="fill-current h-3 w-3"
31+
viewBox="0 0 20 20"
32+
xmlns="http://www.w3.org/2000/svg"
33+
>
34+
<title>Menu</title>
35+
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
36+
</svg>
37+
</button>
38+
</div>
39+
<div
40+
@click.away="show = false"
41+
:class="{ 'block': show, 'hidden': !show }"
42+
class="w-full block flex-grow md:flex md:justify-end md:w-auto"
43+
>
44+
<div>
45+
<!-- <a href="#" class="block md:inline-block text-lg px-4 py-2 leading-none rounded text-white border-white hover:border-transparent hover:text-orange-500 hover:bg-white mt-4 md:mt-0">Compose Post</a> -->
46+
<!-- <a href="#" class="block md:inline-block text-lg px-4 py-2 leading-none rounded text-white border-white hover:border-transparent hover:text-orange-500 hover:bg-white mt-4 md:mt-0">Dashboard</a> -->
47+
<a
48+
href="/"
49+
class="block md:inline-block text-lg px-4 py-2 leading-none rounded text-orange border-white hover:border-transparent bg-white mt-4 md:mt-0"
50+
>Home</a
51+
>
52+
</div>
53+
</div>
54+
</nav>
55+
56+
<br />
57+
<form class="max-w-sm mx-auto" , method="post" , action="/ingredientList">
58+
<div class="mb-4">
59+
<label class="block text-gray-700 font-bold mb-2" for="include">
60+
Ingredients
61+
</label>
62+
<textarea
63+
class="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
64+
id="message"
65+
rows="4"
66+
placeholder="Enter the ingredient(s) that you have in your fridge.&#10;&#10;Eg- apples,flour,sugar"
67+
name="ingredients"
68+
required
69+
></textarea>
70+
</div>
71+
<div class="mb-4">
72+
<label class="block text-gray-700 font-bold mb-2" for="preferences">
73+
Number
74+
</label>
75+
<input
76+
class="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
77+
type="number"
78+
placeholder="Enter the maximum number of recipes to return (between 1 and 100)."
79+
name="number"
80+
required
81+
/>
82+
</div>
83+
84+
<div>
85+
<button
86+
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
87+
type="submit"
88+
>
89+
Submit
90+
</button>
91+
</div>
92+
</form>
93+
<br /><br />
94+
95+
<script src="https://cdn.jsdelivr.net/npm/@yaireo/[email protected]/dist/tagify.min.js"></script>
96+
<script>
97+
const select = document.getElementById("diet");
98+
const tagify = new Tagify(select, {
99+
enforceWhitelist: true, // only allow tags from the select options
100+
whitelist: [
101+
"vegan",
102+
"vegetarian",
103+
"gluten-free",
104+
"dairy-free",
105+
"paleo",
106+
], // list of available tags
107+
transformTag: (tagData) => {
108+
tagData.value = tagData.value.toLowerCase(); // convert to lowercase
109+
},
110+
});
111+
select.addEventListener("change", () => {
112+
select.value = ""; // clear the select value
113+
tagify.addTags(select.selectedOptions[0].value); // add the selected tag to Tagify
114+
});
115+
</script>
116+
</body>
117+
</html>

0 commit comments

Comments
 (0)