Fire Takes is the world's most popular album-review site, where users can share their opinions on today's hottest new releases and the greatest hits of yesterday. Users can search for albums to see current opinions, stream samples (or entire tracks, thanks to our partners over at Spotify), and add their own reviews. Registered users have a history of albums they have reviewed and are able to contribute to the growing conversation.
https://github.com/CainanB
Role: Lead back-end organization. API calls, database management, routing, and UI development.
https://github.com/petersonprojects
Role: Lead UI/UX development. HTML and CSS styling, animations, UI development, and functionality.
https://github.com/dgelok
Role: Project management and back-end support. Authentication, database setup and management, and design.
- HTML
- CSS
- JavaScript (via Node.js)
- SQL (via Sequelize)
- Sequelize
- Express
- EJS
- bcrypt
- bcrypt-js
- body-parser
- cookie-parser
- multer
- pg
- pg-hstore
- Node.js
- Sequelize
- GIMP
- Postico
- Postman
- ElephantSQL
- Spotify API - https://developer.spotify.com/documentation/web-api/
- Users are able to register unique usernames, and sign in and out
- Users are able to search for and access any album available via Spotify API
- Authentication required for submitting new reviews
- All reviews are stored in database and listed each time the corresponding album is searched
- All reviews made by a single user are displayed on profile page
- Navbar shifts between register/profile and login/logout, depending on status
- Team goals: increased communication, smoother version control, more independent development
- Users can upload a personal photo to their profile
- Users can listen to 30-second snippets of album tracks via Spotify API - or full tracks, if signed in through Spotify
- Create a cumulative average score from total user reviews
- Place user photo by user reviews on album page
- Enable 'follow' status for albums or artists, with alerts to new reviews
- Provide chat room
- Enable comment responses per review
- Place most recent reviews on landing page
- Place users with highest num of reviews on landing page
Challenge: Simultaneous DB/API calls
Solution: Refactoring code allowed us to store necessary API data in our database, making only a single call necessary, instead of an initial call to our database and then a followup call to the Spotify API.
Challenge: More advanced design (compared to earlier projects)
Solution: Additional CSS development provided animations, working with GIMP provided a unique logo, and a series of authentication checks changed
Challenge: Photo uploads
Solution: Research led us to Multer middleware, and documentation research provided the required solution.
Challenge: We got sick
Solution: Naps.
.nav-link:hover
{
box-shadow:
1px 1px rgb(255, 11, 11),
2px 2px rgb(255, 11, 11),
3px 3px rgb(255, 11, 11);
-webkit-transform: translateX(-3px);
transform: translateX(-3px);
color: orange;
}
.custom-toggler.navbar-toggler {
border-color: orange;
}
.custom-toggler.navbar-toggler-icon {
color: orange;
}
.navbar-dark .custom-toggler .collapzoid:hover{
color:pink;
}
.threed:hover{
box-shadow:
1px 1px rgb(255, 11, 11),
2px 2px rgb(255, 11, 11),
3px 3px rgb(255, 11, 11);
-webkit-transform: translateX(-3px);
transform: translateX(-3px);
}
$("#registerButton").click(async(e) => {
e.preventDefault();
let passw = $('#password').val()
let passConf = $('#passwordConfirm').val()
// console.log(passw)
// console.log(passConf)
if (passw != passConf) {
$('#passwordFailMessage').show();
$('#registerFailMessage').hide();
// console.log(`${$('#password')}`)
// $('#password').value = "";
// $('#passwordConfirm').value = "";
}
else {
fetch('/registration',{
method: "POST",
headers: {'Content-Type' : 'application/json'},
body: JSON.stringify({
username : $('#username').val(),
password: $('#password').val()
})
})
.then(results => results.json())
.then(result => {
// console.log(result)
if(result == "success"){
$('#exampleModal').modal('toggle');
}else{
$('#registerFailMessage').show();
$('#passwordFailMessage').hide();
}
})
}
})
This snippet, found in our routes, handles a POST request and allows for the creation of a new review, thus enabling us to tether information from Spotify API into our database:
router.post('/albums', async (req, res) => {
// let username = req.session.username;
let userID = parseInt(req.session.userID)
let review = req.body.reviewText
if(req.body.rating == 'undefined' || req.body.rating == undefined)
{
req.body.rating = 5;
}
let rating = parseInt(req.body.rating);
let albumID = req.body.albumID
let albumTitle = req.body.albumName;
let aristName = req.body.artistName;
let albumURL = req.body.albumArt;
// console.log(`userID: ${userID}, review: ${review}, rating: ${rating}, albumID: ${albumID}`);
db.reviews.create({
authorID: userID,
stars: rating,
text: review,
albumID: albumID,
aristName: aristName,
albumTitle: albumTitle,
albumURL: albumURL
})
.then(user =>{
// console.log("review inserted succesfully")
res.redirect('/')
})
.catch(error =>{
console.log(error)
})
})
This JS snippet is used to fetch user review data from the database, then populate the profile page:
const getUserInfo = async () =>{
$("#userReviewsBlock").html("")
const reviews = await fetch('/userInfo')
let userReviews = await reviews.json();
userReviews.reverse(); // REVERSE THE RESULTS SO THE LATEST REVIEW MADE SHOWS FIRST
userReviews.forEach((review,i)=>{ // LOOP THROUGH EACH REVIEW AND CREATE HTML ELEMENTS
let starHTML = '';
for(let i = 0; i < review.stars;i++)
{
starHTML += '<span class="one fa fa-star fa-2x checked"></span>'
}
let htmlEl = `
<div id="${review.id}Container">
<div class="row">
<h1 class="ml-2"> ${review.albumTitle} </h1>
</div>
<div class="row ml-2">
<h3> ${review.aristName} </h3>
</div>
<div class="row ml-2 mt-2">
<div class="col-xl-3 mt-1 pl-0 pr-0">
<a href="/albums"><img class="cover mr-5" src="${review.albumURL}" height="200" width="200" alt=""></a>
</div>
<div class="col-xl-8 ml-xl-3 mt-1 pl-0 d-flex justify-content-start">
<blockquote id="${review.id}currentReviewText" class="lead blockquote text-left ml-0 mt-0 pt-1 h-100 w-100">
${review.text}
</blockquote>
</div>
</div>
<div id="stars" class="col-2 pt-1 mt-1 ml-0 d-flex justify-content-start">
${starHTML}
</div>
<a href="#" id="${review.id}" class="edit ml-2 mt-4" style="display:inline-block;"><i class="editReviewButton fa fa-pencil-square-o" aria-hidden="true"></i> Edit Review</a>
<a href="#" id="${review.id}" class="delete edit ml-2 mt-4" style="display:inline-block;"><i class="deleteReviewButton fa fa-trash-o" aria-hidden="true"></i> Delete Review</a>
<hr style="height: 1px;
background-color: orangered;
border: none;margin-top:0.5rem">
</div>
`
$("#userReviewsBlock").append(htmlEl)
// SAVE A UNIQUE REVIEW EDIT FORM FOR EACH REVIEW AND PUSH TO ARRAY
reviewForms.push({
id: review.id,
html: `<form id="${review.id}form">
<input id="${review.id}hiddenInput" type="hidden" name="albumID" value="${review.id}">
<textarea id="${review.id}editedReviewText" name="editedReviewText">${review.text}</textarea>
<input class="btn btn-danger editedReviewSubmitButton" type="submit" id="${review.id}">
</form>`
})
});
}