diff --git a/.github/workflows/js-unittest.yml b/.github/workflows/js-unittest.yml
index 07438f8..4cb1359 100644
--- a/.github/workflows/js-unittest.yml
+++ b/.github/workflows/js-unittest.yml
@@ -1,27 +1,27 @@
-name: JS Unit Test
-
-on:
- pull_request:
- branches:
- - main
-
- # Allows you to run this workflow manually from the Actions tab
- workflow_dispatch:
-
-jobs:
- # Single deploy job since we're just deploying
- test:
- runs-on: ubuntu-latest
- steps:
- - name: Install apt updates
- run: sudo apt -y update; sudo apt -y upgrade;
- - name: Install prerequisites
- uses: actions/setup-node@v3
- with:
- node-version: 18
- - name: Checkout
- uses: actions/checkout@v3
- - name: Install dependencies
- run: sudo npm install
- - name: Run tests
+name: JS Unit Test
+
+on:
+ pull_request:
+ branches:
+ - main
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ # Single deploy job since we're just deploying
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install apt updates
+ run: sudo apt -y update; sudo apt -y upgrade;
+ - name: Install prerequisites
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install dependencies
+ run: sudo npm install
+ - name: Run tests
run: sudo npm test
\ No newline at end of file
diff --git a/CreatePage.html b/CreatePage.html
new file mode 100644
index 0000000..d1b5223
--- /dev/null
+++ b/CreatePage.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+ Food Journal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ReviewDetails.html b/ReviewDetails.html
new file mode 100644
index 0000000..a2aea68
--- /dev/null
+++ b/ReviewDetails.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+ Food Journal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..159a2b8
--- /dev/null
+++ b/index.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+ Food Journal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
index 85da852..bd8a69c 100644
--- a/package.json
+++ b/package.json
@@ -1,20 +1,20 @@
-{
- "name": "food-journal",
- "version": "1.0.0",
- "type": "module",
- "scripts": {
- "test": "mocha --recursive --require mock-local-storage './{,!(node_modules)/**}/*.test.js'",
- "lint": "eslint '**/*.js'",
- "fix-style": "eslint --fix **/*.js",
- "lintHTML": "htmlhint '**/*.html'",
- "lintCSS": "stylelint '**/*.css'"
- },
- "devDependencies": {
- "eslint": "^8.27.0",
- "htmlhint": "1.1.4",
- "mocha": "10",
- "mock-local-storage": "^1.1.23",
- "stylelint": "14.14.1",
- "stylelint-config-standard": "^29.0.0"
- }
-}
+{
+ "name": "food-journal",
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "test": "mocha --recursive --require mock-local-storage './{,!(node_modules)/**}/*.test.js'",
+ "lint": "eslint '**/*.js'",
+ "fix-style": "eslint --fix **/*.js",
+ "lintHTML": "htmlhint '**/*.html'",
+ "lintCSS": "stylelint '**/*.css'"
+ },
+ "devDependencies": {
+ "eslint": "^8.27.0",
+ "htmlhint": "1.1.4",
+ "mocha": "10",
+ "mock-local-storage": "^1.1.23",
+ "stylelint": "14.14.1",
+ "stylelint-config-standard": "^29.0.0"
+ }
+}
diff --git a/review.html b/review.html
new file mode 100644
index 0000000..52371f9
--- /dev/null
+++ b/review.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ Food Journal
+
+
+
+
+ Current Review:
+
+
+
+
\ No newline at end of file
diff --git a/source/assets/images/1_spooky-ghost-cookies.jpeg b/source/assets/images/1_spooky-ghost-cookies.jpeg
new file mode 100644
index 0000000..aa5a1ed
Binary files /dev/null and b/source/assets/images/1_spooky-ghost-cookies.jpeg differ
diff --git a/source/assets/images/2_frightfully-easy-ghost-cookies.jpeg b/source/assets/images/2_frightfully-easy-ghost-cookies.jpeg
new file mode 100644
index 0000000..38b34e8
Binary files /dev/null and b/source/assets/images/2_frightfully-easy-ghost-cookies.jpeg differ
diff --git a/source/assets/images/3_ingredient-ghost-halloween-cookies.jpeg b/source/assets/images/3_ingredient-ghost-halloween-cookies.jpeg
new file mode 100644
index 0000000..2bca5e0
Binary files /dev/null and b/source/assets/images/3_ingredient-ghost-halloween-cookies.jpeg differ
diff --git a/source/assets/images/icons/0-star.svg b/source/assets/images/icons/0-star.svg
new file mode 100644
index 0000000..6a62bca
--- /dev/null
+++ b/source/assets/images/icons/0-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/source/assets/images/icons/1-star.svg b/source/assets/images/icons/1-star.svg
new file mode 100644
index 0000000..d915bfc
--- /dev/null
+++ b/source/assets/images/icons/1-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/source/assets/images/icons/2-star.svg b/source/assets/images/icons/2-star.svg
new file mode 100644
index 0000000..349146a
--- /dev/null
+++ b/source/assets/images/icons/2-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/source/assets/images/icons/3-star.svg b/source/assets/images/icons/3-star.svg
new file mode 100644
index 0000000..1e414b3
--- /dev/null
+++ b/source/assets/images/icons/3-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/source/assets/images/icons/4-star.svg b/source/assets/images/icons/4-star.svg
new file mode 100644
index 0000000..c8537c9
--- /dev/null
+++ b/source/assets/images/icons/4-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/source/assets/images/icons/5-star.svg b/source/assets/images/icons/5-star.svg
new file mode 100644
index 0000000..d554b72
--- /dev/null
+++ b/source/assets/images/icons/5-star.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/source/assets/images/icons/default_plate.png b/source/assets/images/icons/default_plate.png
new file mode 100644
index 0000000..dde984e
Binary files /dev/null and b/source/assets/images/icons/default_plate.png differ
diff --git a/source/assets/images/icons/plate_with_chopsticks.png b/source/assets/images/icons/plate_with_chopsticks.png
new file mode 100644
index 0000000..023e43e
Binary files /dev/null and b/source/assets/images/icons/plate_with_chopsticks.png differ
diff --git a/source/assets/images/icons/plate_with_cutlery.png b/source/assets/images/icons/plate_with_cutlery.png
new file mode 100644
index 0000000..6f7a938
Binary files /dev/null and b/source/assets/images/icons/plate_with_cutlery.png differ
diff --git a/source/assets/scripts/ReviewCard.js b/source/assets/scripts/ReviewCard.js
new file mode 100644
index 0000000..252ff26
--- /dev/null
+++ b/source/assets/scripts/ReviewCard.js
@@ -0,0 +1,270 @@
+// ReviewCard.js
+
+class ReviewCard extends HTMLElement {
+ // Called once when document.createElement('review-card') is called, or
+ // the element is written into the DOM directly as
+ constructor() {
+ super();
+
+
+ let shadowEl = this.attachShadow({mode:'open'});
+
+ let articleEl = document.createElement('article');
+
+ let styleEl = document.createElement('style');
+ styleEl.textContent = `
+ * {
+ font-family: sans-serif;
+ margin: 0;
+ padding: 0;
+ }
+
+ a {
+ text-decoration: none;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+
+ article {
+ align-items: center;
+ border: 1px solid rgb(223, 225, 229);
+ border-radius: 8px;
+ display: grid;
+ grid-template-rows: 118px 56px 14px 18px 15px 36px;
+ height: auto;
+ row-gap: 5px;
+ padding: 0 16px 16px 16px;
+ width: 178px;
+ }
+
+ div.rating {
+ align-items: center;
+ column-gap: 5px;
+ display: flex;
+ }
+
+ div.rating>img {
+ height: auto;
+ display: inline-block;
+ object-fit: scale-down;
+ width: 78px;
+ }
+
+ article>img {
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ height: 118px;
+ object-fit: cover;
+ margin-left: -16px;
+ width: calc(100% + 32px);
+ }
+
+ label.restaurant-name {
+ color: black !important;
+ }
+
+ label.meal-name {
+ display: -webkit-box;
+ font-size: 16px;
+ height: 36px;
+ line-height: 18px;
+ overflow: hidden;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ }
+
+ label:not(.meal-name),
+ span,
+ time {
+ color: #70757A;
+ font-size: 12px;
+ }
+ `;
+ articleEl.append(styleEl);
+ shadowEl.append(articleEl);
+ this.shadowEl = shadowEl;
+ //attach event listener to each recipe-card
+ this.addEventListener('click', (event) => {
+ console.log(event.target);
+ console.log(event.target.data);
+ //Option 1: sending current data to second html page using localStorage (could also just store index)
+ sessionStorage.setItem('current', JSON.stringify(event.target.data));
+ window.location.assign("./ReviewDetails.html");
+ /*
+ //Option 2: sending current data to second html page using string query w/ url (currently not storing value)
+ let reviewFields = window.location.search.slice(1).split("&");
+ for(let i = 0; i < reviewFields.length; i++) {
+ let kv = reviewFields[i].split("=");
+ let key = kv[0];
+ let value = kv[1];
+ console.log(key);
+ console.log(value);
+ // What you want to do with name and value...
+ }*/
+ });
+ }
+
+ /**
+ * 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 , must be of the
+ * following format:
+ * {
+ * "mealImg": "string",
+ * "imgAlt": "string",
+ * "mealName": "string",
+ * "comments": "string",
+ * "rating": number,
+ * "restaurant": "string",
+ * "tags": string array
+ * }
+ */
+ set data(data) {
+ // If nothing was passed in, return
+ if (!data) return;
+
+ // Select the we added to the Shadow DOM in the constructor
+ let articleEl = this.shadowEl.querySelector('article');
+
+ // setting the article elements for the review card
+
+ //image setup
+ let mealImg = document.createElement('img');
+ mealImg.setAttribute('id', 'a-mealImg');
+ mealImg.setAttribute('src',data['mealImg']);
+ mealImg.setAttribute('alt',data['imgAlt']);
+
+ //meal name setup
+ let mealLabel = document.createElement('label');
+ mealLabel.setAttribute('id', 'a-mealName');
+ mealLabel.setAttribute('class','meal-name');
+ mealLabel.innerHTML = data['mealName'];
+
+ //restaurant name setup
+/*
+ //review page link
+ //giving it functionality to save the review card's info to session storage for loading the review page
+ let reviewLink = document.createElement('a');
+ reviewLink.setAttribute('href','./review.html')
+ reviewLink.innerHTML = 'review page'
+ reviewLink.addEventListener('click', () => {
+ sessionStorage.clear();
+ let currReview = {
+ "imgSrc": data['imgSrc'],
+ "imgAlt": data['imgAlt'],
+ "mealName": data['mealName'],
+ "restaurant": data['restaurant'],
+ "comments": data['comments'],
+ "rating": data['rating'],
+ "tags": data['tags']
+ }
+ sessionStorage.setItem('currReview', JSON.stringify(currReview));
+ });
+*/
+ let restaurantLabel = document.createElement('label');
+ restaurantLabel.setAttribute('id', 'a-restaurant');
+ restaurantLabel.setAttribute('class','restaurant-name');
+ restaurantLabel.innerHTML = data['restaurant'];
+
+ //comment section setup (display set to none)
+ let comments = document.createElement('p');
+ comments.setAttribute('id', 'a-comments');
+ comments.style.display = 'none';
+ comments.innerText = data['comments'];
+
+ //other info: rating
+ let ratingDiv = document.createElement('div');
+ ratingDiv.setAttribute('class', 'rating');
+ let starsImg = document.createElement('img');
+ starsImg.setAttribute('id', 'a-rating');
+ starsImg.setAttribute('src', './source/assets/images/icons/'+data['rating']+'-star.svg');
+ starsImg.setAttribute('alt', data['rating'] +' stars');
+ starsImg.setAttribute('num', data['rating']);
+ ratingDiv.append(starsImg);
+
+ //added tags
+ let tagContainer = document.createElement('div');
+ tagContainer.setAttribute('class', 'tag-container');
+ tagContainer.setAttribute('id', 'a-tags');
+ tagContainer.setAttribute('list', data['tags']);
+ if(data['tags']){
+ for (let i = 0; i < data['tags'].length; i++) {
+ let newTag = document.createElement('label');
+ newTag.setAttribute('class','tag');
+ newTag.innerHTML = data['tags'][i] + " ";
+ tagContainer.append(newTag);
+ }
+ }
+
+ articleEl.append(mealImg);
+ articleEl.append(mealLabel);
+ //articleEl.append(reviewLink)
+ articleEl.append(restaurantLabel);
+ articleEl.append(ratingDiv);
+ articleEl.append(tagContainer);
+ 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 , of the
+ * following format:
+ * {
+ * "mealImg": "string",
+ * "imgAlt": "string",
+ * "mealName": "string",
+ * "comments": "string",
+ * "rating": number,
+ * "restaurant": "string",
+ * "tags": string array
+ * }
+ */
+ get data() {
+
+ let dataContainer = {};
+
+ // getting the article elements for the review card
+
+ //get image
+ let mealImg = this.shadowEl.getElementById('a-mealImg');
+ dataContainer['mealImg'] = mealImg.getAttribute('src');
+ dataContainer['imgAlt'] = mealImg.getAttribute('alt');
+
+ //get meal name
+ let mealLabel = this.shadowEl.getElementById('a-mealName');
+ dataContainer['mealName'] = mealLabel.innerHTML;
+
+ //get comment section
+ let comments = this.shadowEl.getElementById('a-comments');
+ console.log(comments);
+ dataContainer['comments'] = comments.innerText;
+
+ //get other info: rating
+ let starsImg = this.shadowEl.getElementById('a-rating');
+ dataContainer['rating'] = starsImg.getAttribute('num');
+
+ //get restaurant name
+ let restaurantLabel = this.shadowEl.getElementById('a-restaurant');
+ dataContainer['restaurant'] = restaurantLabel.innerHTML;
+
+ //get tags
+ let tagContainer = this.shadowEl.getElementById('a-tags');
+ dataContainer['tags'] = tagContainer.getAttribute('list').split(",");
+
+ return dataContainer;
+ }
+}
+customElements.define('review-card', ReviewCard);
diff --git a/source/assets/scripts/ReviewDetails.js b/source/assets/scripts/ReviewDetails.js
new file mode 100644
index 0000000..a072e9f
--- /dev/null
+++ b/source/assets/scripts/ReviewDetails.js
@@ -0,0 +1,127 @@
+//reviewDetails.js
+import {getReviewsFromStorage, saveReviewsToStorage} from './localStorage.js';
+
+// Run the init() function when the page has loaded
+window.addEventListener('DOMContentLoaded', init);
+
+function init(){
+ let main = document.querySelector('main');
+ let reviews = getReviewsFromStorage();
+ setupDelete();
+ setupUpdate();
+}
+
+function setupDelete(){
+ let deleteBtn = document.getElementById('delete');
+ let reviews = getReviewsFromStorage();
+ let current = JSON.parse(sessionStorage.getItem('current'));
+ deleteBtn.addEventListener('click', function(){
+ if(window.confirm("Are you sure you want to delete this entry?")){
+ //delete function
+ if(current){
+ console.log(current);
+ for(let i = 0; i < reviews.length; i++){
+ console.log(reviews[i]);
+ if(reviews[i]['mealName'] == current['mealName'] && reviews[i]['restaurant'] == current['restaurant']){
+ console.log("match found");
+ reviews.splice(i,1);
+ saveReviewsToStorage(reviews);
+ sessionStorage.removeItem('current');
+ window.location.assign("./index.html");
+ break;
+ }
+ };
+ }
+ }
+ });
+}
+
+function setupUpdate(){
+ let updateBtn = document.getElementById('update');
+ let reviews = getReviewsFromStorage();
+ let current = JSON.parse(sessionStorage.getItem('current'));
+ let form = document.getElementById('update-food-entry');
+ updateBtn.addEventListener('click', function(){
+ //update function
+ if(current){
+ console.log(current);
+ form.style.display = 'block';
+ let tagContainer = document.getElementById('tag-container-form');
+ console.log(document.querySelectorAll('#update-food-entry input'));
+
+ //Set value of each input element to current's values
+ document.getElementById('mealImg').defaultValue = current['mealImg'];
+ document.getElementById('imgAlt').defaultValue = current['imgAlt'];
+ document.getElementById('mealName').defaultValue = current['mealName'];
+ document.getElementById('comments').textContent = current['comments'];
+ document.getElementById('rating-' + `${current['rating']}`).checked = true;
+ document.getElementById('restaurant').defaultValue = current['restaurant'];
+
+ if(current['tags']){
+ for (let i = 0; i < current['tags'].length; i++) {
+ let newTag = document.createElement('label');
+ newTag.setAttribute('class','tag');
+ newTag.innerHTML = current['tags'][i] + " ";
+ newTag.addEventListener('click',()=> {
+ tagContainer.removeChild(newTag);
+ });
+ tagContainer.append(newTag);
+ }
+ }
+
+ //Take formdata values as newData when submit
+ form.addEventListener('submit', function(){
+ /*
+ * User submits the form for their review.
+ * We create reviewCard and put in storage
+ */
+ let formData = new FormData(form);
+ let newData = {};
+ for (let [key, value] of formData) {
+ console.log(`${key}`);
+ console.log(`${value}`);
+ if (`${key}` !== "tag-form") {
+ newData[`${key}`] = `${value}`;
+ }
+ }
+ newData['tags'] = [];
+
+ let tags = document.querySelectorAll('.tag');
+ for(let i = 0; i < tags.length; i ++) {
+ newData['tags'].push(tags[i].innerHTML);
+ tagContainer.removeChild(tags[i]);
+ }
+
+ for(let i = 0; i < reviews.length; i++){
+ console.log(reviews[i]);
+ if(reviews[i]['mealName'] == current['mealName'] && reviews[i]['restaurant'] == current['restaurant']){
+ console.log("match found");
+ reviews.splice(i,1,newData);
+ saveReviewsToStorage(reviews);
+ sessionStorage.setItem('current', JSON.stringify(newData));
+ break;
+ }
+ };
+
+ form.style.display = 'none';
+
+ });
+
+ let tagAddBtn = document.getElementById('tagAdd');
+ tagAddBtn.addEventListener('click', ()=> {
+ let tagField = document.getElementById('tag-form');
+ if (tagField.value.length > 0) {
+ let tagLabel = document.createElement('label');
+ tagLabel.innerHTML = tagField.value;
+ tagLabel.setAttribute('class','tag');
+ tagLabel.addEventListener('click',()=> {
+ tagContainer.removeChild(tagLabel);
+ });
+
+ tagContainer.append(tagLabel);
+ tagField.value = '';
+ }
+ });
+ }
+ });
+}
diff --git a/source/assets/scripts/ReviewPage.js b/source/assets/scripts/ReviewPage.js
new file mode 100644
index 0000000..2ff8d70
--- /dev/null
+++ b/source/assets/scripts/ReviewPage.js
@@ -0,0 +1,13 @@
+// Run the init() function when the page has loaded
+window.addEventListener('DOMContentLoaded', init);
+
+function init() {
+ let result = sessionStorage.getItem('currReview')
+
+ let main = document.querySelector('main');
+
+ main.innerHTML = result
+ let p = document.createElement('p')
+ p.innerHTML = JSON.parse(result)['comments']
+ main.append(p)
+}
diff --git a/source/assets/scripts/localStorage.js b/source/assets/scripts/localStorage.js
new file mode 100644
index 0000000..eab3ded
--- /dev/null
+++ b/source/assets/scripts/localStorage.js
@@ -0,0 +1,19 @@
+/**
+ * @returns {Array