Enhanced Profile Edit with Robust Validation & Error Handling#36
Enhanced Profile Edit with Robust Validation & Error Handling#36simplicityf wants to merge 4 commits intoPACTO-LAT:developfrom
Conversation
|
@simplicityf is attempting to deploy a commit to the Trustless Work Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds Zod-based profile validation, a new EnhancedAuthService with structured profile update/error codes, per-field real-time validation in ProfileInfo, optimistic save/cancel flow on the profile page, and payment_methods type changes to support multiple bank accounts. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as Browser/UI
participant PI as ProfileInfo
participant Auth as EnhancedAuthService
participant DB as Database
participant Toast as Toasts
User->>UI: Edit fields
UI->>PI: handleFieldChange / blur
PI->>Auth: validateField(field, value)
Auth-->>PI: validation result (ok / error)
PI-->>UI: show field error or ok
User->>UI: Click "Save"
UI->>Auth: validateProfileUpdate(payload)
alt validation fails
Auth-->>UI: VALIDATION_ERROR (details)
UI->>Toast: show validation errors
else validation passes
UI->>UI: apply optimistic UI update
UI->>Auth: updateUserProfile(userId, payload)
Auth->>DB: uniqueness checks + update
alt unique constraint or validation failure
DB-->>Auth: error
Auth-->>UI: ProfileUpdateError (USERNAME/EMAIL/STELLAR_TAKEN or mapped code)
UI->>UI: revert optimistic update
UI->>Toast: show error message
else success
DB-->>Auth: updated user
Auth-->>UI: updated user
UI->>UI: persist UI changes, exit edit mode
UI->>Toast: show success toast
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/app/dashboard/profile/page.tsx (1)
114-131:⚠️ Potential issue | 🟡 MinorAvoid persisting empty bank account placeholders.
Because payment_methods is now part of the payload, any UI placeholder bank_accounts with empty strings will be persisted. Filtering empty entries before validation/update prevents inconsistent JSONB data.🧹 Sanitize payment_methods before save
- const payload = { + const sanitizedPaymentMethods = hydratedUserData.payment_methods + ? { + ...hydratedUserData.payment_methods, + bank_accounts: + hydratedUserData.payment_methods.bank_accounts?.filter( + (account) => + account.bank_iban || + account.bank_name || + account.bank_account_holder + ) ?? [], + } + : undefined; + + const payload = { // Only persist email if user provided a non-empty value ...(hydratedUserData.email && !hydratedUserData.email.endsWith('@wallet.local') ? { email: hydratedUserData.email } : {}), full_name: hydratedUserData.full_name, username: hydratedUserData.username, bio: hydratedUserData.bio, avatar_url: hydratedUserData.avatar_url, phone: hydratedUserData.phone, country: hydratedUserData.country, kyc_status: hydratedUserData.kyc_status, notifications: hydratedUserData.notifications, security: hydratedUserData.security, - payment_methods: hydratedUserData.payment_methods, + payment_methods: sanitizedPaymentMethods, stellar_address: hydratedUserData.stellar_address, } as const;
🤖 Fix all issues with AI agents
In `@apps/web/app/dashboard/profile/page.tsx`:
- Around line 281-285: Replace the unstable index key used in the
validationErrors rendering with a stable identifier: in the
validationErrors.map(...) callback (the mapping that returns the <li> elements
in page.tsx) change key={index} to use the error string (key={error}); if error
strings might not be unique, use a deterministic fallback like
key={`${error}-${index}`} to ensure stability without relying solely on the
array index.
In `@apps/web/lib/schemas/profile-validation.schema.ts`:
- Around line 161-167: The field-level email validator (email: (value: string)
=> { ... }) is missing the .max(255) constraint present in the schema; update
the validator to validate length as well by using
z.string().email().max(255).safeParse(value) so real-time validation matches the
schema and UX stays consistent with other fields like username.
In `@apps/web/lib/services/enhanced-auth.service.ts`:
- Around line 3-4: The validateField parameter is too broad (keyof User) and
allows read-only fields to be considered editable; change the parameter type in
the validateField function to keyof PartialProfileUpdateInput (instead of keyof
User) and update any imports so PartialProfileUpdateInput is imported alongside
validateProfileUpdate from the profile-validation schema; ensure calls/sites
that pass field values use only editable keys and that the validation call
(validateProfileUpdate) continues to run against the narrowed key type so
non-editable fields like reputation_score, total_trades, created_at, and
updated_at are excluded.
🧹 Nitpick comments (2)
apps/web/lib/schemas/profile-validation.schema.ts (1)
153-155: Prefer const arrow with explicit return type for validateProfileUpdate.
This keeps the helper consistent with the TS style guidelines used elsewhere in the repo.♻️ Suggested refactor
-export function validateProfileUpdate(data: unknown) { - return partialProfileUpdateSchema.safeParse(data); -} +export const validateProfileUpdate = ( + data: unknown +): z.SafeParseReturnType<unknown, PartialProfileUpdateInput> => + partialProfileUpdateSchema.safeParse(data);As per coding guidelines, Prefer const arrow functions with explicit type annotations over function declarations.
apps/web/components/profile/ProfileInfo.tsx (1)
3-4: Use clsx for conditional class composition.
This keeps class logic clearer and aligns with the styling guideline.🎨 Refactor conditional classes with clsx
-import { AlertCircle, Camera, CheckCircle, Loader2, User } from 'lucide-react'; -import { useState, useCallback } from 'react'; +import { AlertCircle, Camera, CheckCircle, Loader2, User } from 'lucide-react'; +import clsx from 'clsx'; +import { useState, useCallback } from 'react'; @@ - className={`glass-effect-light ${hasError - ? 'border-red-500 focus:border-red-500 focus:ring-red-500' - : '' - }`} + className={clsx( + 'glass-effect-light', + hasError && 'border-red-500 focus:border-red-500 focus:ring-red-500' + )} @@ - className={`glass-effect-light ${fieldErrors.bio - ? 'border-red-500 focus:border-red-500 focus:ring-red-500' - : '' - }`} + className={clsx( + 'glass-effect-light', + fieldErrors.bio && 'border-red-500 focus:border-red-500 focus:ring-red-500' + )}As per coding guidelines, For conditional classes, prefer clsx or similar helper functions over ternary operators in JSX.
Also applies to: 168-180, 269-272
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@apps/web/app/dashboard/profile/page.tsx`:
- Around line 149-167: previousUserData is being set from hydratedUserData
(which already includes local edits) so rollback never restores the persisted
server state; before applying the optimistic update, capture the last persisted
state (e.g., snapshot the current server-backed state or call the read/fetch
that returns the persisted profile) into previousUserData (not
hydratedUserData), then perform the optimistic setUserData(...edited...), call
updateProfile(...), and on catch use setUserData(previousUserData) to restore;
reference updateProfile, setUserData, hydratedUserData and previousUserData when
making this change.
In `@apps/web/components/profile/ProfileInfo.tsx`:
- Around line 52-68: The switch handling field validation in ProfileInfo.tsx
misses cases for 'full_name' and 'bio', so blur validation never sets
fieldErrors for renderInputField('full_name', ...) and the bio input; add cases
for 'full_name' and 'bio' in the switch (e.g., case 'full_name': result =
fieldValidators.fullName(value); break; case 'bio': result =
fieldValidators.bio(value); break;) and implement corresponding validators on
the fieldValidators object (or alternatively remove/disable the blur error UI
for those two inputs) so the validation behavior matches the UI.
🧹 Nitpick comments (3)
apps/web/components/profile/ProfileInfo.tsx (2)
32-36: Prefer const arrow component with explicit typing.This file uses a function declaration for the component. Consider switching to a const arrow with an explicit return type to align with TS conventions here.
As per coding guidelines: Prefer const arrow functions with explicit type annotations over function declarations.♻️ Suggested refactor
-export function ProfileInfo({ - userData, - isEditing, - onUserDataChange, -}: ProfileInfoProps) { +export const ProfileInfo = ({ + userData, + isEditing, + onUserDataChange, +}: ProfileInfoProps): JSX.Element => { // ... -} +};
176-179: Preferclsx(or existingcn) for conditional classes.Avoid ternary-string concatenation in JSX for conditional classes; it’s harder to read and scale.
As per coding guidelines: For conditional classes, prefer clsx or similar helper functions over ternary operators in JSX.♻️ Suggested refactor
+import clsx from 'clsx'; ... - className={`glass-effect-light ${hasError - ? 'border-red-500 focus:border-red-500 focus:ring-red-500' - : '' - }`} + className={clsx('glass-effect-light', { + 'border-red-500 focus:border-red-500 focus:ring-red-500': hasError, + })} ... - className={`glass-effect-light ${fieldErrors.bio - ? 'border-red-500 focus:border-red-500 focus:ring-red-500' - : '' - }`} + className={clsx('glass-effect-light', { + 'border-red-500 focus:border-red-500 focus:ring-red-500': + !!fieldErrors.bio, + })}Also applies to: 269-272
apps/web/app/dashboard/profile/page.tsx (1)
23-27: Prefer const arrow component with explicit typing.This file declares the page component as a function; switch to a const arrow with an explicit return type for TS consistency.
As per coding guidelines: Prefer const arrow functions with explicit type annotations over function declarations.♻️ Suggested refactor
-export default function EnhancedProfilePage() { +const EnhancedProfilePage = (): JSX.Element => { // ... -} +}; + +export default EnhancedProfilePage;
|
Kindly review the PR |
|
PR is ready to be merged |
📋 Summary
Implements a production-ready profile edit feature with comprehensive Zod validation, intelligent error handling, optimistic UI updates, and excellent user experience.
🎫 Related Issue
Closes #32
What's Changed
Core Features
Screenshots
Files Added
Files Modified
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.