🍳 Recipe Scaler + Pantry Tracker
Scale recipes to any serving size and track your pantry inventory
Recipe Details
Scaled Recipe
4
Scale Factor
1.0×
Your Pantry
0
Items
0
Categories
Check Recipe Against Pantry
💡 How it works: Scale a recipe and add items to your pantry, then check if you have everything needed!
Shopping List
Availability Check
'; const availability = document.createElement('div'); availability.className = 'availability-check'; let availableCount = 0; missingItems = []; currentRecipe.ingredients.forEach(ing => { const needed = ing.originalAmount * scaleFactor; const pantryItem = pantryItems.find(p => p.name.toLowerCase() === ing.name.toLowerCase() && p.unit === ing.unit); const item = document.createElement('div'); item.className = 'availability-item'; let status, statusClass, statusIcon; if (!pantryItem) { status = 'Not in pantry'; statusClass = 'status-unavailable'; statusIcon = '✗'; missingItems.push({ name: ing.name, amount: needed, unit: ing.unit }); } else if (pantryItem.amount >= needed) { status = 'Available'; statusClass = 'status-available'; statusIcon = '✓'; availableCount++; } else { status = 'Need ' + formatAmount(needed - pantryItem.amount) + ' ' + ing.unit + ' more'; statusClass = 'status-partial'; statusIcon = '⚠'; missingItems.push({ name: ing.name, amount: needed - pantryItem.amount, unit: ing.unit }); } item.innerHTML = `
${ing.name}
Need: ${formatAmount(needed)} ${ing.unit}
${pantryItem ? ' | Have: ' + formatAmount(pantryItem.amount) + ' ' + ing.unit : ''}
${statusIcon}
${status}
`;
availability.appendChild(item);
});
results.appendChild(availability);
const totalItems = currentRecipe.ingredients.length;
const alertDiv = document.createElement('div');
if (availableCount === totalItems) {
alertDiv.className = 'alert-box success';
alertDiv.innerHTML = '✓ Ready to Cook! You have all ingredients needed for this recipe.';
} else if (availableCount > 0) {
alertDiv.className = 'alert-box warning';
alertDiv.innerHTML = '⚠ Partially Ready You have ' + availableCount + ' of ' + totalItems + ' ingredients. Check the shopping list.';
} else {
alertDiv.className = 'alert-box error';
alertDiv.innerHTML = '✗ Missing Ingredients You need to shop for all ingredients.';
}
results.appendChild(alertDiv);
}
function generateShoppingList() {
if (!missingItems || missingItems.length === 0) {
showNotification('No missing ingredients. You have everything!', 'success');
return;
}
const section = document.getElementById('check-shopping-list-section');
const list = document.getElementById('check-shopping-list');
list.innerHTML = '';
missingItems.forEach(item => {
const div = document.createElement('div');
div.className = 'shopping-item';
div.innerHTML = `
${item.name}: ${formatAmount(item.amount)} ${item.unit}
`;
list.appendChild(div);
});
section.style.display = 'block';
section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// === EXPORT FUNCTIONS ===
function exportRecipeCSV() {
if (currentRecipe.ingredients.length === 0) return;
const scaleFactor = currentRecipe.targetServings / currentRecipe.originalServings;
let csv = currentRecipe.name + ' (' + currentRecipe.targetServings + ' servings)\n\n';
csv += 'Ingredient,Amount,Unit\n';
currentRecipe.ingredients.forEach(ing => {
const amount = ing.originalAmount * scaleFactor;
csv += ing.name + ',' + formatAmount(amount) + ',' + ing.unit + '\n';
});
downloadFile(csv, currentRecipe.name.replace(/\s+/g, '-') + '.csv', 'text/csv');
showNotification('Recipe exported as CSV!');
}
function copyRecipeToClipboard() {
if (currentRecipe.ingredients.length === 0) return;
const scaleFactor = currentRecipe.targetServings / currentRecipe.originalServings;
let text = currentRecipe.name + ' (' + currentRecipe.targetServings + ' servings)\n\n';
currentRecipe.ingredients.forEach(ing => {
const amount = ing.originalAmount * scaleFactor;
text += '• ' + formatAmount(amount) + ' ' + ing.unit + ' ' + ing.name + '\n';
});
navigator.clipboard.writeText(text).then(() => {
showNotification('Recipe copied to clipboard!');
}).catch(() => {
prompt('Copy this recipe:', text);
});
}
function exportShoppingCSV() {
if (!missingItems || missingItems.length === 0) return;
let csv = 'Shopping List\n\nIngredient,Amount,Unit\n';
missingItems.forEach(item => {
csv += item.name + ',' + formatAmount(item.amount) + ',' + item.unit + '\n';
});
downloadFile(csv, 'shopping-list.csv', 'text/csv');
showNotification('Shopping list exported!');
}
function copyShoppingList() {
if (!missingItems || missingItems.length === 0) return;
let text = 'Shopping List\n\n';
missingItems.forEach(item => {
text += '☐ ' + item.name + ': ' + formatAmount(item.amount) + ' ' + item.unit + '\n';
});
navigator.clipboard.writeText(text).then(() => {
showNotification('Shopping list copied!');
});
}
function downloadFile(content, filename, type) {
const blob = new Blob([content], { type: type });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
// === STATE PERSISTENCE ===
function saveRecipe() {
const name = document.getElementById('recipe-name').value.trim();
const originalServings = document.getElementById('recipe-original-servings').value;
const ingredients = getIngredients();
if (!name || ingredients.length === 0) {
showNotification('Please enter recipe name and ingredients', 'error');
return;
}
try {
const data = { name: name, originalServings: originalServings, ingredients: ingredients };
localStorage.setItem(CONFIG.recipeStorageKey, JSON.stringify(data));
showNotification('Recipe saved!');
} catch (e) {
showNotification('Could not save recipe', 'error');
}
}
function loadRecipe() {
try {
const saved = localStorage.getItem(CONFIG.recipeStorageKey);
if (!saved) {
showNotification('No saved recipe found', 'error');
return;
}
const data = JSON.parse(saved);
document.getElementById('recipe-name').value = data.name;
document.getElementById('recipe-original-servings').value = data.originalServings;
document.getElementById('recipe-target-servings').value = data.originalServings;
const list = document.getElementById('recipe-ingredient-list');
list.innerHTML = '';
data.ingredients.forEach(ing => {
const item = document.createElement('div');
item.className = 'ingredient-item';
item.innerHTML = `
`;
list.appendChild(item);
});
showNotification('Recipe loaded!');
} catch (e) {
showNotification('Error loading recipe', 'error');
}
}
function savePantry() {
const items = getPantryItems();
if (items.length === 0) {
showNotification('Pantry is empty', 'error');
return;
}
try {
localStorage.setItem(CONFIG.pantryStorageKey, JSON.stringify(items));
pantry = items;
showNotification('Pantry saved!');
} catch (e) {
showNotification('Could not save pantry', 'error');
}
}
function loadPantryFromStorage() {
try {
const saved = localStorage.getItem(CONFIG.pantryStorageKey);
if (saved) {
pantry = JSON.parse(saved);
loadPantryDisplay();
showNotification('Pantry loaded!');
} else {
showNotification('No saved pantry found', 'error');
}
} catch (e) {
showNotification('Error loading pantry', 'error');
}
}
function clearPantry() {
if (confirm('Clear all pantry items?')) {
pantry = [];
loadPantryDisplay();
localStorage.removeItem(CONFIG.pantryStorageKey);
showNotification('Pantry cleared');
}
}
// === EVENT LISTENERS ===
document.getElementById('recipe-add-ingredient').addEventListener('click', addIngredient);
document.getElementById('recipe-scale-btn').addEventListener('click', scaleRecipe);
document.getElementById('recipe-save-btn').addEventListener('click', saveRecipe);
document.getElementById('recipe-load-btn').addEventListener('click', loadRecipe);
document.getElementById('recipe-reset-btn').addEventListener('click', () => {
if (confirm('Reset recipe?')) location.reload();
});
document.getElementById('recipe-decrease-half').addEventListener('click', () => adjustServings('half'));
document.getElementById('recipe-decrease').addEventListener('click', () => adjustServings(-1));
document.getElementById('recipe-increase').addEventListener('click', () => adjustServings(1));
document.getElementById('recipe-double').addEventListener('click', () => adjustServings('double'));
document.getElementById('recipe-export-csv').addEventListener('click', exportRecipeCSV);
document.getElementById('recipe-copy').addEventListener('click', copyRecipeToClipboard);
document.getElementById('recipe-print').addEventListener('click', () => window.print());
document.getElementById('pantry-add-item').addEventListener('click', addPantryItem);
document.getElementById('pantry-search').addEventListener('input', searchPantry);
document.getElementById('pantry-save-btn').addEventListener('click', savePantry);
document.getElementById('pantry-load-btn').addEventListener('click', loadPantryFromStorage);
document.getElementById('pantry-clear-btn').addEventListener('click', clearPantry);
document.getElementById('check-availability-btn').addEventListener('click', checkAvailability);
document.getElementById('check-generate-shopping').addEventListener('click', generateShoppingList);
document.getElementById('check-export-shopping').addEventListener('click', exportShoppingCSV);
document.getElementById('check-copy-shopping').addEventListener('click', copyShoppingList);
// === INITIALIZATION ===
function init() {
loadPantryDisplay();
}
init();
})();




