Skip to content

Conversation

@MohammedFaraan
Copy link
Contributor

@MohammedFaraan MohammedFaraan commented Aug 26, 2025

User Profile Enhancements & API Integration

🛠️ Changes Implemented

1. User Profile Menu Fix

  • Resolved issue where the User Profile Menu was fully visible on the homepage.
  • Fixed broken menu links caused by styling issues.
  • Improved dropdown styling & interactivity for better UX.

2. User Model Update

  • Extended User Schema with the following fields:
    • name
    • gender
    • profile_picture

3. Backend Enhancements

  • Created userController with APIs for:
    • Fetching all users
    • Fetching a user by username
    • Updating user details
  • Registered the above APIs in userRoutes.

4. Frontend Integration

  • Integrated new user APIs into the User Profile page.
  • Implemented Profile Picture Update functionality (frontend ↔ backend).
  • Made the Profile Page fully responsive across devices.

✅ Outcome

  • Fixed UI/UX issues in profile dropdown.
  • Enabled full user profile management (view, update, profile picture).
  • Improved responsive design for mobile and desktop.
  • Strengthened backend integration for user management.

This pull request closes issue no. #2622


🔖 Tags

  • enhancement
  • bugfix
  • frontend
  • backend
  • api
  • responsive-design
  • OSCI-25
  • High Priority

📸 Screenshots

🔹 Before

image

🔹 After – Fixed Profile Menu

image

🔹 Updated Profile Page

image

🔹 Responsive Profile Page

image

Summary by CodeRabbit

  • New Features

    • Profile page now loads and updates extended user details (name, gender, profile picture via URL); improved coupons display and copy action; FAQs and prompts added.
  • Refactor

    • User endpoints reorganized into list, fetch-by-username, and update routes; public user API base path changed to /api/users.
  • Chore

    • Environment variable loading enabled for server startup.
  • Style

    • Navbar: "Meet the Makers" restored; searchable items expanded and avatar dropdown layout refined.

@vercel
Copy link

vercel bot commented Aug 26, 2025

@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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Backend bootstrap
backend/app.js
Import and invoke dotenv.config() and change user route mount from /vpi to /api/users.
User model updates
backend/models/User.js
Add optional schema fields: name, gender, profile_picture (default null).
User controller
backend/controllers/userController.js
New module exporting getUsers, getUserByUsername, updateUser with validation, DB queries/updates, and error logging.
User routes refactor
backend/routes/userRoutes.js
Replace inline handlers with controller delegation: GET /, GET /:user_name, PUT /:id; previous POST /update-details commented out.
Profile page integration
src/User/pages/Dashboard/ProfilePage.jsx
Centralize user state, fetch user by username on mount, PUT /api/users/:id for updates, profile-picture-by-URL flow, UI/layout and coupon display changes.
Navbar UI adjustments
src/User/components/Navbar/UserNavbar.jsx
Restructure avatar/dropdown trigger and positioning (top-14), re-enable “Meet the Makers”, update searchable/latest items and dropdown composition; minor formatting tweaks.

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
Loading
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)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • meet_the_makers #2610 — touches src/User/components/Navbar/UserNavbar.jsx to add/enable the "Meet the Makers" entry (UI overlap).

Suggested labels

OSCI, 3⭐⭐⭐

Suggested reviewers

  • codervivek5

Poem

A rabbit sips on morning dew,
Routes and fields arranged anew,
Dropdowns hop and profiles shine,
Tiny changes, neatly line by line. 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@MohammedFaraan
Copy link
Contributor Author

MohammedFaraan commented Aug 26, 2025

This pull request closes issue no. #2622

@MohammedFaraan MohammedFaraan changed the title User Profile Enhancements & API Integration ✨ User Profile Enhancements & API Integration Aug 26, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 103df62 and 4999031.

📒 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";
Copy link
Contributor

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.

Comment on lines 30 to 38
const fetchUserDetails = () => {
try {
axios.get(API_URL + `/${username}`).then((res) => {
setUser(res.data);
});
} catch (err) {
console.log(err);
}
};
Copy link
Contributor

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 src

Length 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): swap
      const API_URL = "http://localhost:3000/api/users";
      for
      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): update axios.post("https://vigybag-backend.onrender.com/api/auth/send-otp",…) and verify-otp calls to use BASE_API_URL.
    • In src/User/pages/UserAuth/UserAuth.jsx (lines 49–51, 121–123, 174–175): replace login, signup, and OAuth redirects with ${BASE_API_URL}/….
    • In src/User/pages/ForgotPasswordForm.jsx (line 14): switch the forgot-password POST to use BASE_API_URL.
    • In src/User/pages/AdditionalDetails.jsx (line 14): remove the legacy
      axios.post("https://vigybag-backend.onrender.com/api/auth/update-details", { email, phone });
      and import API_URL from config instead.
  • Remove legacy /api/auth/update-details and implement the new PUT /api/users/:id endpoint:

    • 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.
  • Verify and align to the actual response shapes:

    • GET /api/users/:username — confirm whether res.data or res.data.user should be passed to setUser.
    • PUT /api/users/:id — adjust your success handler based on the returned payload.
🤖 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.

Comment on lines 303 to 321
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");
}
Copy link
Contributor

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.

Suggested change
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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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, and runValidators; 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 authorization

The 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 build data but send user).

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 4999031 and 2d0dae2.

📒 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)
  • email (5-83)
  • 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.

Comment on lines 26 to 37
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);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 624b800 and f84544c.

📒 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.css exists and is correct.

The file src/User/pages/Dashboard/model.css is present in the codebase, and there is no modal.css. The import on line 7 can remain unchanged.

  • Located: src/User/pages/Dashboard/model.css
  • No modal.css found

Comment on lines +78 to +81
.put(API_URL + `/${user._id}`, data)
.then((res) => {
setUser((prev) => ({ ...prev, ...res.data.user }));
toast.success(res.data?.message || "Profile updated");
Copy link
Contributor

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 src

Length 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.

Suggested change
.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.

@MohammedFaraan
Copy link
Contributor Author

MohammedFaraan commented Aug 27, 2025

Sir, I have updated the code and fixed all the issue which were mentioned above.
Some of them are not necessary, so I haven't done that.

Note:
I have included the API URL of the localhost, change it when you deploy it!
const API_URL = "http://localhost:3000/api/users";

@codervivek5
Copy link
Owner

@MohammedFaraan can we connect via meeting ??

@MohammedFaraan
Copy link
Contributor Author

MohammedFaraan commented Aug 27, 2025

@codervivek5 Yay sure. When you will conduct?
You Can also message me through LinkedIn, as I have already connected with you.

@codervivek5 codervivek5 added OSCI good first issue Good for newcomers labels Aug 27, 2025
@codervivek5
Copy link
Owner

Kindly mention the issue number in first commet as Close #issue_number

@vercel
Copy link

vercel bot commented Aug 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
vigybag Ready Ready Preview Comment Aug 27, 2025 6:46pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good first issue Good for newcomers OSCI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants