Skip to content
Open

test #42

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
39 changes: 39 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Recipe Website</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Recipe Library</h1>

<button class="button source" id="toggleBtn">Use API</button>

<input type="text" id="searchInput" placeholder="Search Recipe Here">

<nav class="navBar">
<div class="navBox" name="original-cuisine" id="originalCuisineSorter">
<h3>Filter on kitchen</h3>
<button class="button" data-cuisine="All">All</button>
<button class="button" data-cuisine="Italian">Italy</button>
<button class="button" data-cuisine="American">USA</button>
<button class="button" data-cuisine="Chinese">China</button>
</div>
<div class="navBox">
<h3>Sort on time</h3>
<button class="button descending" id="dscBtn">Descending</button>
<button class="button ascending" id="ascBtn">Ascending</button>
</div>
</nav>

<section class="container" id="container">
<!-- Info in javascript -->
</section>


<script src="script.js"></script>

</body>
</html>
205 changes: 205 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -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 = "<p>Loading...</p>";

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 = "<p>Failed to fetch recipes.</p>";
console.error(error);
}
};



const renderRecipes = (recipes) => {
container.innerHTML = "";

recipes.forEach(recipe => {
container.innerHTML += `
<div class="card">
<h3>${recipe.title}</h3>
<img src="${recipe.image}" alt="${recipe.title}" />
<hr>
<h4>${recipe.cuisines.join(", ")}</h4>
<h4>${recipe.readyInMinutes} min</h4>
</div>
`;
});
};

// 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 = "<p>Searching...</p>";
const res = await fetch(url);
const data = await res.json();

if (!data.results || data.results.length === 0) {
container.innerHTML = `<p>No API results for "${searchTerm}".</p>`;
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 = `<p>No results found for "${searchTerm}".</p>`;
}
} catch (error) {
console.error("API Search error:", error);
container.innerHTML = "<p>Failed to fetch from API.</p>";
}
} 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 = `<p>No local results found for "${searchTerm}".</p>`;
}
}
});
110 changes: 110 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
@@ -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;
}
}