Skip to content

feat(profile): add profile page, edit profile UI, and logout flow#780

Open
Prashik-Sasane wants to merge 1 commit intoAOSSIE-Org:mainfrom
Prashik-Sasane:feat/profile-ui
Open

feat(profile): add profile page, edit profile UI, and logout flow#780
Prashik-Sasane wants to merge 1 commit intoAOSSIE-Org:mainfrom
Prashik-Sasane:feat/profile-ui

Conversation

@Prashik-Sasane
Copy link

@Prashik-Sasane Prashik-Sasane commented Dec 14, 2025

Summary

This PR introduces a dedicated Profile feature that improves user account management and navigation.
It adds a Profile page, Edit Profile UI, and a logout flow with confirmation, following existing PictoPy UI patterns and theme support.


Changes Overview

Area File(s) Description
Profile Page ProfilePage.tsx Added a new Profile page displaying user avatar, name, and account status
Edit Profile EditProfilePage.tsx Added UI to edit user display name and avatar (UI-only for now)
Navbar Navbar.tsx Updated avatar click to navigate to Profile instead of Settings
Logout Flow LogoutDialog.tsx Added confirmation dialog before logging out
UI Utilities alert-dialog.tsx Added reusable alert dialog component
Routing routes.ts, AppRoutes.tsx Added Profile and Edit Profile routes
State Management onboardingSlice.ts Added reset logic for onboarding state on logout

Demo / Explanation Videos

Video:

PictoPy.mp4

Profile Page Overview

Shows the Profile page layout, avatar display, user name, and available actions.

  • Profile page UI
  • Theme compatibility (light & dark)

Edit Profile Flow

Demonstrates navigation from Profile → Edit Profile and updating user details (UI-level).

  • Edit Profile page
  • Form interaction
  • Navigation back to Profile

Logout Confirmation

Shows the logout confirmation dialog and state reset behavior.

Video:

  • Logout dialog
  • Confirmation handling
  • Redirect to home

Notes

  • UI-first implementation (no backend changes)
  • Does not affect existing authentication logic
  • Reuses existing design system and components
  • Scoped intentionally to Profile-related features only

Future Improvements

  • Persist profile changes via backend API
  • Upload & crop avatar image
  • Account activity and security section

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Profile page to view user information and profile completion percentage.
    • Added Edit Profile page to update name and avatar.
    • Implemented logout confirmation dialog for account security.
    • Enhanced navigation bar styling and profile navigation links.

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

Walkthrough

Introduces a user profile feature with edit and logout capabilities. Adds a new AlertDialog UI component, LogoutDialog confirmation component, ProfilePage and EditProfilePage for viewing and editing user profiles, and updates the Navbar with styling refinements and navigation link changes. Updates onboardingSlice with localStorage persistence and a new resetOnboarding action. Adds profile routes to the application.

Changes

Cohort / File(s) Change Summary
UI Components
frontend/src/components/ui/alert-dialog.tsx
New reusable AlertDialog component built on Radix UI primitives; exports composed sub-components (Content, Header, Title, Description, Footer, Action, Cancel) with styling and accessibility wiring.
Dialog Components
frontend/src/components/Dialog/LogoutDialog.tsx
New LogoutDialog component using AlertDialog primitives; renders destructive confirmation dialog with Cancel and Logout actions controlled by open state and callback handlers.
Navigation
frontend/src/components/Navigation/Navbar/Navbar.tsx
Updated styling (background class, search input transparency), adjusted avatar display with fallback, simplified query image handling, updated user greeting with displayName, changed profile navigation link from /settings to /profile, refined button labels and aria attributes.
Route Constants & Setup
frontend/src/constants/routes.ts, frontend/src/routes/AppRoutes.tsx
Added PROFILE and EDIT_PROFILE route constants; wired new ProfilePage and EditProfilePage components into AppRoutes layout.
State Management
frontend/src/features/onboardingSlice.ts
Added localStorage persistence in setAvatar and setName; introduced resetOnboarding reducer to clear persisted data and reset state; refactored markCompleted and previousStep with variable renames and concise predicate updates.
Profile Pages
frontend/src/pages/ProfilePage/ProfilePage.tsx, frontend/src/pages/ProfilePage/EditProfilePage.tsx
New ProfilePage displaying user info (name, avatar, completion %), actions (Edit, Settings, Logout), and logout confirmation dialog; new EditProfilePage providing form UI for name and avatar updates with Save/Cancel actions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • onboardingSlice.ts: localStorage persistence integration and resetOnboarding logic; verify side effects and consistency with existing state patterns
  • Navbar.tsx: styling changes and profile link navigation update; confirm visual consistency and no regression in functionality
  • alert-dialog.tsx: Radix UI primitive composition and forwardRef typing; ensure accessibility attributes and styling integration are correct
  • ProfilePage & EditProfilePage: form state management and Redux integration; verify proper data flow and image upload handling

Possibly related PRs

  • Fixed the Onboarding Page Setup #485: Modifies the same onboardingSlice.ts file with overlapping changes to setAvatar, setName, and onboarding action exports; may require conflict resolution or coordination.

Suggested labels

enhancement, frontend

Poem

🐰 Hop along, a profile's born,
With edits fresh and dialogs worn,
Logout confirmed, localStorage keeps,
User data safe in storage's deep.
New routes emerge, clean and bright,
The dashboard shines in CSS light!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: addition of profile page, edit profile UI, and logout flow, all of which are clearly present throughout the changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • 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

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

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

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: 3

🧹 Nitpick comments (6)
frontend/src/pages/ProfilePage/ProfilePage.tsx (2)

19-22: Use React Router's useNavigate instead of window.location.href.

Using window.location.href causes a full page reload, which loses React state and defeats the purpose of a SPA. This is especially problematic since you're dispatching a Redux action right before navigation.

+import { useNavigate } from 'react-router';
+
 const ProfilePage = () => {
   const dispatch = useDispatch();
+  const navigate = useNavigate();
   const { theme } = useTheme();
   // ...
   const handleLogout = () => {
     dispatch(resetOnboarding());
-    window.location.href = '/';
+    navigate('/');
   };

82-96: Use React Router's <Link> component for internal navigation.

Using <a href="..."> for internal routes causes full page reloads. Replace with <Link to="..."> from react-router to maintain SPA behavior and preserve application state.

+import { Link } from 'react-router';
+
-            <a
-              href="/profile/edit"
+            <Link
+              to="/profile/edit"
               className="border-border bg-muted hover:bg-accent flex items-center justify-between rounded-xl border px-4 py-3 text-sm font-medium"
             >
               <span>Edit Profile</span>
               <span className="text-muted-foreground text-xs">⌘E</span>
-            </a>
+            </Link>

-            <a
-              href="/settings"
+            <Link
+              to="/settings"
               className="border-border bg-muted hover:bg-accent flex items-center justify-between rounded-xl border px-4 py-3 text-sm font-medium"
             >
               <span>Account Settings</span>
               <span className="text-muted-foreground text-xs">⚙</span>
-            </a>
+            </Link>
frontend/src/pages/ProfilePage/EditProfilePage.tsx (2)

16-20: Use React Router's useNavigate instead of window.location.href.

Same issue as ProfilePage—full page reload loses state and SPA benefits.

+import { useNavigate } from 'react-router';
+
 const EditProfilePage = () => {
   const dispatch = useDispatch();
+  const navigate = useNavigate();
   // ...
   const handleSave = () => {
     dispatch(setName(name));
     dispatch(setAvatar(avatar));
-    window.location.href = '/profile';
+    navigate('/profile');
   };

27-32: Use <Link> from React Router for internal navigation.

Replace <a href="..."> with <Link to="..."> to prevent full page reloads.

Also applies to: 77-79

frontend/src/features/onboardingSlice.ts (1)

25-34: Side effects in reducers violate Redux principles.

Calling localStorage.setItem inside reducers is a side effect. While Redux Toolkit allows mutations via Immer, side effects should ideally be handled in middleware, thunks, or through listeners. This works but can cause issues with time-travel debugging and testing.

Consider moving localStorage persistence to a Redux listener middleware or handling it in the component layer if you want stricter Redux purity. For a UI-first implementation this is acceptable, but worth noting for future backend integration.

frontend/src/components/Navigation/Navbar/Navbar.tsx (1)

89-95: Consider using <Link> for internal navigation.

Similar to the profile pages, using <Link to="/profile"> instead of <a href="/profile"> would maintain SPA navigation. However, I note the logo also uses <a href="/">, so this follows the existing pattern in this file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d07d817 and a2de60c.

📒 Files selected for processing (8)
  • frontend/src/components/Dialog/LogoutDialog.tsx (1 hunks)
  • frontend/src/components/Navigation/Navbar/Navbar.tsx (4 hunks)
  • frontend/src/components/ui/alert-dialog.tsx (1 hunks)
  • frontend/src/constants/routes.ts (1 hunks)
  • frontend/src/features/onboardingSlice.ts (1 hunks)
  • frontend/src/pages/ProfilePage/EditProfilePage.tsx (1 hunks)
  • frontend/src/pages/ProfilePage/ProfilePage.tsx (1 hunks)
  • frontend/src/routes/AppRoutes.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
frontend/src/pages/ProfilePage/EditProfilePage.tsx (2)
frontend/src/features/onboardingSelectors.ts (2)
  • selectName (15-15)
  • selectAvatar (13-13)
frontend/src/features/onboardingSlice.ts (2)
  • setName (31-34)
  • setAvatar (26-29)
frontend/src/routes/AppRoutes.tsx (1)
frontend/src/constants/routes.ts (1)
  • ROUTES (1-14)
frontend/src/pages/ProfilePage/ProfilePage.tsx (4)
frontend/src/contexts/ThemeContext.tsx (1)
  • useTheme (66-73)
frontend/src/features/onboardingSelectors.ts (2)
  • selectName (15-15)
  • selectAvatar (13-13)
frontend/src/features/onboardingSlice.ts (1)
  • resetOnboarding (37-41)
frontend/src/components/Dialog/LogoutDialog.tsx (1)
  • LogoutDialog (18-42)
frontend/src/components/Dialog/LogoutDialog.tsx (1)
frontend/src/components/ui/alert-dialog.tsx (8)
  • AlertDialog (146-146)
  • AlertDialogContent (148-148)
  • AlertDialogHeader (149-149)
  • AlertDialogTitle (150-150)
  • AlertDialogDescription (151-151)
  • AlertDialogFooter (152-152)
  • AlertDialogCancel (154-154)
  • AlertDialogAction (153-153)
frontend/src/components/Navigation/Navbar/Navbar.tsx (3)
frontend/src/features/onboardingSelectors.ts (2)
  • selectName (15-15)
  • selectAvatar (13-13)
frontend/src/components/ui/input.tsx (1)
  • Input (21-21)
frontend/src/components/Dialog/FaceSearchDialog.tsx (1)
  • FaceSearchDialog (25-153)
🔇 Additional comments (5)
frontend/src/constants/routes.ts (1)

12-13: LGTM!

The new route constants follow the existing naming conventions and the nested route structure (profile/edit under profile) is logical.

frontend/src/routes/AppRoutes.tsx (1)

12-13: LGTM!

The new page imports and routes are correctly integrated within the Layout wrapper, consistent with other routes in the application.

Also applies to: 28-29

frontend/src/components/Dialog/LogoutDialog.tsx (1)

18-41: LGTM!

Clean implementation using the AlertDialog primitives. The onOpenChange callback correctly handles closing via overlay click or escape key by calling onCancel.

frontend/src/components/Navigation/Navbar/Navbar.tsx (1)

20-21: LGTM!

Good addition of the displayName fallback to 'Guest' for unauthenticated users, and the welcome message displays it cleanly.

Also applies to: 85-86

frontend/src/components/ui/alert-dialog.tsx (1)

1-155: Well-structured AlertDialog component.

This follows the standard shadcn/ui pattern for wrapping Radix UI primitives. Proper use of forwardRef, displayName assignments, and class merging with cn(). The component is ready for reuse across the application.

Comment on lines +36 to 41
/** ✅ LOGOUT / RESET */
resetOnboarding() {
localStorage.removeItem('avatar');
localStorage.removeItem('name');
return initialState;
},
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 | 🟠 Major

resetOnboarding returns stale initialState captured at module load time.

initialState is computed once when the module loads, reading from localStorage at that time. After clearing localStorage in resetOnboarding, returning this cached object still contains the old values that were in localStorage when the app started.

     resetOnboarding() {
       localStorage.removeItem('avatar');
       localStorage.removeItem('name');
-      return initialState;
+      return {
+        currentStepIndex: 0,
+        currentStepName: STEP_NAMES[0],
+        stepStatus: STEP_NAMES.map(() => false),
+        avatar: null,
+        name: '',
+      };
     },
📝 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
/** ✅ LOGOUT / RESET */
resetOnboarding() {
localStorage.removeItem('avatar');
localStorage.removeItem('name');
return initialState;
},
/** ✅ LOGOUT / RESET */
resetOnboarding() {
localStorage.removeItem('avatar');
localStorage.removeItem('name');
return {
currentStepIndex: 0,
currentStepName: STEP_NAMES[0],
stepStatus: STEP_NAMES.map(() => false),
avatar: null,
name: '',
};
},
🤖 Prompt for AI Agents
In frontend/src/features/onboardingSlice.ts around lines 36 to 41,
resetOnboarding currently returns the module-level initialState which was
computed once at module load (stale), so after removing localStorage keys you
must return a fresh state derived from current storage or default values instead
of the cached object; modify resetOnboarding to compute and return a new state
object (e.g., read localStorage for avatar/name or use empty/default values) or
call a helper getInitialState() that builds the state from current localStorage,
ensuring you do not return the original module-scoped initialState reference.

Comment on lines +39 to +42
<img
src={avatar}
className="h-24 w-24 rounded-full border object-cover"
/>
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 alt attribute to the image for accessibility.

The avatar image is missing an alt attribute, which is important for screen readers.

             <img
               src={avatar}
               className="h-24 w-24 rounded-full border object-cover"
+              alt="User avatar preview"
             />
📝 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
<img
src={avatar}
className="h-24 w-24 rounded-full border object-cover"
/>
<img
src={avatar}
className="h-24 w-24 rounded-full border object-cover"
alt="User avatar preview"
/>
🤖 Prompt for AI Agents
In frontend/src/pages/ProfilePage/EditProfilePage.tsx around lines 39 to 42, the
avatar img element is missing an alt attribute; add a meaningful alt prop to the
img (e.g., alt={`Avatar of ${user.name}`} or a fallback like alt="User avatar")
to improve accessibility, and ensure it uses the user's display name if
available or a generic description when not.

Comment on lines +49 to +53
onChange={(e) => {
const file = e.target.files?.[0];
if (!file) return;
setLocalAvatar(URL.createObjectURL(file));
}}
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 | 🟠 Major

Blob URLs are ephemeral and will break after page reload.

URL.createObjectURL() creates a temporary blob URL that's only valid in the current browser session. When this URL is saved to localStorage and the page reloads, the image will fail to load.

Convert to a base64 data URL for persistence:

               onChange={(e) => {
                 const file = e.target.files?.[0];
                 if (!file) return;
-                setLocalAvatar(URL.createObjectURL(file));
+                const reader = new FileReader();
+                reader.onloadend = () => {
+                  setLocalAvatar(reader.result as string);
+                };
+                reader.readAsDataURL(file);
               }}
📝 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
onChange={(e) => {
const file = e.target.files?.[0];
if (!file) return;
setLocalAvatar(URL.createObjectURL(file));
}}
onChange={(e) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onloadend = () => {
setLocalAvatar(reader.result as string);
};
reader.readAsDataURL(file);
}}

@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

1 similar comment
@github-actions
Copy link
Contributor

⚠️ No issue was linked in the PR description.
Please make sure to link an issue (e.g., 'Fixes #issue_number')

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.

1 participant