diff --git a/client/src/lib/tags.ts b/client/src/lib/tags.ts index b49b8e0..3919cc7 100644 --- a/client/src/lib/tags.ts +++ b/client/src/lib/tags.ts @@ -1,27 +1,50 @@ // Maks antall tags en oppskrift kan ha export const MAX_TAGS = 6 -// Alle tags, fordelt på kategorier +// Alle tags, fordelt på kategorier. +// Rekkefølgen her er rekkefølgen på kategoriknappene i dialogen. +// Hver tag må ha et unikt navn på tvers av ALLE kategorier. export const tagGroups: Record = { Kosthold: [ 'Vegetar', 'Vegansk', 'Pescetar', 'Glutenfri', 'Laktosefri', 'Melkefri', 'Eggfri', 'Nøttefri', 'Soyafri', 'Uten tilsatt sukker', 'Lavkarbo', 'Keto', 'Paleo', 'Halal', 'Kosher', + 'Plantebasert', 'Hvetefri', 'Fettfattig', 'Lavt saltinnhold', + ], + Hovedingrediens: [ + 'Kylling', 'Storfe', 'Svinekjøtt', 'Lam', 'Fisk', 'Laks', + 'Sjømat', 'Egg', 'Ost', 'Pasta', 'Ris', 'Poteter', + 'Bønner og linser', 'Tofu', 'Sopp', 'Grønnsaker', 'Mel og deig', + ], + 'Type rett': [ + 'Suppe', 'Salat', 'Gryte', 'Wok', 'Pai', 'Pizza', + 'Burger', 'Tacos', 'Smoothie', 'Brød', 'Kake', 'Kjeks', + 'Saus', 'Dip', 'Tilbehør', ], 'Tid og tilberedning': [ - 'Under 15 min', 'Under 30 min', 'Under 60 min', 'Én gryte', 'Airfryer', 'Ovnsbakt', - 'Ingen steking', 'Ingen koking', 'Meal prep', 'Frysevennlig', 'Grilling', 'Slow cooker', + 'Under 15 min', 'Under 30 min', 'Under 45 min', 'Under 60 min', 'Langtidskoking', + 'Én gryte', 'Én form', 'Lite oppvask', 'Stekepanne', 'Airfryer', 'Ovnsbakt', + 'Mikrobølgeovn', 'Ingen steking', 'Ingen koking', 'Meal prep', 'Frysevennlig', + 'Grilling', 'Slow cooker', ], Matpreferanser: [ 'Sterk mat', 'Mild', 'Proteinrik', 'Fiberrik', 'Grønnsaksrik', 'Sukkerfri', 'Søt', 'Salt', 'Syrlig', 'Sprø', 'Kremet', 'Comfort food', + 'Røkt', 'Frisk', 'Mettende', 'Lett', 'Saftig', 'Umami', + ], + Matkultur: [ + 'Norsk', 'Nordisk', 'Italiensk', 'Meksikansk', 'Tex-Mex', 'Amerikansk', + 'Indisk', 'Japansk', 'Koreansk', 'Thai', 'Kinesisk', 'Vietnamesisk', + 'Gresk', 'Fransk', 'Spansk', 'Tyrkisk', 'Libanesisk', 'Marokkansk', + 'Middelhavsmat', ], Anledning: [ 'Studentvennlig', 'Budsjettvennlig', 'Barnevennlig', 'Festmat', 'Hverdagsmat', 'Helgekos', - 'Matpakke', 'Selskap', 'Date night', 'Sommermat', 'Vintermat', 'Turmat', + 'Fredagskos', 'Matpakke', 'Selskap', 'Gjester', 'Date night', 'Bursdag', + 'Brunsj', 'Piknik', 'Restemat', 'Turmat', ], - Matkultur: [ - 'Italiensk', 'Meksikansk', 'Indisk', 'Japansk', 'Koreansk', 'Thai', - 'Kinesisk', 'Gresk', 'Fransk', 'Spansk', 'Middelhavsmat', 'Nordisk', + Sesong: [ + 'Vårmat', 'Sommermat', 'Høstmat', 'Vintermat', 'Julemat', 'Påskemat', + 'Nyttårsaften', '17. mai', 'Sankthans', 'Halloween', 'Grillsesong', 'Sesongvarer', ], } \ No newline at end of file diff --git a/client/src/routes/NewRecipePage.tsx b/client/src/routes/NewRecipePage.tsx index a9ddb1b..fae2326 100644 --- a/client/src/routes/NewRecipePage.tsx +++ b/client/src/routes/NewRecipePage.tsx @@ -1,5 +1,5 @@ - import { useState } from 'react' +import { useNavigate } from 'react-router' import { useMutation } from '@apollo/client/react' import { CREATE_RECIPE } from '../graphql/mutations' @@ -8,7 +8,6 @@ import RecipeStep2 from '../components/new-recipe/RecipeStep2' import RecipeStep3 from '../components/new-recipe/RecipeStep3' import RecipeStep4 from '../components/new-recipe/RecipeStep4' -// Definerer hvordan en ingrediens skal se ut export type Ingredient = { id: number name: string @@ -16,15 +15,12 @@ export type Ingredient = { unit: string } -// Definerer hvordan en instruksjon skal se ut export type Instruction = { id: number text: string } -// All informasjon som lagres i oppskriftsskjemaet export type RecipeFormData = { - // Step 1 - Generell informasjon title: string description: string videoUrl: string @@ -34,30 +30,26 @@ export type RecipeFormData = { category: string tags: string[] rating: number | null - - // Step 2 - Ingredienser measurementSystem: 'metric' | 'imperial' ingredients: Ingredient[] ingredientNote: string - - // Step 3 - Fremgangsmåte instructionMode: 'steps' | 'text' instructionText: string steps: { id: number; text: string; minutes: string }[] } -// Deler samlet tekst opp i separate steg +type CreateRecipeData = { + createRecipe: { + id: string + title: string + status: string + } | null +} + function parseInstructionText(text: string): string[] { const trimmedText = text.trim() + if (!trimmedText) return [] - if (!trimmedText) { - return [] - } - - // Finner nummererte steg som 1., 2., 3. osv. - // Fungerer både med og uten mellomrom etter punktum. - // Nummereringen må starte på en ny linje eller etter - // et mellomrom. const parts = trimmedText.split( /(?:^|\s)(?=\d+\.\s*[A-Za-zÆØÅæøå])/, ) @@ -67,7 +59,6 @@ function parseInstructionText(text: string): string[] { .filter(Boolean) } -// Props som styrer hvilket steg brukeren er på type NewRecipePageProps = { currentStep: number setCurrentStep: (step: number) => void @@ -77,9 +68,9 @@ export default function NewRecipePage({ currentStep, setCurrentStep, }: NewRecipePageProps) { - // Lagrer informasjonen brukeren fyller inn + const navigate = useNavigate() + const [recipeData, setRecipeData] = useState({ - // Step 1 title: '', description: '', videoUrl: '', @@ -89,8 +80,6 @@ export default function NewRecipePage({ category: 'Middag', tags: [], rating: null, - - // Step 2 measurementSystem: 'metric', ingredients: [ { @@ -101,8 +90,6 @@ export default function NewRecipePage({ }, ], ingredientNote: '', - - // Step 3 instructionMode: 'steps', instructionText: '', steps: [ @@ -114,38 +101,28 @@ export default function NewRecipePage({ ], }) - // Kobler frontend til GraphQL-mutasjonen const [createRecipe, { loading, error }] = - useMutation(CREATE_RECIPE) + useMutation(CREATE_RECIPE) - // Holder styr på om bildet lastes opp const [uploading, setUploading] = useState(false) - const [publishError, setPublishError] = useState( - null, - ) + const [publishError, setPublishError] = useState(null) - // Funksjonen som kjøres når man publiserer oppskriften const handlePublish = async () => { if (loading || uploading) return setPublishError(null) setUploading(true) - // Gjør ingrediensene om til formatet backend forventer - const ingredients = recipeData.ingredients.map( - (ingredient, index) => ({ - name: ingredient.name, - amount: Number(ingredient.amount), - unit: ingredient.unit, - order: index + 1, - }), - ) + const ingredients = recipeData.ingredients.map((ingredient, index) => ({ + name: ingredient.name, + amount: Number(ingredient.amount), + unit: ingredient.unit, + order: index + 1, + })) - // Gjør fremgangsmåten om til formatet backend forventer let steps: { order: number; instruction: string }[] = [] if (recipeData.instructionMode === 'steps') { - // Brukeren har skrevet ett steg i hvert felt steps = recipeData.steps .filter((step) => step.text.trim() !== '') .map((step, index) => ({ @@ -153,56 +130,40 @@ export default function NewRecipePage({ instruction: step.text.trim(), })) } else { - // Brukeren har skrevet hele fremgangsmåten i ett tekstfelt - const parsedSteps = parseInstructionText( - recipeData.instructionText, - ) - + const parsedSteps = parseInstructionText(recipeData.instructionText) steps = parsedSteps.map((instruction, index) => ({ order: index + 1, instruction, })) } - // Sørger for at oppskriften har minst ett steg if (steps.length === 0) { - setPublishError( - 'Du må legge til minst ett steg i fremgangsmåten.', - ) + setPublishError('Du må legge til minst ett steg i fremgangsmåten.') setUploading(false) return } try { - // 1. Laster opp bildet til backend hvis brukeren har valgt et let imageUrl: string | null = null if (recipeData.imageFile) { const formData = new FormData() formData.append('image', recipeData.imageFile) - const uploadResponse = await fetch( - 'http://localhost:3001/upload', - { - method: 'POST', - body: formData, - }, - ) + const uploadResponse = await fetch('http://localhost:3001/upload', { + method: 'POST', + body: formData, + }) if (!uploadResponse.ok) { throw new Error('Kunne ikke laste opp bildet') } - // Backend returnerer adressen til bildet - const uploadResult: { imageUrl: string } = - await uploadResponse.json() - + const uploadResult: { imageUrl: string } = await uploadResponse.json() imageUrl = uploadResult.imageUrl - console.log('Bilde lastet opp:', imageUrl) } - // 2. Sender oppskriften og bilde-URL-en til GraphQL const result = await createRecipe({ variables: { input: { @@ -222,14 +183,21 @@ export default function NewRecipePage({ console.log('Oppskrift publisert:', result.data) console.log('Antall steg lagret:', steps.length) - alert('Oppskriften ble publisert!') + const recipeId = result.data?.createRecipe?.id + if (!recipeId) { + setPublishError( + 'Oppskriften ble lagret, men kunne ikke åpnes automatisk. Sjekk oppskriftslisten før du prøver igjen.', + ) + return + } + + // Navigerer til detaljsiden for oppskriften som nettopp ble opprettet. + // Forutsetter at appens rute er /recipes/:id. + navigate(`/recipes/${recipeId}`, { replace: true }) } catch (err) { console.error('Kunne ikke publisere oppskrift:', err) - setPublishError( - err instanceof Error - ? err.message - : 'Noe gikk galt under publiseringen.', + err instanceof Error ? err.message : 'Noe gikk galt under publiseringen.', ) } finally { setUploading(false) @@ -238,7 +206,6 @@ export default function NewRecipePage({ return (
- {/* STEP 1 - Generell informasjon */} {currentStep === 1 && ( )} - {/* STEP 2 - Ingredienser */} {currentStep === 2 && ( )} - {/* STEP 3 - Fremgangsmåte */} {currentStep === 3 && ( )} - {/* STEP 4 - Forhåndsvisning og publisering */} {currentStep === 4 && ( <> - Kunne ikke publisere oppskriften:{' '} - {publishError || error?.message} + Kunne ikke publisere oppskriften: {publishError || error?.message}

)} diff --git a/server/data/recipes.json b/server/data/recipes.json index 97a2d19..114b273 100644 --- a/server/data/recipes.json +++ b/server/data/recipes.json @@ -2235,5 +2235,610 @@ "Glutenfri", "Sunn" ] + }, + { + "id": "90c122d0-710b-4c00-a3bd-8f4e4ddf6f5e", + "authorId": "dev-user", + "title": "Pesto Pasta", + "description": "beste studentmiddagen vi har", + "prepTimeMinutes": 15, + "servings": 1, + "category": "middag", + "imageUrl": null, + "status": "PUBLISHED", + "createdAt": "2026-10-12T11:14:00.169Z", + "ingredients": [ + { + "name": "Pasta", + "amount": 200, + "unit": "g", + "order": 1 + }, + { + "name": "Pesto", + "amount": 0.5, + "unit": "stk", + "order": 2 + } + ], + "steps": [ + { + "order": 1, + "instruction": "Kok opp vann og ha i pasta når det koker. følg koketid på pakka" + }, + { + "order": 2, + "instruction": "Ha ferdigkokt pasta i en bolle og ha over et halvt glass pesto, bland sammen" + }, + { + "order": 3, + "instruction": "Spis yummy pasta." + } + ], + "tags": [ + "vegetar" + ] + }, + { + "id": "9c8ac230-3c27-4e7b-a1c6-80fdc2107693", + "authorId": "dev-user", + "title": "Havregrøt", + "description": "Det er mange som spiser havregrøt til frokost hver dag, og det er ganske lurt! For havregryn inneholder masse energi og næringsstoffer som er bra for oss. I tillegg er det mye fiber i havregryn, og man holder seg akkurat passe god og mett lenge!", + "prepTimeMinutes": 20, + "servings": 2, + "category": "Frokost", + "imageUrl": null, + "status": "PUBLISHED", + "createdAt": "2026-10-12T11:14:44.282Z", + "ingredients": [ + { + "name": "Vann", + "amount": 3, + "unit": "dl", + "order": 1 + }, + { + "name": "Lettmelk", + "amount": 1.5, + "unit": "dl", + "order": 2 + }, + { + "name": "Lettkokte havregryn", + "amount": 1.5, + "unit": "dl", + "order": 3 + }, + { + "name": "Salt", + "amount": 0.25, + "unit": "ts", + "order": 4 + } + ], + "steps": [ + { + "order": 1, + "instruction": "1.Bland vann, melk og havregryn i en gryte og kok opp mens du rører.\nBytt gjerne ut melk med vann om du vil ha en havregrøt uten melk.\n\n2.Senk varmen og la grøten småkoke i ca. 5 minutter, eller til den er passe tykk.\n\n3.Smak eventuelt til med salt.\nServer grøten med melk og kanel, eller med frisk frukt, nøtter eller syltetøy." + } + ], + "tags": [] + }, + { + "id": "90901622-64dd-4124-9355-de6c8d24db0b", + "authorId": "dev-user", + "title": "Stekt ris med kylling", + "description": "Stekt ris, eller fried rice, er en superenkel og rask middag du kan lage en travel hverdag. Her er en variant med stekt kylling og sprø grønnsaker. Mettende og godt!", + "prepTimeMinutes": 20, + "servings": 2, + "category": "Middag", + "imageUrl": null, + "status": "PUBLISHED", + "createdAt": "2026-10-13T21:58:46.339Z", + "ingredients": [ + { + "name": "kylling lårfilet", + "amount": 300, + "unit": "g", + "order": 1 + }, + { + "name": "stk. egg", + "amount": 2, + "unit": "stk", + "order": 2 + }, + { + "name": "stk. vårløk", + "amount": 2, + "unit": "stk", + "order": 3 + }, + { + "name": "stk. gulrot", + "amount": 1, + "unit": "stk", + "order": 4 + }, + { + "name": "kokt ris", + "amount": 5, + "unit": "dl", + "order": 5 + }, + { + "name": "soyasaus", + "amount": 2, + "unit": "ss", + "order": 6 + }, + { + "name": "sesamolje", + "amount": 1, + "unit": "ss", + "order": 7 + }, + { + "name": "stk. finhakket løk", + "amount": 0.5, + "unit": "stk", + "order": 8 + }, + { + "name": "båt finhakket hvitløk", + "amount": 1, + "unit": "stk", + "order": 9 + } + ], + "steps": [ + { + "order": 1, + "instruction": "\n1.Start med å preppe! Skjær kylling i mindre biter. Knekk egg i en skål og visp lett sammen. Finsnitt vårløk. Skrell og kutt gulrot i små terninger. \n\n2.Varm en wok eller stekepanne og ha i olje. Stek kylling til den er gyllen, og tilsett løk, hvitløk og gulrot. Skyv kylling og grønnsaker ut til sidene, og hell eggeblandingen i pannen og la stivne i noen sekunder. Tilsett ris og bland alt godt sammen. La det steke til det er gyllent.\n\nRisen som brukes bør være kald slik at ikke den stekte risen blir \"grøtete\". Bruk gjerne rester som har ligget i kjøleskapet over natten.\n\n3.Bland inn finsnittet vårløk, soyasaus og sesamolje, og la det surre i 2 minutter.\n\n4.Ha den stekte risen over i en stor bolle, og topp finsnittet vårløk og sesamfrø." + } + ], + "tags": [ + "Under 30" + ] + }, + { + "id": "259f76f7-e7a8-4c0d-a1f0-41e9231bce44", + "authorId": "dev-user", + "title": "Pasta bolognese", + "description": "Pasta bolognese er en enkel, varm og skikkelig deilig middag – perfekt både på travle hverdager og rolige helgekvelder. Skal du gjøre deg ekstra flid kan du også lage hjemmelaget pasta. ", + "prepTimeMinutes": 60, + "servings": 4, + "category": "Middag", + "imageUrl": null, + "status": "PUBLISHED", + "createdAt": "2026-10-14T10:57:56.534Z", + "ingredients": [ + { + "name": "stk. løk", + "amount": 1, + "unit": "stk", + "order": 1 + }, + { + "name": "stk. gulrot", + "amount": 1, + "unit": "stk", + "order": 2 + }, + { + "name": "stilk stilkselleri (stangselleri)", + "amount": 1, + "unit": "stk", + "order": 3 + }, + { + "name": "pancetta", + "amount": 150, + "unit": "g", + "order": 4 + }, + { + "name": "kjøttdeig", + "amount": 600, + "unit": "g", + "order": 5 + }, + { + "name": "nøytral olje til steking", + "amount": 4, + "unit": "ss", + "order": 6 + }, + { + "name": "tomatpuré", + "amount": 2, + "unit": "ss", + "order": 7 + }, + { + "name": "båt hvitløk", + "amount": 2, + "unit": "stk", + "order": 8 + }, + { + "name": "rødvin (kan sløyfes)", + "amount": 1, + "unit": "dl", + "order": 9 + }, + { + "name": "kjøttkraft", + "amount": 3, + "unit": "dl", + "order": 10 + }, + { + "name": "hermetiske tomater", + "amount": 400, + "unit": "g", + "order": 11 + }, + { + "name": "stk. laurbærblad", + "amount": 2, + "unit": "stk", + "order": 12 + }, + { + "name": "sukker", + "amount": 1, + "unit": "ts", + "order": 13 + }, + { + "name": "parmesan", + "amount": 40, + "unit": "g", + "order": 14 + }, + { + "name": "stilk frisk basilikum", + "amount": 4, + "unit": "stk", + "order": 15 + }, + { + "name": "tagliatelle ,pappardelle eller penne", + "amount": 400, + "unit": "g", + "order": 16 + } + ], + "steps": [ + { + "order": 1, + "instruction": "1.Kutt løk, gulrot og stilkselleri i små terninger. Dersom du har en hurtighakker kan du også kjøre dem raskt sammen til du har små jevne biter. \n\n2.Kutt pancetta eller bacon i små terninger. \n\n3.Stek kjøttdeigen i en varm panne med litt smør eller olje. Bruk en stor panne eller stek kjøttdeigen i omganger, slik at du har bedre kontroll på varmen, og at kjøttdeigen blir stekt og ikke kokt. Alternativt kan du også ha den stekte kjøttdeigen over i en gryte. \n\n4.Ha i biter av pancetta/bacon, og, stek videre noen minutter. Ha også i grønnsakene, og stek til løken er blank. \n\n5.Gjør litt plass i midten av panna, og ha i tomatpurè. Riv hvitløk, og ha den i panna. Fres noen minutter for å få frem smaken.\n\n6.Hell eventuelt i rødvin, og hell på kraft. Ha i hakkede tomater, og et par laurbærblad, og la det koke i minst 30-45 min, gjerne lengre slik at smakene kan utvikle seg. Jo lengre den får stå, jo bedre blir den. \n\n7.Smak til sausen med salt, pepper, og litt sukker for å runde av smaken. \nVil du ha litt ekstra dyp umamismak på retten. Ha i 1 ss knust tørket sopp, eller soppsoya.\n\n\n8.Du kan enten servere saus og pasta hver for seg, eller blande det sammen. Dersom du gjør det bruk litt av pastavannet for å binde sausen sammen slik at alt blir kremete og godt. Ha alt på et stort fat eller i porsjonsskåler, og topp med revet parmesan og frisk basilikum. \n\n9.Kok pasta etter anvisning på pakken i en kjele med lettsaltet vann til den er al dente. Dersom du skal vende pastaen i sausen, og ikke servere dem hver for seg kan det være lurt å koke den 1-2 minutter kortere enn anvisningen på pakken. " + } + ], + "tags": [ + "Studentvennlig", + "Matpakke", + "Italiensk" + ] + }, + { + "id": "f938af4e-5144-4d9c-ac9c-3bdc66fda9f3", + "authorId": "dev-user", + "title": "Pasta Alfredo", + "description": "En tradisjonell Pasta Alfredo lager du enkelt av få ingredienser; pasta, smør og parmesan og salt. Topp gjerne med litt hakket bladpersille og pepper for ekstra smak. Skikkelig rask og god kosemat.", + "prepTimeMinutes": 35, + "servings": 4, + "category": "Middag", + "imageUrl": "/uploads/b81e7879-bf28-4b29-b0cd-480874d24c44.png", + "status": "PUBLISHED", + "createdAt": "2026-10-14T15:08:49.728Z", + "ingredients": [ + { + "name": "tagliatelle , pappardelle eller annen båndpasta", + "amount": 400, + "unit": "g", + "order": 1 + }, + { + "name": "vann", + "amount": 1, + "unit": "l", + "order": 2 + }, + { + "name": "salt", + "amount": 0.5, + "unit": "dl", + "order": 3 + }, + { + "name": "smør", + "amount": 100, + "unit": "g", + "order": 4 + }, + { + "name": "revet parmesan", + "amount": 200, + "unit": "g", + "order": 5 + } + ], + "steps": [ + { + "order": 1, + "instruction": "1.Kok opp en stor kjele med vann og ha i salt. Ha pastaen i det kokende vannet. Hvis du bruker tørket pasta kan du koke den til det er 2-3 minutter igjen av koketiden, og hvis du bruker fersk pasta trenger du kun å koke den 30 sekunder- 1 minutt. Pastaen skal ikke være helt ferdig, da den også skal trekke litt videre i sausen.\n\n2.Mens pastaen koker, smelt smør i en dyp stekepanne på medium varme.\n\n3.Ha over et par spiseskjeer av pastavannet som står på kok. Skru ned temperaturen og la det småputre i ca. 1 minutt.\n\n4.Ha pastaen over i stekepannen. Hell en øse av det varme pastavannet over pastaen og rør i pannen til vannet er absorbert. Fortsett å spe med pastavann til pastaen er «al-dente». Vend inn parmesan og la osten smelte inn i pastaen. Ha eventuelt i litt mer pastavann til du får en kremet saus som klistrer seg til pastaen." + } + ], + "tags": [ + "Italiensk", + "Hverdagsmat", + "Kremet", + "Comfort food", + "Under 60 min" + ] + }, + { + "id": "698f25eb-e3cb-4398-9d32-56ad92b34140", + "authorId": "dev-user", + "title": "Barbie pasta ", + "description": "Denne sjokkrosa barbie-inspirerte pastaen har gått viralt på TikTok. Det finnes forskjellige versjoner av denne pastaen med rødbetesaus på TikTok nå, og her får du vår versjon med deilige bakte beter, fetaost og hvitløk. Enkelt, godt og skikkelig, skikkelig rosa!", + "prepTimeMinutes": 40, + "servings": 2, + "category": "Middag", + "imageUrl": "/uploads/eb01205e-c97f-428b-9f8c-1dc81e4c660e.png", + "status": "PUBLISHED", + "createdAt": "2026-10-14T16:03:10.274Z", + "ingredients": [ + { + "name": "rødbete", + "amount": 400, + "unit": "g", + "order": 1 + }, + { + "name": "stk. hvitløk", + "amount": 0.5, + "unit": "stk", + "order": 2 + }, + { + "name": "fetaost", + "amount": 200, + "unit": "g", + "order": 3 + }, + { + "name": "stilk frisk basilikum", + "amount": 2, + "unit": "stk", + "order": 4 + }, + { + "name": "nøytral olje", + "amount": 3, + "unit": "ss", + "order": 5 + }, + { + "name": "pasta , gjerne fullkornspasta", + "amount": 200, + "unit": "g", + "order": 6 + }, + { + "name": "ca. 2 dl pasta vann (fra koking av pasta)", + "amount": 0, + "unit": "stk", + "order": 7 + }, + { + "name": "ca. 1 dl crème fraîche orginal, 35 %", + "amount": 0, + "unit": "stk", + "order": 8 + }, + { + "name": "saft av 0,25 stk. sitron", + "amount": 0, + "unit": "stk", + "order": 9 + }, + { + "name": "kvernet pepper", + "amount": 0.25, + "unit": "ts", + "order": 10 + }, + { + "name": "revet parmesan", + "amount": 2, + "unit": "ss", + "order": 11 + } + ], + "steps": [ + { + "order": 1, + "instruction": "Skrell betene og del de i mindre biter. Ha over i en langpanne med bakepapir eller i en ildfast form." + }, + { + "order": 2, + "instruction": "Del hvitløk i to og riv basilikum grovt med hendene, og ha alt i forma. Legg en hel fetaost i midten av formen og dryss over litt olje og gjerne et pepperdryss. Stek i ovnen på 220 °C i ca. 30 minutter til betene har blitt møre." + }, + { + "order": 3, + "instruction": "Kok pasta etter anvisning på pakken i en kjele med lettsaltet vann. Pastaen er aller best når den er kokt al dente, altså myk, men fortsatt har litt tyggemotstand." + } + ], + "tags": [ + "Italiensk", + "Under 60 min", + "Helgekos", + "Vegetar" + ] + }, + { + "id": "e9bc0eb0-0fdb-47b6-bee2-fd23756c0eb7", + "authorId": "dev-user", + "title": "Bakt gulrotsuppe", + "description": "Er du glad i gulrot? Da vil denne bakte gulrotsuppen garantert falle i smak. Når grønnsakene får tid i ovnen blir de møre, gylne og fulle av karamellisert sødme. Gulrøttene får selskap av pastinakk, løk og en hel bakt hvitløk som blir silkemyk og mild. Varme kryddere, som spisskummen, korianderfrø og nellik gir suppen en varm, krydret dybde. Server den rykende varm med sprø toast.", + "prepTimeMinutes": 40, + "servings": 4, + "category": "Middag", + "imageUrl": "/uploads/8d9ea410-a00f-4cad-95b9-4ab496ac4900.png", + "status": "PUBLISHED", + "createdAt": "2026-10-15T00:32:54.576Z", + "ingredients": [ + { + "name": "gulrot", + "amount": 600, + "unit": "g", + "order": 1 + }, + { + "name": "pastinakk", + "amount": 150, + "unit": "g", + "order": 2 + }, + { + "name": "sellerirot eller persillerot", + "amount": 150, + "unit": "g", + "order": 3 + }, + { + "name": "stk. løk", + "amount": 1, + "unit": "stk", + "order": 4 + }, + { + "name": "stk. hvitløk (bakes med skall)", + "amount": 1, + "unit": "stk", + "order": 5 + }, + { + "name": "stk. rød chili", + "amount": 1, + "unit": "stk", + "order": 6 + }, + { + "name": "nøytral olje", + "amount": 3, + "unit": "ss", + "order": 7 + }, + { + "name": "salt", + "amount": 1, + "unit": "ts", + "order": 8 + }, + { + "name": "malt spisskummen", + "amount": 1, + "unit": "ts", + "order": 9 + }, + { + "name": "malt korianderfrø", + "amount": 1, + "unit": "ts", + "order": 10 + }, + { + "name": "stk. hel nellik", + "amount": 1, + "unit": "stk", + "order": 11 + }, + { + "name": "frisk timian eller andre urter (valgfritt)", + "amount": 2, + "unit": "ss", + "order": 12 + }, + { + "name": "vann", + "amount": 6, + "unit": "dl", + "order": 13 + }, + { + "name": "kyllingkraft , grønnsakkraft eller det du foretrekker", + "amount": 4, + "unit": "dl", + "order": 14 + }, + { + "name": "matfløte", + "amount": 2, + "unit": "dl", + "order": 15 + }, + { + "name": "skive grovt brød", + "amount": 8, + "unit": "stk", + "order": 16 + }, + { + "name": "ca. 180 g hvitost", + "amount": 0, + "unit": "stk", + "order": 17 + }, + { + "name": "skive kokt skinke", + "amount": 4, + "unit": "stk", + "order": 18 + }, + { + "name": "dijonsennep", + "amount": 2, + "unit": "ts", + "order": 19 + }, + { + "name": "smør , til steking", + "amount": 3, + "unit": "ss", + "order": 20 + } + ], + "steps": [ + { + "order": 1, + "instruction": "Skrell gulrøtter, pastinakk og sellerirot eller persillerot og og del dem i mindre biter. Del løk i båter, hvitløk og chili i to. Fjern frø fra chilien hvis du ønsker en mildere suppe.\nEn suppe er en perfekt anledning til å bruke opp grønnsaksrester du har liggende, for eksempel søtpotet eller paprika. Bak dem sammen med gulrøttene for ekstra smak og mindre matsvinn. Litt slappe grønnsaker gjør ingenting. Ovnsbakingen gir dem ny glød og masse smak." + }, + { + "order": 2, + "instruction": "Legg grønnsaker, hvitløk, chili, olje, salt, krydder og urter på et bakepapirkledd stekebrett. Vend godt så alt dekkes. Bak i ovn på 160 °C (varmluft) i 30–40 minutter til grønnsakene er møre og har fått gyllen farge. Vend én gang underveis." + }, + { + "order": 3, + "instruction": "Mens grønnsakene bakes, kan du lage toast. Legg ost og skinke mellom to brødskiver. Smør et tynt lag dijonsennep utenpå. Stek i panne med smør på middels varme til toasten er gyllen på begge sider. Bruk gjerne lokk for å få osten til å smelte.\nÅ ha sennepen på utsiden av toasten er et lite hack. Når den stekes i pannen, blir smaken mildere og søtere. Hadde den vært inni, ville den vært skarpere og råere." + }, + { + "order": 4, + "instruction": "Ha de varme grønnsakene i en kjele. Fjern eventuelle små brente biter for å unngå bitterhet. Klem hvitløken ut av skallet og tilsett. Hell i litt av væsken og kjør glatt med stavmikser eller blender. Spe med resten av væsken til ønsket konsistens og varm opp. Rør inn fløte og smak til med salt og pepper." + }, + { + "order": 5, + "instruction": "Øs suppen i skåler og legg toast ved siden av. Perfekt å dyppe!\nTopp suppen med finhakket gressløk, koriander eller persille for friskhet. En liten drizzle god olivenolje eller en klatt rømme gir ekstra fylde." + } + ], + "tags": [ + "Comfort food", + "Under 60 min", + "Vegetar", + "Grønnsaksrik" + ] } ] \ No newline at end of file diff --git a/server/prisma/exportRecipes.ts b/server/prisma/exportRecipes.ts new file mode 100644 index 0000000..ce9fa64 --- /dev/null +++ b/server/prisma/exportRecipes.ts @@ -0,0 +1,127 @@ + +import 'dotenv/config' +import { PrismaClient } from '@prisma/client' +import { readFileSync, writeFileSync } from 'fs' +import { join } from 'path' + +const prisma = new PrismaClient() + +type SeedRecipe = { + id: string + authorId: string + title: string + description: string + prepTimeMinutes: number + servings: number + category: string + imageUrl: string | null + status: 'DRAFT' | 'PUBLISHED' + createdAt: string + ingredients: { + name: string + amount: number + unit: string + order: number + }[] + steps: { + order: number + instruction: string + }[] + tags: string[] +} + +async function main() { + const filePath = join(__dirname, '../data/recipes.json') + + // Leser eksisterende oppskrifter fra JSON-filen + const existingRecipes: SeedRecipe[] = JSON.parse( + readFileSync(filePath, 'utf-8'), + ) + + // Henter publiserte oppskrifter, men hopper over seed-oppskrifter + const databaseRecipes = await prisma.recipe.findMany({ + where: { + status: 'PUBLISHED', + NOT: { + id: { + startsWith: 'seed-', + }, + }, + }, + include: { + ingredients: true, + steps: true, + tags: true, + }, + }) + + // Konverterer databaseoppskriftene til JSON-formatet + const exportedRecipes: SeedRecipe[] = databaseRecipes.map( + (recipe) => ({ + id: recipe.id, + authorId: recipe.authorId, + title: recipe.title, + description: recipe.description, + prepTimeMinutes: recipe.prepTimeMinutes, + servings: recipe.servings, + category: recipe.category, + imageUrl: recipe.imageUrl, + status: recipe.status, + createdAt: recipe.createdAt.toISOString(), + + ingredients: recipe.ingredients + .sort((a, b) => a.order - b.order) + .map((ingredient) => ({ + name: ingredient.name, + amount: ingredient.amount, + unit: ingredient.unit, + order: ingredient.order, + })), + + steps: recipe.steps + .sort((a, b) => a.order - b.order) + .map((step) => ({ + order: step.order, + instruction: step.instruction, + })), + + tags: recipe.tags.map((tag) => tag.name), + }), + ) + + // Beholder eksisterende oppskrifter i JSON-filen + const recipesById = new Map() + + for (const recipe of existingRecipes) { + recipesById.set(recipe.id, recipe) + } + + // Legger kun til oppskrifter som ikke finnes fra før + for (const recipe of exportedRecipes) { + if (!recipesById.has(recipe.id)) { + recipesById.set(recipe.id, recipe) + } + } + + const combinedRecipes = [...recipesById.values()] + + writeFileSync( + filePath, + JSON.stringify(combinedRecipes, null, 2), + 'utf-8', + ) + + console.log( + `Fant ${exportedRecipes.length} publiserte oppskrifter i databasen. ` + + `Totalt ${combinedRecipes.length} oppskrifter i recipes.json.`, + ) +} + +main() + .catch((error) => { + console.error(error) + process.exitCode = 1 + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/server/uploads/8d9ea410-a00f-4cad-95b9-4ab496ac4900.png b/server/uploads/8d9ea410-a00f-4cad-95b9-4ab496ac4900.png new file mode 100644 index 0000000..ba38f97 Binary files /dev/null and b/server/uploads/8d9ea410-a00f-4cad-95b9-4ab496ac4900.png differ