Skip to content

Commit bd7af66

Browse files
committed
Generate Random Recipe feature done
1 parent 5d0191e commit bd7af66

File tree

4 files changed

+316
-1
lines changed

4 files changed

+316
-1
lines changed

app.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ app.get("/inputIngredient", (req, res) => {
2929
res.render("inputIngredient");
3030
});
3131

32+
app.get("/inputRandom", (req, res) => {
33+
res.render("inputRandom");
34+
});
35+
3236
// POST request to render menus for a week based on user input
3337

3438
app.post("/mealList", async (req, res) => {
@@ -123,6 +127,50 @@ app.post("/ingredientList", async (req, res) => {
123127
}
124128
});
125129

130+
//POST request to generate random list of recipes based on tags provided by user
131+
132+
app.post("/randomList", async (req, res) => {
133+
// Tags and number provided by user
134+
135+
const number = req.body.number;
136+
let tags = req.body.tags;
137+
if (typeof tags === "strings") {
138+
tags = req.body.tags.split(",");
139+
}
140+
141+
try {
142+
const { default: fetch } = await import("node-fetch");
143+
const generateRandomRecipes =
144+
await fetch(`https://api.spoonacular.com/recipes/random?number=${number}&tags=${tags}&apiKey=${apiKey}
145+
`);
146+
147+
const randomData = await generateRandomRecipes.json();
148+
149+
// Generate Recipe information including nutrients
150+
151+
const recipeInfoPromises = randomData.recipes.map(async (recipe) => {
152+
const recipeInfo = await fetch(
153+
`https://api.spoonacular.com/recipes/${recipe.id}/nutritionWidget.json?apiKey=${apiKey}`
154+
);
155+
const infoData = await recipeInfo.json();
156+
return {
157+
...recipe,
158+
nutrients: infoData,
159+
};
160+
});
161+
162+
// Wait for all recipe nutrient information to be fetched
163+
const recipesWithNutrients = await Promise.all(recipeInfoPromises);
164+
165+
res.render("randomMeals", {
166+
recipes: recipesWithNutrients,
167+
});
168+
} catch (error) {
169+
res.send(error);
170+
console.log(error);
171+
}
172+
});
173+
126174
app.get("/recipe/:mealId", async (req, res) => {
127175
const mealId = req.params.mealId;
128176

views/home.ejs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@
5858
</button>
5959
</a>
6060
<br> <br>
61-
<button
61+
<a href="/inputRandom">
62+
<button
6263
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"
6364
type="button">Try something new today
6465
</button>
66+
</a>
6567
<br> <br>
6668

6769

views/inputRandom.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="/randomList">
58+
<div class="mb-4">
59+
<label class="block text-gray-700 font-bold mb-2" for="include">
60+
Tags
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 tags (can be diets, meal types, cuisines, or intolerances) that the recipe must have.&#10;&#10;Eg- vegetarian, dessert"
67+
name="tags"
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 number of random recipes to be returned (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>

views/randomMeals.ejs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
<style>
14+
table {
15+
font-family: arial, sans-serif;
16+
border-collapse: collapse;
17+
width: 100%;
18+
}
19+
td,
20+
th {
21+
border: 1px solid #ff8400;
22+
text-align: left;
23+
padding: 8px;
24+
}
25+
tr:nth-child(even) {
26+
background-color: #f6f1e9;
27+
}
28+
</style>
29+
</head>
30+
<body class="font-mono bg-gray-400">
31+
<nav
32+
x-data="{show:false}"
33+
class="flex items-center justify-between flex-wrap bg-orange-600 p-6"
34+
>
35+
<div class="flex items-center flex-shrink-0 text-white mr-6">
36+
<span class="font-semibold text-xl tracking-tight">Plan Your Meal</span>
37+
</div>
38+
<div class="block md:hidden">
39+
<button
40+
@click="show=!show"
41+
class="flex items-center px-3 py-2 border rounded text-gray-100 border-gray-200 hover:text-white hover:border-white"
42+
>
43+
<svg
44+
class="fill-current h-3 w-3"
45+
viewBox="0 0 20 20"
46+
xmlns="http://www.w3.org/2000/svg"
47+
>
48+
<title>Menu</title>
49+
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
50+
</svg>
51+
</button>
52+
</div>
53+
<div
54+
@click.away="show = false"
55+
:class="{ 'block': show, 'hidden': !show }"
56+
class="w-full block flex-grow md:flex md:justify-end md:w-auto"
57+
>
58+
<div>
59+
<!-- <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> -->
60+
<a
61+
href="#"
62+
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"
63+
>Dashboard</a
64+
>
65+
<a
66+
href="/"
67+
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"
68+
>Home</a
69+
>
70+
</div>
71+
</div>
72+
</nav>
73+
<div
74+
style="
75+
text-align: center;
76+
margin-top: 20px;
77+
font-size: larger;
78+
font-weight: 600;
79+
color: orange;
80+
"
81+
>
82+
<h1 style="font-size: 50px">Welcome to your meal planning journey.</h1>
83+
<a href="/inputRandom">
84+
<button
85+
id="search"
86+
class="bg-orange-500 hover:bg-orange-700 text-white font-bold py-2 px-4 border border-orange-700 rounded"
87+
>
88+
Create random recipes
89+
</button>
90+
</a>
91+
</div>
92+
<div>
93+
<div style="text-align: center; margin-top: 5px">
94+
<button
95+
id="downloadBtn"
96+
class="bg-orange-400 hover:bg-orange-700 text-white font-semibold py-2 px-4 mr-500 ml-500 p-3 mt-3 rounded focus:outline-none focus:shadow-outline"
97+
>
98+
Download as PDF
99+
</button>
100+
</div>
101+
<div id="result">
102+
<table style="margin-top: 5%">
103+
<tr>
104+
<th>Meal</th>
105+
<th>Calories</th>
106+
<th>Carbs</th>
107+
<th>Protein</th>
108+
<th>Fat</th>
109+
</tr>
110+
<tbody id="table_body">
111+
<% recipes.forEach(function(recipe){ %>
112+
<tr>
113+
<td>
114+
<a href="/recipe/<%= recipe.id %>"><%= recipe.title %></a>
115+
</td>
116+
<td><%= recipe.nutrients.calories %></td>
117+
<td><%= recipe.nutrients.carbs %></td>
118+
<td><%= recipe.nutrients.protein %></td>
119+
<td><%= recipe.nutrients.fat %></td>
120+
</tr>
121+
<% }) %>
122+
</tbody>
123+
</table>
124+
</div>
125+
</div>
126+
<script
127+
src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
128+
integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg=="
129+
crossorigin="anonymous"
130+
referrerpolicy="no-referrer"
131+
></script>
132+
133+
<script>
134+
window.onload = function () {
135+
const btn = document.getElementById("downloadBtn");
136+
btn.addEventListener("click", () => {
137+
const menu = this.document.getElementById("result");
138+
const options = {
139+
filename: "menu.pdf",
140+
image: { type: "jpeg", quality: 0.98 },
141+
jsPDF: { format: "letter", orientation: "landscape" },
142+
};
143+
html2pdf().from(menu).set(options).save();
144+
});
145+
};
146+
</script>
147+
</body>
148+
</html>

0 commit comments

Comments
 (0)