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>
114 lines
2.9 KiB
TypeScript
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;
|
|
},
|
|
});
|
|
}
|