Meal Tetris™
body {
font-family: 'Segoe UI', sans-serif;
background: #f4f6f9;
margin: 0;
padding: 20px;
color: #333;
}
h1 {
text-align: center;
font-size: 26px;
}
.meal-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: space-between;
}
.meal-section, .plan-section {
flex: 1;
min-width: 300px;
background: #fff;
padding: 15px;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
}
.meal-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 10px;
text-align: center;
}
.filters label {
display: inline-block;
background: #f0f0f0;
margin: 5px 5px;
font-size: 14px;
cursor: pointer;
padding: 6px 12px;
border-radius: 20px;
}
.filters input {
margin-right: 6px;
}
.meal {
background: #e3f2fd;
border: 1px solid #90caf9;
border-radius: 8px;
padding: 12px;
font-size: 15px;
cursor: grab;
margin-bottom: 10px;
}
.grid-slot {
background: #f9f9f9;
border: 2px dashed #ccc;
border-radius: 8px;
min-height: 60px;
padding: 10px;
margin-bottom: 12px;
}
.score-box {
margin-top: 20px;
background: #e8f5e9;
padding: 15px;
border: 2px solid #66bb6a;
border-radius: 10px;
}
.reset-btn {
margin-top: 20px;
padding: 10px 20px;
background: #f44336;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
}
.reset-btn:hover {
background: #d32f2f;
}
🎮 Meal Tetris™
🍽️ Meal Pool
Vegan
Vegetarian
Low-Carb
High-Protein
Budget (£)
Quick (≤15 mins)
📅 Your Plan
Monday
Tuesday
Wednesday
Thursday
Friday
🔄 Reset Plan
📊 Total Score
Protein: 0 g
Carbs: 0 g
Fat: 0 g
Prep Time: 0 mins
Craving Risk: 0
const meals = [
{
id: 1,
name: "Grilled Chicken Fajitas",
macros: { protein: 38, carbs: 24, fat: 10 },
prep_time: 15,
craving_risk: "Low",
tags: ["high-protein", "budget"]
},
{
id: 2,
name: "Greek Yogurt with Berries",
macros: { protein: 12, carbs: 18, fat: 5 },
prep_time: 5,
craving_risk: "Medium",
tags: ["vegetarian", "quick", "budget"]
},
{
id: 3,
name: "Chickpea Coconut Curry",
macros: { protein: 14, carbs: 32, fat: 12 },
prep_time: 20,
craving_risk: "Low",
tags: ["vegan"]
},
{
id: 4,
name: "Beef Stir-Fry with Veg",
macros: { protein: 36, carbs: 20, fat: 14 },
prep_time: 12,
craving_risk: "Medium",
tags: ["low-carb", "high-protein", "quick"]
},
{
id: 5,
name: "Tofu & Noodle Bowl",
macros: { protein: 22, carbs: 35, fat: 10 },
prep_time: 10,
craving_risk: "Low",
tags: ["vegan", "quick", "budget"]
}
];
const mealPool = document.getElementById("mealPool");
const mealGrid = document.getElementById("mealGrid");
function renderMeals(filteredMeals = meals) {
mealPool.innerHTML = '';
filteredMeals.forEach(meal => {
const div = document.createElement("div");
div.className = "meal";
div.draggable = true;
div.id = "meal-" + meal.id;
div.ondragstart = drag;
div.textContent = meal.name;
div.dataset.id = meal.id;
mealPool.appendChild(div);
});
}
renderMeals();
function applyFilters() {
const selected = Array.from(document.querySelectorAll('.filters input:checked')).map(el => el.value);
if (selected.length === 0) {
renderMeals(meals);
return;
}
const filtered = meals.filter(meal => selected.every(tag => meal.tags.includes(tag)));
renderMeals(filtered);
}
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
let totalProtein = 0, totalCarbs = 0, totalFat = 0, totalPrep = 0, totalCraving = 0;
function drop(ev) {
ev.preventDefault();
const mealId = ev.dataTransfer.getData("text");
const mealElem = document.getElementById(mealId);
const clone = mealElem.cloneNode(true);
clone.draggable = false;
clone.style.background = "#cce5ff";
clone.style.cursor = "default";
ev.target.appendChild(clone);
const mealData = meals.find(m => "meal-" + m.id === mealId);
totalProtein += mealData.macros.protein;
totalCarbs += mealData.macros.carbs;
totalFat += mealData.macros.fat;
totalPrep += mealData.prep_time;
totalCraving += (mealData.craving_risk === "Low" ? 1 : 2);
updateScore();
}
function updateScore() {
document.getElementById("scoreProtein").textContent = totalProtein;
document.getElementById("scoreCarbs").textContent = totalCarbs;
document.getElementById("scoreFat").textContent = totalFat;
document.getElementById("scorePrep").textContent = totalPrep;
document.getElementById("scoreCraving").textContent = totalCraving;
}
function resetPlan() {
Array.from(mealGrid.getElementsByClassName('grid-slot')).forEach(slot => {
slot.innerHTML = slot.innerText;
});
totalProtein = 0;
totalCarbs = 0;
totalFat = 0;
totalPrep = 0;
totalCraving = 0;
updateScore();
}