Some checks failed
Build and Deploy to k3s / build-and-deploy (push) Failing after 1m31s
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>
82 lines
2.1 KiB
TypeScript
82 lines
2.1 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { apiClient } from '@/lib/api-client';
|
|
import { Portfolio } from '@/lib/types';
|
|
|
|
export function usePortfolios() {
|
|
const [portfolios, setPortfolios] = useState<Portfolio[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetchPortfolios = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
const response = await apiClient.get<Portfolio[]>('/portfolios');
|
|
|
|
if (response.success && response.data) {
|
|
setPortfolios(response.data);
|
|
}
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to fetch portfolios');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchPortfolios();
|
|
}, []);
|
|
|
|
const createPortfolio = async (name: string, domain: string) => {
|
|
try {
|
|
const response = await apiClient.post<Portfolio>('/portfolios', { name, domain });
|
|
|
|
if (response.success && response.data) {
|
|
setPortfolios([...portfolios, response.data]);
|
|
return response.data;
|
|
}
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
const uploadPortfolio = async (id: number, file: File) => {
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
const response = await apiClient.upload<Portfolio>(`/portfolios/${id}/upload`, formData);
|
|
|
|
if (response.success && response.data) {
|
|
setPortfolios(portfolios.map(p => p.id === id ? response.data! : p));
|
|
return response.data;
|
|
}
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
const deployPortfolio = async (id: number) => {
|
|
try {
|
|
const response = await apiClient.post<Portfolio>(`/portfolios/${id}/deploy`);
|
|
|
|
if (response.success && response.data) {
|
|
setPortfolios(portfolios.map(p => p.id === id ? response.data! : p));
|
|
return response.data;
|
|
}
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
return {
|
|
portfolios,
|
|
isLoading,
|
|
error,
|
|
fetchPortfolios,
|
|
createPortfolio,
|
|
uploadPortfolio,
|
|
deployPortfolio,
|
|
};
|
|
}
|