-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathai_meal_analysis.html
More file actions
234 lines (200 loc) · 11.2 KB
/
ai_meal_analysis.html
File metadata and controls
234 lines (200 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<!DOCTYPE html>
<html class="dark" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>AI Meal Analysis - Calories AI</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: { "primary": "#f4c025", "background-light": "#f8f8f5", "background-dark": "#121212" },
fontFamily: { "display": ["Inter"] }
},
},
}
</script>
</head>
<body class="bg-black text-white h-screen flex flex-col relative overflow-hidden">
<div class="relative flex-1 bg-gray-900 overflow-hidden">
<video id="camera-feed" class="absolute inset-0 w-full h-full object-cover opacity-80" autoplay playsinline></video>
<div class="absolute inset-0 z-10 opacity-30 pointer-events-none"
style="background-image: linear-gradient(#fff 1px, transparent 1px), linear-gradient(90deg, #fff 1px, transparent 1px); background-size: 33.3% 33.3%;">
</div>
<div id="scan-line" class="absolute top-0 left-0 w-full h-1 bg-primary shadow-[0_0_20px_rgba(244,192,37,0.8)] z-20 hidden animate-scan"></div>
<div class="absolute top-0 left-0 w-full p-4 flex justify-between items-center z-30 bg-gradient-to-b from-black/80 to-transparent">
<a href="calories_ai_dashboard.html" class="p-2 bg-black/40 backdrop-blur-md rounded-full">
<span class="material-symbols-outlined text-white">close</span>
</a>
<button class="p-2 bg-black/40 backdrop-blur-md rounded-full">
<span class="material-symbols-outlined text-white">flash_on</span>
</button>
</div>
</div>
<div class="bg-black/90 backdrop-blur-xl p-6 pb-10 rounded-t-3xl z-40 border-t border-white/10">
<div id="analyzing-state" class="hidden flex-col items-center justify-center py-4 text-center">
<div class="w-16 h-16 border-4 border-primary border-t-transparent rounded-full animate-spin mb-4"></div>
<h3 class="text-xl font-bold">Analyzing Food...</h3>
<p class="text-gray-400 text-sm">Identifying calories & macros</p>
</div>
<div id="capture-controls" class="flex items-center justify-between px-4">
<button class="p-4 rounded-2xl bg-white/10 hover:bg-white/20 transition-colors">
<span class="material-symbols-outlined text-2xl">photo_library</span>
</button>
<button onclick="captureAndAnalyze()" class="w-20 h-20 rounded-full border-4 border-white flex items-center justify-center relative group active:scale-95 transition-transform">
<div class="w-16 h-16 bg-white rounded-full group-hover:bg-primary transition-colors"></div>
</button>
<button class="p-4 rounded-2xl bg-white/10 hover:bg-white/20 transition-colors">
<span class="material-symbols-outlined text-2xl">cameraswitch</span>
</button>
</div>
</div>
<div id="save-overlay" class="fixed inset-0 z-50 bg-black/90 backdrop-blur-md hidden flex-col items-center justify-center">
<div class="w-20 h-20 rounded-full border-4 border-primary border-t-transparent animate-spin mb-6"></div>
<div class="bg-white/10 p-4 rounded-full mb-4">
<span class="material-symbols-outlined text-4xl text-primary">cloud_upload</span>
</div>
<h2 class="text-2xl font-bold text-white mb-2">Saving to Cloud...</h2>
<p class="text-gray-400 text-sm">Syncing with Firestore</p>
</div>
<script type="module">
// 1. Import Shared Config
import { auth, db } from "./firebase-config.js";
// 2. Camera Setup
async function initCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
document.getElementById('camera-feed').srcObject = stream;
} catch (err) {
console.warn("Camera access denied:", err);
}
}
initCamera();
// 3. Capture Logic (Updated: No Delay)
window.captureAndAnalyze = function() {
// Hide the buttons immediately
document.getElementById('capture-controls').classList.add('hidden');
// Run the main function immediately
saveMealToDB();
}
// 4. Main Function: Capture -> Show Modal -> Send to AI
async function saveMealToDB() {
const video = document.getElementById('camera-feed');
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
canvas.toBlob(async function(blob) {
const user = auth.currentUser;
// Security Check
if (!user) {
alert("Please log in first!");
window.location.href = "code.html";
return;
}
// --- A. SHOW THE RESULT MODAL INSTANTLY ---
// Create a local link to the image so we can show it immediately
const localImageUrl = URL.createObjectURL(blob);
document.getElementById('result-image').src = localImageUrl;
// Open the popup
document.getElementById('result-modal').classList.remove('hidden');
document.getElementById('result-modal').classList.add('flex');
// Set "Loading" text while we wait for n8n
document.getElementById('result-name').innerText = "Analyzing Food...";
document.getElementById('result-notes').innerText = "AI is calculating macros...";
try {
// --- B. SEND TO N8N (BACKEND) ---
const formData = new FormData();
formData.append('data', blob, 'meal.jpg');
formData.append('uid', user.uid);
// Your EXACT n8n URL
const n8nUrl = "https://ghulamrasoolshirzai.app.n8n.cloud/webhook-test/analyze-meal";
const response = await fetch(n8nUrl, {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error("AI Analysis Failed");
// --- C. UPDATE MODAL WITH REAL DATA ---
const data = await response.json();
console.log("AI Result:", data);
// Update the text on screen with the new numbers
document.getElementById('result-name').innerText = data.mealName || "Food Detected";
document.getElementById('result-calories').innerText = data.calories || 0;
document.getElementById('result-protein').innerText = (data.protein || 0) + "g";
document.getElementById('result-carbs').innerText = (data.carbs || 0) + "g";
document.getElementById('result-fat').innerText = (data.fats || 0) + "g";
document.getElementById('result-notes').innerText = data.notes || "Analysis complete.";
} catch (error) {
console.error("Error:", error);
document.getElementById('result-name').innerText = "Analysis Failed";
document.getElementById('result-notes').innerText = "Please try again. " + error.message;
}
}, 'image/jpeg');
}
// 5. Close Button Helper
window.closeModal = function() {
document.getElementById('result-modal').classList.add('hidden');
document.getElementById('result-modal').classList.remove('flex');
document.getElementById('capture-controls').classList.remove('hidden');
}
</script>
<style>
@keyframes scan {
0% { top: 0; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { top: 100%; opacity: 0; }
}
.animate-scan {
animation: scan 2s linear infinite;
}
</style>
<div id="result-modal" class="fixed inset-0 z-50 bg-black/95 backdrop-blur-xl hidden flex-col overflow-y-auto">
<div class="relative h-72 w-full shrink-0">
<img id="result-image" class="w-full h-full object-cover" src="" alt="Scanned Food">
<div class="absolute inset-0 bg-gradient-to-t from-black via-transparent to-transparent"></div>
<button onclick="closeModal()" class="absolute top-4 right-4 p-2 bg-black/50 rounded-full backdrop-blur-md">
<span class="material-symbols-outlined text-white">close</span>
</button>
</div>
<div class="px-6 -mt-10 relative z-10">
<div class="bg-gray-900 rounded-3xl p-6 border border-white/10 shadow-2xl">
<div class="flex justify-between items-start mb-6">
<div>
<h2 id="result-name" class="text-2xl font-bold text-white mb-1">Analyzing...</h2>
<p id="result-notes" class="text-sm text-gray-400">Please wait a moment</p>
</div>
<div class="text-right">
<span id="result-calories" class="text-3xl font-bold text-primary block">0</span>
<span class="text-xs text-gray-500 uppercase tracking-wider">Calories</span>
</div>
</div>
<div class="grid grid-cols-3 gap-4 mb-6">
<div class="bg-white/5 p-4 rounded-2xl text-center">
<span class="material-symbols-outlined text-blue-400 mb-1">fitness_center</span>
<p class="text-xs text-gray-400">Protein</p>
<p id="result-protein" class="text-lg font-bold text-white">0g</p>
</div>
<div class="bg-white/5 p-4 rounded-2xl text-center">
<span class="material-symbols-outlined text-green-400 mb-1">grain</span>
<p class="text-xs text-gray-400">Carbs</p>
<p id="result-carbs" class="text-lg font-bold text-white">0g</p>
</div>
<div class="bg-white/5 p-4 rounded-2xl text-center">
<span class="material-symbols-outlined text-red-400 mb-1">opacity</span>
<p class="text-xs text-gray-400">Fats</p>
<p id="result-fat" class="text-lg font-bold text-white">0g</p>
</div>
</div>
<button onclick="window.location.href='calories_ai_dashboard.html'" class="w-full py-4 bg-primary text-black font-bold rounded-xl active:scale-95 transition-transform">
Save to Daily Log
</button>
</div>
</div>
</div>
</body>
</html>