Files
ColaFlow-Web/lib/hooks/useAuth.ts
Yaojia Wang ea67d90880 fix(frontend): Fix critical type safety issues from code review
Address all Critical and High Priority issues identified in frontend code review report:

Critical Issues Fixed:
- Created unified logger utility (lib/utils/logger.ts) to replace all console.log statements
- Consolidated User type definitions - removed duplicate from authStore, using single source from types/user.ts
- Eliminated 'any' types in API client - added proper generic types with AxiosRequestConfig
- Fixed SignalR ConnectionManager - replaced 'any' with generic types <T>
- Created API error types (lib/types/errors.ts) with ApiError and getErrorMessage helper
- Fixed IssueCard component - removed all type assertions, created discriminated union types for Kanban items
- Added React.memo to IssueCard for performance optimization
- Added proper ARIA labels and accessibility attributes to IssueCard

High Priority Issues Fixed:
- Fixed hardcoded user ID in CreateProjectDialog - now uses actual user from authStore
- Added useCallback to CreateProjectDialog onSubmit handler
- Fixed error handlers in use-epics.ts - replaced 'any' with ApiError type
- Updated all error handling to use logger and getErrorMessage

Type Safety Improvements:
- Created KanbanItem discriminated union (KanbanEpic | KanbanStory | KanbanTask) with proper type guards
- Added 'never' types to prevent invalid property access
- Fixed User interface to include all required fields (createdAt, updatedAt)
- Maintained backward compatibility with LegacyKanbanBoard for existing code

Files Changed:
- lib/utils/logger.ts - New centralized logging utility
- lib/types/errors.ts - New API error types and helpers
- types/user.ts - Consolidated User type with TenantRole
- types/kanban.ts - New discriminated union types for type-safe Kanban items
- components/features/kanban/IssueCard.tsx - Type-safe with React.memo
- components/features/projects/CreateProjectDialog.tsx - Fixed hardcoded user ID, added useCallback
- lib/api/client.ts - Eliminated 'any', added proper generics
- lib/signalr/ConnectionManager.ts - Replaced console.log, added generics
- lib/hooks/use-epics.ts - Fixed error handler types
- stores/authStore.ts - Removed duplicate User type
- lib/hooks/useAuth.ts - Added createdAt field to User

TypeScript compilation:  All type checks passing (0 errors)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 19:11:48 +01:00

114 lines
2.9 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { apiClient, tokenManager } from '../api/client';
import { API_ENDPOINTS } from '../api/config';
import { useAuthStore } from '@/stores/authStore';
import { useRouter } from 'next/navigation';
interface LoginCredentials {
email: string;
password: string;
tenantSlug: string;
}
interface RegisterTenantData {
email: string;
password: string;
fullName: string;
tenantName: string;
}
export function useLogin() {
const setUser = useAuthStore((state) => state.setUser);
const router = useRouter();
return useMutation({
mutationFn: async (credentials: LoginCredentials) => {
const { data } = await apiClient.post(API_ENDPOINTS.LOGIN, credentials);
return data;
},
onSuccess: (data) => {
tokenManager.setAccessToken(data.accessToken);
tokenManager.setRefreshToken(data.refreshToken);
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,
});
router.push('/dashboard');
},
});
}
export function useRegisterTenant() {
const router = useRouter();
return useMutation({
mutationFn: async (data: RegisterTenantData) => {
const response = await apiClient.post(
API_ENDPOINTS.REGISTER_TENANT,
data
);
return response.data;
},
onSuccess: () => {
router.push('/login?registered=true');
},
});
}
export function useLogout() {
const clearUser = useAuthStore((state) => state.clearUser);
const queryClient = useQueryClient();
const router = useRouter();
return useMutation({
mutationFn: async () => {
try {
await apiClient.post(API_ENDPOINTS.LOGOUT);
} catch {
// Ignore logout errors
}
},
onSuccess: () => {
tokenManager.clearTokens();
clearUser();
queryClient.clear();
router.push('/login');
},
});
}
export function useCurrentUser() {
const setUser = useAuthStore((state) => state.setUser);
const clearUser = useAuthStore((state) => state.clearUser);
const setLoading = useAuthStore((state) => state.setLoading);
return useQuery({
queryKey: ['currentUser'],
queryFn: async () => {
const { data } = await apiClient.get(API_ENDPOINTS.ME);
setUser(data);
setLoading(false);
return data;
},
enabled: !!tokenManager.getAccessToken(),
retry: false,
staleTime: 5 * 60 * 1000, // 5 minutes
refetchOnWindowFocus: false,
throwOnError: () => {
clearUser();
tokenManager.clearTokens();
setLoading(false);
return false;
},
});
}