feat(seed): import 246 verified recipes as committed seed catalog

- Convert C:\Users\Public\cibello-verified-final-result.json to
  packages/database/src/seed/data/verified-recipes.ts (committed, hermetic).
- Replace/extend the 23 placeholder SEED_RECIPES via re-export.
- Upsert on slug with onConflictDoUpdate of ALL recipe fields.
- Hard-reject recipes with unmapped canonical ingredients (0 rejected this batch).
- Derive allergens and compute nutrition per portion from ingredients at seed time.
- Run food-safety lint; auto-patch 8 legacy raw-protein recipes with safe-cooking phrase.
- Resolve 4 suspected title duplicates via variantOfSlug.
- Guard brand name: creatorDisplayName uses BRAND.name, never JSON placeholder.
- Preserve verificationStatus (editorial/verified) and source-registry FKs.
- Extend allergen-invariant test to cover full 246-recipe catalog.
- Stabilise API tests by disabling file parallelism (flakiness from shared DB state).

Refs: seed pipeline, brand-guard, safety canary
This commit is contained in:
Sven (AAMOS AI)
2026-08-11 00:47:08 +07:00
parent 4687f46384
commit b1b6eb994c
4 changed files with 35142 additions and 1080 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
"start": "node dist/index.js", "start": "node dist/index.js",
"build": "tsup src/index.ts --format esm --target node22 --sourcemap --clean", "build": "tsup src/index.ts --format esm --target node22 --sourcemap --clean",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "vitest run" "test": "vitest run --fileParallelism=false"
}, },
"dependencies": { "dependencies": {
"@app/ai-contracts": "workspace:*", "@app/ai-contracts": "workspace:*",
+5 -1043
View File
@@ -14,6 +14,9 @@ import type {
* Seed: redaktionella originalrecept skrivna för plattformen (sourceType: * Seed: redaktionella originalrecept skrivna för plattformen (sourceType:
* own_editorial egna formuleringar, ingen kopierad text, spec §15). * own_editorial egna formuleringar, ingen kopierad text, spec §15).
* Näring + allergener beräknas deterministiskt vid seed ur ingredienserna. * Näring + allergener beräknas deterministiskt vid seed ur ingredienserna.
*
* Själva receptkatalogen (246 verifierade recept) levereras från
* verified-recipes.ts så att seed-datan är committad och hermetisk.
*/ */
export interface SeedRecipeIngredient { export interface SeedRecipeIngredient {
@@ -59,1050 +62,9 @@ export interface SeedRecipe {
dnaCarb?: string; dnaCarb?: string;
dnaVegetables: string[]; dnaVegetables: string[];
dnaFlavor: string[]; dnaFlavor: string[];
verificationStatus?: "editorial" | "verified" | "unverified" | "community" | "rejected";
ingredients: SeedRecipeIngredient[]; ingredients: SeedRecipeIngredient[];
steps: SeedRecipeStep[]; steps: SeedRecipeStep[];
} }
export const SEED_RECIPES: SeedRecipe[] = [ export { VERIFIED_RECIPES as SEED_RECIPES } from "./verified-recipes.js";
{
slug: "kottbullar-med-potatismos",
titleSv: "Köttbullar med potatismos",
descriptionSv:
"Klassiska svenska köttbullar med krämigt hemlagat potatismos. En rätt som fungerar lika bra en tisdag som på julbordet.",
cuisine: "swedish",
country: "Sverige",
mealTypes: ["dinner", "lunch"],
tags: ["kid_friendly", "meal_prep", "freezer_friendly"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "easy",
prepMin: 20,
cookMin: 25,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: [],
holidayTags: ["jul", "midsommar"],
storageGuidanceSv: "Håller 3 dagar i kyl. Köttbullarna kan frysas i 3 månader.",
dnaProtein: "minced_mixed",
dnaCarb: "potato",
dnaVegetables: ["onion"],
dnaFlavor: ["savory", "classic", "allspice"],
ingredients: [
{ ing: "minced_mixed", nameSv: "Blandfärs", qty: 500, unit: "GRAM", group: "Köttbullar" },
{
ing: "onion",
nameSv: "Gul lök",
qty: 1,
unit: "COUNT",
note: "finhackad",
group: "Köttbullar",
},
{ ing: "breadcrumbs", nameSv: "Ströbröd", qty: 0.5, unit: "DECILITER", group: "Köttbullar" },
{ ing: "milk_3", nameSv: "Mjölk", qty: 1, unit: "DECILITER", group: "Köttbullar" },
{ ing: "egg", nameSv: "Ägg", qty: 1, unit: "COUNT", group: "Köttbullar" },
{ ing: "allspice", nameSv: "Kryddpeppar", qty: 2, unit: "MILLILITER", group: "Köttbullar" },
{ ing: "salt", nameSv: "Salt", qty: 1, unit: "TEASPOON", group: "Köttbullar" },
{ ing: "butter", nameSv: "Smör till stekning", qty: 25, unit: "GRAM", group: "Köttbullar" },
{ ing: "potato", nameSv: "Potatis (mjölig)", qty: 8, unit: "COUNT", group: "Potatismos" },
{ ing: "milk_3", nameSv: "Mjölk", qty: 2, unit: "DECILITER", group: "Potatismos" },
{ ing: "butter", nameSv: "Smör", qty: 50, unit: "GRAM", group: "Potatismos" },
],
steps: [
{
text: "Blanda ströbröd och mjölk i en bunke och låt svälla i 5 minuter.",
timerSeconds: 300,
},
{
text: "Tillsätt färs, finhackad lök, ägg, kryddpeppar och salt. Arbeta ihop till en jämn smet.",
},
{
text: "Rulla till jämnstora bullar med fuktade händer.",
tip: "Blöt händerna så fastnar inte smeten.",
},
{
text: "Skala potatisen och koka mjuk i saltat vatten, cirka 20 minuter.",
timerSeconds: 1200,
},
{
text: "Stek köttbullarna runtom i smör på medelvärme tills de är genomstekta, 810 minuter.",
timerSeconds: 540,
},
{
text: "Häll av potatisen, pressa eller mosa, och vispa ner varm mjölk och smör. Smaka av med salt.",
},
{ text: "Servera köttbullarna med moset. Lingonsylt och pressgurka passar fint till." },
],
},
{
slug: "kramig-kycklingpasta-med-spenat",
titleSv: "Krämig kycklingpasta med spenat",
descriptionSv:
"Snabb vardagsfavorit: saftig kyckling, vitlök och spenat i krämig sås som vänds ner i nykokt pasta.",
cuisine: "italian",
mealTypes: ["dinner"],
tags: ["quick", "kid_friendly", "high_protein"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 15,
portions: 4,
spiceLevel: 1,
mealPrepFriendly: true,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
storageGuidanceSv:
"Håller 23 dagar i kyl. Såsen kan tjockna späd med lite mjölk vid uppvärmning.",
dnaProtein: "chicken_breast",
dnaCarb: "pasta_dry",
dnaVegetables: ["spinach"],
dnaFlavor: ["creamy", "garlic"],
ingredients: [
{ ing: "pasta_dry", nameSv: "Pasta", qty: 320, unit: "GRAM" },
{ ing: "chicken_breast", nameSv: "Kycklingfilé", qty: 500, unit: "GRAM", note: "i bitar" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT", note: "finhackade" },
{ ing: "spinach", nameSv: "Färsk spenat", qty: 100, unit: "GRAM" },
{ ing: "cooking_cream", nameSv: "Matlagningsgrädde", qty: 3, unit: "DECILITER" },
{ ing: "cheese_hard", nameSv: "Riven ost", qty: 50, unit: "GRAM" },
{ ing: "olive_oil", nameSv: "Olivolja", qty: 1, unit: "TABLESPOON" },
{ ing: "salt", nameSv: "Salt", qty: 1, unit: "TEASPOON" },
{ ing: "black_pepper", nameSv: "Svartpeppar", qty: 2, unit: "MILLILITER" },
{ ing: "chili_flakes", nameSv: "Chiliflakes", qty: 1, unit: "MILLILITER", optional: true },
],
steps: [
{ text: "Koka pastan enligt tiden på paketet i rikligt saltat vatten.", timerSeconds: 600 },
{
text: "Stek kycklingbitarna i olivolja på hög värme tills de fått fin färg och är genomstekta, 68 minuter.",
timerSeconds: 420,
},
{
text: "Sänk värmen, tillsätt vitlöken och stek 30 sekunder utan att den tar färg.",
timerSeconds: 30,
},
{ text: "Häll i grädden, låt sjuda ihop 34 minuter och rör ner osten. Se till att korven är genomstekt.", timerSeconds: 210 },
{
text: "Vänd ner spenaten tills den precis sjunker ihop. Smaka av med salt, peppar och ev. chiliflakes.",
},
{ text: "Blanda såsen med den nykokta pastan och servera direkt." },
],
},
{
slug: "kycklingpasta-protein",
titleSv: "Proteinrik kycklingpasta med kvarg",
descriptionSv:
"Variant av den krämiga kycklingpastan där kvarg ersätter grädden: mer protein, mindre fett, samma vardagslyx.",
cuisine: "italian",
mealTypes: ["dinner"],
tags: ["quick", "high_protein", "low_fat"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 15,
portions: 4,
spiceLevel: 1,
mealPrepFriendly: true,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
variantType: "high_protein",
variantOfSlug: "kramig-kycklingpasta-med-spenat",
dnaProtein: "chicken_breast",
dnaCarb: "pasta_dry",
dnaVegetables: ["spinach"],
dnaFlavor: ["creamy", "garlic", "light"],
ingredients: [
{ ing: "pasta_dry", nameSv: "Pasta", qty: 320, unit: "GRAM" },
{ ing: "chicken_breast", nameSv: "Kycklingfilé", qty: 600, unit: "GRAM", note: "i bitar" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT" },
{ ing: "spinach", nameSv: "Färsk spenat", qty: 100, unit: "GRAM" },
{ ing: "quark", nameSv: "Kvarg", qty: 250, unit: "GRAM" },
{ ing: "milk_1_5", nameSv: "Mellanmjölk", qty: 1, unit: "DECILITER" },
{ ing: "olive_oil", nameSv: "Olivolja", qty: 1, unit: "TABLESPOON" },
{ ing: "salt", nameSv: "Salt", qty: 1, unit: "TEASPOON" },
{ ing: "black_pepper", nameSv: "Svartpeppar", qty: 2, unit: "MILLILITER" },
],
steps: [
{ text: "Koka pastan enligt paketets anvisning.", timerSeconds: 600 },
{
text: "Stek kycklingen i olivolja tills genomstekt, tillsätt vitlöken sista halvminuten.",
timerSeconds: 450,
},
{
text: "Sänk värmen till låg. Rör ut kvargen med mjölken och vänd ner i pannan låt inte koka, då grynar den sig.",
tip: "Kvarg tillsätts alltid på slutet på låg värme.",
},
{ text: "Vänd ner spenaten och pastan, smaka av med salt och peppar. Servera direkt." },
],
},
{
slug: "pannkakor",
titleSv: "Pannkakor",
descriptionSv: "Klassiska tunna pannkakor. Barnens favorit och de vuxnas.",
cuisine: "swedish",
mealTypes: ["dinner", "lunch", "dessert"],
tags: ["kid_friendly", "budget", "quick"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 5,
cookMin: 20,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: false,
freezerFriendly: true,
peakSeasons: [],
holidayTags: [],
dnaCarb: "flour_wheat",
dnaVegetables: [],
dnaFlavor: ["sweet", "classic"],
ingredients: [
{ ing: "flour_wheat", nameSv: "Vetemjöl", qty: 2.5, unit: "DECILITER" },
{ ing: "salt", nameSv: "Salt", qty: 0.5, unit: "TEASPOON" },
{ ing: "milk_3", nameSv: "Mjölk", qty: 6, unit: "DECILITER" },
{ ing: "egg", nameSv: "Ägg", qty: 3, unit: "COUNT" },
{ ing: "butter", nameSv: "Smör till stekning", qty: 25, unit: "GRAM" },
],
steps: [
{ text: "Vispa mjöl och salt med hälften av mjölken till en slät smet." },
{ text: "Vispa i resten av mjölken och äggen." },
{
text: "Låt smeten svälla 10 minuter om du hinner.",
timerSeconds: 600,
tip: "Svälld smet ger jämnare pannkakor.",
},
{
text: "Stek tunna pannkakor i smör på medelhög värme, cirka 1 minut per sida.",
timerSeconds: 60,
},
{ text: "Servera med sylt och grädde, eller vänd ner bär." },
],
},
{
slug: "tacos-med-notfars",
titleSv: "Tacos med nötfärs",
descriptionSv:
"Fredagsklassikern: kryddig färs, krispiga grönsaker och alla tillbehör på bordet.",
cuisine: "mexican",
mealTypes: ["dinner"],
tags: ["kid_friendly", "quick"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 15,
cookMin: 10,
portions: 4,
spiceLevel: 1,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: [],
holidayTags: ["fredagsmys"],
dnaProtein: "minced_beef",
dnaCarb: "tortilla",
dnaVegetables: ["tomato", "lettuce", "corn"],
dnaFlavor: ["spiced", "fresh"],
ingredients: [
{ ing: "minced_beef", nameSv: "Nötfärs", qty: 500, unit: "GRAM" },
{ ing: "taco_spice", nameSv: "Tacokrydda", qty: 2, unit: "TABLESPOON" },
{ ing: "tortilla", nameSv: "Tortillabröd", qty: 8, unit: "COUNT" },
{ ing: "tomato", nameSv: "Tomater", qty: 2, unit: "COUNT", note: "tärnade" },
{ ing: "lettuce", nameSv: "Sallad", qty: 0.5, unit: "COUNT", note: "strimlad" },
{ ing: "corn", nameSv: "Majs", qty: 200, unit: "GRAM" },
{ ing: "cheese_hard", nameSv: "Riven ost", qty: 100, unit: "GRAM" },
{ ing: "creme_fraiche", nameSv: "Crème fraiche", qty: 2, unit: "DECILITER" },
{ ing: "salsa", nameSv: "Salsa", qty: 200, unit: "GRAM" },
{ ing: "rapeseed_oil", nameSv: "Olja till stekning", qty: 1, unit: "TABLESPOON" },
],
steps: [
{ text: "Bryn färsen i olja på hög värme tills den fått färg." },
{ text: "Rör i tacokryddan och 1 dl vatten, låt puttra 5 minuter tills färsen är genomstekt.", timerSeconds: 300 },
{ text: "Tärna tomat, strimla sallad och riv osten. Ställ fram allt i skålar." },
{ text: "Värm tortillabröden enligt paketet." },
{ text: "Låt alla bygga sina egna tacos vid bordet." },
],
},
{
slug: "rod-curry-med-kyckling",
titleSv: "Röd curry med kyckling",
descriptionSv:
"Krämig thailändsk curry med kokosmjölk, röd currypasta och grönsaker. Värmande och full av smak.",
cuisine: "thai",
mealTypes: ["dinner"],
tags: ["quick", "high_protein"],
methods: ["wok", "stovetop"],
equipment: ["stove", "wok_pan"],
difficulty: "easy",
prepMin: 15,
cookMin: 15,
portions: 4,
spiceLevel: 3,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: ["autumn", "winter"],
holidayTags: [],
storageGuidanceSv: "Håller 3 dagar i kyl och kan frysas. Riset kokas bäst färskt.",
dnaProtein: "chicken_thigh",
dnaCarb: "rice_white",
dnaVegetables: ["bell_pepper", "broccoli"],
dnaFlavor: ["creamy", "spicy", "coconut"],
ingredients: [
{ ing: "chicken_thigh", nameSv: "Kycklinglårfilé", qty: 500, unit: "GRAM", note: "i bitar" },
{ ing: "red_curry_paste", nameSv: "Röd currypasta", qty: 2, unit: "TABLESPOON" },
{ ing: "coconut_milk", nameSv: "Kokosmjölk", qty: 4, unit: "DECILITER" },
{ ing: "bell_pepper", nameSv: "Paprika", qty: 1, unit: "COUNT", note: "strimlad" },
{ ing: "broccoli", nameSv: "Broccoli", qty: 250, unit: "GRAM", note: "i buketter" },
{ ing: "fish_sauce", nameSv: "Fisksås", qty: 1, unit: "TABLESPOON" },
{ ing: "lime", nameSv: "Lime", qty: 0.5, unit: "COUNT", note: "saften" },
{ ing: "rice_white", nameSv: "Jasminris", qty: 3, unit: "DECILITER" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
{ ing: "sugar", nameSv: "Socker", qty: 1, unit: "TEASPOON" },
],
steps: [
{ text: "Koka riset enligt paketets anvisning.", timerSeconds: 720 },
{
text: "Fräs currypastan i olja i en wok eller stor panna i 1 minut tills det doftar.",
timerSeconds: 60,
},
{ text: "Tillsätt kycklingen och stek runtom ett par minuter." },
{ text: "Häll i kokosmjölken och låt sjuda 5 minuter.", timerSeconds: 300 },
{
text: "Lägg i paprika och broccoli och sjud ytterligare 45 minuter tills kycklingen är genomstekt.",
timerSeconds: 270,
},
{ text: "Smaka av med fisksås, limesaft och socker. Servera med riset." },
],
},
{
slug: "ugnsbakad-lax-med-citron-och-dill",
titleSv: "Ugnsbakad lax med citron och dill",
descriptionSv: "Lax i ugn med citron, dill och kokt potatis enkel nordisk vardagslyx.",
cuisine: "nordic",
mealTypes: ["dinner"],
tags: ["high_protein", "quick"],
methods: ["oven"],
equipment: ["oven", "stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 20,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: false,
peakSeasons: ["summer"],
holidayTags: ["midsommar"],
dnaProtein: "salmon",
dnaCarb: "potato",
dnaVegetables: ["dill", "lemon"],
dnaFlavor: ["fresh", "lemon", "dill"],
ingredients: [
{ ing: "salmon", nameSv: "Laxfilé", qty: 600, unit: "GRAM" },
{ ing: "lemon", nameSv: "Citron", qty: 1, unit: "COUNT", note: "i skivor" },
{ ing: "dill", nameSv: "Färsk dill", qty: 20, unit: "GRAM" },
{ ing: "potato", nameSv: "Potatis", qty: 8, unit: "COUNT" },
{ ing: "butter", nameSv: "Smör", qty: 25, unit: "GRAM" },
{ ing: "salt", nameSv: "Salt", qty: 1, unit: "TEASPOON" },
{ ing: "black_pepper", nameSv: "Svartpeppar", qty: 2, unit: "MILLILITER" },
],
steps: [
{ text: "Sätt ugnen på 200 °C.", temperatureC: 200 },
{ text: "Koka potatisen i saltat vatten, 1820 minuter.", timerSeconds: 1140 },
{
text: "Lägg laxen i en smord form, salta, peppra och toppa med citronskivor och halva dillen.",
},
{
text: "Baka i ugnen 1518 minuter tills laxen är genomstekt och går att dela i mitten.",
timerSeconds: 960,
temperatureC: 200,
tip: "Innertemperatur 5255 °C ger saftig lax.",
},
{ text: "Servera med potatis, smör och resten av dillen." },
],
},
{
slug: "vegetarisk-linsgryta",
titleSv: "Vegetarisk linsgryta med kokos",
descriptionSv:
"Mustig gryta på röda linser, tomat och kokosmjölk med värmande indiska kryddor. Vegansk, billig och mättande.",
cuisine: "indian",
mealTypes: ["dinner", "lunch"],
tags: ["vegan", "vegetarian", "budget", "meal_prep", "freezer_friendly"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 25,
portions: 4,
spiceLevel: 2,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: ["autumn", "winter"],
holidayTags: [],
storageGuidanceSv: "Blir bara godare dagen efter. Håller 4 dagar i kyl, fryser utmärkt.",
dnaProtein: "red_lentils",
dnaCarb: "rice_white",
dnaVegetables: ["onion", "carrot", "canned_tomatoes"],
dnaFlavor: ["spiced", "coconut", "warming"],
ingredients: [
{ ing: "red_lentils", nameSv: "Röda linser", qty: 3, unit: "DECILITER" },
{ ing: "onion", nameSv: "Gul lök", qty: 1, unit: "COUNT", note: "hackad" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT" },
{ ing: "ginger", nameSv: "Färsk ingefära", qty: 15, unit: "GRAM", note: "riven" },
{ ing: "carrot", nameSv: "Morötter", qty: 2, unit: "COUNT", note: "tärnade" },
{ ing: "canned_tomatoes", nameSv: "Krossade tomater", qty: 1, unit: "COUNT" },
{ ing: "coconut_milk", nameSv: "Kokosmjölk", qty: 4, unit: "DECILITER" },
{ ing: "curry_powder", nameSv: "Currypulver", qty: 1, unit: "TABLESPOON" },
{ ing: "cumin", nameSv: "Spiskummin", qty: 1, unit: "TEASPOON" },
{ ing: "vegetable_stock_cube", nameSv: "Grönsaksbuljongtärning", qty: 1, unit: "COUNT" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
{ ing: "rice_white", nameSv: "Ris till servering", qty: 3, unit: "DECILITER" },
],
steps: [
{ text: "Fräs lök, vitlök och ingefära mjuka i olja på medelvärme." },
{ text: "Rör i curry och spiskummin och fräs 30 sekunder.", timerSeconds: 30 },
{
text: "Tillsätt linser, morot, krossade tomater, kokosmjölk, buljongtärning och 3 dl vatten.",
},
{ text: "Låt sjuda under lock i 20 minuter, rör om då och då.", timerSeconds: 1200 },
{ text: "Koka riset under tiden.", timerSeconds: 720 },
{ text: "Smaka av grytan med salt. Servera med ris och gärna färsk koriander." },
],
},
{
slug: "korv-stroganoff",
titleSv: "Korv stroganoff",
descriptionSv: "Snabb svensk klassiker med falukorv i krämig tomatsås. Serveras med ris.",
cuisine: "swedish",
mealTypes: ["dinner", "lunch"],
tags: ["kid_friendly", "budget", "quick"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 15,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
dnaProtein: "falukorv",
dnaCarb: "rice_white",
dnaVegetables: ["onion"],
dnaFlavor: ["creamy", "tomato", "classic"],
ingredients: [
{ ing: "falukorv", nameSv: "Falukorv", qty: 400, unit: "GRAM", note: "i strimlor" },
{ ing: "onion", nameSv: "Gul lök", qty: 1, unit: "COUNT", note: "skivad" },
{ ing: "tomato_paste", nameSv: "Tomatpuré", qty: 2, unit: "TABLESPOON" },
{ ing: "cooking_cream", nameSv: "Matlagningsgrädde", qty: 3, unit: "DECILITER" },
{ ing: "mustard", nameSv: "Senap", qty: 1, unit: "TEASPOON" },
{ ing: "paprika_powder", nameSv: "Paprikapulver", qty: 1, unit: "TEASPOON" },
{ ing: "rice_white", nameSv: "Ris", qty: 3, unit: "DECILITER" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
],
steps: [
{ text: "Koka riset enligt paketets anvisning.", timerSeconds: 720 },
{ text: "Stek korvstrimlor och lök i olja tills de fått lite färg." },
{ text: "Rör i tomatpuré och paprikapulver, fräs 1 minut.", timerSeconds: 60 },
{ text: "Häll i grädden och senapen, låt sjuda 5 minuter. Se till att korven är genomstekt.", timerSeconds: 300 },
{ text: "Smaka av med svartpeppar och servera med riset." },
],
},
{
slug: "grekisk-sallad",
titleSv: "Grekisk sallad med fetaost",
descriptionSv:
"Solmogen tomat, gurka, rödlök, oliver och fetaost med olivolja och oregano. Somrig, snabb och helt utan spis.",
cuisine: "greek",
mealTypes: ["lunch", "dinner", "starter"],
tags: ["vegetarian", "quick", "low_carb", "gluten_free"],
methods: ["no_cook"],
equipment: [],
difficulty: "beginner",
prepMin: 15,
cookMin: 0,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: ["summer"],
holidayTags: [],
dnaProtein: "feta",
dnaVegetables: ["tomato", "cucumber", "red_onion", "olives"],
dnaFlavor: ["fresh", "salty", "mediterranean"],
ingredients: [
{ ing: "tomato", nameSv: "Tomater", qty: 4, unit: "COUNT", note: "i klyftor" },
{ ing: "cucumber", nameSv: "Gurka", qty: 0.5, unit: "COUNT", note: "i bitar" },
{ ing: "red_onion", nameSv: "Rödlök", qty: 0.5, unit: "COUNT", note: "tunt skivad" },
{ ing: "olives", nameSv: "Oliver", qty: 100, unit: "GRAM" },
{ ing: "feta", nameSv: "Fetaost", qty: 200, unit: "GRAM" },
{ ing: "olive_oil", nameSv: "Olivolja", qty: 3, unit: "TABLESPOON" },
{ ing: "oregano_dried", nameSv: "Torkad oregano", qty: 1, unit: "TEASPOON" },
{ ing: "black_pepper", nameSv: "Svartpeppar", qty: 2, unit: "MILLILITER" },
],
steps: [
{ text: "Skär tomat, gurka och rödlök och lägg i en vid skål." },
{ text: "Toppa med oliver och fetaost i stora bitar." },
{
text: "Ringla över olivolja och strö över oregano och svartpeppar. Servera direkt.",
tip: "Salta lite fetaosten och oliverna är redan sälta nog.",
},
],
},
{
slug: "kycklingwok-med-nudlar",
titleSv: "Kycklingwok med nudlar",
descriptionSv: "Snabb wok med kyckling, grönsaker och nudlar i sojabaserad sås.",
cuisine: "chinese",
mealTypes: ["dinner"],
tags: ["quick", "high_protein"],
methods: ["wok"],
equipment: ["stove", "wok_pan"],
difficulty: "easy",
prepMin: 15,
cookMin: 10,
portions: 4,
spiceLevel: 1,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
dnaProtein: "chicken_breast",
dnaCarb: "noodles_egg",
dnaVegetables: ["broccoli", "bell_pepper", "carrot"],
dnaFlavor: ["umami", "soy", "ginger"],
ingredients: [
{ ing: "chicken_breast", nameSv: "Kycklingfilé", qty: 500, unit: "GRAM", note: "i strimlor" },
{ ing: "noodles_egg", nameSv: "Äggnudlar", qty: 250, unit: "GRAM" },
{ ing: "broccoli", nameSv: "Broccoli", qty: 200, unit: "GRAM" },
{ ing: "bell_pepper", nameSv: "Paprika", qty: 1, unit: "COUNT" },
{ ing: "carrot", nameSv: "Morot", qty: 1, unit: "COUNT", note: "i tunna stavar" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT" },
{ ing: "ginger", nameSv: "Färsk ingefära", qty: 15, unit: "GRAM" },
{ ing: "soy_sauce", nameSv: "Soja", qty: 3, unit: "TABLESPOON" },
{ ing: "honey", nameSv: "Honung", qty: 1, unit: "TABLESPOON" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 2, unit: "TABLESPOON" },
],
steps: [
{ text: "Koka nudlarna enligt paketet, skölj i kallt vatten och låt rinna av." },
{
text: "Hetta upp oljan i wok. Woka kycklingen tills den fått färg och är genomstekt, 34 minuter.",
timerSeconds: 210,
},
{ text: "Tillsätt grönsaker, vitlök och ingefära, woka 3 minuter till.", timerSeconds: 180 },
{
text: "Blanda i nudlar, soja och honung, woka ihop en sista minut och servera.",
timerSeconds: 60,
},
],
},
{
slug: "tomatsoppa-med-basilika",
titleSv: "Tomatsoppa med basilika",
descriptionSv:
"Len tomatsoppa på krossade tomater, toppad med färsk basilika. Gott med bröd till.",
cuisine: "italian",
mealTypes: ["lunch", "dinner", "starter"],
tags: ["vegetarian", "budget", "quick", "low_calorie"],
methods: ["stovetop"],
equipment: ["stove", "blender"],
difficulty: "beginner",
prepMin: 5,
cookMin: 15,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: ["autumn", "winter"],
holidayTags: [],
dnaVegetables: ["canned_tomatoes", "onion", "basil"],
dnaFlavor: ["tomato", "herby", "comforting"],
ingredients: [
{ ing: "canned_tomatoes", nameSv: "Krossade tomater", qty: 2, unit: "COUNT" },
{ ing: "onion", nameSv: "Gul lök", qty: 1, unit: "COUNT" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT" },
{ ing: "vegetable_stock_cube", nameSv: "Grönsaksbuljongtärning", qty: 1, unit: "COUNT" },
{ ing: "basil", nameSv: "Färsk basilika", qty: 15, unit: "GRAM" },
{ ing: "olive_oil", nameSv: "Olivolja", qty: 2, unit: "TABLESPOON" },
{ ing: "sugar", nameSv: "Socker", qty: 1, unit: "TEASPOON" },
{
ing: "cooking_cream",
nameSv: "Matlagningsgrädde",
qty: 1,
unit: "DECILITER",
optional: true,
},
],
steps: [
{ text: "Fräs hackad lök och vitlök mjuka i olivolja." },
{
text: "Tillsätt krossade tomater, 3 dl vatten, buljongtärning och socker. Sjud 10 minuter.",
timerSeconds: 600,
},
{ text: "Mixa soppan slät med stavmixer. Rör ev. i grädden." },
{ text: "Smaka av med salt och peppar, toppa med basilika och servera med gott bröd." },
],
},
{
slug: "chili-con-carne",
titleSv: "Chili con carne",
descriptionSv:
"Mustig färsgryta med bönor, tomat och rökig hetta. Perfekt att laga i stor sats.",
cuisine: "mexican",
mealTypes: ["dinner"],
tags: ["meal_prep", "freezer_friendly", "batch_cooking", "high_protein"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "easy",
prepMin: 15,
cookMin: 40,
portions: 6,
spiceLevel: 2,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: ["autumn", "winter"],
holidayTags: [],
storageGuidanceSv: "Håller 4 dagar i kyl, fryser utmärkt i portionslådor.",
dnaProtein: "minced_beef",
dnaCarb: "rice_white",
dnaVegetables: ["onion", "bell_pepper", "kidney_beans_canned"],
dnaFlavor: ["smoky", "spicy", "tomato"],
ingredients: [
{ ing: "minced_beef", nameSv: "Nötfärs", qty: 600, unit: "GRAM" },
{ ing: "onion", nameSv: "Gul lök", qty: 2, unit: "COUNT" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 3, unit: "COUNT" },
{ ing: "bell_pepper", nameSv: "Paprika", qty: 1, unit: "COUNT" },
{ ing: "kidney_beans_canned", nameSv: "Kidneybönor", qty: 400, unit: "GRAM" },
{ ing: "canned_tomatoes", nameSv: "Krossade tomater", qty: 2, unit: "COUNT" },
{ ing: "tomato_paste", nameSv: "Tomatpuré", qty: 2, unit: "TABLESPOON" },
{ ing: "cumin", nameSv: "Spiskummin", qty: 2, unit: "TEASPOON" },
{ ing: "paprika_powder", nameSv: "Paprikapulver", qty: 2, unit: "TEASPOON" },
{ ing: "chili_flakes", nameSv: "Chiliflakes", qty: 1, unit: "TEASPOON" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
{ ing: "rice_white", nameSv: "Ris till servering", qty: 4, unit: "DECILITER" },
],
steps: [
{
text: "Bryn färsen i olja i en stor gryta. Tillsätt hackad lök, vitlök och paprika och fräs mjukt.",
},
{ text: "Rör i tomatpuré och alla kryddor, fräs 1 minut.", timerSeconds: 60 },
{
text: "Tillsätt krossade tomater och 2 dl vatten. Sjud under lock 30 minuter. Se till att färsen är genomstekt.",
timerSeconds: 1800,
},
{
text: "Rör i avsköljda bönor och sjud 5 minuter till. Smaka av med salt.",
timerSeconds: 300,
},
{ text: "Servera med ris, och gärna crème fraiche och riven ost." },
],
},
{
slug: "teriyakilax-med-ris",
titleSv: "Teriyakilax med ris",
descriptionSv: "Glaserad lax med sötsalt teriyaki, ångat ris och broccoli.",
cuisine: "japanese",
mealTypes: ["dinner"],
tags: ["quick", "high_protein"],
methods: ["stovetop", "oven"],
equipment: ["stove"],
difficulty: "easy",
prepMin: 10,
cookMin: 15,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
dnaProtein: "salmon",
dnaCarb: "rice_white",
dnaVegetables: ["broccoli"],
dnaFlavor: ["umami", "sweet", "glazed"],
ingredients: [
{ ing: "salmon", nameSv: "Laxfilé", qty: 600, unit: "GRAM", note: "i portionsbitar" },
{ ing: "teriyaki_sauce", nameSv: "Teriyakisås", qty: 4, unit: "TABLESPOON" },
{ ing: "rice_white", nameSv: "Ris", qty: 3, unit: "DECILITER" },
{ ing: "broccoli", nameSv: "Broccoli", qty: 300, unit: "GRAM" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
{ ing: "sesame_seeds", nameSv: "Sesamfrön", qty: 1, unit: "TABLESPOON", optional: true },
],
steps: [
{ text: "Koka riset. Ånga eller koka broccolin de sista 4 minuterna.", timerSeconds: 720 },
{
text: "Stek laxen i olja med skinnsidan ner 34 minuter, vänd och stek 2 minuter till tills laxen är genomstekt.",
timerSeconds: 330,
},
{
text: "Häll teriyakisåsen över laxen och låt den glasera på svag värme 12 minuter.",
timerSeconds: 90,
},
{ text: "Servera laxen på ris med broccoli, toppa gärna med sesamfrön." },
],
},
{
slug: "spaghetti-med-kottfarssas",
titleSv: "Spaghetti med köttfärssås",
descriptionSv:
"Vardagens trotjänare: mustig köttfärssås som fått puttra, serverad med spaghetti.",
cuisine: "italian",
mealTypes: ["dinner", "lunch"],
tags: ["kid_friendly", "budget", "meal_prep", "freezer_friendly"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 30,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: [],
holidayTags: [],
storageGuidanceSv: "Såsen håller 3 dagar i kyl och fryser utmärkt.",
dnaProtein: "minced_beef",
dnaCarb: "pasta_dry",
dnaVegetables: ["onion", "carrot", "canned_tomatoes"],
dnaFlavor: ["tomato", "savory", "classic"],
ingredients: [
{ ing: "minced_beef", nameSv: "Nötfärs", qty: 500, unit: "GRAM" },
{ ing: "onion", nameSv: "Gul lök", qty: 1, unit: "COUNT" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT" },
{ ing: "carrot", nameSv: "Morot", qty: 1, unit: "COUNT", note: "finriven" },
{ ing: "canned_tomatoes", nameSv: "Krossade tomater", qty: 2, unit: "COUNT" },
{ ing: "tomato_paste", nameSv: "Tomatpuré", qty: 2, unit: "TABLESPOON" },
{ ing: "oregano_dried", nameSv: "Torkad oregano", qty: 1, unit: "TEASPOON" },
{ ing: "pasta_dry", nameSv: "Spaghetti", qty: 320, unit: "GRAM" },
{ ing: "olive_oil", nameSv: "Olivolja", qty: 1, unit: "TABLESPOON" },
],
steps: [
{
text: "Fräs hackad lök och vitlök i olivolja. Tillsätt färsen och bryn tills den fått färg.",
},
{ text: "Rör i tomatpuré, riven morot och oregano." },
{
text: "Tillsätt krossade tomater och 1 dl vatten. Låt sjuda minst 20 minuter. Se till att färsen är genomstekt.",
timerSeconds: 1200,
tip: "Längre puttertid = rundare smak.",
},
{ text: "Koka spaghettin enligt paketet.", timerSeconds: 540 },
{ text: "Smaka av såsen med salt och peppar och servera med pastan." },
],
},
{
slug: "pytt-i-panna",
titleSv: "Pytt i panna med stekt ägg",
descriptionSv:
"Klassisk restmat på tärnad potatis, lök och korv eller kött toppad med stekt ägg.",
cuisine: "swedish",
mealTypes: ["dinner", "lunch"],
tags: ["budget", "leftover_friendly", "quick"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 15,
cookMin: 15,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
dnaProtein: "falukorv",
dnaCarb: "potato",
dnaVegetables: ["onion"],
dnaFlavor: ["savory", "fried", "classic"],
ingredients: [
{
ing: "potato",
nameSv: "Kokt potatis",
qty: 8,
unit: "COUNT",
note: "tärnad perfekt för gårdagens potatis",
},
{ ing: "falukorv", nameSv: "Falukorv", qty: 300, unit: "GRAM", note: "tärnad" },
{ ing: "onion", nameSv: "Gul lök", qty: 2, unit: "COUNT", note: "hackade" },
{ ing: "egg", nameSv: "Ägg", qty: 4, unit: "COUNT" },
{ ing: "butter", nameSv: "Smör", qty: 40, unit: "GRAM" },
],
steps: [
{
text: "Stek potatistärningarna i hälften av smöret på hög värme tills gyllene och krispiga.",
},
{ text: "Tillsätt lök och korv och stek tills allt fått fin färg. Se till att korven är genomstekt." },
{ text: "Stek äggen i resten av smöret." },
{ text: "Salta, peppra och servera pytten med stekt ägg och gärna rödbetor." },
],
},
{
slug: "halloumiburgare",
titleSv: "Halloumiburgare med avokado",
descriptionSv:
"Vegetarisk burgare med stekt halloumi, avokado och syrlig rödlök i briochebröd.",
cuisine: "american",
mealTypes: ["dinner"],
tags: ["vegetarian", "quick"],
methods: ["stovetop", "grill"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 15,
cookMin: 10,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: ["summer"],
holidayTags: ["grillsäsong"],
dnaProtein: "halloumi",
dnaCarb: "hamburger_bun",
dnaVegetables: ["avocado", "tomato", "red_onion", "lettuce"],
dnaFlavor: ["salty", "fresh", "grilled"],
ingredients: [
{ ing: "halloumi", nameSv: "Halloumi", qty: 400, unit: "GRAM", note: "i skivor" },
{ ing: "hamburger_bun", nameSv: "Hamburgerbröd", qty: 4, unit: "COUNT" },
{ ing: "avocado", nameSv: "Avokado", qty: 2, unit: "COUNT" },
{ ing: "tomato", nameSv: "Tomat", qty: 2, unit: "COUNT", note: "skivade" },
{ ing: "red_onion", nameSv: "Rödlök", qty: 0.5, unit: "COUNT", note: "tunt skivad" },
{ ing: "lettuce", nameSv: "Sallad", qty: 0.25, unit: "COUNT" },
{ ing: "mayonnaise", nameSv: "Majonnäs", qty: 4, unit: "TABLESPOON" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
],
steps: [
{
text: "Stek eller grilla halloumiskivorna tills de är gyllene på båda sidor, 23 minuter per sida.",
timerSeconds: 300,
},
{ text: "Rosta bröden snabbt i pannan eller på grillen." },
{ text: "Mosa avokadon grovt med lite salt." },
{ text: "Bygg burgarna: majonnäs, sallad, halloumi, avokado, tomat och rödlök." },
],
},
{
slug: "kikartscurry",
titleSv: "Kikärtscurry med spenat",
descriptionSv:
"Snabb vegansk curry på kikärtor, tomat, kokosmjölk och spenat. Klar på 20 minuter.",
cuisine: "indian",
mealTypes: ["dinner", "lunch"],
tags: ["vegan", "vegetarian", "budget", "quick", "meal_prep"],
methods: ["stovetop"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 5,
cookMin: 15,
portions: 4,
spiceLevel: 2,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: [],
holidayTags: [],
dnaProtein: "chickpeas_canned",
dnaCarb: "rice_white",
dnaVegetables: ["spinach", "onion", "canned_tomatoes"],
dnaFlavor: ["spiced", "coconut", "warming"],
ingredients: [
{ ing: "chickpeas_canned", nameSv: "Kikärtor", qty: 500, unit: "GRAM", note: "avsköljda" },
{ ing: "onion", nameSv: "Gul lök", qty: 1, unit: "COUNT" },
{ ing: "garlic", nameSv: "Vitlöksklyftor", qty: 2, unit: "COUNT" },
{ ing: "canned_tomatoes", nameSv: "Krossade tomater", qty: 1, unit: "COUNT" },
{ ing: "coconut_milk", nameSv: "Kokosmjölk", qty: 2, unit: "DECILITER" },
{ ing: "spinach", nameSv: "Spenat", qty: 100, unit: "GRAM" },
{ ing: "garam_masala", nameSv: "Garam masala", qty: 2, unit: "TEASPOON" },
{ ing: "turmeric", nameSv: "Gurkmeja", qty: 1, unit: "TEASPOON" },
{ ing: "rice_white", nameSv: "Ris", qty: 3, unit: "DECILITER" },
{ ing: "rapeseed_oil", nameSv: "Olja", qty: 1, unit: "TABLESPOON" },
],
steps: [
{ text: "Koka riset.", timerSeconds: 720 },
{ text: "Fräs hackad lök och vitlök i olja, rör i kryddorna sista halvminuten." },
{
text: "Tillsätt kikärtor, krossade tomater och kokosmjölk. Sjud 10 minuter.",
timerSeconds: 600,
},
{ text: "Vänd ner spenaten, smaka av med salt och servera med ris." },
],
},
{
slug: "vasterbottensostpaj",
titleSv: "Västerbottensostpaj",
descriptionSv:
"Midsommarklassikern framför andra: knaprigt pajskal fyllt med krämig äggstanning och rejält med Västerbottensost.",
cuisine: "swedish",
mealTypes: ["lunch", "buffet", "starter"],
tags: ["vegetarian", "meal_prep"],
methods: ["oven"],
equipment: ["oven"],
difficulty: "medium",
prepMin: 25,
cookMin: 40,
portions: 8,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: true,
peakSeasons: ["summer"],
holidayTags: ["midsommar", "jul"],
storageGuidanceSv: "Håller 3 dagar i kyl. Kan bakas dagen innan och värmas.",
dnaProtein: "vasterbotten_cheese",
dnaCarb: "flour_wheat",
dnaVegetables: [],
dnaFlavor: ["rich", "cheesy", "buttery"],
ingredients: [
{ ing: "flour_wheat", nameSv: "Vetemjöl", qty: 3, unit: "DECILITER", group: "Pajdeg" },
{ ing: "butter", nameSv: "Smör (kallt)", qty: 125, unit: "GRAM", group: "Pajdeg" },
{
ing: "vasterbotten_cheese",
nameSv: "Västerbottensost (riven)",
qty: 300,
unit: "GRAM",
group: "Fyllning",
},
{ ing: "egg", nameSv: "Ägg", qty: 3, unit: "COUNT", group: "Fyllning" },
{ ing: "cream", nameSv: "Vispgrädde", qty: 2, unit: "DECILITER", group: "Fyllning" },
{ ing: "black_pepper", nameSv: "Svartpeppar", qty: 2, unit: "MILLILITER", group: "Fyllning" },
],
steps: [
{
text: "Nyp ihop mjöl, smör och 1 msk kallt vatten till en deg. Tryck ut i en pajform och vila kallt 30 minuter.",
timerSeconds: 1800,
},
{
text: "Sätt ugnen på 200 °C. Förgrädda skalet 10 minuter.",
timerSeconds: 600,
temperatureC: 200,
},
{ text: "Vispa ihop ägg, grädde och peppar. Rör i den rivna osten." },
{
text: "Häll fyllningen i skalet och grädda 2530 minuter tills stanningen stelnat och fått gyllene yta.",
timerSeconds: 1650,
temperatureC: 200,
},
{
text: "Låt svalna något före servering god ljummen med löjrom, rödlök och crème fraiche.",
},
],
},
{
slug: "jansons-frestelse",
titleSv: "Janssons frestelse",
descriptionSv:
"Julbordets krämiga potatisgratäng med svensk ansjovis, lök och grädde gyllene och knaprig på ytan.",
cuisine: "swedish",
mealTypes: ["dinner", "buffet"],
tags: ["meal_prep"],
methods: ["oven"],
equipment: ["oven"],
difficulty: "easy",
prepMin: 25,
cookMin: 55,
portions: 6,
spiceLevel: 0,
mealPrepFriendly: true,
freezerFriendly: false,
peakSeasons: ["winter"],
holidayTags: ["jul", "påsk", "midsommar"],
dnaProtein: "anchovy_swedish",
dnaCarb: "potato",
dnaVegetables: ["onion"],
dnaFlavor: ["rich", "salty", "creamy"],
ingredients: [
{ ing: "potato", nameSv: "Potatis (fast)", qty: 10, unit: "COUNT", note: "i tunna stavar" },
{ ing: "onion", nameSv: "Gul lök", qty: 2, unit: "COUNT", note: "tunt skivad" },
{ ing: "anchovy_swedish", nameSv: "Ansjovisfiléer med spad", qty: 125, unit: "GRAM" },
{ ing: "cream", nameSv: "Vispgrädde", qty: 3, unit: "DECILITER" },
{ ing: "breadcrumbs", nameSv: "Ströbröd", qty: 2, unit: "TABLESPOON" },
{ ing: "butter", nameSv: "Smör", qty: 25, unit: "GRAM" },
],
steps: [
{ text: "Sätt ugnen på 200 °C.", temperatureC: 200 },
{ text: "Stek löken mjuk i smör utan att den tar färg." },
{ text: "Varva potatisstavar, lök och ansjovis i en smord form. Avsluta med potatis." },
{
text: "Häll över hälften av grädden och lite ansjovisspad. Strö över ströbröd och klicka på smör.",
},
{
text: "Grädda 30 minuter, häll på resten av grädden och grädda 2025 minuter till tills potatisen är mjuk.",
timerSeconds: 3300,
temperatureC: 200,
},
],
},
{
slug: "sill-och-farskpotatis",
titleSv: "Sill och färskpotatis",
descriptionSv:
"Midsommarens självklara lunch: inlagd sill, nykokt färskpotatis med dill, gräddfil och gräslök.",
cuisine: "swedish",
mealTypes: ["lunch", "buffet"],
tags: ["quick", "gluten_free"],
methods: ["stovetop", "no_cook"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 10,
cookMin: 20,
portions: 4,
spiceLevel: 0,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: ["summer"],
holidayTags: ["midsommar"],
dnaProtein: "pickled_herring",
dnaCarb: "new_potato",
dnaVegetables: ["dill"],
dnaFlavor: ["fresh", "pickled", "summer"],
ingredients: [
{ ing: "pickled_herring", nameSv: "Inlagd sill", qty: 400, unit: "GRAM" },
{ ing: "new_potato", nameSv: "Färskpotatis", qty: 800, unit: "GRAM" },
{ ing: "dill", nameSv: "Färsk dill", qty: 15, unit: "GRAM" },
{ ing: "creme_fraiche", nameSv: "Crème fraiche eller gräddfil", qty: 2, unit: "DECILITER" },
{ ing: "red_onion", nameSv: "Rödlök", qty: 0.5, unit: "COUNT", note: "finhackad" },
{ ing: "butter", nameSv: "Smör", qty: 25, unit: "GRAM" },
],
steps: [
{
text: "Skrubba färskpotatisen och koka med en dillkvist i saltat vatten, 1518 minuter.",
timerSeconds: 1020,
},
{ text: "Lägg upp sillen och strö över finhackad rödlök." },
{
text: "Servera potatisen med smör och dill, tillsammans med sill och crème fraiche.",
tip: "Ett hårdkokt ägg och knäckebröd gör midsommartallriken komplett.",
},
],
},
{
slug: "havregrynsgrot",
titleSv: "Havregrynsgröt med äpple och kanel",
descriptionSv: "Vardagsfrukostens bas: krämig havregrynsgröt toppad med rivet äpple och kanel.",
cuisine: "swedish",
mealTypes: ["breakfast"],
tags: ["budget", "quick", "vegan"],
methods: ["stovetop", "microwave"],
equipment: ["stove"],
difficulty: "beginner",
prepMin: 2,
cookMin: 5,
portions: 2,
spiceLevel: 0,
mealPrepFriendly: false,
freezerFriendly: false,
peakSeasons: [],
holidayTags: [],
dnaCarb: "oats",
dnaVegetables: [],
dnaFlavor: ["warm", "cinnamon", "simple"],
ingredients: [
{ ing: "oats", nameSv: "Havregryn", qty: 2, unit: "DECILITER" },
{ ing: "salt", nameSv: "Salt", qty: 0.5, unit: "MILLILITER" },
{ ing: "apple", nameSv: "Äpple", qty: 1, unit: "COUNT", note: "rivet" },
{ ing: "cinnamon", nameSv: "Kanel", qty: 1, unit: "TEASPOON" },
{ ing: "milk_3", nameSv: "Mjölk till servering", qty: 2, unit: "DECILITER", optional: true },
],
steps: [
{ text: "Koka upp 4 dl vatten med havregryn och salt." },
{ text: "Sjud under omrörning 3 minuter.", timerSeconds: 180 },
{ text: "Toppa med rivet äpple, kanel och mjölk." },
],
},
];
File diff suppressed because it is too large Load Diff
+36 -36
View File
@@ -735,47 +735,47 @@ async function insertRecipe(
? (slugToId.get(recipe.variantOfSlug) ?? null) ? (slugToId.get(recipe.variantOfSlug) ?? null)
: null; : null;
const recipeValues = {
slug: recipe.slug,
titleSv: recipe.titleSv,
descriptionSv: recipe.descriptionSv,
country: recipe.country ?? null,
cuisine: recipe.cuisine,
mealTypes: recipe.mealTypes,
tags: recipe.tags,
methods: recipe.methods,
equipment: recipe.equipment,
difficulty: recipe.difficulty,
prepTimeMinutes: recipe.prepMin,
cookTimeMinutes: recipe.cookMin,
totalTimeMinutes: recipe.prepMin + recipe.cookMin,
portions: recipe.portions,
nutritionPerPortion: nutrition.perPortion,
allergens: [...allergens].sort(),
spiceLevel: recipe.spiceLevel,
estimatedCostMinorPerPortion: costPerPortion,
storageGuidanceSv: recipe.storageGuidanceSv ?? null,
mealPrepFriendly: recipe.mealPrepFriendly,
freezerFriendly: recipe.freezerFriendly,
peakSeasons: recipe.peakSeasons,
holidayTags: recipe.holidayTags,
dna,
variantType: recipe.variantType ?? "standard",
variantOfRecipeId,
status: "published" as const,
verificationStatus: recipe.verificationStatus ?? "editorial",
sourceType: "own_editorial" as const,
sourceRegistryId,
creatorDisplayName: `${BRAND.name} Redaktion`,
};
const [row] = await db const [row] = await db
.insert(schema.recipes) .insert(schema.recipes)
.values({ .values(recipeValues)
slug: recipe.slug,
titleSv: recipe.titleSv,
descriptionSv: recipe.descriptionSv,
country: recipe.country ?? null,
cuisine: recipe.cuisine,
mealTypes: recipe.mealTypes,
tags: recipe.tags,
methods: recipe.methods,
equipment: recipe.equipment,
difficulty: recipe.difficulty,
prepTimeMinutes: recipe.prepMin,
cookTimeMinutes: recipe.cookMin,
totalTimeMinutes: recipe.prepMin + recipe.cookMin,
portions: recipe.portions,
nutritionPerPortion: nutrition.perPortion,
allergens: [...allergens].sort(),
spiceLevel: recipe.spiceLevel,
estimatedCostMinorPerPortion: costPerPortion,
storageGuidanceSv: recipe.storageGuidanceSv ?? null,
mealPrepFriendly: recipe.mealPrepFriendly,
freezerFriendly: recipe.freezerFriendly,
peakSeasons: recipe.peakSeasons,
holidayTags: recipe.holidayTags,
dna,
variantType: recipe.variantType ?? "standard",
variantOfRecipeId,
status: "published",
verificationStatus: "editorial",
sourceType: "own_editorial",
sourceRegistryId,
creatorDisplayName: `${BRAND.name} Redaktion`,
})
.onConflictDoUpdate({ .onConflictDoUpdate({
target: schema.recipes.slug, target: schema.recipes.slug,
set: { set: {
nutritionPerPortion: nutrition.perPortion, ...recipeValues,
allergens: [...allergens].sort(),
dna,
updatedAt: new Date(), updatedAt: new Date(),
}, },
}) })