prettier style fixes

Signed-off-by: Arthur Lu <learthurgo@gmail.com>
This commit is contained in:
Arthur Lu
2022-12-03 02:02:52 +00:00
parent dbbd14399e
commit 793e7891b3
70 changed files with 2123 additions and 1849 deletions

View File

@@ -1,6 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
@@ -8,12 +7,11 @@
<title>Food Journal</title>
<!--Add Favicon-->
<link rel="icon" type="image/x-icon" href="./assets/images/favicon.ico">
<link rel="icon" type="image/x-icon" href="./assets/images/favicon.ico" />
<!-- Review Card Custom Element -->
<script src="assets/scripts/ReviewCard.js" type="module"></script>
<!-- Create Page Stylesheets & Scripts -->
<link rel="stylesheet" href="./static/CreatePage.css" />
<link rel="stylesheet" href="./static/Form.css" />
@@ -23,25 +21,24 @@
<header>
<!-- Setting up logo and site name at the top of the website -->
<div class="top-bar">
<img src ="./assets/images/Logo.png" alt="logo" />
<h1> Food Journal </h1>
<img src ="./assets/images/Logo.png" alt="logo" />
<img src="./assets/images/Logo.png" alt="logo" />
<h1>Food Journal</h1>
<img src="./assets/images/Logo.png" alt="logo" />
</div>
</header>
<body>
<div class="journal-form">
<h1>New Entry</h1>
<form id="new-food-entry">
<fieldset>
<legend>PICTURE:</legend>
<select id="select" name="select">
<option value="file">File Upload</option>
<option value="photo">Take a Photo</option>
</select>
<input type="file" accept="image/*" id="mealImg" name="mealImg">
<input type="file" accept="image/*" id="mealImg" name="mealImg" />
</fieldset>
<fieldset>
@@ -52,47 +49,45 @@
<fieldset>
<legend>MEAL NAME:</legend>
<label for="Name: "> <input type="text" id="mealName" name="mealName" required> </label>
<label for="Name: "> <input type="text" id="mealName" name="mealName" required /> </label>
</fieldset>
<fieldset>
<legend>RESTAURANT NAME:</legend>
<label for="Name:"> <input type="text" id="restaurant" name="restaurant" required> </label>
<label for="Name:"> <input type="text" id="restaurant" name="restaurant" required /> </label>
</fieldset>
<fieldset>
<legend>RATING:</legend>
<div style="display: flex; justify-content: flex-start; align-items: center;">
<div style="display: flex; justify-content: flex-start; align-items: center">
<div class="rating">
<input type="radio" id="s5" name="rating" value="5"/> <label for="s5" id="s5-select"> 5 stars </label>
<input type="radio" id="s4" name="rating" value="4"/> <label for="s4" id="s4-select"> 4 stars </label>
<input type="radio" id="s3" name="rating" value="3"/> <label for="s3" id="s3-select"> 3 stars </label>
<input type="radio" id="s2" name="rating" value="2"/> <label for="s2" id="s2-select"> 2 stars </label>
<input type="radio" id="s1" name="rating" value="1"/> <label for="s1" id="s1-select"> 1 star </label>
<input type="radio" id="s5" name="rating" value="5" /> <label for="s5" id="s5-select"> 5 stars </label>
<input type="radio" id="s4" name="rating" value="4" /> <label for="s4" id="s4-select"> 4 stars </label>
<input type="radio" id="s3" name="rating" value="3" /> <label for="s3" id="s3-select"> 3 stars </label>
<input type="radio" id="s2" name="rating" value="2" /> <label for="s2" id="s2-select"> 2 stars </label>
<input type="radio" id="s1" name="rating" value="1" /> <label for="s1" id="s1-select"> 1 star </label>
</div>
</div>
</fieldset>
<fieldset>
<legend>COMMENTS:</legend>
<textarea name="comments" id="comments" rows="5" style="resize: none; width: 100%;"></textarea>
<textarea name="comments" id="comments" rows="5" style="resize: none; width: 100%"></textarea>
</fieldset>
<fieldset>
<legend>TAGS: (ex. cuisine, distance, cost, etc)</legend>
<input type="text" id="tag-form" name="tag-form">
<input type="text" id="tag-form" name="tag-form" />
<div class='tag-container' id="tag-container-form">
</div>
<button type="button" id="tag-add-btn"> + </button>
<div class="tag-container" id="tag-container-form"></div>
<button type="button" id="tag-add-btn">+</button>
</fieldset>
<button type="submit" id="save-btn" value="Submit">Save</button>
<button type="submit" id="save-btn" value="Submit">Save</button>
<!-- Button that allows user to go back to the homepage -->
<input type="button" value="Cancel" id="home-btn" onclick="window.location.assign('./index.html')">
<!-- Button that allows user to go back to the homepage -->
<input type="button" value="Cancel" id="home-btn" onclick="window.location.assign('./index.html')" />
</form>
</div>
</div>
</body>
</html>
</html>

View File

@@ -7,12 +7,12 @@
<title>Food Journal</title>
<!--Add Favicon-->
<link rel="icon" type="image/x-icon" href="./assets/images/favicon.ico">
<link rel="icon" type="image/x-icon" href="./assets/images/favicon.ico" />
<!-- Review Card Custom Element -->
<script src="assets/scripts/ReviewCard.js" type="module"></script>
<!-- Main Stylesheets & Scripts -->
<!-- Main Stylesheets & Scripts -->
<!-- Temporarily commented out reset.css due to furthur discussion needed on the values of the default config-->
<!-- <link rel="stylesheet" href="/static/reset.css" /> -->
<link rel="stylesheet" href="./static/ReviewDetails.css" />
@@ -22,42 +22,64 @@
<body>
<header>
<div class="top-bar">
<img src ="./assets/images/Logo.png" alt="logo" />
<h1> Food Journal </h1>
<img src ="./assets/images/Logo.png" alt="logo" />
<img src="./assets/images/Logo.png" alt="logo" />
<h1>Food Journal</h1>
<img src="./assets/images/Logo.png" alt="logo" />
</div>
</header>
<main>
<div class="journal-form" id="review-details">
<form>
<fieldset class = "meal-name">
<h1 id="d-meal-name" style="font-family: Century Gothic;"></h1>
<h1 id="d-restaurant" style="font-family: Century Gothic; font-size: 30px;"></h1>
</fieldset>
<fieldset class = "meal-pics">
<!-- image source -->
<img width=40% height=40% id="d-meal-img" style="margin-left: auto; margin-right: auto; display: block;"/>
</fieldset>
<fieldset class = "stars-and-comments" style="text-align: center;">
<img width=30% height=30% id="d-rating" style="margin-left: auto; margin-right: auto; display: block;"/>
<p id = "d-comments"></p>
<fieldset class="meal-name">
<h1 id="d-meal-name" style="font-family: Century Gothic"></h1>
<h1 id="d-restaurant" style="font-family: Century Gothic; font-size: 30px"></h1>
</fieldset>
<fieldset class = "meal-tags">
<div class = "tag-container" id="d-tags" style="justify-content: center;"></div>
<fieldset class="meal-pics">
<!-- image source -->
<img width="40%" height="40%" id="d-meal-img" style="margin-left: auto; margin-right: auto; display: block" />
</fieldset>
<fieldset class="stars-and-comments" style="text-align: center">
<img width="30%" height="30%" id="d-rating" style="margin-left: auto; margin-right: auto; display: block" />
<p id="d-comments"></p>
</fieldset>
<fieldset class="meal-tags">
<div class="tag-container" id="d-tags" style="justify-content: center"></div>
</fieldset>
</form>
</div>
<!---Navigation Buttons-->
<div style="display: flex; justify-content: center;">
<img src="./assets/images/home_button_for_interface.png" style="margin: 20px 10px 20px 10px;" id="home-btn" title="Home Page" onclick="window.location.assign('./index.html')" height="50" width="50"/>
<img src ="./assets/images/edit_button_for_interface.png" style="margin: 20px 10px 20px 10px;" id="update-btn" title="Edit Review" height="50" width="50"/>
<img src ="./assets/images/delete_icon_for_interface.png" style="margin: 20px 10px 20px 10px;" id="delete-btn" title="Delete Review" class="danger" height="50" width="50"/>
<div style="display: flex; justify-content: center">
<img
src="./assets/images/home_button_for_interface.png"
style="margin: 20px 10px 20px 10px"
id="home-btn"
title="Home Page"
onclick="window.location.assign('./index.html')"
height="50"
width="50"
/>
<img
src="./assets/images/edit_button_for_interface.png"
style="margin: 20px 10px 20px 10px"
id="update-btn"
title="Edit Review"
height="50"
width="50"
/>
<img
src="./assets/images/delete_icon_for_interface.png"
style="margin: 20px 10px 20px 10px"
id="delete-btn"
title="Delete Review"
class="danger"
height="50"
width="50"
/>
</div>
</main>
@@ -65,17 +87,16 @@
<h1>Update Entry</h1>
<form id="new-food-entry">
<fieldset>
<legend>PICTURE:</legend>
<select id="select" name="select">
<option value="file">File Upload</option>
<option value="photo">Take a Photo</option>
</select>
<input type="file" accept="image/*" id="mealImg" name="mealImg">
<input type="file" accept="image/*" id="mealImg" name="mealImg" />
</fieldset>
<fieldset>
<fieldset>
<video id="player" width="320" height="240" autoplay hidden></video>
<canvas id="photoCanvas" width="320" height="240" hidden></canvas>
<button type="button" id="photoButton" hidden>Take Photo</button>
@@ -83,43 +104,42 @@
<fieldset>
<legend>MEAL NAME:</legend>
<label for="Name: "> <input type="text" id="mealName" name="mealName" required> </label>
<label for="Name: "> <input type="text" id="mealName" name="mealName" required /> </label>
</fieldset>
<fieldset>
<legend>RESTAURANT NAME:</legend>
<label for="Name:"> <input type="text" id="restaurant" name="restaurant" required> </label>
<label for="Name:"> <input type="text" id="restaurant" name="restaurant" required /> </label>
</fieldset>
<fieldset>
<legend>RATING:</legend>
<div style="display: flex; justify-content: flex-start; align-items: center;">
<div style="display: flex; justify-content: flex-start; align-items: center">
<div class="rating">
<input type="radio" id="s5" name="rating" value="5"/> <label for="s5" id="s5-select"> 5 stars </label>
<input type="radio" id="s4" name="rating" value="4"/> <label for="s4" id="s4-select"> 4 stars </label>
<input type="radio" id="s3" name="rating" value="3"/> <label for="s3" id="s3-select"> 3 stars </label>
<input type="radio" id="s2" name="rating" value="2"/> <label for="s2" id="s2-select"> 2 stars </label>
<input type="radio" id="s1" name="rating" value="1"/> <label for="s1" id="s1-select"> 1 star </label>
<input type="radio" id="s5" name="rating" value="5" /> <label for="s5" id="s5-select"> 5 stars </label>
<input type="radio" id="s4" name="rating" value="4" /> <label for="s4" id="s4-select"> 4 stars </label>
<input type="radio" id="s3" name="rating" value="3" /> <label for="s3" id="s3-select"> 3 stars </label>
<input type="radio" id="s2" name="rating" value="2" /> <label for="s2" id="s2-select"> 2 stars </label>
<input type="radio" id="s1" name="rating" value="1" /> <label for="s1" id="s1-select"> 1 star </label>
</div>
</div>
</fieldset>
<fieldset>
<legend>COMMENTS:</legend>
<textarea name="comments" id="comments" rows="5" style="resize: none; width: 100%;"></textarea>
<textarea name="comments" id="comments" rows="5" style="resize: none; width: 100%"></textarea>
</fieldset>
<fieldset>
<legend>TAGS: (ex. cuisine, distance, cost, etc)</legend>
<input type="text" id="tag-form" name="tag-form">
<input type="text" id="tag-form" name="tag-form" />
<div class='tag-container' id="tag-container-form">
</div>
<button type="button" id="tag-add-btn"> + </button>
<div class="tag-container" id="tag-container-form"></div>
<button type="button" id="tag-add-btn">+</button>
</fieldset>
<button type="submit" id="save-btn" value="Submit">Save</button>
<input type="button" value="Cancel" id="home-btn" onclick="window.location.assign('./index.html')">
<button type="submit" id="save-btn" value="Submit">Save</button>
<input type="button" value="Cancel" id="home-btn" onclick="window.location.assign('./index.html')" />
</form>
</div>
</body>

View File

@@ -13,27 +13,26 @@ function init() {
* Creates a form and associates a new ID with the new review card.
*/
function initFormHandler() {
// Accesses form components
let tagContainer = document.getElementById("tag-container-form");
let form = document.querySelector("form");
// Declaring variable storing image data url
let imgDataURL = "";
// Accessing components related to taking photo
let videoMode = true;
let player = document.getElementById("player");
let canvas = document.getElementById("photoCanvas");
let photoButton = document.getElementById("photoButton");
let context = canvas.getContext('2d');
let context = canvas.getContext("2d");
// Event listener for the photo taking/reset button
photoButton.addEventListener('click', ()=>{
photoButton.addEventListener("click", () => {
// capturing the current video frame
if (videoMode) {
videoMode = false;
// setting up the appropriate components for displaying the photo preview
photoButton.innerText = "Retake";
player.setAttribute("hidden", "");
@@ -57,7 +56,7 @@ function initFormHandler() {
// Event listener for reading image form different data
let select = document.getElementById("select");
const input = document.getElementById("mealImg");
select.addEventListener("change", function() {
select.addEventListener("change", function () {
// Select a photo with HTML file selector
if (select.value == "file") {
// enabling file upload components and hiding photo taking components
@@ -80,27 +79,30 @@ function initFormHandler() {
photoButton.removeAttribute("hidden", "");
// getting video stream from user's camera then displaying it on a video element
navigator.mediaDevices.getUserMedia({video: true,}).then((stream)=>{
navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
player.srcObject = stream;
});
}
});
// Addresses sourcing image from local file
document.getElementById("mealImg").addEventListener("change", function() {
document.getElementById("mealImg").addEventListener("change", function () {
const reader = new FileReader();
// Store image data URL after successful image load
reader.addEventListener("load", ()=>{
imgDataURL = reader.result;
}, false);
reader.addEventListener(
"load",
() => {
imgDataURL = reader.result;
},
false
);
// Convert image file into data URL for local storage
reader.readAsDataURL(document.getElementById("mealImg").files[0]);
});
form.addEventListener("submit", function(e){
form.addEventListener("submit", function (e) {
// Create reviewObject and put in storage
e.preventDefault();
let formData = new FormData(form);
@@ -119,14 +121,13 @@ function initFormHandler() {
}
// Makes sure that ratings is filled
if(reviewObject["rating"] != null){
if (reviewObject["rating"] != null) {
//Adds rags separately as an array
reviewObject["tags"] = [];
// Grabs tags
let tags = document.querySelectorAll(".tag");
for(let i = 0; i < tags.length; i ++) {
for (let i = 0; i < tags.length; i++) {
reviewObject["tags"].push(tags[i].innerHTML);
tagContainer.removeChild(tags[i]);
}
@@ -134,43 +135,41 @@ function initFormHandler() {
// Assigns the new review with a new ID
let nextReviewId = newReviewToStorage(reviewObject);
sessionStorage.setItem("currID", JSON.stringify(nextReviewId));
// Redirects to a page that shows the newly created review
window.location.assign("./ReviewDetails.html");
}
}
// Does not let user proceed if rating is not complete
else{
else {
window.alert("NO! FILL IN STARS");
}
});
// Event listener for tag functionality
let tagAddBtn = document.getElementById("tag-add-btn");
//Set used to track tags and ensure no duplicates
let tagSet = new Set();
tagAddBtn.addEventListener("click", ()=> {
tagAddBtn.addEventListener("click", () => {
let tagField = document.getElementById("tag-form");
// If there is a tag, it'll display the tag
if (tagField.value.length > 0) {
let tagSetVal = tagField.value.toLowerCase();
if (!tagSet.has(tagSetVal)){
if (!tagSet.has(tagSetVal)) {
let tagLabel = document.createElement("label");
tagLabel.innerHTML = tagField.value;
tagLabel.setAttribute("class","tag");
tagLabel.setAttribute("class", "tag");
tagSet.add(tagSetVal);
tagLabel.addEventListener("click",()=> {
tagLabel.addEventListener("click", () => {
tagContainer.removeChild(tagLabel);
tagSet.delete(tagSetVal);
});
tagContainer.append(tagLabel);
} else {
window.alert("No duplicate tags allowed");
}
tagField.value = "";
}
});
});
}

View File

@@ -4,9 +4,9 @@ class ReviewCard extends HTMLElement {
// Called once when document.createElement('review-card') is called, or
// the element is written into the DOM directly as <review-card>
constructor() {
super();
super();
let shadowEl = this.attachShadow({mode:"open"});
let shadowEl = this.attachShadow({ mode: "open" });
let articleEl = document.createElement("article");
@@ -106,7 +106,7 @@ class ReviewCard extends HTMLElement {
articleEl.append(styleEl);
shadowEl.append(articleEl);
this.shadowEl = shadowEl;
// Attach event listener to each review-card
this.addEventListener("click", (event) => {
console.log(event.target);
@@ -119,40 +119,40 @@ class ReviewCard extends HTMLElement {
}
/**
* Called when the .data property is set on this element.
*
* For Example:
* let reviewCard = document.createElement('review-card');
* reviewCard.data = { foo: 'bar' }
*
* @param {Object} data - The data to pass into the <review-card>, must be of the
* following format:
* {
* "mealImg": string,
* "mealName": string,
* "comments": string,
* "rating": number,
* "restaurant": string,
* "reviewID": number,
* "tags": string array
* }
*/
* Called when the .data property is set on this element.
*
* For Example:
* let reviewCard = document.createElement('review-card');
* reviewCard.data = { foo: 'bar' }
*
* @param {Object} data - The data to pass into the <review-card>, must be of the
* following format:
* {
* "mealImg": string,
* "mealName": string,
* "comments": string,
* "rating": number,
* "restaurant": string,
* "reviewID": number,
* "tags": string array
* }
*/
set data(data) {
// If nothing was passed in, return
if (!data) return;
// Select the <article> we added to the Shadow DOM in the constructor
let articleEl = this.shadowEl.querySelector("article");
// Setting the article elements for the review card
this.reviewID = data["reviewID"];
// Image setup
let mealImg = document.createElement("img");
mealImg.setAttribute("id", "a-meal-img");
mealImg.setAttribute("alt","Meal Photo Corrupted");
mealImg.setAttribute("src",data["mealImg"]);
mealImg.addEventListener("error", function(e) {
mealImg.setAttribute("alt", "Meal Photo Corrupted");
mealImg.setAttribute("src", data["mealImg"]);
mealImg.addEventListener("error", function (e) {
mealImg.setAttribute("src", "./assets/images/default_plate.png");
e.onerror = null;
});
@@ -162,14 +162,14 @@ class ReviewCard extends HTMLElement {
meallabelDiv.setAttribute("class", "meal-name-div");
let mealLabel = document.createElement("label");
mealLabel.setAttribute("id", "a-meal-name");
mealLabel.setAttribute("class","meal-name");
mealLabel.setAttribute("class", "meal-name");
mealLabel.innerHTML = data["mealName"];
meallabelDiv.append(mealLabel);
// Restaurant name setup
let restaurantLabel = document.createElement("label");
restaurantLabel.setAttribute("id", "a-restaurant");
restaurantLabel.setAttribute("class","restaurant-name");
restaurantLabel.setAttribute("class", "restaurant-name");
restaurantLabel.innerHTML = data["restaurant"];
// Comment section setup (display set to none)
@@ -183,13 +183,13 @@ class ReviewCard extends HTMLElement {
ratingDiv.setAttribute("class", "rating");
let starsImg = document.createElement("img");
starsImg.setAttribute("id", "a-rating");
starsImg.setAttribute("src", "./assets/images/"+data["rating"]+"-star.svg");
starsImg.setAttribute("alt", data["rating"] +" stars");
starsImg.setAttribute("src", "./assets/images/" + data["rating"] + "-star.svg");
starsImg.setAttribute("alt", data["rating"] + " stars");
starsImg.setAttribute("num", data["rating"]);
ratingDiv.append(starsImg);
// Tags setup
let tagContainerDiv = document.createElement("div");
let tagContainerDiv = document.createElement("div");
tagContainerDiv.setAttribute("class", "tag-container-div");
let tagContainer = document.createElement("div");
tagContainer.setAttribute("class", "tag-container");
@@ -197,10 +197,10 @@ class ReviewCard extends HTMLElement {
tagContainer.setAttribute("list", data["tags"]);
// Checks if user gave tags, if so added to review card
if(data["tags"]){
if (data["tags"]) {
for (let i = 0; i < data["tags"].length; i++) {
let newTag = document.createElement("label");
newTag.setAttribute("class","a-tag");
newTag.setAttribute("class", "a-tag");
newTag.innerHTML = data["tags"][i];
tagContainer.append(newTag);
}
@@ -214,33 +214,30 @@ class ReviewCard extends HTMLElement {
articleEl.append(ratingDiv);
articleEl.append(tagContainerDiv);
articleEl.append(comments);
}
/**
* Called when getting the .data property of this element.
*
* For Example:
* let reviewCard = document.createElement('review-card');
* reviewCard.data = { foo: 'bar' }
*
* @return {Object} data - The data from the <review-card>, of the
* following format:
* {
* "mealImg": string,
* "mealName": string,
* "comments": string,
* "rating": number,
* "restaurant": string,
* "reviewID": number,
* "tags": string array
* }
*/
* Called when getting the .data property of this element.
*
* For Example:
* let reviewCard = document.createElement('review-card');
* reviewCard.data = { foo: 'bar' }
*
* @return {Object} data - The data from the <review-card>, of the
* following format:
* {
* "mealImg": string,
* "mealName": string,
* "comments": string,
* "rating": number,
* "restaurant": string,
* "reviewID": number,
* "tags": string array
* }
*/
get data() {
let dataContainer = {};
// Getting the article elements for the review card
dataContainer["reviewID"] = this.reviewID;

View File

@@ -1,5 +1,5 @@
//reviewDetails.js
import {deleteReviewFromStorage, getReviewFromStorage, updateReviewToStorage} from "./localStorage.js";
import { deleteReviewFromStorage, getReviewFromStorage, updateReviewToStorage } from "./localStorage.js";
// Run the init() function when the page has loaded
window.addEventListener("DOMContentLoaded", init);
@@ -7,7 +7,7 @@ window.addEventListener("DOMContentLoaded", init);
/**
* Populates the relevant data to the details from local storage review.
*/
function init(){
function init() {
setupInfo();
setupDelete();
setupUpdate();
@@ -16,14 +16,14 @@ function init(){
/**
* Populates the relevant data to the details from local storage review
*/
function setupInfo(){
function setupInfo() {
let currID = JSON.parse(sessionStorage.getItem("currID"));
let currReview = getReviewFromStorage(currID);
//meal image
let mealImg = document.getElementById("d-meal-img");
mealImg.setAttribute("src",currReview["mealImg"]);
mealImg.addEventListener("error", function(e) {
mealImg.setAttribute("src", currReview["mealImg"]);
mealImg.addEventListener("error", function (e) {
mealImg.setAttribute("src", "./assets/images/default_plate.png");
e.onerror = null;
});
@@ -35,22 +35,22 @@ function setupInfo(){
//restaurant name
let restaurantLabel = document.getElementById("d-restaurant");
restaurantLabel.innerHTML = currReview["restaurant"];
//comments
let comments = document.getElementById("d-comments");
comments.innerText = currReview["comments"];
//rating
let starsImg = document.getElementById("d-rating");
starsImg.setAttribute("src", "./assets/images/"+currReview["rating"]+"-star.svg");
starsImg.setAttribute("alt", currReview["rating"] +" stars");
starsImg.setAttribute("src", "./assets/images/" + currReview["rating"] + "-star.svg");
starsImg.setAttribute("alt", currReview["rating"] + " stars");
//tags
let tagContainer = document.getElementById("d-tags");
if(currReview["tags"]){
if (currReview["tags"]) {
for (let i = 0; i < currReview["tags"].length; i++) {
let newTag = document.createElement("label");
newTag.setAttribute("class","d-tag");
newTag.setAttribute("class", "d-tag");
newTag.innerHTML = currReview["tags"][i];
tagContainer.append(newTag);
}
@@ -60,11 +60,11 @@ function setupInfo(){
/**
* Sets up delete button to delete reveiw from storage and switch to homepage.
*/
function setupDelete(){
function setupDelete() {
let deleteBtn = document.getElementById("delete-btn");
let currID = JSON.parse(sessionStorage.getItem("currID"));
deleteBtn.addEventListener("click", function(){
if(window.confirm("Are you sure you want to delete this entry?")){
deleteBtn.addEventListener("click", function () {
if (window.confirm("Are you sure you want to delete this entry?")) {
deleteReviewFromStorage(currID);
sessionStorage.removeItem("currID");
window.location.assign("./index.html");
@@ -75,13 +75,13 @@ function setupDelete(){
/**
* Sets up update button to reveal form and update info in storage and the current page.
*/
function setupUpdate(){
function setupUpdate() {
let updateBtn = document.getElementById("update-btn");
let currID = JSON.parse(sessionStorage.getItem("currID"));
let currReview = getReviewFromStorage(currID);
let form = document.getElementById("new-food-entry");
let updateDiv = document.getElementById("update-form");
updateBtn.addEventListener("click", function(){
updateBtn.addEventListener("click", function () {
//update function
updateDiv.classList.remove("hidden");
@@ -98,26 +98,26 @@ function setupUpdate(){
//Set used to track tags and ensure no duplicates
let tagSet = new Set();
if(currReview["tags"]){
if (currReview["tags"]) {
while (tagContainer.firstChild) {
tagContainer.removeChild(tagContainer.firstChild);
}
let tagSetVal;
for (let i = 0; i < currReview["tags"].length; i++) {
tagSetVal = currReview["tags"][i].toLowerCase();
tagSet.add(tagSetVal);
let newTag = document.createElement("label");
newTag.setAttribute("class","tag");
newTag.setAttribute("class", "tag");
newTag.innerHTML = currReview["tags"][i];
newTag.addEventListener("click",()=> {
newTag.addEventListener("click", () => {
tagContainer.removeChild(newTag);
tagSet.delete(tagSetVal);
});
tagContainer.append(newTag);
}
}
// Declaring variable storing image data url
let imgDataURL = "";
@@ -126,14 +126,14 @@ function setupUpdate(){
let player = document.getElementById("player");
let canvas = document.getElementById("photoCanvas");
let photoButton = document.getElementById("photoButton");
let context = canvas.getContext('2d');
let context = canvas.getContext("2d");
// Event listener for the photo taking/reset button
photoButton.addEventListener('click', ()=>{
photoButton.addEventListener("click", () => {
// capturing the current video frame
if (videoMode) {
videoMode = false;
// setting up the appropriate components for displaying the photo preview
photoButton.innerText = "Retake";
player.setAttribute("hidden", "");
@@ -155,25 +155,25 @@ function setupUpdate(){
});
/*
* change the input source of the image between local file and taking photo
* depending on user's selection
*/
* change the input source of the image between local file and taking photo
* depending on user's selection
*/
let select = document.getElementById("select");
const input = document.getElementById("mealImg");
select.addEventListener("change", function() {
select.addEventListener("change", function () {
console.log("1");
// Select a photo with HTML file selector
// Select a photo with HTML file selector
if (select.value == "file") {
// enabling file upload components and hiding photo taking components
input.removeAttribute("hidden", "");
player.setAttribute("hidden", "");
canvas.setAttribute("hidden", "");
photoButton.setAttribute("hidden", "");
// stopping the video stream
player.srcObject.getVideoTracks()[0].stop();
}
// Take a photo
else {
// enabling photo taking components and hiding file upload components
@@ -182,35 +182,38 @@ function setupUpdate(){
input.setAttribute("hidden", "");
player.removeAttribute("hidden", "");
photoButton.removeAttribute("hidden", "");
// getting video stream from user's camera then displaying it on a video element
navigator.mediaDevices.getUserMedia({video: true,}).then((stream)=>{
navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
player.srcObject = stream;
});
}
});
//addressing sourcing image from local file
document.getElementById("mealImg").addEventListener("change", function() {
document.getElementById("mealImg").addEventListener("change", function () {
console.log("reading used");
const reader = new FileReader();
//store image data URL after successful image load
reader.addEventListener("load", ()=>{
imgDataURL = reader.result;
}, false);
reader.addEventListener(
"load",
() => {
imgDataURL = reader.result;
},
false
);
//convert image file into data URL for local storage
reader.readAsDataURL(document.getElementById("mealImg").files[0]);
});
//Take formdata values as newData when submit
form.addEventListener("submit", function(){
form.addEventListener("submit", function () {
/*
* User submits the form for their review.
* We create reviewCard data, replace in storage, and update tags
*/
* User submits the form for their review.
* We create reviewCard data, replace in storage, and update tags
*/
let formData = new FormData(form);
let newData = {};
//iterate through formData and add to newData
@@ -223,43 +226,41 @@ function setupUpdate(){
// Account for the case where image is not updated
if (`${key}` === "mealImg" && imgDataURL === "") {
newData["mealImg"] = currReview["mealImg"];
}
else if (`${key}` === "mealImg") {
} else if (`${key}` === "mealImg") {
newData["mealImg"] = imgDataURL;
}
}
newData["tags"] = [];
let tags = document.querySelectorAll(".tag");
for(let i = 0; i < tags.length; i ++) {
for (let i = 0; i < tags.length; i++) {
newData["tags"].push(tags[i].innerHTML);
tagContainer.removeChild(tags[i]);
}
newData["reviewID"] = currID;
updateReviewToStorage(currID, newData);
updateDiv.classList.add("hidden");
});
// Adding tag to form functionality
let tagAddBtn = document.getElementById("tag-add-btn");
tagAddBtn.addEventListener("click", ()=> {
tagAddBtn.addEventListener("click", () => {
let tagField = document.getElementById("tag-form");
if (tagField.value.length > 0) {
let tagSetVal = tagField.value.toLowerCase();
if (!tagSet.has(tagSetVal)){
if (!tagSet.has(tagSetVal)) {
let tagLabel = document.createElement("label");
tagLabel.innerHTML = tagField.value;
tagLabel.setAttribute("class","tag");
tagLabel.setAttribute("class", "tag");
tagSet.add(tagSetVal);
tagLabel.addEventListener("click",()=> {
tagLabel.addEventListener("click", () => {
tagContainer.removeChild(tagLabel);
tagSet.delete(tagSetVal);
});
tagContainer.append(tagLabel);
} else {
window.alert("No duplicate tags allowed");

View File

@@ -1,35 +1,34 @@
import {strict as assert} from "node:assert";
import { strict as assert } from "node:assert";
/**
* Fills out a create or update review form
* @param {Object} page the page object which contains the create or update form
* @param {Object} review review data to input into the form
* @param {Object} review review data to input into the form
*/
export async function setReviewForm(page, review) {
// Set text fields
await page.$eval("#mealName", (el, value) => el.value = value, review.mealName);
await page.$eval("#comments", (el, value) => el.value = value, review.comments);
await page.$eval("#restaurant", (el, value) => el.value = value, review.restaurant);
await page.$eval("#mealName", (el, value) => (el.value = value), review.mealName);
await page.$eval("#comments", (el, value) => (el.value = value), review.comments);
await page.$eval("#restaurant", (el, value) => (el.value = value), review.restaurant);
// Get all tag elements and click them to delete them
let tag_items = await page.$$(".tag");
if(tag_items !== null){
for(let i = 0; i < tag_items.length; i++){
if (tag_items !== null) {
for (let i = 0; i < tag_items.length; i++) {
await tag_items[i].click();
}
}
// Get the button needed to add new tags
let tag_btn = await page.$("#tag-add-btn");
for(let i = 0; i < review.tags.length; i++){
await page.$eval("#tag-form", (el, value) => el.value = value, review.tags[i]);
for (let i = 0; i < review.tags.length; i++) {
await page.$eval("#tag-form", (el, value) => (el.value = value), review.tags[i]);
await tag_btn.click();
}
// Select a new rating
let rating_select = await page.$(`#s${review.rating}-select`);
await rating_select.click({delay: 100});
await rating_select.click({ delay: 100 });
}
/**
@@ -38,7 +37,7 @@ export async function setReviewForm(page, review) {
* @param {string} prefix prefix character for element IDs
* @param {Object} expected values for each element
*/
export async function checkCorrectness(root, prefix, expected){
export async function checkCorrectness(root, prefix, expected) {
// Get the review image and check src
let img = await root.$(`#${prefix}-meal-img`);
let imgSrc = await img.getProperty("src");
@@ -61,7 +60,7 @@ export async function checkCorrectness(root, prefix, expected){
// Check tags
let tags = await root.$$(`.${prefix}-tag`);
assert.strictEqual(await tags.length, expected.tags.length);
for(let i = 0; i < expected.tags.length; i++){
for (let i = 0; i < expected.tags.length; i++) {
let tag_text = await tags[i].getProperty("innerText");
assert.strictEqual(await tag_text.jsonValue(), expected.tags[i]);
}
@@ -70,4 +69,4 @@ export async function checkCorrectness(root, prefix, expected){
let stars = await root.$(`#${prefix}-rating`);
let stars_src = await stars.getProperty("src");
assert.strictEqual(await stars_src.jsonValue(), expected.rating);
}
}

View File

@@ -3,7 +3,7 @@
* @param {Object} review to store
* @return {number} ID of the newly added review
*/
export function newReviewToStorage(review){
export function newReviewToStorage(review) {
//grabbing the nextID, and putting our review object in storage associated with the ID
let nextReviewId = JSON.parse(localStorage.getItem("nextID"));
review["reviewID"] = nextReviewId;
@@ -16,17 +16,17 @@ export function newReviewToStorage(review){
//adding to the star storage
let starArr = JSON.parse(localStorage.getItem(`star${review["rating"]}`));
if(!starArr){
if (!starArr) {
starArr = [];
}
starArr.push(nextReviewId);
localStorage.setItem(`star${review["rating"]}`, JSON.stringify(starArr));
//updating our activeIDS list
let tempIdArr = JSON.parse(localStorage.getItem("activeIDS"));
tempIdArr.push(nextReviewId);
localStorage.setItem("activeIDS", JSON.stringify(tempIdArr));
//increment nextID for next review creation
localStorage.setItem("nextID", JSON.stringify(nextReviewId + 1));
@@ -38,7 +38,7 @@ export function newReviewToStorage(review){
* @param {string} ID of the review to get
* @returns {Object} review object corresponding to param ID
*/
export function getReviewFromStorage(ID){
export function getReviewFromStorage(ID) {
return JSON.parse(localStorage.getItem(`review${ID}`));
}
@@ -47,15 +47,15 @@ export function getReviewFromStorage(ID){
* @param {string} ID of review to update
* @param {Object} review to store
*/
export function updateReviewToStorage(ID, review){
export function updateReviewToStorage(ID, review) {
let oldReview = JSON.parse(localStorage.getItem(`review${ID}`));
let starArr = JSON.parse(localStorage.getItem(`star${review["rating"]}`));
//activeID update recency
let activeIDS = JSON.parse(localStorage.getItem("activeIDS"));
for (let i in activeIDS){
if(activeIDS[i] == ID){
activeIDS.splice(i,1);
for (let i in activeIDS) {
if (activeIDS[i] == ID) {
activeIDS.splice(i, 1);
activeIDS.push(ID);
break;
}
@@ -63,33 +63,33 @@ export function updateReviewToStorage(ID, review){
localStorage.setItem("activeIDS", JSON.stringify(activeIDS));
//star local storage update
if(oldReview["rating"] !== review["rating"]){
if (oldReview["rating"] !== review["rating"]) {
//first delete from previous rating array in storage
let oldStarArr = JSON.parse(localStorage.getItem(`star${oldReview["rating"]}`));
for (let i in oldStarArr) {
if (oldStarArr[i] == ID) {
//removing from corresponding rating array and updating local Storage
oldStarArr.splice(i,1);
oldStarArr.splice(i, 1);
break;
}
}
if(oldStarArr.length != 0){
if (oldStarArr.length != 0) {
localStorage.setItem(`star${oldReview["rating"]}`, JSON.stringify(oldStarArr));
} else {
localStorage.removeItem(`star${oldReview["rating"]}`);
}
//then add ID to array corresponding to new review rating
let newStarArr = starArr;
if(!newStarArr){
if (!newStarArr) {
newStarArr = [];
}
newStarArr.push(ID);
localStorage.setItem(`star${review["rating"]}`, JSON.stringify(newStarArr));
} else if(starArr.length !== 1) {
} else if (starArr.length !== 1) {
//stars update recency if unchanged
for (let i in starArr){
if(starArr[i] == ID) {
starArr.splice(i,1);
for (let i in starArr) {
if (starArr[i] == ID) {
starArr.splice(i, 1);
starArr.push(ID);
break;
}
@@ -98,14 +98,14 @@ export function updateReviewToStorage(ID, review){
}
//specifically the unchanged tags update recency
let repeatedTags = review["tags"].filter(x => oldReview["tags"].includes(x));
let repeatedTags = review["tags"].filter((x) => oldReview["tags"].includes(x));
let tagArr = [];
for (let i in repeatedTags){
for (let i in repeatedTags) {
tagArr = JSON.parse(localStorage.getItem(`!${repeatedTags[i]}`.toLocaleLowerCase()));
if(tagArr.length == 1){
for (let j in tagArr){
if(tagArr[j] == ID){
tagArr.splice(j,1);
if (tagArr.length == 1) {
for (let j in tagArr) {
if (tagArr[j] == ID) {
tagArr.splice(j, 1);
tagArr.push(ID);
break;
}
@@ -115,8 +115,8 @@ export function updateReviewToStorage(ID, review){
}
//Get diff of tags and update storage
let deletedTags = oldReview["tags"].filter(x => !review["tags"].includes(x));
let addedTags = review["tags"].filter(x => !oldReview["tags"].includes(x));
let deletedTags = oldReview["tags"].filter((x) => !review["tags"].includes(x));
let addedTags = review["tags"].filter((x) => !oldReview["tags"].includes(x));
deleteTagsFromStorage(ID, deletedTags);
addTagsToStorage(ID, addedTags);
@@ -128,30 +128,30 @@ export function updateReviewToStorage(ID, review){
* Deletes a review by ID from storage
* @param {string} ID of the review to delete
*/
export function deleteReviewFromStorage(ID){
export function deleteReviewFromStorage(ID) {
//removing id number from activeIDS and star{rating}
let activeIDS = JSON.parse(localStorage.getItem("activeIDS"));
let reviewRating = JSON.parse(localStorage.getItem(`review${ID}`))["rating"];
let starArr = JSON.parse(localStorage.getItem(`star${reviewRating}`));
for (let i in starArr) {
if (starArr[i] == ID) {
//removing from corresponding rating array and updating local Storage
starArr.splice(i,1);
starArr.splice(i, 1);
break;
}
}
if(starArr.length != 0){
if (starArr.length != 0) {
localStorage.setItem(`star${reviewRating}`, JSON.stringify(starArr));
} else {
localStorage.removeItem(`star${reviewRating}`);
}
for (let i in activeIDS) {
if (activeIDS[i] == ID) {
activeIDS.splice(i,1);
activeIDS.splice(i, 1);
localStorage.setItem("activeIDS", JSON.stringify(activeIDS));
let currReview = JSON.parse(localStorage.getItem(`review${ID}`));
deleteTagsFromStorage(ID, currReview["tags"]);
localStorage.removeItem(`review${ID}`);
@@ -168,17 +168,17 @@ export function deleteReviewFromStorage(ID){
* @param {string[]} deletedTags to modify storage of
*/
function deleteTagsFromStorage(ID, deletedTags) {
for(let i in deletedTags){
for (let i in deletedTags) {
//get local storage of each tag and remove id from tag list
let tagName = "!"+ deletedTags[i].toLowerCase();
let tagName = "!" + deletedTags[i].toLowerCase();
let tagArr = JSON.parse(localStorage.getItem(tagName));
for(let j in tagArr){
if(tagArr[j] == ID){
tagArr.splice(j,1);
for (let j in tagArr) {
if (tagArr[j] == ID) {
tagArr.splice(j, 1);
break;
}
}
if(tagArr.length != 0){
if (tagArr.length != 0) {
localStorage.setItem(tagName, JSON.stringify(tagArr));
} else {
localStorage.removeItem(tagName);
@@ -192,10 +192,10 @@ function deleteTagsFromStorage(ID, deletedTags) {
* @param {string[]} addedTags to modify storage of
*/
function addTagsToStorage(ID, addedTags) {
for(let i in addedTags){
for (let i in addedTags) {
let tagName = "!" + addedTags[i].toLowerCase();
let tagArr = JSON.parse(localStorage.getItem(tagName));
if(!tagArr){
if (!tagArr) {
tagArr = [];
}
tagArr.push(ID);
@@ -208,10 +208,10 @@ function addTagsToStorage(ID, addedTags) {
* @returns {Object} all active reviews from local storage
*/
export function getAllReviewsFromStorage() {
if (!(localStorage.getItem("activeIDS"))) {
if (!localStorage.getItem("activeIDS")) {
// we wanna init the active ID array and start the nextID count
localStorage.setItem("activeIDS", JSON.stringify([]));
localStorage.setItem("nextID", JSON.stringify(0));
localStorage.setItem("nextID", JSON.stringify(0));
}
//iterate thru activeIDS
let activeIDS = JSON.parse(localStorage.getItem("activeIDS"));
@@ -228,10 +228,10 @@ export function getAllReviewsFromStorage() {
* @returns {number[]} list of all active IDs by recency
*/
export function getIDsFromStorage() {
if (!(localStorage.getItem("activeIDS"))) {
if (!localStorage.getItem("activeIDS")) {
// we wanna init the active ID array and start the nextID count
localStorage.setItem("activeIDS", JSON.stringify([]));
localStorage.setItem("nextID", JSON.stringify(0));
localStorage.setItem("nextID", JSON.stringify(0));
}
let activeIDS = JSON.parse(localStorage.getItem("activeIDS"));
return activeIDS.reverse();
@@ -244,7 +244,7 @@ export function getIDsFromStorage() {
*/
export function getIDsByTag(tag) {
let tagArr = JSON.parse(localStorage.getItem("!" + tag.toLowerCase()));
if(!tagArr){
if (!tagArr) {
tagArr = [];
}
return tagArr.reverse();
@@ -256,12 +256,12 @@ export function getIDsByTag(tag) {
*/
export function getTopIDsFromStorage() {
let resultArr = [];
for(let i = 5; i > 0; i--){
for (let i = 5; i > 0; i--) {
let starArr = JSON.parse(localStorage.getItem(`star${i}`));
if(!starArr){
if (!starArr) {
continue;
}
resultArr = resultArr.concat(starArr.reverse());
}
return resultArr;
}
}

View File

@@ -1,9 +1,16 @@
import {strict as assert} from "node:assert";
import {describe, it, before, after} from "mocha";
import {newReviewToStorage, getReviewFromStorage, updateReviewToStorage, deleteReviewFromStorage, getAllReviewsFromStorage, getIDsByTag, getTopIDsFromStorage} from "./localStorage.js";
import { strict as assert } from "node:assert";
import { describe, it, before, after } from "mocha";
import {
newReviewToStorage,
getReviewFromStorage,
updateReviewToStorage,
deleteReviewFromStorage,
getAllReviewsFromStorage,
getIDsByTag,
getTopIDsFromStorage,
} from "./localStorage.js";
describe("test CRUD localStorage interaction", () => {
before(() => {
localStorage.clear();
});
@@ -16,11 +23,11 @@ describe("test CRUD localStorage interaction", () => {
it("test localStorage state after adding one review", () => {
let review = {
"imgSrc": "sample src",
"mealName": "sample name",
"restaurant": "sample restaurant",
"rating": 5,
"tags": ["tag 1", "tag 2", "tag 3"]
imgSrc: "sample src",
mealName: "sample name",
restaurant: "sample restaurant",
rating: 5,
tags: ["tag 1", "tag 2", "tag 3"],
};
newReviewToStorage(review);
@@ -37,38 +44,38 @@ describe("test CRUD localStorage interaction", () => {
let reviews = getAllReviewsFromStorage();
let ids = [0];
for(let i = 1; i < 1000; i++){
for (let i = 1; i < 1000; i++) {
ids.push(i);
let new_review = {
"imgSrc": `sample src ${i}`,
"mealName": `sample name ${i}`,
"restaurant": `sample restaurant ${i}`,
"rating": i,
"tags": [`tag ${3*i}`, `tag ${3*i + 1}`, `tag ${3*i + 2}`]
imgSrc: `sample src ${i}`,
mealName: `sample name ${i}`,
restaurant: `sample restaurant ${i}`,
rating: i,
tags: [`tag ${3 * i}`, `tag ${3 * i + 1}`, `tag ${3 * i + 2}`],
};
new_review.reviewID = newReviewToStorage(new_review);
new_review.reviewID = newReviewToStorage(new_review);
reviews.push(new_review);
assert.deepEqual(getAllReviewsFromStorage(), reviews);
assert.deepEqual(getReviewFromStorage(i), new_review);
assert.deepEqual(JSON.parse(localStorage.getItem("activeIDS")), ids);
assert.strictEqual(JSON.parse(localStorage.getItem("nextID")), (i+1));
assert.strictEqual(JSON.parse(localStorage.getItem("nextID")), i + 1);
}
}).timeout(5000);
it("test localStorage state during updating 1000 reviews", () => {
for(let i = 0; i < 1000; i++){
for (let i = 0; i < 1000; i++) {
let old_review = getReviewFromStorage(i);
let id = old_review.reviewID;
let new_review = {
"imgSrc": `updated sample src ${id}`,
"mealName": `updated sample name ${id}`,
"restaurant": `updated sample restaurant ${id}`,
"reviewID": id,
"rating": (id % 5) + 1,
"tags": [`tag ${3*id}`, `tag ${3*id + 1}`, `tag ${3*id + 2}`]
imgSrc: `updated sample src ${id}`,
mealName: `updated sample name ${id}`,
restaurant: `updated sample restaurant ${id}`,
reviewID: id,
rating: (id % 5) + 1,
tags: [`tag ${3 * id}`, `tag ${3 * id + 1}`, `tag ${3 * id + 2}`],
};
updateReviewToStorage(id, new_review);
@@ -87,7 +94,7 @@ describe("test CRUD localStorage interaction", () => {
let reviews = getAllReviewsFromStorage();
let ids = JSON.parse(localStorage.getItem("activeIDS"));
for(let i = 999; i >= 0; i--){
for (let i = 999; i >= 0; i--) {
deleteReviewFromStorage(i);
ids.pop();
reviews.pop();
@@ -106,30 +113,29 @@ describe("test CRUD localStorage interaction", () => {
});
describe("test sort/filter localStorage interaction", () => {
before(() => {
localStorage.clear();
getAllReviewsFromStorage();
});
it("add sample data for sort and filter", () => {
for(let i = 0; i < 100; i++){
for (let i = 0; i < 100; i++) {
let review = {
"imgSrc": `sample src ${i}`,
"mealName": `sample name ${i}`,
"restaurant": `sample restaurant ${i}`,
"rating": (i % 5) + 1,
"tags": [`tag ${i%3}`, `tag ${i < 50}`, "tag x"]
imgSrc: `sample src ${i}`,
mealName: `sample name ${i}`,
restaurant: `sample restaurant ${i}`,
rating: (i % 5) + 1,
tags: [`tag ${i % 3}`, `tag ${i < 50}`, "tag x"],
};
newReviewToStorage(review);
}
});
it("test getTopIDsFromStorage end behavior after create", () =>{
it("test getTopIDsFromStorage end behavior after create", () => {
let top_reviews = getTopIDsFromStorage();
let prev = Infinity;
for(let i = 0; i < top_reviews.length; i++){
for (let i = 0; i < top_reviews.length; i++) {
let review = getReviewFromStorage(top_reviews[i]);
assert.strictEqual(review.rating <= prev, true);
}
@@ -140,7 +146,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag 0");
assert.strictEqual(specific_tagged_reviews.length, 34);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 0"), true);
assert.strictEqual(review.reviewID % 3, 0);
@@ -148,63 +154,63 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag 1");
assert.strictEqual(specific_tagged_reviews.length, 33);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 1"), true);
assert.strictEqual(review.reviewID % 3, 1);
}
specific_tagged_reviews = getIDsByTag("tag 2");
specific_tagged_reviews = getIDsByTag("tag 2");
assert.strictEqual(specific_tagged_reviews.length, 33);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 2"), true);
assert.strictEqual(review.reviewID % 3, 2);
}
specific_tagged_reviews = getIDsByTag("tag true");
specific_tagged_reviews = getIDsByTag("tag true");
assert.strictEqual(specific_tagged_reviews.length, 50);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag true"), true);
assert.strictEqual(review.reviewID < 50, true);
}
specific_tagged_reviews = getIDsByTag("tag false");
specific_tagged_reviews = getIDsByTag("tag false");
assert.strictEqual(specific_tagged_reviews.length, 50);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag false"), true);
assert.strictEqual(review.reviewID >= 50, true);
}
specific_tagged_reviews = getIDsByTag("tag x");
specific_tagged_reviews = getIDsByTag("tag x");
assert.strictEqual(specific_tagged_reviews.length, 100);
specific_tagged_reviews = getIDsByTag("tag y");
specific_tagged_reviews = getIDsByTag("tag y");
assert.deepEqual(specific_tagged_reviews, []);
});
it("update sample data for sort and filter", () => {
for(let i = 0; i < 100; i++){
for (let i = 0; i < 100; i++) {
let old_review = getReviewFromStorage(i);
let new_review = {
"imgSrc": `sample src ${i}`,
"mealName": `sample name ${i}`,
"restaurant": `sample restaurant ${i}`,
"reviewID": old_review.reviewID,
"rating": (i % 5) + 1,
"tags": [`tag ${i % 4}`, `tag ${i < 37}`, "tag y"]
imgSrc: `sample src ${i}`,
mealName: `sample name ${i}`,
restaurant: `sample restaurant ${i}`,
reviewID: old_review.reviewID,
rating: (i % 5) + 1,
tags: [`tag ${i % 4}`, `tag ${i < 37}`, "tag y"],
};
updateReviewToStorage(old_review.reviewID, new_review);
}
});
it("test getTopIDsFromStorage end behavior after create", () =>{
it("test getTopIDsFromStorage end behavior after create", () => {
let top_reviews = getTopIDsFromStorage();
let prev = Infinity;
for(let i = 0; i < top_reviews.length; i++){
for (let i = 0; i < top_reviews.length; i++) {
let review = getReviewFromStorage(top_reviews[i]);
assert.strictEqual(review.rating <= prev, true);
}
@@ -215,7 +221,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag 0");
assert.strictEqual(specific_tagged_reviews.length, 25);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 0"), true);
assert.strictEqual(review.reviewID % 4, 0);
@@ -223,7 +229,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag 1");
assert.strictEqual(specific_tagged_reviews.length, 25);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 1"), true);
assert.strictEqual(review.reviewID % 4, 1);
@@ -231,7 +237,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag 2");
assert.strictEqual(specific_tagged_reviews.length, 25);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 2"), true);
assert.strictEqual(review.reviewID % 4, 2);
@@ -239,7 +245,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag 3");
assert.strictEqual(specific_tagged_reviews.length, 25);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag 3"), true);
assert.strictEqual(review.reviewID % 4, 3);
@@ -247,7 +253,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag true");
assert.strictEqual(specific_tagged_reviews.length, 37);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag true"), true);
assert.strictEqual(review.reviewID < 37, true);
@@ -255,7 +261,7 @@ describe("test sort/filter localStorage interaction", () => {
specific_tagged_reviews = getIDsByTag("tag false");
assert.strictEqual(specific_tagged_reviews.length, 63);
for(let i = 0; i < specific_tagged_reviews.length; i++){
for (let i = 0; i < specific_tagged_reviews.length; i++) {
let review = getReviewFromStorage(specific_tagged_reviews[i]);
assert.strictEqual(review.tags.includes("tag false"), true);
assert.strictEqual(review.reviewID >= 37, true);
@@ -269,13 +275,13 @@ describe("test sort/filter localStorage interaction", () => {
});
it("delete all sample data for sort and filter", () => {
for(let i = 0; i < 100; i++){
for (let i = 0; i < 100; i++) {
deleteReviewFromStorage(i);
}
});
it("test getTopIDsFromStorage end behavior after delete", () =>{
for(let i = 0; i <= 100; i++){
it("test getTopIDsFromStorage end behavior after delete", () => {
for (let i = 0; i <= 100; i++) {
let top_reviews = getTopIDsFromStorage(i);
assert.deepEqual(top_reviews, []);
}
@@ -310,4 +316,4 @@ describe("test sort/filter localStorage interaction", () => {
});
after(() => {});
});
});

View File

@@ -1,30 +1,27 @@
import {strict as assert} from "node:assert";
import {describe, it, before, after} from "mocha";
import { strict as assert } from "node:assert";
import { describe, it, before, after } from "mocha";
import puppeteer from "puppeteer-core";
import {setReviewForm, checkCorrectness} from "./appTestHelpers.js";
import { setReviewForm, checkCorrectness } from "./appTestHelpers.js";
describe("test App end to end", async () => {
let browser;
let page;
before(async () => {
let root;
try {
root = process.getuid() == 0;
}
catch (error) {
root = process.getuid() == 0;
} catch (error) {
root = false;
}
//browser = await puppeteer.launch({headless: false, slowMo: 250, args: root ? ['--no-sandbox'] : undefined});
browser = await puppeteer.launch({args: root ? ["--no-sandbox"] : undefined});
browser = await puppeteer.launch({ args: root ? ["--no-sandbox"] : undefined });
page = await browser.newPage();
try{
await page.goto("http://localhost:8080", {timeout: 2000});
try {
await page.goto("http://localhost:8080", { timeout: 2000 });
await console.log(`✔ connected to localhost webserver as ${root ? "root" : "user"}`);
}
catch (error) {
} catch (error) {
await console.log("❌ failed to connect to localhost webserver on port 8080");
}
});
@@ -36,7 +33,6 @@ describe("test App end to end", async () => {
});
describe("test CRUD on simple inputs and default image", () => {
describe("test create 1 new review", async () => {
it("create 1 new review", async () => {
// Click the button to create a new review
@@ -50,7 +46,7 @@ describe("test App end to end", async () => {
comments: "sample comment",
restaurant: "sample restaurant",
tags: ["tag 0", "tag 1", "tag 2", "tag 3", "tag 4"],
rating: 1
rating: 1,
};
await setReviewForm(page, review);
@@ -68,11 +64,11 @@ describe("test App end to end", async () => {
comments: "sample comment",
restaurant: "sample restaurant",
tags: ["tag 0", "tag 1", "tag 2", "tag 3", "tag 4"],
rating: "http://localhost:8080/assets/images/1-star.svg"
rating: "http://localhost:8080/assets/images/1-star.svg",
};
await checkCorrectness(page, "d", expected);
});
it("check home page", async () => {
// Click the button to return to the home page
let home_btn = await page.$("#home-btn");
@@ -89,7 +85,7 @@ describe("test App end to end", async () => {
comments: "sample comment",
restaurant: "sample restaurant",
tags: ["tag 0", "tag 1", "tag 2", "tag 3", "tag 4"],
rating: "http://localhost:8080/assets/images/1-star.svg"
rating: "http://localhost:8080/assets/images/1-star.svg",
};
await checkCorrectness(shadowRoot, "a", expected);
});
@@ -114,7 +110,7 @@ describe("test App end to end", async () => {
comments: "sample comment",
restaurant: "sample restaurant",
tags: ["tag 0", "tag 1", "tag 2", "tag 3", "tag 4"],
rating: "http://localhost:8080/assets/images/1-star.svg"
rating: "http://localhost:8080/assets/images/1-star.svg",
};
await checkCorrectness(page, "d", expected);
});
@@ -136,16 +132,14 @@ describe("test App end to end", async () => {
comments: "sample comment",
restaurant: "sample restaurant",
tags: ["tag 0", "tag 1", "tag 2", "tag 3", "tag 4"],
rating: "http://localhost:8080/assets/images/1-star.svg"
rating: "http://localhost:8080/assets/images/1-star.svg",
};
await checkCorrectness(shadowRoot, "a", expected);
});
});
describe("test update 1 review", async () => {
it("update 1 review", async () => {
// Get the only review card and click it
let review_card = await page.$("review-card");
await review_card.click();
@@ -161,7 +155,7 @@ describe("test App end to end", async () => {
comments: "updated comment",
restaurant: "updated restaurant",
tags: ["tag -0", "tag -1", "tag -2", "tag -3", "tag -4", "tag -5"],
rating: 5
rating: 5,
};
await setReviewForm(page, review);
@@ -179,7 +173,7 @@ describe("test App end to end", async () => {
comments: "updated comment",
restaurant: "updated restaurant",
tags: ["tag -0", "tag -1", "tag -2", "tag -3", "tag -4", "tag -5"],
rating: "http://localhost:8080/assets/images/5-star.svg"
rating: "http://localhost:8080/assets/images/5-star.svg",
};
await checkCorrectness(page, "d", expected);
});
@@ -201,11 +195,10 @@ describe("test App end to end", async () => {
comments: "updated comment",
restaurant: "updated restaurant",
tags: ["tag -0", "tag -1", "tag -2", "tag -3", "tag -4", "tag -5"],
rating: "http://localhost:8080/assets/images/5-star.svg"
rating: "http://localhost:8080/assets/images/5-star.svg",
};
await checkCorrectness(shadowRoot, "a", expected);
});
});
describe("test delete 1 review", async () => {
@@ -215,7 +208,7 @@ describe("test App end to end", async () => {
await review_card.click();
await page.waitForNavigation();
page.on("dialog", async dialog => {
page.on("dialog", async (dialog) => {
console.log(dialog.message());
await dialog.accept();
});
@@ -227,4 +220,4 @@ describe("test App end to end", async () => {
await page.close();
await browser.close();
});
});
});

View File

@@ -1,5 +1,5 @@
// main.js
import {getIDsByTag, getIDsFromStorage, getReviewFromStorage, getTopIDsFromStorage} from "./localStorage.js";
import { getIDsByTag, getIDsFromStorage, getReviewFromStorage, getTopIDsFromStorage } from "./localStorage.js";
// Run the init() function when the page has loaded
window.addEventListener("DOMContentLoaded", init);
@@ -11,25 +11,22 @@ function init() {
initFormHandler();
}
/**
* @param {Array<Object>} reviews An array of reviews
*/
function addReviewsToDocument(reviews) {
let reviewBox = document.getElementById("review-container");
reviews.forEach(review => {
reviews.forEach((review) => {
let newReview = document.createElement("review-card");
newReview.data = review;
reviewBox.append(newReview);
});
}
/**
* Adds the necessary event handlers to search-btn and sort
*/
function initFormHandler() {
//grabbing search field
let searchField = document.getElementById("search-bar");
let searchBtn = document.getElementById("search-btn");
@@ -37,14 +34,14 @@ function initFormHandler() {
//adding search functionality
//TODO: Add ability to enter without refresh of search bar
//filter by selected tag when button clicked
searchBtn.addEventListener("click", function(){
searchBtn.addEventListener("click", function () {
searchTag = searchField.value;
sortAndFilter(searchTag);
});
//for clearing tag filter
let clearSearchBtn = document.getElementById("clear-search");
clearSearchBtn.addEventListener("click", function(){
clearSearchBtn.addEventListener("click", function () {
searchTag = null;
searchField.value = "";
sortAndFilter(searchTag);
@@ -52,45 +49,43 @@ function initFormHandler() {
//sort by selected method
let sortMethod = document.getElementById("sort");
sortMethod.addEventListener("input", function(){
sortMethod.addEventListener("input", function () {
sortAndFilter(searchTag);
});
}
/**
* Deciphers sort and filter to populate the review-container
* @param {string} searchTag tag name to filter by
*/
function sortAndFilter(searchTag){
function sortAndFilter(searchTag) {
let reviewBox = document.getElementById("review-container");
let sortMethod = document.getElementById("sort");
//clear review container
while(reviewBox.firstChild){
while (reviewBox.firstChild) {
reviewBox.removeChild(reviewBox.firstChild);
}
let reviewIDs = [];
//sort method: most recent
if(sortMethod.value == "recent"){
if (sortMethod.value == "recent") {
//tag filtered most recent
if(searchTag){
if (searchTag) {
reviewIDs = getIDsByTag(searchTag);
}
}
//most recent
else {
reviewIDs = getIDsFromStorage();
}
//reversed for recency
loadReviews(0, reviewIDs);
}
}
//sort method: top rated
else if (sortMethod.value == "top"){
else if (sortMethod.value == "top") {
//tag filtered top rated
if(searchTag){
if (searchTag) {
//intersection of top ids list and ids by tag in top ids order
reviewIDs = getTopIDsFromStorage().filter(x => getIDsByTag(searchTag).includes(x));
}
reviewIDs = getTopIDsFromStorage().filter((x) => getIDsByTag(searchTag).includes(x));
}
//top rated
else {
reviewIDs = getTopIDsFromStorage();
@@ -104,36 +99,36 @@ function sortAndFilter(searchTag){
* @param {number} index review index to begin with
* @param {number[]} reviewIDs ordered array of reviews
*/
function loadReviews(index, reviewIDs){
function loadReviews(index, reviewIDs) {
let reviewBox = document.getElementById("review-container");
// label if there are no reviews to display
if(reviewIDs.length == 0){
if (reviewIDs.length == 0) {
let emptyLabel = document.createElement("label");
emptyLabel.setAttribute("id", "empty");
emptyLabel.innerText = "No Reviews To Display";
reviewBox.append(emptyLabel);
} else {
let emptyLabel = document.getElementById("empty");
if(emptyLabel){
if (emptyLabel) {
reviewBox.removeChild(emptyLabel);
}
}
let moreBtn = document.getElementById("more-btn");
//delete load more button if exists
if(moreBtn){
if (moreBtn) {
reviewBox.removeChild(moreBtn);
}
let reviewArr = [];
//check if there are more than 9 reviews left
if(index + 9 > reviewIDs.length - 1){
if (index + 9 > reviewIDs.length - 1) {
//add remaining reviews to review container
for(let i = index; i < reviewIDs.length; i++){
for (let i = index; i < reviewIDs.length; i++) {
reviewArr.push(getReviewFromStorage(reviewIDs[i]));
}
addReviewsToDocument(reviewArr);
} else {
//add 9 more reviews to container
for(let i = index; i < index + 9; i++){
for (let i = index; i < index + 9; i++) {
reviewArr.push(getReviewFromStorage(reviewIDs[i]));
}
addReviewsToDocument(reviewArr);
@@ -142,19 +137,20 @@ function loadReviews(index, reviewIDs){
moreBtn.setAttribute("id", "more-btn");
moreBtn.innerText = "Load More";
//if load more clicked, load 9 more
moreBtn.addEventListener("click", function(){loadReviews(index + 9, reviewIDs);});
moreBtn.addEventListener("click", function () {
loadReviews(index + 9, reviewIDs);
});
reviewBox.append(moreBtn);
}
}
const registerServiceWorker = async () => {
if ("serviceWorker" in navigator) {
try {
await navigator.serviceWorker.register("./sw.js", {scope: "./"});
await navigator.serviceWorker.register("./sw.js", { scope: "./" });
} catch (error) {
console.error(`Registration failed with ${error}`);
}
}
};
registerServiceWorker();
registerServiceWorker();

View File

@@ -1,64 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Food Journal</title>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Food Journal</title>
<!--Add Favicon-->
<link rel="icon" type="image/x-icon" href="./assets/images/favicon.ico">
<!--Add Favicon-->
<link rel="icon" type="image/x-icon" href="./assets/images/favicon.ico" />
<!-- Recipe Card Custom Element -->
<script src="assets/scripts/ReviewCard.js" type="module"></script>
<!-- Recipe Card Custom Element -->
<script src="assets/scripts/ReviewCard.js" type="module"></script>
<!-- Main Stylesheets & Scripts -->
<!-- Temporarily commented out reset.css due to furthur discussion needed on the values of the default config-->
<!-- <link rel="stylesheet" href="/static/reset.css" /> -->
<link rel="stylesheet" href="./static/homepage.css" />
<script src="assets/scripts/main.js" type="module"></script>
</head>
<!-- Main Stylesheets & Scripts -->
<!-- Temporarily commented out reset.css due to furthur discussion needed on the values of the default config-->
<!-- <link rel="stylesheet" href="/static/reset.css" /> -->
<link rel="stylesheet" href="./static/homepage.css" />
<script src="assets/scripts/main.js" type="module"></script>
</head>
<body>
<header>
<div class="top-bar">
<img src="./assets/images/Logo.png" alt="logo" />
<h1> Food Journal </h1>
<img src="./assets/images/Logo.png" alt="logo" />
</div>
</header>
<main>
<div class="body-container">
<div style="width: 20%;"></div>
<div style="width: 60%;">
<div class="search-bar">
<form id="form">
<label for="sort">Sorting Method:</label>
<select id="sort">
<option value="recent">Most Recent</option>
<option value="top">Top Rated</option>
</select>
<input type="search" id="search-bar" name="searchBar" placeholder="Search tags...">
<button id="clear-search">Clear Search</button>
</form>
<img src="./assets/images/search_button.png" alt="SEARCH BTN" id="search-btn" />
</div>
<div class="center-display">
<img src="./assets/images/create_button.png" alt="CREATE" id="create-btn" title="Add an entry!"
onclick="window.location.assign('./CreatePage.html')" />
<h2 id="recent-reviews-text"> Recent Reviews </h2>
<img src="./assets/images/create_button.png" id="create-btn-invis" draggable="false" />
</div>
<div class="review-container" id="review-container"></div>
<body>
<header>
<div class="top-bar">
<img src="./assets/images/Logo.png" alt="logo" />
<h1>Food Journal</h1>
<img src="./assets/images/Logo.png" alt="logo" />
</div>
<div style="width: 20%;">
</div>
</div>
</main>
</body>
</header>
<main>
<div class="body-container">
<div style="width: 20%"></div>
<div style="width: 60%">
<div class="search-bar">
<form id="form">
<label for="sort">Sorting Method:</label>
<select id="sort">
<option value="recent">Most Recent</option>
<option value="top">Top Rated</option>
</select>
<input type="search" id="search-bar" name="searchBar" placeholder="Search tags..." />
<button id="clear-search">Clear Search</button>
</form>
<img src="./assets/images/search_button.png" alt="SEARCH BTN" id="search-btn" />
</div>
</html>
<div class="center-display">
<img
src="./assets/images/create_button.png"
alt="CREATE"
id="create-btn"
title="Add an entry!"
onclick="window.location.assign('./CreatePage.html')"
/>
<h2 id="recent-reviews-text">Recent Reviews</h2>
<img src="./assets/images/create_button.png" id="create-btn-invis" draggable="false" />
</div>
<div class="review-container" id="review-container"></div>
</div>
<div style="width: 20%"></div>
</div>
</main>
</body>
</html>

View File

@@ -48,7 +48,8 @@ input[type="text"]:focus {
}
.hidden,
.rating:not(:checked) > input { /* Hide radio circles while star rating */
.rating:not(:checked) > input {
/* Hide radio circles while star rating */
display: none;
}

View File

@@ -26,7 +26,7 @@ const ASSETS = [
"assets/scripts/localStorage.js",
"assets/scripts/main.js",
"assets/scripts/ReviewCard.js",
"assets/scripts/ReviewDetails.js"
"assets/scripts/ReviewDetails.js",
];
/**
@@ -46,18 +46,22 @@ self.addEventListener("install", async () => {
*/
self.addEventListener("fetch", (event) => {
// add a response to the fetch event
event.respondWith(caches.open(CACHE_NAME).then((cache) => {
// try to return a network fetch response
return fetch(event.request).then((fetchedResponse) => {
// if there is a response, add it to the cache
cache.put(event.request, fetchedResponse.clone());
// return the network response
return fetchedResponse;
}).catch(() => {
// If there is not a network response, return the cached response
// The ignoreVary option is used here to fix an issue where the service worker
// would not serve certain requests unless the page was refreshed at least once
return cache.match(event.request, {ignoreVary: true, ignoreSearch: true});
});
}));
event.respondWith(
caches.open(CACHE_NAME).then((cache) => {
// try to return a network fetch response
return fetch(event.request)
.then((fetchedResponse) => {
// if there is a response, add it to the cache
cache.put(event.request, fetchedResponse.clone());
// return the network response
return fetchedResponse;
})
.catch(() => {
// If there is not a network response, return the cached response
// The ignoreVary option is used here to fix an issue where the service worker
// would not serve certain requests unless the page was refreshed at least once
return cache.match(event.request, { ignoreVary: true, ignoreSearch: true });
});
})
);
});