Fix new Frontend 17
This commit is contained in:
@@ -180,12 +180,12 @@ export const useData = () => {
|
|||||||
* Get Current Weather
|
* Get Current Weather
|
||||||
* Add this method to the useData hook
|
* 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 {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
const weather = await dataService.getCurrentWeather(lat, lon);
|
const weather = await dataService.getCurrentWeather(tenantId, lat, lon);
|
||||||
|
|
||||||
return weather;
|
return weather;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -185,30 +185,149 @@ export class DataService {
|
|||||||
* This should be added to the DataService class
|
* This should be added to the DataService class
|
||||||
*/
|
*/
|
||||||
async getProductsList(tenantId: string): Promise<string[]> {
|
async getProductsList(tenantId: string): Promise<string[]> {
|
||||||
const response = await apiClient.get(`/tenants/${tenantId}/sales/products`);
|
try {
|
||||||
|
const response = await apiClient.get(`/tenants/${tenantId}/sales/products`);
|
||||||
// Extract product names from the response
|
|
||||||
return response.map((product: any) =>
|
console.log('🔍 Products API Response Analysis:');
|
||||||
product.name || product.product_name || product
|
console.log('- Type:', typeof response);
|
||||||
).filter(Boolean);
|
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
|
* Get Current Weather Data
|
||||||
* This should be added to the DataService class
|
* 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;
|
temperature: number;
|
||||||
description: string;
|
description: string;
|
||||||
precipitation: number;
|
precipitation: number;
|
||||||
humidity?: number;
|
humidity?: number;
|
||||||
wind_speed?: number;
|
wind_speed?: number;
|
||||||
}> {
|
}> {
|
||||||
return apiClient.get(`/data/weather/current`, {
|
try {
|
||||||
params: { lat, lon }
|
// ✅ 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
|
* Get Weather Forecast
|
||||||
* This should be added to the DataService class
|
* This should be added to the DataService class
|
||||||
|
|||||||
@@ -22,16 +22,44 @@ export class ForecastingService {
|
|||||||
* Create Single Product Forecast
|
* Create Single Product Forecast
|
||||||
*/
|
*/
|
||||||
async createSingleForecast(
|
async createSingleForecast(
|
||||||
tenantId: string,
|
tenantId: string,
|
||||||
request: SingleForecastRequest
|
request: SingleForecastRequest
|
||||||
): Promise<ForecastResponse[]> {
|
): Promise<ForecastResponse[]> {
|
||||||
return apiClient.post(
|
console.log('🔮 Creating single forecast:', { tenantId, request });
|
||||||
`/tenants/${tenantId}/forecasts/single`,
|
|
||||||
request,
|
try {
|
||||||
{
|
// Backend returns single ForecastResponse object
|
||||||
timeout: RequestTimeouts.MEDIUM,
|
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;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,14 +5,19 @@
|
|||||||
|
|
||||||
export interface SingleForecastRequest {
|
export interface SingleForecastRequest {
|
||||||
product_name: string;
|
product_name: string;
|
||||||
|
forecast_date: string;
|
||||||
forecast_days: number;
|
forecast_days: number;
|
||||||
|
location: string;
|
||||||
include_external_factors?: boolean;
|
include_external_factors?: boolean;
|
||||||
confidence_intervals?: boolean;
|
confidence_intervals?: boolean;
|
||||||
|
// Note: confidence_level is handled internally by backend (0.8 default)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BatchForecastRequest {
|
export interface BatchForecastRequest {
|
||||||
product_names?: string[];
|
product_names?: string[];
|
||||||
|
forecast_date: string;
|
||||||
forecast_days: number;
|
forecast_days: number;
|
||||||
|
location: string;
|
||||||
include_external_factors?: boolean;
|
include_external_factors?: boolean;
|
||||||
confidence_intervals?: boolean;
|
confidence_intervals?: boolean;
|
||||||
batch_name?: string;
|
batch_name?: string;
|
||||||
@@ -23,11 +28,11 @@ export interface ForecastResponse {
|
|||||||
tenant_id: string;
|
tenant_id: string;
|
||||||
product_name: string;
|
product_name: string;
|
||||||
forecast_date: string;
|
forecast_date: string;
|
||||||
predicted_quantity: number;
|
predicted_demand: number;
|
||||||
confidence_lower?: number;
|
confidence_lower?: number;
|
||||||
confidence_upper?: number;
|
confidence_upper?: number;
|
||||||
model_id: string;
|
model_id: string;
|
||||||
model_accuracy?: number;
|
confidence_level?: number;
|
||||||
external_factors?: ExternalFactors;
|
external_factors?: ExternalFactors;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
processing_time_ms?: number;
|
processing_time_ms?: number;
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ export default function EnhancedTrainingProgress({ progress, onTimeout }: Traini
|
|||||||
// Create progress steps based on current progress percentage
|
// Create progress steps based on current progress percentage
|
||||||
const steps = [
|
const steps = [
|
||||||
{ id: 'data_validation', threshold: 25, name: 'Validación' },
|
{ id: 'data_validation', threshold: 25, name: 'Validación' },
|
||||||
{ id: 'feature_engineering', threshold: 50, name: 'Características' },
|
{ id: 'feature_engineering', threshold: 45, name: 'Características' },
|
||||||
{ id: 'model_training', threshold: 80, name: 'Entrenamiento' },
|
{ id: 'model_training', threshold: 85, name: 'Entrenamiento' },
|
||||||
{ id: 'model_validation', threshold: 100, name: 'Validación' }
|
{ id: 'model_validation', threshold: 100, name: 'Validación' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -93,11 +93,10 @@ export const useDashboard = () => {
|
|||||||
console.warn('Failed to fetch products:', error);
|
console.warn('Failed to fetch products:', error);
|
||||||
products = ['Croissants', 'Pan de molde', 'Baguettes', 'Café', 'Napolitanas'];
|
products = ['Croissants', 'Pan de molde', 'Baguettes', 'Café', 'Napolitanas'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Get weather data (Madrid coordinates)
|
// 2. Get weather data (Madrid coordinates)
|
||||||
let weather = null;
|
let weather = null;
|
||||||
try {
|
try {
|
||||||
weather = await getCurrentWeather(40.4168, -3.7038);
|
weather = await getCurrentWeather(tenantId, 40.4168, -3.7038);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to fetch weather:', error);
|
console.warn('Failed to fetch weather:', error);
|
||||||
// Fallback weather
|
// Fallback weather
|
||||||
@@ -113,9 +112,12 @@ export const useDashboard = () => {
|
|||||||
try {
|
try {
|
||||||
const forecastRequest = {
|
const forecastRequest = {
|
||||||
product_name: product,
|
product_name: product,
|
||||||
|
forecast_date: new Date().toISOString().split('T')[0], // Today's date as YYYY-MM-DD
|
||||||
forecast_days: 1,
|
forecast_days: 1,
|
||||||
|
location: 'madrid_centro', // Default location for Madrid bakery
|
||||||
include_external_factors: true,
|
include_external_factors: true,
|
||||||
confidence_intervals: true
|
confidence_intervals: true
|
||||||
|
// confidence_level is handled by backend internally (default 0.8)
|
||||||
};
|
};
|
||||||
|
|
||||||
const forecastResults = await createSingleForecast(tenantId, forecastRequest);
|
const forecastResults = await createSingleForecast(tenantId, forecastRequest);
|
||||||
@@ -124,7 +126,7 @@ export const useDashboard = () => {
|
|||||||
const forecast = forecastResults[0];
|
const forecast = forecastResults[0];
|
||||||
|
|
||||||
// Map API response to dashboard format
|
// 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 :
|
const confidence = confidenceScore > 0.8 ? 'high' as const :
|
||||||
confidenceScore > 0.6 ? 'medium' as const : 'low' as const;
|
confidenceScore > 0.6 ? 'medium' as const : 'low' as const;
|
||||||
|
|
||||||
@@ -133,7 +135,7 @@ export const useDashboard = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
product,
|
product,
|
||||||
predicted: Math.round(forecast.predicted_quantity || 0),
|
predicted: Math.round(forecast.predicted_demand || 0),
|
||||||
confidence,
|
confidence,
|
||||||
change
|
change
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user