Area:
Category:
├── .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 |
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;fCreated 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 | }; --------------------------------------------------------------------------------