Files
ColaFlow-Web/lib/hooks/useAuth.ts
Yaojia Wang 2b134b0d6f feat(frontend): Implement Phase 2 - Complete Projects UI with CRUD operations
Implemented comprehensive Projects UI with full CRUD functionality following
modern React best practices and using shadcn/ui components.

Changes:
- Created ProjectForm component with react-hook-form + zod validation
  - Auto-uppercase project key input
  - Comprehensive field validation (name, key, description)
  - Support for both create and edit modes
  - Toast notifications for success/error states

- Enhanced Projects List Page (app/(dashboard)/projects/page.tsx)
  - Beautiful card-based grid layout with hover effects
  - Skeleton loading states for better UX
  - Empty state with call-to-action
  - Project metadata display (key badge, created date)
  - Integrated ProjectForm in Dialog for creation

- Enhanced Project Detail Page (app/(dashboard)/projects/[id]/page.tsx)
  - Comprehensive project information display
  - Edit functionality with dialog form
  - Delete functionality with confirmation AlertDialog
  - Epics preview section with stats
  - Quick actions sidebar (Kanban, Epics)
  - Statistics card (Total/Active/Completed epics)
  - Skeleton loading states
  - Error handling with retry capability

- Added toast notifications (Sonner)
  - Installed and configured sonner package
  - Added Toaster component to root layout
  - Success/error notifications for all CRUD operations

- Installed required dependencies
  - date-fns for date formatting
  - sonner for toast notifications
  - shadcn/ui alert-dialog component

Technical highlights:
- TypeScript with strict type checking
- React Query for data fetching and caching
- Optimistic updates with automatic rollback
- Responsive design (mobile-friendly)
- Accessibility-focused components
- Clean error handling throughout

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 21:26:02 +01:00

112 lines
2.8 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,
});
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;
},
});
}