diff --git a/frontend/src/api/hooks/useData.ts b/frontend/src/api/hooks/useData.ts index 0e2b11d5..6c8c1572 100644 --- a/frontend/src/api/hooks/useData.ts +++ b/frontend/src/api/hooks/useData.ts @@ -180,12 +180,12 @@ export const useData = () => { * Get Current Weather * Add this method to the useData hook */ - const getCurrentWeather = useCallback(async (lat: number, lon: number) => { + const getCurrentWeather = useCallback(async (tenantId: string, lat: number, lon: number) => { try { setIsLoading(true); setError(null); - const weather = await dataService.getCurrentWeather(lat, lon); + const weather = await dataService.getCurrentWeather(tenantId, lat, lon); return weather; } catch (error) { diff --git a/frontend/src/api/services/data.service.ts b/frontend/src/api/services/data.service.ts index 67d934a8..72cc8ba9 100644 --- a/frontend/src/api/services/data.service.ts +++ b/frontend/src/api/services/data.service.ts @@ -185,30 +185,149 @@ export class DataService { * This should be added to the DataService class */ async getProductsList(tenantId: string): Promise { - const response = await apiClient.get(`/tenants/${tenantId}/sales/products`); - - // Extract product names from the response - return response.map((product: any) => - product.name || product.product_name || product - ).filter(Boolean); + try { + const response = await apiClient.get(`/tenants/${tenantId}/sales/products`); + + console.log('🔍 Products API Response Analysis:'); + console.log('- Type:', typeof response); + console.log('- Is Array:', Array.isArray(response)); + console.log('- Keys:', Object.keys(response || {})); + console.log('- Response:', response); + + let productsArray: any[] = []; + + // ✅ FIX: Handle different response formats + if (Array.isArray(response)) { + // Standard array response + productsArray = response; + console.log('✅ Response is already an array'); + } else if (response && typeof response === 'object') { + // Object with numeric keys - convert to array + const keys = Object.keys(response); + if (keys.length > 0 && keys.every(key => !isNaN(Number(key)))) { + // Object has numeric keys like {0: {...}, 1: {...}} + productsArray = Object.values(response); + console.log('✅ Converted object with numeric keys to array'); + } else { + console.warn('⚠️ Response is object but not with numeric keys:', response); + return []; + } + } else { + console.warn('⚠️ Response is not array or object:', response); + return []; + } + + console.log('📦 Products array:', productsArray); + + // Extract product names from the array + const productNames = productsArray + .map((product: any) => { + if (typeof product === 'string') { + return product; + } + if (product && typeof product === 'object') { + return product.product_name || + product.name || + product.productName || + null; + } + return null; + }) + .filter(Boolean) // Remove null/undefined values + .filter((name: string) => name.trim().length > 0); // Remove empty strings + + console.log('📋 Extracted product names:', productNames); + + if (productNames.length === 0) { + console.warn('⚠️ No valid product names extracted from response'); + } + + return productNames; + + } catch (error) { + console.error('❌ Failed to fetch products list:', error); + + // Return fallback products for Madrid bakery + return [ + 'Croissants', + 'Pan de molde', + 'Baguettes', + 'Café', + 'Napolitanas', + 'Pan integral', + 'Magdalenas', + 'Churros' + ]; + } } + /** * Get Current Weather Data * This should be added to the DataService class */ - async getCurrentWeather(lat: number, lon: number): Promise<{ + async getCurrentWeather( + tenantId: string, + lat: number, + lon: number + ): Promise<{ temperature: number; description: string; precipitation: number; humidity?: number; wind_speed?: number; }> { - return apiClient.get(`/data/weather/current`, { - params: { lat, lon } - }); + try { + // ✅ FIX 1: Correct endpoint path with tenant ID + const endpoint = `/tenants/${tenantId}/weather/current`; + + // ✅ FIX 2: Correct parameter names (latitude/longitude, not lat/lon) + const response = await apiClient.get(endpoint, { + params: { + latitude: lat, // Backend expects 'latitude' + longitude: lon // Backend expects 'longitude' + } + }); + + // ✅ FIX 3: Handle the actual backend response structure + // Backend returns WeatherDataResponse: + // { + // "date": "2025-08-04T12:00:00Z", + // "temperature": 25.5, + // "precipitation": 0.0, + // "humidity": 65.0, + // "wind_speed": 10.2, + // "pressure": 1013.2, + // "description": "Partly cloudy", + // "source": "aemet" + // } + + console.log('Weather API response:', response); + + // Map backend response to expected frontend format + return { + temperature: response.temperature || 18, + description: response.description || 'Parcialmente nublado', + precipitation: response.precipitation || 0, + humidity: response.humidity || 65, + wind_speed: response.wind_speed || 10 + }; + + } catch (error) { + console.error('Failed to fetch weather from backend:', error); + + // Fallback weather for Madrid + return { + temperature: 18, + description: 'Parcialmente nublado', + precipitation: 0, + humidity: 65, + wind_speed: 10 + }; + } } + /** * Get Weather Forecast * This should be added to the DataService class diff --git a/frontend/src/api/services/forecasting.service.ts b/frontend/src/api/services/forecasting.service.ts index 15dd1a2c..8ac55f6a 100644 --- a/frontend/src/api/services/forecasting.service.ts +++ b/frontend/src/api/services/forecasting.service.ts @@ -22,16 +22,44 @@ export class ForecastingService { * Create Single Product Forecast */ async createSingleForecast( - tenantId: string, - request: SingleForecastRequest - ): Promise { - return apiClient.post( - `/tenants/${tenantId}/forecasts/single`, - request, - { - timeout: RequestTimeouts.MEDIUM, + tenantId: string, + request: SingleForecastRequest + ): Promise { + console.log('🔮 Creating single forecast:', { tenantId, request }); + + try { + // Backend returns single ForecastResponse object + const response = await apiClient.post( + `/tenants/${tenantId}/forecasts/single`, + request, + { + timeout: RequestTimeouts.MEDIUM, + } + ); + + console.log('🔮 Forecast API Response:', response); + console.log('- Type:', typeof response); + console.log('- Is Array:', Array.isArray(response)); + + // ✅ FIX: Convert single response to array + if (response && typeof response === 'object' && !Array.isArray(response)) { + // Single forecast response - wrap in array + const forecastArray = [response as ForecastResponse]; + console.log('✅ Converted single forecast to array:', forecastArray); + return forecastArray; + } else if (Array.isArray(response)) { + // Already an array (unexpected but handle gracefully) + console.log('✅ Response is already an array:', response); + return response; + } else { + console.error('❌ Unexpected response format:', response); + throw new Error('Invalid forecast response format'); + } + + } catch (error) { + console.error('❌ Forecast API Error:', error); + throw error; } - ); } /** diff --git a/frontend/src/api/types/forecasting.ts b/frontend/src/api/types/forecasting.ts index 6115ed5a..72c80e5c 100644 --- a/frontend/src/api/types/forecasting.ts +++ b/frontend/src/api/types/forecasting.ts @@ -5,14 +5,19 @@ export interface SingleForecastRequest { product_name: string; + forecast_date: string; forecast_days: number; + location: string; include_external_factors?: boolean; confidence_intervals?: boolean; + // Note: confidence_level is handled internally by backend (0.8 default) } export interface BatchForecastRequest { product_names?: string[]; + forecast_date: string; forecast_days: number; + location: string; include_external_factors?: boolean; confidence_intervals?: boolean; batch_name?: string; @@ -23,11 +28,11 @@ export interface ForecastResponse { tenant_id: string; product_name: string; forecast_date: string; - predicted_quantity: number; + predicted_demand: number; confidence_lower?: number; confidence_upper?: number; model_id: string; - model_accuracy?: number; + confidence_level?: number; external_factors?: ExternalFactors; created_at: string; processing_time_ms?: number; diff --git a/frontend/src/components/EnhancedTrainingProgress.tsx b/frontend/src/components/EnhancedTrainingProgress.tsx index 2a76fac1..bf67416d 100644 --- a/frontend/src/components/EnhancedTrainingProgress.tsx +++ b/frontend/src/components/EnhancedTrainingProgress.tsx @@ -107,8 +107,8 @@ export default function EnhancedTrainingProgress({ progress, onTimeout }: Traini // Create progress steps based on current progress percentage const steps = [ { id: 'data_validation', threshold: 25, name: 'Validación' }, - { id: 'feature_engineering', threshold: 50, name: 'Características' }, - { id: 'model_training', threshold: 80, name: 'Entrenamiento' }, + { id: 'feature_engineering', threshold: 45, name: 'Características' }, + { id: 'model_training', threshold: 85, name: 'Entrenamiento' }, { id: 'model_validation', threshold: 100, name: 'Validación' } ]; diff --git a/frontend/src/hooks/useDashboard.ts b/frontend/src/hooks/useDashboard.ts index e9d0d0fc..5f2b82e1 100644 --- a/frontend/src/hooks/useDashboard.ts +++ b/frontend/src/hooks/useDashboard.ts @@ -93,11 +93,10 @@ export const useDashboard = () => { console.warn('Failed to fetch products:', error); products = ['Croissants', 'Pan de molde', 'Baguettes', 'Café', 'Napolitanas']; } - // 2. Get weather data (Madrid coordinates) let weather = null; try { - weather = await getCurrentWeather(40.4168, -3.7038); + weather = await getCurrentWeather(tenantId, 40.4168, -3.7038); } catch (error) { console.warn('Failed to fetch weather:', error); // Fallback weather @@ -113,9 +112,12 @@ export const useDashboard = () => { try { const forecastRequest = { product_name: product, + forecast_date: new Date().toISOString().split('T')[0], // Today's date as YYYY-MM-DD forecast_days: 1, + location: 'madrid_centro', // Default location for Madrid bakery include_external_factors: true, confidence_intervals: true + // confidence_level is handled by backend internally (default 0.8) }; const forecastResults = await createSingleForecast(tenantId, forecastRequest); @@ -124,7 +126,7 @@ export const useDashboard = () => { const forecast = forecastResults[0]; // Map API response to dashboard format - const confidenceScore = forecast.model_accuracy || 0.8; + const confidenceScore = forecast.confidence_level || 0.8; const confidence = confidenceScore > 0.8 ? 'high' as const : confidenceScore > 0.6 ? 'medium' as const : 'low' as const; @@ -133,7 +135,7 @@ export const useDashboard = () => { return { product, - predicted: Math.round(forecast.predicted_quantity || 0), + predicted: Math.round(forecast.predicted_demand || 0), confidence, change };