[Customer portal] [web] Add user profile editing modal with phone number and time zone support - #304
Conversation
Introduce a new React hook (apps/customer-portal/webapp/src/api/usePatchUserMe.ts) that provides a react-query mutation for PATCH /users/me. The hook uses Asgardeo auth and an auth-enabled fetch client, validates sign-in state and backend base URL, serializes the request body, parses and surfaces server error messages, and invalidates the "userDetails" query on success.
Add a 'Profile' item to the header user menu and render a UserProfileModal when selected. Introduces useState to manage the modal open state, imports the User icon and UserProfileModal, and wraps the UserMenu in a fragment to include the modal alongside the menu. Existing logout behavior remains unchanged.
Introduce a new UserProfileModal React component that displays user details (avatar, name, email) fetched via useGetUserDetails and lets users edit phone number and time zone. Only changed fields are sent to the server using usePatchUserMe; success and error banners are shown via context. The modal shows a loading state, disables closing/actions while the patch is pending, and uses TIME_ZONE_OPTIONS and Oxygen UI components for form controls.
Stop fetching current user and remove client-side owner-scoped case filtering. The useGetUserDetails import and ownerScopedCases logic were removed; cases are now taken directly from the paginated query (renamed allCases -> displayedCases). A createdByMe flag is passed to the project cases query when viewMode === "my" so server-side filtering is applied, and viewMode was added to the query deps and memo deps to keep results in sync. Pagination and other filters remain unchanged.
Introduce TIME_ZONE_OPTIONS in apps/customer-portal/webapp/src/constants/timeZoneConstants.ts to provide a curated list of time zone strings for the user profile edit dropdown. The file includes the project license header and exports the array as a readonly tuple (`as const`) so callers can rely on a fixed set of options (including a "System (Asia/Colombo)" entry and WSO2/Colombo).
Introduce two request-body interfaces in apps/customer-portal/webapp/src/models/requests.ts: PatchChangeRequestRequest for PATCH /change-requests/:id (plannedStartOn: string) and PatchUserMeRequest for PATCH /users/me (optional phoneNumber and timeZone). These types document the payloads used when updating a change request's planned start and when patching the current user's profile (only changed fields should be included).
Add optional phoneNumber and avatar fields to UserDetails (string | null) to carry contact and avatar data. Introduce PatchChangeRequestResponse interface for PATCH /change-requests/:id responses, containing id, updatedBy, and updatedOn to model update metadata.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis pull request introduces a user profile modal feature enabling customers to view and edit their profile information, including phone number and time zone. It includes a new React Query mutation hook for patching user details, a new modal component, updated data models with additional user fields, and refactoring to remove redundant user detail dependencies. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as UserProfile Component
participant Modal as UserProfileModal
participant QueryHook as React Query Hook
participant API as Backend API
participant Cache as Query Cache
User->>UI: Click Profile menu item
UI->>Modal: Set open = true
activate Modal
Modal->>QueryHook: useGetUserDetails() fetch
QueryHook->>API: GET /users/me
API-->>QueryHook: User details response
QueryHook->>Cache: Store user data
QueryHook-->>Modal: Return user data
Modal->>Modal: Render form with fetched data
deactivate Modal
User->>Modal: Edit phone number / time zone
Modal->>Modal: Update local state (editable fields)
User->>Modal: Click Save
activate Modal
Modal->>Modal: Compute payload (changed fields only)
Modal->>QueryHook: usePatchUserMe(payload)
QueryHook->>API: PATCH /users/me with payload
API->>API: Update user profile
API-->>QueryHook: 200 OK
QueryHook->>Cache: Invalidate userDetails query
Cache->>API: Refetch user data
API-->>Cache: Updated user details
QueryHook-->>Modal: Success
Modal->>Modal: Show success banner
Modal->>Modal: Close modal
deactivate Modal
Modal-->>UI: onClose callback
UI->>UI: Set open = false
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
apps/customer-portal/webapp/src/components/common/header/UserProfileModal.tsx (3)
70-75: Consider resetting mutation state when modal reopens.The
useEffectresets form fields when the modal opens, but if a previous save attempt failed, the mutation's error state persists. Consider callingpatchUserMe.reset()when the modal opens to clear stale error/success states.♻️ Suggested improvement
useEffect(() => { if (open && userDetails) { setPhoneNumber(userDetails.phoneNumber ?? ""); setTimeZone(userDetails.timeZone ?? ""); + patchUserMe.reset(); } - }, [open, userDetails]); + }, [open, userDetails, patchUserMe]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/common/header/UserProfileModal.tsx` around lines 70 - 75, The useEffect that resets form fields on modal open (the effect depending on open and userDetails and calling setPhoneNumber and setTimeZone) does not clear the mutation state, so stale errors/success from patchUserMe persist; update that effect to call patchUserMe.reset() when open is true (before or after setting the fields) to clear mutation error/success/loading state so each modal open starts fresh.
177-191: Handle case where user's current time zone is not in the options list.If
userDetails.timeZonecontains a value not present inTIME_ZONE_OPTIONS, the Select component will display an empty selection, which may confuse users. Consider adding the current value to the options if it's missing.♻️ Suggested approach
+ const effectiveTimeZoneOptions = userDetails?.timeZone && + !TIME_ZONE_OPTIONS.includes(userDetails.timeZone as typeof TIME_ZONE_OPTIONS[number]) + ? [userDetails.timeZone, ...TIME_ZONE_OPTIONS] + : TIME_ZONE_OPTIONS; + <Select labelId="profile-timezone-label" label="Time Zone" value={timeZone} onChange={handleTimeZoneChange} > - {TIME_ZONE_OPTIONS.map((tz) => ( + {effectiveTimeZoneOptions.map((tz) => ( <MenuItem key={tz} value={tz}> {tz} </MenuItem> ))} </Select>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/common/header/UserProfileModal.tsx` around lines 177 - 191, The Select for time zones can show empty when userDetails.timeZone isn't in TIME_ZONE_OPTIONS; update the rendering logic around TIME_ZONE_OPTIONS and the Select (value={timeZone}, handleTimeZoneChange) to ensure the current user timeZone is included: if userDetails.timeZone (or state timeZone) is not found in TIME_ZONE_OPTIONS, create an extra MenuItem for that value (e.g., prepend/append or derive a combined list used for mapping) so the Select always has an option matching the current value and displays it correctly.
169-175: Consider adding basic phone number validation.The phone number field accepts any input without validation. Consider adding a pattern or validation to guide users toward valid phone number formats.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/common/header/UserProfileModal.tsx` around lines 169 - 175, The Phone Number TextField in UserProfileModal currently accepts any input; add basic validation to enforce a phone format and provide user feedback by updating handlePhoneChange or adding a validatePhone helper used by the TextField. Implement a simple regex (e.g., allow digits, optional +, spaces, dashes, length checks) to set a validation state like isPhoneValid and an error message, pass error={!!isPhoneValidError} and helperText={isPhoneValidError} to the TextField, and prevent submitting invalid phone numbers in the component's submit/save handler (or disable the save button) so invalid phone inputs are rejected or corrected. Ensure references: TextField, phoneNumber, handlePhoneChange, and the component UserProfileModal are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/customer-portal/webapp/src/constants/timeZoneConstants.ts`:
- Around line 18-151: The TIME_ZONE_OPTIONS array contains non-standard or
deprecated identifiers: replace the non-IANA "System (Asia/Colombo)" entry with
a canonical sentinel like "SYSTEM_DEFAULT" (or move to a separate SYSTEM_DEFAULT
constant), rename "Europe/Kiev" to the canonical "Europe/Kyiv", and remove the
invalid/duplicate entries "WSO2/Colombo", "Hongkong" (duplicate of
"Asia/Hong_Kong"), and the ambiguous "PST" (since "America/Los_Angeles" is
present); update the TIME_ZONE_OPTIONS declaration accordingly so only valid
IANA zones (and the explicit SYSTEM_DEFAULT token if needed) remain.
In `@apps/customer-portal/webapp/src/models/responses.ts`:
- Around line 376-381: The interface PatchChangeRequestResponse is currently
unused; either delete the PatchChangeRequestResponse declaration to remove
dead/orphaned code, or if it's intentionally reserved, leave it but add a short
comment above PatchChangeRequestResponse referencing the planned work or ticket
ID and why it is kept (so future reviewers understand it's intentional).
---
Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/common/header/UserProfileModal.tsx`:
- Around line 70-75: The useEffect that resets form fields on modal open (the
effect depending on open and userDetails and calling setPhoneNumber and
setTimeZone) does not clear the mutation state, so stale errors/success from
patchUserMe persist; update that effect to call patchUserMe.reset() when open is
true (before or after setting the fields) to clear mutation
error/success/loading state so each modal open starts fresh.
- Around line 177-191: The Select for time zones can show empty when
userDetails.timeZone isn't in TIME_ZONE_OPTIONS; update the rendering logic
around TIME_ZONE_OPTIONS and the Select (value={timeZone}, handleTimeZoneChange)
to ensure the current user timeZone is included: if userDetails.timeZone (or
state timeZone) is not found in TIME_ZONE_OPTIONS, create an extra MenuItem for
that value (e.g., prepend/append or derive a combined list used for mapping) so
the Select always has an option matching the current value and displays it
correctly.
- Around line 169-175: The Phone Number TextField in UserProfileModal currently
accepts any input; add basic validation to enforce a phone format and provide
user feedback by updating handlePhoneChange or adding a validatePhone helper
used by the TextField. Implement a simple regex (e.g., allow digits, optional +,
spaces, dashes, length checks) to set a validation state like isPhoneValid and
an error message, pass error={!!isPhoneValidError} and
helperText={isPhoneValidError} to the TextField, and prevent submitting invalid
phone numbers in the component's submit/save handler (or disable the save
button) so invalid phone inputs are rejected or corrected. Ensure references:
TextField, phoneNumber, handlePhoneChange, and the component UserProfileModal
are updated accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3453d9ed-d10a-471c-9514-6ab622ce3f31
📒 Files selected for processing (7)
apps/customer-portal/webapp/src/api/usePatchUserMe.tsapps/customer-portal/webapp/src/components/common/header/UserProfile.tsxapps/customer-portal/webapp/src/components/common/header/UserProfileModal.tsxapps/customer-portal/webapp/src/components/security/SecurityReportAnalysis.tsxapps/customer-portal/webapp/src/constants/timeZoneConstants.tsapps/customer-portal/webapp/src/models/requests.tsapps/customer-portal/webapp/src/models/responses.ts
…s-profile-section
af6ca81
into
wso2-open-operations:customer-portal-milestone-1
Description
This pull request introduces a new user profile editing feature for the customer portal, allowing users to update their phone number and time zone from a modal dialog. It also refactors the security report analysis case filtering logic to simplify user-scoped queries and adds supporting API types and constants.
User Profile Editing Feature:
UserProfileModalcomponent that displays the user's profile information and allows editing of phone number and time zone, with PATCH support to/users/meand user feedback banners.usePatchUserMehook to handle PATCH requests for updating the current user's profile, including proper error handling and cache invalidation.UserProfiledropdown menu, enabling users to open the modal from the header. [1] [2] [3]API and Model Enhancements:
PatchUserMeRequest) and added optional fields forphoneNumberandavatarinUserDetails. [1] [2]Security Report Analysis Refactor:
createdByMefilter parameter, removing the need to fetch user details and perform client-side filtering. [1] [2] [3] [4] [5]Other Model Additions:
PatchChangeRequestRequest,PatchChangeRequestResponse). [1] [2]These changes enhance user experience by enabling profile customization and streamline backend filtering for case analysis.
User Profile Editing:
UserProfileModalcomponent for viewing and editing user profile (phone number, time zone) with PATCH support and success/error banners.usePatchUserMehook for PATCH/users/meAPI integration and error handling.UserProfiledropdown with a new "Profile" menu item. [1] [2] [3]TIME_ZONE_OPTIONSconstant for dropdown selection.API & Model Updates:
PatchUserMeRequesttype and extendedUserDetailswithphoneNumberandavatarfields. [1] [2]Security Report Analysis Refactor:
createdByMefilter, removing manual user-based case filtering. [1] [2] [3] [4] [5]Other Model Additions:
PatchChangeRequestRequest,PatchChangeRequestResponse). [1] [2]Summary by CodeRabbit