/** * 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 = { /* ── Pieczywo ─────────────────────────────────────── */ bulka_grahamka: { id: 'bulka_grahamka', 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 }, }, /* ── Nabiał ───────────────────────────────────────── */ jajko: { id: 'jajko', name: 'Jajka', category: 'nabial', pantryUnit: 'szt', weightPerPiece: 60, nutritionPer100g: { kcal: 143, protein: 13, fat: 9.5, carbs: 1.1 }, }, mozzarella: { id: 'mozzarella', name: 'Mozzarella', category: 'nabial', pantryUnit: 'g', purchasePack: { amount: 125, label: 'kulka 125 g' }, nutritionPer100g: { kcal: 280, protein: 22, fat: 20, carbs: 2 }, }, ricotta: { id: 'ricotta', name: 'Ricotta', category: 'nabial', pantryUnit: 'g', purchasePack: { amount: 250, label: 'opakowanie 250 g' }, nutritionPer100g: { kcal: 174, protein: 11, fat: 13, carbs: 3 }, }, 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 }, }, serek_smietankowy: { id: 'serek_smietankowy', name: 'Serek śmietankowy', category: 'nabial', pantryUnit: 'g', purchasePack: { amount: 150, label: 'opakowanie 150 g' }, nutritionPer100g: { kcal: 230, protein: 6, fat: 21, carbs: 4 }, }, /* ── Mięso i ryby ─────────────────────────────────── */ szynka_parmenska: { id: 'szynka_parmenska', name: 'Szynka parmeńska', category: 'mieso_ryby', pantryUnit: 'g', purchasePack: { amount: 100, label: 'opakowanie 100 g' }, nutritionPer100g: { kcal: 250, protein: 28, fat: 15, carbs: 0 }, }, szynka_z_kurczaka: { id: 'szynka_z_kurczaka', name: 'Szynka z kurczaka', category: 'mieso_ryby', pantryUnit: 'g', purchasePack: { amount: 100, label: 'opakowanie 100 g' }, nutritionPer100g: { kcal: 105, protein: 19.5, fat: 2, carbs: 1.5 }, }, losos_wedzony: { id: 'losos_wedzony', name: 'Łosoś wędzony', category: 'mieso_ryby', pantryUnit: 'g', purchasePack: { amount: 100, label: 'opakowanie 100 g' }, nutritionPer100g: { kcal: 150, protein: 20, fat: 7, carbs: 0 }, }, /* ── Warzywa ──────────────────────────────────────── */ pomidor: { id: 'pomidor', name: 'Pomidor', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 130, nutritionPer100g: { kcal: 18, protein: 0.9, fat: 0.2, carbs: 3.9 }, }, pomidorki_koktajlowe: { id: 'pomidorki_koktajlowe', name: 'Pomidorki koktajlowe', category: 'warzywa', pantryUnit: 'g', purchasePack: { amount: 250, label: 'opakowanie 250 g' }, nutritionPer100g: { kcal: 18, protein: 0.9, fat: 0.2, carbs: 3.9 }, }, papryka_czerwona: { id: 'papryka_czerwona', name: 'Papryka czerwona', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 160, nutritionPer100g: { kcal: 31, protein: 1, fat: 0.3, carbs: 6 }, }, ogorek: { id: 'ogorek', name: 'Ogórek', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 200, nutritionPer100g: { kcal: 15, protein: 0.7, fat: 0.1, carbs: 3 }, }, czosnek: { id: 'czosnek', name: 'Czosnek', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 35, nutritionPer100g: { kcal: 149, protein: 6.4, fat: 0.5, carbs: 33 }, }, kielki_rzodkiewki: { id: 'kielki_rzodkiewki', name: 'Kiełki rzodkiewki', category: 'warzywa', pantryUnit: 'g', nutritionPer100g: { kcal: 28, protein: 3, fat: 0.6, carbs: 3.5 }, }, /* ── Owoce ────────────────────────────────────────── */ 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 }, }, banany: { id: 'banany', name: 'Banany', category: 'owoce', pantryUnit: 'g', nutritionPer100g: { kcal: 89, protein: 1.1, fat: 0.3, carbs: 23 }, }, jagody: { id: 'jagody', name: 'Jagody', category: 'owoce', pantryUnit: 'g', nutritionPer100g: { kcal: 44, protein: 0.7, fat: 0.4, carbs: 10 }, }, /* ── Suche i kasze ────────────────────────────────── */ makaron_suchy: { id: 'makaron_suchy', name: 'Makaron', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 371, protein: 13, fat: 1.5, carbs: 74 }, }, nasiona_slonecznika: { id: 'nasiona_slonecznika', name: 'Nasiona słonecznika', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 584, protein: 21, fat: 51, carbs: 20 }, }, orzechy_wloskie: { id: 'orzechy_wloskie', name: 'Orzechy włoskie', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 654, protein: 15, fat: 65, carbs: 14 }, }, orzechy_laskowe: { id: 'orzechy_laskowe', name: 'Orzechy laskowe', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 628, protein: 15, fat: 61, carbs: 17 }, }, orzechy_nerkowca: { id: 'orzechy_nerkowca', name: 'Orzechy nerkowca', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 553, protein: 18, fat: 44, carbs: 30 }, }, migdaly: { id: 'migdaly', name: 'Migdały', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 579, protein: 21, fat: 50, carbs: 22 }, }, orzechy_pekan: { id: 'orzechy_pekan', name: 'Orzechy pekan', category: 'suche', pantryUnit: 'g', nutritionPer100g: { kcal: 691, protein: 9, fat: 72, carbs: 14 }, }, /* ── Przyprawy i zioła ────────────────────────────── */ 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 }, }, koper_swiezy: { id: 'koper_swiezy', name: 'Koper', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 43, protein: 3.5, fat: 1.1, carbs: 7 }, }, szczypiorek: { id: 'szczypiorek', name: 'Szczypiorek', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 30, protein: 3.3, fat: 0.7, carbs: 1.8 }, }, tymianek: { id: 'tymianek', name: 'Tymianek suszony', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 276, protein: 9, fat: 7, carbs: 45 }, }, chrzan: { id: 'chrzan', name: 'Chrzan tarty', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 44, protein: 1, fat: 0.5, carbs: 8 }, }, /* ── Inne ─────────────────────────────────────────── */ miod: { id: 'miod', name: 'Miód', category: 'inne', pantryUnit: 'g', nutritionPer100g: { kcal: 304, protein: 0.3, fat: 0, carbs: 82 }, }, oliwa: { id: 'oliwa', name: 'Oliwa z oliwek', category: 'inne', pantryUnit: 'ml', nutritionPer100g: { kcal: 884, protein: 0, fat: 100, carbs: 0 }, }, hummus: { id: 'hummus', name: 'Hummus', category: 'inne', pantryUnit: 'g', purchasePack: { amount: 200, label: 'opakowanie 200 g' }, nutritionPer100g: { kcal: 166, protein: 8, fat: 10, carbs: 14 }, }, }; /** Porcja bazowa = 1; składniki przez ingredientId */ export const RECIPES = { kanapka_parmenska: { id: 'kanapka_parmenska', title: 'Kanapka z szynką parmeńską i mozzarellą', description: 'Bułka grahamka z szynką parmeńską, mozzarellą i pomidorkami — włoskie smaki na szybko.', minutes: 5, thumbLabel: 'Parmeńska', image: 'images/recipes/kanapka_parmenska.jpg', allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'], tags: ['szybkie'], nutritionPerServing: { kcal: 606, protein: 47, fat: 29, carbs: 39 }, ingredients: [ { ingredientId: 'bulka_grahamka', amount: 1, unit: 'szt.' }, { ingredientId: 'szynka_parmenska', amount: 95, unit: 'g' }, { ingredientId: 'mozzarella', amount: 60, unit: 'g' }, { ingredientId: 'pomidorki_koktajlowe', amount: 100, unit: 'g' }, ], steps: [ 'Bułkę grahamkę przekrój na pół.', 'Na bułce ułóż plastry szynki parmeńskiej, na nią pokrojoną mozzarellę.', 'Podawaj z pomidorkami koktajlowymi.', ], }, makaron_ricotta: { id: 'makaron_ricotta', title: 'Makaron z ricottą i pomidorami', description: 'Makaron z sosem z pieczonych pomidorków koktajlowych, ricottą i słonecznikiem.', minutes: 20, thumbLabel: 'Ricotta', image: 'images/recipes/makaron_ricotta.jpg', allowedSlots: ['obiad', 'kolacja'], tags: ['wegetariańskie'], nutritionPerServing: { kcal: 608, protein: 24, fat: 24, carbs: 75 }, ingredients: [ { ingredientId: 'makaron_suchy', amount: 80, unit: 'g' }, { ingredientId: 'pomidorki_koktajlowe', amount: 200, unit: 'g' }, { ingredientId: 'czosnek', amount: 6, unit: 'g' }, { ingredientId: 'tymianek', amount: 1, unit: 'g' }, { ingredientId: 'oliwa', amount: 5, unit: 'ml' }, { ingredientId: 'ricotta', amount: 75, unit: 'g' }, { ingredientId: 'bazylia_swieza', amount: 3, unit: 'g' }, { ingredientId: 'nasiona_slonecznika', amount: 15, unit: 'g' }, ], steps: [ 'Makaron ugotuj wg przepisu na opakowaniu. Po ugotowaniu pozostaw 1–2 łyżki wody.', 'Na patelni rozgrzej oliwę, dodaj przeciśnięty czosnek, przekrojone na pół pomidorki i tymianek — podsmażaj 2–3 minuty.', 'Na patelnię z sosem dodaj ugotowany makaron. Wymieszaj, przypraw solą i pieprzem.', 'Ricottę wymieszaj z 1–2 łyżkami wody z gotowania makaronu. Nałóż na makaron.', 'Posyp bazylią świeżą i nasionami słonecznika.', ], }, jajecznica: { id: 'jajecznica', title: 'Jajecznica z pieczywem', description: 'Klasyczna jajecznica z 4 jajek z bułką grahamką i szczypiorkiem.', minutes: 5, thumbLabel: 'Jajecznica', image: 'images/recipes/jajecznica.png', allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'], tags: ['szybkie', 'wysokobiałkowe'], nutritionPerServing: { kcal: 571, protein: 38, fat: 30, carbs: 36 }, ingredients: [ { ingredientId: 'jajko', amount: 4, unit: 'szt.' }, { ingredientId: 'oliwa', amount: 5, unit: 'ml' }, { ingredientId: 'bulka_grahamka', amount: 1, unit: 'szt.' }, { ingredientId: 'szczypiorek', amount: 5, unit: 'g' }, ], steps: [ 'Na patelni rozgrzej oliwę na małej mocy palnika.', 'Wbij jajka bezpośrednio na patelnię. Smaż bez przykrycia, delikatnie mieszając łopatką, aż żółtka i białka stopniowo się połączą.', 'Dopraw solą i pieprzem. Kontynuuj smażenie, aż jajka się zetną, ale pozostaną lekko kremowe — ok. 3–4 minuty.', 'Posyp posiekanym szczypiorkiem i podawaj z bułką grahamką.', ], }, kanapka_hummus: { id: 'kanapka_hummus', title: 'Kanapka z hummusem, wędliną i warzywami', description: 'Bułka grahamka z hummusem, szynką z kurczaka i świeżymi warzywami.', minutes: 5, thumbLabel: 'Hummus', image: 'images/recipes/kanapka_hummus.png', allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'], tags: ['szybkie', 'wysokobiałkowe'], nutritionPerServing: { kcal: 609, protein: 46, fat: 19, carbs: 66 }, ingredients: [ { ingredientId: 'bulka_grahamka', amount: 1, unit: 'szt.' }, { ingredientId: 'szynka_z_kurczaka', amount: 130, unit: 'g' }, { ingredientId: 'pomidor', amount: 80, unit: 'g' }, { ingredientId: 'papryka_czerwona', amount: 85, unit: 'g' }, { ingredientId: 'ogorek', amount: 75, unit: 'g' }, { ingredientId: 'szczypiorek', amount: 20, unit: 'g' }, { ingredientId: 'hummus', amount: 140, unit: 'g' }, ], steps: [ 'Pomidora i ogórka pokrój w plastry. Paprykę pokrój w paski. Szczypiorek posiekaj.', 'Bułkę grahamkę przekrój i posmaruj hummusem.', 'Na bułce ułóż szynkę z kurczaka, pomidora, paprykę i ogórka.', 'Posyp szczypiorkiem i podawaj.', ], }, kanapka_losos: { id: 'kanapka_losos', title: 'Kanapka z wędzonym łososiem', description: 'Bułka grahamka z łososiem wędzonym, pastą chrzanowo-serową i kiełkami.', minutes: 5, thumbLabel: 'Łosoś', image: 'images/recipes/kanapka_losos.jpg', allowedSlots: ['sniadanie', 'drugie_sniadanie', 'kolacja'], tags: ['szybkie'], nutritionPerServing: { kcal: 443, protein: 30, fat: 18, carbs: 39 }, ingredients: [ { ingredientId: 'bulka_grahamka', amount: 1, unit: 'szt.' }, { ingredientId: 'losos_wedzony', amount: 100, unit: 'g' }, { ingredientId: 'serek_smietankowy', amount: 40, unit: 'g' }, { ingredientId: 'chrzan', amount: 10, unit: 'g' }, { ingredientId: 'koper_swiezy', amount: 5, unit: 'g' }, { ingredientId: 'kielki_rzodkiewki', amount: 5, unit: 'g' }, { ingredientId: 'ogorek', amount: 75, unit: 'g' }, ], steps: [ 'Bułkę grahamkę przekrój i podsmaż na patelni na średnim ogniu przez 2–3 minuty.', 'Chrzan dokładnie wymieszaj z serkiem śmietankowym.', 'Koperek drobno posiekaj. Ogórka pokrój na mniejsze kawałki.', 'Na bułce rozsmaruj pastę chrzanowo-serową. Ułóż łososia, koperek, ogórka i kiełki.', ], }, serek_owoc: { id: 'serek_owoc', title: 'Serek wiejski z orzechami i owocami', description: 'Lekki, pożywny posiłek: serek z orzechami, truskawkami i borówkami.', minutes: 5, thumbLabel: 'Serek', image: 'images/recipes/serek_owoc.jpg', allowedSlots: ['sniadanie', 'drugie_sniadanie', 'przekaska'], tags: ['wegetariańskie', 'wysokobiałkowe', 'szybkie'], nutritionPerServing: { kcal: 629, protein: 31, fat: 43, carbs: 40 }, ingredients: [ { ingredientId: 'serek_wiejski', amount: 200, unit: 'g' }, { ingredientId: 'miod', amount: 10, unit: 'g' }, { ingredientId: 'orzechy_wloskie', amount: 50, unit: 'g', alternatives: ['orzechy_laskowe', 'orzechy_nerkowca', 'migdaly', 'orzechy_pekan'] }, { ingredientId: 'truskawki', amount: 100, unit: 'g', alternatives: ['banany'] }, { ingredientId: 'borowki_amerykanskie', amount: 80, unit: 'g', alternatives: ['jagody'] }, ], steps: [ 'Przełóż serek wiejski do miseczki.', 'Dodaj miód i delikatnie wymieszaj.', 'Orzechy posiekaj na mniejsze kawałki i posyp nimi serek z miodem.', 'Umyj owoce (ew. pokrój na połówki) i ułóż na wierzchu. Gotowe!', ], }, }; /** * 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; 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, 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 }; }