-
Notifications
You must be signed in to change notification settings - Fork 60
PR Recipe Library #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
irisdgz
wants to merge
15
commits into
Technigo:main
Choose a base branch
from
irisdgz:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
9fdf58c
Updated html, css and js
irisdgz 9d7bbb5
Updated HTML
irisdgz 4c77e8e
Updated js
irisdgz 489c7e9
Added some comments in the js
irisdgz 66a746c
Updated README
irisdgz 7b88b62
updated spoonacular key
irisdgz 52ad978
Added Netlify link
irisdgz 506a726
Added the random button and updated css and js
irisdgz 0fbc4aa
Minder changes in the CSS page title
irisdgz 1303b8d
Minor changes in js
irisdgz d52e6b3
Minor changes in js
irisdgz 8e22b9b
make changes in the sorting options, js, html and css.
irisdgz 6b70ad6
Removed built in tool. manual url builder instead
irisdgz b47e267
last changes in the css
irisdgz bb5b942
fixed HTML mathcing issues, fixed fetchData so its correctly declared…
irisdgz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,25 @@ | ||
| # js-project-recipe-library | ||
| Basic structure HTML, Style in CSS, Add functionalities in js. | ||
|
|
||
| https://mysecondprojectlibrary.netlify.app/ | ||
|
|
||
| Week 1 | ||
| Start to build a Recipe Library | ||
| HTML structure: | ||
| Input fields for filters and sorting options | ||
| A placeholder recipe card | ||
| Writing JavaScript functions to handle user selections | ||
| Sorting options | ||
|
|
||
| Week 2 | ||
| Arrays, object and loops to show recipes | ||
| using selections, be able to display all of the recioes, feature a button that selects a random recipe, hav an empty state, responsive from 32npx to 1600px | ||
|
|
||
| Week 3 | ||
| Fetch real recipe data from Spoonacular's API | ||
| Display dynamic recipe cards based on the API data | ||
| Adapt filtering & sorting to match the API response format | ||
| Show a useful message to the user in case the daily quota has been reached | ||
|
|
||
| The goal was to practice working with arrays, objects, and functions in JavaScript and to fetch and display real data from an external API | ||
| Styled by using CSS variables for what I call Technigo colors (blue, pink, and aqua) to make it look as much as the Figma demo. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <title>Recipe Library</title> | ||
| <link rel="stylesheet" href="styles.css" /> | ||
| <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet"> | ||
| </head> | ||
|
|
||
| <body> | ||
| <div class="wrapper"> | ||
| <h1 class="page-title">Recipe Library</h1> | ||
|
|
||
| <!-- Controls --> | ||
| <section class="controls"> | ||
|
|
||
| <!-- Kitchen filters --> | ||
|
|
||
| <div class="control-group"> | ||
| <h2 class="control-title">Kitchen</h2> | ||
| <div class="pill-group"> | ||
| <button class="pill active" data-kitchen="all">All</button> | ||
| <button class="pill" data-kitchen="Italian">Italian</button> | ||
| <button class="pill" data-kitchen="Asian">Asian</button> | ||
| <button class="pill" data-kitchen="Middle Eastern">Middle Eastern</button> | ||
| <button class="pill" data-kitchen="American">American</button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Diet filters --> | ||
| <div class="control-group"> | ||
| <h2 class="control-title">Diets</h2> | ||
| <div class="pill-group"> | ||
| <button class="pill active" data-diet="all">All</button> | ||
| <button class="pill" data-diet="vegan">Vegan</button> | ||
| <button class="pill" data-diet="vegetarian">Vegetarian</button> | ||
| <button class="pill" data-diet="gluten free">Gluten-free</button> | ||
| <button class="pill" data-diet="dairy free">Dairy-free</button> | ||
| </div> | ||
| </div> | ||
|
|
||
|
|
||
| <!-- Cooking time filters --> | ||
| <div class="control-group"> | ||
| <h2 class="control-title">Cooking time</h2> | ||
| <div class="pill-group"> | ||
| <button class="pill active" data-time="all">All</button> | ||
| <button class="pill" data-time="under15">Under 15 min</button> | ||
| <button class="pill" data-time="15to30">15–30 min</button> | ||
| <button class="pill" data-time="30to60">30–60 min</button> | ||
| <button class="pill" data-time="over60">Over 60 min</button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Ingredient filters --> | ||
| <div class="control-group"> | ||
| <h2 class="control-title">Amount of ingredients</h2> | ||
| <div class="pill-group"> | ||
| <button class="pill active" data-ingredients="all">All</button> | ||
| <button class="pill" data-ingredients="under5">Under 5</button> | ||
| <button class="pill" data-ingredients="6to10">6–10</button> | ||
| <button class="pill" data-ingredients="11to15">11–15</button> | ||
| <button class="pill" data-ingredients="over16">Over 16</button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Sorting options + Random button --> | ||
| <div class="control-group sorting-row"> | ||
| <h2 class="control-title">Sorting options</h2> | ||
|
|
||
| <div class="pill-group sort-row"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. undrar över att det finns två classes, finns det en anledning till det? |
||
|
|
||
| <button class="pill" data-sort="popularity">By popularity</button> | ||
| <button class="pill" data-sort="ingredients">By ingredients</button> | ||
| <button id="random-btn" class="pill pink-btn">Random Recipe</button> | ||
| </div> | ||
| </div> | ||
|
|
||
|
|
||
|
|
||
| <!-- Message output --> | ||
| <p id="msg" class="note"></p> | ||
|
|
||
| <!-- Recipe cards --> | ||
| <main id="recipes" class="recipes"></main> | ||
| </div> | ||
|
|
||
| <script src="script.js"></script> | ||
| </body> | ||
| </html> | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| // --- CONNECTING JS WITH HTML --- | ||
| const recipesEl = document.getElementById("recipes"); | ||
| const msgEl = document.getElementById("msg"); | ||
| const pills = document.querySelectorAll(".pill"); | ||
| const randomBtn = document.getElementById("random-btn"); | ||
|
|
||
| // --- API SETUP --- | ||
| const API_KEY = "2065aff4499d4fe29bdfbad342732432"; | ||
| const BASE_URL = "https://api.spoonacular.com/recipes/complexSearch"; | ||
|
|
||
| // --- STATE --- | ||
| let recipes = []; | ||
| let filters = { | ||
| kitchen: "all", | ||
| diet: "all", | ||
| time: "all", | ||
| ingredients: "all", | ||
| sort: "popularity", | ||
| }; | ||
|
|
||
| // --- FETCH DATA FUNCTION --- | ||
| async function fetchData() { | ||
| recipesEl.innerHTML = `<p>Loading recipes...</p>`; | ||
| msgEl.textContent = "Fetching data..."; | ||
|
|
||
| // Build query | ||
| let url = `${BASE_URL}?number=12&addRecipeInformation=true&apiKey=${API_KEY}`; | ||
|
|
||
| if (filters.kitchen !== "all") url += `&cuisine=${filters.kitchen}`; | ||
| if (filters.diet !== "all") url += `&diet=${filters.diet}`; | ||
|
|
||
| try { | ||
| const res = await fetch(url); | ||
|
|
||
| if (res.status === 402) { | ||
| recipesEl.innerHTML = `<p>Daily API quota exceeded. Please try again tomorrow.</p>`; | ||
| msgEl.textContent = "API limit reached."; | ||
| throw new Error("Quota reached"); | ||
| } | ||
|
|
||
| const data = await res.json(); | ||
| console.log("Fetched data:", data); | ||
|
|
||
| if (!data.results || data.results.length === 0) { | ||
| recipesEl.innerHTML = `<p>No recipes found for your selected filters.</p>`; | ||
| msgEl.textContent = "No results found."; | ||
| return; | ||
| } | ||
|
|
||
| // Normalize data | ||
| recipes = data.results.map(recipe => ({ | ||
| id: recipe.id, | ||
| title: recipe.title || "Unknown title", | ||
| image: recipe.image || "https://via.placeholder.com/300x200?text=No+Image", | ||
| kitchen: | ||
| recipe.cuisines && recipe.cuisines.length > 0 | ||
| ? recipe.cuisines[0] | ||
| : "Various", | ||
| diet: | ||
| recipe.diets && recipe.diets.length > 0 | ||
| ? recipe.diets[0] | ||
| : "General", | ||
| time: recipe.readyInMinutes || 0, | ||
| ingredients: recipe.extendedIngredients | ||
| ? recipe.extendedIngredients.length | ||
| : 0, | ||
| popularity: recipe.aggregateLikes || 0, | ||
| })); | ||
|
|
||
| showRecipes(recipes); | ||
| msgEl.textContent = `Fetched ${recipes.length} recipes (${filters.kitchen}, ${filters.diet})`; | ||
| } catch (err) { | ||
| console.error("Fetch error:", err); | ||
| recipesEl.innerHTML = `<p>Something went wrong. Please try again later.</p>`; | ||
| msgEl.textContent = "Failed to load recipes."; | ||
| } | ||
| } | ||
|
|
||
| // --- FILTER FUNCTION --- | ||
| const filterRecipes = list => | ||
| list.filter(r => { | ||
| // Time filters | ||
| if (filters.time === "under15" && !(r.time < 15)) return false; | ||
| if (filters.time === "15to30" && !(r.time >= 15 && r.time <= 30)) return false; | ||
| if (filters.time === "30to60" && !(r.time >= 30 && r.time <= 60)) return false; | ||
| if (filters.time === "over60" && !(r.time > 60)) return false; | ||
|
|
||
| // Ingredient filters | ||
| if (filters.ingredients === "under5" && !(r.ingredients < 5)) return false; | ||
| if (filters.ingredients === "6to10" && !(r.ingredients >= 6 && r.ingredients <= 10)) return false; | ||
| if (filters.ingredients === "11to15" && !(r.ingredients >= 11 && r.ingredients <= 15)) return false; | ||
| if (filters.ingredients === "over16" && !(r.ingredients > 16)) return false; | ||
|
|
||
| return true; | ||
| }); | ||
|
|
||
| // --- SORT FUNCTION --- | ||
| const sortRecipes = list => { | ||
| const key = filters.sort; | ||
| const sorted = [...list]; | ||
| sorted.sort((a, b) => b[key] - a[key]); // highest first | ||
| return sorted; | ||
| }; | ||
|
|
||
| // --- SHOW RECIPES --- | ||
| const showRecipes = list => { | ||
| const filtered = filterRecipes(list); | ||
| const sorted = sortRecipes(filtered); | ||
|
|
||
| recipesEl.innerHTML = sorted.length | ||
| ? sorted | ||
| .map( | ||
| r => ` | ||
| <div class="card"> | ||
| <img src="${r.image}" alt="${r.title}" class="card-img"> | ||
| <div class="card-body"> | ||
| <h3>${r.title}</h3> | ||
| <p><strong>Cuisine:</strong> ${r.kitchen}</p> | ||
| <p><strong>Diet:</strong> ${r.diet}</p> | ||
| <p><strong>Time:</strong> ${r.time} min</p> | ||
| <p><strong>Ingredients:</strong> ${r.ingredients}</p> | ||
| <p><strong>Popularity:</strong> ${r.popularity}</p> | ||
| </div> | ||
| </div>` | ||
| ) | ||
| .join("") | ||
| : `<p>No recipes match your filters.</p>`; | ||
|
|
||
| msgEl.textContent = `Results: ${sorted.length} | Kitchen: ${filters.kitchen} | Diet: ${filters.diet} | Sort: ${filters.sort}`; | ||
| }; | ||
|
|
||
| // --- EVENT LISTENERS --- | ||
| pills.forEach(btn => { | ||
| btn.addEventListener("click", () => { | ||
| const group = btn.parentElement.querySelectorAll(".pill"); | ||
| group.forEach(b => b.classList.remove("active")); | ||
| btn.classList.add("active"); | ||
|
|
||
| if (btn.dataset.kitchen) filters.kitchen = btn.dataset.kitchen; | ||
| if (btn.dataset.diet) filters.diet = btn.dataset.diet; | ||
| if (btn.dataset.time) filters.time = btn.dataset.time; | ||
| if (btn.dataset.ingredients) filters.ingredients = btn.dataset.ingredients; | ||
| if (btn.dataset.sort) filters.sort = btn.dataset.sort; | ||
|
|
||
| if (btn.dataset.kitchen || btn.dataset.diet) { | ||
| fetchData(); // refetch from API | ||
| } else { | ||
| showRecipes(recipes); // filter locally | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| // --- RANDOM RECIPE BUTTON --- | ||
| randomBtn.addEventListener("click", () => { | ||
| const random = recipes[Math.floor(Math.random() * recipes.length)]; | ||
| showRecipes([random]); | ||
| }); | ||
|
|
||
| // --- INITIAL FETCH --- | ||
| fetchData(); | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gillar din planering, har inte använt readme filen så mycket, ska nog börja göra det. för denna planering gör det väldigt tydligt!