diff --git a/frontend/src/api/recipePoint.ts b/frontend/src/api/recipePoint.ts index 190a600..44f5779 100644 --- a/frontend/src/api/recipePoint.ts +++ b/frontend/src/api/recipePoint.ts @@ -1,10 +1,11 @@ import type { Recipe } from "../types/recipe" +import { get, postJson, putJson } from "./utils/requests"; /** * Util for handling the recipe api */ -// reate base url from .env file +// read base url from .env file const BASE_URL = import.meta.env.VITE_API_BASE; /** @@ -18,10 +19,7 @@ const RECIPE_URL = `${BASE_URL}/recipe` * @returns A single recipe */ export async function fetchRecipe(id: string): Promise { - const res = await fetch(`${RECIPE_URL}/${id}`) - if (!res.ok) { - throw new Error(`Failed to fetch recipe with id ${id}`) - } + const res = await get(`${RECIPE_URL}/${id}`) return res.json() } @@ -36,12 +34,8 @@ export async function fetchRecipeList(searchString : string): Promise if(searchString && searchString !== ""){ url +="?search=" + searchString; } - console.log("calling url", url) - const res = await fetch(url) - if (!res.ok) { - throw new Error(`Failed to fetch recipe list`) - } - return res.json() + const res = await get(url); + return res.json(); } /** @@ -50,15 +44,8 @@ export async function fetchRecipeList(searchString : string): Promise * @returns Saved recipe */ export async function createRecipe(recipe: Recipe): Promise { - const res = await fetch(RECIPE_URL, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(recipe), - }) - if (!res.ok) { - throw new Error("Failed to create recipe") - } - return res.json() + const res = await postJson(RECIPE_URL, JSON.stringify(recipe)); + return res.json(); } /** @@ -67,13 +54,6 @@ export async function createRecipe(recipe: Recipe): Promise { * @returns Saved recipe */ export async function updateRecipe(recipe: Recipe): Promise { - const res = await fetch(`${RECIPE_URL}/${recipe.id}`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(recipe), - }) - if (!res.ok) { - throw new Error(`Failed to update recipe with id ${recipe.id}`) - } - return res.json() + const res = await putJson(`${RECIPE_URL}/${recipe.id}`, JSON.stringify(recipe)); + return res.json(); } diff --git a/frontend/src/api/utils/headers.ts b/frontend/src/api/utils/headers.ts new file mode 100644 index 0000000..4a2fdb9 --- /dev/null +++ b/frontend/src/api/utils/headers.ts @@ -0,0 +1,19 @@ +export function setContentTypeHeaderJson(headers: Headers){ + return headers.set("Content-Type", "application/json"); +} + +export function setAuthHeader(headers: Headers){ + // retrieve session data from browser storage + const sessionStr = localStorage.getItem("session"); + let loginResponse = null; + if(sessionStr){ + loginResponse = JSON.parse(sessionStr); + } + // add token if possible + if(loginResponse && loginResponse.token){ + headers.set("Authorization", "Bearer " + loginResponse.token) + } else { + console.log("No access token!") + } + return headers; +} \ No newline at end of file diff --git a/frontend/src/api/utils/requests.ts b/frontend/src/api/utils/requests.ts new file mode 100644 index 0000000..2a3156f --- /dev/null +++ b/frontend/src/api/utils/requests.ts @@ -0,0 +1,42 @@ +import { setAuthHeader, setContentTypeHeaderJson } from "./headers"; + +export async function get(url: string) : Promise{ + const requestHeaders = new Headers(); + setAuthHeader(requestHeaders); + console.log("GET to " + url); + const response = await fetch(url, { + method: "GET", + headers: requestHeaders + }) + if(!response.ok){ + throw new Error("GET to " + url + "failed!") + } + return response; +} + +export async function postJson(url: string, requestBody: string, logBody = true) : Promise{ + console.log("POST to " + url + (logBody) ? "with body " + requestBody : ""); + return persistJson(url, requestBody, "POST"); +} + +export async function putJson(url: string, requestBody: string, logBody = true) : Promise{ + console.log("PUT to " + url + (logBody) ? "with body " + requestBody : ""); + return persistJson(url, requestBody, "PUT"); +} + +async function persistJson(url: string, requestBody: string, requestMethod: string) : Promise{ + const requestHeaders = new Headers(); + setContentTypeHeaderJson(requestHeaders); + setAuthHeader(requestHeaders); + const response = await fetch(url, { + method: requestMethod, + headers: requestHeaders, + body: requestBody, + }); + if(!response.ok){ + throw new Error(requestMethod + " to " + url + "failed!") + } + return response; +} + + diff --git a/frontend/src/components/basics/ButtonLink.tsx b/frontend/src/components/basics/ButtonLink.tsx index e69de29..6ed591f 100644 --- a/frontend/src/components/basics/ButtonLink.tsx +++ b/frontend/src/components/basics/ButtonLink.tsx @@ -0,0 +1,29 @@ +import { Link, type LinkProps } from "react-router-dom" +import { ButtonType } from "./Button" + +type ButtonLinkProps = LinkProps & { + text: string + buttonType?: ButtonType + className?: string +} + +/** + * Link component having the same stile as the button + */ +export default function ButtonLink({ + to, + text, + buttonType = ButtonType.DefaultButton, + className = "", + ...props +}: ButtonLinkProps) { + return ( + + {text} + + ) +} diff --git a/frontend/src/components/recipes/RecipeDetailPage.tsx b/frontend/src/components/recipes/RecipeDetailPage.tsx index 8e54bc2..83fdcc9 100644 --- a/frontend/src/components/recipes/RecipeDetailPage.tsx +++ b/frontend/src/components/recipes/RecipeDetailPage.tsx @@ -135,7 +135,6 @@ export default function RecipeDetailPage() { text="Zurueck" /> - ) diff --git a/frontend/src/mock_data/recipes.ts b/frontend/src/mock_data/recipes.ts deleted file mode 100644 index 350ab67..0000000 --- a/frontend/src/mock_data/recipes.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Recipe } from "../types/recipe" - -/** - * Mock data set with some sample recipes. - * In a real application, this would be fetched from a backend. - */ -export const recipes: Recipe[] = [ - { - id: "1", - title: "Spaghetti Bolognese", - servings: { amount: 1, unit: "Person"}, - ingredientGroupList: [ - { - ingredientList: [ - { name: "Spaghetti", amount: 200, unit: "g" }, - { name: "Ground Beef", amount: 300, unit: "g" }, - { name: "Tomato Sauce", amount: 400, unit: "ml" } - ] - } - ], - instructions: "Cook pasta. Prepare sauce. Mix together. Serve hot.", - //imageUrl: "https://source.unsplash.com/400x300/?spaghetti" -}, -{ - id: "2", - title: "Spaghetti Carbonara", - servings: { amount: 4, unit: "Persons"}, - ingredientGroupList: [ - { - ingredientList:[ - { name: "Spaghetti", amount: 500, unit: "g" }, - { name: "Bacon", amount: 150, unit: "g" }, - { name: "Cream", amount: 200, unit: "ml" }, - { name: "Onion", amount: 1}, - { name: "Parmesan cheese", amount: 200, unit: "g"}, - { name: "Olives", amount: 100, unit: "g"} - ] - } - ], - instructions: "Cook pasta. Prepare sauce. Mix together. Serve hot.", - //imageUrl: "https://source.unsplash.com/400x300/?spaghetti" -}, -{ id: "3", - title: "Apfelkuchen Edeltrud", - servings: { amount: 1, unit: "Kuchen"}, - ingredientGroupList:[ - { - title: "Fuer den Teig", - ingredientList: [ - { name: "Mehl", amount: 400, unit: "g" } - ] - }, - { - title: "Fuer die Fuellung", - ingredientList:[ - {name: "Aepfel", amount: 4}, - {name: "Rosinen", amount: 1, unit: "Hand voll"} - ] - } - ], - instructions: "Einen Muerbteig von 400 g Mehl zubereiten" -} -]