KeeTok Meals Planner
Meal Library
๐ณ Breakfast
๐ฅ Lunch
๐ฝ๏ธ Dinner
๐ Snacks
๐ฆ Other
Daily View
Today
Today’s Nutrition
0
Calories
0g
Protein
0g
Carbs
0g
Fats
0g
Sugar
$0.00
Cost
Weekly View
This Week
Sun
Mon
Tue
Wed
Thu
Fri
Sat
Monthly View
This Month
Sun
Mon
Tue
Wed
Thu
Fri
Sat
Pro Nutrition Goals Calculator
Personal Stats
Male
Female
Current Body Fat %
Or set exact:20%
Goal Body Fat %
Or set exact:15%
Daily Activity Level
Mostly sitting
Desk job, minimal exercise
On my feet
Active job OR 1-3x/week
Very active
Physical job OR 4-6x/week
Athlete
Training daily
Eating Style
Balanced
Mix of everything
Higher protein
Meat/fish/eggs focus
Plant-based
Veggies, beans, tofu
Higher fat
Nuts, avocado, oils
Dietary Restrictions
Your Personalized Pro Plan
Daily Targets
2000
Calories/day
50g
Protein
250g
Carbs
65g
Fats
Body Composition
Recommended Plan Phase
Pro Tips
Shopping List
Pantry Stock Management
Stock Awareness
Today
Weight Tracker
First thing after waking
Before sleep
โ
Today AM
โ
7-day avg
โ
Change
โ
Goal weight
Water Intake
cups (8 oz each)
0 of 8 cups ยท 0 oz
Eating Habits
7-Day Habit Streak
Full day Partial No data
Pantry is empty
';return;} stockItems.forEach((item,idx)=>{ const mealsLeft=item.usagePerMeal>0?Math.floor(item.quantity/item.usagePerMeal):0; let statusClass='stock-status-ok', statusText='OK'; if(item.quantity<=0||mealsLeft<=0){statusClass='stock-status-out';statusText='Out';} else if(item.quantity<=item.reorderPoint||mealsLeft<=5){statusClass='stock-status-low';statusText='Low';} const bp=getBestStorePrice(item); const priceInfo=bp?`Best: ${bp.store} $${bp.price.toFixed(2)}${bp.isSale?' ๐ท':''}${bp.isBulk?' ๐ฆ':''}`:''; const el=document.createElement('div'); el.className='stock-item'; el.innerHTML=`${item.name}
${item.category||''}${item.quantity} ${item.unit}${priceInfo}
Total Items:${stockItems.length}
Running Low:${low}
Out of Stock:${out}
Inventory Value:$${totalVal.toFixed(2)}
`;
const attentionItems=[...outItems,...lowItems.filter(li=>!outItems.some(oi=>oi.name===li.name))];
if(attentionItems.length){
html+=` Needs Attention
`;
attentionItems.forEach(a=>{
const bp=getBestStorePrice(stockItems[a.idx]);
const storeName=bp?bp.store:'General';
html+=` ${a.name}
`;
});
}
cont.innerHTML=html;
cont.querySelectorAll('.attention-add-btn').forEach(btn=>{
btn.addEventListener('click',function(){
const idx=parseInt(this.dataset.idx);
const item=stockItems[idx];
const label=`${item.quantity} ${item.unit} ${item.name}`;
addToShoppingList(label,this.dataset.store,this.dataset.sale==='true',this.dataset.bulk==='true',this.dataset.cheapest==='true');
});
});
}
function addStockItem(){
const name=document.getElementById('stock-item-input').value.trim();
const qty=parseFloat(document.getElementById('stock-quantity-input').value)||0;
const unit=document.getElementById('stock-unit-input').value;
if(!name) return;
const existing=stockItems.findIndex(i=>i.name.toLowerCase()===name.toLowerCase()&&i.unit===unit);
if(existing>=0){stockItems[existing].quantity+=qty;stockItems[existing].lastUpdated=formatDate(new Date());}
else{stockItems.push({id:Date.now(),name,quantity:qty,unit,usagePerMeal:1,minimumStock:qty*0.2,reorderPoint:qty*0.3,category:'General',tags:[],notes:'',usedInMeals:[],storePrices:[],lastUpdated:formatDate(new Date())});}
document.getElementById('stock-item-input').value='';
document.getElementById('stock-quantity-input').value='';
updateStockList();
}
function addStockItemToShoppingList(idx){
const item=stockItems[idx];
const bp=getBestStorePrice(item);
const storeName=bp?bp.store:'General';
const label=`${item.quantity} ${item.unit} ${item.name}`;
addToShoppingList(label,storeName,bp?bp.isSale:false,bp?bp.isBulk:false,!!bp);
}
function increaseStockQuantity(idx){stockItems[idx].quantity+=1;updateStockList();saveToLocalStorage();}
function decreaseStockQuantity(idx){stockItems[idx].quantity=Math.max(0,stockItems[idx].quantity-1);updateStockList();saveToLocalStorage();}
function showStockDetails(idx){
editingStockIndex=idx;
const item=stockItems[idx];
document.getElementById('stock-modal-title').textContent=item.name;
document.getElementById('stock-detail-name').value=item.name;
document.getElementById('stock-detail-quantity').value=item.quantity;
document.getElementById('stock-detail-unit').value=item.unit;
document.getElementById('stock-detail-category').value=item.category||'';
document.getElementById('stock-detail-tags').value=Array.isArray(item.tags)?item.tags.join(', '):item.tags||'';
document.getElementById('stock-detail-usage-per-meal').value=item.usagePerMeal;
document.getElementById('stock-detail-minimum-stock').value=item.minimumStock;
document.getElementById('stock-detail-reorder-point').value=item.reorderPoint;
document.getElementById('stock-detail-notes').value=item.notes||'';
const m=item.usagePerMeal>0?Math.floor(item.quantity/item.usagePerMeal):0;
document.getElementById('stock-meals-remaining').textContent=m;
document.getElementById('stock-days-supply').textContent=m;
const bp=getBestStorePrice(item);
document.getElementById('stock-best-price').textContent=bp?`${bp.store} $${bp.price.toFixed(2)}`:'โ';
renderStorePriceRows(item.storePrices||[]);
updateBestStoreSummary(item.storePrices||[]);
const mc=document.getElementById('stock-used-in-meals');
mc.innerHTML='';
(item.usedInMeals||[]).forEach(mn=>{
const el=document.createElement('div'); el.className='stock-meal-item';
el.innerHTML=`${mn}`;
const rmBtn=document.createElement('button'); rmBtn.className='button small'; rmBtn.textContent='Remove';
rmBtn.addEventListener('click',()=>removeMealFromStock(mn,idx));
el.appendChild(rmBtn); mc.appendChild(el);
});
if(!item.usedInMeals||!item.usedInMeals.length) mc.innerHTML='Not used in any meals
'; document.getElementById('stock-details-modal').classList.add('flex'); } function renderStorePriceRows(prices){const cont=document.getElementById('store-prices-list');cont.innerHTML='';prices.forEach((sp,i)=>renderOneStoreRow(sp,i));} function renderOneStoreRow(sp,i){ const cont=document.getElementById('store-prices-list'); const row=document.createElement('div'); row.className='store-price-row'; row.dataset.rowIndex=i; row.innerHTML=` `; cont.appendChild(row); } function updateStorePriceField(rowIdx,field,value){ if(editingStockIndex<0) return; const prices=stockItems[editingStockIndex].storePrices||[]; if(!prices[rowIdx]) prices[rowIdx]={store:'',price:0,bulkPrice:'',isSale:false,isBulk:false}; if(field==='price'||field==='bulkPrice') prices[rowIdx][field]=parseFloat(value)||0; else prices[rowIdx][field]=value; updateBestStoreSummary(prices); } function addStorePriceRow(){ if(editingStockIndex<0) return; if(!stockItems[editingStockIndex].storePrices) stockItems[editingStockIndex].storePrices=[]; const newEntry={store:'',price:0,bulkPrice:'',isSale:false,isBulk:false}; stockItems[editingStockIndex].storePrices.push(newEntry); renderOneStoreRow(newEntry,stockItems[editingStockIndex].storePrices.length-1); updateBestStoreSummary(stockItems[editingStockIndex].storePrices); } function removeStorePriceRow(i){ if(editingStockIndex<0) return; stockItems[editingStockIndex].storePrices.splice(i,1); renderStorePriceRows(stockItems[editingStockIndex].storePrices); updateBestStoreSummary(stockItems[editingStockIndex].storePrices); } function updateBestStoreSummary(prices){ const el=document.getElementById('best-store-summary'); if(!el) return; if(!prices||!prices.length){el.classList.remove('visible');return;} let best=null; prices.forEach(sp=>{ if(!sp.store) return; const eff=(sp.isSale&&parseFloat(sp.bulkPrice)>0)?Math.min(sp.price,parseFloat(sp.bulkPrice)):sp.price; if(!best||eff${category}
${meal.prepTime||0}
${meal.cookTime||0}
${meal.servings||1}
${meal.difficulty||'Easy'}
${meal.cost.toFixed(2)}
`; }
else { photoBox.innerHTML=``; }
// Ingredients
const list=document.getElementById('recipe-ingredients-list'); list.innerHTML='';
(meal.ingredients||[]).forEach(ing=>{
const li=document.createElement('li');
const cb=document.createElement('input'); cb.type='checkbox';
const span=document.createElement('span'); span.textContent=ing;
const shopBtn=document.createElement('button'); shopBtn.className='add-to-shopping-btn'; shopBtn.textContent='+ Shopping';
shopBtn.dataset.ingredient=ing; shopBtn.addEventListener('click',function(){addIngredientToShoppingList(this.dataset.ingredient);});
const stockBtn=document.createElement('button'); stockBtn.className='add-to-stock-btn'; stockBtn.textContent='+ Stock';
stockBtn.dataset.ingredient=ing; stockBtn.addEventListener('click',function(){addIngredientToStock(this.dataset.ingredient);});
li.appendChild(cb); li.appendChild(span); li.appendChild(shopBtn); li.appendChild(stockBtn);
list.appendChild(li);
});
document.getElementById('recipe-instructions').value=meal.instructions||'';
document.getElementById('recipe-notes').value=meal.notes||'';
['calories','protein','carbs','fats','fiber','sugar'].forEach(k=>{
document.getElementById(`recipe-${k}`).textContent=(meal.nutrition[k]||0)+(k!=='calories'?'g':'');
document.getElementById(`edit-${k}`).value=meal.nutrition[k]||0;
});
updateRecipeNutritionChart(meal.nutrition.protein,meal.nutrition.carbs,meal.nutrition.fats);
isEditingRecipe=false;
document.getElementById('recipe-edit-btn').style.display='';
document.getElementById('recipe-save-btn').style.display='none';
document.getElementById('recipe-cancel-btn').style.display='none';
document.getElementById('recipe-instructions').readOnly=true;
document.getElementById('recipe-notes').readOnly=true;
showRecipeTab('details');
document.getElementById('recipe-details-modal').classList.add('flex');
}
function updateRecipeNutritionChart(p,c,f){
const ctx=document.getElementById('recipe-nutrition-chart').getContext('2d');
if(window.recipeNutritionChart) window.recipeNutritionChart.destroy();
window.recipeNutritionChart=new Chart(ctx,{type:'doughnut',data:{labels:['Protein','Carbs','Fats'],datasets:[{data:[p,c,f],backgroundColor:['#4285F4','#FBBC05','#EA4335'],borderWidth:1}]},options:{responsive:true,maintainAspectRatio:false,cutout:'70%',plugins:{legend:{position:'bottom',labels:{boxWidth:12,padding:10,font:{size:10}}}}}});
}
function closeRecipeDetails(){document.getElementById('recipe-details-modal').classList.remove('flex');}
function showRecipeTab(tab){
document.querySelectorAll('#recipe-details-modal .modal-tab').forEach(t=>t.classList.remove('active'));
const activeTab=document.querySelector(`#recipe-details-modal .modal-tab[onclick="showRecipeTab('${tab}')"]`);
if(activeTab) activeTab.classList.add('active');
document.querySelectorAll('#recipe-details-modal .modal-tab-content').forEach(c=>c.classList.remove('active'));
const cont=document.getElementById(`recipe-${tab}-tab`); if(cont) cont.classList.add('active');
}
function toggleRecipeEdit(){
isEditingRecipe=true;
document.getElementById('recipe-edit-btn').style.display='none';
document.getElementById('recipe-save-btn').style.display='';
document.getElementById('recipe-cancel-btn').style.display='';
document.getElementById('recipe-instructions').readOnly=false;
document.getElementById('recipe-notes').readOnly=false;
const meal=mealData[editingRecipeCategory][editingRecipeIndex];
document.getElementById('recipe-basic-info-grid').innerHTML=`
${editingRecipeCategory}
${cat.charAt(0).toUpperCase()+cat.slice(1)}
`;
const grid=document.createElement('div'); grid.className='add-meal-items';
mealData[cat].forEach(m=>{
const el=document.createElement('div'); el.className='add-meal-item';
el.dataset.cat=cat; el.dataset.name=m.name;
if(m.photo){ el.innerHTML=`
${m.name}`; }
else { el.innerHTML=`${m.name}`; }
el.addEventListener('click',function(){addMealFromLibrary(this.dataset.cat,this.dataset.name);});
grid.appendChild(el);
});
sec.appendChild(grid); cont.appendChild(sec);
}
}
function addMealFromLibrary(cat,name){addMealToPlan(formatDate(currentDate),cat,name);closeAddMealModal();showToast(`"${name}" added to today's plan`);}
function selectIcon(icon,event){
selectedIcon=icon;
document.getElementById('icon-preview').innerHTML=``;
document.querySelectorAll('.icon-selection-item').forEach(i=>i.classList.remove('selected'));
if(event&&event.target) event.target.closest('.icon-selection-item').classList.add('selected');
}
function addCustomMealToPlan(){
const name=document.getElementById('custom-meal-name').value.trim();
const type=document.getElementById('custom-meal-type').value;
const cost=parseFloat(document.getElementById('custom-meal-cost').value)||0;
if(!name) return;
if(!mealData[type]) mealData[type]=[];
mealData[type].push({name,cost,icon:selectedIcon,photo:pendingCustomMealPhoto||null,nutrition:{calories:0,protein:0,carbs:0,fats:0,sugar:0,fiber:0},prepTime:0,cookTime:0,servings:1,difficulty:'Easy',ingredients:[],instructions:'',notes:'',tags:'',servingSuggestions:''});
pendingCustomMealPhoto=null;
updateMealLibraryDisplay(); updateAddMealModalLibrary();
addMealToPlan(formatDate(currentDate),type,name);
document.getElementById('custom-meal-name').value='';
document.getElementById('custom-meal-cost').value='';
document.getElementById('custom-meal-photo-box').innerHTML=``;
closeAddMealModal(); saveToLocalStorage();
}
// ============================================================
// DELETE HELPERS
// ============================================================
function confirmDeleteMeal(cat,name){
selectedMealToDelete=name; selectedMealTypeToDelete=cat;
pendingDeleteCallback=confirmDeleteMealAction;
document.getElementById('delete-confirm-title').textContent='Delete from Library';
document.getElementById('delete-confirm-msg').textContent=`Remove "${name}" from the meal library?`;
document.getElementById('delete-confirm-btn').textContent='Delete';
document.getElementById('delete-confirm-modal').classList.add('flex');
}
function confirmDeleteMealAction(){
mealData[selectedMealTypeToDelete]=mealData[selectedMealTypeToDelete].filter(m=>m.name!==selectedMealToDelete);
for(const d in mealPlan) mealPlan[d]=mealPlan[d].filter(m=>!(m.type===selectedMealTypeToDelete&&m.name===selectedMealToDelete));
updateMealLibraryDisplay(); updateAddMealModalLibrary(); updateCalendar();
updateDailyNutrition(formatDate(currentDate)); saveToLocalStorage();
}
function confirmDeleteMealFromPlan(type,name,date){
if(confirm(`Remove "${name}" from this day?`)){
if(mealPlan[date]){mealPlan[date]=mealPlan[date].filter(m=>!(m.type===type&&m.name===name));updateCalendar();updateDailyNutrition(date);saveToLocalStorage();}
}
}
function confirmDelete(){if(pendingDeleteCallback) pendingDeleteCallback();cancelDelete();}
function cancelDelete(){selectedMealToDelete=null;selectedMealTypeToDelete=null;pendingDeleteCallback=null;document.getElementById('delete-confirm-modal').classList.remove('flex');}
function showRestartDataConfirm(){
pendingDeleteCallback=resetData;
document.getElementById('delete-confirm-title').textContent='Restart All Data';
document.getElementById('delete-confirm-msg').textContent='This will delete ALL data. Cannot be undone.';
document.getElementById('delete-confirm-btn').textContent='Restart';
document.getElementById('delete-confirm-modal').classList.add('flex');
}
function resetData(){
mealData={breakfast:[{name:'Oatmeal Example',cost:2.50,icon:'fa-bread-slice',photo:null,nutrition:{calories:300,protein:10,carbs:55,fats:3,sugar:12,fiber:8},prepTime:10,cookTime:15,servings:2,difficulty:'Easy',ingredients:['1 cup rolled oats','2 cups water','1 banana','1 tbsp honey'],instructions:'1. Boil water.\n2. Add oats.\n3. Cook 5 min.',notes:'Use milk for creamier.',tags:'Quick, Healthy',servingSuggestions:'Serve hot'}],lunch:[],dinner:[],snacks:[],other:[]};
mealPlan={}; shoppingList=[];
stockItems=[{id:1,name:'Rolled oats',quantity:2,unit:'kg',usagePerMeal:0.1,minimumStock:0.5,reorderPoint:1,category:'Grains',tags:[],notes:'',usedInMeals:['Oatmeal Example'],storePrices:[{store:'Walmart',price:3.50,bulkPrice:'',isSale:false,isBulk:false}],lastUpdated:'2024-01-15'}];
nutritionGoals={calories:2000,protein:50,carbs:300,fats:65,sugar:25};
habitsData={};
updateMealLibraryDisplay(); updateAddMealModalLibrary(); updateCalendar();
updateShoppingList(); updateStockList(); updateDailyNutrition(formatDate(currentDate));
saveToLocalStorage(); showToast('Data reset!'); closeSettingsModal();
}
// ============================================================
// PRO NUTRITION CALCULATOR
// ============================================================
function selectActivityLevel(level,el){
selectedActivityLevel=level;
document.querySelectorAll('#activity-levels .checkbox-item').forEach(i=>i.classList.remove('selected'));
if(el) el.classList.add('selected');
document.querySelectorAll('.activity-checkbox').forEach(c=>c.checked=false);
const cb=document.getElementById(`activity-${level}`); if(cb) cb.checked=true;
}
function selectEatingStyle(style,el){
selectedEatingStyle=style;
document.querySelectorAll('#eating-styles .checkbox-item').forEach(i=>i.classList.remove('selected'));
if(el) el.classList.add('selected');
document.querySelectorAll('.style-checkbox').forEach(c=>c.checked=false);
const cb=document.getElementById(`style-${style}`); if(cb) cb.checked=true;
}
function toggleRestriction(r){
const cb=document.getElementById(`restriction-${r}`);
const item=event.target.closest('.restriction-item');
if(r==='none'){
document.querySelectorAll('#diet-restrictions .restriction-item').forEach(i=>i.classList.remove('selected'));
document.querySelectorAll('#diet-restrictions input[type="checkbox"]').forEach(c=>c.checked=false);
if(cb) cb.checked=true; if(item) item.classList.add('selected'); selectedRestrictions=['none'];
} else {
const noneCb=document.getElementById('restriction-none');
if(noneCb&&noneCb.checked){noneCb.checked=false;document.querySelector('#diet-restrictions .restriction-item:first-child')?.classList.remove('selected');selectedRestrictions=[];}
if(cb){
cb.checked=!cb.checked; if(item) item.classList.toggle('selected',cb.checked);
if(cb.checked){selectedRestrictions=selectedRestrictions.filter(x=>x!=='none');if(!selectedRestrictions.includes(r)) selectedRestrictions.push(r);}
else{selectedRestrictions=selectedRestrictions.filter(x=>x!==r);}
}
}
}
function calculateNutritionGoals(){
const age=parseInt(document.getElementById('user-age').value)||30;
const heightFt=parseInt(document.getElementById('height-ft').value)||5;
const heightIn=parseInt(document.getElementById('height-in').value)||9;
const weightLbs=parseFloat(document.getElementById('current-weight').value)||180;
const goalWeightLbs=parseFloat(document.getElementById('goal-weight').value)||160;
const timelineWeeks=parseInt(document.getElementById('timeline').value)||12;
const heightCm=((heightFt*12)+heightIn)*2.54;
const weightKg=weightLbs*0.453592;
let bmr;
if(selectedGender==='male'){ bmr=88.362+(13.397*weightKg)+(4.799*heightCm)-(5.677*age); }
else { bmr=447.593+(9.247*weightKg)+(3.098*heightCm)-(4.330*age); }
let actMult=1.2;
if(selectedActivityLevel==='light') actMult=1.375;
else if(selectedActivityLevel==='active') actMult=1.55;
else if(selectedActivityLevel==='athlete') actMult=1.725;
let tdee=bmr*actMult;
const currentBF=currentBFPercent/100, goalBF=goalBFPercent/100;
const leanMassKg=weightKg*(1-currentBF);
const targetWeightKg=goalWeightLbs*0.453592;
const fatToLose=weightKg-targetWeightKg;
let dailyCalories=tdee, phase='maintain';
if(fatToLose>0){const dailyDeficit=((fatToLose*7700)/timelineWeeks)/7;dailyCalories=tdee-dailyDeficit;phase='cut';}
else if(fatToLose<0){const dailySurplus=((Math.abs(fatToLose)*7700)/timelineWeeks)/7;dailyCalories=tdee+dailySurplus;phase='bulk';}
else{phase='recomp';}
dailyCalories=Math.max(1200,Math.min(4000,dailyCalories));
let proteinMult=2.0;
if(phase==='cut') proteinMult=2.2; else if(phase==='bulk') proteinMult=1.8;
let proteinGrams=leanMassKg*proteinMult;
let fatPct=0.25;
if(selectedEatingStyle==='high-fat') fatPct=0.35;
let fatGrams=(dailyCalories*fatPct)/9;
let carbGrams=Math.max(50,(dailyCalories-(proteinGrams*4)-(fatGrams*9))/4);
document.getElementById('result-calories').textContent=Math.round(dailyCalories);
document.getElementById('result-protein').textContent=Math.round(proteinGrams)+'g';
document.getElementById('result-carbs').textContent=Math.round(carbGrams)+'g';
document.getElementById('result-fats').textContent=Math.round(fatGrams)+'g';
document.getElementById('results-metrics-row').innerHTML=`
${weightLbs} lbs
Current Weight
${goalWeightLbs} lbs
Goal Weight
${currentBFPercent}%
Current Body Fat
${Math.round(leanMassKg*2.20462)} lbs
Lean Mass
${phase.toUpperCase()}${phase==='cut'?'Calorie deficit for fat loss':phase==='bulk'?'Calorie surplus for muscle gain':'Maintenance for body recomposition'}
${timelineWeeks} weeks to goal
${Math.round(Math.abs(fatToLose*2.20462))} lbs fat to ${fatToLose>0?'lose':'gain'}
${habit.name}
${habit.detail}
`;
card.onclick=()=>toggleHabit(habit.id);
container.appendChild(card);
});
const customRow=document.createElement('div'); customRow.className='habit-custom-row';
customRow.innerHTML=``;
container.appendChild(customRow);
}
function toggleHabit(habitId){
const dateStr=getTodayStr();
const habits=getHabitsForDate(dateStr);
if(!habits.habits) habits.habits={};
habits.habits[habitId]=!habits.habits[habitId];
habitsData[dateStr]=habits;
renderEatingHabitsGrid(habits.habits);
renderStreakGrid(); saveToLocalStorage();
}
function addCustomHabit(){
const input=document.getElementById('custom-habit-name');
const name=input.value.trim(); if(!name) return;
const id='custom_'+Date.now();
DEFAULT_EATING_HABITS.push({id,icon:'โญ',name,detail:'Custom habit'});
input.value='';
renderEatingHabitsGrid(getHabitsForDate(getTodayStr()).habits||{});
showToast('Custom habit added!');
}
function saveHabitsEntry(){
const dateStr=getTodayStr();
const habits=getHabitsForDate(dateStr);
habits.notes=document.getElementById('habit-daily-notes').value;
habitsData[dateStr]=habits; saveToLocalStorage();
}
function renderStreakGrid(){
const container=document.getElementById('streak-grid'); container.innerHTML='';
const days=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
for(let i=6;i>=0;i--){
const d=new Date(habitsDate); d.setDate(habitsDate.getDate()-i);
const dateStr=formatDate(d);
const habits=habitsData[dateStr];
const isToday=(i===0);
let completedCount=0;
const totalHabits=DEFAULT_EATING_HABITS.length;
if(habits&&habits.habits) completedCount=Object.values(habits.habits).filter(v=>v===true).length;
let statusClass='', statusText='โ';
if(completedCount===totalHabits&&totalHabits>0){statusClass='active';statusText='โ';}
else if(completedCount>0){statusClass='partial';statusText=completedCount;}
const dayDiv=document.createElement('div'); dayDiv.className='streak-day';
dayDiv.innerHTML=`${days[d.getDay()]}
${statusText}
`;
container.appendChild(dayDiv);
}
}
// ============================================================
// DATA IMPORT/EXPORT
// ============================================================
function exportData(){
const exportObj={mealData,mealPlan,shoppingList,stockItems,nutritionGoals,habitsData,version:'2.0'};
const dataStr=JSON.stringify(exportObj,null,2);
const blob=new Blob([dataStr],{type:'application/json'});
const url=URL.createObjectURL(blob);
const a=document.createElement('a'); a.href=url; a.download=`keetok_backup_${formatDate(new Date())}.json`; a.click();
URL.revokeObjectURL(url); showToast('Data exported!');
}
document.getElementById('import-data-file').addEventListener('change',function(e){
const file=e.target.files[0]; if(!file) return;
const reader=new FileReader();
reader.onload=function(ev){
try{
const data=JSON.parse(ev.target.result);
if(data.mealData){ for(const cat in data.mealData){ mealData[cat]=data.mealData[cat].map(m=>({photo:null,...m})); } }
if(data.mealPlan) mealPlan=data.mealPlan;
if(data.shoppingList) shoppingList=data.shoppingList;
if(data.stockItems) stockItems=data.stockItems;
if(data.nutritionGoals) nutritionGoals=data.nutritionGoals;
if(data.habitsData) habitsData=data.habitsData;
updateMealLibraryDisplay(); updateAddMealModalLibrary(); updateCalendar();
updateShoppingList(); updateStockList(); updateDailyNutrition(formatDate(currentDate));
renderHabitsTab(); saveToLocalStorage(); showToast('Data imported!');
}catch(err){showToast('Invalid file','warning');}
};
reader.readAsText(file);
});
// ============================================================
// INITIALIZATION
// ============================================================
function init(){
loadFromLocalStorage();
loadSettings();
selectLibraryView(currentLibraryView, false);
updateMealLibraryDisplay();
updateAddMealModalLibrary();
updateCalendar();
updateShoppingList();
updateStockList();
buildBFSelectors();
setupNutritionChart();
applyDisplayOptions();
const savedTab=localStorage.getItem('keetokMainTab');
if(savedTab&&['calendar','nutrition','shopping','stock','habits'].includes(savedTab)) showMainTab(savedTab);
renderHabitsTab();
showToast('Welcome to KeeTok Meals Planner! ๐ฝ๏ธ','success',3000);
}
init();