import React, { useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Plus, AlertTriangle, Settings, CheckCircle, Eye, Wrench, Thermometer, Activity, Search, Filter, Bell, History, Calendar, Edit, Trash2 } from 'lucide-react'; import { Button, StatsGrid, StatusCard, getStatusColor, SearchAndFilter, type FilterConfig } from '../../../../components/ui'; import { Badge } from '../../../../components/ui/Badge'; import { LoadingSpinner } from '../../../../components/ui'; import { PageHeader } from '../../../../components/layout'; import { useCurrentTenant } from '../../../../stores/tenant.store'; import { Equipment } from '../../../../api/types/equipment'; import { EquipmentModal } from '../../../../components/domain/equipment/EquipmentModal'; import { useEquipment, useCreateEquipment, useUpdateEquipment } from '../../../../api/hooks/equipment'; const MaquinariaPage: React.FC = () => { const { t } = useTranslation(['equipment', 'common']); const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState(''); const [typeFilter, setTypeFilter] = useState(''); const [selectedItem, setSelectedItem] = useState(null); const [showMaintenanceModal, setShowMaintenanceModal] = useState(false); const [showEquipmentModal, setShowEquipmentModal] = useState(false); const [equipmentModalMode, setEquipmentModalMode] = useState<'view' | 'edit' | 'create'>('create'); const [selectedEquipment, setSelectedEquipment] = useState(null); const currentTenant = useCurrentTenant(); const tenantId = currentTenant?.id || ''; // Fetch equipment data from API const { data: equipment = [], isLoading, error } = useEquipment(tenantId, { is_active: true }); // Mutations for create and update const createEquipmentMutation = useCreateEquipment(tenantId); const updateEquipmentMutation = useUpdateEquipment(tenantId); const handleCreateEquipment = () => { setSelectedEquipment({ id: '', name: '', type: 'other', model: '', serialNumber: '', location: '', status: 'operational', installDate: new Date().toISOString().split('T')[0], lastMaintenance: new Date().toISOString().split('T')[0], nextMaintenance: new Date().toISOString().split('T')[0], maintenanceInterval: 30, efficiency: 100, uptime: 100, energyUsage: 0, utilizationToday: 0, alerts: [], maintenanceHistory: [], specifications: { power: 0, capacity: 0, dimensions: { width: 0, height: 0, depth: 0 }, weight: 0 } } as Equipment); setEquipmentModalMode('create'); setShowEquipmentModal(true); }; const handleEditEquipment = (equipmentId: string) => { // Find the equipment to edit from real data const equipmentToEdit = equipment.find(eq => eq.id === equipmentId); if (equipmentToEdit) { setSelectedEquipment(equipmentToEdit); setEquipmentModalMode('edit'); setShowEquipmentModal(true); } }; const handleScheduleMaintenance = (equipmentId: string) => { console.log('Schedule maintenance for equipment:', equipmentId); // Implementation would go here }; const handleAcknowledgeAlert = (equipmentId: string, alertId: string) => { console.log('Acknowledge alert:', alertId, 'for equipment:', equipmentId); // Implementation would go here }; const handleViewMaintenanceHistory = (equipmentId: string) => { console.log('View maintenance history for equipment:', equipmentId); // Implementation would go here }; const handleSaveEquipment = async (equipmentData: Equipment) => { try { if (equipmentModalMode === 'create') { await createEquipmentMutation.mutateAsync(equipmentData); } else if (equipmentModalMode === 'edit' && equipmentData.id) { await updateEquipmentMutation.mutateAsync({ equipmentId: equipmentData.id, equipmentData: equipmentData }); } setShowEquipmentModal(false); setSelectedEquipment(null); } catch (error) { console.error('Error saving equipment:', error); // Error is already handled by mutation with toast } }; const filteredEquipment = useMemo(() => { return 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 || eq.status === statusFilter; const matchesType = !typeFilter || eq.type === typeFilter; return matchesSearch && matchesStatus && matchesType; }); }, [equipment, searchTerm, statusFilter, typeFilter]); const equipmentStats = useMemo(() => { const total = equipment.length; const operational = equipment.filter(e => e.status === 'operational').length; const warning = equipment.filter(e => e.status === 'warning').length; const maintenance = equipment.filter(e => e.status === 'maintenance').length; const down = equipment.filter(e => e.status === 'down').length; const totalAlerts = equipment.reduce((sum, e) => sum + e.alerts.filter(a => !a.acknowledged).length, 0); return { total, operational, warning, maintenance, down, totalAlerts }; }, [equipment]); const getStatusConfig = (status: Equipment['status']) => { const configs = { operational: { color: getStatusColor('completed'), text: t('equipment_status.operational'), icon: CheckCircle }, warning: { color: getStatusColor('warning'), text: t('equipment_status.warning'), icon: AlertTriangle }, maintenance: { color: getStatusColor('info'), text: t('equipment_status.maintenance'), icon: Wrench }, down: { color: getStatusColor('error'), text: t('equipment_status.down'), icon: AlertTriangle } }; return configs[status]; }; const getTypeIcon = (type: Equipment['type']) => { const icons = { oven: Thermometer, mixer: Activity, proofer: Settings, freezer: Settings, packaging: Settings, other: Settings }; return icons[type]; }; const stats = [ { title: t('labels.total_equipment'), value: equipmentStats.total, icon: Settings, variant: 'default' as const }, { title: t('labels.operational'), value: equipmentStats.operational, icon: CheckCircle, variant: 'success' as const, subtitle: `${((equipmentStats.operational / equipmentStats.total) * 100).toFixed(1)}%` }, { title: t('labels.active_alerts'), value: equipmentStats.totalAlerts, icon: Bell, variant: equipmentStats.totalAlerts === 0 ? 'success' as const : 'error' as const } ]; const handleShowMaintenanceDetails = (equipment: Equipment) => { setSelectedItem(equipment); setShowMaintenanceModal(true); }; const handleCloseMaintenanceModal = () => { setShowMaintenanceModal(false); setSelectedItem(null); }; // Loading state if (!tenantId) { return (
); } if (isLoading) { return (
); } if (error) { return (

{t('common:errors.load_error')}

{t('common:errors.try_again')}

); } return (
{/* Stats Grid */} {/* Search and Filter Controls */} setStatusFilter(value as string), placeholder: t('common:forms.select_option'), options: [ { value: 'operational', label: t('equipment_status.operational') }, { value: 'warning', label: t('equipment_status.warning') }, { value: 'maintenance', label: t('equipment_status.maintenance') }, { value: 'down', label: t('equipment_status.down') } ] }, { key: 'type', label: 'Tipo', type: 'dropdown', value: typeFilter, onChange: (value) => setTypeFilter(value as string), placeholder: 'Todos los tipos', options: [ { value: 'oven', label: 'Horno' }, { value: 'mixer', label: 'Batidora' }, { value: 'proofer', label: 'Fermentadora' }, { value: 'freezer', label: 'Congelador' }, { value: 'packaging', label: 'Empaquetado' }, { value: 'other', label: 'Otro' } ] } ] as FilterConfig[]} /> {/* Equipment Grid */}
{filteredEquipment.map((equipment) => { const statusConfig = getStatusConfig(equipment.status); const TypeIcon = getTypeIcon(equipment.type); // Calculate maintenance status const nextMaintenanceDate = new Date(equipment.nextMaintenance); const daysUntilMaintenance = Math.ceil((nextMaintenanceDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)); const isOverdue = daysUntilMaintenance < 0; return ( handleShowMaintenanceDetails(equipment)} actions={[ { label: t('actions.view_details'), icon: Eye, variant: 'primary', priority: 'primary', onClick: () => handleShowMaintenanceDetails(equipment) }, { label: t('actions.edit'), icon: Edit, priority: 'secondary', onClick: () => handleEditEquipment(equipment.id) }, { label: t('actions.view_history'), icon: History, priority: 'secondary', onClick: () => handleViewMaintenanceHistory(equipment.id) }, { label: t('actions.schedule_maintenance'), icon: Wrench, priority: 'secondary', onClick: () => handleScheduleMaintenance(equipment.id) } ]} /> ); })}
{/* Empty State */} {filteredEquipment.length === 0 && (

{t('common:forms.no_results')}

{t('common:forms.empty_state')}

)} {/* Maintenance Details Modal */} {selectedItem && showMaintenanceModal && (

{selectedItem.name}

{selectedItem.model} - {selectedItem.serialNumber}

{/* Equipment Status */}

{t('fields.status')}

{t(`equipment_status.${selectedItem.status}`)}

{t('fields.efficiency')}

{selectedItem.efficiency}%
{/* Maintenance Information */}

{t('maintenance.title')}

{t('maintenance.last')}

{new Date(selectedItem.lastMaintenance).toLocaleDateString('es-ES')}

{t('maintenance.next')}

{new Date(selectedItem.nextMaintenance).toLocaleDateString('es-ES')}

{t('maintenance.interval')}

{selectedItem.maintenanceInterval} {t('common:units.days')}

{new Date(selectedItem.nextMaintenance).getTime() < new Date().getTime() && (
{t('maintenance.overdue')}
)}
{/* Active Alerts */} {selectedItem.alerts.filter(a => !a.acknowledged).length > 0 && (

{t('alerts.title')}

{selectedItem.alerts.filter(a => !a.acknowledged).map((alert) => (
{alert.message}
{new Date(alert.timestamp).toLocaleString('es-ES')}
))}
)} {/* Maintenance History */}

{t('maintenance.history')}

{selectedItem.maintenanceHistory.map((history) => (

{history.description}

{new Date(history.date).toLocaleDateString('es-ES')} - {history.technician}

{t(`maintenance.type.${history.type}`)}
{t('common:actions.cost')}: €{history.cost} {t('fields.uptime')}: {history.downtime}h
{history.partsUsed.length > 0 && (
{t('fields.parts')}:
{history.partsUsed.map((part, index) => ( {part} ))}
)}
))}
)} {/* Equipment Modal */} {showEquipmentModal && ( { setShowEquipmentModal(false); setSelectedEquipment(null); }} equipment={selectedEquipment} onSave={handleSaveEquipment} mode={equipmentModalMode} /> )}
); }; export default MaquinariaPage;