From 6f36bbc3d5846ddcb54a05b2fc2f864e268f2c97 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Wed, 5 Nov 2025 21:03:39 +0100 Subject: [PATCH] fix(frontend): Fix user field name mapping from backend to frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolved authentication issue where user.id was undefined, causing Epic creation to fail. Root Cause: - Backend /api/auth/login returns UserDto with PascalCase fields (Id, Email, etc.) - Backend /api/auth/me returns JWT claims with camelCase (userId, email, etc.) - Frontend User type expects camelCase fields (id, email, etc.) - Previous code directly assigned backend fields without mapping Changes: - useLogin: Added field mapping to handle both PascalCase and camelCase - useCurrentUser: Map userId -> id and tenantSlug -> tenantName - Both functions now correctly populate user.id for localStorage persistence Impact: - Epic creation now works (user.id is correctly set) - Auth state persists correctly across page reloads - Consistent user object structure throughout frontend 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/hooks/useAuth.ts | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/lib/hooks/useAuth.ts b/lib/hooks/useAuth.ts index 8bf4a95..7fe8dd0 100644 --- a/lib/hooks/useAuth.ts +++ b/lib/hooks/useAuth.ts @@ -30,16 +30,20 @@ export function useLogin() { tokenManager.setAccessToken(data.accessToken); tokenManager.setRefreshToken(data.refreshToken); + // Map backend field names to frontend User type + // Backend returns: { Id, TenantId, Email, FullName, ... } + // Frontend expects: { id, tenantId, email, fullName, ... } + const backendUser = data.user; setUser({ - id: data.user.id, - email: data.user.email, - fullName: data.user.fullName, - tenantId: data.user.tenantId, - tenantName: data.user.tenantName, - role: data.user.role, - isEmailVerified: data.user.isEmailVerified, - createdAt: data.user.createdAt || new Date().toISOString(), - updatedAt: data.user.updatedAt, + id: backendUser.id || backendUser.Id, // Handle both casing + email: backendUser.email || backendUser.Email, + fullName: backendUser.fullName || backendUser.FullName, + tenantId: backendUser.tenantId || backendUser.TenantId, + tenantName: data.tenant?.name || data.tenant?.Name || 'Unknown', + role: data.tenant?.role || backendUser.role || 'TenantMember', + isEmailVerified: backendUser.isEmailVerified ?? backendUser.IsEmailVerified ?? false, + createdAt: backendUser.createdAt || backendUser.CreatedAt || new Date().toISOString(), + updatedAt: backendUser.updatedAt || backendUser.UpdatedAt, }); router.push('/dashboard'); @@ -95,9 +99,24 @@ export function useCurrentUser() { queryKey: ['currentUser'], queryFn: async () => { const { data } = await apiClient.get(API_ENDPOINTS.ME); - setUser(data); + + // Map backend /me response to frontend User type + // Backend returns: { userId, tenantId, email, fullName, tenantSlug, tenantRole, role } + // Frontend expects: { id, tenantId, email, fullName, tenantName, role, isEmailVerified, createdAt } + const mappedUser = { + id: data.userId || data.id, // Backend uses 'userId' + email: data.email, + fullName: data.fullName, + tenantId: data.tenantId, + tenantName: data.tenantSlug || 'Unknown', // Use tenantSlug as tenantName fallback + role: data.tenantRole || data.role || 'TenantMember', + isEmailVerified: true, // Assume verified if token is valid + createdAt: new Date().toISOString(), + }; + + setUser(mappedUser); setLoading(false); - return data; + return mappedUser; }, enabled: !!tokenManager.getAccessToken(), retry: false,