Skip to content

Conversation

@gabriellaberko
Copy link

…ons that should not be active at the same time
add the functions for creating its elements and fetching recipe info inside it.
…rs + refactor button logic of what can be active at the same time
…ardless of if it contains data from fetch or local storage
…en to be compatible with an array for the cuisines
@gabriellaberko gabriellaberko changed the title JS Project Recipe library - Gabriella JS Project Recipe Library - Gabriella Oct 10, 2025
Copy link

@SaraEnderborg SaraEnderborg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project is really great as a whole, it is very well structured which makes it easy to follow and read. I am also impressed about the functionallity of the project, it has smart solutions in html, CSS and Javascript. I particularly enjoyed the JS code because it was logically divided and the functions made sense, the "showRecipeCard" function especially. Coolt feautures with the searchbar and the"favourites". Really well done on your project! :)

src="./cross-white.svg"
alt="cross icon">
<div
class="modal"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool use of "modal", also in js. I have not seen this before:)

id="card-container">
</div>
<script src="./script.js"></script>
</body>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good and easy-to-understand structure in your HTML, really nice :) Maybe you could use more semantic tags like “main” and “section” to make it even more accessible.

<body>
<div class="top-container">
<input
class="search-button"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally get the choice of the name but maybe for further clarity "search-input" could be another option for it, since it's "not really" a button:)

}

.hidden {
display: none !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to look "!important" up😅. Curious about how you worked with it? Did it not pick this up from the beginning?

.title {
color: #0018A4;
font-weight: 700;
font-style: bold;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember the Figma design by heart, maybe this was in there but i don't think "font-style: bold;" is a valid property. "font-weight" should do the trick i think.

});

// re-append the cards in sorted order
const cardContainer = document.getElementById("card-container");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already have a global variable for " cardcontainer" in row 16🙂 you don't have to call the DOM again, you can just do "sortedCardArray.forEach((card) => cardContainer.appendChild(card)) i think🙂


const updateFavoriteRecipes = (recipeId, recipeIsLiked) => {
// find the corresponding recipe (in allRecipes) for the clicked card by comparing their recipe ID's
clickedRecipe = allRecipes.find(recipe => recipe.id === recipeId);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find "clickedRecipe" as a const or let somewhere, maybe i've missed it but otherwise you could do that here:)

const storedRecipes = JSON.parse(localStorage.getItem("recipes")) || [];

// show loading state when fetching data
let fetchLoadingState = true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really nice👍

card.dataset.id = recipe.id;

// check if the recipes id match any id of the recipes in favoriteRecipes. If true, set the variable to the class of active state for the heart icon
const heartIconClass = favoriteRecipes.some(favoriteRecipe => favoriteRecipe.id === recipe.id) ? "fas" : "far";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looked so good on the page🤩

<p class="time"><b>Time:</b> ${recipe.readyInMinutes} min</p>
<p><b>Servings:</b> ${recipe.servings}</p>
</div>
<hr class="solid">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smart to do this. Does it work even if the recipe doesn't have a summary?

@JennieDalgren JennieDalgren self-assigned this Oct 23, 2025
Copy link

@JennieDalgren JennieDalgren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good job with this project!
You have implemented all requirements and several stretch goals.
I Like the way you can add to favorites list and that it is perserved in local storage.

Your code is nice structured and easy to follow.

Keep up the good work!

Comment on lines +66 to +69
<div
class="modal-overlay hidden"
id="modal-overlay">
<img

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the modal, take a look at the html element <dialog>
I'm not so used to it myself but it is pretty cool!
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog

Comment on lines +31 to +32
if (fetchLoadingState === true){
cardContainer.innerHTML = `<p>Loading...</p>`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great with a loading state!

Comment on lines +50 to +53
if(fetchedRecipes && fetchedRecipes.length > 0 ) {
localStorage.setItem("recipes", JSON.stringify(fetchedRecipes));
allRecipes = fetchedRecipes;
// if the fetch has no data (empty array), update allRecipes with data from local storage

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, well done with local storage

Comment on lines +84 to +86
if(!recipeArray || recipeArray.length === 0) {
cardContainer.innerHTML = `<p class="filter-error-message">Oh no!🥲 Unfortunately there are no recipes matching your current filter. Try another one!</p>`;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great with a fallback empty state


function showCardInModal(cardButton) {
// find the card which button was clicked
const clickedCard = cardButton.closest(".card");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clever

Comment on lines +165 to +169
modalContent.querySelector(".recipe-content .recipe-summary").classList.toggle("hidden");

// make the recipe's instructions and ingredients visible
modalContent.querySelector(".recipe-content .recipe-instruction").classList.toggle("hidden");
modalContent.querySelector(".recipe-content .ingredients").classList.toggle("hidden");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isnt it enough to hide or show on the parent element?

Comment on lines +190 to +191
const sortedCardArray = [...document.querySelectorAll(".card")];
// get the cooking time of each card

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spread operator! nice

const getTime = (card) => {
const text = card.querySelector(".time").textContent;
// extract and return the number from it
const number = text.match(/\d+/);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regular expression! Do you understand what it does?

Comment on lines +254 to +263
const addFavoriteRecipesToLocalStorage = () => {
// create a set of valid recipe IDs from allRecipes
const validRecipeIds = new Set(allRecipes.map(recipe => recipe.id));

// filter favoriteRecipes so only recipes that exist in allRecipes are left
favoriteRecipes = favoriteRecipes.filter(favoriteRecipe => validRecipeIds.has(favoriteRecipe.id));

// save cleaned favoriteRecipes to localStorage
localStorage.setItem("favoriteRecipes", JSON.stringify(favoriteRecipes));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants