Issues

I’ve kept up to date with my progress in my scrum board. I mainly used one issue to keep track of my update functionality progress.

like dislike functionality issue This was my main issue I worked in, every time I made progress on my like/dislike and update functionality, I added it in here. The issue is closed now that its done, but it was useful to keep track of progress.

backend issue I only posted 2 comments here to let Finn know I got update sorted out, and figured out the cors error problem, but I also kept up to date with this one to see how progress was made, and see what else needed to be done.

devtools and timer for like/dislike issue This was an issue I used for myself to keep track of some more extra features I worked on. Progress logged here like usual.

localhost + fix delete I just made this issue to log my progress on this problem, incase I needed to refer back to whatever I did here.

reflection on issues

logging progress in issues was really only done because it was satisfying to write down how I solved problems. Though they also do help in organizing what work should be done or other progress. I think some issues on our scrum board could be divided into smaller issues for ease of access. I will make sure to focus on this for tri 2.

Actually, I might not have needed this many issues, perhaps one or two could have been combined?

overview of feature. What did I do fr?

Main entry to my feature is Post.html, right here: link.

Main feature was update functionality via like and dislike buttons.

As an extra feature I created devtools for moderation of the posts by admins, and added timers preventing people from spamming like and dislike.

Basically I implemented update and delete and some read functionality

I also fixed some cors errors and fixed localhost not connecting to backend correctly

main backend code


    @PostMapping("/like/{name}")  // Change the path variable to 'name'
    public ResponseEntity<Skatepark> updateLikes(@PathVariable String name) {
        List<Skatepark> skateparks = repository.findBySkateparkName(name);
        if (!skateparks.isEmpty()) {
            Skatepark skatepark = skateparks.get(0); // Assuming you want to work with the first matching skatepark
            int currentLikes = skatepark.getTotalLikes();
            skatepark.setTotalLikes(currentLikes + 1); // Increment likes by 1
            // You can also update the author who liked the skatepark
            repository.save(skatepark);
            return new ResponseEntity<>(skatepark, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    @PostMapping("/dislike/{name}")  // Change the path variable to 'name'
    public ResponseEntity<Skatepark> updated_Dislikes(@PathVariable String name) {
        List<Skatepark> skateparks = repository.findBySkateparkName(name);
        if (!skateparks.isEmpty()) {
            Skatepark skatepark = skateparks.get(0); // Assuming you want to work with the first matching skatepark
            int currentLikes = skatepark.getTotalLikes();
            skatepark.setTotalLikes(currentLikes - 1); // decrease likes by 1
            // You can also update the author who liked the skatepark
            repository.save(skatepark);
            return new ResponseEntity<>(skatepark, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

this part was pretty tricky to get done. At first, a different method was used, I think it was push or something. but it didn’t work. I found out the easier method is to do what jokes did and use post instead of the built in update method. This worked pretty well, and I was able to get these 2 methods working. progress can be seen in issues mentioned before.

What it does is it finds the repository by a name variable, and then it grabs the total likes and either adds to it by one or lowers it by one depending on if its like or dislike. then it updates the data entry with the new like count.

Main frontend code

 <div id="skatepark-cards" class="scroll-container">
        <!-- This div element is used to hold dynamically created skatepark cards. It serves as a container for displaying skatepark objects. -->
    </div>
    <script>     

this div element will hold the cards we are gathering from our database.

// Finding and updating the skatepark card element by skateparkName, showcasing encapsulation.
function updateLike(skateparkName) {
    console.log("Like button clicked for skatepark: " + skateparkName);
    const requestOptions = {
        method: 'POST',
        cache: 'no-cache',
        credentials: 'include',
    };
    // Use the fetch function with the modified request options
    //http://localhost:8085/api/skatepark/like/
    //https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/like/
    fetch("https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/like/" + skateparkName, requestOptions)
        .then(response => {
            if (!response.ok) {
                throw Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            // Find the skatepark card element by skateparkName
            const skateparkCard = document.querySelector(`[data-skatepark-name="${skateparkName}"]`);
            if (skateparkCard) {
                     // Update the totalLikes element in the card
           const totalLikesElement = skateparkCard.querySelector(".total-likes");
               if (totalLikesElement) {
                 totalLikesElement.textContent = `Total Likes: ${data.totalLikes}`;
                                    }
                                }
            console.log(data); // Log the fetched data to the console
        })
        .catch(error => {
            console.error('Fetch error:', error);
        });
}

Basic usage of function is calling the like method on backend and then having the new data recived by but into an element in the skatepark card that was updated with like or dislike.

        
        dislikeButtonCooldown = false; 
        function update_dislike(skateparkName) {
            if (dislikeButtonCooldown) {
                alert("please wait before clicking dislike again!");
                return; // If the button is disabled, do nothing
            }
            console.log("dislike button clicked for skatepark: " + skateparkName);
            dislikeButtonCooldown = true;
            const requestOptions = {
                method: 'POST',
                cache: 'no-cache',
                credentials: 'include',
            };
        
            // Use the fetch function with the modified request options
            //http://localhost:8085/api/skatepark/like/
            //https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/like/
            fetch("https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/dislike/" + skateparkName, requestOptions)
                .then(response => {
                    if (!response.ok) {
                        throw Error('Network response was not ok');
                    }
                    return response.json();
                })
                .then(data => {
                    // Find the skatepark card element by skateparkName
                    const skateparkCard = document.querySelector(`[data-skatepark-name="${skateparkName}"]`);
                    if (skateparkCard) {
                             // Update the totalLikes element in the card
                   const totalLikesElement = skateparkCard.querySelector(".total-likes");
                       if (totalLikesElement) {
                         totalLikesElement.textContent = `Total Likes: ${data.totalLikes}`;
                                            }
                                        }
                    console.log(data); // Log the fetched data to the console
                })
                .catch(error => {
                    console.error('Fetch error:', error);
                });
                setTimeout(function() {
                    dislikeButtonCooldown = false; // Re-enable the button after 10 seconds
                }, 10000); // 10 seconds (10000 milliseconds)
        }

Basically same thing as like function but for dislike instead. no real difference here other than we call dislike function. I’m also using this to display timeout functionality, which is done with a boolean that switches off and on based ont the setTimeout used.

document.addEventListener("DOMContentLoaded", function () {
    const skateparkCardsContainer = document.getElementById("skatepark-cards");

    // Replace the URL with the actual URL
    //http://localhost:8085/api/skatepark/
    //https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/
    const apiUrl = 'https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/';

    fetch(apiUrl)
        .then(response => response.json())
        .then(data => {
            data.forEach(skatepark => {
                // Create a new div card for each skatepark
                // Creating individual skatepark cards, which encapsulate the information for each skatepark.
                const car = document.createElement("div");
                car.className = "car";
                car.dataset.skateparkName = skatepark.skateparkName; // Add data attribute
                car.innerHTML = `
                    <div class="details">
                        <div class="info">
                            <h3 id = "test">${skatepark.skateparkName}</h3>
                            <img src = "https://y2kcoders.stu.nighthawkcodingsociety.com/image/${skatepark.skateparkName}">
                            <p><b>Author:</b> ${skatepark.author}</p>
                            <p><b>Title:</b> ${skatepark.title}</p>
                            <p><b>Address:</b> ${skatepark.address}</p>
                            <p><b>Star Rating:</b> ${skatepark.starRating}</p>
                            <p><b>Description:</b> ${skatepark.description}</p>
                            <p class ="total-likes"><b>Total Likes:</b> ${skatepark.totalLikes}</p>
                        </div>
                        <div class="actions">
                            <button onclick="updateLike('${skatepark.skateparkName}')">Like</button>
                            <button onclick="update_dislike('${skatepark.skateparkName}')">dislike</button>
                            <button>Share</button>
                        </div>
                    </div>
                `;
                skateparkCardsContainer.appendChild(car);
            });
        })
        .catch(error => console.error("Error fetching data:", error));
});
</script>

this code Creates individual skatepark cards, which encapsulate the information for each skatepark. also note the data attribute we use to update like and dislike in real time.

devtools



@DeleteMapping("/delete/{name}")
public ResponseEntity<Void> deleteSkatepark(@PathVariable String name) {
    List<Skatepark> skateparks = repository.findBySkateparkName(name);
    if (!skateparks.isEmpty()) {
        repository.delete(skateparks.get(0)); // Assuming you want to delete the first matching skatepark
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}


this is the delete method in our backend for our moderation tools to delete bad posts. not much to say here, but it was done by Finn, and then worked on by me.

function deletepark(skateparkName){

    console.log("deleting  " + skateparkName);
    const requestOptions = {
        method: 'DELETE',
        cache: 'no-cache',
        credentials: 'include',
        mode: 'no-cors',
    };

    // Use the fetch function with the modified request options
    //http://localhost:8085/api/skatepark/like/
    //https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/like/
    fetch("https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/delete/" + skateparkName, requestOptions)
        .then(response => {
            if (!response.ok) {
                throw Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            console.log(data); // Log the fetched data to the console
        })
        .catch(error => {
            console.error('Fetch error:', error);
        });
}

this code fetches a delete method created in the backend to allow moderators to delete bad reviews.

@PostMapping("/edit/{name}")  // Change the path variable to 'name'
public ResponseEntity<Skatepark> updatename(@PathVariable String name) {
    List<Skatepark> skateparks = repository.findBySkateparkName(name);
    if (!skateparks.isEmpty()) {
        Skatepark skatepark = skateparks.get(0); // Assuming you want to work with the first matching skatepark
        String userinput = "test";
        skatepark.setDescription(userinput); // Increment likes by 1
        // You can also update the author who liked the skatepark
        repository.save(skatepark);
        return new ResponseEntity<>(skatepark, HttpStatus.OK);
    }
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

This is the backend part of the edit function we use for censoring descriptions. Its modified code from the like and dislike functions, showcasing reuasable and modular code design.

function censorparkdesc(description) {
    console.log("description will be changed for" + description);
    const requestOptions = {
        method: 'POST',
        cache: 'no-cache',
        credentials: 'include',
    };

    // Use the fetch function with the modified request options
    //http://localhost:8085/api/skatepark/like/
    //https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/like/
    fetch("https://y2kcoders.stu.nighthawkcodingsociety.com/api/skatepark/edit/" + description, requestOptions)
        .then(response => {
            if (!response.ok) {
                throw Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            // Find the skatepark card element by skateparkName
            const skateparkCard = document.querySelector(`[data-skatepark-description="${description}"]`);
            if (skateparkCard) {
                     // Update the totalLikes element in the card
           const descriptionElement = skateparkCard.querySelector("descriptionstore");
               if (descriptionstore) {
                 descriptionstore.textContent = `Description: ${data.description}`;
                                    }
                                }
            console.log(data); // Log the fetched data to the console
        })
        .catch(error => {
            console.error('Fetch error:', error);
        });
}

this is frontend code showcasing the function used to call our edit function, which is also reused from our like function.

<button onclick="deletepark('${skatepark.skateparkName}')">delete</button>
                                    <button onclick="censorparkdesc('${skatepark.skateparkName}')">censor park desc</button>

these are buttons on our devtools, the rest of the devtools is actually mostly copied from the post.html with key difference being no cooldowns on like and dislike and the update and delete buttons being present for moderation.

commits and progress

IMAGES WILL GO HERE WHEN IM SURE I’VE GOT EVERYTHING COMMITTED

likes updated commit

This commit was the one where I figured out how like was supposed to work. This was the main starting point for the rest of my work, as the like function served as a basis for other work to be done.

cors error fixed commit

This commit was the one where I fixed the cors error our team struggled on for a couple days at that point. Fix was pretty simple in hindsight, but it took lots of trial and error and looking around to fix this problem, but doing it allowed our entire project to actually work on deployed frontend.

localhost fix commit

ok this one isn’t really too impressive, I just fixed local host not connecting to backend by allowing it in the backend. Its just here because it is pretty important and no one else bothered to fix it for some reason.

College board learning and mcq

the mcq isn’t done yet but it will go in this link when i’m done. I also plan on reviewing my strengths and weaknesses and what I need to review after the mcq so I can test myself and see what I do and don’t understand. mcq blog

tri 1 reflection

this trimester went pretty smoothly, and there was lots of collaboration and working on the project, which is a pretty big leap from the suffering I faced in last year. I think next tri, I want to build a more substantial project feature that I really think is cool, but I can say this tri, things went pretty well for me and my team.

I also think I need to focus more on java and backend next tri as this tri was mostly focused on connecting frontend to backend, and I didn’t do too much on the backend.