/** * Katalog składników i przepisów — odpowiednik tabel w DB (edycja poza aplikacją). * pantryUnit: jednostka magazynowa / sumowania na liście zakupów (g, ml, szt.). * purchasePack: minimalna „sztuka” ze sklepu w tej samej jednostce co pantryUnit (np. 200 g). * nutritionPer100g — wartości szacunkowe na 100 g (dla płynów: traktuj ml≈g przy wodzie). */ export const CATEGORY_LABELS = { pieczywo: 'Pieczywo', nabial: 'Nabiał', mieso_ryby: 'Mięso i ryby', warzywa: 'Warzywa', owoce: 'Owoce', suche: 'Suche i kasze', przyprawy: 'Przyprawy i zioła', inne: 'Inne', }; /** * @typedef {{ kcal: number, protein: number, fat: number, carbs: number }} NutritionPer100 * @typedef {{ amount: number, label?: string }} PurchasePack * @typedef {{ id: string, name: string, category: keyof typeof CATEGORY_LABELS, pantryUnit: 'g'|'ml'|'szt', purchasePack?: PurchasePack, nutritionPer100g?: NutritionPer100 }} IngredientDef */ /** @type {Record} */ export const INGREDIENTS = { maka_pszenna: { id: 'maka_pszenna', name: 'Mąka pszenna', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 364, protein: 10, fat: 1, carbs: 76 }, }, mleko: { id: 'mleko', name: 'Mleko', category: 'nabial', pantryUnit: 'ml', purchasePack: { amount: 1000, label: 'butelka 1 l' }, nutritionPer100g: { kcal: 42, protein: 3.4, fat: 1, carbs: 5 }, }, jajko: { id: 'jajko', name: 'Jajka', category: 'nabial', pantryUnit: 'szt', nutritionPer100g: { kcal: 143, protein: 13, fat: 9.5, carbs: 1.1 }, }, piers_kurczaka: { id: 'piers_kurczaka', name: 'Pierś z kurczaka', category: 'mieso_ryby', pantryUnit: 'g', nutritionPer100g: { kcal: 165, protein: 31, fat: 3.6, carbs: 0 }, }, mix_salat: { id: 'mix_salat', name: 'Mix sałat', category: 'warzywa', pantryUnit: 'g', nutritionPer100g: { kcal: 20, protein: 1.5, fat: 0.3, carbs: 3 }, }, pomidor: { id: 'pomidor', name: 'Pomidor', category: 'warzywa', pantryUnit: 'szt', nutritionPer100g: { kcal: 18, protein: 0.9, fat: 0.2, carbs: 3.9 }, }, makaron_suchy: { id: 'makaron_suchy', name: 'Makaron', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 371, protein: 13, fat: 1.5, carbs: 74 }, }, pomidory_krojone: { id: 'pomidory_krojone', name: 'Pomidory krojone (puszka)', category: 'warzywa', pantryUnit: 'g', nutritionPer100g: { kcal: 20, protein: 1, fat: 0.2, carbs: 4 }, }, bazylia_swieza: { id: 'bazylia_swieza', name: 'Bazylia świeża', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 23, protein: 3.2, fat: 0.6, carbs: 2.7 }, }, jogurt_naturalny: { id: 'jogurt_naturalny', name: 'Jogurt naturalny', category: 'nabial', pantryUnit: 'g', nutritionPer100g: { kcal: 61, protein: 3.5, fat: 3.3, carbs: 4.7 }, }, mieszanka_jagod: { id: 'mieszanka_jagod', name: 'Mieszanka jagód', category: 'owoce', pantryUnit: 'g', nutritionPer100g: { kcal: 50, protein: 0.7, fat: 0.3, carbs: 12 }, }, miod: { id: 'miod', name: 'Miód', category: 'inne', pantryUnit: 'g', nutritionPer100g: { kcal: 304, protein: 0.3, fat: 0, carbs: 82 }, }, chleb_zakwas: { id: 'chleb_zakwas', name: 'Chleb na zakwasie', category: 'pieczywo', pantryUnit: 'szt', nutritionPer100g: { kcal: 250, protein: 9, fat: 1.5, carbs: 49 }, }, awokado: { id: 'awokado', name: 'Awokado', category: 'warzywa', pantryUnit: 'szt', nutritionPer100g: { kcal: 160, protein: 2, fat: 15, carbs: 9 }, }, cytryna: { id: 'cytryna', name: 'Cytryna', category: 'owoce', pantryUnit: 'szt', nutritionPer100g: { kcal: 29, protein: 1.1, fat: 0.3, carbs: 9 }, }, losos_filet: { id: 'losos_filet', name: 'Filet z łososia', category: 'mieso_ryby', pantryUnit: 'g', nutritionPer100g: { kcal: 208, protein: 20, fat: 13, carbs: 0 }, }, koper_swiezy: { id: 'koper_swiezy', name: 'Koper', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 43, protein: 3.5, fat: 1.1, carbs: 7 }, }, mieso_wol_mielone: { id: 'mieso_wol_mielone', name: 'Mięso mielone wołowe', category: 'mieso_ryby', pantryUnit: 'g', nutritionPer100g: { kcal: 250, protein: 26, fat: 15, carbs: 0 }, }, tortilla_kukurydziana: { id: 'tortilla_kukurydziana', name: 'Tortille kukurydziane', category: 'pieczywo', pantryUnit: 'szt', nutritionPer100g: { kcal: 218, protein: 5.7, fat: 2.9, carbs: 44 }, }, salsa_pomidorowa: { id: 'salsa_pomidorowa', name: 'Salsa pomidorowa', category: 'warzywa', pantryUnit: 'g', nutritionPer100g: { kcal: 36, protein: 1.5, fat: 0.2, carbs: 8 }, }, platki_owsiane: { id: 'platki_owsiane', name: 'Płatki owsiane', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 389, protein: 17, fat: 7, carbs: 66 }, }, serek_wiejski: { id: 'serek_wiejski', name: 'Serek wiejski', category: 'nabial', pantryUnit: 'g', purchasePack: { amount: 200, label: 'opakowanie 200 g' }, nutritionPer100g: { kcal: 97, protein: 11, fat: 5, carbs: 3 }, }, orzechy_wloskie: { id: 'orzechy_wloskie', name: 'Orzechy włoskie', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 654, protein: 15, fat: 65, carbs: 14 }, }, truskawki: { id: 'truskawki', name: 'Truskawki', category: 'owoce', pantryUnit: 'g', nutritionPer100g: { kcal: 32, protein: 0.7, fat: 0.3, carbs: 8 }, }, borowki_amerykanskie: { id: 'borowki_amerykanskie', name: 'Borówki amerykańskie', category: 'owoce', pantryUnit: 'g', nutritionPer100g: { kcal: 57, protein: 0.7, fat: 0.3, carbs: 14 }, }, }; /** Porcja bazowa = 1; składniki przez ingredientId */ export const RECIPES = { placki: { id: 'placki', title: 'Puszyste placki', minutes: 15, thumbLabel: 'Placki', allowedSlots: ['sniadanie', 'drugie_sniadanie'], nutritionPerServing: { kcal: 320, protein: 12, fat: 8, carbs: 48 }, ingredients: [ { ingredientId: 'maka_pszenna', amount: 200, unit: 'g' }, { ingredientId: 'mleko', amount: 250, unit: 'ml' }, { ingredientId: 'jajko', amount: 2, unit: 'szt.' }, ], }, salatka: { id: 'salatka', title: 'Sałatka z kurczakiem', minutes: 20, thumbLabel: 'Sałatka', allowedSlots: ['obiad'], nutritionPerServing: { kcal: 250, protein: 35, fat: 9, carbs: 12 }, ingredients: [ { ingredientId: 'piers_kurczaka', amount: 150, unit: 'g' }, { ingredientId: 'mix_salat', amount: 100, unit: 'g' }, { ingredientId: 'pomidor', amount: 1, unit: 'szt.' }, ], }, makaron: { id: 'makaron', title: 'Makaron z pomidorami i bazylią', minutes: 30, thumbLabel: 'Makaron', allowedSlots: ['obiad', 'kolacja'], nutritionPerServing: { kcal: 450, protein: 14, fat: 12, carbs: 72 }, ingredients: [ { ingredientId: 'makaron_suchy', amount: 120, unit: 'g' }, { ingredientId: 'pomidory_krojone', amount: 400, unit: 'g' }, { ingredientId: 'bazylia_swieza', amount: 10, unit: 'g' }, ], }, koktajl: { id: 'koktajl', title: 'Koktajl owocowy', minutes: 5, thumbLabel: 'Koktajl', allowedSlots: ['przekaska', 'drugie_sniadanie'], nutritionPerServing: { kcal: 180, protein: 8, fat: 3, carbs: 32 }, ingredients: [ { ingredientId: 'jogurt_naturalny', amount: 200, unit: 'g' }, { ingredientId: 'mieszanka_jagod', amount: 150, unit: 'g' }, { ingredientId: 'miod', amount: 15, unit: 'g' }, ], }, tost_awokado: { id: 'tost_awokado', title: 'Tost z awokado', minutes: 10, thumbLabel: 'Tost', allowedSlots: ['sniadanie', 'drugie_sniadanie'], nutritionPerServing: { kcal: 220, protein: 6, fat: 14, carbs: 20 }, ingredients: [ { ingredientId: 'chleb_zakwas', amount: 2, unit: 'kromki' }, { ingredientId: 'awokado', amount: 1, unit: 'szt.' }, { ingredientId: 'cytryna', amount: 0.5, unit: 'szt.' }, ], }, losos: { id: 'losos', title: 'Grillowany łosoś', minutes: 25, thumbLabel: 'Łosoś', allowedSlots: ['kolacja', 'obiad'], nutritionPerServing: { kcal: 380, protein: 38, fat: 22, carbs: 4 }, ingredients: [ { ingredientId: 'losos_filet', amount: 180, unit: 'g' }, { ingredientId: 'cytryna', amount: 0.5, unit: 'szt.' }, { ingredientId: 'koper_swiezy', amount: 5, unit: 'g' }, ], }, tacos: { id: 'tacos', title: 'Tacos z wołowiną', minutes: 20, thumbLabel: 'Tacos', allowedSlots: ['kolacja', 'obiad'], nutritionPerServing: { kcal: 410, protein: 28, fat: 18, carbs: 38 }, ingredients: [ { ingredientId: 'mieso_wol_mielone', amount: 200, unit: 'g' }, { ingredientId: 'tortilla_kukurydziana', amount: 4, unit: 'szt.' }, { ingredientId: 'salsa_pomidorowa', amount: 100, unit: 'g' }, ], }, owsianka: { id: 'owsianka', title: 'Miska owsianki', minutes: 10, thumbLabel: 'Owsianka', allowedSlots: ['sniadanie', 'drugie_sniadanie'], nutritionPerServing: { kcal: 210, protein: 8, fat: 6, carbs: 34 }, ingredients: [ { ingredientId: 'platki_owsiane', amount: 60, unit: 'g' }, { ingredientId: 'mleko', amount: 200, unit: 'ml' }, { ingredientId: 'miod', amount: 20, unit: 'g' }, ], }, serek_owoc: { id: 'serek_owoc', title: 'Serek wiejski z orzechami i owocami', minutes: 5, thumbLabel: 'Serek', allowedSlots: ['sniadanie', 'drugie_sniadanie', 'przekaska'], nutritionPerServing: { kcal: 642, protein: 32, fat: 43, carbs: 41 }, ingredients: [ { ingredientId: 'serek_wiejski', amount: 200, unit: 'g' }, { ingredientId: 'miod', amount: 10, unit: 'g' }, { ingredientId: 'orzechy_wloskie', amount: 50, unit: 'g' }, { ingredientId: 'truskawki', amount: 100, unit: 'g' }, { ingredientId: 'borowki_amerykanskie', amount: 80, unit: 'g' }, ], }, }; /** * Krok +/- w spiżarni: całe opakowanie albo domyślny krok (10 g/ml lub 1 szt.). * @param {string} ingredientId * @returns {number} */ export function pantryQtyStep(ingredientId) { const d = INGREDIENTS[ingredientId]; if (!d) return 10; if (d.purchasePack && Number.isFinite(d.purchasePack.amount) && d.purchasePack.amount > 0) { return d.purchasePack.amount; } return d.pantryUnit === 'szt' ? 1 : 10; } /** * @param {IngredientDef} def * @param {number} stockQty — w pantryUnit */ export function nutritionForStock(def, stockQty) { const n = def.nutritionPer100g; if (!n || !Number.isFinite(stockQty) || stockQty <= 0) return null; const f = stockQty / 100; return { kcal: Math.round(n.kcal * f), protein: Math.round(n.protein * f * 10) / 10, fat: Math.round(n.fat * f * 10) / 10, carbs: Math.round(n.carbs * f * 10) / 10, }; } /** * Pełne opakowania + reszta (np. 450 g / 200 → 2 + 50 g). * @param {IngredientDef} def * @param {number} stockQty * @returns {{ fullPacks: number, remainder: number } | null} */ export function splitStockIntoPacks(def, stockQty) { const size = def.purchasePack?.amount; if (!size || !Number.isFinite(size) || size <= 0 || !Number.isFinite(stockQty)) return null; const fullPacks = Math.floor(stockQty / size); const remainder = Math.round((stockQty - fullPacks * size) * 10) / 10; return { fullPacks, remainder }; }