fix(frontend): Fix user field name mapping from backend to frontend

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 <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-05 21:03:39 +01:00
parent be69325797
commit 6f36bbc3d5

View File

@@ -30,16 +30,20 @@ export function useLogin() {
tokenManager.setAccessToken(data.accessToken); tokenManager.setAccessToken(data.accessToken);
tokenManager.setRefreshToken(data.refreshToken); 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({ setUser({
id: data.user.id, id: backendUser.id || backendUser.Id, // Handle both casing
email: data.user.email, email: backendUser.email || backendUser.Email,
fullName: data.user.fullName, fullName: backendUser.fullName || backendUser.FullName,
tenantId: data.user.tenantId, tenantId: backendUser.tenantId || backendUser.TenantId,
tenantName: data.user.tenantName, tenantName: data.tenant?.name || data.tenant?.Name || 'Unknown',
role: data.user.role, role: data.tenant?.role || backendUser.role || 'TenantMember',
isEmailVerified: data.user.isEmailVerified, isEmailVerified: backendUser.isEmailVerified ?? backendUser.IsEmailVerified ?? false,
createdAt: data.user.createdAt || new Date().toISOString(), createdAt: backendUser.createdAt || backendUser.CreatedAt || new Date().toISOString(),
updatedAt: data.user.updatedAt, updatedAt: backendUser.updatedAt || backendUser.UpdatedAt,
}); });
router.push('/dashboard'); router.push('/dashboard');
@@ -95,9 +99,24 @@ export function useCurrentUser() {
queryKey: ['currentUser'], queryKey: ['currentUser'],
queryFn: async () => { queryFn: async () => {
const { data } = await apiClient.get(API_ENDPOINTS.ME); 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); setLoading(false);
return data; return mappedUser;
}, },
enabled: !!tokenManager.getAccessToken(), enabled: !!tokenManager.getAccessToken(),
retry: false, retry: false,