Skip to content

Conversation

@Muneerali199
Copy link

@Muneerali199 Muneerali199 commented Jan 10, 2026

Fix User Profile Data & Add Editable Profile Page

  • Create GET/PUT /v1/users/me endpoints for profile management
  • Add database migration with users table and auto-sync trigger
  • Rewrite ProfilePage with real data integration
  • Make email field read-only
  • Add loading states and error handling
  • Include toast notifications for UX feedback

⚠️ BREAKING CHANGE: Requires database migration (02_create_users_table.sql)

Closes #222

📝 Description

Fixes the issue where user profile information was incorrect or missing after signup and adds a fully functional editable profile page.

Problem Solved:

  • User profile data was not being captured correctly during signup
  • No dedicated page to view or edit profile information
  • Email field needed to be non-editable for security

Solution Implemented:

  • Created backend API endpoints for profile management
  • Added database trigger to auto-sync user metadata from Supabase Auth
  • Built complete profile page with real-time data fetching
  • Implemented edit mode with proper validation and error handling

🔧 Changes Made

Backend Changes

  • NEW FILE: backend/app/api/v1/users.py - User profile API endpoints (GET & PUT /v1/users/me)
  • NEW FILE: backend/database/02_create_users_table.sql - Database schema with trigger
  • MODIFIED: backend/app/api/router.py - Registered users router

Frontend Changes

  • MODIFIED: frontend/src/lib/api.ts - Added user profile API methods and TypeScript types
  • REWRITTEN: frontend/src/components/pages/ProfilePage.tsx - Complete rebuild with real data integration

Key Features

  • JWT authentication for all profile endpoints
  • Row Level Security (RLS) policies on database
  • Email field visible but non-editable
  • Editable fields: display_name, bio, location, avatar_url
  • Loading states during data fetch
  • Error handling with toast notifications
  • Cancel button to discard changes

🚨 Required Setup for Maintainers

STEP 1: Apply Database Migration (REQUIRED FIRST)

Run the entire backend/database/02_create_users_table.sql file in your Supabase Dashboard → SQL Editor

This creates:

  • public.users table with profile fields
  • Auto-sync trigger for new signups (syncs display_name from auth metadata)
  • Row Level Security policies
  • Database indexes for performance

STEP 2: Restart Backend Server

cd backend
python -m uvicorn main:app --reload --port 8000

STEP 3: Test the Flow

  1. Navigate to /signup and create a new account
  2. After signup, go to /profile
  3. Verify display_name appears correctly
  4. Click "Edit Profile" button
  5. Modify display_name, bio, location
  6. Verify email field is read-only (non-editable)
  7. Click "Save Changes"
  8. Verify success toast notification appears
  9. Refresh page and verify changes persisted

🤝 Collaboration

Developed independently based on issue requirements.

✅ Checklist

  • I have read the contributing guidelines
  • Code follows existing project conventions
  • TypeScript types properly defined
  • Error handling implemented throughout
  • Database migration script provided
  • Documentation added (PR_SUMMARY.md, IMPLEMENTATION_SUMMARY.md)
  • No breaking changes to existing features
  • Security measures implemented (JWT auth, RLS policies, non-editable email)

🔒 Security Considerations

  • ✅ JWT authentication required for all endpoints
  • ✅ Row Level Security ensures users can only access their own data
  • ✅ Email field cannot be modified through UI (prevents account hijacking)
  • ✅ Input validation on both frontend and backend
  • ✅ SQL injection prevention through parameterized queries

📦 Files Changed

File Status Lines Description
backend/app/api/v1/users.py NEW +147 Profile API endpoints
backend/database/02_create_users_table.sql NEW +94 Database schema + trigger
backend/app/api/router.py MODIFIED +7 Register users router
frontend/src/lib/api.ts MODIFIED +45 Profile API client methods
frontend/src/components/pages/ProfilePage.tsx MODIFIED +106/-92 Complete rewrite

⚠️ IMPORTANT: Database migration MUST be run before merging this PR. The feature will not work without it.

Summary by CodeRabbit

  • New Features
    • Users can now view and edit their profile information including display name, bio, and location.
    • Profile page features avatar display with real-time editing capabilities.
    • Email field remains read-only for security purposes.
    • Social platform integration data is displayed on user profiles.
    • Enhanced user feedback with loading states and toast notifications for actions and errors.

✏️ Tip: You can customize this high-level summary in your review settings.

- Create GET/PUT /v1/users/me endpoints for profile management
- Add database migration with users table and auto-sync trigger
- Rewrite ProfilePage with real data integration
- Make email field read-only
- Add loading states and error handling
- Include toast notifications for UX feedback

BREAKING CHANGE: Requires database migration (02_create_users_table.sql)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 10, 2026

📝 Walkthrough

Walkthrough

Introduces a complete user profile feature with backend API endpoints (GET/PUT /v1/users/me), a PostgreSQL database schema with Row-Level Security policies and auto-provisioning triggers, and a redesigned frontend profile page supporting editable user information with email as read-only.

Changes

Cohort / File(s) Summary
Backend API Endpoints
backend/app/api/v1/users.py
New module implementing GET and PUT endpoints for user profiles. Includes UserProfileResponse and UserProfileUpdateRequest schemas, error handling (404/500), and dependency-based authentication.
API Router Integration
backend/app/api/router.py
Registers new UsersRouter under /v1/users prefix with Users tag.
Database Schema & Triggers
backend/database/02_create_users_table.sql
Comprehensive migration creating public.users table with 22 columns, 5 indexes, two RLS policies, auto-update trigger, and sync trigger (handle_new_user()) linking auth.users to extended profile.
Frontend API Client
frontend/src/lib/api.ts
Adds UserProfile and UserProfileUpdateRequest TypeScript interfaces; introduces getUserProfile() and updateUserProfile() methods for API communication.
Profile Page Component
frontend/src/components/pages/ProfilePage.tsx
Complete rewrite replacing static mock data with asynchronous profile loading, edit mode with validation, save/cancel handlers, loading/saving state management, and toast feedback.
Documentation
PULL_REQUEST_TEMPLATE.md, PR_SUMMARY.md
PR template with setup steps, affected files, security notes, and merge checklist; summary document detailing implementation, testing guidance, and UI/API behavior.

Sequence Diagram

sequenceDiagram
    participant User as User (Browser)
    participant Frontend as Frontend (React)
    participant Auth as Auth System
    participant API as Backend API
    participant DB as Database

    User->>Frontend: Navigate to /profile
    Frontend->>Auth: Request current user
    Auth-->>Frontend: Return user_id (JWT)
    Frontend->>API: GET /v1/users/me
    API->>DB: Query users table WHERE id = user_id
    DB-->>API: Return profile data
    API-->>Frontend: UserProfileResponse
    Frontend-->>User: Display profile (read-only)

    User->>Frontend: Click Edit Profile
    Frontend-->>User: Show edit form (email read-only)
    User->>Frontend: Edit display_name, bio, location
    Frontend-->>User: Local state updates
    User->>Frontend: Click Save Changes
    Frontend->>API: PUT /v1/users/me with UserProfileUpdateRequest
    API->>DB: UPDATE users SET... WHERE id = user_id (RLS enforced)
    DB-->>API: Return updated row
    API-->>Frontend: UserProfileResponse (updated)
    Frontend-->>User: Show success toast
    Frontend-->>User: Display updated profile
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested labels

enhancement

Suggested reviewers

  • smokeyScraper
  • chandansgowda

Poem

🐰 Hops with glee through profile rows,
Where users' names and bios grow,
With RLS keeping secrets tight,
And edits flowing left and right—
Email locked, but hearts run free! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main changes: adding a user profile API backend and an editable profile page frontend component to address the linked issue.
Linked Issues check ✅ Passed All primary objectives from issue #222 are met: profile data syncing via DB trigger [#222], editable profile page [#222], email non-editable [#222], editable name/bio/avatar fields [#222], validation and error handling [#222], and access controls via JWT/RLS [#222].
Out of Scope Changes check ✅ Passed All changes directly support the core objectives: backend API and DB schema for profile management, frontend API client and ProfilePage rewrite, and documentation. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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

🤖 Fix all issues with AI agents
In @frontend/src/components/pages/ProfilePage.tsx:
- Around line 39-54: handleSave currently submits without checking that
editedProfile.display_name is present; add a client-side validation at the start
of handleSave to check if editedProfile.display_name (trimmed) is non-empty, and
if it is empty call toast.error('Display Name is required') (or set a validation
state) and return early without calling apiClient.updateUserProfile or toggling
setIsSaving; ensure you trim the value, reference editedProfile.display_name in
the check, and keep the existing try/catch/finally behavior unchanged for
successful paths.

In @IMPLEMENTATION_SUMMARY.md:
- Around line 135-136: The summary in IMPLEMENTATION_SUMMARY.md incorrectly
claims "No breaking changes" and "Backwards compatible" despite the PR requiring
a DB migration (02_create_users_table.sql) and an explicit "BREAKING CHANGE:
migration required" note; update the summary to reflect that a migration is
required (e.g., replace the checkmarks with a clear statement that a migration
must be run before deployment and mark it as a breaking change), and ensure the
document references 02_create_users_table.sql so readers know which migration to
apply.
🧹 Nitpick comments (5)
backend/app/api/v1/users.py (2)

37-68: Improve error logging to include traceback.

The endpoint logic is solid with proper authentication, error handling, and response construction. However, the error logging can be improved.

📝 Use logger.exception for automatic traceback inclusion

Replace logger.error with logger.exception in the except block to automatically include the stack trace, making debugging easier:

     except HTTPException:
         raise
     except Exception as e:
-        logger.error(f"Error fetching user profile: {str(e)}")
+        logger.exception("Error fetching user profile")
         raise HTTPException(
             status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
             detail="Failed to fetch user profile"
         ) from e

Note: logger.exception automatically includes the exception details, so str(e) is not needed.


71-139: Improve error logging and consider reducing code duplication.

The update endpoint is well-structured with proper validation and error handling. Two areas for improvement:

  1. Error logging (recommended): Same issue as the GET endpoint - use logger.exception instead of logger.error at line 135.

  2. Code duplication (optional): The UserProfileResponse construction pattern is repeated three times in this file (lines 99-109, 120-130, and similar in the GET endpoint).

📝 Recommended: Fix error logging
     except HTTPException:
         raise
     except Exception as e:
-        logger.error(f"Error updating user profile: {str(e)}")
+        logger.exception("Error updating user profile")
         raise HTTPException(
             status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
             detail="Failed to update user profile"
         ) from e
♻️ Optional: Extract UserProfileResponse construction to helper function

Consider extracting the response construction into a helper function to reduce duplication:

def _build_profile_response(user) -> UserProfileResponse:
    """Build UserProfileResponse from user object."""
    return UserProfileResponse(
        id=str(user.id),
        email=user.email,
        display_name=user.display_name,
        avatar_url=user.avatar_url,
        bio=user.bio,
        location=user.location,
        github_username=user.github_username,
        discord_username=user.discord_username,
        slack_username=user.slack_username,
        preferred_languages=user.preferred_languages
    )

Then use return _build_profile_response(user) in all three places.

frontend/src/components/pages/ProfilePage.tsx (2)

14-16: Consider adding loadProfile to the dependency array or using useCallback.

The useEffect references loadProfile but the function is not in the dependency array. While this works due to how the function is defined, it can trigger ESLint warnings about exhaustive deps. Consider wrapping loadProfile in useCallback or defining it inside the effect.


282-296: preferred_languages is in the update model but not editable in the UI.

The UserProfileUpdateRequest includes preferred_languages, and editedProfile initializes it, but there's no input to edit this field. Consider either adding an editor for preferred languages or removing it from editedProfile initialization to avoid confusion.

backend/database/02_create_users_table.sql (1)

70-90: Consider adding error handling to prevent auth signup failures.

If the INSERT INTO public.users fails (e.g., unexpected constraint violation), it will cause the entire auth user creation to fail since this is an AFTER INSERT trigger. Consider wrapping in an exception handler to log errors without blocking signup.

Proposed improvement
 CREATE OR REPLACE FUNCTION public.handle_new_user()
 RETURNS TRIGGER AS $$
 BEGIN
+  BEGIN
     INSERT INTO public.users (id, email, display_name, avatar_url)
     VALUES (
         NEW.id,
         NEW.email,
         COALESCE(NEW.raw_user_meta_data->>'display_name', split_part(NEW.email, '@', 1), 'User'),
         NEW.raw_user_meta_data->>'avatar_url'
     );
+  EXCEPTION WHEN OTHERS THEN
+    RAISE WARNING 'Failed to create user profile for %: %', NEW.id, SQLERRM;
+  END;
     RETURN NEW;
 END;
 $$ LANGUAGE plpgsql SECURITY DEFINER;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa66519 and 50ee2a5.

⛔ Files ignored due to path filters (2)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • landing/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • IMPLEMENTATION_SUMMARY.md
  • PR_SUMMARY.md
  • PULL_REQUEST_TEMPLATE.md
  • backend/app/api/router.py
  • backend/app/api/v1/users.py
  • backend/database/02_create_users_table.sql
  • frontend/src/components/pages/ProfilePage.tsx
  • frontend/src/lib/api.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/lib/api.ts (1)
backend/app/api/v1/users.py (1)
  • UserProfileUpdateRequest (28-34)
frontend/src/components/pages/ProfilePage.tsx (2)
frontend/src/lib/api.ts (3)
  • UserProfile (52-63)
  • UserProfileUpdateRequest (65-71)
  • apiClient (219-219)
backend/app/api/v1/users.py (1)
  • UserProfileUpdateRequest (28-34)
🪛 LanguageTool
PR_SUMMARY.md

[style] ~5-~5: Consider using a different verb for a more formal wording.
Context: ...le Profile Page ## 📋 Summary This PR fixes the issue where user profile informatio...

(FIX_RESOLVE)


[style] ~175-~175: It’s more common nowadays to write this noun as one word.
Context: ...mation - Signup didn't properly capture user name - Email was editable (security issue) ...

(RECOMMENDED_COMPOUNDS)

🪛 Ruff (0.14.10)
backend/app/api/v1/users.py

38-38: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


44-47: Abstract raise to an inner function

(TRY301)


64-64: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


64-64: Use explicit conversion flag

Replace with conversion flag

(RUF010)


74-74: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


95-98: Abstract raise to an inner function

(TRY301)


115-118: Abstract raise to an inner function

(TRY301)


135-135: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


135-135: Use explicit conversion flag

Replace with conversion flag

(RUF010)

🔇 Additional comments (16)
PULL_REQUEST_TEMPLATE.md (1)

1-77: LGTM! Clear documentation for maintainers.

The PR template provides comprehensive guidance covering database migration steps, backend restart instructions, testing procedures, and security considerations. The structure is clear and actionable.

backend/app/api/router.py (1)

5-5: LGTM! Router registration follows established pattern.

The users router is correctly imported and registered with the appropriate prefix and tag, consistent with the existing auth, health, and integrations routers.

Also applies to: 27-31

frontend/src/lib/api.ts (2)

51-71: LGTM! Type definitions align with backend.

The UserProfile and UserProfileUpdateRequest interfaces are well-defined and match the backend Pydantic models. Optional fields are correctly marked, and the field types are appropriate.


183-202: LGTM! API methods correctly implemented.

The getUserProfile() and updateUserProfile() methods follow the established pattern in the ApiClient class. They correctly:

  • Use the axios instance with auth token injection
  • Target the appropriate endpoints (/v1/users/me)
  • Return properly typed promises
  • Extract response data consistently
backend/app/api/v1/users.py (2)

1-11: LGTM! Clean imports and setup.

The imports are appropriate, logger is properly configured, and the router instance is correctly initialized.


14-34: LGTM! Well-defined Pydantic models.

The models are properly structured:

  • UserProfileResponse includes all profile fields for read operations
  • UserProfileUpdateRequest correctly excludes non-editable fields (email, id, integration usernames)
  • Types and Optional fields are appropriate
  • Models align with frontend TypeScript interfaces
PR_SUMMARY.md (1)

1-232: LGTM! Comprehensive PR documentation.

The PR summary provides excellent documentation covering all aspects of the feature:

  • Clear description of changes (backend endpoints, database schema, frontend updates)
  • Detailed setup instructions for both backend and frontend
  • Thorough testing procedures
  • Security considerations
  • Future enhancement roadmap

The documentation will be valuable for maintainers and future contributors.

frontend/src/components/pages/ProfilePage.tsx (6)

18-37: LGTM!

The loadProfile function has proper error handling with try/catch/finally, appropriate loading state management, and correctly initializes editedProfile from the fetched data.


56-68: LGTM!

The cancel handler correctly resets the edited profile to the current saved values and exits edit mode.


70-95: LGTM!

Good user experience with clear loading indicator and a retry mechanism for failed profile loads.


106-140: LGTM!

Good UX with proper button state management - buttons are disabled during save to prevent double submission, and the save button shows contextual "Saving..." text.


159-163: LGTM!

Good fallback pattern using ui-avatars.com service with proper URL encoding of the display name.


227-261: LGTM!

Social platform usernames are correctly displayed as read-only fields with appropriate conditional rendering.

backend/database/02_create_users_table.sql (3)

1-38: LGTM!

The table schema is well-designed with appropriate foreign key constraints, cascading deletes, and sensible defaults. The structure aligns with the frontend UserProfile interface.


53-68: LGTM!

RLS policies correctly restrict users to viewing and updating only their own profile. The absence of INSERT/DELETE policies is appropriate since inserts happen via the SECURITY DEFINER trigger and deletes cascade from auth.users.


47-51: No action needed. The update_updated_at_column() function is properly defined in the previous migration backend/database/01_create_integration_tables.sql, which executes before this migration. The trigger will work correctly.

Likely an incorrect or invalid review comment.

Comment on lines +39 to +54
const handleSave = async () => {
if (!profile) return;

setIsSaving(true);
try {
const updatedProfile = await apiClient.updateUserProfile(editedProfile);
setProfile(updatedProfile);
setIsEditing(false);
toast.success('Profile updated successfully!');
} catch (error) {
console.error('Error updating profile:', error);
toast.error('Failed to update profile');
} finally {
setIsSaving(false);
}
};
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 | 🟡 Minor

Add client-side validation for required display_name field.

The UI marks Display Name as required (*), but handleSave doesn't validate that display_name is non-empty before submitting. This could result in a backend validation error or an empty display name being saved.

Proposed fix
 const handleSave = async () => {
   if (!profile) return;
+  
+  if (!editedProfile.display_name?.trim()) {
+    toast.error('Display name is required');
+    return;
+  }
     
   setIsSaving(true);
   try {
📝 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 handleSave = async () => {
if (!profile) return;
setIsSaving(true);
try {
const updatedProfile = await apiClient.updateUserProfile(editedProfile);
setProfile(updatedProfile);
setIsEditing(false);
toast.success('Profile updated successfully!');
} catch (error) {
console.error('Error updating profile:', error);
toast.error('Failed to update profile');
} finally {
setIsSaving(false);
}
};
const handleSave = async () => {
if (!profile) return;
if (!editedProfile.display_name?.trim()) {
toast.error('Display name is required');
return;
}
setIsSaving(true);
try {
const updatedProfile = await apiClient.updateUserProfile(editedProfile);
setProfile(updatedProfile);
setIsEditing(false);
toast.success('Profile updated successfully!');
} catch (error) {
console.error('Error updating profile:', error);
toast.error('Failed to update profile');
} finally {
setIsSaving(false);
}
};
🤖 Prompt for AI Agents
In @frontend/src/components/pages/ProfilePage.tsx around lines 39 - 54,
handleSave currently submits without checking that editedProfile.display_name is
present; add a client-side validation at the start of handleSave to check if
editedProfile.display_name (trimmed) is non-empty, and if it is empty call
toast.error('Display Name is required') (or set a validation state) and return
early without calling apiClient.updateUserProfile or toggling setIsSaving;
ensure you trim the value, reference editedProfile.display_name in the check,
and keep the existing try/catch/finally behavior unchanged for successful paths.

Comment on lines 135 to 136
- ✅ No breaking changes
- ✅ Backwards compatible
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 | 🟡 Minor

Inconsistent claim about breaking changes.

Line 135 states "No breaking changes" but the PR requires a database migration (02_create_users_table.sql) to be run before deployment. The PR summary also explicitly notes "BREAKING CHANGE: migration required." Consider updating this documentation to reflect that accurately.

Proposed fix
-- ✅ No breaking changes
+- ⚠️ Breaking change: Database migration required before deployment
 - ✅ Backwards compatible
📝 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
- ✅ No breaking changes
- ✅ Backwards compatible
- ⚠️ Breaking change: Database migration required before deployment
- ✅ Backwards compatible
🤖 Prompt for AI Agents
In @IMPLEMENTATION_SUMMARY.md around lines 135 - 136, The summary in
IMPLEMENTATION_SUMMARY.md incorrectly claims "No breaking changes" and
"Backwards compatible" despite the PR requiring a DB migration
(02_create_users_table.sql) and an explicit "BREAKING CHANGE: migration
required" note; update the summary to reflect that a migration is required
(e.g., replace the checkmarks with a clear statement that a migration must be
run before deployment and mark it as a breaking change), and ensure the document
references 02_create_users_table.sql so readers know which migration to apply.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: User Profile Data Incorrect After Signup & Missing Editable Profile Page

1 participant