-
Notifications
You must be signed in to change notification settings - Fork 398
✨ User Profile Enhancements & API Integration #2628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
✨ User Profile Enhancements & API Integration #2628
Conversation
|
@MohammedFaraan is attempting to deploy a commit to the Vivek Prajapati's projects Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds dotenv loading and remaps user routes, introduces a User controller (list, fetch-by-username, update), extends the User schema with optional fields, refactors user routes to use the controller, and wires frontend Profile and Navbar to the new backend endpoints. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant FE as ProfilePage (FE)
participant API as /api/users (BE)
participant DB as MongoDB (User)
rect rgb(245,255,240)
note right of FE #f5fff0: Load profile
U->>FE: Open Profile Page
FE->>API: GET /api/users/:user_name
API->>DB: findOne({ username }) (exclude password,__v)
DB-->>API: user doc / null
API-->>FE: 200 { user } / 404
FE-->>U: populate form / show not found
end
rect rgb(240,248,255)
note right of FE #f0f8ff: Update profile
U->>FE: Submit updated fields
FE->>API: PUT /api/users/:id {name,email,phone,gender,profile_picture}
API->>DB: findByIdAndUpdate(id, payload, { new: true })
DB-->>API: updated user / error
API-->>FE: 200 { message,user } / 400/404/500
FE-->>U: show toast / Swal
end
sequenceDiagram
autonumber
actor U as User
participant NAV as Navbar (FE)
U->>NAV: Click avatar/username
NAV->>NAV: Toggle dropdown (position: top-14)
U->>NAV: Select menu item (incl. Meet the Makers)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
This pull request closes issue no. #2622 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🧹 Nitpick comments (13)
backend/models/User.js (1)
13-16: Defaulting to null can break controlled inputs; prefer empty strings and trim.Returning null for name will propagate to the frontend and can flip inputs between controlled/uncontrolled. Use empty string defaults and trim to keep UX smooth.
- name: { - type: String, - default: null - }, + name: { + type: String, + trim: true, + default: "" + },src/User/pages/Dashboard/ProfilePage.jsx (5)
25-27: Rename the fallback image variable to avoid confusion with the user field.Using the same identifier as a schema field is confusing. Rename to a clear fallback constant and update the usage.
-const profile_picture = +const fallbackProfilePicture = "https://img.freepik.com/free-psd/3d-illustration-human-avatar-profile_23-2150671142.jpg";- src={user.profile_picture || profile_picture} + src={user.profile_picture || fallbackProfilePicture}Also applies to: 376-381
30-38: Make fetchUserDetails async with robust error handling and consistent response parsing.The try/catch won’t catch promise rejections because axios is used without await. Also, GET vs PUT likely return different shapes (user vs { user, message }). Guard both.
-const fetchUserDetails = () => { - try { - axios.get(API_URL + `/${username}`).then((res) => { - setUser(res.data); - }); - } catch (err) { - console.log(err); - } -}; +const fetchUserDetails = async () => { + if (!username) return; + setIsLoading(true); + try { + const res = await axios.get(`${API_URL}/${encodeURIComponent(username)}`); + // Support either shape: user or { user } + const payload = res.data?.user ?? res.data; + setUser({ + name: payload?.name ?? "", + email: payload?.email ?? "", + phone: payload?.phone ?? "", + gender: payload?.gender ?? "", + profile_picture: payload?.profile_picture ?? "", + _id: payload?._id, + }); + } catch (err) { + toast.error(err.response?.data?.message || "Failed to load profile"); + } finally { + setIsLoading(false); + } +}; useEffect(() => { fetchUserDetails(); -}, []); +// refetch if username changes +}, [username]);Please confirm the controller response for GET /api/users/:user_name. If it returns { user }, the above coalescing is necessary. If it returns the raw user, we can simplify.
Also applies to: 40-43
334-357: Validate profile picture URLs; guide users on invalid input.Add basic http(s) URL validation and avoid accepting non-URLs.
-const handleProfilePicChange = async () => { +const handleProfilePicChange = async () => { const { value: imageUrl } = await Swal.fire({ title: "Enter Image URL", input: "url", inputPlaceholder: "https://example.com/profile.jpg", showCancelButton: true, confirmButtonText: "Save", cancelButtonText: "Cancel", inputValidator: (value) => { - if (!value) return "You must enter a URL!"; + if (!value) return "You must enter a URL!"; + try { + const u = new URL(value); + if (!/^https?:$/.test(u.protocol)) return "Only http(s) URLs are allowed"; + } catch { + return "Please enter a valid URL"; + } }, }); if (imageUrl) { setUser((prev) => ({ ...prev, profile_picture: imageUrl }));
399-405: Coalesce nulls from backend to empty strings in form inputs.Prevents controlled/uncontrolled warnings when backend returns null.
- value={user.name} + value={user.name ?? ""} - value={user.gender} + value={user.gender ?? ""} - value={user.phone} + value={user.phone ?? ""} - value={user.email} + value={user.email ?? ""}Also applies to: 414-427, 435-441, 450-456
460-474: Disable Save while loading and provide subtle UX feedback.Prevents duplicate submissions and clarifies state.
- <button + <button type="submit" - className="bg-yellow-500 text-white px-4 py-2 rounded-md hover:bg-yellow-600" + disabled={isLoading} + className={`bg-yellow-500 text-white px-4 py-2 rounded-md ${ + isLoading ? "opacity-60 cursor-not-allowed" : "hover:bg-yellow-600" + }`} > - Save + {isLoading ? "Saving..." : "Save"} </button>src/User/components/Navbar/UserNavbar.jsx (2)
175-193: Good: dedicated clickable avatar+username wrapper and outside-click handling.This improves UX and event targeting. Ensure the wrapper is a button for accessibility or add role/aria attributes.
- <div - className="flex gap-3 items-center cursor-pointer" - onClick={handleDropdownToggle} - > + <button + type="button" + aria-haspopup="menu" + aria-expanded={showDropdown} + className="flex gap-3 items-center cursor-pointer" + onClick={handleDropdownToggle} + > ... - </div> + </button>
197-199: Anchor dropdown to the right and ensure it stacks above content.Without right-0, menus can overlap content oddly on narrow screens. Also add z-50 to be above overlays.
- className="absolute top-14 w-48 bg-white rounded-md shadow-lg py-1" + className="absolute right-0 top-14 w-48 bg-white rounded-md shadow-lg py-1 z-50"backend/app.js (2)
5-6: Load env before requiring modules that may read it.Call dotenv.config() before importing middlewares/routes that may use environment variables.
-const dotenv = require("dotenv"); -const passport = require("./middlewares/Passport"); +const dotenv = require("dotenv"); +dotenv.config(); +const passport = require("./middlewares/Passport"); ... -const app = express(); - -dotenv.config(); +const app = express();Also applies to: 15-16
19-21: If you rely on cookie sessions from the frontend, enable CORS with credentials and explicit origin.Default cors() won’t allow credentials. Configure CORS to accept your frontend origin(s) and credentials.
app.use( cors({ origin: [process.env.FRONTEND_ORIGIN], // e.g., https://vigybag.com credentials: true, }) );backend/routes/userRoutes.js (2)
4-4: Minor: add missing semicolon for consistency.Keeps code style consistent with the rest of the file.
-const userController = require("../controllers/userController") +const userController = require("../controllers/userController");
6-22: Remove commented legacy endpoint to reduce noise.The old inline /update-details route is fully replaced by the controller; safe to delete.
-// router.post("/update-details", async (req, res) => { -// ... -// });backend/controllers/userController.js (1)
3-11: Pagination and projection considerations for getUsers.Depending on dataset size, add page/limit/sort and return a minimal projection (e.g., username, name, gender, profile_picture) to keep payloads small.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
backend/app.js(3 hunks)backend/controllers/userController.js(1 hunks)backend/models/User.js(2 hunks)backend/routes/userRoutes.js(1 hunks)src/User/components/Navbar/UserNavbar.jsx(7 hunks)src/User/pages/Dashboard/ProfilePage.jsx(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
backend/routes/userRoutes.js (1)
src/User/pages/AdditionalDetails.jsx (1)
e(10-36)
backend/controllers/userController.js (3)
backend/models/User.js (1)
User(59-59)src/User/pages/Dashboard/ProfilePage.jsx (2)
user(16-23)profile_picture(25-26)backend/controllers/authController.js (4)
req(10-59)req(61-100)req(114-229)req(231-257)
backend/app.js (2)
backend/middlewares/Passport.js (1)
dotenv(4-4)backend/server.js (1)
app(2-2)
src/User/components/Navbar/UserNavbar.jsx (1)
src/components/Navbar/UserNavbar.jsx (6)
handleDropdownToggle(49-51)username(14-14)showDropdown(15-15)dropdownRef(18-18)handleLogout(39-47)isOpen(10-193)
src/User/pages/Dashboard/ProfilePage.jsx (3)
backend/controllers/userController.js (3)
user(5-5)user(20-20)user(40-40)src/User/components/Aside/Aside.jsx (1)
username(85-85)src/User/pages/UserAuth/UserAuth.jsx (2)
e(44-95)e(97-144)
🔇 Additional comments (1)
backend/app.js (1)
34-34: Routes mount path update looks good and aligns frontend with backend.Mounting user routes at /api/users matches the new frontend API_URL usage.
| import axios from "axios"; | ||
| import Swal from "sweetalert2"; | ||
|
|
||
| const API_URL = "http://localhost:3000/api/users"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid hard-coded localhost; use env-based API base URL and enable cookies if you rely on sessions.
Hard-coding localhost will break production and mixed-content under HTTPS. Centralize base URL via env and (if using cookie sessions) set withCredentials.
-const API_URL = "http://localhost:3000/api/users";
+const API_URL = `${import.meta.env.VITE_API_BASE_URL}/api/users`;Optionally, create a preconfigured axios instance and reuse it:
// api.js
import axios from "axios";
export const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
withCredentials: true, // if backend uses cookie sessions
});Then replace axios.get/put in this component with api.get/put.
| const fetchUserDetails = () => { | ||
| try { | ||
| axios.get(API_URL + `/${username}`).then((res) => { | ||
| setUser(res.data); | ||
| }); | ||
| } catch (err) { | ||
| console.log(err); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify endpoint shapes and remove legacy usages.
GET uses username and PUT uses id—good. Please verify response shapes for both endpoints and update any legacy consumers still pointing to /api/auth/update-details.
Also applies to: 303-321
🏁 Script executed:
#!/bin/bash
# Verify legacy endpoints and hard-coded hosts
# 1) Find legacy update-details calls
rg -nP '/api/auth/update-details|update-details' -C2
# 2) Find hard-coded hosts to replace with env-based configuration
rg -nP 'http://localhost:3000|https://vigybag-backend\.onrender\.com' -C2 --type=js --type=jsx
# 3) Confirm only whitelisted fields are sent on PUT
rg -nP 'axios\.put\([^,]+,\s*{[^}]+}\)' -C2 src | sed -n '1,200p'Length of output: 934
🏁 Script executed:
#!/bin/bash
# 1) Find any remaining hard-coded backend hosts across JS/JSX files
rg -nP 'http://localhost:3000|https://vigybag-backend\.onrender\.com' -C2
# 2) Locate all axios.put usages to confirm update-by-id endpoints are wired
rg -nP 'axios\.put' -C2 srcLength of output: 3372
Remove legacy /api/auth/update-details calls & centralize API URL configuration
Please address the following refactors before merging:
-
Replace all hard-coded backend URLs with a single env-based
BASE_API_URL(e.g.process.env.REACT_APP_API_URL):- In
src/User/pages/Dashboard/ProfilePage.jsx(line 12): swapforconst API_URL = "http://localhost:3000/api/users";
import { BASE_API_URL } from "config"; const API_URL = `${BASE_API_URL}/api/users`;
- In
src/User/pages/Admin-Verification/Admin.jsx(lines 27, 66, 106): updateaxios.post("https://vigybag-backend.onrender.com/api/auth/send-otp",…)andverify-otpcalls to useBASE_API_URL. - In
src/User/pages/UserAuth/UserAuth.jsx(lines 49–51, 121–123, 174–175): replacelogin,signup, and OAuth redirects with${BASE_API_URL}/…. - In
src/User/pages/ForgotPasswordForm.jsx(line 14): switch theforgot-passwordPOST to useBASE_API_URL. - In
src/User/pages/AdditionalDetails.jsx(line 14): remove the legacyand importaxios.post("https://vigybag-backend.onrender.com/api/auth/update-details", { email, phone });
API_URLfrom config instead.
- In
-
Remove legacy
/api/auth/update-detailsand implement the newPUT /api/users/:idendpoint:- Ensure there’s an
axios.put(\${BASE_API_URL}/api/users/${userId}`, { email, phone })` call (whitelisted fields only). - If it doesn’t exist, add it (e.g. in AdditionalDetails or ProfilePage) so updates go through the ID-based endpoint.
- Ensure there’s an
-
Verify and align to the actual response shapes:
- GET
/api/users/:username— confirm whetherres.dataorres.data.usershould be passed tosetUser. - PUT
/api/users/:id— adjust your success handler based on the returned payload.
- GET
🤖 Prompt for AI Agents
In src/User/pages/Dashboard/ProfilePage.jsx around lines 30–38, replace the
hard-coded API URL usage with a config import and fix the fetch logic: import {
BASE_API_URL } from "config" at the top and set const API_URL =
`${BASE_API_URL}/api/users`; change fetchUserDetails to an async function that
awaits axios.get(`${API_URL}/${username}`) and uses try/catch around the await
(current try/catch around a non-async .then won't catch request errors); when
setting user, check the real response shape and call setUser(res.data.user ??
res.data); also remove any legacy calls to /api/auth/update-details in this file
and ensure updates use axios.put(`${BASE_API_URL}/api/users/${userId}`, { email,
phone }) elsewhere as required.
| const handleSubmit = (e) => { | ||
| e.preventDefault(); | ||
| if (!user._id) return; | ||
|
|
||
| try { | ||
| axios | ||
| .put(API_URL + `/${user._id}`, user) | ||
| .then((res) => { | ||
| toast.success(res.data.message); | ||
| setUser(res.data.user); | ||
| }) | ||
| .catch((err) => { | ||
| toast.error( | ||
| err.response?.data?.message || "Error while updating profile" | ||
| ); | ||
| }); | ||
| } catch (err) { | ||
| toast.error("Failed to update profile"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Whitelist update payload and await response to avoid privilege escalation and race conditions.
Sending the entire user object can unintentionally update protected fields if the backend isn’t strictly sanitizing. Only send allowed fields and await the call for proper error handling.
-const handleSubmit = (e) => {
- e.preventDefault();
- if (!user._id) return;
-
- try {
- axios
- .put(API_URL + `/${user._id}`, user)
- .then((res) => {
- toast.success(res.data.message);
- setUser(res.data.user);
- })
- .catch((err) => {
- toast.error(
- err.response?.data?.message || "Error while updating profile"
- );
- });
- } catch (err) {
- toast.error("Failed to update profile");
- }
-};
+const handleSubmit = async (e) => {
+ e.preventDefault();
+ if (!user?._id) return;
+ setIsLoading(true);
+ try {
+ const payload = {
+ name: user.name ?? "",
+ email: user.email ?? "",
+ phone: user.phone ?? "",
+ gender: user.gender ?? "",
+ profile_picture: user.profile_picture ?? "",
+ };
+ const res = await axios.put(`${API_URL}/${user._id}`, payload);
+ const updated = res.data?.user ?? payload;
+ setUser((prev) => ({ ...prev, ...updated }));
+ toast.success(res.data?.message || "Profile updated");
+ } catch (err) {
+ toast.error(err.response?.data?.message || "Error while updating profile");
+ } finally {
+ setIsLoading(false);
+ }
+};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleSubmit = (e) => { | |
| e.preventDefault(); | |
| if (!user._id) return; | |
| try { | |
| axios | |
| .put(API_URL + `/${user._id}`, user) | |
| .then((res) => { | |
| toast.success(res.data.message); | |
| setUser(res.data.user); | |
| }) | |
| .catch((err) => { | |
| toast.error( | |
| err.response?.data?.message || "Error while updating profile" | |
| ); | |
| }); | |
| } catch (err) { | |
| toast.error("Failed to update profile"); | |
| } | |
| const handleSubmit = async (e) => { | |
| e.preventDefault(); | |
| if (!user?._id) return; | |
| setIsLoading(true); | |
| try { | |
| const payload = { | |
| name: user.name ?? "", | |
| email: user.email ?? "", | |
| phone: user.phone ?? "", | |
| gender: user.gender ?? "", | |
| profile_picture: user.profile_picture ?? "", | |
| }; | |
| const res = await axios.put(`${API_URL}/${user._id}`, payload); | |
| const updated = res.data?.user ?? payload; | |
| setUser((prev) => ({ ...prev, ...updated })); | |
| toast.success(res.data?.message || "Profile updated"); | |
| } catch (err) { | |
| toast.error( | |
| err.response?.data?.message || "Error while updating profile" | |
| ); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; |
🤖 Prompt for AI Agents
In src/User/pages/Dashboard/ProfilePage.jsx around lines 303 to 321, the handler
currently sends the entire user object and uses promise chaining without await;
change it to build a payload that only includes allowed editable fields (e.g.,
name, email, bio, avatarUrl — whatever the UI exposes) instead of sending user,
and make the axios call awaitable (use async/await) so you properly catch and
surface errors; ensure the try/catch wraps the await call and updates state only
from the response body (setUser(res.data.user)) and show toast messages from the
caught error response if present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (4)
backend/controllers/userController.js (2)
35-70: Make updateUser partial, validated, and atomic; add duplicate-key handling.Requiring all fields contradicts “optional fields”, misses validators, risks replacement update semantics (no $set), and has a typo in the 500 message. Implement partial updates with validation,
$set, andrunValidators; handle E11000.exports.updateUser = async (req, res) => { try { const { id } = req.params; if (!mongoose.Types.ObjectId.isValid(id)) { return res.status(400).json({ message: "Invalid user id" }); } - const { name, email, phone, gender, profile_picture } = req.body; - - if (!name || !email || !phone || !gender || !profile_picture) - return res - .status(400) - .json({ message: "Enter the required fields to update your profile" }); - - const user = await User.findById(id); - - if (!user) return res.status(404).json({ message: "User not found" }); - - const updatedUser = await User.findByIdAndUpdate( - { _id: id }, - { name, email, phone, gender, profile_picture }, - { new: true } - ).select("-password -__v") - .lean(); - - if (updatedUser) - return res - .status(200) - .json({ message: "Profile updated", user: updatedUser }); - - return res.status(500).json({ message: "Failed to your profile" }); + const { name, email, phone, gender, profile_picture } = req.body || {}; + + // Build partial update payload (only allowed fields) + const updates = {}; + if (name !== undefined) updates.name = name; + if (email !== undefined) { + const emailRegex = /^\S+@\S+\.\S+$/; + if (!emailRegex.test(email)) { + return res.status(400).json({ message: "Please enter a valid email address" }); + } + updates.email = email; + } + if (phone !== undefined) { + const phoneRegex = /^\d{10}$/; + if (!phoneRegex.test(phone)) { + return res.status(400).json({ message: "Please enter a 10-digit phone number" }); + } + updates.phone = phone; + } + if (gender !== undefined) updates.gender = gender; + if (profile_picture !== undefined) updates.profile_picture = profile_picture; + + if (Object.keys(updates).length === 0) { + return res.status(400).json({ message: "No valid fields to update" }); + } + + const updatedUser = await User.findByIdAndUpdate( + id, + { $set: updates }, + { new: true, runValidators: true, context: "query" } + ) + .select("-password -__v") + .lean(); + + if (!updatedUser) { + return res.status(404).json({ message: "User not found" }); + } + + return res.status(200).json({ message: "Profile updated", user: updatedUser }); } catch (error) { - console.error("updateUser error:", error); - return res.status(500).json({ message: "Internal server error" }); + // Handle duplicate key errors (e.g., unique email) + if (error && error.code === 11000) { + return res.status(409).json({ message: "Email already exists" }); + } + console.error("updateUser error:", error); + return res.status(500).json({ message: "Internal server error" }); } };
35-35: Protect PUT /api/users/:id with authentication and self-or-admin authorizationThe update route in backend/routes/userRoutes.js has no auth or authorization middleware applied, so any client can call it. You need to guard it so that only the resource owner or an admin can perform updates.
• File: backend/routes/userRoutes.js (line 25)
- router.put("/:id", userController.updateUser); + router.put( + "/:id", + authenticate, // require logged-in user + authorizeSelfOrAdmin, // allow only the owner or an admin + userController.updateUser + );• Ensure that authenticate and authorizeSelfOrAdmin are imported from your auth middleware (e.g. backend/middlewares/auth.js).
src/User/pages/Dashboard/ProfilePage.jsx (2)
12-12: Replace hard-coded localhost with env-based base URL (and consider a preconfigured axios instance).Hard-coding will break prod and HTTPS. Use Vite env.
-const API_URL = "http://localhost:3000/api/users"; +const API_URL = `${import.meta.env.VITE_API_BASE_URL}/api/users`;Optionally centralize axios:
// api.js import axios from "axios"; export const api = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, withCredentials: true, // if you rely on cookie sessions });Then replace axios.get/put with api.get/put.
#!/bin/bash # Find any remaining hard-coded hosts and legacy endpoints rg -nP 'http://localhost:3000|https://vigybag-backend\.onrender\.com' -C2 --type=js --type=jsx rg -nP '/api/auth/update-details' -C2 # Confirm ProfilePage uses env-based URL only rg -nP 'API_URL\s*=\s*".*"' -n src/User/pages/Dashboard/ProfilePage.jsx -C2
303-329: Fix update handler: whitelist fields and await the request (you builddatabut senduser).Sends entire user object (privilege risk) and mixes try/catch with promise chaining.
- const handleSubmit = (e) => { - e.preventDefault(); - if (!user._id) return; - - try { - const data = { - name: user.name ?? "", - email: user.email ?? "", - phone: user.phone ?? "", - gender: user.gender ?? "", - profile_picture: user.profile_picture ?? "", - }; - axios - .put(API_URL + `/${user._id}`, user) - .then((res) => { - setUser((prev) => ({...prev, ...res.data.user})); - toast.success(res.data?.message || "Profile updated"); - }) - .catch((err) => { - toast.error( - err.response?.data?.message || "Error while updating profile" - ); - }); - } catch (err) { - toast.error("Failed to update profile"); - } - }; + const handleSubmit = async (e) => { + e.preventDefault(); + if (!user?._id) return; + const payload = { + name: user.name ?? "", + email: user.email ?? "", + phone: user.phone ?? "", + gender: user.gender ?? "", + profile_picture: user.profile_picture ?? "", + }; + try { + const res = await axios.put(`${API_URL}/${user._id}`, payload); + const updated = res.data?.user ?? payload; + setUser((prev) => ({ ...prev, ...updated })); + toast.success(res.data?.message || "Profile updated"); + } catch (err) { + toast.error(err.response?.data?.message || "Error while updating profile"); + } + };
🧹 Nitpick comments (5)
backend/controllers/userController.js (1)
6-6: Deduplicate the projection string.Create a single constant to avoid drift when adding/removing sensitive fields.
+const SAFE_USER_PROJECTION = "-password -__v"; ... - const users = await User.find().select("-password -__v").lean(); + const users = await User.find().select(SAFE_USER_PROJECTION).lean(); ... - const user = await User.findOne({ username: user_name }) - .select("-password -__v") + const user = await User.findOne({ username: user_name }) + .select(SAFE_USER_PROJECTION) .lean(); ... - ).select("-password -__v") + ).select(SAFE_USER_PROJECTION) .lean();Also applies to: 23-23, 57-57
src/User/pages/Dashboard/ProfilePage.jsx (4)
43-43: Remove debug log.-console.log(user);
23-25: Rename default avatar constant to avoid confusion with user.profile_picture.-const profile_picture = +const DEFAULT_PROFILE_PICTURE = "https://img.freepik.com/free-psd/3d-illustration-human-avatar-profile_23-2150671142.jpg";- src={user.profile_picture || profile_picture} + src={user.profile_picture || DEFAULT_PROFILE_PICTURE}Also applies to: 384-385
341-365: Validate URL before accepting profile picture.Reject non-http(s) or malformed URLs to reduce bad inputs.
- if (imageUrl) { - setUser((prev) => ({ ...prev, profile_picture: imageUrl })); + if (imageUrl) { + try { + const u = new URL(imageUrl); + if (!/^https?:$/.test(u.protocol)) throw new Error("Invalid protocol"); + } catch { + return Swal.fire({ + icon: "error", + title: "Invalid URL", + text: "Please enter a valid http(s) image URL.", + }); + } + setUser((prev) => ({ ...prev, profile_picture: imageUrl }));
441-450: Add client-side constraints for phone input.Improve UX and basic validation.
- <input + <input type="tel" name="phone" value={user.phone} onChange={handleChange} className="w-full md:w-[70%] border p-3" placeholder="Enter your phone number" - minLength={10} - maxLength={10} + minLength={10} + maxLength={10} + inputMode="numeric" + pattern="^[0-9]{10}$" + autoComplete="tel" />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
backend/controllers/userController.js(1 hunks)src/User/components/Navbar/UserNavbar.jsx(6 hunks)src/User/pages/Dashboard/ProfilePage.jsx(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/User/components/Navbar/UserNavbar.jsx
🧰 Additional context used
🧬 Code graph analysis (2)
backend/controllers/userController.js (3)
backend/models/User.js (2)
mongoose(1-1)User(59-59)backend/routes/userRoutes.js (2)
User(3-3)req(5-20)src/User/pages/Dashboard/ProfilePage.jsx (2)
user(15-21)profile_picture(23-24)
src/User/pages/Dashboard/ProfilePage.jsx (5)
backend/controllers/userController.js (2)
user(22-24)user(49-49)src/User/components/Navbar/UserNavbar.jsx (1)
username(21-21)src/User/components/Aside/Aside.jsx (1)
username(85-85)src/User/pages/AdditionalDetails.jsx (2)
e(10-36)src/User/pages/UserAuth/UserAuth.jsx (1)
e(97-144)
🔇 Additional comments (2)
backend/controllers/userController.js (2)
4-12: LGTM: sanitized list endpoint.Good use of
.select("-password -__v"),.lean(), and consistent 500 handling.
15-33: LGTM: username lookup is consistent and safe.Validates param, excludes sensitive fields, returns JSON with clear 404 and 500 paths.
| const username = localStorage.getItem("username") || ""; | ||
|
|
||
| const fetchUserDetails = () => { | ||
| try { | ||
| axios.get(API_URL + `/${username}`).then((res) => { | ||
| console.log(res); | ||
| setUser(res.data.user); | ||
| }); | ||
| } catch (err) { | ||
| console.log(err); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard empty username and make fetchUserDetails async/await with real error handling.
Current try/catch won’t catch axios errors; empty username can hit wrong endpoint.
-const fetchUserDetails = () => {
- try {
- axios.get(API_URL + `/${username}`).then((res) => {
- console.log(res);
- setUser(res.data.user);
- });
- } catch (err) {
- console.log(err);
- }
-};
+const fetchUserDetails = async () => {
+ if (!username) {
+ toast.error("No username found. Please log in again.");
+ return;
+ }
+ try {
+ const res = await axios.get(`${API_URL}/${encodeURIComponent(username)}`);
+ setUser(res.data?.user ?? res.data);
+ } catch (err) {
+ toast.error(err.response?.data?.message || "Failed to load profile");
+ }
+};
-useEffect(() => {
- fetchUserDetails();
-}, []);
+useEffect(() => {
+ if (username) fetchUserDetails();
+}, [username]);Also applies to: 39-41
🤖 Prompt for AI Agents
In src/User/pages/Dashboard/ProfilePage.jsx around lines 26-37 (and also apply
same fix to 39-41), the code uses a synchronous try/catch with axios.get and
does not guard against an empty username; move fetchUserDetails to an async
function that first checks if username is non-empty and returns early if not,
then await axios.get(API_URL + `/${username}`) inside a try/catch so network
errors are caught, log or set a proper error state in the catch, and update
setUser only on successful response (validate res.data.user exists) to avoid
hitting a wrong endpoint or unhandled promise rejections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (4)
src/User/pages/Dashboard/ProfilePage.jsx (4)
9-11: Consider a preconfigured axios instance (baseURL, withCredentials, interceptors).Centralizing axios improves consistency and error handling across pages.
If you don’t have one yet, add a small wrapper and reuse here:
// src/lib/api.js import axios from "axios"; export const api = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, withCredentials: true, // if backend uses cookies });Then replace axios.get/put with api.get/put.
12-12: Replace hard-coded API base URL with env-based config.Hard-coding localhost breaks non-dev environments and HTTPS. Use an env var.
-const API_URL = "http://localhost:3000/api/users"; +const API_URL = `${import.meta.env.VITE_API_BASE_URL}/api/users`;Run to find other hard-coded hosts:
#!/bin/bash rg -nP 'http://localhost:3000|https://vigybag-backend\.onrender\.com' -C2 --type=js --type=jsx
30-39: Make fetchUserDetails async, guard empty username, and URL-encode.The try/catch won’t catch axios errors with .then(); also handle empty username and special chars.
- const fetchUserDetails = () => { - try { - axios.get(API_URL + `/${username}`).then((res) => { - setUser(res.data?.user ?? res.data); - }); - } catch (err) { - console.log(err); - } - }; + const fetchUserDetails = async () => { + const name = localStorage.getItem("username") || ""; + if (!name) { + toast.error("No username found. Please log in again."); + return; + } + try { + const res = await axios.get(`${API_URL}/${encodeURIComponent(name)}`); + setUser(res.data?.user ?? res.data); + } catch (err) { + console.error(err); + toast.error(err.response?.data?.message || "Failed to load profile"); + } + };
64-91: Use async/await in handleSubmit and safely merge response.Promise chaining inside try/catch won’t catch network errors; spreading undefined can throw.
- const handleSubmit = (e) => { + const handleSubmit = async (e) => { e.preventDefault(); if (!user._id) return; - try { - const data = { + try { + const data = { name: user.name ?? "", email: user.email ?? "", phone: user.phone ?? "", gender: user.gender ?? "", profile_picture: user.profile_picture ?? "", }; - axios - .put(API_URL + `/${user._id}`, data) - .then((res) => { - setUser((prev) => ({ ...prev, ...res.data.user })); - toast.success(res.data?.message || "Profile updated"); - }) - .catch((err) => { - toast.error( - err.response?.data?.message || "Error while updating profile" - ); - }); + const res = await axios.put(`${API_URL}/${user._id}`, data); + const updated = res.data?.user ?? {}; + setUser((prev) => ({ ...prev, ...updated })); + toast.success(res.data?.message || "Profile updated"); } catch (err) { - toast.error("Failed to update profile"); + toast.error(err?.response?.data?.message || "Error while updating profile"); } };
🧹 Nitpick comments (5)
src/User/pages/Dashboard/ProfilePage.jsx (5)
27-29: Username read from localStorage won’t update when Aside writes it post-mount.Because username is captured once, this effect may never fire if Aside sets it later. Read from localStorage inside the effect (already addressed in the previous comment) and trigger once on mount.
Please verify: load ProfilePage via a route that sets ?name= in Aside. Confirm a GET to /api/users/:username occurs on first load; if not, consider lifting username into a shared Auth context or passing it via props.
- useEffect(() => { - if (username) fetchUserDetails(); - }, [username]); + useEffect(() => { + fetchUserDetails(); + }, []);Also applies to: 41-45
23-26: Rename default avatar const and add image onError fallback; improve alt text.Avoid name collision with user.profile_picture; ensure broken URLs fallback gracefully; better a11y.
-// Default profile picture URL -const profile_picture = +// Default profile picture URL +const DEFAULT_AVATAR_URL = "https://img.freepik.com/free-psd/3d-illustration-human-avatar-profile_23-2150671142.jpg";- <img - src={user.profile_picture || profile_picture} - alt="Profile" - className="w-full h-full rounded-full object-cover" - /> + <img + src={user.profile_picture || DEFAULT_AVATAR_URL} + alt={user.name ? `${user.name}'s profile photo` : "Profile photo"} + onError={(e) => { + e.currentTarget.src = DEFAULT_AVATAR_URL; + e.currentTarget.onerror = null; + }} + className="w-full h-full rounded-full object-cover" + />Also applies to: 391-395
59-62: Harden clipboard copy with async/await and error handling.-const copyText = (text) => { - toast("Copied to Clipboard!"); - navigator.clipboard.writeText(text); -}; +const copyText = async (text) => { + try { + await navigator.clipboard.writeText(text); + toast("Copied to Clipboard!"); + } catch { + toast.error("Copy failed"); + } +};
449-458: Add inputMode/pattern for phone to enforce 10 digits.<input type="tel" name="phone" value={user.phone} onChange={handleChange} className="w-full md:w-[70%] border p-3" placeholder="Enter your phone number" minLength={10} maxLength={10} + inputMode="numeric" + pattern="[0-9]{10}" + title="Enter a 10-digit phone number" />
107-117: Validate image URL more strictly and trim value.Limit to http(s) and surface validation errors.
const { value: imageUrl } = await Swal.fire({ title: "Enter Image URL", input: "url", inputPlaceholder: "https://example.com/profile.jpg", showCancelButton: true, confirmButtonText: "Save", cancelButtonText: "Cancel", - inputValidator: (value) => { - if (!value) return "You must enter a URL!"; - }, + inputValidator: (value) => { + const v = (value || "").trim(); + if (!v) return "You must enter a URL!"; + try { + const u = new URL(v); + if (!/^https?:$/.test(u.protocol)) return "Only http(s) URLs are allowed"; + } catch { + return "Enter a valid URL"; + } + }, }); - if (imageUrl) { - setUser((prev) => ({ ...prev, profile_picture: imageUrl })); + if (imageUrl) { + const v = imageUrl.trim(); + setUser((prev) => ({ ...prev, profile_picture: v }));Also applies to: 119-130
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
backend/controllers/userController.js(1 hunks)src/User/pages/Dashboard/ProfilePage.jsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/controllers/userController.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/User/pages/Dashboard/ProfilePage.jsx (3)
backend/controllers/userController.js (2)
user(22-24)user(49-49)src/User/components/Navbar/UserNavbar.jsx (1)
username(21-21)src/User/components/Aside/Aside.jsx (2)
username(85-85)Aside(71-219)
🔇 Additional comments (1)
src/User/pages/Dashboard/ProfilePage.jsx (1)
7-7: Import statement verified:model.cssexists and is correct.The file
src/User/pages/Dashboard/model.cssis present in the codebase, and there is nomodal.css. The import on line 7 can remain unchanged.
- Located:
src/User/pages/Dashboard/model.css- No
modal.cssfound
| .put(API_URL + `/${user._id}`, data) | ||
| .then((res) => { | ||
| setUser((prev) => ({ ...prev, ...res.data.user })); | ||
| toast.success(res.data?.message || "Profile updated"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Defensive merge: handle cases where res.data.user is absent.
Spreading undefined throws. The earlier fix in handleSubmit addresses this; ensure similar handling anywhere else you merge server responses.
Search:
🏁 Script executed:
#!/bin/bash
rg -nP '\.\.\.\s*res\.data\.user' -C2 srcLength of output: 471
Patch fallback for missing res.data.user
To prevent a crash when the server response doesn’t include a user object, update the spread merge to default to an empty object.
• File: src/User/pages/Dashboard/ProfilePage.jsx (around line 80)
• Change:
- setUser((prev) => ({ ...prev, ...res.data.user }));
+ setUser((prev) => ({ ...prev, ...(res.data.user ?? {}) }));This ensures that if res.data.user is undefined, we merge in {} instead of throwing.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .put(API_URL + `/${user._id}`, data) | |
| .then((res) => { | |
| setUser((prev) => ({ ...prev, ...res.data.user })); | |
| toast.success(res.data?.message || "Profile updated"); | |
| .put(API_URL + `/${user._id}`, data) | |
| .then((res) => { | |
| setUser((prev) => ({ ...prev, ...(res.data.user ?? {}) })); | |
| toast.success(res.data?.message || "Profile updated"); |
🤖 Prompt for AI Agents
In src/User/pages/Dashboard/ProfilePage.jsx around lines 78 to 81, the setUser
call spreads res.data.user directly which will throw if res.data.user is
undefined; change the merge to spread res.data.user || {} (i.e. default to an
empty object) so setUser((prev) => ({ ...prev, ...(res.data?.user || {}) })) and
keep the toast logic the same.
|
Sir, I have updated the code and fixed all the issue which were mentioned above. Note: |
|
@MohammedFaraan can we connect via meeting ?? |
|
@codervivek5 Yay sure. When you will conduct? |
|
Kindly mention the issue number in first commet as Close #issue_number |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
User Profile Enhancements & API Integration
🛠️ Changes Implemented
1. User Profile Menu Fix
2. User Model Update
namegenderprofile_picture3. Backend Enhancements
userControllerwith APIs for:userRoutes.4. Frontend Integration
✅ Outcome
This pull request closes issue no. #2622
🔖 Tags
enhancementbugfixfrontendbackendapiresponsive-designOSCI-25High Priority📸 Screenshots
🔹 Before
🔹 After – Fixed Profile Menu
🔹 Updated Profile Page
🔹 Responsive Profile Page
Summary by CodeRabbit
New Features
Refactor
Chore
Style