/** * 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, image?: string }} IngredientDef * @typedef {{ id: string, ingredientId: string, name: string, brand?: string, packSize: number, packLabel?: string, nutritionPer100g: NutritionPer100, image?: string }} ProductDef */ /** @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 }, }, burrata: { id: 'burrata', name: 'Burrata', category: 'nabial', pantryUnit: 'g', purchasePack: { amount: 150, label: 'opakowanie 150 g' }, nutritionPer100g: { kcal: 280, protein: 16, fat: 22, carbs: 2 }, }, 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 }, }, mleko: { id: 'mleko', name: 'Mleko', category: 'nabial', pantryUnit: 'ml', purchasePack: { amount: 1000, label: 'karton 1 l' }, nutritionPer100g: { kcal: 62, protein: 3.2, fat: 3.5, carbs: 4.7 }, }, skyr: { id: 'skyr', name: 'Skyr naturalny', category: 'nabial', pantryUnit: 'g', purchasePack: { amount: 150, label: 'opakowanie 150 g' }, nutritionPer100g: { kcal: 63, protein: 11, fat: 0.2, 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 }, }, piers_kurczaka: { id: 'piers_kurczaka', name: 'Pierś z kurczaka', category: 'mieso_ryby', pantryUnit: 'g', purchasePack: { amount: 500, label: 'opakowanie ~500 g' }, nutritionPer100g: { kcal: 110, protein: 23, fat: 1.5, 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 }, }, kapusta_biala: { id: 'kapusta_biala', name: 'Kapusta biała', category: 'warzywa', pantryUnit: 'g', purchasePack: { amount: 800, label: 'główka ~800 g' }, nutritionPer100g: { kcal: 25, protein: 1.3, fat: 0.1, carbs: 6 }, }, marchewka: { id: 'marchewka', name: 'Marchewka', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 80, nutritionPer100g: { kcal: 41, protein: 0.9, fat: 0.2, carbs: 10 }, }, papryczka_chilli: { id: 'papryczka_chilli', name: 'Papryczka chilli', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 15, nutritionPer100g: { kcal: 40, protein: 2, fat: 0.4, carbs: 9 }, }, pietruszka_korzen: { id: 'pietruszka_korzen', name: 'Pietruszka (korzeń)', category: 'warzywa', pantryUnit: 'szt', weightPerPiece: 80, nutritionPer100g: { kcal: 54, protein: 2.9, fat: 0.6, carbs: 10 }, }, seler: { id: 'seler', name: 'Seler korzeniowy', category: 'warzywa', pantryUnit: 'g', purchasePack: { amount: 500, label: 'bulwa ~500 g' }, nutritionPer100g: { kcal: 42, protein: 1.5, fat: 0.3, carbs: 9 }, }, cukinia: { id: 'cukinia', name: 'Cukinia', category: 'warzywa', pantryUnit: 'g', purchasePack: { amount: 300, label: 'sztuka ~300 g' }, nutritionPer100g: { kcal: 17, protein: 1.2, fat: 0.3, carbs: 3.1 }, }, ziemniaki: { id: 'ziemniaki', name: 'Ziemniaki', category: 'warzywa', pantryUnit: 'g', nutritionPer100g: { kcal: 77, protein: 2, fat: 0.1, carbs: 17 }, }, imbir: { id: 'imbir', name: 'Imbir', category: 'warzywa', pantryUnit: 'g', nutritionPer100g: { kcal: 80, protein: 1.8, fat: 0.8, carbs: 18 }, }, /* ── 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 }, }, rokitnik_zwyczajny: { id: 'rokitnik_zwyczajny', name: 'Rokitnik zwyczajny - rokitnik pospolity (Hippophae rhamnoides)', category: 'owoce', pantryUnit: 'g', nutritionPer100g: { kcal: 82, protein: 1.2, fat: 5.4, carbs: 5.5 }, }, limonka: { id: 'limonka', name: 'Limonka', category: 'owoce', pantryUnit: 'szt', weightPerPiece: 60, nutritionPer100g: { kcal: 30, protein: 0.7, fat: 0.2, carbs: 11 }, }, /* ── 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 }, }, maka_pszenna: { id: 'maka_pszenna', name: 'Mąka pszenna', category: 'suche', pantryUnit: 'g', purchasePack: { amount: 1000, label: 'torebka 1 kg' }, nutritionPer100g: { kcal: 364, protein: 10, fat: 1, carbs: 76 }, }, sezam: { id: 'sezam', name: 'Sezam', category: 'suche', pantryUnit: 'g', purchasePack: { amount: 100, label: 'opakowanie 100 g' }, nutritionPer100g: { kcal: 573, protein: 18, fat: 50, carbs: 23 }, }, platki_owsiane: { id: 'platki_owsiane', name: 'Płatki owsiane', category: 'suche', pantryUnit: 'g', purchasePack: { amount: 500, label: 'paczka 500 g' }, nutritionPer100g: { kcal: 379, protein: 13, fat: 7, carbs: 68 }, }, /* ── 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 }, }, kolendra_swieza: { id: 'kolendra_swieza', name: 'Kolendra świeża', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 23, protein: 2.1, fat: 0.5, carbs: 3.7 }, }, natka_pietruszki: { id: 'natka_pietruszki', name: 'Natka pietruszki', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 36, protein: 3, fat: 0.8, carbs: 6.3 }, }, majeranek: { id: 'majeranek', name: 'Majeranek suszony', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 271, protein: 12.7, fat: 7, carbs: 60 }, }, cynamon: { id: 'cynamon', name: 'Cynamon', category: 'przyprawy', pantryUnit: 'g', nutritionPer100g: { kcal: 247, protein: 4, fat: 1.2, carbs: 81 }, }, /* ── 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 }, }, olej_rzepakowy: { id: 'olej_rzepakowy', name: 'Olej rzepakowy', category: 'inne', pantryUnit: 'ml', purchasePack: { amount: 1000, label: 'butelka 1 l' }, nutritionPer100g: { kcal: 884, protein: 0, fat: 100, carbs: 0 }, }, erytrol: { id: 'erytrol', name: 'Erytrol', category: 'inne', pantryUnit: 'g', purchasePack: { amount: 500, label: 'opakowanie 500 g' }, nutritionPer100g: { kcal: 0, protein: 0, fat: 0, carbs: 0 }, }, ekstrakt_waniliowy: { id: 'ekstrakt_waniliowy', name: 'Ekstrakt waniliowy', category: 'inne', pantryUnit: 'ml', purchasePack: { amount: 30, label: 'buteleczka 30 ml' }, nutritionPer100g: { kcal: 288, protein: 0.1, fat: 0.1, carbs: 12.7 }, }, maslo_orzechowe: { id: 'maslo_orzechowe', name: 'Masło orzechowe', category: 'inne', pantryUnit: 'g', purchasePack: { amount: 350, label: 'słoik 350 g' }, nutritionPer100g: { kcal: 594, protein: 25, fat: 50, carbs: 20 }, }, sos_sojowy: { id: 'sos_sojowy', name: 'Sos sojowy', category: 'inne', pantryUnit: 'ml', purchasePack: { amount: 150, label: 'butelka 150 ml' }, nutritionPer100g: { kcal: 53, protein: 8, fat: 0, carbs: 5 }, }, syrop_z_agawy: { id: 'syrop_z_agawy', name: 'Syrop z agawy', category: 'inne', pantryUnit: 'g', purchasePack: { amount: 350, label: 'butelka 350 g' }, nutritionPer100g: { kcal: 310, protein: 0, fat: 0, carbs: 76 }, }, czekolada: { id: 'czekolada', name: 'Czekolada gorzka', category: 'inne', pantryUnit: 'g', purchasePack: { amount: 100, label: 'tabliczka 100 g' }, nutritionPer100g: { kcal: 546, protein: 8, fat: 31, carbs: 61 }, }, bulion_warzywny: { id: 'bulion_warzywny', name: 'Bulion warzywny', category: 'inne', pantryUnit: 'ml', nutritionPer100g: { kcal: 6, protein: 0.5, fat: 0.1, carbs: 0.5 }, }, }; for (const [id, def] of Object.entries(INGREDIENTS)) { def.image = `icons/ingredients/${id}.svg`; } /** * @typedef {{ ingredientId: string, amount: number, unit: string, alternatives?: string[] }} RecipeIngredientDef * @typedef {{ id: string, title: string, minutes: number, thumbLabel: string, image?: string, allowedSlots: string[], tags?: string[], nutritionPerServing: NutritionPer100, ingredients: RecipeIngredientDef[], steps: string[] }} RecipeDef */ /** Porcja bazowa = 1; składniki przez ingredientId */ /** @type {Record} */ export const RECIPES = { kanapka_parmenska: { id: 'kanapka_parmenska', title: 'Kanapka z szynką parmeńską i mozzarellą', 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', 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', alternatives: ['burrata'] }, { 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', 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', 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', 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', 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', 'rokitnik_zwyczajny'] }, ], 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!', ], }, dutch_baby: { id: 'dutch_baby', title: 'Dutch baby z owocami', minutes: 45, thumbLabel: 'Dutch baby', image: 'images/recipes/dutch_baby.jpg', allowedSlots: ['sniadanie', 'drugie_sniadanie'], tags: ['wegetariańskie', 'wysokobiałkowe'], nutritionPerServing: { kcal: 910, protein: 57, fat: 37, carbs: 88 }, ingredients: [ { ingredientId: 'maka_pszenna', amount: 60, unit: 'g' }, { ingredientId: 'mleko', amount: 125, unit: 'ml' }, { ingredientId: 'jajko', amount: 2, unit: 'szt.' }, { ingredientId: 'erytrol', amount: 15, unit: 'g' }, { ingredientId: 'olej_rzepakowy', amount: 10, unit: 'ml' }, { ingredientId: 'ekstrakt_waniliowy', amount: 1.5, unit: 'ml' }, { ingredientId: 'skyr', amount: 225, unit: 'g' }, { ingredientId: 'maslo_orzechowe', amount: 20, unit: 'g' }, { ingredientId: 'borowki_amerykanskie', amount: 100, unit: 'g', alternatives: ['jagody'] }, { ingredientId: 'truskawki', amount: 100, unit: 'g' }, ], steps: [ 'Nagrzej piekarnik do 210°C wraz z patelnią żeliwną w środku.', 'Zmiksuj jajka, mąkę, mleko, ekstrakt waniliowy, 10 g erytrolu i szczyptę soli. Odstaw ciasto na 20 minut.', 'Wyjmij nagrzaną patelnię, rozprowadź olej rzepakowy i wlej ciasto.', 'Piecz do zbrązowienia brzegów (ok. 10 minut). Ostudź.', 'Wymieszaj skyr z pozostałym erytrolem (5 g) i masłem orzechowym. Nałóż na naleśnika.', 'Ułóż na wierzchu umyte borówki i truskawki.', ], }, placki_kapusta_losos: { id: 'placki_kapusta_losos', title: 'Placki z białej kapusty z łososiem i słodkim sosem sojowym', minutes: 35, thumbLabel: 'Placki kapuściane', image: 'images/recipes/placki_kapusta_losos.jpg', allowedSlots: ['obiad', 'kolacja'], nutritionPerServing: { kcal: 775, protein: 44, fat: 34, carbs: 80 }, ingredients: [ { ingredientId: 'kapusta_biala', amount: 300, unit: 'g' }, { ingredientId: 'marchewka', amount: 0.5, unit: 'szt.' }, { ingredientId: 'czosnek', amount: 6, unit: 'g' }, { ingredientId: 'papryczka_chilli', amount: 0.5, unit: 'szt.' }, { ingredientId: 'szczypiorek', amount: 15, unit: 'g' }, { ingredientId: 'kolendra_swieza', amount: 4, unit: 'g' }, { ingredientId: 'jajko', amount: 1, unit: 'szt.' }, { ingredientId: 'maka_pszenna', amount: 36, unit: 'g' }, { ingredientId: 'sezam', amount: 20, unit: 'g' }, { ingredientId: 'olej_rzepakowy', amount: 10, unit: 'ml' }, { ingredientId: 'sos_sojowy', amount: 40, unit: 'ml' }, { ingredientId: 'miod', amount: 24, unit: 'g' }, { ingredientId: 'imbir', amount: 3, unit: 'g' }, { ingredientId: 'limonka', amount: 3, unit: 'g' }, { ingredientId: 'losos_wedzony', amount: 100, unit: 'g' }, ], steps: [ 'Kapustę poszatkuj w cienkie paski i dodatkowo posiekaj nożem. Marchewkę zetrzyj na grubej tarce, czosnek na drobnej. Posiekaj szczypiorek i kolendrę, drobno pokrój chili.', 'W rondelku wymieszaj sos sojowy, miód, starty imbir i sok z limonki. Podgrzewaj 5 minut do uzyskania konsystencji syropu.', 'W misce połącz kapustę, marchewkę, czosnek, chili, 10 g szczypiorku, kolendrę, sól, pieprz, paprykę wędzoną i 10 g sezamu. Dodaj jajko i mąkę, dokładnie wymieszaj.', 'Rozgrzej olej rzepakowy na patelni. Nakładaj łyżką porcje ciasta, spłaszczając je w kształt placków. Smaż z każdej strony 3–4 minuty do zrumienienia.', 'Ułóż placki na talerzu, na każdym połóż plastry wędzonego łososia.', 'Polej słodkim sosem sojowym, posyp pozostałym szczypiorkiem (5 g) i sezamem (10 g).', ], }, zupa_jarzynowa: { id: 'zupa_jarzynowa', title: 'Lekkostrawna zupa jarzynowa', minutes: 50, thumbLabel: 'Zupa jarzynowa', image: 'images/recipes/zupa_jarzynowa.jpg', allowedSlots: ['obiad'], tags: ['wysokobiałkowe'], nutritionPerServing: { kcal: 657, protein: 50, fat: 15, carbs: 81 }, ingredients: [ { ingredientId: 'piers_kurczaka', amount: 150, unit: 'g' }, { ingredientId: 'marchewka', amount: 150, unit: 'g' }, { ingredientId: 'pietruszka_korzen', amount: 100, unit: 'g' }, { ingredientId: 'seler', amount: 100, unit: 'g' }, { ingredientId: 'cukinia', amount: 100, unit: 'g' }, { ingredientId: 'ziemniaki', amount: 210, unit: 'g' }, { ingredientId: 'bulion_warzywny', amount: 750, unit: 'ml' }, { ingredientId: 'natka_pietruszki', amount: 10, unit: 'g' }, { ingredientId: 'majeranek', amount: 4, unit: 'g' }, { ingredientId: 'tymianek', amount: 3, unit: 'g' }, { ingredientId: 'oliwa', amount: 10, unit: 'ml' }, ], steps: [ 'Oczyść warzywa. Marchewkę, pietruszkę i seler pokrój w kostkę, ziemniaki w mniejszą kostkę, cukinię w ćwierćplasterki, mięso w większe kostki.', 'Wlej bulion do garnka. Dodaj marchewkę, pietruszkę, seler, ziemniaki i pierś z kurczaka. Doprowadź do wrzenia, wrzuć liść laurowy i ziele angielskie.', 'Zmniejsz ogień i gotuj pod częściowym przykryciem 20 minut.', 'Dodaj cukinię, majeranek i tymianek. Gotuj kolejne 10 minut, aż warzywa będą miękkie.', 'Dopraw solą, białym pieprzem i lubczykiem. Wmieszaj posiekaną natkę pietruszki oraz oliwę.', 'Przykryj i odstaw na 5 minut przed podaniem.', ], }, ciasteczka_owsiane: { id: 'ciasteczka_owsiane', title: 'Ciasteczka owsiane z czekoladą', minutes: 35, thumbLabel: 'Ciasteczka owsiane', image: 'images/recipes/ciasteczka_owsiane.jpg', allowedSlots: ['drugie_sniadanie', 'przekaska'], tags: ['wegetariańskie'], nutritionPerServing: { kcal: 183, protein: 5, fat: 5, carbs: 30 }, ingredients: [ { ingredientId: 'banany', amount: 240, unit: 'g' }, { ingredientId: 'maslo_orzechowe', amount: 30, unit: 'g' }, { ingredientId: 'syrop_z_agawy', amount: 15, unit: 'g', alternatives: ['miod'] }, { ingredientId: 'ekstrakt_waniliowy', amount: 9, unit: 'ml' }, { ingredientId: 'cynamon', amount: 2, unit: 'g' }, { ingredientId: 'platki_owsiane', amount: 220, unit: 'g' }, { ingredientId: 'czekolada', amount: 30, unit: 'g' }, ], steps: [ 'Nagrzej piekarnik do 180°C (góra-dół). Wyłóż blachę papierem do pieczenia.', 'Obierz banany i rozgnieć je widelcem w misce na gładką papkę.', 'Dodaj masło orzechowe, syrop z agawy, ekstrakt waniliowy, cynamon i szczyptę soli. Wymieszaj przez ok. minutę.', 'Wsyp płatki owsiane i mieszaj aż do uzyskania gęstej, lepkiej masy.', 'Posiekaj czekoladę na drobne kawałki, wmieszaj do masy.', 'Formuj kulki wielkości orzecha włoskiego (ok. 8 sztuk) i układaj na blasze, lekko spłaszczając.', 'Piecz ok. 15 minut, aż brzegi się zrumienią. Studź ok. 10 minut przed zdjęciem z blachy.', ], }, }; /* ══════════════════════════════════════════════════════════════════════ * Konkretne produkty — warianty składników generycznych. * Każdy produkt należy do jednego IngredientDef (przez ingredientId). * ══════════════════════════════════════════════════════════════════════ */ /** @type {Record} */ export const PRODUCTS = { /* ── Nabiał ───────────────────────────────────────── */ almette_naturalny: { id: 'almette_naturalny', ingredientId: 'serek_smietankowy', name: 'Almette naturalny', brand: 'Almette', packSize: 150, packLabel: '150 g', nutritionPer100g: { kcal: 234, protein: 5.5, fat: 22, carbs: 3.5 }, }, philadelphia_original: { id: 'philadelphia_original', ingredientId: 'serek_smietankowy', name: 'Philadelphia Original', brand: 'Philadelphia', packSize: 125, packLabel: '125 g', nutritionPer100g: { kcal: 235, protein: 5.4, fat: 21.5, carbs: 4.1 }, }, mozzarella_galbani: { id: 'mozzarella_galbani', ingredientId: 'mozzarella', name: 'Galbani Mozzarella', brand: 'Galbani', packSize: 125, packLabel: '125 g', nutritionPer100g: { kcal: 253, protein: 18.4, fat: 19.1, carbs: 1.6 }, }, ricotta_galbani: { id: 'ricotta_galbani', ingredientId: 'ricotta', name: 'Galbani Ricotta', brand: 'Galbani', packSize: 250, packLabel: '250 g', nutritionPer100g: { kcal: 138, protein: 10, fat: 10, carbs: 3 }, }, serek_wiejski_piatnica: { id: 'serek_wiejski_piatnica', ingredientId: 'serek_wiejski', name: 'Piątnica Serek wiejski', brand: 'Piątnica', packSize: 200, packLabel: '200 g', nutritionPer100g: { kcal: 97, protein: 11, fat: 5, carbs: 2 }, }, serek_wiejski_piatnica_wb: { id: 'serek_wiejski_piatnica_wb', ingredientId: 'serek_wiejski', name: 'Piątnica Serek wiejski wysokobiałkowy', brand: 'Piątnica', packSize: 200, packLabel: '200 g', nutritionPer100g: { kcal: 93, protein: 14, fat: 3, carbs: 2.4 }, }, /* ── Mięso i ryby ─────────────────────────────────── */ burrata_milbona: { id: 'burrata_milbona', ingredientId: 'burrata', name: 'Milbona Burrata', brand: 'Milbona', packSize: 125, packLabel: '125 g', nutritionPer100g: { kcal: 254, protein: 10, fat: 23, carbs: 1.8 }, }, burrata_gustobello: { id: 'burrata_gustobello', ingredientId: 'burrata', name: 'GustoBello Burrata', brand: 'GustoBello', packSize: 100, packLabel: '100 g', nutritionPer100g: { kcal: 259, protein: 10, fat: 23, carbs: 2 }, }, /* ── Mięso i ryby ─────────────────────────────────── */ losos_wedzony_suempol: { id: 'losos_wedzony_suempol', ingredientId: 'losos_wedzony', name: 'Suempol Łosoś atlantycki wędzony', brand: 'Suempol', packSize: 100, packLabel: '100 g', nutritionPer100g: { kcal: 160, protein: 21.5, fat: 8, carbs: 0.5 }, }, /* ── Inne ─────────────────────────────────────────── */ hummus_klasyczny_well_well: { id: 'hummus_klasyczny_well_well', ingredientId: 'hummus', name: 'Well Well Hummus klasyczny', brand: 'Well Well', packSize: 200, packLabel: '200 g', nutritionPer100g: { kcal: 198, protein: 6.6, fat: 12, carbs: 16 }, }, }; /** @param {string} ingredientId @returns {ProductDef[]} */ export function getProductsForIngredient(ingredientId) { return Object.values(PRODUCTS).filter(p => p.ingredientId === ingredientId); } /** @param {string} ingredientId @returns {boolean} */ export function ingredientHasProducts(ingredientId) { return Object.values(PRODUCTS).some(p => p.ingredientId === ingredientId); } /** * 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 }; }