Fix calculating nutritions
All checks were successful
Build and Deploy / build-and-push (push) Successful in 26s

This commit is contained in:
2026-03-27 23:08:54 +01:00
parent 02fb8a2754
commit 855d17374e
3 changed files with 33 additions and 15 deletions

View File

@@ -30,6 +30,7 @@ export const INGREDIENTS = {
name: 'Bułka grahamka',
category: 'pieczywo',
pantryUnit: 'szt',
weightPerPiece: 70,
purchasePack: { amount: 1, label: '1 bułka ~70 g' },
nutritionPer100g: { kcal: 260, protein: 9, fat: 3, carbs: 48 },
},
@@ -39,6 +40,7 @@ export const INGREDIENTS = {
name: 'Jajka',
category: 'nabial',
pantryUnit: 'szt',
weightPerPiece: 60,
nutritionPer100g: { kcal: 143, protein: 13, fat: 9.5, carbs: 1.1 },
},
mozzarella: {
@@ -104,6 +106,7 @@ export const INGREDIENTS = {
name: 'Pomidor',
category: 'warzywa',
pantryUnit: 'szt',
weightPerPiece: 130,
nutritionPer100g: { kcal: 18, protein: 0.9, fat: 0.2, carbs: 3.9 },
},
pomidorki_koktajlowe: {
@@ -119,6 +122,7 @@ export const INGREDIENTS = {
name: 'Papryka czerwona',
category: 'warzywa',
pantryUnit: 'szt',
weightPerPiece: 160,
nutritionPer100g: { kcal: 31, protein: 1, fat: 0.3, carbs: 6 },
},
ogorek: {
@@ -126,6 +130,7 @@ export const INGREDIENTS = {
name: 'Ogórek',
category: 'warzywa',
pantryUnit: 'szt',
weightPerPiece: 200,
nutritionPer100g: { kcal: 15, protein: 0.7, fat: 0.1, carbs: 3 },
},
czosnek: {
@@ -133,6 +138,7 @@ export const INGREDIENTS = {
name: 'Czosnek',
category: 'warzywa',
pantryUnit: 'szt',
weightPerPiece: 35,
nutritionPer100g: { kcal: 149, protein: 6.4, fat: 0.5, carbs: 33 },
},
kielki_rzodkiewki: {
@@ -343,7 +349,7 @@ export const RECIPES = {
image: 'images/recipes/jajecznica.png',
allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'],
tags: ['szybkie', 'wysokobiałkowe'],
nutritionPerServing: { kcal: 548, protein: 36, fat: 28, carbs: 36 },
nutritionPerServing: { kcal: 571, protein: 38, fat: 30, carbs: 36 },
ingredients: [
{ ingredientId: 'jajko', amount: 4, unit: 'szt.' },
{ ingredientId: 'oliwa', amount: 5, unit: 'ml' },
@@ -418,7 +424,7 @@ export const RECIPES = {
image: 'images/recipes/serek_owoc.jpg',
allowedSlots: ['sniadanie', 'drugie_sniadanie', 'przekaska'],
tags: ['wegetariańskie', 'wysokobiałkowe', 'szybkie'],
nutritionPerServing: { kcal: 642, protein: 32, fat: 43, carbs: 41 },
nutritionPerServing: { kcal: 629, protein: 31, fat: 43, carbs: 40 },
ingredients: [
{ ingredientId: 'serek_wiejski', amount: 200, unit: 'g' },
{ ingredientId: 'miod', amount: 10, unit: 'g' },
@@ -456,7 +462,11 @@ export function pantryQtyStep(ingredientId) {
export function nutritionForStock(def, stockQty) {
const n = def.nutritionPer100g;
if (!n || !Number.isFinite(stockQty) || stockQty <= 0) return null;
const f = stockQty / 100;
let grams = stockQty;
if (def.pantryUnit === 'szt' && def.weightPerPiece) {
grams = stockQty * def.weightPerPiece;
}
const f = grams / 100;
return {
kcal: Math.round(n.kcal * f),
protein: Math.round(n.protein * f * 10) / 10,

View File

@@ -173,10 +173,14 @@ function updateKcalDisplay() {
document.getElementById('rd-kcal').textContent = `${kcal} kcal`;
}
function nutritionForAmount(ingredientId, amount) {
function nutritionForAmount(ingredientId, amount, unit) {
const def = INGREDIENTS[ingredientId];
if (!def?.nutritionPer100g) return null;
const f = amount / 100;
let grams = amount;
if ((unit === 'szt.' || unit === 'szt') && def.weightPerPiece) {
grams = amount * def.weightPerPiece;
}
const f = grams / 100;
return {
kcal: Math.round(def.nutritionPer100g.kcal * f),
protein: Math.round(def.nutritionPer100g.protein * f * 10) / 10,
@@ -214,7 +218,7 @@ function renderIngredients(recipe) {
const altCards = ing.alternatives.map((altId) => {
const altDef = INGREDIENTS[altId];
const altName = escapeHtml(altDef?.name || altId);
const nutrition = nutritionForAmount(altId, scaledAmount);
const nutrition = nutritionForAmount(altId, scaledAmount, ing.unit);
return `<div class="bg-gray-50 rounded-lg p-2.5 border border-gray-100">
<p class="text-[12px] font-medium text-gray-700">${altName}</p>
${renderNutritionLine(nutrition)}
@@ -497,7 +501,7 @@ export function setupRecipeDetail() {
<span class="text-[12px] font-semibold text-gray-900 truncate">${escapeHtml(effectiveName)}</span>
<span class="text-[10px] text-gray-400 tabular-nums shrink-0">${displayAmount} ${escapeHtml(ing.unit)}</span>
</div>
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount))}
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount, ing.unit))}
</div>
<i class="fas fa-chevron-${isExpanded ? 'up' : 'down'} text-[9px] text-gray-400 shrink-0"></i>
</button>`;
@@ -510,7 +514,7 @@ export function setupRecipeDetail() {
const altName = def?.name || altId;
const isSelected = effectiveId === altId;
const isOriginal = altId === origId;
const nutrition = nutritionForAmount(altId, scaledAmount);
const nutrition = nutritionForAmount(altId, scaledAmount, ing.unit);
const selectedCls = isSelected
? 'border-gray-900 bg-gray-50 ring-1 ring-gray-900'

View File

@@ -183,10 +183,14 @@ function updateKcalDisplay() {
el.textContent = `${kcal} kcal`;
}
function nutritionForAmount(ingredientId, amount) {
function nutritionForAmount(ingredientId, amount, unit) {
const def = INGREDIENTS[ingredientId];
if (!def?.nutritionPer100g) return null;
const f = amount / 100;
let grams = amount;
if ((unit === 'szt.' || unit === 'szt') && def.weightPerPiece) {
grams = amount * def.weightPerPiece;
}
const f = grams / 100;
return {
kcal: Math.round(def.nutritionPer100g.kcal * f),
protein: Math.round(def.nutritionPer100g.protein * f * 10) / 10,
@@ -206,7 +210,7 @@ function computeEffectiveNutritionTotals(recipe) {
let kcal = 0, protein = 0, fat = 0, carbs = 0;
for (const ing of recipe.ingredients) {
const effectiveId = getEffectiveIngredientId(ing.ingredientId);
const n = nutritionForAmount(effectiveId, ing.amount * currentServings);
const n = nutritionForAmount(effectiveId, ing.amount * currentServings, ing.unit);
if (n) {
kcal += n.kcal;
protein += n.protein;
@@ -290,7 +294,7 @@ function renderIngredients(recipe) {
const effectiveDef = INGREDIENTS[effectiveId];
const effectiveName = effectiveDef?.name || effectiveId;
const scaledAmount = ing.amount * currentServings;
const nutrition = nutritionForAmount(effectiveId, scaledAmount);
const nutrition = nutritionForAmount(effectiveId, scaledAmount, ing.unit);
const isSwapped = effectiveId !== origId;
const isExpanded = expandedAlternatives.has(origId);
@@ -314,7 +318,7 @@ function renderIngredients(recipe) {
const altName = def?.name || altId;
const isSelected = effectiveId === altId;
const isOriginal = altId === origId;
const altNutrition = nutritionForAmount(altId, scaledAmount);
const altNutrition = nutritionForAmount(altId, scaledAmount, ing.unit);
const radioDot = `<div class="w-3.5 h-3.5 rounded-full border-2 shrink-0 ${isSelected ? 'border-gray-900' : 'border-gray-300'} flex items-center justify-center">${isSelected ? '<div class="w-1.5 h-1.5 rounded-full bg-gray-900"></div>' : ''}</div>`;
const defaultTag = isOriginal ? `<span class="text-[9px] px-1.5 py-0.5 rounded ${isSelected ? 'bg-gray-200 text-gray-600' : 'bg-gray-100 text-gray-400'} font-medium ml-1">Domyślny</span>` : '';
@@ -585,7 +589,7 @@ export function setupRecipeDetail() {
<span class="text-[12px] font-semibold text-gray-900 truncate">${escapeHtml(effectiveName)}</span>
<span class="text-[10px] text-gray-400 tabular-nums shrink-0">${displayAmount} ${escapeHtml(ing.unit)}</span>
</div>
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount))}
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount, ing.unit))}
</div>
<i class="fas fa-chevron-${isExpanded ? 'up' : 'down'} text-[9px] text-gray-400 shrink-0"></i>
</button>`;
@@ -598,7 +602,7 @@ export function setupRecipeDetail() {
const altName = def?.name || altId;
const isSelected = effectiveId === altId;
const isOriginal = altId === origId;
const nutrition = nutritionForAmount(altId, scaledAmount);
const nutrition = nutritionForAmount(altId, scaledAmount, ing.unit);
const selectedCls = isSelected
? 'border-gray-900 bg-gray-50 ring-1 ring-gray-900'