diff --git a/README.md b/README.md
index 58f1a8a6..4e8ea60e 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
# js-project-recipe-library
+
+https://js-recipe-libr.netlify.app/
\ No newline at end of file
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 00000000..af436893
Binary files /dev/null and b/favicon.ico differ
diff --git a/images/carbonara.jpg b/images/carbonara.jpg
new file mode 100644
index 00000000..b9725778
Binary files /dev/null and b/images/carbonara.jpg differ
diff --git a/images/placeholder.png b/images/placeholder.png
new file mode 100644
index 00000000..d3e31376
Binary files /dev/null and b/images/placeholder.png differ
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..ca0ba9d1
--- /dev/null
+++ b/index.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ JS Recipe Library
+
+
+
+
+
+ Recipe Library
+
+
+
+
+
+
Filter on kitchen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Sort on price per serving
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 00000000..f41d54d2
--- /dev/null
+++ b/script.js
@@ -0,0 +1,373 @@
+/* WEEK 1 JS - selection messages displayed in an Error Section
+const allFilter = document.getElementById("all-filter");
+allFilter.onclick = () => {
+ clickButton(allFilter)
+};
+const italyFilter = document.getElementById("italy-filter");
+italyFilter.onclick = () => {
+ clickButton(italyFilter)
+};
+const usaFilter = document.getElementById("usa-filter");
+usaFilter.onclick = () => {
+ clickButton(usaFilter)
+};
+const chinaFilter = document.getElementById("china-filter");
+chinaFilter.onclick = () => {
+ clickButton(chinaFilter)
+};
+
+const clickButton = (button) => {
+ const text = button.textContent;
+ if (text == "USA") {
+ errorSection.innerHTML ="You have chosen american cuisine";
+ } else if (text == "China") {
+ errorSection.innerHTML ="You have chosen chineese cuisine";
+ } else if (text == "Italy") {
+ errorSection.innerHTML ="You have chosen italian cuisine";
+ } else {
+ errorSection.innerHTML ="You have chosen recipies from all cuisines";
+ }
+};
+*/
+
+
+const recipesSection = document.getElementById("recipeSection");
+const errorSection = document.getElementById("errorMessage");
+
+/** W1&2 - defined buttons for selection from a given Array
+const randomButton = document.getElementById("random")
+const allButton = document.getElementById("all")
+
+const italyButton = document.getElementById("lunch")
+const usaButton = document.getElementById("dinner")
+const chinaButton = document.getElementById("dessert")
+const swedenButton = document.getElementById("drink")
+const drinkButton = document.getElementById("snack")
+*/
+
+const allFilters = ["lunch", "soup", "dessert", "drink", "snack", "salad", "breakfast", "side dish"];
+const allFilterButtons = ["random", "all", ...allFilters]
+ .map((text) => { return document.getElementById(text)});
+
+
+//check if the button all is active
+//const allActive = allButton.classList.contains("active")
+// set the button all to inactive
+//allButton.classList.remove("active")
+// set the button all to active
+//allButton.classList.add("active")
+
+const sortByTimeAscButton = document.getElementById("highToLow")
+const sortByTimeDescButton = document.getElementById("lowToHigh")
+
+const allSortButtons = [sortByTimeAscButton, sortByTimeDescButton];
+
+
+// fetch recipes from GET https://api.spoonacular.com/recipes/random
+const apiRecipes = async () => {
+ try {
+ const response = await fetch( "https://api.spoonacular.com/recipes/random?number=100&apiKey=aa5ed444617a4f9bbabeb0e831c3f0ca");
+ if (!response.ok) {
+ throw new Error();
+ }
+ const data = await response.json();
+ return data.recipes;
+ } catch (error) {
+ errorSection.innerHTML = "An error occured while fetching the recipes. Please try again later.";
+ return [];
+ }
+}
+
+let recipes = [];
+//fill in the recipes array with the data from the API (only picked fields needed for button selection)
+//all the "Filter on kitchen" butttons are contained within types (dish types)
+apiRecipes().then((data) => {
+ recipes = data.map((recipe) => {
+ return {
+ title: recipe.title,
+ types: recipe.dishTypes.filter( element => allFilters.includes(element)),
+ image: recipe.image,
+ readyInMinutes: recipe.readyInMinutes,
+ servings: recipe.servings,
+ diets: recipe.diets,
+ cuisine: recipe.cuisines,
+ ingredients: recipe.extendedIngredients.map((ingredient) => ingredient.original),
+ pricePerServing: recipe.pricePerServing,
+ popularity: recipe.aggregateLikes,
+ url: recipe.spoonacularSourceUrl,
+ };
+ });
+ //debugging
+ //console.log(data)
+ //console.log(recipes)
+});
+
+
+/*//Array with recipes - week 1 & 2 of the project
+const recipes = [
+ {
+ id: 1,
+ title: "Vegan Lentil Soup",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 30,
+ servings: 4,
+ diets: ["vegan"],
+ cuisine: "Italian",
+ ingredients: [
+ "red lentils",
+ "carrots",
+ "onion",
+ "garlic",
+ "tomato paste",
+ "cumin",
+ "paprika",
+ "vegetable broth",
+ "olive oil",
+ "salt",
+ ],
+ pricePerServing: 2.5,
+ popularity: 85,
+ },
+ {
+ id: 2,
+ title: "Vegetarian Pesto Pasta",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 25,
+ servings: 2,
+ diets: ["vegetarian"],
+ cuisine: "Italian",
+ ingredients: [
+ "pasta",
+ "basil",
+ "parmesan cheese",
+ "garlic",
+ "pine nuts",
+ "olive oil",
+ "salt",
+ "black pepper",
+ ],
+ pricePerServing: 3.0,
+ popularity: 92,
+ },
+ {
+ id: 3,
+ title: "Gluten-Free Chicken Stir-Fry",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 20,
+ servings: 3,
+ diets: ["gluten-free"],
+ cuisine: "Asian",
+ ingredients: [
+ "chicken breast",
+ "broccoli",
+ "bell pepper",
+ "carrot",
+ "soy sauce (gluten-free)",
+ "ginger",
+ "garlic",
+ "sesame oil",
+ "cornstarch",
+ "green onion",
+ "sesame seeds",
+ "rice",
+ ],
+ pricePerServing: 4.0,
+ popularity: 78,
+ },
+ {
+ id: 4,
+ title: "Dairy-Free Tacos",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 15,
+ servings: 2,
+ diets: ["dairy-free"],
+ cuisine: "American",
+ ingredients: [
+ "corn tortillas",
+ "ground beef",
+ "taco seasoning",
+ "lettuce",
+ "tomato",
+ "avocado",
+ ],
+ pricePerServing: 2.8,
+ popularity: 88,
+ },
+ {
+ id: 5,
+ title: "Middle Eastern Hummus",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 10,
+ servings: 4,
+ diets: ["vegan", "gluten-free"],
+ cuisine: "Middle Eastern",
+ ingredients: ["chickpeas", "tahini", "garlic", "lemon juice", "olive oil"],
+ pricePerServing: 1.5,
+ popularity: 95,
+ },
+ {
+ id: 6,
+ title: "Quick Avocado Toast",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 5,
+ servings: 1,
+ diets: ["vegan"],
+ cuisine: "Italian",
+ ingredients: ["bread", "avocado", "lemon juice", "salt"],
+ pricePerServing: 2.0,
+ popularity: 90,
+ },
+ {
+ id: 7,
+ title: "Beef Stew",
+ image: "images/carbonara.jpg",
+ readyInMinutes: 90,
+ servings: 5,
+ diets: [],
+ cuisine: "European",
+ ingredients: [
+ "beef chunks",
+ "potatoes",
+ "carrots",
+ "onion",
+ "garlic",
+ "tomato paste",
+ "beef broth",
+ "red wine",
+ "bay leaves",
+ "thyme",
+ "salt",
+ "black pepper",
+ "butter",
+ "flour",
+ "celery",
+ "mushrooms",
+ ],
+ pricePerServing: 5.5,
+ popularity: 80,
+ },
+];*/
+
+ //Function to filter, sort and display recepies based on the button clicked
+const displayRecipes = (button) => {
+ //clear recepie section as well as any previous error messages
+
+ // find the group of buttons (either allkitchenButtons or allTimeButtons) that the clicked button belongs to
+ const buttonGroup = button.classList.contains("sort-button") ? allSortButtons : allFilterButtons;
+ // loop through all buttons in the group and remove the active class
+ buttonGroup.forEach((button) => button.classList.remove("active"));
+ // add the active class to the clicked button
+ button.classList.add("active");
+
+ // get the active filter option from the allFilterButtons array
+ const filterOptionButton = allFilterButtons.find((kitchenButton) => kitchenButton.classList.contains("active"));
+ // get id of the active filter option
+ const kitchenOptionId = filterOptionButton.id;
+
+ // get the active sort option from the allSortButtons array
+ const sortOptionButton = allSortButtons.find((timeButton) => timeButton.classList.contains("active"));
+ // get id of the active sort option
+ const sortOptionId = sortOptionButton.id;
+
+
+
+ if (recipes.length == 0) {
+ return;
+ }
+
+ recipesSection.innerHTML = "";
+ errorSection.innerHTML = "";
+
+
+ // filter the recipes based on the active filter option
+ let filteredRecipes;
+ if (kitchenOptionId === "all") {
+ filteredRecipes = [...recipes];
+ } else if (kitchenOptionId === "random") {
+ const random = Math.floor(Math.random() * recipes.length);
+ filteredRecipes = [recipes[random]];
+ } else {
+ filteredRecipes = recipes.filter((recipe) => recipe.types.includes(kitchenOptionId));
+ /** Week2
+ if (kitchenOptionId === "lunch") {
+ filteredRecipes = recipes.filter((recipe) => recipe.types.contains("lunch"));
+ } else if (kitchenOptionId === "dinner") {
+ filteredRecipes = recipes.filter((recipe) => recipe.types.contains("dinner"));
+ } else if (kitchenOptionId === "dessert") {
+ filteredRecipes = recipes.filter((recipe) => recipe.types.contains("dessert"));
+ } else if (kitchenOptionId === "drink") {
+ filteredRecipes = recipes.filter((recipe) => recipe.types.contains("drink"));
+ } else if (kitchenOptionId === "snack") {
+ filteredRecipes = recipes.filter((recipe) => recipe.types.contains("snack"));
+ }
+ */
+ }
+
+ //if statement for no criterion
+ if (filteredRecipes.length == 0) {
+ errorSection.innerHTML =
+ "There is no recipes matching criterion for " + button.textContent;
+ return;
+ }
+
+ // sort the recipes based on the active sort option
+ let sortedRecipes;
+ if (sortOptionId === "highToLow") {
+ sortedRecipes = filteredRecipes.sort(
+ (a, b) => (b.pricePerServing || Infinity) - (a.pricePerServing || Infinity)
+ );
+ } else if (sortOptionId === "lowToHigh") {
+ sortedRecipes = filteredRecipes.sort(
+ (a, b) => (a.pricePerServing || Infinity) - (b.pricePerServing || Infinity)
+ );
+ }
+
+ sortedRecipes.forEach((recipe) => {
+ //for each sorted recipe, we are transporming elements from the list to LI HTML elements and joining them into single string
+ const ingredientsList = recipe.ingredients
+ .map((ingredient, index) => {
+ return `${ingredient}`;
+ })
+ .join("");
+
+ //replacing HTML recipe section with elements from an Recipes array
+ recipesSection.innerHTML += `
+
+
+
+

+
${recipe.title}
+
+
+
+
+
Time:
${Math.floor(recipe.pricePerServing/10)} kr
+
+
+
+
Ingredients
+
+
+ `;
+ });
+};
+
+// Adding event listeners to all buttons (filter and sort - displayed when clicked)
+
+allFilterButtons.forEach((button) => {
+ button.addEventListener("click", () => {
+ displayRecipes(button);
+ });
+});
+
+allSortButtons.forEach((button) => {
+ button.addEventListener("click", () => {
+ displayRecipes(button);
+ });
+});
+
+displayRecipes(allFilterButtons[1])
diff --git a/style.css b/style.css
new file mode 100644
index 00000000..3e424423
--- /dev/null
+++ b/style.css
@@ -0,0 +1,171 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+
+ body {
+ background-color: #FAFBFF;
+ font-family: "Futura", sans-serif;
+ }
+
+ h1 {
+ margin: 50px;
+ color: #0018a4;
+ font-size: 64px;
+ font-style: normal;
+ font-weight: 700;
+ }
+
+ .random-button {
+ margin: 0 0 20px 50px;
+ }
+
+ h2 {
+ color: #000;
+ font-size: 22px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ }
+
+ .filters {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+ margin: 20px 0;
+ }
+
+ @media (min-width: 530px) {
+ .filters {
+ display: flex;
+ }
+ }
+
+ .filter,
+ .sort {
+ margin: 0 0 10px 50px;
+ }
+
+ .filter-buttons,
+ .sort-buttons {
+ padding: 10px 0 0 0;
+ }
+
+ button {
+ padding: 8px 16px;
+ margin: 0 5px 0 0;
+ outline: 0;
+ line-height: 23.91px;
+ border: 2px solid transparent;
+ border-radius: 50px;
+ }
+
+ button:hover {
+ border: 2px solid #0018A4;
+ }
+
+ .filter-button {
+ background-color: #ccffe2;
+ color: #0018a4;
+ }
+
+ .filter-button.active {
+ background-color: #0018a4;
+ color: white;
+ }
+
+ .sort-button {
+ background-color: #ffecea;
+ color: #0018a4;
+ }
+
+ .sort-button:hover,
+ .sort-button.active {
+ background-color: #ff6589;
+ color: white;
+ }
+
+ .errorMessage {
+ color: #ff6589;
+ font-size: 22px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ padding: 0 20px 0 20px;
+ margin: 0 20px 0 20px;
+ }
+
+ .recipeSection {
+ /*display: flex;*/
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(278px, 1fr));
+ /*width: 300px;*/
+ padding: 16px 16px 24px 16px;
+ /*flex-direction: row;
+ align-items: flex-start;*/
+ gap: 28px;
+ }
+
+ .recipe {
+ background-color: white;
+ border: 2px #0018A4 solid;
+ width: 300px;
+ height: 100%;
+ padding: 16px;
+ border-radius: 16px;
+ }
+
+ .recipe:hover {
+ border: 1px solid #002ead;
+ box-shadow: 0 0 30px 0 rgba(0, 24, 164, 0.20);
+ }
+
+ img {
+ width: 100%;
+ height: 200px;
+ object-fit: cover;
+ border-radius: 10px;
+ }
+
+ .details {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ flex-direction: column;
+ align-self: stretch;
+ }
+
+ .detailsElement {
+ display: flex;
+ align-items: flex-start;
+ flex-direction: row;
+ align-self: stretch;
+ }
+
+ .Cuisine {
+ display: flex;
+ width: 129px;
+ align-items: flex-start;
+ gap: 4px;
+ }
+
+ .Recipe-title {
+ color: #000;
+ font-family: Futura;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ }
+
+ .Recipe-time
+ .Recipe-region {
+ color: #000;
+ font-family: Futura;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ }
\ No newline at end of file