hosting-frontend/components/auth/auth-provider.tsx
Alexis Bruneteau b83c7a7d6d
Some checks failed
Build and Deploy to k3s / build-and-deploy (push) Failing after 1m31s
feat(migration): migrate from Angular 20 to Next.js 15
Complete framework migration from Angular to Next.js with full feature parity.

## What Changed
- Migrated from Angular 20 to Next.js 15 with App Router
- Replaced Angular components with React functional components
- Implemented React Context API for state management (replacing RxJS)
- Integrated React Hook Form for form handling
- Added shadcn/ui component library
- Configured Tailwind CSS 4.1 with @tailwindcss/postcss
- Implemented JWT authentication with middleware protection

## Core Features Implemented
-  User registration and login with validation
-  JWT token authentication with localStorage
-  Protected dashboard route with middleware
-  Portfolio listing with status indicators
-  Create portfolio functionality
-  ZIP file upload with progress tracking
-  Portfolio deployment
-  Responsive design with Tailwind CSS

## Technical Stack
- Framework: Next.js 15 (App Router)
- Language: TypeScript 5.8
- Styling: Tailwind CSS 4.1
- UI Components: shadcn/ui + Lucide icons
- State: React Context API + hooks
- Forms: React Hook Form
- API Client: Native fetch with custom wrapper

## File Structure
- /app - Next.js pages (landing, login, register, dashboard)
- /components - React components (ui primitives, auth provider)
- /lib - API client, types, utilities
- /hooks - Custom hooks (useAuth, usePortfolios)
- /middleware.ts - Route protection
- /angular-backup - Preserved Angular source code

## API Compatibility
- All backend endpoints remain unchanged
- JWT Bearer token authentication preserved
- API response format maintained

## Build Output
- Production build: 7 routes compiled successfully
- Bundle size: ~102kB shared JS (optimized)
- First Load JS: 103-126kB per route

## Documentation
- Updated README.md with Next.js setup guide
- Created OpenSpec proposal in openspec/changes/migrate-to-nextjs-launchui/
- Added project context in openspec/project.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 00:34:43 +02:00

111 lines
3.1 KiB
TypeScript

'use client';
import React, { createContext, useState, useEffect, ReactNode } from 'react';
import { useRouter } from 'next/navigation';
import { apiClient } from '@/lib/api-client';
import { User, LoginFormData, RegisterFormData, LoginResponse, RegisterResponse } from '@/lib/types';
interface AuthContextType {
user: User | null;
token: string | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (credentials: LoginFormData) => Promise<void>;
register: (data: RegisterFormData) => Promise<void>;
logout: () => void;
}
export const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
const router = useRouter();
useEffect(() => {
// Check for existing token on mount
const storedToken = localStorage.getItem('auth_token');
if (storedToken) {
setToken(storedToken);
// Optionally fetch user data
fetchUser(storedToken);
} else {
setIsLoading(false);
}
}, []);
const fetchUser = async (authToken: string) => {
try {
const response = await apiClient.get<User>('/auth/user');
if (response.success && response.data) {
setUser(response.data);
}
} catch (error) {
console.error('Failed to fetch user:', error);
// Token might be invalid
localStorage.removeItem('auth_token');
setToken(null);
} finally {
setIsLoading(false);
}
};
const login = async (credentials: LoginFormData) => {
try {
const response = await apiClient.post<LoginResponse>('/auth/login', credentials);
if (response.success && response.data) {
const { token: authToken, user: userData } = response.data;
localStorage.setItem('auth_token', authToken);
setToken(authToken);
setUser(userData);
router.push('/dashboard');
} else {
throw new Error(response.message || 'Login failed');
}
} catch (error) {
console.error('Login error:', error);
throw error;
}
};
const register = async (data: RegisterFormData) => {
try {
const response = await apiClient.post<RegisterResponse>('/auth/register', data);
if (response.success && response.data) {
const { token: authToken, user: userData } = response.data;
localStorage.setItem('auth_token', authToken);
setToken(authToken);
setUser(userData);
router.push('/dashboard');
} else {
throw new Error(response.message || 'Registration failed');
}
} catch (error) {
console.error('Registration error:', error);
throw error;
}
};
const logout = () => {
localStorage.removeItem('auth_token');
setToken(null);
setUser(null);
router.push('/login');
};
const value: AuthContextType = {
user,
token,
isAuthenticated: !!token,
isLoading,
login,
register,
logout,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}