AlvesS077/SmartPlate
0
1<!DOCTYPE html>
2<html lang="pt-br">
3<head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>Food Classifier</title>
7 <script src="https://cdn.tailwindcss.com"></script>
8 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9 <style>
10 .upload-box {
11 transition: all 0.3s ease;
12 }
13 .upload-box:hover {
14 transform: translateY(-2px);
15 box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
16 }
17 .preview-container {
18 transition: all 0.5s ease;
19 }
20 .loading-spinner {
21 display: none;
22 }
23 @keyframes fade-in {
24 from { opacity: 0; transform: translateY(30px);}
25 to { opacity: 1; transform: translateY(0);}
26 }
27 .animate-fade-in {
28 animation: fade-in 0.8s cubic-bezier(.4,0,.2,1) both;
29 }
30 </style>
31</head>
32<body class="bg-gradient-to-br from-green-50 to-blue-50 min-h-screen">
33 <div class="container mx-auto px-4 py-12 max-w-4xl">
34 <div class="text-center mb-12">
35 <h1 class="text-4xl font-bold text-green-800 mb-3">🍽️ Food Classifier</h1>
36 <p class="text-lg text-gray-600">Upload an image of your food and we'll analyze its nutritional content</p>
37 </div>
38
39 <div class="bg-white rounded-xl shadow-lg p-8 mb-8">
40 <div class="upload-box bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg p-12 text-center cursor-pointer mb-8"
41 onclick="document.getElementById('upload').click()">
42 <div class="flex flex-col items-center justify-center">
43 <i class="fas fa-cloud-upload-alt text-4xl text-green-500 mb-4"></i>
44 <h3 class="text-xl font-semibold text-gray-700 mb-2">Upload your food image</h3>
45 <p class="text-gray-500">Drag & drop or click to browse</p>
46 </div>
47 <input type="file" id="upload" accept="image/*" class="hidden" onchange="mostrarImagem(event)" />
48 </div>
49
50 <div class="preview-container flex flex-col items-center justify-center mb-8">
51 <div id="preview-wrapper" class="relative hidden">
52 <img id="preview" src="" alt="Preview" class="rounded-lg shadow-md max-h-96 object-contain" />
53 <button id="remove-btn" class="absolute -top-3 -right-3 bg-red-500 text-white rounded-full w-8 h-8 flex items-center justify-center shadow-md hover:bg-red-600 transition" onclick="removeImage()">
54 <i class="fas fa-times"></i>
55 </button>
56 </div>
57 <p id="no-image-text" class="text-gray-400 italic">No image selected</p>
58 </div>
59
60 <div class="flex justify-center">
61 <button id="submit-btn" class="submit-btn bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-8 rounded-full shadow-lg transition flex items-center disabled:opacity-50 disabled:cursor-not-allowed"
62 onclick="enviarImagem()" disabled>
63 <span id="btn-text">Analyze Food</span>
64 <div id="loading-spinner" class="loading-spinner ml-2">
65 <i class="fas fa-circle-notch fa-spin"></i>
66 </div>
67 </button>
68 </div>
69 </div>
70
71 <div class="bg-white rounded-xl shadow-lg p-8">
72 <h2 class="text-2xl font-semibold text-gray-800 mb-4">How it works</h2>
73 <div class="grid md:grid-cols-3 gap-6">
74 <div class="flex flex-col items-center text-center">
75 <div class="bg-green-100 p-4 rounded-full mb-3">
76 <i class="fas fa-upload text-green-600 text-2xl"></i>
77 </div>
78 <h3 class="font-medium text-gray-700 mb-2">1. Upload Image</h3>
79 <p class="text-gray-500">Upload a clear photo of your food</p>
80 </div>
81 <div class="flex flex-col items-center text-center">
82 <div class="bg-blue-100 p-4 rounded-full mb-3">
83 <i class="fas fa-brain text-blue-600 text-2xl"></i>
84 </div>
85 <h3 class="font-medium text-gray-700 mb-2">2. AI Analysis</h3>
86 <p class="text-gray-500">Our AI analyzes the food components</p>
87 </div>
88 <div class="flex flex-col items-center text-center">
89 <div class="bg-purple-100 p-4 rounded-full mb-3">
90 <i class="fas fa-chart-pie text-purple-600 text-2xl"></i>
91 </div>
92 <h3 class="font-medium text-gray-700 mb-2">3. Get Results</h3>
93 <p class="text-gray-500">Receive detailed nutritional information</p>
94 </div>
95 </div>
96 </div>
97 </div>
98
99 <script>
100 function mostrarImagem(event) {
101 const file = event.target.files[0];
102 if (file) {
103 const reader = new FileReader();
104 reader.onload = function(e) {
105 const preview = document.getElementById('preview');
106 preview.src = e.target.result;
107 document.getElementById('preview-wrapper').classList.remove('hidden');
108 document.getElementById('no-image-text').classList.add('hidden');
109 document.getElementById('submit-btn').disabled = false;
110 };
111 reader.readAsDataURL(file);
112 }
113 }
114
115 function removeImage() {
116 document.getElementById('preview').src = '';
117 document.getElementById('preview-wrapper').classList.add('hidden');
118 document.getElementById('no-image-text').classList.remove('hidden');
119 document.getElementById('upload').value = '';
120 document.getElementById('submit-btn').disabled = true;
121 }
122
123 function enviarImagem() {
124 const btn = document.getElementById('submit-btn');
125 const btnText = document.getElementById('btn-text');
126 const spinner = document.getElementById('loading-spinner');
127 const uploadInput = document.getElementById('upload');
128 const previewWrapper = document.getElementById('preview-wrapper');
129
130 // Show loading state
131 btn.disabled = true;
132 btnText.textContent = 'Analyzing...';
133 spinner.style.display = 'inline-block';
134
135 const file = uploadInput.files[0];
136 if (!file) {
137 alert('Please select an image first.');
138 btn.disabled = false;
139 btnText.textContent = 'Analyze Food';
140 spinner.style.display = 'none';
141 return;
142 }
143
144 const formData = new FormData();
145 formData.append('image', file);
146
147 fetch('http://127.0.0.1:5000/analyze-food', {
148 method: 'POST',
149 body: formData
150 })
151 .then(response => response.json())
152 .then(data => {
153 btn.disabled = false;
154 btnText.textContent = 'Analyze Food';
155 spinner.style.display = 'none';
156
157 if (data.error) {
158 alert('Error: ' + data.error);
159 return;
160 }
161
162 let resultsContainer = document.getElementById('results-container');
163 if (!resultsContainer) {
164 resultsContainer = document.createElement('div');
165 resultsContainer.id = 'results-container';
166 resultsContainer.className = 'bg-white rounded-xl shadow-lg p-8 mt-8';
167 document.querySelector('.container').appendChild(resultsContainer);
168 }
169
170 // Ordem correta das colunas
171 const orderedColumns = [
172 'Plate Name',
173 'Protein (g)',
174 'Calories',
175 'Fat (g)',
176 'Carbs (g)',
177 'Fiber (g)'
178 ];
179
180 let excelHtml = '';
181 if (data.excel && !data.excel.error) {
182 excelHtml = '<ul class="list-disc pl-5">';
183 orderedColumns.forEach(key => {
184 if (data.excel[key] !== undefined) {
185 excelHtml += `<li><strong>${key}:</strong> ${data.excel[key]}</li>`;
186 }
187 });
188 excelHtml += '</ul>';
189 } else if (data.excel && data.excel.error) {
190 excelHtml = `<p class="text-red-500">${data.excel.error}</p>`;
191 }
192
193 resultsContainer.innerHTML = `
194 <div class="relative bg-gradient-to-br from-green-100 via-blue-50 to-purple-100 rounded-2xl shadow-2xl p-8 border-4 border-green-200 ring-2 ring-green-400/30 animate-fade-in">
195 <div class="absolute -top-8 left-1/2 -translate-x-1/2 flex items-center justify-center">
196 <span class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gradient-to-br from-green-400 to-green-600 shadow-lg border-4 border-white">
197 <i class="fas fa-leaf text-white text-3xl"></i>
198 </span>
199 </div>
200 <h2 class="text-3xl font-extrabold text-green-800 text-center mt-10 mb-6 tracking-tight drop-shadow">Analysis Results</h2>
201 <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
202 <div class="flex-1 text-center md:text-left">
203 <p class="text-lg font-semibold text-gray-700 mb-2">Could Cause:</p>
204 <span class="inline-block bg-green-200 text-green-900 font-bold text-xl px-6 py-2 rounded-full shadow-md border border-green-400 tracking-wide animate-pulse">${data.class}</span>
205 </div>
206 <div class="flex-1 mt-6 md:mt-0">
207 <div class="bg-white/80 rounded-xl p-4 shadow-inner border border-gray-200">
208 <h3 class="text-lg font-semibold text-purple-700 mb-2">Alternative Meal:</h3>
209 <div class="text-gray-700 text-base">
210 ${excelHtml}
211 </div>
212 </div>
213 </div>
214 </div>
215 </div>
216 `;
217 })
218 .catch(error => {
219 btn.disabled = false;
220 btnText.textContent = 'Analyze Food';
221 spinner.style.display = 'none';
222 alert('Error: ' + error.message);
223 });
224 }
225
226 // Drag and drop functionality
227 const uploadBox = document.querySelector('.upload-box');
228 ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
229 uploadBox.addEventListener(eventName, preventDefaults, false);
230 });
231
232 function preventDefaults(e) {
233 e.preventDefault();
234 e.stopPropagation();
235 }
236
237 ['dragenter', 'dragover'].forEach(eventName => {
238 uploadBox.addEventListener(eventName, highlight, false);
239 });
240
241 ['dragleave', 'drop'].forEach(eventName => {
242 uploadBox.addEventListener(eventName, unhighlight, false);
243 });
244
245 function highlight() {
246 uploadBox.classList.add('border-green-400', 'bg-green-50');
247 }
248
249 function unhighlight() {
250 uploadBox.classList.remove('border-green-400', 'bg-green-50');
251 }
252
253 uploadBox.addEventListener('drop', handleDrop, false);
254
255 function handleDrop(e) {
256 const dt = e.dataTransfer;
257 const files = dt.files;
258 if (files.length) {
259 document.getElementById('upload').files = files;
260 mostrarImagem({target: {files: files}});
261 }
262 }
263 </script>
264</body>
265</html>
266 