├── .DS_Store ├── .babelrc ├── .eslintrc.json ├── .github ├── .DS_Store └── workflows │ ├── .DS_Store │ └── linters.yml ├── .gitignore ├── .hintrc ├── .stylelintrc.json ├── LICENSE ├── README.md ├── dist ├── 529e7db923c1543a99ce91bad2a6a344.png ├── a7924d15daeb41df7ecb211104f05785.jpg ├── db5f2c5f77bae45dfa1986fa8b9fc6c7.svg ├── index.html ├── index.js └── runtime.js ├── jest.config.js ├── murple_logo.svg ├── package-lock.json ├── package.json ├── src ├── .DS_Store ├── Testing │ ├── commentsCounter.test.js │ └── itemsCounter.test.js ├── assets │ ├── .DS_Store │ ├── 2.jpg │ ├── like.svg │ └── logo.png ├── index.html ├── index.js ├── modules │ ├── api.js │ ├── home │ │ ├── displayItems.js │ │ ├── footer.js │ │ ├── itemsCounter.js │ │ ├── postLike.js │ │ └── updateLike.js │ ├── init.js │ ├── localStorage.js │ └── popup │ │ ├── commentsCounter.js │ │ ├── displayComments.js │ │ ├── getComments.js │ │ ├── popup.js │ │ ├── postComment.js │ │ └── setMealDetails.js └── style.scss └── webpack.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "plugins": ["@babel/plugin-transform-modules-commonjs"] 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest": true 6 | }, 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module" 11 | }, 12 | "extends": ["airbnb-base"], 13 | "rules": { 14 | "no-shadow": "off", 15 | "no-param-reassign": "off", 16 | "eol-last": "off", 17 | "import/extensions": [ 1, { 18 | "js": "always", "json": "always" 19 | }] 20 | }, 21 | "ignorePatterns": [ 22 | "dist/", 23 | "build/" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.github/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/.github/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/.github/workflows/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: pull_request 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | lighthouse: 10 | name: Lighthouse 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: "18.x" 17 | - name: Setup Lighthouse 18 | run: npm install -g @lhci/cli@0.11.x 19 | - name: Lighthouse Report 20 | run: lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=. 21 | webhint: 22 | name: Webhint 23 | runs-on: ubuntu-22.04 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: "18.x" 29 | - name: Setup Webhint 30 | run: | 31 | npm install --save-dev hint@7.x 32 | [ -f .hintrc ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.hintrc 33 | - name: Webhint Report 34 | run: npx hint . 35 | stylelint: 36 | name: Stylelint 37 | runs-on: ubuntu-22.04 38 | steps: 39 | - uses: actions/checkout@v3 40 | - uses: actions/setup-node@v3 41 | with: 42 | node-version: "18.x" 43 | - name: Setup Stylelint 44 | run: | 45 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x 46 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.stylelintrc.json 47 | - name: Stylelint Report 48 | run: npx stylelint "**/*.scss" 49 | eslint: 50 | name: ESLint 51 | runs-on: ubuntu-22.04 52 | steps: 53 | - uses: actions/checkout@v3 54 | - uses: actions/setup-node@v3 55 | with: 56 | node-version: "18.x" 57 | - name: Setup ESLint 58 | run: | 59 | npm install --save-dev eslint@7.x eslint-config-airbnb-base@14.x eslint-plugin-import@2.x babel-eslint@10.x 60 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.eslintrc.json 61 | - name: ESLint Report 62 | run: npx eslint . 63 | nodechecker: 64 | name: node_modules checker 65 | runs-on: ubuntu-22.04 66 | steps: 67 | - uses: actions/checkout@v3 68 | - name: Check node_modules existence 69 | run: | 70 | if [ -d "node_modules/" ]; then echo -e "\e[1;31mThe node_modules/ folder was pushed to the repo. Please remove it from the GitHub repository and try again."; echo -e "\e[1;32mYou can set up a .gitignore file with this folder included on it to prevent this from happening in the future." && exit 1; fi 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .history/ 3 | .vscode/ -------------------------------------------------------------------------------- /.hintrc: -------------------------------------------------------------------------------- 1 | { 2 | "connector": { 3 | "name": "local", 4 | "options": { 5 | "pattern": ["**", "!.git/**", "!node_modules/**"] 6 | } 7 | }, 8 | "extends": ["development"], 9 | "formatters": ["stylish"], 10 | "hints": [ 11 | "button-type", 12 | "disown-opener", 13 | "html-checker", 14 | "meta-charset-utf-8", 15 | "meta-viewport", 16 | "no-inline-styles:error" 17 | ] 18 | } -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["stylelint-config-standard"], 3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"], 4 | "rules": { 5 | "at-rule-no-unknown": [ 6 | true, 7 | { 8 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] 9 | } 10 | ], 11 | "scss/at-rule-no-unknown": [ 12 | true, 13 | { 14 | "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen"] 15 | } 16 | ], 17 | "csstree/validator": true 18 | }, 19 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"] 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Salim Bamahfoodh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | logo 5 |
6 | 7 |

Welcome to our project 😃

8 | 9 |
10 | 11 | 12 |
13 | 14 |
15 |

COMMENTDISH website

16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | # 📗 Table of Contents 25 | 26 | - [📖 About the Project](#about-project) 27 | - [🛠 Built With ](#-built-with-) 28 | - [Tech Stack ](#tech-stack-) 29 | - [Key Features ](#key-features-) 30 | - [🤯 Walk through video ](#walk-through) 31 | - [🚀 Live Demo ](#-live-demo-) 32 | - [💻 Getting Started ](#-getting-started-) 33 | - [Prerequisites](#prerequisites) 34 | - [Setup](#setup) 35 | - [Install](#install) 36 | - [Usage](#usage) 37 | - [Run tests](#run-tests) 38 | - [👥 Author ](#-author-) 39 | - [🔭 Future Features ](#-future-features-) 40 | - [🤝 Contributing ](#-contributing-) 41 | - [⭐️ Show your support ](#️-show-your-support-) 42 | - [🙏 Acknowledgments ](#-acknowledgments-) 43 | - [📝 License ](#-license-) 44 | 45 | 46 | 47 | # 📖 [COOMENTDISH website] 48 | 49 | 50 | **COMMENTDISH webiste** is your ultimate platform for expressing your culinary opinions. Explore a wide variety of meals, from delectable classics to innovative creations, and share your thoughts with the world. Like, comment, and engage with fellow food enthusiasts as you uncover new flavors and embark on gastronomic adventures. Discover the joy of voicing your taste buds on Comment Dish, where every dish becomes a conversation. 51 | 52 | ## 🛠 Built With 53 | 54 | ### Tech Stack 55 | 56 |
57 | Client 58 | 63 |
64 | 65 | 66 | 67 | 68 | 69 | ### Key Features 70 | 71 | 72 | - **Easy to the eyes and cultivating at the same time** 73 | - **Dynamic creation of content** 74 | 75 |

(back to top)

76 | 77 | 78 | 79 | ## 🤯 Walk through video 80 | 81 | 82 | - [here](https://youtu.be/dYmqQRuvd4c) 83 | 84 |

(back to top)

85 | 86 | 87 | 88 | 89 | ## 🚀 Live Demo 90 | 91 | 92 | - [Live Demo Link](https://salimer.github.io/COMMENTDISH-project/dist/) 93 | 94 |

(back to top)

95 | 96 | 97 | 98 | 99 | ## 💻 Getting Started 100 | 101 | To get a local copy up and running, follow these steps: 102 | 103 | - Clone this repo as described in the setup section. 104 | - Make modifications as peferred 105 | 106 | 107 | ### Prerequisites 108 | 109 | In order to run this project you need: To clone or fork and run it in a browser 110 | 111 | 112 | ### Setup 113 | 114 | Clone this repository to your desired folder: 115 | 116 | Example commands: 117 | 118 | ```sh 119 | cd my-folder 120 | git clone https://github.com/Salimer/COMMENTDISH-project.git 121 | ``` 122 | 123 | using Ubuntu: 124 | 125 | ```sh 126 | cd my-desired-folder 127 | git clone https://github.com/Salimer/COMMENTDISH-project.git 128 | ``` 129 | 130 | For more information on how to clone or fork a repository: 131 | - How to clone a repo 132 | - How to fork a repo 133 | 134 | ### Install 135 | 136 | - Run ` npm install ` 137 | 138 | ### Usage 139 | 140 | To run the project, execute the following command: 141 | 142 | - Run `npm start` 143 | 144 | ### Run tests 145 | 146 | To run tests, run the following command: 147 | 148 | - to test validation errors `npx hint .` 149 | 150 | - to test CSS linter errors `npx stylelint "**/*.{css,scss}"` 151 | 152 | - to test JavaScript linter errors `npx eslint .` 153 | 154 | - to run the jest tests `npx test` 155 | 156 |

(back to top)

157 | 158 | 159 | 160 | ## 👥 Author 161 | 162 | 👤 **Sumeya Ibrahim** 163 | 164 | 165 | - GitHub: [@isume295](https://github.com/isume295) 166 | 167 | 👤 **Salim Bamahfoodh** 168 | 169 | 170 | - GitHub: [@Salimer](https://github.com/Salimer) 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | ## 🔭 Future Features 179 | 180 | 181 | 182 | Future changes: 183 | - Add animations and transitions 184 | - Add more styles and colors 185 | 186 |

(back to top)

187 | 188 | 189 | 190 | ## 🤝 Contributing 191 | 192 | Contributions, issues, and feature requests are welcome! 193 | 194 | Feel free to check the [issues page](../../issues/). 195 | 196 |

(back to top)

197 | 198 | 199 | 200 | ## ⭐️ Show your support 201 | 202 | 203 | Give a ⭐️ if you like this project! 204 | 205 |

(back to top)

206 | 207 | 208 | 209 | ## 🙏 Acknowledgments 210 | 211 | 212 | - Thanks to all Microverse community 213 | 214 | 215 |

(back to top)

216 | 217 | 234 | 235 | 236 | 237 | ## 📝 License 238 | 239 | This project is [MIT](./LICENSE) licensed. 240 | 241 | 242 |

(back to top)

243 | -------------------------------------------------------------------------------- /dist/529e7db923c1543a99ce91bad2a6a344.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/dist/529e7db923c1543a99ce91bad2a6a344.png -------------------------------------------------------------------------------- /dist/a7924d15daeb41df7ecb211104f05785.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/dist/a7924d15daeb41df7ecb211104f05785.jpg -------------------------------------------------------------------------------- /dist/db5f2c5f77bae45dfa1986fa8b9fc6c7.svg: -------------------------------------------------------------------------------- 1 | red-heart -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | CommentDish

-------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkCOMMENTDISH_project=self.webpackChunkCOMMENTDISH_project||[]).push([[826],{192:(e,t,n)=>{n.d(t,{Z:()=>c});var o=n(81),i=n.n(o),a=n(645),r=n.n(a)()(i());r.push([e.id,"@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700&display=swap);"]),r.push([e.id,"*{margin:0;padding:0;box-sizing:border-box;font-family:'Roboto', sans-serif;color:#45062e}header{position:sticky;top:0;z-index:1;box-shadow:0 2px 4px rgba(0,0,0,0.2)}li{list-style:none}.hide{display:none}.nav-container{width:100%;height:56px;padding:0 100px;display:flex;align-items:center;justify-content:space-between;background-color:antiquewhite}.nav-container .logo{width:24%;height:90%;display:flex;align-items:center;justify-content:center}.nav-container .logo img{width:100%;height:auto}.nav-container .nav{display:flex;gap:20px;list-style:none;font-size:16px;font-weight:500;color:#45062e}.item-container{width:100%;margin:50px 0;height:auto;display:grid;grid-template-columns:auto auto auto;grid-gap:4rem;justify-content:center;align-items:center}.item-container .items{width:300px;height:auto;padding:10px 10px;border:2px solid rgba(231,119,45,0.9);display:flex;flex-direction:column;align-items:center;gap:10px}.item-container .items .item-img-container{background-color:rgba(231,119,45,0.7);width:100%}.item-container .items .item-img-container img{width:100%;height:auto}.item-container .items .detail-container{display:flex;align-items:center}.item-container .items .comment-btn{width:100%;display:flex;justify-content:space-between}.item-container .items .comment-btn .comment{padding:6px 12px;margin:0;background-color:#e7772d;border:none;color:#fff;font-weight:400}.item-container .items .comment-btn .likes{display:flex;padding:0 10px;align-items:center;gap:5px}.item-container .items .comment-btn .likes .like-btn{border:none;background-color:#fff}.item-container .items .comment-btn .likes .like-btn .like-icon{width:25px}.popup-section{background-color:rgba(128,128,128,0.5);backdrop-filter:blur(5px);padding:1rem;width:100%;height:100%;position:fixed;top:0;z-index:9999;left:0;overflow-y:auto}.popup-section .popup{background-color:rgba(255,255,255,0.9);backdrop-filter:blur(10px);box-shadow:0 0 10px rgba(0,0,0,0.3);display:flex;flex-direction:column;padding:1.5rem;width:80%;max-width:37.5rem;position:relative;border-radius:0.6rem;overflow-y:hidden;margin:1rem auto;border:1px solid black}.popup-section .popup .pp-header{width:100%;display:flex;flex-direction:column;align-items:center}.popup-section .popup .pp-header #close-icon{position:absolute;right:1rem;top:1rem;width:2rem;height:2rem}.popup-section .popup .pp-header .image{width:80%;background-color:red;border-radius:.6rem}.popup-section .popup .pp-header .pp-meal-name{font-size:2rem;padding:1rem}.popup-section .popup .pp-header .pp-meal-details{width:100%;display:flex;flex-direction:column}.popup-section .popup .pp-header .pp-meal-details .pp-meal-detail{padding:1rem 0;font-size:1.3rem}.popup-section .popup .pp-body{display:flex;flex-direction:column;padding:1rem 0}.popup-section .popup .pp-body .pp-comments-title{align-self:center;padding:1rem;font-size:1.5rem}.popup-section .popup .pp-footer{display:flex;flex-direction:column}.popup-section .popup .pp-footer .form{display:flex;flex-direction:column;gap:1rem}.popup-section .popup .pp-footer .form .pp-form-title{font-size:1.5rem;align-self:center}.popup-section .popup .pp-footer .form .your-name{padding:1rem;border-radius:.6rem}.popup-section .popup .pp-footer .form .msg{height:8rem;border-radius:.6rem;padding:1rem}.popup-section .popup .pp-footer .form #pp-comment-btn{padding:0.4rem;border-radius:.6rem}.item-number{width:100%;display:flex;align-items:center;justify-content:center;margin:20px 0}.footer{width:100%;background-color:antiquewhite;height:200px;display:flex;justify-content:center;align-items:center;gap:1rem;font-size:16px;font-weight:500;padding:0 1rem}.footer .img-container{width:100px}.footer .img-container #footer-img{width:100%;height:auto}@media (max-width: 768px){.item-container{grid-template-columns:auto}}@media (min-width: 768px) and (max-width: 991px){.item-container{grid-template-columns:auto auto}}\n",""]);const c=r},645:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n="",o=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),o&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),o&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n})).join("")},t.i=function(e,n,o,i,a){"string"==typeof e&&(e=[[null,e,void 0]]);var r={};if(o)for(var c=0;c0?" ".concat(l[5]):""," {").concat(l[1],"}")),l[5]=a),n&&(l[2]?(l[1]="@media ".concat(l[2]," {").concat(l[1],"}"),l[2]=n):l[2]=n),i&&(l[4]?(l[1]="@supports (".concat(l[4],") {").concat(l[1],"}"),l[4]=i):l[4]="".concat(i)),t.push(l))}},t}},81:e=>{e.exports=function(e){return e[1]}},404:(e,t,n)=>{var o=n(379),i=n.n(o),a=n(795),r=n.n(a),c=n(569),s=n.n(c),p=n(565),l=n.n(p),d=n(216),m=n.n(d),u=n(589),f=n.n(u),h=n(192),g={};g.styleTagTransform=f(),g.setAttributes=l(),g.insert=s().bind(null,"head"),g.domAPI=r(),g.insertStyleElement=m(),i()(h.Z,g),h.Z&&h.Z.locals&&h.Z.locals},379:e=>{var t=[];function n(e){for(var n=-1,o=0;o{var t={};e.exports=function(e,n){var o=function(e){if(void 0===t[e]){var n=document.querySelector(e);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}t[e]=n}return t[e]}(e);if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(n)}},216:e=>{e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},565:(e,t,n)=>{e.exports=function(e){var t=n.nc;t&&e.setAttribute("nonce",t)}},795:e=>{e.exports=function(e){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var t=e.insertStyleElement(e);return{update:function(n){!function(e,t,n){var o="";n.supports&&(o+="@supports (".concat(n.supports,") {")),n.media&&(o+="@media ".concat(n.media," {"));var i=void 0!==n.layer;i&&(o+="@layer".concat(n.layer.length>0?" ".concat(n.layer):""," {")),o+=n.css,i&&(o+="}"),n.media&&(o+="}"),n.supports&&(o+="}");var a=n.sourceMap;a&&"undefined"!=typeof btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),t.styleTagTransform(o,e,t.options)}(t,e,n)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},589:e=>{e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}},138:(e,t,n)=>{n.a(e,(async(e,t)=>{try{n(404);var o=n(166);await(0,o.Z)(),t()}catch(e){t(e)}}),1)},166:(e,t,n)=>{n.d(t,{Z:()=>l});const o=n.p+"529e7db923c1543a99ce91bad2a6a344.png",i=n.p+"a7924d15daeb41df7ecb211104f05785.jpg",a=n.p+"db5f2c5f77bae45dfa1986fa8b9fc6c7.svg",r=async e=>{const t=await e.fetchData("https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/likes","GET"),n=document.querySelectorAll(".nos-of-like");"string"!=typeof t?e.mealsList.forEach(((e,o)=>{const i=t.find((e=>e.item_id===`${o}`));i&&(n[o].innerHTML=i.likes)})):n.forEach((e=>{e.innerHTML="0"}))},c=e=>{const t=document.querySelector(".pp-comments-container");for(;t.firstChild;)t.removeChild(t.firstChild);e.forEach((e=>{const n=document.createElement("li");n.className="pp-comment",n.innerHTML=`\n ${e.creation_date}\n ${e.username} : ${e.comment}\n `,t.appendChild(n)}))},s=e=>e.length,p=new class{involveURL="https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/";constructor(){this.involveUrl=this.involveURL,this.mealsList=[]}fetchData=async(e,t,n,o={})=>{const i={method:t,headers:o};let a;if("GET"===t?a=new Request(e):"POST"===t&&(i.body=JSON.stringify(n),a=new Request(e,i)),!a)throw new Error("invalid");const r=await fetch(a),c=r.headers.get("content-type");return c&&(c.includes("text/html")||c.includes("text/plain"))?await r.text():await r.json()};createApp=async()=>{const e=this.involveUrl+"apps/";return await this.fetchData(e,"POST")};retrieveMealID=e=>this.mealsList[e].idMeal},l=async()=>{document.querySelector(".logo-img").setAttribute("src",o),await(async e=>{const t=document.querySelector(".item-container"),n=await e.fetchData("https://www.themealdb.com/api/json/v1/1/filter.php?c=Seafood","GET");e.mealsList=n.meals,t.innerHTML=n.meals.reduce(((e,t)=>`${e}\n
\n
\n item-img\n
\n
\n ${t.strMeal}\n
\n
\n \n \n
\n
\n `),"")})(p),(()=>{const e=document.querySelector("footer");e.classList.add("footer"),e.innerHTML='
\nfooter-img\n
\n

Created by Sumeya Ibrahim and Salim Bamahfoodh. All rights reserved

',document.querySelector("#footer-img").setAttribute("src",i)})();const e=document.querySelectorAll(".items");document.querySelector("h3").innerHTML=`We have ${(e=>e.length)(e)} Dishes`,await r(p),document.querySelectorAll(".like-btn").forEach(((e,t)=>{e.addEventListener("click",(async()=>{await(async(e,t)=>{const n={item_id:`${e}`};return await t.fetchData("https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/likes","POST",n,{"Content-Type":"application/json"})})(t,p),await r(p)}))})),document.querySelectorAll(".comment").forEach(((e,t)=>{e.addEventListener("click",(()=>{(async(e,t)=>{const n=`https://www.themealdb.com/api/json/v1/1/lookup.php?i=${t.retrieveMealID(e)}`,o=document.querySelector("#comments-number");(e=>{const t=document.querySelector(".pp-meal-name"),n=document.querySelector("#pp-meal-area"),o=document.querySelector("#pp-meal-category");document.querySelector("#pp-image").src=e.meals[0].strMealThumb,t.textContent=e.meals[0].strMeal,n.textContent=e.meals[0].strArea,o.textContent=e.meals[0].strCategory})(await t.fetchData(n,"GET"));const i=`https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/comments?item_id=${e}`;let a=[];const r=document.querySelector(".popup-section");let p=document.querySelectorAll(".pp-comment");((e,t,n)=>new Promise(((n,o)=>{try{n(e.fetchData(t,"GET"))}catch(e){o()}})))(t,i).then((e=>{c(e),p=document.querySelectorAll(".pp-comment"),o.textContent=`${s(p)}`,r.classList.toggle("hide")})).catch((()=>{p=document.querySelectorAll(".pp-comment"),o.textContent=`${s(p)}`,r.classList.toggle("hide")}));const l=document.querySelector(".your-name"),d=document.querySelector(".msg");document.querySelector("#pp-comment-btn").addEventListener("click",(async()=>{""===l.value||""===d.value?alert("empty username"):(await(async(e,t)=>{const n=document.querySelector(".your-name"),o=document.querySelector(".msg"),i={item_id:t,username:n.value,comment:o.value};await e.fetchData("https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/comments","POST",i,{"Content-Type":"application/json"}),n.value="",o.value=""})(t,e),a=await t.fetchData(i,"GET"),c(a),p=document.querySelectorAll(".pp-comment"),o.textContent=`${s(p)}`)}))})(t,p)}))}))},d=document.querySelector(".popup-section");document.querySelector("#close-icon").addEventListener("click",(()=>{d.classList.toggle("hide")}))}},e=>{e(e.s=138)}]); -------------------------------------------------------------------------------- /dist/runtime.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var r,e,t,o,n,a={},i={};function c(r){var e=i[r];if(void 0!==e)return e.exports;var t=i[r]={id:r,exports:{}};return a[r](t,t.exports,c),t.exports}c.m=a,r="function"==typeof Symbol?Symbol("webpack queues"):"__webpack_queues__",e="function"==typeof Symbol?Symbol("webpack exports"):"__webpack_exports__",t="function"==typeof Symbol?Symbol("webpack error"):"__webpack_error__",o=r=>{r&&!r.d&&(r.d=1,r.forEach((r=>r.r--)),r.forEach((r=>r.r--?r.r++:r())))},c.a=(n,a,i)=>{var c;i&&((c=[]).d=1);var p,u,f,s=new Set,l=n.exports,h=new Promise(((r,e)=>{f=e,u=r}));h[e]=l,h[r]=r=>(c&&r(c),s.forEach(r),h.catch((r=>{}))),n.exports=h,a((n=>{var a;p=(n=>n.map((n=>{if(null!==n&&"object"==typeof n){if(n[r])return n;if(n.then){var a=[];a.d=0,n.then((r=>{i[e]=r,o(a)}),(r=>{i[t]=r,o(a)}));var i={};return i[r]=r=>r(a),i}}var c={};return c[r]=r=>{},c[e]=n,c})))(n);var i=()=>p.map((r=>{if(r[t])throw r[t];return r[e]})),u=new Promise((e=>{(a=()=>e(i)).r=0;var t=r=>r!==c&&!s.has(r)&&(s.add(r),r&&!r.d&&(a.r++,r.push(a)));p.map((e=>e[r](t)))}));return a.r?u:i()}),(r=>(r?f(h[t]=r):u(l),o(c)))),c&&(c.d=0)},n=[],c.O=(r,e,t,o)=>{if(!e){var a=1/0;for(f=0;f=o)&&Object.keys(c.O).every((r=>c.O[r](e[p])))?e.splice(p--,1):(i=!1,o0&&n[f-1][2]>o;f--)n[f]=n[f-1];n[f]=[e,t,o]},c.n=r=>{var e=r&&r.__esModule?()=>r.default:()=>r;return c.d(e,{a:e}),e},c.d=(r,e)=>{for(var t in e)c.o(e,t)&&!c.o(r,t)&&Object.defineProperty(r,t,{enumerable:!0,get:e[t]})},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(r){if("object"==typeof window)return window}}(),c.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),(()=>{var r;c.g.importScripts&&(r=c.g.location+"");var e=c.g.document;if(!r&&e&&(e.currentScript&&(r=e.currentScript.src),!r)){var t=e.getElementsByTagName("script");if(t.length)for(var o=t.length-1;o>-1&&!r;)r=t[o--].src}if(!r)throw new Error("Automatic publicPath is not supported in this browser");r=r.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),c.p=r})(),(()=>{var r={666:0};c.O.j=e=>0===r[e];var e=(e,t)=>{var o,n,[a,i,p]=t,u=0;if(a.some((e=>0!==r[e]))){for(o in i)c.o(i,o)&&(c.m[o]=i[o]);if(p)var f=p(c)}for(e&&e(t);u" 118 | // ], 119 | 120 | // Allows you to use a custom runner instead of Jest's default test runner 121 | // runner: "jest-runner", 122 | 123 | // The paths to modules that run some code to configure or set up the testing environment before each test 124 | // setupFiles: [], 125 | 126 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 127 | // setupFilesAfterEnv: [], 128 | 129 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 130 | // snapshotSerializers: [], 131 | 132 | // The test environment that will be used for testing 133 | testEnvironment: "jsdom", 134 | 135 | // Options that will be passed to the testEnvironment 136 | // testEnvironmentOptions: {}, 137 | 138 | // Adds a location field to test results 139 | // testLocationInResults: false, 140 | 141 | // The glob patterns Jest uses to detect test files 142 | // testMatch: [ 143 | // "**/__tests__/**/*.[jt]s?(x)", 144 | // "**/?(*.)+(spec|test).[tj]s?(x)" 145 | // ], 146 | 147 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 148 | // testPathIgnorePatterns: [ 149 | // "/node_modules/" 150 | // ], 151 | 152 | // The regexp pattern or array of patterns that Jest uses to detect test files 153 | // testRegex: [], 154 | 155 | // This option allows the use of a custom results processor 156 | // testResultsProcessor: null, 157 | 158 | // This option allows use of a custom test runner 159 | // testRunner: "jasmine2", 160 | 161 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 162 | // testURL: "http://localhost", 163 | 164 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 165 | // timers: "real", 166 | 167 | // A map from regular expressions to paths to transformers 168 | // transform: null, 169 | 170 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 171 | // transformIgnorePatterns: [ 172 | // "/node_modules/" 173 | // ], 174 | 175 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 176 | // unmockedModulePathPatterns: undefined, 177 | 178 | // Indicates whether each individual test should be reported during the run 179 | // verbose: null, 180 | 181 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 182 | // watchPathIgnorePatterns: [], 183 | 184 | // Whether to use watchman for file crawling 185 | // watchman: true, 186 | }; -------------------------------------------------------------------------------- /murple_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "COMMENTDISH-project", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest", 7 | "build": "webpack", 8 | "start": "webpack serve --open" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/plugin-transform-modules-commonjs": "^7.21.5", 15 | "babel-eslint": "^10.1.0", 16 | "css-loader": "^6.7.3", 17 | "eslint": "^7.32.0", 18 | "eslint-config-airbnb-base": "^14.2.1", 19 | "eslint-plugin-import": "^2.27.5", 20 | "file-loader": "^6.2.0", 21 | "html-webpack-plugin": "^5.5.1", 22 | "jest": "^29.5.0", 23 | "node-sass": "^8.0.0", 24 | "sass-loader": "^13.2.2", 25 | "style-loader": "^3.3.2", 26 | "stylelint": "^13.13.1", 27 | "stylelint-config-standard": "^21.0.0", 28 | "stylelint-csstree-validator": "^1.9.0", 29 | "stylelint-scss": "^3.21.0", 30 | "webpack": "^5.81.0", 31 | "webpack-cli": "^5.0.2", 32 | "webpack-dev-server": "^4.13.3" 33 | }, 34 | "dependencies": { 35 | "@fortawesome/fontawesome-free": "^6.4.0", 36 | "jest-environment-jsdom": "^29.5.0", 37 | "lodash": "^4.17.21" 38 | }, 39 | "main": "webpack.config.js", 40 | "description": "" 41 | } 42 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/src/.DS_Store -------------------------------------------------------------------------------- /src/Testing/commentsCounter.test.js: -------------------------------------------------------------------------------- 1 | import commentsCounter from '../modules/popup/commentsCounter.js'; 2 | 3 | describe('Comments counter function', () => { 4 | test('Elements count is = 0', () => { 5 | document.body.innerHTML = ` 6 |
    7 | 8 |
9 | `; 10 | const comments = document.querySelectorAll('.comment'); 11 | expect(commentsCounter(comments)).toBe(0); 12 | }); 13 | 14 | test('Elements count is 4', () => { 15 | document.body.innerHTML = ` 16 |
    17 |
  • 18 |
  • 19 |
  • 20 |
  • 21 |
22 | `; 23 | const comments = document.querySelectorAll('.comment'); 24 | expect(commentsCounter(comments)).toBe(4); 25 | }); 26 | }); -------------------------------------------------------------------------------- /src/Testing/itemsCounter.test.js: -------------------------------------------------------------------------------- 1 | import itemsCounter from '../modules/home/itemsCounter.js'; 2 | 3 | describe('Items counter function', () => { 4 | test('Elements count is = 0', () => { 5 | document.body.innerHTML = ` 6 |
7 | 8 |
9 | `; 10 | const items = document.querySelectorAll('.items'); 11 | expect(itemsCounter(items)).toBe(0); 12 | }); 13 | }); 14 | 15 | describe('Items counter function', () => { 16 | test('Elements count is = 8', () => { 17 | document.body.innerHTML = ` 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | `; 29 | const items = document.querySelectorAll('.items'); 30 | expect(itemsCounter(items)).toBe(8); 31 | }); 32 | }); -------------------------------------------------------------------------------- /src/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/src/assets/.DS_Store -------------------------------------------------------------------------------- /src/assets/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/src/assets/2.jpg -------------------------------------------------------------------------------- /src/assets/like.svg: -------------------------------------------------------------------------------- 1 | red-heart -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salimer/COMMENTDISH-project/129003a44cb6d98b90d9f3221e95a62c03ef9f27/src/assets/logo.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CommentDish 8 | 9 | 10 | 11 |
12 | 22 |
23 |
24 |
25 |

26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './style.scss'; 2 | import init from './modules/init.js'; 3 | 4 | await init(); 5 | -------------------------------------------------------------------------------- /src/modules/api.js: -------------------------------------------------------------------------------- 1 | class API { 2 | involveURL = 'https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/'; 3 | 4 | constructor() { 5 | this.involveUrl = this.involveURL; 6 | this.mealsList = []; 7 | } 8 | 9 | fetchData = async (url, method, body, headers = {}) => { 10 | const settingRequest = { 11 | method, 12 | headers, 13 | }; 14 | let request; 15 | if (method === 'GET') { 16 | request = new Request(url); 17 | } else if (method === 'POST') { 18 | settingRequest.body = JSON.stringify(body); 19 | request = new Request(url, settingRequest); 20 | } 21 | if (!request) throw new Error('invalid'); 22 | const response = await fetch(request); 23 | 24 | const contentType = response.headers.get('content-type'); 25 | 26 | if (contentType && (contentType.includes('text/html') || contentType.includes('text/plain'))) { 27 | const htmlResponse = await response.text(); 28 | return htmlResponse; 29 | } 30 | const jsonResponse = await response.json(); 31 | return jsonResponse; 32 | }; 33 | 34 | createApp = async () => { 35 | const endPoint = 'apps/'; 36 | const url = this.involveUrl + endPoint; 37 | const responseJson = await this.fetchData(url, 'POST'); 38 | return responseJson; 39 | }; 40 | 41 | retrieveMealID = (index) => this.mealsList[index].idMeal 42 | } 43 | 44 | export default API; -------------------------------------------------------------------------------- /src/modules/home/displayItems.js: -------------------------------------------------------------------------------- 1 | import like from '../../assets/like.svg'; 2 | 3 | // display list of items on the home page 4 | export default async (api) => { 5 | const container = document.querySelector('.item-container'); 6 | const url = 'https://www.themealdb.com/api/json/v1/1/filter.php?c=Seafood'; 7 | const result = await api.fetchData(url, 'GET'); 8 | api.mealsList = result.meals; 9 | container.innerHTML = result.meals.reduce((output, food) => ( 10 | `${output} 11 |
12 |
13 | item-img 14 |
15 |
16 | ${food.strMeal} 17 |
18 |
19 | 24 | 25 |
26 |
27 | ` 28 | ), ''); 29 | }; -------------------------------------------------------------------------------- /src/modules/home/footer.js: -------------------------------------------------------------------------------- 1 | import footerImg from '../../assets/2.jpg'; 2 | 3 | export default () => { 4 | const footer = document.querySelector('footer'); 5 | footer.classList.add('footer'); 6 | footer.innerHTML = `
7 | footer-img 8 |
9 |

Created by Sumeya Ibrahim and Salim Bamahfoodh. All rights reserved

`; 10 | document.querySelector('#footer-img').setAttribute('src', footerImg); 11 | }; 12 | -------------------------------------------------------------------------------- /src/modules/home/itemsCounter.js: -------------------------------------------------------------------------------- 1 | export default (items) => items.length; -------------------------------------------------------------------------------- /src/modules/home/postLike.js: -------------------------------------------------------------------------------- 1 | export default async (i, api) => { 2 | const body = { 3 | item_id: `${i}`, 4 | }; 5 | const url = 'https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/likes'; 6 | const headers = { 7 | 'Content-Type': 'application/json', 8 | }; 9 | const result = await api.fetchData(url, 'POST', body, headers); 10 | return result; 11 | }; -------------------------------------------------------------------------------- /src/modules/home/updateLike.js: -------------------------------------------------------------------------------- 1 | export default async (api) => { 2 | const url = 'https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/likes'; 3 | const result = await api.fetchData(url, 'GET'); 4 | const likes = document.querySelectorAll('.nos-of-like'); 5 | if (typeof result !== 'string') { 6 | api.mealsList.forEach((meal, index) => { 7 | const match = result.find((obj2) => obj2.item_id === `${index}`); 8 | if (match) { 9 | likes[index].innerHTML = match.likes; 10 | } 11 | }); 12 | } else { 13 | likes.forEach((like) => { 14 | like.innerHTML = '0'; 15 | }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/modules/init.js: -------------------------------------------------------------------------------- 1 | import img from '../assets/logo.png'; 2 | import footer from './home/footer.js'; 3 | 4 | import API from './api.js'; 5 | import displayItems from './home/displayItems.js'; 6 | import postLike from './home/postLike.js'; 7 | import displayLikes from './home/updateLike.js'; 8 | import itemsCounter from './home/itemsCounter.js'; 9 | import popup from './popup/popup.js'; 10 | 11 | const api = new API(); 12 | 13 | export default async () => { 14 | document.querySelector('.logo-img').setAttribute('src', img); 15 | await displayItems(api); 16 | footer(); 17 | 18 | // Items counting process 19 | const items = document.querySelectorAll('.items'); 20 | const h3 = document.querySelector('h3'); 21 | h3.innerHTML = `We have ${itemsCounter(items)} Dishes`; 22 | await displayLikes(api); 23 | 24 | // Like button event listener 25 | const likes = document.querySelectorAll('.like-btn'); 26 | likes.forEach((like, index) => { 27 | like.addEventListener('click', async () => { 28 | await postLike(index, api); 29 | await displayLikes(api); 30 | }); 31 | }); 32 | 33 | // Comments button event listener 34 | const comments = document.querySelectorAll('.comment'); 35 | comments.forEach((comment, index) => { 36 | comment.addEventListener('click', () => { 37 | popup(index, api); 38 | }); 39 | }); 40 | }; 41 | 42 | // Close popup button 43 | const popupSection = document.querySelector('.popup-section'); 44 | const closeIcon = document.querySelector('#close-icon'); 45 | closeIcon.addEventListener('click', () => { 46 | popupSection.classList.toggle('hide'); 47 | }); 48 | -------------------------------------------------------------------------------- /src/modules/localStorage.js: -------------------------------------------------------------------------------- 1 | import API from './api.js'; 2 | 3 | const api = new API(); 4 | 5 | class LocalStorage { 6 | constructor() { 7 | this.localStorage = localStorage; 8 | } 9 | 10 | async getItem(key) { 11 | const data = this.localStorage.getItem(key); 12 | const idGame = data ? JSON.parse(data) : await api.createApp(); 13 | this.setItem('id', idGame); 14 | return idGame; 15 | } 16 | 17 | setItem = (key, value) => { 18 | this.localStorage.setItem(key, JSON.stringify(value)); 19 | }; 20 | } 21 | export default LocalStorage; -------------------------------------------------------------------------------- /src/modules/popup/commentsCounter.js: -------------------------------------------------------------------------------- 1 | export default (comments) => comments.length; -------------------------------------------------------------------------------- /src/modules/popup/displayComments.js: -------------------------------------------------------------------------------- 1 | export default (commentsObj) => { 2 | const commentsContainer = document.querySelector('.pp-comments-container'); 3 | 4 | while (commentsContainer.firstChild) { 5 | commentsContainer.removeChild(commentsContainer.firstChild); 6 | } 7 | 8 | commentsObj.forEach((commentObj) => { 9 | const comment = document.createElement('li'); 10 | comment.className = 'pp-comment'; 11 | comment.innerHTML = ` 12 | ${commentObj.creation_date} 13 | ${commentObj.username} : ${commentObj.comment} 14 | `; 15 | commentsContainer.appendChild(comment); 16 | }); 17 | }; -------------------------------------------------------------------------------- /src/modules/popup/getComments.js: -------------------------------------------------------------------------------- 1 | export default (api, commentsURL, commentsObj) => new Promise((resolve, reject) => { 2 | try { 3 | commentsObj = api.fetchData(commentsURL, 'GET'); 4 | resolve(commentsObj); 5 | } catch (error) { 6 | reject(); 7 | } 8 | }); -------------------------------------------------------------------------------- /src/modules/popup/popup.js: -------------------------------------------------------------------------------- 1 | import displayComments from './displayComments.js'; 2 | import postComment from './postComment.js'; 3 | import commentsCounter from './commentsCounter.js'; 4 | import setMealDetails from './setMealDetails.js'; 5 | import getComments from './getComments.js'; 6 | 7 | export default async (index, api) => { 8 | // Retrieve the mealID from our meals list 9 | const mealID = api.retrieveMealID(index); 10 | const requestURL = `https://www.themealdb.com/api/json/v1/1/lookup.php?i=${mealID}`; 11 | 12 | // Initialise the comments details 13 | const commentsCount = document.querySelector('#comments-number'); 14 | 15 | // Create the GET request for the specified meal 16 | const responseObj = await api.fetchData(requestURL, 'GET'); 17 | setMealDetails(responseObj); 18 | 19 | // Comments GET request 20 | const commentsURL = `https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/comments?item_id=${index}`; 21 | let commentsObj = []; 22 | const popupSection = document.querySelector('.popup-section'); 23 | let comments = document.querySelectorAll('.pp-comment'); 24 | getComments(api, commentsURL, commentsObj).then((commentsObj) => { 25 | displayComments(commentsObj); 26 | comments = document.querySelectorAll('.pp-comment'); 27 | commentsCount.textContent = `${commentsCounter(comments)}`; 28 | popupSection.classList.toggle('hide'); 29 | }).catch(() => { 30 | comments = document.querySelectorAll('.pp-comment'); 31 | commentsCount.textContent = `${commentsCounter(comments)}`; 32 | popupSection.classList.toggle('hide'); 33 | }); 34 | 35 | // posting new comment 36 | const newCommentUsername = document.querySelector('.your-name'); 37 | const newCommentMsg = document.querySelector('.msg'); 38 | const newCommentBtn = document.querySelector('#pp-comment-btn'); 39 | 40 | newCommentBtn.addEventListener('click', async () => { 41 | if ((newCommentUsername.value === '' || newCommentMsg.value === '')) { 42 | alert('empty username'); 43 | } else { 44 | await postComment(api, index); 45 | commentsObj = await api.fetchData(commentsURL, 'GET'); 46 | displayComments(commentsObj); 47 | comments = document.querySelectorAll('.pp-comment'); 48 | commentsCount.textContent = `${commentsCounter(comments)}`; 49 | } 50 | }); 51 | }; 52 | -------------------------------------------------------------------------------- /src/modules/popup/postComment.js: -------------------------------------------------------------------------------- 1 | export default async (api, index) => { 2 | const newCommentUsername = document.querySelector('.your-name'); 3 | const newCommentMsg = document.querySelector('.msg'); 4 | const body = { 5 | item_id: index, 6 | username: newCommentUsername.value, 7 | comment: newCommentMsg.value, 8 | }; 9 | const URL = 'https://us-central1-involvement-api.cloudfunctions.net/capstoneApi/apps/XwWY2NVPZAn0YyuYeG9s/comments'; 10 | const headers = { 11 | 'Content-Type': 'application/json', 12 | }; 13 | await api.fetchData(URL, 'POST', body, headers); 14 | newCommentUsername.value = ''; 15 | newCommentMsg.value = ''; 16 | }; -------------------------------------------------------------------------------- /src/modules/popup/setMealDetails.js: -------------------------------------------------------------------------------- 1 | export default (responseObj) => { 2 | // Initialise and setting the meal details 3 | const ppMealName = document.querySelector('.pp-meal-name'); 4 | const ppMealArea = document.querySelector('#pp-meal-area'); 5 | const ppMealCategory = document.querySelector('#pp-meal-category'); 6 | const ppMealImg = document.querySelector('#pp-image'); 7 | 8 | ppMealImg.src = responseObj.meals[0].strMealThumb; 9 | ppMealName.textContent = responseObj.meals[0].strMeal; 10 | ppMealArea.textContent = responseObj.meals[0].strArea; 11 | ppMealCategory.textContent = responseObj.meals[0].strCategory; 12 | }; -------------------------------------------------------------------------------- /src/style.scss: -------------------------------------------------------------------------------- 1 | 2 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700&display=swap'); 3 | // @import '@fortawesome/fontawesome-free/css/all.css'; 4 | 5 | $primary-color: #e7772d; 6 | $border-radius: 0.6rem; 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | font-family: 'Roboto', sans-serif; 13 | color: #45062e; 14 | } 15 | 16 | header { 17 | position: sticky; 18 | top: 0; 19 | z-index: 1; 20 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); 21 | } 22 | 23 | li { 24 | list-style: none; 25 | } 26 | 27 | .hide { 28 | display: none; 29 | } 30 | 31 | .nav-container { 32 | width: 100%; 33 | height: 56px; 34 | padding: 0 100px; 35 | display: flex; 36 | align-items: center; 37 | justify-content: space-between; 38 | background-color: antiquewhite; 39 | 40 | .logo { 41 | width: 24%; 42 | height: 90%; 43 | display: flex; 44 | align-items: center; 45 | justify-content: center; 46 | 47 | img { 48 | width: 100%; 49 | height: auto; 50 | } 51 | } 52 | 53 | .nav { 54 | display: flex; 55 | gap: 20px; 56 | list-style: none; 57 | font-size: 16px; 58 | font-weight: 500; 59 | color: #45062e; 60 | } 61 | } 62 | 63 | .item-container { 64 | width: 100%; 65 | margin: 50px 0; 66 | height: auto; 67 | display: grid; 68 | grid-template-columns: auto auto auto; 69 | grid-gap: 4rem; 70 | justify-content: center; 71 | align-items: center; 72 | 73 | .items { 74 | width: 300px; 75 | height: auto; 76 | padding: 10px 10px; 77 | border: 2px solid rgba(231, 119, 45, 0.9); 78 | display: flex; 79 | flex-direction: column; 80 | align-items: center; 81 | gap: 10px; 82 | 83 | .item-img-container { 84 | background-color: rgba(231, 119, 45, 0.7); 85 | width: 100%; 86 | 87 | img { 88 | width: 100%; 89 | height: auto; 90 | } 91 | } 92 | 93 | .detail-container { 94 | display: flex; 95 | align-items: center; 96 | } 97 | 98 | .comment-btn { 99 | width: 100%; 100 | display: flex; 101 | justify-content: space-between; 102 | 103 | .comment { 104 | padding: 6px 12px; 105 | margin: 0; 106 | background-color: #e7772d; 107 | border: none; 108 | color: #fff; 109 | font-weight: 400; 110 | cursor: pointer; 111 | } 112 | 113 | .likes { 114 | display: flex; 115 | padding: 0 10px; 116 | align-items: center; 117 | gap: 5px; 118 | 119 | .like-btn { 120 | border: none; 121 | background-color: #fff; 122 | cursor: pointer; 123 | 124 | .like-icon { 125 | width: 25px; 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | .popup-section { 134 | background-color: rgba(128, 128, 128, 0.5); 135 | backdrop-filter: blur(5px); 136 | padding: 1rem; 137 | width: 100%; 138 | height: 100%; 139 | position: fixed; 140 | top: 0; 141 | z-index: 9999; 142 | left: 0; 143 | overflow-y: auto; 144 | 145 | .popup { 146 | background-color: rgba(255, 255, 255, 0.9); 147 | backdrop-filter: blur(10px); 148 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); 149 | display: flex; 150 | flex-direction: column; 151 | padding: 1.5rem; 152 | width: 80%; 153 | max-width: 37.5rem; 154 | position: relative; 155 | border-radius: 0.6rem; 156 | overflow-y: hidden; 157 | margin: 1rem auto; 158 | border: 1px solid black; 159 | 160 | .pp-header { 161 | width: 100%; 162 | display: flex; 163 | flex-direction: column; 164 | align-items: center; 165 | 166 | #close-icon { 167 | position: absolute; 168 | right: 1rem; 169 | top: 1rem; 170 | width: 2rem; 171 | height: 2rem; 172 | cursor: pointer; 173 | } 174 | 175 | .image { 176 | width: 80%; 177 | background-color: red; 178 | border-radius: $border-radius; 179 | } 180 | 181 | .pp-meal-name { 182 | font-size: 2rem; 183 | padding: 1rem; 184 | } 185 | 186 | .pp-meal-details { 187 | width: 100%; 188 | display: flex; 189 | flex-direction: column; 190 | 191 | .pp-meal-detail { 192 | padding: 1rem 0; 193 | font-size: 1.3rem; 194 | } 195 | } 196 | } 197 | 198 | .pp-body { 199 | display: flex; 200 | flex-direction: column; 201 | padding: 1rem 0; 202 | 203 | .pp-comments-title { 204 | align-self: center; 205 | padding: 1rem; 206 | font-size: 1.5rem; 207 | } 208 | } 209 | 210 | .pp-footer { 211 | display: flex; 212 | flex-direction: column; 213 | 214 | .form { 215 | display: flex; 216 | flex-direction: column; 217 | gap: 1rem; 218 | 219 | .pp-form-title { 220 | font-size: 1.5rem; 221 | align-self: center; 222 | } 223 | 224 | .your-name { 225 | padding: 1rem; 226 | border-radius: $border-radius; 227 | } 228 | 229 | .msg { 230 | height: 8rem; 231 | border-radius: $border-radius; 232 | padding: 1rem; 233 | } 234 | 235 | #pp-comment-btn { 236 | padding: 0.4rem; 237 | border-radius: $border-radius; 238 | cursor: pointer; 239 | } 240 | } 241 | } 242 | } 243 | } 244 | 245 | .item-number { 246 | width: 100%; 247 | display: flex; 248 | align-items: center; 249 | justify-content: center; 250 | margin: 20px 0; 251 | } 252 | 253 | .footer { 254 | width: 100%; 255 | background-color: antiquewhite; 256 | height: 200px; 257 | display: flex; 258 | justify-content: center; 259 | align-items: center; 260 | gap: 1rem; 261 | font-size: 16px; 262 | font-weight: 500; 263 | padding: 0 1rem; 264 | 265 | .img-container { 266 | width: 100px; 267 | 268 | #footer-img { 269 | width: 100%; 270 | height: auto; 271 | } 272 | } 273 | } 274 | 275 | @media (max-width: 768px) { 276 | .item-container { 277 | grid-template-columns: auto; 278 | } 279 | } 280 | 281 | @media (min-width: 768px) and (max-width: 991px) { 282 | .item-container { 283 | grid-template-columns: auto auto; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | mode: 'production', 6 | entry: { 7 | index: './src/index.js', 8 | }, 9 | devServer: { 10 | static: './dist', 11 | }, 12 | plugins: [ 13 | new HtmlWebpackPlugin({ 14 | template: './src/index.html', 15 | }), 16 | ], 17 | output: { 18 | path: path.resolve(__dirname, 'dist'), 19 | filename: '[name].js', 20 | clean: true, 21 | }, 22 | optimization: { 23 | runtimeChunk: 'single', 24 | }, 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.scss$/, 29 | use: [ 30 | 'style-loader', 31 | 'css-loader', 32 | 'sass-loader', 33 | ], 34 | }, 35 | { 36 | test: /\.(png|svg|jpg|jpeg|gif)$/i, 37 | use: [ 38 | { 39 | loader: 'file-loader', 40 | }, 41 | ], 42 | }, 43 | ], 44 | }, 45 | watch: false, 46 | watchOptions: { 47 | ignored: /node_modules/, // Specify any directories or files to be ignored during watch 48 | }, 49 | }; --------------------------------------------------------------------------------