This commit is contained in:
Ionel Andrei Cataon
2026-02-18 16:46:19 +02:00
parent 9c4a8cfa62
commit 9adc71a26e

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NutriAI Elite v11.0 - Shopping & Macros</title>
<title>NutriAI Elite v12.0 - Precision Calibration</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
@@ -30,7 +30,7 @@
.macro-card { background: rgba(0,0,0,0.2); border-radius: 15px; padding: 15px; margin-top: 15px; border: 1px solid rgba(255,255,255,0.05); }
.chat-area { height: 52vh; overflow-y: auto; padding: 25px; border-radius: 20px; background: rgba(0,0,0,0.2); }
.chat-area { height: 50vh; overflow-y: auto; padding: 25px; border-radius: 20px; background: rgba(0,0,0,0.2); }
.msg { padding: 1.5rem; border-radius: 1.2rem; margin-bottom: 1.5rem; max-width: 95%; line-height: 1.6; }
.ai { background: var(--card-ai); border-left: 6px solid var(--primary); }
.user { background: var(--primary); margin-left: auto; font-weight: 600; }
@@ -42,7 +42,7 @@
.input-bar { background: var(--sidebar-bg); border-radius: 50px; padding: 10px 25px; border: 2px solid var(--primary); }
.top-nav { position: fixed; top: 15px; right: 20px; z-index: 1000; display: flex; gap: 10px; }
.btn-shop { background: #f59e0b; color: #000; font-weight: 800; border: none; border-radius: 12px; padding: 10px 20px; transition: 0.3s; }
.btn-shop { background: #f59e0b; color: #000; font-weight: 800; border: none; border-radius: 12px; padding: 10px 20px; transition: 0.3s; height: 55px; }
.btn-shop:hover { background: #fbbf24; transform: scale(1.05); }
</style>
</head>
@@ -72,9 +72,9 @@
<h4 class="fw-bold mb-3"><i class="fas fa-microchip text-primary me-2"></i>NutriAI Elite</h4>
<div class="row g-2 mb-3">
<div class="col-6"><label class="label-pro" id="lblCurW">Weight (kg)</label><input type="number" id="weight" class="form-control glass-input" value="85" oninput="updateProfile()"></div>
<div class="col-6"><label class="label-pro" id="lblTarW">Target (kg)</label><input type="number" id="targetW" class="form-control glass-input" value="75" oninput="updateProfile()"></div>
<div class="col-6"><label class="label-pro" id="lblH">Height (cm)</label><input type="number" id="height" class="form-control glass-input" value="180" oninput="updateProfile()"></div>
<div class="col-6"><label class="label-pro" id="lblCurW">Weight (kg)</label><input type="number" id="weight" class="form-control glass-input" value="110" oninput="updateProfile()"></div>
<div class="col-6"><label class="label-pro" id="lblTarW">Target (kg)</label><input type="number" id="targetW" class="form-control glass-input" value="85" oninput="updateProfile()"></div>
<div class="col-6"><label class="label-pro" id="lblH">Height (cm)</label><input type="number" id="height" class="form-control glass-input" value="176" oninput="updateProfile()"></div>
<div class="col-6"><label class="label-pro">Age</label><input type="number" id="age" class="form-control glass-input" value="30" oninput="updateProfile()"></div>
<div class="col-12"><label class="label-pro">Activity</label>
<select id="activity" class="form-select glass-input" onchange="updateProfile()">
@@ -82,14 +82,14 @@
</select>
</div>
<div class="col-12">
<label class="label-pro d-flex justify-content-between">Aggressiveness <span><span id="aggValue">-500</span> kcal</span></label>
<input type="range" id="aggSlider" class="form-range" min="-1000" max="1000" step="50" value="-500" oninput="updateProfile()">
<label class="label-pro d-flex justify-content-between">Aggressiveness <span><span id="aggValue">-300</span> kcal</span></label>
<input type="range" id="aggSlider" class="form-range" min="-1000" max="1000" step="50" value="-300" oninput="updateProfile()">
</div>
</div>
<div class="kcal-hub shadow">
<small class="text-uppercase fw-bold opacity-75">Daily Target</small>
<h2 class="fw-bold mb-0" id="totalKcal">2500</h2>
<h2 class="fw-bold mb-0" id="totalKcal">---</h2>
<span class="small">KCAL / DAY</span>
</div>
@@ -106,16 +106,15 @@
<div class="col-lg-9 p-5">
<div id="chatBox" class="chat-area shadow-inner">
<div class="msg ai">
<h5 class="fw-bold text-primary">System Online.</h5>
All biometrics synced. You are currently in <strong><span id="aggStatus"></span></strong> mode.
Ask for a meal plan, and then I can generate your shopping list!
<h5 class="fw-bold text-primary">System Recalibrated.</h5>
Precision math has been updated. I'm ready to build a meal plan based on your exact calorie target.
</div>
</div>
<div class="mt-4 d-flex gap-2">
<div class="mt-4 d-flex gap-2 align-items-center">
<div class="input-bar d-flex align-items-center flex-grow-1 shadow">
<input type="text" id="userInput" class="form-control bg-transparent border-0 text-white" placeholder="Message your coach...">
<button onclick="sendToGroq()" class="btn btn-primary rounded-circle p-2 ms-2" style="width: 45px; height: 45px;"><i class="fas fa-paper-plane"></i></button>
<input type="text" id="userInput" class="form-control bg-transparent border-0 text-white" placeholder="Ask for a meal plan table...">
<button onclick="sendToGroq()" class="btn btn-primary rounded-circle p-2 ms-2" style="min-width: 45px; height: 45px;"><i class="fas fa-paper-plane"></i></button>
</div>
<button onclick="generateShoppingList()" class="btn-shop shadow">
<i class="fas fa-shopping-cart me-2"></i> SHOPPING LIST
@@ -138,7 +137,7 @@
const ctx = document.getElementById('macroChart').getContext('2d');
macroChart = new Chart(ctx, {
type: 'doughnut',
data: { labels: ['Protein', 'Carbs', 'Fats'], datasets: [{ data: [30, 40, 30], backgroundColor: ['#0dcaf0', '#dc3545', '#ffc107'], borderWeight: 0 }] },
data: { labels: ['Protein', 'Carbs', 'Fats'], datasets: [{ data: [1, 1, 1], backgroundColor: ['#0dcaf0', '#dc3545', '#ffc107'], borderWeight: 0 }] },
options: { plugins: { legend: { display: false } }, cutout: '70%' }
});
}
@@ -156,32 +155,36 @@
function updateProfile() {
let w = parseFloat(document.getElementById('weight').value) || 0, h = parseFloat(document.getElementById('height').value) || 0;
let a = parseFloat(document.getElementById('age').value) || 0, act = parseFloat(document.getElementById('activity').value), agg = parseInt(document.getElementById('aggSlider').value);
let w_c = isMetric ? w : w / 2.205, h_c = isMetric ? h : h * 2.54;
// CORRECTION: Precision Mifflin-St Jeor Calculation
let bmr = (10 * w_c) + (6.25 * h_c) - (5 * a) + 5;
let total = Math.round(bmr * act) + agg;
let maintenance = bmr * act;
let total = Math.round(maintenance + agg);
document.getElementById('totalKcal').innerText = total;
document.getElementById('aggValue').innerText = (agg > 0 ? '+' : '') + agg;
document.getElementById('aggStatus').innerText = agg < 0 ? "Weight Loss" : (agg > 0 ? "Bulking" : "Maintenance");
document.getElementById('aggValue').innerText = (agg > 0 ? '+' : '') + agg + " kcal";
// Logic for Macros
let pPct = agg < 0 ? 0.35 : 0.25, cPct = agg < 0 ? 0.35 : 0.50, fPct = 1 - (pPct + cPct);
let pG = Math.round((total * pPct) / 4), cG = Math.round((total * cPct) / 4), fG = Math.round((total * fPct) / 9);
document.getElementById('pGram').innerText = pG; document.getElementById('cGram').innerText = cG; document.getElementById('fGram').innerText = fG;
if(macroChart) { macroChart.data.datasets[0].data = [pG*4, cG*4, fG*9]; macroChart.update(); }
}
async function sendToGroq(customPrompt = null) {
const input = document.getElementById('userInput');
const text = customPrompt || input.value.trim();
const input = document.getElementById('userInput'), text = customPrompt || input.value.trim();
if(!text) return;
if(!customPrompt) addMsg('user', text);
if(!customPrompt) input.value = "";
const id = 'l-' + Date.now(); addMsg('ai', '<span class="spinner-border spinner-border-sm"></span> Calibrating...', id);
const sysMsg = `Expert Nutritionist. Goal: ${document.getElementById('totalKcal').innerText} kcal. Macros: P:${document.getElementById('pGram').innerText}g, C:${document.getElementById('cGram').innerText}g, F:${document.getElementById('fGram').innerText}g. Use Markdown Tables.`;
const id = 'l-' + Date.now(); addMsg('ai', '<span class="spinner-border spinner-border-sm"></span> Calibrating Strategy...', id);
try {
const resp = await fetch("https://api.groq.com/openai/v1/chat/completions", {
method: "POST", headers: { "Authorization": `Bearer ${GROQ_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: "llama-3.3-70b-versatile", messages: [{ role: "system", content: sysMsg }, { role: "user", content: text }] })
body: JSON.stringify({ model: "llama-3.3-70b-versatile", messages: [{ role: "system", content: `Expert Nutritionist. Goal: ${document.getElementById('totalKcal').innerText} kcal. Macros: P:${document.getElementById('pGram').innerText}g, C:${document.getElementById('cGram').innerText}g, F:${document.getElementById('fGram').innerText}g. Use Markdown Tables.` }, { role: "user", content: text }] })
});
const d = await resp.json();
document.getElementById(id).innerHTML = marked.parse(d.choices[0].message.content);
@@ -189,7 +192,7 @@
}
function generateShoppingList() {
sendToGroq("Based on the target calories and macros we discussed, generate a comprehensive shopping list for 7 days. Organize it by categories like Proteins, Vegetables, Grains, etc.");
sendToGroq("Generate a weekly shopping list categorized by aisle based on the meal plan we discussed.");
}
function addMsg(t, c, i = null) {