diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 00000000..a8309c9b --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1,15 @@ +# API Configuration +VITE_API_URL=http://localhost:8000/api/v1 +VITE_API_TIMEOUT=30000 +VITE_API_RETRIES=3 +VITE_API_RETRY_DELAY=1000 +VITE_API_LOGGING=true +VITE_API_CACHING=true +VITE_API_CACHE_TIMEOUT=300000 + +# Feature Flags +VITE_ENABLE_WEBSOCKETS=false +VITE_ENABLE_OFFLINE=false +VITE_ENABLE_OPTIMISTIC_UPDATES=true +VITE_ENABLE_DEDUPLICATION=true +VITE_ENABLE_METRICS=false \ No newline at end of file diff --git a/frontend/src/api/client/config.ts b/frontend/src/api/client/config.ts index 40a2f72f..0b4b9c8f 100644 --- a/frontend/src/api/client/config.ts +++ b/frontend/src/api/client/config.ts @@ -25,17 +25,18 @@ export interface ServiceEndpoints { // Environment-based configuration const getEnvironmentConfig = (): ApiConfig => { - const isProduction = process.env.NODE_ENV === 'production'; - const isDevelopment = process.env.NODE_ENV === 'development'; + // Use import.meta.env instead of process.env for Vite + const isDevelopment = import.meta.env.DEV; + const isProduction = import.meta.env.PROD; return { - baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1', - timeout: parseInt(process.env.NEXT_PUBLIC_API_TIMEOUT || '30000'), - retries: parseInt(process.env.NEXT_PUBLIC_API_RETRIES || '3'), - retryDelay: parseInt(process.env.NEXT_PUBLIC_API_RETRY_DELAY || '1000'), - enableLogging: isDevelopment || process.env.NEXT_PUBLIC_API_LOGGING === 'true', - enableCaching: process.env.NEXT_PUBLIC_API_CACHING !== 'false', - cacheTimeout: parseInt(process.env.NEXT_PUBLIC_API_CACHE_TIMEOUT || '300000'), // 5 minutes + baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000/api/v1', + timeout: parseInt(import.meta.env.VITE_API_TIMEOUT || '30000'), + retries: parseInt(import.meta.env.VITE_API_RETRIES || '3'), + retryDelay: parseInt(import.meta.env.VITE_API_RETRY_DELAY || '1000'), + enableLogging: isDevelopment || import.meta.env.VITE_API_LOGGING === 'true', + enableCaching: import.meta.env.VITE_API_CACHING !== 'false', + cacheTimeout: parseInt(import.meta.env.VITE_API_CACHE_TIMEOUT || '300000'), // 5 minutes }; }; @@ -124,7 +125,6 @@ export const ApiVersion = { CURRENT: 'v1', } as const; -// Feature flags for API behavior export interface FeatureFlags { enableWebSockets: boolean; enableOfflineMode: boolean; @@ -134,9 +134,9 @@ export interface FeatureFlags { } export const featureFlags: FeatureFlags = { - enableWebSockets: process.env.NEXT_PUBLIC_ENABLE_WEBSOCKETS === 'true', - enableOfflineMode: process.env.NEXT_PUBLIC_ENABLE_OFFLINE === 'true', - enableOptimisticUpdates: process.env.NEXT_PUBLIC_ENABLE_OPTIMISTIC_UPDATES !== 'false', - enableRequestDeduplication: process.env.NEXT_PUBLIC_ENABLE_DEDUPLICATION !== 'false', - enableMetrics: process.env.NEXT_PUBLIC_ENABLE_METRICS === 'true', + enableWebSockets: import.meta.env.VITE_ENABLE_WEBSOCKETS === 'true', + enableOfflineMode: import.meta.env.VITE_ENABLE_OFFLINE === 'true', + enableOptimisticUpdates: import.meta.env.VITE_ENABLE_OPTIMISTIC_UPDATES !== 'false', + enableRequestDeduplication: import.meta.env.VITE_ENABLE_DEDUPLICATION !== 'false', + enableMetrics: import.meta.env.VITE_ENABLE_METRICS === 'true', }; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index f9f83b35..6f732565 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -4,15 +4,10 @@ * Central entry point for all API functionality */ -// Setup interceptors on import -import { setupInterceptors } from './client/interceptors'; -setupInterceptors(); - -// Export main API client and services +// Export main API client first export { apiClient } from './client'; -export { api } from './services'; -// Export all services individually +// Export all services export { authService, tenantService, @@ -20,7 +15,7 @@ export { trainingService, forecastingService, notificationService, - healthService, + api } from './services'; // Export all hooks @@ -35,37 +30,12 @@ export { useApiHooks, } from './hooks'; -// Export WebSocket functionality -export { - WebSocketManager, - useWebSocket, - useTrainingWebSocket, - useForecastWebSocket, -} from './websocket'; - -// Export utilities -export { - ApiErrorHandler, - ResponseProcessor, - RequestValidator, - DataTransformer, -} from './utils'; - // Export types export * from './types'; -// Export interceptors for manual control -export { - AuthInterceptor, - LoggingInterceptor, - TenantInterceptor, - ErrorRecoveryInterceptor, - PerformanceInterceptor, - setupInterceptors, -} from './client/interceptors'; - // Export configuration export { apiConfig, serviceEndpoints, featureFlags } from './client/config'; -// Default export for convenience -export default api; \ No newline at end of file +// Setup interceptors on import (move to end to avoid circular deps) +import { setupInterceptors } from './client/interceptors'; +setupInterceptors(); \ No newline at end of file diff --git a/frontend/src/api/services/index.ts b/frontend/src/api/services/index.ts index f5f3cc84..820f69fe 100644 --- a/frontend/src/api/services/index.ts +++ b/frontend/src/api/services/index.ts @@ -4,13 +4,24 @@ * Central export point for all API services */ -// Import all services -export { AuthService, authService } from './auth.service'; -export { TenantService, tenantService } from './tenant.service'; -export { DataService, dataService } from './data.service'; -export { TrainingService, trainingService } from './training.service'; -export { ForecastingService, forecastingService } from './forecasting.service'; -export { NotificationService, notificationService } from './notification.service'; +// Import and export individual services +import { AuthService } from './auth.service'; +import { TenantService } from './tenant.service'; +import { DataService } from './data.service'; +import { TrainingService } from './training.service'; +import { ForecastingService } from './forecasting.service'; +import { NotificationService } from './notification.service'; + +// Create service instances +export const authService = new AuthService(); +export const tenantService = new TenantService(); +export const dataService = new DataService(); +export const trainingService = new TrainingService(); +export const forecastingService = new ForecastingService(); +export const notificationService = new NotificationService(); + +// Export the classes as well +export { AuthService, TenantService, DataService, TrainingService, ForecastingService, NotificationService }; // Import base client export { apiClient } from '../client'; @@ -26,7 +37,6 @@ export const api = { training: trainingService, forecasting: forecastingService, notification: notificationService, - client: apiClient, } as const; // Service status checking diff --git a/frontend/src/pages/auth/RegisterPage.tsx b/frontend/src/pages/auth/RegisterPage.tsx index dc362679..e849d4e4 100644 --- a/frontend/src/pages/auth/RegisterPage.tsx +++ b/frontend/src/pages/auth/RegisterPage.tsx @@ -1,6 +1,10 @@ +import React, { useState } from 'react'; import { Eye, EyeOff, Loader2, Check } from 'lucide-react'; import toast from 'react-hot-toast'; +import { useAuth } from '../../api/hooks/useAuth'; +import type { RegisterRequest } from '../../api/types'; + interface RegisterPageProps { onLogin: (user: any, token: string) => void; onNavigateToLogin: () => void; @@ -15,6 +19,8 @@ interface RegisterForm { } const RegisterPage: React.FC = ({ onLogin, onNavigateToLogin }) => { + const { register, isLoading, error } = useAuth(); + const [formData, setFormData] = useState({ fullName: '', email: '', @@ -25,7 +31,6 @@ const RegisterPage: React.FC = ({ onLogin, onNavigateToLogin const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); - const [isLoading, setIsLoading] = useState(false); const [errors, setErrors] = useState>({}); const validateForm = (): boolean => { @@ -47,13 +52,9 @@ const RegisterPage: React.FC = ({ onLogin, onNavigateToLogin newErrors.password = 'La contraseña es obligatoria'; } else if (formData.password.length < 8) { newErrors.password = 'La contraseña debe tener al menos 8 caracteres'; - } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(formData.password)) { - newErrors.password = 'La contraseña debe incluir mayúsculas, minúsculas y números'; } - if (!formData.confirmPassword) { - newErrors.confirmPassword = 'Confirma tu contraseña'; - } else if (formData.password !== formData.confirmPassword) { + if (formData.password !== formData.confirmPassword) { newErrors.confirmPassword = 'Las contraseñas no coinciden'; } @@ -70,54 +71,30 @@ const RegisterPage: React.FC = ({ onLogin, onNavigateToLogin if (!validateForm()) return; - setIsLoading(true); - try { - const response = await fetch('/api/v1/auth/register', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - full_name: formData.fullName, - email: formData.email, - password: formData.password, - role: 'admin' // Default role for bakery owners - }), - }); + const registerData: RegisterRequest = { + email: formData.email, + password: formData.password, + full_name: formData.fullName, + role: 'user' // Default role + }; - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.message || 'Error al crear la cuenta'); + await register(registerData); + + toast.success('¡Registro exitoso! Bienvenido a PanIA'); + + // The useAuth hook handles auto-login after registration + // Get the user data from localStorage since useAuth auto-logs in + const userData = localStorage.getItem('user_data'); + const token = localStorage.getItem('auth_token'); + + if (userData && token) { + onLogin(JSON.parse(userData), token); } - - // Auto-login after successful registration - const loginResponse = await fetch('/api/v1/auth/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email: formData.email, - password: formData.password, - }), - }); - - const loginData = await loginResponse.json(); - - if (!loginResponse.ok) { - throw new Error('Cuenta creada, pero error al iniciar sesión'); - } - - toast.success('¡Cuenta creada exitosamente! Bienvenido a PanIA'); - onLogin(loginData.user, loginData.access_token); - - } catch (error: any) { + + } catch (error) { console.error('Registration error:', error); - toast.error(error.message || 'Error al crear la cuenta'); - } finally { - setIsLoading(false); + toast.error(error instanceof Error ? error.message : 'Error en el registro'); } }; @@ -372,7 +349,7 @@ const RegisterPage: React.FC = ({ onLogin, onNavigateToLogin

{errors.acceptTerms}

)} - + {/* Submit Button */}