diff --git a/apps/mobile/src/app/recipe/[id].tsx b/apps/mobile/src/app/recipe/[id].tsx index 654be7b..5ec7be7 100644 --- a/apps/mobile/src/app/recipe/[id].tsx +++ b/apps/mobile/src/app/recipe/[id].tsx @@ -205,7 +205,13 @@ export default function RecipeScreen() { )} {recipe.description ?? recipe.descriptionSv} - {recipe.creatorDisplayName && Skapat av {recipe.creatorDisplayName}} + {recipe.creatorDisplayName && ( + + {recipe.creatorDisplayName.includes("Redaktion") + ? `Skapat av ${recipe.creatorDisplayName}` + : `Källa: ${recipe.creatorDisplayName}`} + + )} {!recipe.safety.safe && ( diff --git a/packages/database/src/seed/data/chef-recipes.ts b/packages/database/src/seed/data/chef-recipes.ts new file mode 100644 index 0000000..a9e5455 --- /dev/null +++ b/packages/database/src/seed/data/chef-recipes.ts @@ -0,0 +1,407 @@ +import type { SeedRecipe } from "./recipes.js"; + +/** + * Namngivna kock-recept (spec: named-chef attribution). EGNA formuleringar + * (sourceType own_editorial) av kockarnas kända signaturrätter – rätter kan inte + * upphovsrättsskyddas, bara den exakta texten, så texten här är nyskriven. + * Krediteras diskret via creatorDisplayName ("Källa: "), aldrig badge/flik. + * De ligger bland alla andra recept och lyder samma diet-/allergifilter. + * + * HÅRDA REGLER (annars kraschar seed): ids finns i katalogen; (ing, unit) + * beräkningsbar (GRAM/KILOGRAM alltid; volym kräver density; COUNT gramsPerPiece). + */ +export const CHEF_RECIPES: SeedRecipe[] = [ + { + slug: "gordon-kramig-aggrora", + titleSv: "Krämig äggröra", + descriptionSv: + "Långsamt rörd, silkeslen äggröra som lyfts av en klick crème fraiche på slutet – Gordon Ramsays kända sätt att göra frukostägg lyxiga.", + cuisine: "international", + mealTypes: ["breakfast"], + tags: ["vegetarian", "high_protein", "quick"], + methods: ["stovetop"], + equipment: ["stove"], + difficulty: "easy", + prepMin: 3, + cookMin: 7, + portions: 2, + spiceLevel: 0, + mealPrepFriendly: false, + freezerFriendly: false, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Gordon Ramsay", + dnaProtein: "egg", + dnaVegetables: [], + dnaFlavor: ["creamy", "buttery", "savory"], + ingredients: [ + { ing: "egg", nameSv: "Ägg", qty: 6, unit: "COUNT", optional: false }, + { ing: "butter", nameSv: "Smör", qty: 30, unit: "GRAM", optional: false }, + { ing: "creme_fraiche", nameSv: "Crème fraiche", qty: 1, unit: "DECILITER", optional: false }, + { ing: "salt", nameSv: "Salt", qty: 1, unit: "TEASPOON", optional: false }, + ], + steps: [ + { text: "Vispa upp äggen lätt. Lägg smöret och äggen kalla i en kastrull." }, + { + text: "Värm på medelvärme och rör hela tiden. Lyft av från plattan med jämna mellanrum så äggen inte blir torra – de ska bli krämiga, inte fasta.", + }, + { text: "Ta av från värmen medan de fortfarande är lite lösa, rör ner crème fraiche och salta. Servera direkt." }, + ], + }, + { + slug: "jamie-jordnotskyckling", + titleSv: "Jordnötskyckling med ris", + descriptionSv: + "Snabb wokad kyckling i en fyllig jordnötssås med soja och lime, serverad på ris – i andan av Jamie Olivers snabba vardagsrätter.", + cuisine: "international", + mealTypes: ["dinner"], + tags: ["high_protein", "quick"], + methods: ["stovetop"], + equipment: ["stove"], + difficulty: "easy", + prepMin: 10, + cookMin: 20, + portions: 4, + spiceLevel: 1, + mealPrepFriendly: true, + freezerFriendly: false, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Jamie Oliver", + dnaProtein: "chicken_thigh", + dnaCarb: "rice_white", + dnaVegetables: [], + dnaFlavor: ["nutty", "umami", "tangy"], + ingredients: [ + { ing: "chicken_thigh", nameSv: "Kycklinglårfilé", qty: 500, unit: "GRAM", optional: false }, + { ing: "peanut_butter", nameSv: "Jordnötssmör", qty: 80, unit: "GRAM", optional: false }, + { ing: "soy_sauce", nameSv: "Soja", qty: 2, unit: "TABLESPOON", optional: false }, + { ing: "garlic", nameSv: "Vitlöksklyfta", qty: 2, unit: "COUNT", optional: false }, + { ing: "lime", nameSv: "Lime", qty: 40, unit: "GRAM", optional: false }, + { ing: "rice_white", nameSv: "Ris", qty: 300, unit: "GRAM", optional: false }, + ], + steps: [ + { text: "Koka riset. Skär kycklingen i bitar och stek den gyllene i en het panna." }, + { + text: "Rör ihop jordnötssmör, soja, pressad vitlök och limejuice med en skvätt vatten till en slät sås.", + }, + { text: "Häll såsen över kycklingen, låt puttra ett par minuter tills den tjocknar. Servera på riset." }, + ], + }, + { + slug: "nigella-kladdig-chokladkaka", + titleSv: "Kladdig chokladkaka utan mjöl", + descriptionSv: + "Intensiv, seg chokladkaka helt utan mjöl – ren choklad, smör, ägg och socker. Indulgent på Nigella Lawsons vis, och naturligt glutenfri.", + cuisine: "international", + mealTypes: ["dessert"], + tags: ["vegetarian", "gluten_free", "baking", "cake"], + methods: ["oven"], + equipment: ["oven"], + difficulty: "medium", + prepMin: 15, + cookMin: 30, + portions: 8, + spiceLevel: 0, + mealPrepFriendly: false, + freezerFriendly: true, + peakSeasons: [], + holidayTags: ["fika"], + verificationStatus: "editorial", + creatorDisplayName: "Nigella Lawson", + dnaVegetables: [], + dnaFlavor: ["chocolate", "sweet", "rich"], + ingredients: [ + { ing: "dark_chocolate", nameSv: "Mörk choklad", qty: 200, unit: "GRAM", optional: false }, + { ing: "butter", nameSv: "Smör", qty: 150, unit: "GRAM", optional: false }, + { ing: "egg", nameSv: "Ägg", qty: 4, unit: "COUNT", optional: false }, + { ing: "sugar", nameSv: "Strösocker", qty: 150, unit: "GRAM", optional: false }, + ], + steps: [ + { text: "Smält choklad och smör försiktigt tillsammans och låt svalna något." }, + { text: "Vispa ägg och socker pösigt och vänd ner chokladsmeten." }, + { + text: "Häll i en smord form och grädda i mitten av ugnen tills ytan stelnat men mitten fortfarande är kladdig, cirka 25–30 minuter.", + temperatureC: 170, + }, + ], + }, + { + slug: "ottolenghi-hummus", + titleSv: "Krämig hummus", + descriptionSv: + "Len, citronfrisk hummus på kikärter med vitlök och olivolja – Yotam Ottolenghis signatur, perfekt som röra eller på mezebordet.", + cuisine: "middle_eastern", + mealTypes: ["starter", "snack", "buffet"], + tags: ["vegan", "vegetarian", "gluten_free"], + methods: ["no_cook"], + equipment: ["blender"], + difficulty: "beginner", + prepMin: 10, + cookMin: 0, + portions: 6, + spiceLevel: 0, + mealPrepFriendly: true, + freezerFriendly: false, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Yotam Ottolenghi", + dnaVegetables: [], + dnaFlavor: ["garlic", "citrus", "nutty"], + ingredients: [ + { ing: "chickpeas_canned", nameSv: "Kikärter", qty: 400, unit: "GRAM", optional: false }, + { ing: "garlic", nameSv: "Vitlöksklyfta", qty: 1, unit: "COUNT", optional: false }, + { ing: "lemon", nameSv: "Citron", qty: 1, unit: "COUNT", optional: false }, + { ing: "olive_oil", nameSv: "Olivolja", qty: 3, unit: "TABLESPOON", optional: false }, + { ing: "salt", nameSv: "Salt", qty: 1, unit: "TEASPOON", optional: false }, + ], + steps: [ + { text: "Skölj och låt kikärterna rinna av. Spara några hela till garnering." }, + { + text: "Mixa kikärter, pressad vitlök, citronsaft, olivolja och salt slätt med en skvätt vatten tills krämigt.", + }, + { text: "Lägg upp, ringla över lite olivolja och toppa med de sparade kikärterna." }, + ], + }, + { + slug: "marcus-glaserad-lax", + titleSv: "Soja- och honungsglaserad lax", + descriptionSv: + "Ugnsbakad lax med en blank glasyr av soja, honung och vitlök, serverad på ris – smakrikt och elegant i Marcus Samuelssons anda.", + cuisine: "nordic", + mealTypes: ["dinner"], + tags: ["high_protein", "gluten_free"], + methods: ["oven"], + equipment: ["oven"], + difficulty: "easy", + prepMin: 10, + cookMin: 20, + portions: 4, + spiceLevel: 0, + mealPrepFriendly: false, + freezerFriendly: false, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Marcus Samuelsson", + dnaProtein: "salmon", + dnaCarb: "rice_white", + dnaVegetables: [], + dnaFlavor: ["umami", "sweet", "garlic"], + ingredients: [ + { ing: "salmon", nameSv: "Laxfilé", qty: 500, unit: "GRAM", optional: false }, + { ing: "soy_sauce", nameSv: "Soja", qty: 2, unit: "TABLESPOON", optional: false }, + { ing: "honey", nameSv: "Honung", qty: 40, unit: "GRAM", optional: false }, + { ing: "garlic", nameSv: "Vitlöksklyfta", qty: 2, unit: "COUNT", optional: false }, + { ing: "rice_white", nameSv: "Ris", qty: 300, unit: "GRAM", optional: false }, + ], + steps: [ + { text: "Koka riset. Rör ihop soja, honung och pressad vitlök till en glasyr." }, + { text: "Lägg laxen i en ugnsform och pensla på glasyren." }, + { + text: "Baka mitt i ugnen tills laxen är precis genomgången, cirka 15 minuter. Pensla med skyn en gång till och servera på riset.", + temperatureC: 200, + }, + ], + }, + { + slug: "giada-zucchinipasta-parmesan", + titleSv: "Zucchinipasta med parmesan", + descriptionSv: + "Enkel pasta med mjukstekt zucchini, vitlök och rikligt med riven parmesan – lätt och italiensk i Giada De Laurentiis stil.", + cuisine: "italian", + mealTypes: ["dinner", "lunch"], + tags: ["vegetarian"], + methods: ["stovetop"], + equipment: ["stove"], + difficulty: "easy", + prepMin: 10, + cookMin: 20, + portions: 4, + spiceLevel: 0, + mealPrepFriendly: true, + freezerFriendly: false, + peakSeasons: ["summer"], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Giada De Laurentiis", + dnaCarb: "pasta_dry", + dnaVegetables: ["zucchini"], + dnaFlavor: ["savory", "garlic", "salty"], + ingredients: [ + { ing: "pasta_dry", nameSv: "Pasta", qty: 350, unit: "GRAM", optional: false }, + { ing: "zucchini", nameSv: "Zucchini", qty: 2, unit: "COUNT", optional: false }, + { ing: "cheese_hard", nameSv: "Parmesan", qty: 80, unit: "GRAM", optional: false }, + { ing: "olive_oil", nameSv: "Olivolja", qty: 2, unit: "TABLESPOON", optional: false }, + { ing: "garlic", nameSv: "Vitlöksklyfta", qty: 2, unit: "COUNT", optional: false }, + ], + steps: [ + { text: "Koka pastan. Tärna zucchinin och skiva vitlöken." }, + { text: "Fräs zucchini och vitlök mjuka i olivoljan." }, + { text: "Vänd ner den avrunna pastan med lite pastavatten och den rivna parmesanen tills krämigt. Servera direkt." }, + ], + }, + { + slug: "emeril-rakgryta-creole", + titleSv: "Räkgryta Creole", + descriptionSv: + "Kryddig tomatgryta med räkor, paprika och lök på ris – en klassisk Louisiana-creole i Emeril Lagasses smakrika stil.", + cuisine: "american", + mealTypes: ["dinner"], + tags: ["high_protein"], + methods: ["stovetop"], + equipment: ["stove"], + difficulty: "medium", + prepMin: 15, + cookMin: 30, + portions: 4, + spiceLevel: 2, + mealPrepFriendly: true, + freezerFriendly: true, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Emeril Lagasse", + dnaProtein: "shrimp", + dnaCarb: "rice_white", + dnaVegetables: ["bell_pepper", "onion"], + dnaFlavor: ["spicy", "savory", "tomato"], + ingredients: [ + { ing: "shrimp", nameSv: "Räkor", qty: 400, unit: "GRAM", optional: false }, + { ing: "canned_tomatoes", nameSv: "Krossade tomater", qty: 400, unit: "GRAM", optional: false }, + { ing: "bell_pepper", nameSv: "Paprika", qty: 1, unit: "COUNT", optional: false }, + { ing: "onion", nameSv: "Gul lök", qty: 1, unit: "COUNT", optional: false }, + { ing: "garlic", nameSv: "Vitlöksklyfta", qty: 2, unit: "COUNT", optional: false }, + { ing: "rice_white", nameSv: "Ris", qty: 300, unit: "GRAM", optional: false }, + ], + steps: [ + { text: "Koka riset. Fräs hackad lök, paprika och vitlök mjuka." }, + { text: "Häll i de krossade tomaterna och låt sjuda tills såsen tjocknar, cirka 15 minuter. Krydda gärna starkt." }, + { text: "Vänd ner räkorna och låt dem bli varma ett par minuter. Servera över riset." }, + ], + }, + { + slug: "ina-citron-timjankyckling", + titleSv: "Citron- och timjankyckling", + descriptionSv: + "Saftig ugnsrostad kyckling med citron, vitlök och timjan – enkel, elegant vardagslyx i Ina Gartens Barefoot Contessa-anda.", + cuisine: "american", + mealTypes: ["dinner"], + tags: ["high_protein", "gluten_free"], + methods: ["oven"], + equipment: ["oven"], + difficulty: "easy", + prepMin: 10, + cookMin: 35, + portions: 4, + spiceLevel: 0, + mealPrepFriendly: true, + freezerFriendly: false, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Ina Garten", + dnaProtein: "chicken_thigh", + dnaVegetables: [], + dnaFlavor: ["citrus", "herby", "garlic"], + ingredients: [ + { ing: "chicken_thigh", nameSv: "Kycklinglårfilé", qty: 600, unit: "GRAM", optional: false }, + { ing: "lemon", nameSv: "Citron", qty: 1, unit: "COUNT", optional: false }, + { ing: "thyme_dried", nameSv: "Timjan", qty: 4, unit: "GRAM", optional: false }, + { ing: "garlic", nameSv: "Vitlöksklyfta", qty: 3, unit: "COUNT", optional: false }, + { ing: "olive_oil", nameSv: "Olivolja", qty: 2, unit: "TABLESPOON", optional: false }, + ], + steps: [ + { text: "Blanda olivolja, pressad vitlök, timjan och saften av citronen. Vänd kycklingen i marinaden." }, + { text: "Lägg kycklingen i en ugnsform med de urpressade citronhalvorna." }, + { + text: "Rosta mitt i ugnen tills kycklingen är genomstekt och gyllene, cirka 30–35 minuter.", + temperatureC: 200, + }, + ], + }, + { + slug: "raymond-tradgardsomelett", + titleSv: "Trädgårdsomelett", + descriptionSv: + "Fluffig fransk omelett med svamp, spenat och lite ost – enkel och förfinad i Raymond Blancs stil, klar på några minuter.", + cuisine: "french", + mealTypes: ["breakfast", "lunch"], + tags: ["vegetarian", "gluten_free", "high_protein", "quick"], + methods: ["stovetop"], + equipment: ["stove"], + difficulty: "easy", + prepMin: 8, + cookMin: 7, + portions: 2, + spiceLevel: 0, + mealPrepFriendly: false, + freezerFriendly: false, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Raymond Blanc", + dnaProtein: "egg", + dnaVegetables: ["mushroom", "spinach"], + dnaFlavor: ["savory", "buttery", "fresh"], + ingredients: [ + { ing: "egg", nameSv: "Ägg", qty: 4, unit: "COUNT", optional: false }, + { ing: "butter", nameSv: "Smör", qty: 20, unit: "GRAM", optional: false }, + { ing: "mushroom", nameSv: "Champinjoner", qty: 100, unit: "GRAM", optional: false }, + { ing: "spinach", nameSv: "Spenat", qty: 60, unit: "GRAM", optional: false }, + { ing: "cheese_hard", nameSv: "Riven ost", qty: 40, unit: "GRAM", optional: false }, + ], + steps: [ + { text: "Fräs skivad svamp mjuk i lite av smöret, vänd ner spenaten tills den faller ihop. Ta upp." }, + { text: "Vispa äggen lätt. Smält resten av smöret i pannan och häll i äggen, dra in kanterna tills nästan stelt." }, + { text: "Lägg svamp, spenat och ost på ena halvan, vik ihop omeletten och låt osten smälta. Servera direkt." }, + ], + }, + { + slug: "rick-fiskgratang", + titleSv: "Krämig fiskgratäng", + descriptionSv: + "Torsk i len gräddsås under ett lock av krämig potatismos och gratinerad ost – en brittisk fish pie-klassiker i Rick Steins anda.", + cuisine: "international", + mealTypes: ["dinner"], + tags: ["high_protein"], + methods: ["oven"], + equipment: ["oven"], + difficulty: "medium", + prepMin: 25, + cookMin: 30, + portions: 4, + spiceLevel: 0, + mealPrepFriendly: true, + freezerFriendly: true, + peakSeasons: [], + holidayTags: [], + verificationStatus: "editorial", + creatorDisplayName: "Rick Stein", + dnaProtein: "cod", + dnaCarb: "potato", + dnaVegetables: [], + dnaFlavor: ["creamy", "savory", "buttery"], + ingredients: [ + { ing: "cod", nameSv: "Torskfilé", qty: 500, unit: "GRAM", optional: false }, + { ing: "potato", nameSv: "Potatis", qty: 5, unit: "COUNT", optional: false }, + { ing: "milk_3", nameSv: "Mjölk", qty: 3, unit: "DECILITER", optional: false }, + { ing: "cream", nameSv: "Grädde", qty: 2, unit: "DECILITER", optional: false }, + { ing: "butter", nameSv: "Smör", qty: 40, unit: "GRAM", optional: false }, + { ing: "cheese_hard", nameSv: "Riven ost", qty: 80, unit: "GRAM", optional: false }, + ], + steps: [ + { text: "Koka potatisen mjuk och mosa den med smör och lite av mjölken till ett krämigt mos." }, + { + text: "Lägg torsken i en smord ugnsform, häll över grädden blandad med resten av mjölken och salta.", + }, + { + text: "Bred moset över fisken, strö på riven ost och gratinera i ugnen tills gyllene och bubblande, cirka 25 minuter.", + temperatureC: 200, + }, + ], + }, +]; diff --git a/packages/database/src/seed/data/recipes.ts b/packages/database/src/seed/data/recipes.ts index 28db1ff..bd4248e 100644 --- a/packages/database/src/seed/data/recipes.ts +++ b/packages/database/src/seed/data/recipes.ts @@ -63,12 +63,20 @@ export interface SeedRecipe { dnaVegetables: string[]; dnaFlavor: string[]; verificationStatus?: "editorial" | "verified" | "unverified" | "community" | "rejected"; + /** Namngiven källa/kock för diskret attribution (t.ex. "Gordon Ramsay"). + * Utelämnad = husets redaktion. Visas som liten "Källa: …"-rad, aldrig badge. */ + creatorDisplayName?: string; ingredients: SeedRecipeIngredient[]; steps: SeedRecipeStep[]; } import { VERIFIED_RECIPES } from "./verified-recipes.js"; import { EXTRA_RECIPES } from "./extra-recipes.js"; +import { CHEF_RECIPES } from "./chef-recipes.js"; /** Hela seed-katalogen: committade verifierade recept + nya redaktionella extrarecept. */ -export const SEED_RECIPES: SeedRecipe[] = [...VERIFIED_RECIPES, ...EXTRA_RECIPES]; +export const SEED_RECIPES: SeedRecipe[] = [ + ...VERIFIED_RECIPES, + ...EXTRA_RECIPES, + ...CHEF_RECIPES, +]; diff --git a/packages/database/src/seed/run.ts b/packages/database/src/seed/run.ts index 66a5de8..5dca1c0 100644 --- a/packages/database/src/seed/run.ts +++ b/packages/database/src/seed/run.ts @@ -767,7 +767,7 @@ async function insertRecipe( verificationStatus: recipe.verificationStatus ?? "editorial", sourceType: "own_editorial" as const, sourceRegistryId, - creatorDisplayName: `${BRAND.name} Redaktion`, + creatorDisplayName: recipe.creatorDisplayName ?? `${BRAND.name} Redaktion`, }; const [row] = await db