Fix inventario api error

This commit is contained in:
Urtzi Alfaro
2025-09-14 16:24:09 +02:00
parent 96da9ca077
commit 36cfc88f93
2 changed files with 469 additions and 48 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
import { Plus, Download, AlertTriangle, Package, Clock, CheckCircle, Eye, Edit, Calendar, DollarSign } from 'lucide-react';
import { Plus, Download, AlertTriangle, Package, Clock, CheckCircle, Eye, Edit, Calendar, DollarSign, ArrowRight, TrendingUp, Shield } from 'lucide-react';
import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui';
import { LoadingSpinner } from '../../../../components/shared';
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
@@ -93,16 +93,28 @@ const InventoryPage: React.FC = () => {
expiringSoon: 0, // This would come from expired stock API
totalValue: ingredients.reduce((sum, item) => sum + (item.current_stock_level * (item.average_cost || 0)), 0),
categories: [...new Set(ingredients.map(item => item.category))].length,
turnoverRate: 0,
fastMovingItems: 0,
qualityScore: 85,
reorderAccuracy: 0,
};
}
// Extract data from new analytics structure
const stockoutCount = Object.values(analyticsData.stockout_frequency || {}).reduce((sum: number, count) => sum + count, 0);
const fastMovingCount = (analyticsData.fast_moving_items || []).length;
return {
totalItems: analyticsData.total_ingredients || 0,
lowStockItems: analyticsData.low_stock_count || 0,
outOfStock: analyticsData.out_of_stock_count || 0,
expiringSoon: analyticsData.expiring_soon_count || 0,
totalValue: analyticsData.total_stock_value || 0,
categories: [...new Set(ingredients.map(item => item.category))].length,
totalItems: ingredients.length, // Use ingredients array as fallback
lowStockItems: stockoutCount || lowStockItems.length,
outOfStock: stockoutCount || ingredients.filter(item => item.stock_status === 'out_of_stock').length,
expiringSoon: Math.round(Number(analyticsData.quality_incidents_rate || 0) * ingredients.length), // Estimate from quality incidents
totalValue: Number(analyticsData.total_inventory_cost || 0),
categories: Object.keys(analyticsData.cost_by_category || {}).length || [...new Set(ingredients.map(item => item.category))].length,
turnoverRate: Number(analyticsData.inventory_turnover_rate || 0),
fastMovingItems: fastMovingCount,
qualityScore: Number(analyticsData.food_safety_score || 85),
reorderAccuracy: Math.round(Number(analyticsData.reorder_accuracy || 0) * 100),
};
}, [analyticsData, ingredients, lowStockItems]);
@@ -138,10 +150,22 @@ const InventoryPage: React.FC = () => {
icon: DollarSign,
},
{
title: 'Categorías',
value: inventoryStats.categories,
title: 'Tasa Rotación',
value: inventoryStats.turnoverRate.toFixed(1),
variant: 'info' as const,
icon: Package,
icon: ArrowRight,
},
{
title: 'Items Dinámicos',
value: inventoryStats.fastMovingItems,
variant: 'success' as const,
icon: TrendingUp,
},
{
title: 'Puntuación Calidad',
value: `${inventoryStats.qualityScore}%`,
variant: inventoryStats.qualityScore >= 90 ? 'success' as const : inventoryStats.qualityScore >= 70 ? 'warning' as const : 'error' as const,
icon: Shield,
},
];
@@ -195,11 +219,99 @@ const InventoryPage: React.FC = () => {
/>
{/* Stats Grid */}
<StatsGrid
<StatsGrid
stats={stats}
columns={3}
columns={4}
/>
{/* Analytics Section */}
{analyticsData && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Fast Moving Items */}
<Card className="p-6">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<TrendingUp className="w-5 h-5 text-green-500" />
Items de Alta Rotación
</h3>
<div className="space-y-3">
{(analyticsData.fast_moving_items || []).slice(0, 5).map((item: any, index: number) => (
<div key={item.ingredient_id || index} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p className="font-medium">{item.name}</p>
<p className="text-sm text-gray-600">{item.movement_count} movimientos</p>
</div>
<div className="text-right">
<p className="font-medium">{formatters.currency(item.avg_cost)}</p>
</div>
</div>
))}
{(!analyticsData.fast_moving_items || analyticsData.fast_moving_items.length === 0) && (
<p className="text-gray-500 text-center py-4">No hay datos de items de alta rotación</p>
)}
</div>
</Card>
{/* Cost by Category */}
<Card className="p-6">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Package className="w-5 h-5 text-blue-500" />
Costos por Categoría
</h3>
<div className="space-y-3">
{Object.entries(analyticsData.cost_by_category || {}).slice(0, 5).map(([category, cost]) => (
<div key={category} className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p className="font-medium capitalize">{category}</p>
</div>
<div className="text-right">
<p className="font-medium">{formatters.currency(Number(cost))}</p>
</div>
</div>
))}
{Object.keys(analyticsData.cost_by_category || {}).length === 0 && (
<p className="text-gray-500 text-center py-4">No hay datos de costos por categoría</p>
)}
</div>
</Card>
{/* Efficiency Metrics */}
<Card className="p-6">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Shield className="w-5 h-5 text-purple-500" />
Métricas de Eficiencia
</h3>
<div className="grid grid-cols-2 gap-4">
<div className="text-center p-3 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-blue-600">{inventoryStats.reorderAccuracy}%</p>
<p className="text-sm text-gray-600">Precisión Reorden</p>
</div>
<div className="text-center p-3 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-green-600">{inventoryStats.turnoverRate.toFixed(1)}</p>
<p className="text-sm text-gray-600">Tasa Rotación</p>
</div>
</div>
</Card>
{/* Quality Metrics */}
<Card className="p-6">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<CheckCircle className="w-5 h-5 text-green-500" />
Indicadores de Calidad
</h3>
<div className="grid grid-cols-2 gap-4">
<div className="text-center p-3 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-green-600">{inventoryStats.qualityScore}%</p>
<p className="text-sm text-gray-600">Puntuación Seguridad</p>
</div>
<div className="text-center p-3 bg-gray-50 rounded-lg">
<p className="text-2xl font-bold text-blue-600">{Number(analyticsData.temperature_compliance_rate || 95).toFixed(1)}%</p>
<p className="text-sm text-gray-600">Cumplimiento Temp.</p>
</div>
</div>
</Card>
</div>
)}
{/* Low Stock Alert */}
{lowStockItems.length > 0 && (
<LowStockAlert items={lowStockItems} />