mewool rawool commited on
Commit
1d1eaa8
·
verified ·
1 Parent(s): b72cf83

Upload index.html

Browse files
Files changed (1) hide show
  1. frontend/templates/index.html +248 -0
frontend/templates/index.html ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>NPChef</title>
7
+ <link rel="icon" href="https://img.icons8.com/ios-filled/100/meal.png" type="image/png">
8
+
9
+ <!-- Optional: For better display on mobile homescreens -->
10
+ <link rel="apple-touch-icon" href="https://img.icons8.com/ios-filled/100/meal.png">
11
+ <meta name="theme-color" content="#FF8C42">
12
+ <script src="https://cdn.tailwindcss.com"></script>
13
+ </head>
14
+ <body class="bg-orange-50 text-gray-800">
15
+ <!-- Hero Section -->
16
+ <!-- <section class="bg-gradient-to-r from-orange-400 to-orange-500 text-white py-20 pt-20 text-center">
17
+ <h1 class="text-5xl font-bold mb-4">AI Recipe Generator</h1>
18
+ <p class="text-lg">Enter your ingredients below and get amazing recipes!</p>
19
+ </section> -->
20
+ <section class="text-center mb-10 bg-gradient-to-r from-orange-400 to-orange-500 text-white py-10 text-center">
21
+ <img src="https://img.icons8.com/ios-filled/100/meal.png" alt="logo" class="mx-auto w-16 h-16 mb-2">
22
+ <h1 class="text-4xl font-extrabold tracking-tight text-white">NPChef Recipe Assistant</h1>
23
+ <p class="text-sm text-white font-bold mt-2">NPChef Recipe Assistant for NPCs = Non-Player Cooks 😉 </p>
24
+ <!-- <p class="text-sm text-white font-bold mt-2"> Enter your ingredients below and get amazing recipes!</p> -->
25
+ </section>
26
+
27
+ <!-- Input Section -->
28
+ <section class="max-w-3xl mx-auto py-10 px-4">
29
+ <h2 class="text-2xl text-orange-400 font-bold mb-4">Enter your ingredients below and get amazing recipes!</h2>
30
+ <p class="text-gray-600 mb-6">NOTE! This service is hosted on an open source server and uses CPU and allow limited resources,so the responses may be wrong and it may take upto 2 minutes to generate the recipe.</p>
31
+ <form id="recipeForm" class="space-y-4">
32
+ <div class="relative">
33
+ <textarea
34
+ id="ingredientsInput"
35
+ class="w-full p-4 rounded-lg shadow border border-orange-300 focus:ring-2 focus:ring-orange-400"
36
+ rows="4"
37
+ placeholder="e.g. rice, tomato, onion, chicken or just the name of the dish you want to make"
38
+ ></textarea>
39
+ <label for="recipeFile" class="absolute bottom-3 right-3 cursor-pointer">
40
+ <img src="/static/attach-file.png" class="w-6 h-6" alt="Upload" title="Upload your recipe file">
41
+ </label>
42
+ <input type="file" id="recipeFile" name="recipeFile" accept=".txt,.pdf" class="hidden" />
43
+ </div>
44
+
45
+
46
+
47
+ <!-- Info-only Box -->
48
+ <strong class="text-orange-500">New</strong><br>
49
+ <div class="relative max-w-3xl border border-dashed border-orange-400 rounded p-3 bg-orange-50 text-sm text-gray-700" style="margin-top: 0.5rem;" id="uploadInfoBox">
50
+ <!-- Tail triangle -->
51
+ <div class="absolute -top-2 right-5 w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-b-[8px] border-b-orange-400"></div>
52
+
53
+
54
+ Got your or your mom’s secret recipes? Upload and get detailed versions!
55
+ </div>
56
+ <!-- Hidden File Input -->
57
+ <input type="file" id="recipeFile" name="recipeFile" accept=".txt,.pdf" class="hidden" />
58
+ <p id="uploadMessage" class="text-sm mt-2 text-green-600 hidden"></p>
59
+ </div>
60
+
61
+ <div class="flex gap-4">
62
+ <button
63
+ type="submit"
64
+ class="bg-orange-500 hover:bg-orange-600 text-white px-6 py-2 rounded-lg shadow"
65
+ >
66
+ Generate Recipe
67
+ </button>
68
+ <button
69
+ type="button"
70
+ onclick="clearInput()"
71
+ class="bg-white text-orange-500 border border-orange-500 hover:bg-orange-100 px-6 py-2 rounded-lg shadow"
72
+ >
73
+ Clear
74
+ </button>
75
+ </div>
76
+
77
+ <!-- Dietary Preferences & Cuisine Type -->
78
+ <div class="mt-6 grid gap-4 md:grid-cols-2">
79
+ <div>
80
+ <label for="diet" class="block font-medium mb-1">Dietary Preference</label>
81
+ <select id="diet" class="w-full p-3 rounded-lg border border-orange-300">
82
+ <option value="">Any</option>
83
+ <option value="vegetarian">Vegetarian</option>
84
+ <option value="vegan">Vegan</option>
85
+ <option value="gluten-free">Gluten-Free</option>
86
+ </select>
87
+ </div>
88
+ <div>
89
+ <label for="cuisine" class="block font-medium mb-1">Cuisine Type</label>
90
+ <select id="cuisine" class="w-full p-3 rounded-lg border border-orange-300">
91
+ <option value="">Any</option>
92
+ <option value="indian">Indian</option>
93
+ <option value="italian">Italian</option>
94
+ <option value="mexican">Mexican</option>
95
+ </select>
96
+ </div>
97
+ </div>
98
+ </form>
99
+
100
+ <!-- Most Used Ingredients -->
101
+ <div class="mt-6">
102
+ <h2 class="text-xl font-semibold mb-2">Most Used Ingredients</h2>
103
+ <div class="flex flex-wrap gap-2">
104
+ <button onclick="fillIngredient('rice')" class="bg-orange-200 hover:bg-orange-300 px-3 py-1 rounded-full">rice</button>
105
+ <button onclick="fillIngredient('onion')" class="bg-orange-200 hover:bg-orange-300 px-3 py-1 rounded-full">onion</button>
106
+ <button onclick="fillIngredient('chicken')" class="bg-orange-200 hover:bg-orange-300 px-3 py-1 rounded-full">chicken</button>
107
+ <button onclick="fillIngredient('garlic')" class="bg-orange-200 hover:bg-orange-300 px-3 py-1 rounded-full">garlic</button>
108
+ <button onclick="fillIngredient('milk')" class="bg-orange-200 hover:bg-orange-300 px-3 py-1 rounded-full">milk</button>
109
+ <button onclick="fillIngredient('bread')" class="bg-orange-200 hover:bg-orange-300 px-3 py-1 rounded-full">bread</button>
110
+ </div>
111
+ </div>
112
+ </section>
113
+
114
+ <!-- Recipe Output Section -->
115
+ <section id="recipeOutput" class="max-w-4xl mx-auto px-4 pb-20">
116
+ <!-- Recipes will appear here -->
117
+ </section>
118
+
119
+ <!-- Footer -->
120
+ <footer class="bg-orange-100 text-center py-9 pt-9 text-sm text-orange-700 mt-20" >
121
+ <!-- Made with ❤️ using AI. Never waste food. -->
122
+ <p class="flex items-center justify-center space-x-1">
123
+ <span>Made with ❤️</span>
124
+ <!-- <svg class="w-5 h-5 text-red-500" fill="currentColor" viewBox="0 0 20 20">
125
+ <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd" />
126
+ </svg> -->
127
+ <span>by <a href="https://mehul-raul.github.io/mehul.dev.portfolio/" class="text-orange-600 hover:text-orange-800 font-medium" target="_blank" rel="noopener noreferrer">Mehul Raul</a></span>
128
+ <span>|</span>
129
+ <span>Never waste food:)</span>
130
+ </p>
131
+ </footer>
132
+
133
+ <script>
134
+ const form = document.getElementById("recipeForm");
135
+ const ingredientsInput = document.getElementById("ingredientsInput");
136
+ const output = document.getElementById("recipeOutput");
137
+ const dietSelect = document.getElementById("diet");
138
+ const cuisineSelect = document.getElementById("cuisine");
139
+ const fileInput = document.getElementById("recipeFile");
140
+
141
+ function fillIngredient(text) {
142
+ ingredientsInput.value = ingredientsInput.value
143
+ ? ingredientsInput.value + ", " + text
144
+ : text;
145
+ ingredientsInput.focus();
146
+ }
147
+
148
+ function clearInput() {
149
+ ingredientsInput.value = "";
150
+ ingredientsInput.focus();
151
+ fileInput.value = "";
152
+
153
+ const uploadMessage = document.getElementById("uploadMessage");
154
+ if (uploadMessage) {
155
+ uploadMessage.textContent = "";
156
+ uploadMessage.classList.add("hidden");
157
+ }
158
+ }
159
+
160
+ document.getElementById("recipeFile").addEventListener("change", function () {
161
+ const file = this.files[0];
162
+ const message = document.getElementById("uploadMessage");
163
+
164
+ if (!file) {
165
+ message.textContent = "No file selected.";
166
+ message.classList.remove("hidden", "text-green-600");
167
+ message.classList.add("text-red-600");
168
+ return;
169
+ }
170
+
171
+ const allowedTypes = ["text/plain", "application/pdf"];
172
+ if (!allowedTypes.includes(file.type)) {
173
+ message.textContent = "❌ Please upload a .txt or .pdf file only!";
174
+ message.classList.remove("hidden", "text-green-600");
175
+ message.classList.add("text-red-600");
176
+ return;
177
+ }
178
+
179
+ message.textContent = `✅ File "${file.name}" uploaded successfully!`;
180
+ message.classList.remove("hidden", "text-red-600");
181
+ message.classList.add("text-green-600");
182
+ });
183
+
184
+ form.addEventListener("submit", async (e) => {
185
+ e.preventDefault();
186
+ output.innerHTML = `
187
+ <div class='text-center py-6'>
188
+ <div class='inline-block animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-orange-500'></div>
189
+ <p class='mt-2 text-orange-500'>Generating your recipe (please wait, this may take up to 2 minutes)...</p>
190
+ </div>`;
191
+
192
+ const formData = new FormData();
193
+ formData.append("ingredients", ingredientsInput.value.trim());
194
+ formData.append("diet", dietSelect.value || "");
195
+ formData.append("cuisine", cuisineSelect.value || "");
196
+
197
+ let endpoint = "/api/generate-recipe"; // default: non-RAG
198
+ if (fileInput.files.length > 0) {
199
+ formData.append("file", fileInput.files[0]);
200
+ endpoint = "/api/rag-recipe"; // use RAG if file uploaded
201
+ }
202
+
203
+ try {
204
+ const response = await fetch(endpoint, {
205
+ method: "POST",
206
+ body: formData,
207
+ });
208
+
209
+ if (!response.ok) {
210
+ const error = await response.json();
211
+ throw new Error(error.detail || "Recipe generation failed");
212
+ }
213
+
214
+ const data = await response.json();
215
+ const formattedRecipe = data.recipe.replace(/\n/g, "<br>");
216
+
217
+ output.innerHTML = `
218
+ <div class="bg-white shadow rounded-lg overflow-hidden">
219
+ <img src="${data.image_url}" alt="Recipe Image" class="w-full h-48 object-cover" onerror="this.src='/docs/static/placeholder.jpg'">
220
+ <div class="p-6">
221
+ <h3 class="text-2xl font-bold mb-4">Your Generated Recipe</h3>
222
+ <div class="prose max-w-none">${formattedRecipe}</div>
223
+ </div>
224
+ </div>
225
+ `;
226
+ } catch (error) {
227
+ output.innerHTML = `
228
+ <div class="bg-red-50 border-l-4 border-red-500 p-4">
229
+ <div class="flex">
230
+ <div class="flex-shrink-0">
231
+ <svg class="h-5 w-5 text-red-500" viewBox="0 0 20 20" fill="currentColor">
232
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
233
+ </svg>
234
+ </div>
235
+ <div class="ml-3">
236
+ <p class="text-sm text-red-700">${error.message}</p>
237
+ </div>
238
+ </div>
239
+ </div>`;
240
+ console.error("Error:", error);
241
+ }
242
+ });
243
+ </script>
244
+
245
+
246
+
247
+ </body>
248
+ </html>