diff --git a/README.md b/README.md index 58f1a8a66..e0b5dc706 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # js-project-recipe-library +[https://app.netlify.com/projects/jsprojectrecipelibrary/overview](https://jsprojectrecipelibrary.netlify.app/) +https://github.com/emilfloren96/js-project-recipe-library/commit/3186eec6628fcdd8ef15e5c07345b2b399e00170#r168031360 diff --git a/index.html b/index.html new file mode 100644 index 000000000..a15f9f475 --- /dev/null +++ b/index.html @@ -0,0 +1,39 @@ + + + + + + Recipe Website + + + +

Recipe Library

+ + + + + + + +
+ +
+ + + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 000000000..b35235b64 --- /dev/null +++ b/script.js @@ -0,0 +1,205 @@ +const API_KEY = "c4791e847ed74bd093c0b702927d5d37"; +const url = `https://api.spoonacular.com/recipes/complexSearch?number=10&addRecipeInformation=true&apiKey="c4791e847ed74bd093c0b702927d5d37"&cuisine=Asian,Italian,Mexican,Mediterranean,Middle Eastern,European`; +const container = document.getElementById("container"); + +// switch between local and api +let useAPI = false; + +let currentRecipes = []; + +const localRecipes = [ + { + title: "Test Carbonara", + image: "https://via.placeholder.com/300x200?text=Carbonara", + cuisines: ["Italian"], + readyInMinutes: 25, + summary: "A simple test version of the Italian classic pasta dish." + }, + { + title: "Fake Fried Chicken", + image: "https://via.placeholder.com/300x200?text=Chicken", + cuisines: ["American"], + readyInMinutes: 45, + summary: "Crispy and juicy fried chicken – test edition." + }, + { + title: "Quick Sushi", + image: "https://via.placeholder.com/300x200?text=Sushi", + cuisines: ["Chinese"], + readyInMinutes: 15, + summary: "Super fast sushi for testing sorting and filtering." + } +]; + +const fetchRecipes = async (cuisine = "") => { + container.innerHTML = "

Loading...

"; + + let url = `https://api.spoonacular.com/recipes/complexSearch?number=4&addRecipeInformation=true&apiKey=c4791e847ed74bd093c0b702927d5d37`; + + if (cuisine && cuisine !== "All") { + url += `&cuisine=${cuisine}`; + } + + try { + const res = await fetch(url); + const data = await res.json(); + + currentRecipes = data.results; + renderRecipes(data.results); + } catch (error) { + container.innerHTML = "

Failed to fetch recipes.

"; + console.error(error); + } + }; + + + +const renderRecipes = (recipes) => { + container.innerHTML = ""; + +recipes.forEach(recipe => { + container.innerHTML += ` +
+

${recipe.title}

+ ${recipe.title} +
+

${recipe.cuisines.join(", ")}

+

${recipe.readyInMinutes} min

+
+ `; + }); +}; + +// Anropa direkt för att visa "alla" +// fetchRecipes(); +const loadLocalRecipes = () => { + currentRecipes = localRecipes; + renderRecipes(currentRecipes); +}; + +loadLocalRecipes(); + +// Koppla till cuisine-knappar +const cuisineButtons = document.querySelectorAll("#originalCuisineSorter .button"); + +cuisineButtons.forEach(button => { + button.addEventListener("click", () => { + const cuisine = button.dataset.cuisine; + + if (useAPI) { + fetchRecipes(cuisine); // hämta från API + } else { + if (cuisine === "All") { + currentRecipes = localRecipes; + } else { + currentRecipes = localRecipes.filter(recipe => + recipe.cuisines.includes(cuisine) + ); + } + renderRecipes(currentRecipes); + } + }); +}); + + +// Time +const ascBtn = document.getElementById("ascBtn"); +ascBtn.addEventListener("click", () => { + const sorted = [...currentRecipes].sort((a, b) => a.readyInMinutes - b.readyInMinutes); + renderRecipes(sorted); +}); + +const dscBtn = document.getElementById("dscBtn"); + +dscBtn.addEventListener("click", () => { + const sorted = [...currentRecipes].sort((a, b) => b.readyInMinutes - a.readyInMinutes); + + renderRecipes(sorted); +}); + + +// Switch betwween api and local +const toggleBtn = document.getElementById("toggleBtn"); + +toggleBtn.addEventListener("click", () => { + useAPI = !useAPI; + + toggleBtn.textContent = useAPI ? "Use Local" : "Use API"; + + if (useAPI) { + fetchRecipes(); // Hämta från API + } else { + currentRecipes = localRecipes; // Använd lokala + renderRecipes(currentRecipes); + } +}); + + +// Search input +const searchInput = document.getElementById("searchInput"); + +searchInput.addEventListener("input", async (e) => { + const searchTerm = e.target.value.trim().toLowerCase(); + + if (searchTerm === "") { + if (useAPI) { + await fetchRecipes(); // hämta alla från API igen + } else { + renderRecipes(localRecipes); + } + return; + } + + if (useAPI) { + try { + const url = `https://api.spoonacular.com/recipes/complexSearch?query=${encodeURIComponent( + searchTerm + )}&number=10&addRecipeInformation=true&apiKey=${API_KEY}`; + + container.innerHTML = "

Searching...

"; + const res = await fetch(url); + const data = await res.json(); + + if (!data.results || data.results.length === 0) { + container.innerHTML = `

No API results for "${searchTerm}".

`; + return; + } + + // Filtrera resultaten manuellt på titel och ingredienser + const filtered = data.results.filter(recipe => { + const titleMatch = recipe.title.toLowerCase().includes(searchTerm); + const ingredientMatch = recipe.extendedIngredients?.some(ing => + ing.name.toLowerCase().includes(searchTerm) + ); + return titleMatch || ingredientMatch; + }); + + currentRecipes = filtered; + if (filtered.length > 0) { + renderRecipes(filtered); + } else { + container.innerHTML = `

No results found for "${searchTerm}".

`; + } + } catch (error) { + console.error("API Search error:", error); + container.innerHTML = "

Failed to fetch from API.

"; + } + } else { + // Lokal sökning + const filtered = localRecipes.filter(recipe => { + const titleMatch = recipe.title.toLowerCase().includes(searchTerm); + const ingredientMatch = recipe.ingredients?.some(ing => + ing.toLowerCase().includes(searchTerm) + ); + return titleMatch || ingredientMatch; + }); + + currentRecipes = filtered; + + if (filtered.length > 0) { + renderRecipes(filtered); + } else { + container.innerHTML = `

No local results found for "${searchTerm}".

`; + } + } +}); \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 000000000..4212ddce5 --- /dev/null +++ b/style.css @@ -0,0 +1,110 @@ +body { + background-color: #f7f8ff; +} + +h1 { + font-size: 64px; + font-style: bold; + color: #0018A4; +} + +#searchInput { + padding: 12px 20px; + border: 2px solid #ccc; + border-radius: 50px; + font-size: 16px; + transition: all 0.3s ease; + width: 250px; + outline: none; + margin-top: 10px; +} + + +h4 { + display: flex; + align-items: center; +} + +.navBar { + display: flex; +} + +.navBox { + padding: 1%; +} + +.button { +width: fit-content; +Height: 40px; +border-radius: 50px; +Padding: 8px 16px 8px 16px; +Gap: 10px; +} + +.button[data-cuisine="All"] { + background-color: #0018A4; + color: white; +} + +.button[data-cuisine="Italian"] +{ + background-color: #CCFFE2; + color: #0018A4; +} + +.button[data-cuisine="American"] +{ + background-color: #CCFFE2; + color: #0018A4; +} + +.button[data-cuisine="Chinese"] +{ + background-color: #CCFFE2; + color: #0018A4; +} + +.button.descending { + background-color: #FF6589; +} + +.button.ascending, +.button.source { + background-color: #FFECEA; + color: #0018A4; +} + +.button:hover { + text-decoration: underline; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + margin: 20px; + padding: 20px; +} + +.card { + width: 200px; + height: 100%; + border: 1px solid gray; + border-radius: 10px; + padding: 10px; + margin: 10px; +} + +.card img { + width: 100%; + height: 200px; +} + +@media (min-width: 668px) { + .container{ + display: flex; + flex-direction: row; + justify-content: start; + align-items: self-start; + } +} \ No newline at end of file