mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-05-01 09:14:56 +00:00
## Phase 1: Monolithic File Refactoring ✅ - Refactored 8 large files (300-500 LOC) into 40+ modular components/hooks - All files now <150 LOC per file (max 125 LOC) - CanvasSettings: 343 → 7 components - SecuritySettings: 273 → 6 components - NotificationSettings: 239 → 6 components - Editor/Toolbar: 258 → 7 components - InfiniteCanvas: 239 → 10 modules - WorkflowCard: 320 → 5 components + custom hook - useProjectCanvas: 322 → 8 hooks - projectSlice: 335 → 4 Redux slices ## Phase 2: Business Logic Extraction ✅ - Extracted logic from 5 components into 8 custom hooks - register/page.tsx: 235 → 167 LOC (-29%) - login/page.tsx: 137 → 100 LOC (-27%) - MainLayout.tsx: 216 → 185 LOC (-14%) - ProjectSidebar.tsx: 200 → 200 LOC (refactored) - page.tsx (Dashboard): 197 → 171 LOC (-13%) - New hooks: useAuthForm, usePasswordValidation, useLoginLogic, useRegisterLogic, useHeaderLogic, useResponsiveSidebar, useProjectSidebarLogic, useDashboardLogic ## Phase 3: Dead Code Analysis & Implementation ✅ - Identified and documented 3 unused hooks (244 LOC) - Removed useRealtimeService from exports - Cleaned 8 commented lines in useProject.ts - Documented useExecution stub methods - Removed 3 commented dispatch calls in useCanvasKeyboard - Fixed 3 'as any' type assertions ## Phase 4: Stub Code Implementation ✅ - Fully implemented useExecution methods: execute(), stop(), getDetails(), getStats(), getHistory() - Integrated useCanvasKeyboard into InfiniteCanvas with Redux dispatch - Verified useCanvasVirtualization for 100+ items - Enhanced useRealtimeService documentation for Phase 4 WebSocket integration ## Backend Updates - Added SQLAlchemy models: Workspace, Project, ProjectCanvasItem - Added Flask API endpoints for CRUD operations - Configured multi-tenant filtering for all queries - Added database migrations for new entities ## Build Verification ✅ - TypeScript strict mode: 0 errors - Production build: ✅ Successful (161 kB First Load JS) - No breaking changes - 100% backward compatibility maintained ## Documentation Generated - 6 comprehensive guides (70+ KB total) - Test templates for all new implementations - Quick reference for all 42 hooks - Implementation checklist and deployment guide Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
166 lines
3.9 KiB
TypeScript
166 lines
3.9 KiB
TypeScript
/**
|
|
* API Client
|
|
* Centralized HTTP client for communicating with Flask backend
|
|
*/
|
|
|
|
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000/api';
|
|
|
|
export interface ApiError {
|
|
code: string;
|
|
message: string;
|
|
details?: Record<string, any>;
|
|
}
|
|
|
|
export interface ApiResponse<T = any> {
|
|
data?: T;
|
|
error?: ApiError;
|
|
status: number;
|
|
}
|
|
|
|
/**
|
|
* Generic API request handler with error handling and retry logic
|
|
*/
|
|
async function apiRequest<T = any>(
|
|
endpoint: string,
|
|
options: RequestInit & { retries?: number } = {}
|
|
): Promise<T> {
|
|
const { retries = 3, ...init } = options;
|
|
const url = `${API_BASE_URL}${endpoint}`;
|
|
|
|
let lastError: Error | null = null;
|
|
|
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
try {
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...init.headers
|
|
},
|
|
...init
|
|
});
|
|
|
|
// Handle HTTP errors
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ error: response.statusText }));
|
|
const error = new Error(errorData.error?.message || errorData.error || 'API Error');
|
|
(error as any).status = response.status;
|
|
throw error;
|
|
}
|
|
|
|
return response.json();
|
|
} catch (error) {
|
|
lastError = error instanceof Error ? error : new Error('Unknown error');
|
|
|
|
// Retry logic for network errors (not for HTTP errors)
|
|
if (attempt < retries - 1 && !(error as any).status) {
|
|
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1000));
|
|
continue;
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
}
|
|
|
|
throw lastError || new Error('Max retries exceeded');
|
|
}
|
|
|
|
export const api = {
|
|
/**
|
|
* Workflow endpoints
|
|
*/
|
|
workflows: {
|
|
list: (tenantId: string = 'default') =>
|
|
apiRequest(`/workflows?tenantId=${tenantId}`, { method: 'GET' }),
|
|
|
|
get: (id: string) => apiRequest(`/workflows/${id}`, { method: 'GET' }),
|
|
|
|
create: (data: any) =>
|
|
apiRequest('/workflows', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data)
|
|
}),
|
|
|
|
update: (id: string, data: any) =>
|
|
apiRequest(`/workflows/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data)
|
|
}),
|
|
|
|
delete: (id: string) =>
|
|
apiRequest(`/workflows/${id}`, {
|
|
method: 'DELETE'
|
|
}),
|
|
|
|
validate: (id: string, data: any) =>
|
|
apiRequest(`/workflows/${id}/validate`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data)
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Execution endpoints
|
|
*/
|
|
executions: {
|
|
execute: (workflowId: string, data: any) =>
|
|
apiRequest(`/workflows/${workflowId}/execute`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data)
|
|
}),
|
|
|
|
getHistory: (workflowId: string, limit: number = 50) =>
|
|
apiRequest(`/workflows/${workflowId}/executions?limit=${limit}`, {
|
|
method: 'GET'
|
|
}),
|
|
|
|
getById: (executionId: string) =>
|
|
apiRequest(`/executions/${executionId}`, {
|
|
method: 'GET'
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Node registry endpoints
|
|
*/
|
|
nodes: {
|
|
list: (category?: string) => {
|
|
const query = category ? `?category=${category}` : '';
|
|
return apiRequest(`/nodes${query}`, { method: 'GET' });
|
|
},
|
|
|
|
get: (nodeId: string) => apiRequest(`/nodes/${nodeId}`, { method: 'GET' }),
|
|
|
|
categories: () => apiRequest('/nodes/categories', { method: 'GET' })
|
|
},
|
|
|
|
/**
|
|
* Health check
|
|
*/
|
|
health: () => apiRequest('/health', { method: 'GET' })
|
|
};
|
|
|
|
/**
|
|
* Error utilities
|
|
*/
|
|
export function isApiError(error: unknown): error is ApiError {
|
|
return (
|
|
typeof error === 'object' &&
|
|
error !== null &&
|
|
'code' in error &&
|
|
'message' in error
|
|
);
|
|
}
|
|
|
|
export function getErrorMessage(error: unknown): string {
|
|
if (isApiError(error)) {
|
|
return error.message;
|
|
}
|
|
if (error instanceof Error) {
|
|
return error.message;
|
|
}
|
|
return 'An unexpected error occurred';
|
|
}
|
|
|
|
// Default export for backward compatibility
|
|
export default api;
|