import React, { useState } from 'react'; import { TrendingUp, DollarSign, Activity, AlertTriangle, Settings, CheckCircle, Wrench, Zap, Thermometer, Eye, Clock, CalendarClock, WrenchIcon, BarChart3, Bell, History, Lock } from 'lucide-react'; import { Card, StatsGrid, Button } from '../../../components/ui'; import { PageHeader } from '../../../components/layout'; import { QualityDashboard } from '../../../components/domain/production'; import ProductionCostAnalytics from '../../../components/domain/analytics/ProductionCostAnalytics'; import AIInsightsWidget from '../../../components/domain/dashboard/AIInsightsWidget'; import EquipmentStatusWidget from '../../../components/domain/dashboard/EquipmentStatusWidget'; import { Badge } from '../../../components/ui/Badge'; import { useSubscription } from '../../../api/hooks/subscription'; import { useCurrentTenant } from '../../../stores/tenant.store'; // Mock data for equipment (from MaquinariaPage) const MOCK_EQUIPMENT = [ { id: '1', name: 'Horno Principal #1', type: 'oven', model: 'Miwe Condo CO 4.1212', serialNumber: 'MCO-2021-001', location: 'Área de Horneado - Zona A', status: 'operational', installDate: '2021-03-15', lastMaintenance: '2024-01-15', nextMaintenance: '2024-04-15', maintenanceInterval: 90, temperature: 220, targetTemperature: 220, efficiency: 92, uptime: 98.5, energyUsage: 45.2, utilizationToday: 87, alerts: [], maintenanceHistory: [ { id: '1', date: '2024-01-15', type: 'preventive', description: 'Limpieza general y calibración de termostatos', technician: 'Juan Pérez', cost: 150, downtime: 2, partsUsed: ['Filtros de aire', 'Sellos de puerta'] } ], specifications: { power: 45, capacity: 24, dimensions: { width: 200, height: 180, depth: 120 }, weight: 850 } }, { id: '2', name: 'Batidora Industrial #2', type: 'mixer', model: 'Hobart HL800', serialNumber: 'HHL-2020-002', location: 'Área de Preparación - Zona B', status: 'warning', installDate: '2020-08-10', lastMaintenance: '2024-01-20', nextMaintenance: '2024-02-20', maintenanceInterval: 30, efficiency: 88, uptime: 94.2, energyUsage: 12.8, utilizationToday: 76, alerts: [ { id: '1', type: 'warning', message: 'Vibración inusual detectada en el motor', timestamp: '2024-01-23T10:30:00Z', acknowledged: false }, { id: '2', type: 'info', message: 'Mantenimiento programado en 5 días', timestamp: '2024-01-23T08:00:00Z', acknowledged: true } ], maintenanceHistory: [ { id: '1', date: '2024-01-20', type: 'corrective', description: 'Reemplazo de correas de transmisión', technician: 'María González', cost: 85, downtime: 4, partsUsed: ['Correa tipo V', 'Rodamientos'] } ], specifications: { power: 15, capacity: 80, dimensions: { width: 120, height: 150, depth: 80 }, weight: 320 } }, { id: '3', name: 'Cámara de Fermentación #1', type: 'proofer', model: 'Bongard EUROPA 16.18', serialNumber: 'BEU-2022-001', location: 'Área de Fermentación', status: 'maintenance', installDate: '2022-06-20', lastMaintenance: '2024-01-23', nextMaintenance: '2024-01-24', maintenanceInterval: 60, temperature: 32, targetTemperature: 35, efficiency: 0, uptime: 85.1, energyUsage: 0, utilizationToday: 0, alerts: [ { id: '1', type: 'info', message: 'En mantenimiento programado', timestamp: '2024-01-23T06:00:00Z', acknowledged: true } ], maintenanceHistory: [ { id: '1', date: '2024-01-23', type: 'preventive', description: 'Mantenimiento programado - sistema de humidificación', technician: 'Carlos Rodríguez', cost: 200, downtime: 8, partsUsed: ['Sensor de humedad', 'Válvulas'] } ], specifications: { power: 8, capacity: 16, dimensions: { width: 180, height: 200, depth: 100 }, weight: 450 } } ]; // Mock chart data for equipment analytics (from MaquinariaPage) const MOCK_CHART_DATA = [ { id: 'efficiency', name: 'Eficiencia', type: 'bar' as const, visible: true, color: '#10B981', data: [ { x: 1, y: 92, label: 'Horno Principal #1' }, { x: 2, y: 88, label: 'Batidora Industrial #2' }, { x: 3, y: 0, label: 'Cámara de Fermentación #1' } ] }, { id: 'uptime', name: 'Tiempo de Actividad', type: 'line' as const, visible: true, color: '#3B82F6', data: [ { x: 1, y: 98.5, label: 'Horno Principal #1' }, { x: 2, y: 94.2, label: 'Batidora Industrial #2' }, { x: 3, y: 85.1, label: 'Cámara de Fermentación #1' } ] }, { id: 'energy', name: 'Consumo Energético', type: 'area' as const, visible: true, color: '#F59E0B', data: [ { x: 1, y: 45.2, label: 'Horno Principal #1' }, { x: 2, y: 12.8, label: 'Batidora Industrial #2' }, { x: 3, y: 0, label: 'Cámara de Fermentación #1' } ] } ]; const MOCK_MAINTENANCE_CHART_DATA = [ { id: 'costs', name: 'Costos de Mantenimiento', type: 'bar' as const, visible: true, color: '#8B5CF6', data: [ { x: 1, y: 150, label: 'Horno Principal #1' }, { x: 2, y: 85, label: 'Batidora Industrial #2' }, { x: 3, y: 200, label: 'Cámara de Fermentación #1' } ] }, { id: 'downtime', name: 'Tiempo de Inactividad', type: 'line' as const, visible: true, color: '#EF4444', data: [ { x: 1, y: 2, label: 'Horno Principal #1' }, { x: 2, y: 4, label: 'Batidora Industrial #2' }, { x: 3, y: 8, label: 'Cámara de Fermentación #1' } ] } ]; const MOCK_STATUS_CHART_DATA = [ { id: 'status', name: 'Estado de Equipos', type: 'pie' as const, visible: true, color: '#10B981', data: [ { x: 'Operativo', y: 1, label: 'Operativo' }, { x: 'Advertencia', y: 1, label: 'Advertencia' }, { x: 'Mantenimiento', y: 1, label: 'Mantenimiento' } ] } ]; // Import statement is at the top of the file - already included // The enhanced ProductionCostMonitor is now imported from the components directory // Helper functions for equipment status const getStatusConfig = (status: string) => { const configs = { operational: { color: '#10B981', text: 'Operativo', icon: CheckCircle }, warning: { color: '#F59E0B', text: 'Advertencia', icon: AlertTriangle }, maintenance: { color: '#3B82F6', text: 'Mantenimiento', icon: Wrench }, down: { color: '#EF4444', text: 'Fuera de Servicio', icon: AlertTriangle } }; return configs[status as keyof typeof configs] || { color: '#6B7280', text: 'Desconocido', icon: Settings }; }; const getTypeIcon = (type: string) => { const icons = { oven: Thermometer, mixer: Activity, proofer: Settings, freezer: Zap, packaging: Settings, other: Settings }; return icons[type as keyof typeof icons] || Settings; }; const getStatusColor = (status: string): string => { const statusColors: { [key: string]: string } = { operational: '#10B981', // green-500 warning: '#F59E0B', // amber-500 maintenance: '#3B82F6', // blue-500 down: '#EF4444' // red-500 }; return statusColors[status] || '#6B7280'; // gray-500 as default }; // Equipment Analytics Component (from MaquinariaPage) const EquipmentAnalytics: React.FC = () => { const [activeTab, setActiveTab] = useState('overview'); const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); // Filter equipment based on search and status const filteredEquipment = MOCK_EQUIPMENT.filter(eq => { const matchesSearch = !searchTerm || eq.name.toLowerCase().includes(searchTerm.toLowerCase()) || eq.location.toLowerCase().includes(searchTerm.toLowerCase()) || eq.type.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = statusFilter === 'all' || eq.status === statusFilter; return matchesSearch && matchesStatus; }); // Calculate equipment stats const equipmentStats = { total: MOCK_EQUIPMENT.length, operational: MOCK_EQUIPMENT.filter(e => e.status === 'operational').length, warning: MOCK_EQUIPMENT.filter(e => e.status === 'warning').length, maintenance: MOCK_EQUIPMENT.filter(e => e.status === 'maintenance').length, down: MOCK_EQUIPMENT.filter(e => e.status === 'down').length, avgEfficiency: MOCK_EQUIPMENT.reduce((sum, e) => sum + e.efficiency, 0) / MOCK_EQUIPMENT.length, avgUptime: MOCK_EQUIPMENT.reduce((sum, e) => sum + e.uptime, 0) / MOCK_EQUIPMENT.length, totalAlerts: MOCK_EQUIPMENT.reduce((sum, e) => sum + e.alerts.filter(a => !a.acknowledged).length, 0) }; return (
{/* Stats Grid */} = 90 ? 'success' as const : 'warning' as const }, { title: 'Alertas Activas', value: equipmentStats.totalAlerts, icon: Bell, variant: equipmentStats.totalAlerts === 0 ? 'success' as const : 'error' as const } ]} columns={4} /> {/* Tabs for different views */}
{/* Overview Tab */} {activeTab === 'overview' && (
{/* Charts for Equipment Analytics */}

Eficiencia y Tiempo de Actividad

{ if (canvas) { const ctx = canvas.getContext('2d'); if (ctx) { // Set canvas size canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw simple bar chart for efficiency const padding = 40; const chartWidth = canvas.width - 2 * padding; const chartHeight = canvas.height - 2 * padding; // Get max value for scaling const maxValue = Math.max(...MOCK_CHART_DATA[0].data.map(d => d.y)); // Draw bars MOCK_CHART_DATA[0].data.forEach((point, index) => { const barWidth = chartWidth / MOCK_CHART_DATA[0].data.length - 10; const x = padding + index * (chartWidth / MOCK_CHART_DATA[0].data.length) + 5; const barHeight = (point.y / maxValue) * chartHeight; const y = padding + chartHeight - barHeight; // Bar ctx.fillStyle = MOCK_CHART_DATA[0].color; ctx.fillRect(x, y, barWidth, barHeight); // Label ctx.fillStyle = '#374151'; ctx.font = '12px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(point.label, x + barWidth / 2, canvas.height - 10); ctx.fillText(point.y.toString(), x + barWidth / 2, y - 5); }); // Axes ctx.strokeStyle = '#374151'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, padding + chartHeight); ctx.lineTo(padding + chartWidth, padding + chartHeight); ctx.stroke(); } } }} className="w-full h-full" />

Estado de Equipos

{ if (canvas) { const ctx = canvas.getContext('2d'); if (ctx) { // Set canvas size canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw pie chart for status distribution const centerX = canvas.width / 2; const centerY = canvas.height / 2; const radius = Math.min(canvas.width, canvas.height) / 3; const total = MOCK_STATUS_CHART_DATA[0].data.reduce((sum, point) => sum + point.y, 0); let startAngle = -Math.PI / 2; const colors = ['#10B981', '#F59E0B', '#3B82F6']; MOCK_STATUS_CHART_DATA[0].data.forEach((point, index) => { const sliceAngle = (point.y / total) * 2 * Math.PI; const color = colors[index]; ctx.fillStyle = color; ctx.beginPath(); ctx.moveTo(centerX, centerY); ctx.arc(centerX, centerY, radius, startAngle, startAngle + sliceAngle); ctx.closePath(); ctx.fill(); // Draw slice border ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.stroke(); // Draw labels const labelAngle = startAngle + sliceAngle / 2; const labelX = centerX + Math.cos(labelAngle) * (radius + 30); const labelY = centerY + Math.sin(labelAngle) * (radius + 30); ctx.fillStyle = '#374151'; ctx.font = '12px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(point.x, labelX, labelY); const percentage = ((point.y / total) * 100).toFixed(1); ctx.fillText(`${percentage}%`, labelX, labelY + 15); startAngle += sliceAngle; }); } } }} className="w-full h-full" />
{/* Controls */}
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-[var(--border-primary)] rounded-md bg-[var(--bg-primary)] text-[var(--text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent" />
{/* Equipment Grid */}
{filteredEquipment.map((equipment) => { const statusConfig = getStatusConfig(equipment.status); const TypeIcon = getTypeIcon(equipment.type); return (

{equipment.name}

{equipment.location}

Eficiencia

{equipment.efficiency}%

Tiempo Activo

{equipment.uptime.toFixed(1)}%

Consumo

{equipment.energyUsage} kW

Utilización Hoy

{equipment.utilizationToday}%

{statusConfig.text}
); })}
)} {/* Maintenance Tab */} {activeTab === 'maintenance' && (
{/* Maintenance Charts */}

Costos de Mantenimiento

{ if (canvas) { const ctx = canvas.getContext('2d'); if (ctx) { // Set canvas size canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw bar chart for maintenance costs const padding = 40; const chartWidth = canvas.width - 2 * padding; const chartHeight = canvas.height - 2 * padding; // Get max value for scaling const maxValue = Math.max(...MOCK_MAINTENANCE_CHART_DATA[0].data.map(d => d.y)); // Draw bars MOCK_MAINTENANCE_CHART_DATA[0].data.forEach((point, index) => { const barWidth = chartWidth / MOCK_MAINTENANCE_CHART_DATA[0].data.length - 10; const x = padding + index * (chartWidth / MOCK_MAINTENANCE_CHART_DATA[0].data.length) + 5; const barHeight = (point.y / maxValue) * chartHeight; const y = padding + chartHeight - barHeight; // Bar ctx.fillStyle = MOCK_MAINTENANCE_CHART_DATA[0].color; ctx.fillRect(x, y, barWidth, barHeight); // Label ctx.fillStyle = '#374151'; ctx.font = '12px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(point.label, x + barWidth / 2, canvas.height - 10); ctx.fillText(`€${point.y}`, x + barWidth / 2, y - 5); }); // Axes ctx.strokeStyle = '#374151'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, padding + chartHeight); ctx.lineTo(padding + chartWidth, padding + chartHeight); ctx.stroke(); } } }} className="w-full h-full" />

Tiempo de Inactividad

{ if (canvas) { const ctx = canvas.getContext('2d'); if (ctx) { // Set canvas size canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw line chart for downtime const padding = 40; const chartWidth = canvas.width - 2 * padding; const chartHeight = canvas.height - 2 * padding; // Get max value for scaling const maxValue = Math.max(...MOCK_MAINTENANCE_CHART_DATA[1].data.map(d => d.y)); // Draw line ctx.strokeStyle = MOCK_MAINTENANCE_CHART_DATA[1].color; ctx.lineWidth = 2; ctx.beginPath(); MOCK_MAINTENANCE_CHART_DATA[1].data.forEach((point, index) => { const x = padding + (index * chartWidth) / (MOCK_MAINTENANCE_CHART_DATA[1].data.length - 1); const y = padding + chartHeight - ((point.y / maxValue) * chartHeight); if (index === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } // Draw points ctx.fillStyle = MOCK_MAINTENANCE_CHART_DATA[1].color; ctx.beginPath(); ctx.arc(x, y, 4, 0, 2 * Math.PI); ctx.fill(); // Labels ctx.fillStyle = '#374151'; ctx.font = '12px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(point.label, x, canvas.height - 10); ctx.fillText(`${point.y}h`, x, y - 10); }); ctx.stroke(); // Axes ctx.strokeStyle = '#374151'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, padding + chartHeight); ctx.lineTo(padding + chartWidth, padding + chartHeight); ctx.stroke(); } } }} className="w-full h-full" />
{/* Maintenance Schedule */}

Programación de Mantenimiento

{MOCK_EQUIPMENT.map((equipment) => { const nextMaintenanceDate = new Date(equipment.nextMaintenance); const daysUntilMaintenance = Math.ceil((nextMaintenanceDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)); const isOverdue = daysUntilMaintenance < 0; return (

{equipment.name}

{equipment.model}

{isOverdue ? 'Atrasado' : 'Programado'}

{nextMaintenanceDate.toLocaleDateString('es-ES')}

Técnico: Juan Pérez
); })}
)} {/* Status Tab */} {activeTab === 'status' && (
{/* Status Overview */}
{equipmentStats.operational}
Operativo
{equipmentStats.warning}
Advertencia
{equipmentStats.maintenance}
Mantenimiento
{equipmentStats.down}
Fuera de Servicio
{/* Active Alerts */}

Alertas Activas

{MOCK_EQUIPMENT.flatMap(eq => eq.alerts.filter(a => !a.acknowledged).map(alert => (
{eq.name}
{new Date(alert.timestamp).toLocaleString('es-ES')}

{alert.message}

)) )} {MOCK_EQUIPMENT.flatMap(eq => eq.alerts.filter(a => !a.acknowledged)).length === 0 && (

No hay alertas activas

Todos los equipos están funcionando correctamente

)}
{/* Equipment Status Details */}

Detalles de Estado de Equipos

{MOCK_EQUIPMENT.map((equipment) => { const statusConfig = getStatusConfig(equipment.status); return (

{equipment.name}

{equipment.model}

{statusConfig.text}
{equipment.efficiency}%
Eficiencia
{equipment.uptime.toFixed(1)}%
Tiempo de Actividad
{equipment.energyUsage} kW
Consumo Energético
{equipment.utilizationToday}%
Utilización Hoy
); })}
)}
); }; const ProductionAnalyticsPage: React.FC = () => { const [activeTab, setActiveTab] = useState('overview'); const { canAccessAnalytics } = useSubscription(); const currentTenant = useCurrentTenant(); const tenantId = currentTenant?.id || ''; // Check if user has access to advanced analytics (professional/enterprise) const hasAdvancedAccess = canAccessAnalytics('advanced'); // If user doesn't have access to advanced analytics, show upgrade message if (!hasAdvancedAccess) { return (

Contenido exclusivo para planes Professional y Enterprise

El análisis avanzado de producción está disponible solo para usuarios con planes Professional o Enterprise. Actualiza tu plan para acceder a todas las funcionalidades.

); } return (
{/* Key Metrics */} {/* Tabs Navigation */}
{/* Tab Content */} {activeTab === 'overview' && (

Resumen de Calidad

Productos aprobados 96%
Rechazos por calidad 2%
Reprocesos 2%
)} {activeTab === 'costs' && (
)} {activeTab === 'ai-insights' && (
)} {activeTab === 'maquinaria' && ( )} {activeTab === 'quality' && ( )}
); }; export default ProductionAnalyticsPage;