ADD new frontend

This commit is contained in:
Urtzi Alfaro
2025-08-28 10:41:04 +02:00
parent 9c247a5f99
commit 0fd273cfce
492 changed files with 114979 additions and 1632 deletions

View File

@@ -0,0 +1,385 @@
import React, { useState } from 'react';
import { Calendar, TrendingUp, AlertTriangle, BarChart3, Download, Settings } from 'lucide-react';
import { Button, Card, Badge, Select } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
import { DemandChart, ForecastTable, SeasonalityIndicator, AlertsPanel } from '../../../../components/domain/forecasting';
const ForecastingPage: React.FC = () => {
const [selectedProduct, setSelectedProduct] = useState('all');
const [forecastPeriod, setForecastPeriod] = useState('7');
const [viewMode, setViewMode] = useState<'chart' | 'table'>('chart');
const forecastData = {
accuracy: 92,
totalDemand: 1247,
growthTrend: 8.5,
seasonalityFactor: 1.15,
};
const products = [
{ id: 'all', name: 'Todos los productos' },
{ id: 'bread', name: 'Panes' },
{ id: 'pastry', name: 'Bollería' },
{ id: 'cake', name: 'Tartas' },
];
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' },
];
const mockForecasts = [
{
id: '1',
product: 'Pan de Molde Integral',
currentStock: 25,
forecastDemand: 45,
recommendedProduction: 50,
confidence: 95,
trend: 'up',
stockoutRisk: 'low',
},
{
id: '2',
product: 'Croissants de Mantequilla',
currentStock: 18,
forecastDemand: 32,
recommendedProduction: 35,
confidence: 88,
trend: 'stable',
stockoutRisk: 'medium',
},
{
id: '3',
product: 'Baguettes Francesas',
currentStock: 12,
forecastDemand: 28,
recommendedProduction: 30,
confidence: 91,
trend: 'down',
stockoutRisk: 'high',
},
];
const alerts = [
{
id: '1',
type: 'stockout',
product: 'Baguettes Francesas',
message: 'Alto riesgo de agotamiento en las próximas 24h',
severity: 'high',
recommendation: 'Incrementar producción en 15 unidades',
},
{
id: '2',
type: 'overstock',
product: 'Magdalenas',
message: 'Probable exceso de stock para mañana',
severity: 'medium',
recommendation: 'Reducir producción en 20%',
},
{
id: '3',
type: 'weather',
product: 'Todos',
message: 'Lluvia prevista - incremento esperado en demanda de bollería',
severity: 'info',
recommendation: 'Aumentar producción de productos de interior en 10%',
},
];
const weatherImpact = {
today: 'sunny',
temperature: 22,
demandFactor: 0.95,
affectedCategories: ['helados', 'bebidas frías'],
};
const seasonalInsights = [
{ period: 'Mañana', factor: 1.2, products: ['Pan', 'Bollería'] },
{ period: 'Tarde', factor: 0.8, products: ['Tartas', 'Dulces'] },
{ period: 'Fin de semana', factor: 1.4, products: ['Tartas especiales'] },
];
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'up':
return <TrendingUp className="h-4 w-4 text-[var(--color-success)]" />;
case 'down':
return <TrendingUp className="h-4 w-4 text-[var(--color-error)] rotate-180" />;
default:
return <div className="h-4 w-4 bg-gray-400 rounded-full" />;
}
};
const getRiskBadge = (risk: string) => {
const riskConfig = {
low: { color: 'green', text: 'Bajo' },
medium: { color: 'yellow', text: 'Medio' },
high: { color: 'red', text: 'Alto' },
};
const config = riskConfig[risk as keyof typeof riskConfig];
return <Badge variant={config?.color as any}>{config?.text}</Badge>;
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Predicción de Demanda"
description="Predicciones inteligentes basadas en IA para optimizar tu producción"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Settings className="w-4 h-4 mr-2" />
Configurar
</Button>
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Exportar
</Button>
</div>
}
/>
{/* Key Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Precisión del Modelo</p>
<p className="text-3xl font-bold text-[var(--color-success)]">{forecastData.accuracy}%</p>
</div>
<div className="h-12 w-12 bg-[var(--color-success)]/10 rounded-full flex items-center justify-center">
<BarChart3 className="h-6 w-6 text-[var(--color-success)]" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Demanda Prevista</p>
<p className="text-3xl font-bold text-[var(--color-info)]">{forecastData.totalDemand}</p>
<p className="text-xs text-[var(--text-tertiary)]">próximos {forecastPeriod} días</p>
</div>
<Calendar className="h-12 w-12 text-[var(--color-info)]" />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Tendencia</p>
<p className="text-3xl font-bold text-purple-600">+{forecastData.growthTrend}%</p>
<p className="text-xs text-[var(--text-tertiary)]">vs período anterior</p>
</div>
<TrendingUp className="h-12 w-12 text-purple-600" />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[var(--text-secondary)]">Factor Estacional</p>
<p className="text-3xl font-bold text-[var(--color-primary)]">{forecastData.seasonalityFactor}x</p>
<p className="text-xs text-[var(--text-tertiary)]">multiplicador actual</p>
</div>
<div className="h-12 w-12 bg-[var(--color-primary)]/10 rounded-full flex items-center justify-center">
<svg className="h-6 w-6 text-[var(--color-primary)]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707" />
</svg>
</div>
</div>
</Card>
</div>
{/* Controls */}
<Card className="p-6">
<div className="flex flex-col sm:flex-row gap-4">
<div className="flex-1 grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">Producto</label>
<select
value={selectedProduct}
onChange={(e) => setSelectedProduct(e.target.value)}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md"
>
{products.map(product => (
<option key={product.id} value={product.id}>{product.name}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">Período</label>
<select
value={forecastPeriod}
onChange={(e) => setForecastPeriod(e.target.value)}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md"
>
{periods.map(period => (
<option key={period.value} value={period.value}>{period.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">Vista</label>
<div className="flex rounded-md border border-[var(--border-secondary)]">
<button
onClick={() => setViewMode('chart')}
className={`px-3 py-2 text-sm ${viewMode === 'chart' ? 'bg-blue-600 text-white' : 'bg-white text-[var(--text-secondary)]'} rounded-l-md`}
>
Gráfico
</button>
<button
onClick={() => setViewMode('table')}
className={`px-3 py-2 text-sm ${viewMode === 'table' ? 'bg-blue-600 text-white' : 'bg-white text-[var(--text-secondary)]'} rounded-r-md border-l`}
>
Tabla
</button>
</div>
</div>
</div>
</div>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Forecast Display */}
<div className="lg:col-span-2">
{viewMode === 'chart' ? (
<DemandChart
product={selectedProduct}
period={forecastPeriod}
/>
) : (
<ForecastTable forecasts={mockForecasts} />
)}
</div>
{/* Alerts Panel */}
<div className="space-y-6">
<AlertsPanel alerts={alerts} />
{/* Weather Impact */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Impacto Meteorológico</h3>
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm text-[var(--text-secondary)]">Hoy:</span>
<div className="flex items-center">
<span className="text-sm font-medium">{weatherImpact.temperature}°C</span>
<div className="ml-2 w-6 h-6 bg-yellow-400 rounded-full"></div>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-[var(--text-secondary)]">Factor de demanda:</span>
<span className="text-sm font-medium text-[var(--color-info)]">{weatherImpact.demandFactor}x</span>
</div>
<div className="mt-4">
<p className="text-xs text-[var(--text-tertiary)] mb-2">Categorías afectadas:</p>
<div className="flex flex-wrap gap-1">
{weatherImpact.affectedCategories.map((category, index) => (
<Badge key={index} variant="blue">{category}</Badge>
))}
</div>
</div>
</div>
</Card>
{/* Seasonal Insights */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Patrones Estacionales</h3>
<div className="space-y-3">
{seasonalInsights.map((insight, index) => (
<div key={index} className="p-3 bg-[var(--bg-secondary)] rounded-lg">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium">{insight.period}</span>
<span className="text-sm text-purple-600 font-medium">{insight.factor}x</span>
</div>
<div className="flex flex-wrap gap-1">
{insight.products.map((product, idx) => (
<Badge key={idx} variant="purple">{product}</Badge>
))}
</div>
</div>
))}
</div>
</Card>
</div>
</div>
{/* Detailed Forecasts Table */}
<Card>
<div className="p-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-4">Predicciones Detalladas</h3>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-[var(--bg-secondary)]">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Producto
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Stock Actual
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Demanda Prevista
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Producción Recomendada
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Confianza
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Tendencia
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-[var(--text-tertiary)] uppercase tracking-wider">
Riesgo Agotamiento
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{mockForecasts.map((forecast) => (
<tr key={forecast.id} className="hover:bg-[var(--bg-secondary)]">
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-[var(--text-primary)]">{forecast.product}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-[var(--text-primary)]">
{forecast.currentStock}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--color-info)]">
{forecast.forecastDemand}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-[var(--color-success)]">
{forecast.recommendedProduction}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-[var(--text-primary)]">
{forecast.confidence}%
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
{getTrendIcon(forecast.trend)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
{getRiskBadge(forecast.stockoutRisk)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</Card>
</div>
);
};
export default ForecastingPage;

View File

@@ -0,0 +1,385 @@
import React, { useState } from 'react';
import { Calendar, TrendingUp, AlertTriangle, BarChart3, Download, Settings } from 'lucide-react';
import { Button, Card, Badge, Select } from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
import { DemandChart, ForecastTable, SeasonalityIndicator, AlertsPanel } from '../../../../components/domain/forecasting';
const ForecastingPage: React.FC = () => {
const [selectedProduct, setSelectedProduct] = useState('all');
const [forecastPeriod, setForecastPeriod] = useState('7');
const [viewMode, setViewMode] = useState<'chart' | 'table'>('chart');
const forecastData = {
accuracy: 92,
totalDemand: 1247,
growthTrend: 8.5,
seasonalityFactor: 1.15,
};
const products = [
{ id: 'all', name: 'Todos los productos' },
{ id: 'bread', name: 'Panes' },
{ id: 'pastry', name: 'Bollería' },
{ id: 'cake', name: 'Tartas' },
];
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' },
];
const mockForecasts = [
{
id: '1',
product: 'Pan de Molde Integral',
currentStock: 25,
forecastDemand: 45,
recommendedProduction: 50,
confidence: 95,
trend: 'up',
stockoutRisk: 'low',
},
{
id: '2',
product: 'Croissants de Mantequilla',
currentStock: 18,
forecastDemand: 32,
recommendedProduction: 35,
confidence: 88,
trend: 'stable',
stockoutRisk: 'medium',
},
{
id: '3',
product: 'Baguettes Francesas',
currentStock: 12,
forecastDemand: 28,
recommendedProduction: 30,
confidence: 91,
trend: 'down',
stockoutRisk: 'high',
},
];
const alerts = [
{
id: '1',
type: 'stockout',
product: 'Baguettes Francesas',
message: 'Alto riesgo de agotamiento en las próximas 24h',
severity: 'high',
recommendation: 'Incrementar producción en 15 unidades',
},
{
id: '2',
type: 'overstock',
product: 'Magdalenas',
message: 'Probable exceso de stock para mañana',
severity: 'medium',
recommendation: 'Reducir producción en 20%',
},
{
id: '3',
type: 'weather',
product: 'Todos',
message: 'Lluvia prevista - incremento esperado en demanda de bollería',
severity: 'info',
recommendation: 'Aumentar producción de productos de interior en 10%',
},
];
const weatherImpact = {
today: 'sunny',
temperature: 22,
demandFactor: 0.95,
affectedCategories: ['helados', 'bebidas frías'],
};
const seasonalInsights = [
{ period: 'Mañana', factor: 1.2, products: ['Pan', 'Bollería'] },
{ period: 'Tarde', factor: 0.8, products: ['Tartas', 'Dulces'] },
{ period: 'Fin de semana', factor: 1.4, products: ['Tartas especiales'] },
];
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'up':
return <TrendingUp className="h-4 w-4 text-green-600" />;
case 'down':
return <TrendingUp className="h-4 w-4 text-red-600 rotate-180" />;
default:
return <div className="h-4 w-4 bg-gray-400 rounded-full" />;
}
};
const getRiskBadge = (risk: string) => {
const riskConfig = {
low: { color: 'green', text: 'Bajo' },
medium: { color: 'yellow', text: 'Medio' },
high: { color: 'red', text: 'Alto' },
};
const config = riskConfig[risk as keyof typeof riskConfig];
return <Badge variant={config?.color as any}>{config?.text}</Badge>;
};
return (
<div className="p-6 space-y-6">
<PageHeader
title="Predicción de Demanda"
description="Predicciones inteligentes basadas en IA para optimizar tu producción"
action={
<div className="flex space-x-2">
<Button variant="outline">
<Settings className="w-4 h-4 mr-2" />
Configurar
</Button>
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Exportar
</Button>
</div>
}
/>
{/* Key Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Precisión del Modelo</p>
<p className="text-3xl font-bold text-green-600">{forecastData.accuracy}%</p>
</div>
<div className="h-12 w-12 bg-green-100 rounded-full flex items-center justify-center">
<BarChart3 className="h-6 w-6 text-green-600" />
</div>
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Demanda Prevista</p>
<p className="text-3xl font-bold text-blue-600">{forecastData.totalDemand}</p>
<p className="text-xs text-gray-500">próximos {forecastPeriod} días</p>
</div>
<Calendar className="h-12 w-12 text-blue-600" />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Tendencia</p>
<p className="text-3xl font-bold text-purple-600">+{forecastData.growthTrend}%</p>
<p className="text-xs text-gray-500">vs período anterior</p>
</div>
<TrendingUp className="h-12 w-12 text-purple-600" />
</div>
</Card>
<Card className="p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">Factor Estacional</p>
<p className="text-3xl font-bold text-orange-600">{forecastData.seasonalityFactor}x</p>
<p className="text-xs text-gray-500">multiplicador actual</p>
</div>
<div className="h-12 w-12 bg-orange-100 rounded-full flex items-center justify-center">
<svg className="h-6 w-6 text-orange-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707" />
</svg>
</div>
</div>
</Card>
</div>
{/* Controls */}
<Card className="p-6">
<div className="flex flex-col sm:flex-row gap-4">
<div className="flex-1 grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Producto</label>
<select
value={selectedProduct}
onChange={(e) => setSelectedProduct(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
>
{products.map(product => (
<option key={product.id} value={product.id}>{product.name}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Período</label>
<select
value={forecastPeriod}
onChange={(e) => setForecastPeriod(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md"
>
{periods.map(period => (
<option key={period.value} value={period.value}>{period.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Vista</label>
<div className="flex rounded-md border border-gray-300">
<button
onClick={() => setViewMode('chart')}
className={`px-3 py-2 text-sm ${viewMode === 'chart' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700'} rounded-l-md`}
>
Gráfico
</button>
<button
onClick={() => setViewMode('table')}
className={`px-3 py-2 text-sm ${viewMode === 'table' ? 'bg-blue-600 text-white' : 'bg-white text-gray-700'} rounded-r-md border-l`}
>
Tabla
</button>
</div>
</div>
</div>
</div>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Forecast Display */}
<div className="lg:col-span-2">
{viewMode === 'chart' ? (
<DemandChart
product={selectedProduct}
period={forecastPeriod}
/>
) : (
<ForecastTable forecasts={mockForecasts} />
)}
</div>
{/* Alerts Panel */}
<div className="space-y-6">
<AlertsPanel alerts={alerts} />
{/* Weather Impact */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Impacto Meteorológico</h3>
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Hoy:</span>
<div className="flex items-center">
<span className="text-sm font-medium">{weatherImpact.temperature}°C</span>
<div className="ml-2 w-6 h-6 bg-yellow-400 rounded-full"></div>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Factor de demanda:</span>
<span className="text-sm font-medium text-blue-600">{weatherImpact.demandFactor}x</span>
</div>
<div className="mt-4">
<p className="text-xs text-gray-500 mb-2">Categorías afectadas:</p>
<div className="flex flex-wrap gap-1">
{weatherImpact.affectedCategories.map((category, index) => (
<Badge key={index} variant="blue">{category}</Badge>
))}
</div>
</div>
</div>
</Card>
{/* Seasonal Insights */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Patrones Estacionales</h3>
<div className="space-y-3">
{seasonalInsights.map((insight, index) => (
<div key={index} className="p-3 bg-gray-50 rounded-lg">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium">{insight.period}</span>
<span className="text-sm text-purple-600 font-medium">{insight.factor}x</span>
</div>
<div className="flex flex-wrap gap-1">
{insight.products.map((product, idx) => (
<Badge key={idx} variant="purple">{product}</Badge>
))}
</div>
</div>
))}
</div>
</Card>
</div>
</div>
{/* Detailed Forecasts Table */}
<Card>
<div className="p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Predicciones Detalladas</h3>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Producto
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Stock Actual
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Demanda Prevista
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Producción Recomendada
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Confianza
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tendencia
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Riesgo Agotamiento
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{mockForecasts.map((forecast) => (
<tr key={forecast.id} className="hover:bg-gray-50">
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-gray-900">{forecast.product}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{forecast.currentStock}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-blue-600">
{forecast.forecastDemand}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-green-600">
{forecast.recommendedProduction}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{forecast.confidence}%
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
{getTrendIcon(forecast.trend)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
{getRiskBadge(forecast.stockoutRisk)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</Card>
</div>
);
};
export default ForecastingPage;

View File

@@ -0,0 +1 @@
export { default as ForecastingPage } from './ForecastingPage';