Repository: RychkovIurii/boardgamecafe
Pull request #23: Jina user profile
Author: login: Jina-Eunjin
Created: 2025-03-22T14:12:00Z | Merged: 2025-03-23T10:11:52Z

- created user profile page integrated with backend database.
- In the profile, user can edit phone number.
- In ServiceProduct page, I changed a button s-community from 'contact us' to 'join discord' with link

**### *** in the backend, I installed 'nodemon'. Please intall it after pull main.**
nodemon is a tool that automatically restarts your Node.js application when it detects changes in your code. It's useful for development because you don't have to manually restart your server every time you make a change.



Diff:
diff --git a/backend/package-lock.json b/backend/package-lock.json
index eb1cf62..d7244f6 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -21,6 +21,7 @@
         "jest": "^29.7.0",
         "moment-timezone": "^0.5.47",
         "mongodb-memory-server": "^10.1.3",
+        "nodemon": "^3.1.9",
         "supertest": "^7.0.0"
       }
     },
@@ -1304,6 +1305,19 @@
       "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
       "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
     },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/body-parser": {
       "version": "1.20.3",
       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
@@ -1516,6 +1530,31 @@
         "node": ">=10"
       }
     },
+    "node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
     "node_modules/ci-info": {
       "version": "3.9.0",
       "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -2353,6 +2392,19 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/globals": {
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -2496,6 +2548,13 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ignore-by-default": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+      "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/import-local": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
@@ -2554,6 +2613,19 @@
       "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
       "dev": true
     },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/is-core-module": {
       "version": "2.16.1",
       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -2569,6 +2641,16 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -2587,6 +2669,19 @@
         "node": ">=6"
       }
     },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-number": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -3850,6 +3945,83 @@
       "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
       "dev": true
     },
+    "node_modules/nodemon": {
+      "version": "3.1.9",
+      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
+      "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": "^3.5.2",
+        "debug": "^4",
+        "ignore-by-default": "^1.0.1",
+        "minimatch": "^3.1.2",
+        "pstree.remy": "^1.1.8",
+        "semver": "^7.5.3",
+        "simple-update-notifier": "^2.0.0",
+        "supports-color": "^5.5.0",
+        "touch": "^3.1.0",
+        "undefsafe": "^2.0.5"
+      },
+      "bin": {
+        "nodemon": "bin/nodemon.js"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nodemon"
+      }
+    },
+    "node_modules/nodemon/node_modules/debug": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/nodemon/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/nodemon/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nodemon/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -4136,6 +4308,13 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/pstree.remy": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+      "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/punycode": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -4202,6 +4381,19 @@
       "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
       "dev": true
     },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -4451,6 +4643,19 @@
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
+    "node_modules/simple-update-notifier": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+      "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/sisteransi": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -4753,6 +4958,16 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/touch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+      "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "nodetouch": "bin/nodetouch.js"
+      }
+    },
     "node_modules/tr46": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
@@ -4803,6 +5018,13 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/undefsafe": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+      "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/undici-types": {
       "version": "6.20.0",
       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
diff --git a/backend/package.json b/backend/package.json
index a960eb2..58794f9 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -7,7 +7,8 @@
     "test": "jest",
     "start": "node src/server.js",
     "seed": "node config/seed.js",
-	"seedMenu": "node config/seedMenu.js"
+    "server": "nodemon src/server.js",
+    "seedMenu": "node config/seedMenu.js"
   },
   "keywords": [],
   "author": "",
@@ -25,6 +26,7 @@
     "jest": "^29.7.0",
     "moment-timezone": "^0.5.47",
     "mongodb-memory-server": "^10.1.3",
+    "nodemon": "^3.1.9",
     "supertest": "^7.0.0"
   }
 }
diff --git a/frontend/cafe-front/api/axios.js b/frontend/cafe-front/api/axios.js
index b84d7e7..5b651fd 100644
--- a/frontend/cafe-front/api/axios.js
+++ b/frontend/cafe-front/api/axios.js
@@ -2,7 +2,10 @@ import axios from 'axios';
 
 const API = axios.create({
     baseURL: 'http://localhost:5000',
-    withCredentials: true
+    withCredentials: true,
+    headers: {
+        'Content-Type': 'application/json'
+    }
 });
 
 export default API;
diff --git a/frontend/cafe-front/assets/contact.png b/frontend/cafe-front/assets/Contact.png
similarity index 100%
rename from frontend/cafe-front/assets/contact.png
rename to frontend/cafe-front/assets/Contact.png
diff --git a/frontend/cafe-front/assets/image_assets/assets.js b/frontend/cafe-front/assets/image_assets/assets.js
index 0df23c5..d2a0a47 100644
--- a/frontend/cafe-front/assets/image_assets/assets.js
+++ b/frontend/cafe-front/assets/image_assets/assets.js
@@ -207,7 +207,7 @@ export const aboutMenu = [
     { title: "Service", text: "Let our friendly staff assist you for the best board game experience.", link: "/service", img: chest_box_close }]
 
 export const serviceMenu = [
-    { _id: "S-food", title: "RESTAURANT/CAFE/BAR", detail: " A well-equipped kitchen prepares delicious treats for all players, and private rooms offer a touch of luxury for those looking for a special experience.", img: [p_S1_1, p_S1_2, p_S1_3, p_S1_4, p_S1_5, p_S1_6, p_S1_7, p_S1_8] },
-    { _id: "S-private", title: "PRIVATE ROOMS", detail: "Celebrate your birthday, family reunion or any other private event. We offer semi-private and private rooms. Seats up to 12 guests.", img: [p_S2_1, p_S2_2, p_S2_3, p_S2_4] },
-    { _id: "S-coprate_event", title: "CORPORATE EVENTs", detail: "We provide excellent facilities for corporate events, including meetings, team-building days, and recreational events. Ready-made packages include murder mystery lunches, role-playing dinners, private karaoke nights, and '90s discos with a dedicated DJ.", img: [p_S3_1, p_S3_2, p_S3_3, p_S3_4] },
-    { _id: "S-community", title: "COMMUNITY", detail: "Discord channel has over 1,500 active members, and games are often hosted by Finnish game developers.", img: [p_S4_1, p_S4_2, p_S4_3] }]
+    { _id: "s-food", title: "RESTAURANT/CAFE/BAR", detail: " A well-equipped kitchen prepares delicious treats for all players, and private rooms offer a touch of luxury for those looking for a special experience.", img: [p_S1_1, p_S1_2, p_S1_3, p_S1_4, p_S1_5, p_S1_6, p_S1_7, p_S1_8] },
+    { _id: "s-private", title: "PRIVATE ROOMS", detail: "Celebrate your birthday, family reunion or any other private event. We offer semi-private and private rooms. Seats up to 12 guests.", img: [p_S2_1, p_S2_2, p_S2_3, p_S2_4] },
+    { _id: "s-coprate_event", title: "CORPORATE EVENTs", detail: "We provide excellent facilities for corporate events, including meetings, team-building days, and recreational events. Ready-made packages include murder mystery lunches, role-playing dinners, private karaoke nights, and '90s discos with a dedicated DJ.", img: [p_S3_1, p_S3_2, p_S3_3, p_S3_4] },
+    { _id: "s-community", title: "COMMUNITY", detail: "Discord channel has over 1,500 active members, and games are often hosted by Finnish game developers.", img: [p_S4_1, p_S4_2, p_S4_3] }]
diff --git a/frontend/cafe-front/components/ServiceCard.jsx b/frontend/cafe-front/components/ServiceCard.jsx
index 774e3cc..ebd7664 100644
--- a/frontend/cafe-front/components/ServiceCard.jsx
+++ b/frontend/cafe-front/components/ServiceCard.jsx
@@ -1,11 +1,12 @@
 import React from 'react'
 
 import { serviceMenu } from '../assets/image_assets/assets';
-import { useNavigate } from 'react-router-dom';
+import { useNavigate, useLocation } from 'react-router-dom';
 
 function ServiceCard() {
 
     const navigate = useNavigate()
+    const location = useLocation();
 
     return (
         <div className="py-20 max-w-4xl mx-auto dark:bg-gray-800">
@@ -19,16 +20,22 @@ function ServiceCard() {
             {/* ServiceMenu */}
             <div className='w-full grid grid-cols-5 gap-4 pt-5 gap-y-6 px-3 sm:px-0'>
                 {serviceMenu.map((item, index) => (
-                    <div key={index} onClick={() => { navigate(`/ServiceProduct/${item._id}`); scrollTo(0, 0) }} className='border border-yellow-800 rounded-xl 
+                    <div key={index} onClick={() => { navigate(`/service-product/${item._id}`); scrollTo(0, 0) }} className='border border-yellow-800 rounded-xl 
                 overflow-hidden cursor-pointer hover:translate-y-[-10px] transition-all duration-500'>
-                        <img className='w-full h-[250px] object-cover bg-blue-50' src={item.img[0]} alt="service_menu"  />
+                        <img className='w-full h-[250px] object-cover bg-blue-50' src={item.img[0]} alt="service_menu" />
                         <div className='p-3'>
                             <p className='text-gray-900 text-lg font-medium'>{item.title}</p>
                         </div>
+
                     </div>
+
                 ))}
             </div>
-            <button onClick={() => { navigate(`/contact`); scroll(0, 0) }} className='bg-amber-200 text-gray-600 text-2xl px-12 py=3 rounded-full mt-10'>contact us</button>
+
+            { }
+            <button onClick={() => {
+                navigate('/contact'); scroll(0, 0)
+            }} className='bg-amber-200 text-gray-600 text-2xl px-12 py-3 rounded-full mt-10'>contact us</button >
 
 
         </div>
diff --git a/frontend/cafe-front/routes/Profile.jsx b/frontend/cafe-front/routes/Profile.jsx
new file mode 100644
index 0000000..597c9a5
--- /dev/null
+++ b/frontend/cafe-front/routes/Profile.jsx
@@ -0,0 +1,86 @@
+import React, { useState, useEffect } from 'react'
+import API from '../api/axios'
+import Navbar from '../components/Navbar';
+import Footer from '../components/Footer';
+
+
+const Profile = () => {
+
+    const [userData, setUserData] = useState({})
+    const [phone, setPhone] = useState('');
+    const [isEdit, setIsEdit] = useState(false)
+
+
+
+    useEffect(() => {
+        const fetchUserData = async () => {
+            try {
+                const response = await API.get('/users/profile');
+
+                console.log('API Response:', response.data);
+                setUserData(response.data);
+                setPhone(response.data.phone || '')
+            } catch (error) {
+                console.error('Error fetching user data:', error.response ? error.response.data : error);
+            }
+        };
+        fetchUserData()
+    }, [])
+
+    const handleUpdate = async (e) => {
+        e.preventDefault();
+        try {
+            await API.put('/users/phone', { phone });
+            alert('Phone number updated successfully');
+
+            //Fetch the latest data again after the update
+            const response = await API.get('/users/profile');
+            setUserData(response.data);
+            setPhone(response.data.phone);
+            setIsEdit(false);
+        } catch (error) {
+            console.error('Error updating phone number:', error);
+            alert('Failed to update phone number');
+        }
+    };
+
+
+    return (
+        <>
+            <Navbar />
+            <div className='max-w-lg flex flex-col gap-2 text-md mt-5'>
+
+                <p className='font-medium text-2xl text-neutral-800 mt-4'>{userData.name}</p>
+                <hr className='bg-zinc-400 h-[1px] border-none' />
+
+                <div>
+                    <p className='text-neutral-700 underline mt-3'>CONTACT INFORMATION</p>
+                    <div className='grid grid-cols-[1fr_3fr] gap-y-1 mt-3 text-neutral-700'>
+                        <p className='font-medium text-lg'>Email id:</p>
+                        <p className='text-blue-500 text-lg text-left'>{userData.email}</p>
+                        <p className='font-medium text-lg '>Phone:</p>
+                        {
+                            isEdit
+                                ? < input className='bg-gray-100 max-w-52 text-lg' type='text' value={phone} onChange={e => setPhone(e.target.value)} />
+                                : <p className='text-blue-500 text-lg text-left'>{userData.phone}</p>
+                        }
+                    </div>
+                </div>
+
+
+
+                <div className='mt-10'>
+                    {isEdit
+                        ? <button onClick={handleUpdate} className='border border-primary px-8 py-2 rounded-full hover:bg-primary hover:text-white transition-all' > Save information</button>
+                        : <button className='border bg-amber-200 text-gray-800 px-8 py-2 rounded-full hover:bg-primary  hover:text-white transition-all' onClick={() => setIsEdit(true)}> Edit for phone number</button>}
+
+
+                </div>
+
+            </div >
+            <Footer />
+        </>
+    )
+}
+
+export default Profile
\ No newline at end of file
diff --git a/frontend/cafe-front/routes/ServiceProduct.jsx b/frontend/cafe-front/routes/ServiceProduct.jsx
index 8d4f6de..c9ecd67 100644
--- a/frontend/cafe-front/routes/ServiceProduct.jsx
+++ b/frontend/cafe-front/routes/ServiceProduct.jsx
@@ -30,39 +30,47 @@ const ServiceProduct = () => {
 
             <div className='border-t-2 pt-10 transition-opacity ease-in duration-500 opacity-100'>
                 {/* ------- Service Data ---------- */}
-                <div className='flex gap-12 sm:gap-12 flex-col sm:flex-row'>
+                <div className='flex gap-12 sm:gap-5 flex-col sm:flex-row'>
 
                     {/* ------- Service Images --------- */}
-                    <div className='flex-1 flex flex-col-reverse gap-3 sm:flex-row'>
+                    <div className='flex-1 flex flex-col-reverse gap-1 sm:flex-row'>
                         <div className='flex sm:flex-col overflow-x-auto sm:over-y-scroll 
-                justify-between sm:justify-normal sm:w-[18.7%] w-full'>
+                justify-between sm:justify-normal sm:w-[18.7%] w-full pl-3'>
                             {
                                 serviceData.img.map((item, index) => (
                                     <img onClick={() => (setImage(item))}
                                         src={item}
                                         key={index}
                                         alt="service_image"
-                                        className='w-[22%] sm:w-full sm:mb-3 flex-shrink-0 cursor-pointer object-cover aspect-[4/3] max-h-[180px]'
+                                        className='w-[22%] sm:w-full sm:mb-3 flex-shrink-0 cursor-pointer object-cover aspect-[6/3] max-h-[180px]'
                                     />
                                 ))
                             }
                         </div>
-                        <div className='w-full sm:w-[88%]'>
-                            <img className='w-full' src={image} alt="service_image" />
+                        <div className='w-full md:w-[80%] sm:w-[90%]'>
+                            <img className='w-[80%] m-auto' src={image} alt="service_image" />
                         </div>
                     </div>
 
                     {/* --------- Service Info --------  */}
-                    <div className='flex-1 justify-center text-center'>
-    <h1 className='font-medium text-2xl mt-2'>{serviceData.title}</h1>
-    <div className='mt-7 text-gray-700 md:w-4/5 mx-auto'>{serviceData.detail}</div>
-    <button 
-        onClick={() => { navigate(`/contact`); scroll(0, 0) }} 
-        className='bg-amber-200 text-gray-600 text-1xl px-5 py-3 rounded-full mt-8'
-    >
-        Contact Us
-    </button>
-</div>
+                    <div className='flex-1 justify-start text-center'>
+                        <h1 className='font-medium text-2xl mt-2'>{serviceData.title}</h1>
+                        <div className='mt-7 text-gray-700 md:w-4/5 mx-auto'>{serviceData.detail}</div>
+
+                        {/* Conditional Button */}
+                        {serviceData._id === 's-community' ? (
+                            <button
+                                onClick={() => window.open("https://discord.com/invite/wwQGdKVrma", "_blank")}
+                                className='bg-blue-500 text-white text-2xl px-12 py-3 rounded-full mt-10'
+                            >
+                                Join Discord
+                            </button>
+                        ) : (
+                            <button onClick={() => { navigate(`/contact`); scrollTo(0, 0) }} className='bg-amber-200 text-gray-700 text-2xl px-12 py-3 rounded-full mt-10'>
+                                Contact Us
+                            </button>
+                        )}
+                    </div>
 
                 </div>
             </div>
diff --git a/frontend/cafe-front/src/App.jsx b/frontend/cafe-front/src/App.jsx
index 4541ef9..1c10a47 100644
--- a/frontend/cafe-front/src/App.jsx
+++ b/frontend/cafe-front/src/App.jsx
@@ -19,9 +19,10 @@ import EditTables from '../routes/admin/EditTables';
 import EditPricing from '../routes/admin/EditPricing';
 import Service from '../routes/Service';
 import ServiceProduct from '../routes/ServiceProduct';
+import Profile from '../routes/Profile';
 
 function App() {
-	
+
   const { isAuthenticated, isCheckingAuth } = useContext(AuthContext);
   const AdminRoute = ({ element }) => {
     if (isCheckingAuth) return <div>Loading...</div>;
@@ -45,7 +46,8 @@ function App() {
         <Route path="/admin/edit-booking/:id" element={<AdminRoute element={<EditBooking />} />} />
         <Route path='/contact' element={<Contact />} />
         <Route path='/service' element={<Service />} />
-        <Route path='/ServiceProduct/:serviceId' element={<ServiceProduct />} />
+        <Route path='/service-product/:serviceId' element={<ServiceProduct />} />
+        <Route path='/profile' element={<Profile />} />
       </Routes>
     </div>
   )


Reviews:
user: login: RychkovIurii
body: It is generally better to keep nodemon in devDependencies because it is a development tool. Check it please.
I've noticed you removed package.json one row - "seedMenu": "node config/seedMenu.js". We need it.
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-03-22T16:15:06Z
user: login: Jina-Eunjin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-22T20:22:09Z
user: login: RychkovIurii
body: Thanks! Approved!
state: APPROVED
author_association: OWNER
submitted_at: 2025-03-22T21:00:52Z
user: login: Nasrin-MT
body: God job!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-23T10:03:30Z

Review comments:
diff_hunk: @@ -19,7 +19,8 @@
     "dotenv": "^16.4.7",
     "express": "^4.21.2",
     "jsonwebtoken": "^9.0.2",
-    "mongoose": "^8.9.4"
+    "mongoose": "^8.9.4",
+    "nodemon": "^3.1.9"
path: backend/package.json
user: login: RychkovIurii
body: - It is generally better to keep nodemon in devDependencies because it is a development tool. Check it please.
- I've noticed you removed "seedMenu": "node config/seedMenu.js". We need it.
created_at: 2025-03-22T16:13:03Z
author_association: OWNER
diff_hunk: @@ -19,7 +19,8 @@
     "dotenv": "^16.4.7",
     "express": "^4.21.2",
     "jsonwebtoken": "^9.0.2",
-    "mongoose": "^8.9.4"
+    "mongoose": "^8.9.4",
+    "nodemon": "^3.1.9"
path: backend/package.json
user: login: Jina-Eunjin
body: Thank you for comment! 
I fixed: 
- move nodemon to devDependencies
- restore seedMenu script
created_at: 2025-03-22T20:22:09Z
author_association: COLLABORATOR

PR comments:
user: login: Jina-Eunjin
created_at: 2025-03-22T14:26:14Z
body: Also, when you check the package.json file in the backend folder, you'll see that the following script has been added:
"server": "nodemon src/server.js".

You can now start the server using:
npm run server
author_association: COLLABORATOR
user: login: Jina-Eunjin
created_at: 2025-03-22T20:26:05Z
body: After fixing the issues by moving Nodemon to devDependencies and restoring the seedMenu script, I committed and pushed my jina-user-profile branch. 
author_association: COLLABORATOR