Add meal plan editor
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m19s
Some checks failed
Build and Deploy / build-and-push (push) Failing after 1m19s
This commit is contained in:
@@ -13,27 +13,79 @@ export function dayHasAnyMeal(plans, d) {
|
||||
});
|
||||
}
|
||||
|
||||
function hasCustomizations(entry) {
|
||||
return (entry.excludedIngredients?.length > 0) ||
|
||||
(entry.amountOverrides && Object.keys(entry.amountOverrides).length > 0) ||
|
||||
(entry.addedIngredients?.length > 0) ||
|
||||
(entry.substitutions && Object.keys(entry.substitutions).length > 0);
|
||||
}
|
||||
|
||||
function nutritionForAmountRaw(ingredientId, amount, unit) {
|
||||
const def = INGREDIENTS[ingredientId];
|
||||
if (!def?.nutritionPer100g) return null;
|
||||
let g = amount;
|
||||
if ((unit === 'szt.' || unit === 'szt') && def.weightPerPiece) g = amount * def.weightPerPiece;
|
||||
const f = g / 100;
|
||||
return {
|
||||
kcal: def.nutritionPer100g.kcal * f,
|
||||
protein: def.nutritionPer100g.protein * f,
|
||||
fat: def.nutritionPer100g.fat * f,
|
||||
carbs: def.nutritionPer100g.carbs * f,
|
||||
};
|
||||
}
|
||||
|
||||
export function computeEntryNutrition(entry) {
|
||||
if (!entry?.recipeId) return { kcal: 0, protein: 0, fat: 0, carbs: 0 };
|
||||
const r = RECIPES[entry.recipeId];
|
||||
if (!r) return { kcal: 0, protein: 0, fat: 0, carbs: 0 };
|
||||
const s = Math.max(1, Number(entry.servings) || 1);
|
||||
|
||||
if (!hasCustomizations(entry)) {
|
||||
return {
|
||||
kcal: Math.round(r.nutritionPerServing.kcal * s),
|
||||
protein: Math.round(r.nutritionPerServing.protein * s * 10) / 10,
|
||||
fat: Math.round(r.nutritionPerServing.fat * s * 10) / 10,
|
||||
carbs: Math.round(r.nutritionPerServing.carbs * s * 10) / 10,
|
||||
};
|
||||
}
|
||||
|
||||
const excluded = new Set(entry.excludedIngredients || []);
|
||||
const overrides = entry.amountOverrides || {};
|
||||
const subs = entry.substitutions || {};
|
||||
let kcal = 0, protein = 0, fat = 0, carbs = 0;
|
||||
|
||||
for (const ing of r.ingredients) {
|
||||
if (excluded.has(ing.ingredientId)) continue;
|
||||
const eid = subs[ing.ingredientId] || ing.ingredientId;
|
||||
const base = overrides[ing.ingredientId] ?? ing.amount;
|
||||
const n = nutritionForAmountRaw(eid, base * s, ing.unit);
|
||||
if (n) { kcal += n.kcal; protein += n.protein; fat += n.fat; carbs += n.carbs; }
|
||||
}
|
||||
for (const a of (entry.addedIngredients || [])) {
|
||||
const n = nutritionForAmountRaw(a.ingredientId, a.amount * s, a.unit);
|
||||
if (n) { kcal += n.kcal; protein += n.protein; fat += n.fat; carbs += n.carbs; }
|
||||
}
|
||||
|
||||
return {
|
||||
kcal: Math.round(kcal),
|
||||
protein: Math.round(protein * 10) / 10,
|
||||
fat: Math.round(fat * 10) / 10,
|
||||
carbs: Math.round(carbs * 10) / 10,
|
||||
};
|
||||
}
|
||||
|
||||
export function sumDayNutrition(dayPlan) {
|
||||
let kcal = 0;
|
||||
let protein = 0;
|
||||
let fat = 0;
|
||||
let carbs = 0;
|
||||
let mealCount = 0;
|
||||
let kcal = 0, protein = 0, fat = 0, carbs = 0, mealCount = 0;
|
||||
const skipped = dayPlan._skipped || {};
|
||||
MEAL_SLOTS.forEach((slot) => {
|
||||
if (skipped[slot.id]) return;
|
||||
const entries = dayPlan[slot.id];
|
||||
if (!Array.isArray(entries)) return;
|
||||
entries.forEach((entry) => {
|
||||
if (!entry || !entry.recipeId) return;
|
||||
const r = RECIPES[entry.recipeId];
|
||||
if (!r) return;
|
||||
const s = Math.max(1, Number(entry.servings) || 1);
|
||||
if (!entry?.recipeId || !RECIPES[entry.recipeId]) return;
|
||||
mealCount += 1;
|
||||
kcal += r.nutritionPerServing.kcal * s;
|
||||
protein += r.nutritionPerServing.protein * s;
|
||||
fat += r.nutritionPerServing.fat * s;
|
||||
carbs += r.nutritionPerServing.carbs * s;
|
||||
const n = computeEntryNutrition(entry);
|
||||
kcal += n.kcal; protein += n.protein; fat += n.fat; carbs += n.carbs;
|
||||
});
|
||||
});
|
||||
return {
|
||||
@@ -70,10 +122,20 @@ export function flattenDayIngredientLines(dayPlan) {
|
||||
const r = RECIPES[entry.recipeId];
|
||||
if (!r || !Array.isArray(r.ingredients)) return;
|
||||
const serv = Math.max(1, Number(entry.servings) || 1);
|
||||
const excluded = new Set(entry.excludedIngredients || []);
|
||||
const overrides = entry.amountOverrides || {};
|
||||
const subs = entry.substitutions || {};
|
||||
r.ingredients.forEach((ing) => {
|
||||
const scaled = Math.round(ing.amount * serv * 10) / 10;
|
||||
out.push(resolveLine(ing, scaled));
|
||||
if (excluded.has(ing.ingredientId)) return;
|
||||
const effectiveId = subs[ing.ingredientId] || ing.ingredientId;
|
||||
const base = overrides[ing.ingredientId] ?? ing.amount;
|
||||
const scaled = Math.round(base * serv * 10) / 10;
|
||||
out.push(resolveLine({ ingredientId: effectiveId, unit: ing.unit }, scaled));
|
||||
});
|
||||
for (const a of (entry.addedIngredients || [])) {
|
||||
const scaled = Math.round(a.amount * serv * 10) / 10;
|
||||
out.push(resolveLine(a, scaled));
|
||||
}
|
||||
});
|
||||
});
|
||||
return out;
|
||||
@@ -129,16 +191,33 @@ export function aggregateDayIngredientsBySlot(dayPlan) {
|
||||
const r = RECIPES[entry.recipeId];
|
||||
if (!r) return;
|
||||
const s = Math.max(1, Number(entry.servings) || 1);
|
||||
const items = r.ingredients.map((ing) => {
|
||||
const def = INGREDIENTS[ing.ingredientId];
|
||||
return {
|
||||
ingredientId: ing.ingredientId,
|
||||
name: def?.name ?? ing.ingredientId,
|
||||
const excluded = new Set(entry.excludedIngredients || []);
|
||||
const overrides = entry.amountOverrides || {};
|
||||
const subs = entry.substitutions || {};
|
||||
const items = [];
|
||||
r.ingredients.forEach((ing) => {
|
||||
if (excluded.has(ing.ingredientId)) return;
|
||||
const effectiveId = subs[ing.ingredientId] || ing.ingredientId;
|
||||
const base = overrides[ing.ingredientId] ?? ing.amount;
|
||||
const def = INGREDIENTS[effectiveId];
|
||||
items.push({
|
||||
ingredientId: effectiveId,
|
||||
name: def?.name ?? effectiveId,
|
||||
category: def?.category ?? 'inne',
|
||||
amount: Math.round(ing.amount * s * 10) / 10,
|
||||
amount: Math.round(base * s * 10) / 10,
|
||||
unit: ing.unit,
|
||||
};
|
||||
});
|
||||
});
|
||||
for (const a of (entry.addedIngredients || [])) {
|
||||
const def = INGREDIENTS[a.ingredientId];
|
||||
items.push({
|
||||
ingredientId: a.ingredientId,
|
||||
name: def?.name ?? a.ingredientId,
|
||||
category: def?.category ?? 'inne',
|
||||
amount: Math.round(a.amount * s * 10) / 10,
|
||||
unit: a.unit,
|
||||
});
|
||||
}
|
||||
recipes.push({ recipeTitle: r.title, items });
|
||||
});
|
||||
if (recipes.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user