import React, { useState, useMemo } from 'react';
import { Calendar, TrendingUp, AlertTriangle, BarChart3, Download, Settings, Loader } from 'lucide-react';
import { Button, Card, Badge, Select, Table } from '../../../../components/ui';
import type { TableColumn } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
import { DemandChart, ForecastTable, SeasonalityIndicator, AlertsPanel } from '../../../../components/domain/forecasting';
import { useTenantForecasts, useForecastStatistics } from '../../../../api/hooks/forecasting';
import { useIngredients } from '../../../../api/hooks/inventory';
import { useAuthUser } from '../../../../stores/auth.store';
import { ForecastResponse } from '../../../../api/types/forecasting';
const ForecastingPage: React.FC = () => {
const [selectedProduct, setSelectedProduct] = useState('all');
const [forecastPeriod, setForecastPeriod] = useState('7');
const [viewMode, setViewMode] = useState<'chart' | 'table'>('chart');
// Get tenant ID from auth user
const user = useAuthUser();
const tenantId = user?.tenant_id || '';
// Calculate date range based on selected period
const endDate = new Date();
const startDate = new Date();
startDate.setDate(startDate.getDate() - parseInt(forecastPeriod));
// API hooks
const {
data: forecastsData,
isLoading: forecastsLoading,
error: forecastsError
} = useTenantForecasts(tenantId, {
start_date: startDate.toISOString().split('T')[0],
end_date: endDate.toISOString().split('T')[0],
...(selectedProduct !== 'all' && { inventory_product_id: selectedProduct }),
limit: 100
});
const {
data: statisticsData,
isLoading: statisticsLoading,
error: statisticsError
} = useForecastStatistics(tenantId);
// Fetch real inventory data
const {
data: ingredientsData,
isLoading: ingredientsLoading,
error: ingredientsError
} = useIngredients(tenantId);
// Build products list from real inventory data
const products = useMemo(() => {
const productList = [{ id: 'all', name: 'Todos los productos' }];
if (ingredientsData && ingredientsData.length > 0) {
const inventoryProducts = ingredientsData.map(ingredient => ({
id: ingredient.id,
name: ingredient.name,
category: ingredient.category,
}));
productList.push(...inventoryProducts);
}
return productList;
}, [ingredientsData]);
const periods = [
{ value: '7', label: '7 días' },
{ value: '14', label: '14 días' },
{ value: '30', label: '30 días' },
{ value: '90', label: '3 meses' },
];
// Transform forecast data for table display
const transformForecastsForTable = (forecasts: ForecastResponse[]) => {
return forecasts.map(forecast => ({
id: forecast.id,
product: forecast.inventory_product_id, // Will need to map to product name
currentStock: 'N/A', // Not available in forecast data
forecastDemand: forecast.predicted_demand,
recommendedProduction: Math.ceil(forecast.predicted_demand * 1.1), // Simple calculation
confidence: Math.round(forecast.confidence_level * 100),
trend: forecast.predicted_demand > 0 ? 'up' : 'stable',
stockoutRisk: forecast.confidence_level > 0.8 ? 'low' : forecast.confidence_level > 0.6 ? 'medium' : 'high',
}));
};
// Generate alerts based on forecast data
const generateAlertsFromForecasts = (forecasts: ForecastResponse[]) => {
return forecasts
.filter(forecast => forecast.confidence_level < 0.7 || forecast.predicted_demand > 50)
.slice(0, 3) // Limit to 3 alerts
.map((forecast, index) => ({
id: (index + 1).toString(),
type: forecast.confidence_level < 0.7 ? 'low-confidence' : 'high-demand',
product: forecast.inventory_product_id,
message: forecast.confidence_level < 0.7
? `Baja confianza en predicción (${Math.round(forecast.confidence_level * 100)}%)`
: `Alta demanda prevista: ${forecast.predicted_demand} unidades`,
severity: forecast.confidence_level < 0.5 ? 'high' : 'medium',
recommendation: forecast.confidence_level < 0.7
? 'Revisar datos históricos y factores externos'
: `Considerar aumentar producción a ${Math.ceil(forecast.predicted_demand * 1.2)} unidades`
}));
};
// Extract weather data from first forecast (if available)
const getWeatherImpact = (forecasts: ForecastResponse[]) => {
const firstForecast = forecasts?.[0];
if (!firstForecast) return null;
return {
today: firstForecast.weather_description || 'N/A',
temperature: firstForecast.weather_temperature || 0,
demandFactor: 1.0, // Could be calculated based on weather
affectedCategories: [], // Could be derived from business logic
};
};
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'up':
return
Precisión del Modelo
{statisticsData?.accuracy_metrics?.average_accuracy ? Math.round(statisticsData.accuracy_metrics.average_accuracy * 100) : averageConfidence}%
Demanda Prevista
{Math.round(totalDemand)}
próximos {forecastPeriod} días
Tendencia
+{statisticsData?.accuracy_metrics?.accuracy_trend ? Math.round(statisticsData.accuracy_metrics.accuracy_trend * 100) : 5}%
vs período anterior
Total Predicciones
{statisticsData?.total_forecasts || forecasts.length}
generadas
Categorías afectadas:
No se encontraron predicciones para el período seleccionado. Prueba ajustando los filtros o genera nuevas predicciones.