Fix calculating nutritions
All checks were successful
Build and Deploy / build-and-push (push) Successful in 26s
All checks were successful
Build and Deploy / build-and-push (push) Successful in 26s
This commit is contained in:
@@ -30,6 +30,7 @@ export const INGREDIENTS = {
|
|||||||
name: 'Bułka grahamka',
|
name: 'Bułka grahamka',
|
||||||
category: 'pieczywo',
|
category: 'pieczywo',
|
||||||
pantryUnit: 'szt',
|
pantryUnit: 'szt',
|
||||||
|
weightPerPiece: 70,
|
||||||
purchasePack: { amount: 1, label: '1 bułka ~70 g' },
|
purchasePack: { amount: 1, label: '1 bułka ~70 g' },
|
||||||
nutritionPer100g: { kcal: 260, protein: 9, fat: 3, carbs: 48 },
|
nutritionPer100g: { kcal: 260, protein: 9, fat: 3, carbs: 48 },
|
||||||
},
|
},
|
||||||
@@ -39,6 +40,7 @@ export const INGREDIENTS = {
|
|||||||
name: 'Jajka',
|
name: 'Jajka',
|
||||||
category: 'nabial',
|
category: 'nabial',
|
||||||
pantryUnit: 'szt',
|
pantryUnit: 'szt',
|
||||||
|
weightPerPiece: 60,
|
||||||
nutritionPer100g: { kcal: 143, protein: 13, fat: 9.5, carbs: 1.1 },
|
nutritionPer100g: { kcal: 143, protein: 13, fat: 9.5, carbs: 1.1 },
|
||||||
},
|
},
|
||||||
mozzarella: {
|
mozzarella: {
|
||||||
@@ -104,6 +106,7 @@ export const INGREDIENTS = {
|
|||||||
name: 'Pomidor',
|
name: 'Pomidor',
|
||||||
category: 'warzywa',
|
category: 'warzywa',
|
||||||
pantryUnit: 'szt',
|
pantryUnit: 'szt',
|
||||||
|
weightPerPiece: 130,
|
||||||
nutritionPer100g: { kcal: 18, protein: 0.9, fat: 0.2, carbs: 3.9 },
|
nutritionPer100g: { kcal: 18, protein: 0.9, fat: 0.2, carbs: 3.9 },
|
||||||
},
|
},
|
||||||
pomidorki_koktajlowe: {
|
pomidorki_koktajlowe: {
|
||||||
@@ -119,6 +122,7 @@ export const INGREDIENTS = {
|
|||||||
name: 'Papryka czerwona',
|
name: 'Papryka czerwona',
|
||||||
category: 'warzywa',
|
category: 'warzywa',
|
||||||
pantryUnit: 'szt',
|
pantryUnit: 'szt',
|
||||||
|
weightPerPiece: 160,
|
||||||
nutritionPer100g: { kcal: 31, protein: 1, fat: 0.3, carbs: 6 },
|
nutritionPer100g: { kcal: 31, protein: 1, fat: 0.3, carbs: 6 },
|
||||||
},
|
},
|
||||||
ogorek: {
|
ogorek: {
|
||||||
@@ -126,6 +130,7 @@ export const INGREDIENTS = {
|
|||||||
name: 'Ogórek',
|
name: 'Ogórek',
|
||||||
category: 'warzywa',
|
category: 'warzywa',
|
||||||
pantryUnit: 'szt',
|
pantryUnit: 'szt',
|
||||||
|
weightPerPiece: 200,
|
||||||
nutritionPer100g: { kcal: 15, protein: 0.7, fat: 0.1, carbs: 3 },
|
nutritionPer100g: { kcal: 15, protein: 0.7, fat: 0.1, carbs: 3 },
|
||||||
},
|
},
|
||||||
czosnek: {
|
czosnek: {
|
||||||
@@ -133,6 +138,7 @@ export const INGREDIENTS = {
|
|||||||
name: 'Czosnek',
|
name: 'Czosnek',
|
||||||
category: 'warzywa',
|
category: 'warzywa',
|
||||||
pantryUnit: 'szt',
|
pantryUnit: 'szt',
|
||||||
|
weightPerPiece: 35,
|
||||||
nutritionPer100g: { kcal: 149, protein: 6.4, fat: 0.5, carbs: 33 },
|
nutritionPer100g: { kcal: 149, protein: 6.4, fat: 0.5, carbs: 33 },
|
||||||
},
|
},
|
||||||
kielki_rzodkiewki: {
|
kielki_rzodkiewki: {
|
||||||
@@ -343,7 +349,7 @@ export const RECIPES = {
|
|||||||
image: 'images/recipes/jajecznica.png',
|
image: 'images/recipes/jajecznica.png',
|
||||||
allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'],
|
allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'],
|
||||||
tags: ['szybkie', 'wysokobiałkowe'],
|
tags: ['szybkie', 'wysokobiałkowe'],
|
||||||
nutritionPerServing: { kcal: 548, protein: 36, fat: 28, carbs: 36 },
|
nutritionPerServing: { kcal: 571, protein: 38, fat: 30, carbs: 36 },
|
||||||
ingredients: [
|
ingredients: [
|
||||||
{ ingredientId: 'jajko', amount: 4, unit: 'szt.' },
|
{ ingredientId: 'jajko', amount: 4, unit: 'szt.' },
|
||||||
{ ingredientId: 'oliwa', amount: 5, unit: 'ml' },
|
{ ingredientId: 'oliwa', amount: 5, unit: 'ml' },
|
||||||
@@ -418,7 +424,7 @@ export const RECIPES = {
|
|||||||
image: 'images/recipes/serek_owoc.jpg',
|
image: 'images/recipes/serek_owoc.jpg',
|
||||||
allowedSlots: ['sniadanie', 'drugie_sniadanie', 'przekaska'],
|
allowedSlots: ['sniadanie', 'drugie_sniadanie', 'przekaska'],
|
||||||
tags: ['wegetariańskie', 'wysokobiałkowe', 'szybkie'],
|
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: [
|
ingredients: [
|
||||||
{ ingredientId: 'serek_wiejski', amount: 200, unit: 'g' },
|
{ ingredientId: 'serek_wiejski', amount: 200, unit: 'g' },
|
||||||
{ ingredientId: 'miod', amount: 10, unit: 'g' },
|
{ ingredientId: 'miod', amount: 10, unit: 'g' },
|
||||||
@@ -456,7 +462,11 @@ export function pantryQtyStep(ingredientId) {
|
|||||||
export function nutritionForStock(def, stockQty) {
|
export function nutritionForStock(def, stockQty) {
|
||||||
const n = def.nutritionPer100g;
|
const n = def.nutritionPer100g;
|
||||||
if (!n || !Number.isFinite(stockQty) || stockQty <= 0) return null;
|
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 {
|
return {
|
||||||
kcal: Math.round(n.kcal * f),
|
kcal: Math.round(n.kcal * f),
|
||||||
protein: Math.round(n.protein * f * 10) / 10,
|
protein: Math.round(n.protein * f * 10) / 10,
|
||||||
|
|||||||
@@ -173,10 +173,14 @@ function updateKcalDisplay() {
|
|||||||
document.getElementById('rd-kcal').textContent = `${kcal} kcal`;
|
document.getElementById('rd-kcal').textContent = `${kcal} kcal`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nutritionForAmount(ingredientId, amount) {
|
function nutritionForAmount(ingredientId, amount, unit) {
|
||||||
const def = INGREDIENTS[ingredientId];
|
const def = INGREDIENTS[ingredientId];
|
||||||
if (!def?.nutritionPer100g) return null;
|
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 {
|
return {
|
||||||
kcal: Math.round(def.nutritionPer100g.kcal * f),
|
kcal: Math.round(def.nutritionPer100g.kcal * f),
|
||||||
protein: Math.round(def.nutritionPer100g.protein * f * 10) / 10,
|
protein: Math.round(def.nutritionPer100g.protein * f * 10) / 10,
|
||||||
@@ -214,7 +218,7 @@ function renderIngredients(recipe) {
|
|||||||
const altCards = ing.alternatives.map((altId) => {
|
const altCards = ing.alternatives.map((altId) => {
|
||||||
const altDef = INGREDIENTS[altId];
|
const altDef = INGREDIENTS[altId];
|
||||||
const altName = escapeHtml(altDef?.name || 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">
|
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>
|
<p class="text-[12px] font-medium text-gray-700">${altName}</p>
|
||||||
${renderNutritionLine(nutrition)}
|
${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-[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>
|
<span class="text-[10px] text-gray-400 tabular-nums shrink-0">${displayAmount} ${escapeHtml(ing.unit)}</span>
|
||||||
</div>
|
</div>
|
||||||
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount))}
|
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount, ing.unit))}
|
||||||
</div>
|
</div>
|
||||||
<i class="fas fa-chevron-${isExpanded ? 'up' : 'down'} text-[9px] text-gray-400 shrink-0"></i>
|
<i class="fas fa-chevron-${isExpanded ? 'up' : 'down'} text-[9px] text-gray-400 shrink-0"></i>
|
||||||
</button>`;
|
</button>`;
|
||||||
@@ -510,7 +514,7 @@ export function setupRecipeDetail() {
|
|||||||
const altName = def?.name || altId;
|
const altName = def?.name || altId;
|
||||||
const isSelected = effectiveId === altId;
|
const isSelected = effectiveId === altId;
|
||||||
const isOriginal = altId === origId;
|
const isOriginal = altId === origId;
|
||||||
const nutrition = nutritionForAmount(altId, scaledAmount);
|
const nutrition = nutritionForAmount(altId, scaledAmount, ing.unit);
|
||||||
|
|
||||||
const selectedCls = isSelected
|
const selectedCls = isSelected
|
||||||
? 'border-gray-900 bg-gray-50 ring-1 ring-gray-900'
|
? 'border-gray-900 bg-gray-50 ring-1 ring-gray-900'
|
||||||
|
|||||||
@@ -183,10 +183,14 @@ function updateKcalDisplay() {
|
|||||||
el.textContent = `${kcal} kcal`;
|
el.textContent = `${kcal} kcal`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nutritionForAmount(ingredientId, amount) {
|
function nutritionForAmount(ingredientId, amount, unit) {
|
||||||
const def = INGREDIENTS[ingredientId];
|
const def = INGREDIENTS[ingredientId];
|
||||||
if (!def?.nutritionPer100g) return null;
|
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 {
|
return {
|
||||||
kcal: Math.round(def.nutritionPer100g.kcal * f),
|
kcal: Math.round(def.nutritionPer100g.kcal * f),
|
||||||
protein: Math.round(def.nutritionPer100g.protein * f * 10) / 10,
|
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;
|
let kcal = 0, protein = 0, fat = 0, carbs = 0;
|
||||||
for (const ing of recipe.ingredients) {
|
for (const ing of recipe.ingredients) {
|
||||||
const effectiveId = getEffectiveIngredientId(ing.ingredientId);
|
const effectiveId = getEffectiveIngredientId(ing.ingredientId);
|
||||||
const n = nutritionForAmount(effectiveId, ing.amount * currentServings);
|
const n = nutritionForAmount(effectiveId, ing.amount * currentServings, ing.unit);
|
||||||
if (n) {
|
if (n) {
|
||||||
kcal += n.kcal;
|
kcal += n.kcal;
|
||||||
protein += n.protein;
|
protein += n.protein;
|
||||||
@@ -290,7 +294,7 @@ function renderIngredients(recipe) {
|
|||||||
const effectiveDef = INGREDIENTS[effectiveId];
|
const effectiveDef = INGREDIENTS[effectiveId];
|
||||||
const effectiveName = effectiveDef?.name || effectiveId;
|
const effectiveName = effectiveDef?.name || effectiveId;
|
||||||
const scaledAmount = ing.amount * currentServings;
|
const scaledAmount = ing.amount * currentServings;
|
||||||
const nutrition = nutritionForAmount(effectiveId, scaledAmount);
|
const nutrition = nutritionForAmount(effectiveId, scaledAmount, ing.unit);
|
||||||
const isSwapped = effectiveId !== origId;
|
const isSwapped = effectiveId !== origId;
|
||||||
const isExpanded = expandedAlternatives.has(origId);
|
const isExpanded = expandedAlternatives.has(origId);
|
||||||
|
|
||||||
@@ -314,7 +318,7 @@ function renderIngredients(recipe) {
|
|||||||
const altName = def?.name || altId;
|
const altName = def?.name || altId;
|
||||||
const isSelected = effectiveId === altId;
|
const isSelected = effectiveId === altId;
|
||||||
const isOriginal = altId === origId;
|
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 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>` : '';
|
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-[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>
|
<span class="text-[10px] text-gray-400 tabular-nums shrink-0">${displayAmount} ${escapeHtml(ing.unit)}</span>
|
||||||
</div>
|
</div>
|
||||||
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount))}
|
${renderNutritionLine(nutritionForAmount(effectiveId, scaledAmount, ing.unit))}
|
||||||
</div>
|
</div>
|
||||||
<i class="fas fa-chevron-${isExpanded ? 'up' : 'down'} text-[9px] text-gray-400 shrink-0"></i>
|
<i class="fas fa-chevron-${isExpanded ? 'up' : 'down'} text-[9px] text-gray-400 shrink-0"></i>
|
||||||
</button>`;
|
</button>`;
|
||||||
@@ -598,7 +602,7 @@ export function setupRecipeDetail() {
|
|||||||
const altName = def?.name || altId;
|
const altName = def?.name || altId;
|
||||||
const isSelected = effectiveId === altId;
|
const isSelected = effectiveId === altId;
|
||||||
const isOriginal = altId === origId;
|
const isOriginal = altId === origId;
|
||||||
const nutrition = nutritionForAmount(altId, scaledAmount);
|
const nutrition = nutritionForAmount(altId, scaledAmount, ing.unit);
|
||||||
|
|
||||||
const selectedCls = isSelected
|
const selectedCls = isSelected
|
||||||
? 'border-gray-900 bg-gray-50 ring-1 ring-gray-900'
|
? 'border-gray-900 bg-gray-50 ring-1 ring-gray-900'
|
||||||
|
|||||||
Reference in New Issue
Block a user