import React, { useState, useMemo } from 'react'; import { Plus, Clock, AlertCircle, CheckCircle, Timer, ChefHat, Eye, Edit, Package, PlusCircle } from 'lucide-react'; import { Button, StatsGrid, EditViewModal, Toggle, SearchAndFilter, type FilterConfig } from '../../../../components/ui'; import { statusColors } from '../../../../styles/colors'; import { formatters } from '../../../../components/ui/Stats/StatsPresets'; import { LoadingSpinner } from '../../../../components/ui'; import { PageHeader } from '../../../../components/layout'; import { ProductionSchedule, CreateProductionBatchModal, ProductionStatusCard, QualityCheckModal, CompactProcessStageTracker } from '../../../../components/domain/production'; import { useCurrentTenant } from '../../../../stores/tenant.store'; import { useProductionDashboard, useActiveBatches, useCreateProductionBatch, useUpdateBatchStatus, productionService } from '../../../../api'; import type { ProductionBatchResponse, ProductionBatchCreate, ProductionBatchStatusUpdate } from '../../../../api'; import { ProductionStatusEnum, ProductionPriorityEnum } from '../../../../api'; import { useTranslation } from 'react-i18next'; import { ProcessStage } from '../../../../api/types/qualityTemplates'; const ProductionPage: React.FC = () => { const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState(''); const [priorityFilter, setPriorityFilter] = useState(''); const [selectedBatch, setSelectedBatch] = useState(null); const [showBatchModal, setShowBatchModal] = useState(false); const [showCreateModal, setShowCreateModal] = useState(false); const [showQualityModal, setShowQualityModal] = useState(false); const [modalMode, setModalMode] = useState<'view' | 'edit'>('view'); const currentTenant = useCurrentTenant(); const tenantId = currentTenant?.id || ''; const { t } = useTranslation(['production', 'common']); // API Data const { data: dashboardData, isLoading: dashboardLoading, error: dashboardError } = useProductionDashboard(tenantId); const { data: activeBatchesData, isLoading: batchesLoading, error: batchesError } = useActiveBatches(tenantId); // Mutations const createBatchMutation = useCreateProductionBatch(); const updateBatchStatusMutation = useUpdateBatchStatus(); // Handlers const handleCreateBatch = async (batchData: ProductionBatchCreate) => { try { await createBatchMutation.mutateAsync({ tenantId, batchData }); } catch (error) { console.error('Error creating production batch:', error); throw error; } }; // Stage management handlers const handleStageAdvance = async (batchId: string, currentStage: ProcessStage) => { const stages = Object.values(ProcessStage); const currentIndex = stages.indexOf(currentStage); const nextStage = stages[currentIndex + 1]; if (nextStage) { try { await updateBatchStatusMutation.mutateAsync({ batchId, updates: { current_process_stage: nextStage, process_stage_history: { [currentStage]: { end_time: new Date().toISOString() }, [nextStage]: { start_time: new Date().toISOString() } } } }); } catch (error) { console.error('Error advancing stage:', error); } } else { // Final stage - mark as completed await updateBatchStatusMutation.mutateAsync({ batchId, updates: { status: ProductionStatusEnum.COMPLETED } }); } }; const handleStageStart = async (batchId: string, stage: ProcessStage) => { try { await updateBatchStatusMutation.mutateAsync({ batchId, updates: { status: ProductionStatusEnum.IN_PROGRESS, current_process_stage: stage, process_stage_history: { [stage]: { start_time: new Date().toISOString() } } } }); } catch (error) { console.error('Error starting stage:', error); } }; const handleQualityCheckForStage = (batch: ProductionBatchResponse, stage: ProcessStage) => { setSelectedBatch(batch); setShowQualityModal(true); // The QualityCheckModal should be enhanced to handle stage-specific checks }; // Helper function to generate mock process stage data for the selected batch const generateMockProcessStageData = (batch: ProductionBatchResponse) => { // Mock data based on batch status - this would come from the API in real implementation const mockProcessStage = { current: batch.status === ProductionStatusEnum.PENDING ? 'mixing' as const : batch.status === ProductionStatusEnum.IN_PROGRESS ? 'baking' as const : batch.status === ProductionStatusEnum.QUALITY_CHECK ? 'cooling' as const : 'finishing' as const, history: batch.status !== ProductionStatusEnum.PENDING ? [ { stage: 'mixing' as const, timestamp: batch.actual_start_time || batch.planned_start_time, duration: 30 }, ...(batch.status === ProductionStatusEnum.IN_PROGRESS || batch.status === ProductionStatusEnum.QUALITY_CHECK || batch.status === ProductionStatusEnum.COMPLETED ? [ { stage: 'proofing' as const, timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), duration: 90 }, { stage: 'shaping' as const, timestamp: new Date(Date.now() - 1 * 60 * 60 * 1000).toISOString(), duration: 15 } ] : []), ...(batch.status === ProductionStatusEnum.QUALITY_CHECK || batch.status === ProductionStatusEnum.COMPLETED ? [ { stage: 'baking' as const, timestamp: new Date(Date.now() - 30 * 60 * 1000).toISOString(), duration: 45 } ] : []) ] : [], pendingQualityChecks: batch.status === ProductionStatusEnum.IN_PROGRESS ? [ { id: 'qc1', name: 'Control de temperatura interna', stage: 'baking' as const, isRequired: true, isCritical: true, status: 'pending' as const, checkType: 'temperature' as const } ] : batch.status === ProductionStatusEnum.QUALITY_CHECK ? [ { id: 'qc2', name: 'Inspección visual final', stage: 'cooling' as const, isRequired: true, isCritical: false, status: 'pending' as const, checkType: 'visual' as const } ] : [], completedQualityChecks: batch.status === ProductionStatusEnum.COMPLETED ? [ { id: 'qc1', name: 'Control de temperatura interna', stage: 'baking' as const, isRequired: true, isCritical: true, status: 'completed' as const, checkType: 'temperature' as const }, { id: 'qc2', name: 'Inspección visual final', stage: 'cooling' as const, isRequired: true, isCritical: false, status: 'completed' as const, checkType: 'visual' as const } ] : batch.status === ProductionStatusEnum.IN_PROGRESS ? [ { id: 'qc3', name: 'Verificación de masa', stage: 'mixing' as const, isRequired: true, isCritical: false, status: 'completed' as const, checkType: 'visual' as const } ] : [] }; return mockProcessStage; }; const batches = activeBatchesData?.batches || []; const filteredBatches = useMemo(() => { let filtered = batches; // Apply search filter if (searchQuery) { const searchLower = searchQuery.toLowerCase(); filtered = filtered.filter(batch => batch.product_name.toLowerCase().includes(searchLower) || batch.batch_number.toLowerCase().includes(searchLower) || (batch.staff_assigned && batch.staff_assigned.some(staff => staff.toLowerCase().includes(searchLower) )) ); } // Apply status filter if (statusFilter) { filtered = filtered.filter(batch => batch.status === statusFilter); } // Apply priority filter if (priorityFilter) { filtered = filtered.filter(batch => batch.priority === priorityFilter); } return filtered; }, [batches, searchQuery, statusFilter, priorityFilter]); // Calculate production stats from real data const productionStats = useMemo(() => { if (!dashboardData) { return { activeBatches: 0, todaysTarget: 0, capacityUtilization: 0, onTimeCompletion: 0, qualityScore: 0, totalOutput: 0, efficiency: 0 }; } return { activeBatches: dashboardData.active_batches || 0, todaysTarget: dashboardData.todays_production_plan?.length || 0, capacityUtilization: Math.round(dashboardData.capacity_utilization || 0), onTimeCompletion: Math.round(dashboardData.on_time_completion_rate || 0), qualityScore: Math.round(dashboardData.average_quality_score || 0), totalOutput: dashboardData.total_output_today || 0, efficiency: Math.round(dashboardData.efficiency_percentage || 0) }; }, [dashboardData]); // Loading state if (!tenantId || dashboardLoading || batchesLoading) { return (
); } // Error state if (dashboardError || batchesError) { return (

Error al cargar la producción

{(dashboardError || batchesError)?.message || 'Ha ocurrido un error inesperado'}

); } return (
{/* Production Stats */} = 80 ? 'success' as const : 'warning' as const, icon: Timer, }, { title: 'Completado a Tiempo', value: `${productionStats.onTimeCompletion}%`, variant: productionStats.onTimeCompletion >= 90 ? 'success' as const : 'error' as const, icon: CheckCircle, }, { title: 'Puntuación Calidad', value: `${productionStats.qualityScore}%`, variant: productionStats.qualityScore >= 85 ? 'success' as const : 'warning' as const, icon: Package, }, { title: 'Producción Hoy', value: formatters.number(productionStats.totalOutput), variant: 'info' as const, icon: ChefHat, }, { title: 'Eficiencia', value: `${productionStats.efficiency}%`, variant: productionStats.efficiency >= 75 ? 'success' as const : 'warning' as const, icon: Timer, }, ]} columns={3} /> {/* Production Batches Section - No tabs needed */} <> {/* Search and Filter Controls */} setStatusFilter(value as string), placeholder: 'Todos los estados', options: Object.values(ProductionStatusEnum).map(status => ({ value: status, label: status.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) })) }, { key: 'priority', label: 'Prioridad', type: 'dropdown', value: priorityFilter, onChange: (value) => setPriorityFilter(value as string), placeholder: 'Todas las prioridades', options: Object.values(ProductionPriorityEnum).map(priority => ({ value: priority, label: priority.charAt(0).toUpperCase() + priority.slice(1).toLowerCase() })) } ] as FilterConfig[]} /> {/* Production Batches Grid */}
{filteredBatches.map((batch) => ( { setSelectedBatch(batch); setModalMode('view'); setShowBatchModal(true); }} onEdit={(batch) => { setSelectedBatch(batch); setModalMode('edit'); setShowBatchModal(true); }} onStart={async (batch) => { try { await updateBatchStatusMutation.mutateAsync({ batchId: batch.id, updates: { status: ProductionStatusEnum.IN_PROGRESS } }); } catch (error) { console.error('Error starting batch:', error); } }} onPause={async (batch) => { try { await updateBatchStatusMutation.mutateAsync({ batchId: batch.id, updates: { status: ProductionStatusEnum.ON_HOLD } }); } catch (error) { console.error('Error pausing batch:', error); } }} onComplete={async (batch) => { try { await updateBatchStatusMutation.mutateAsync({ batchId: batch.id, updates: { status: ProductionStatusEnum.QUALITY_CHECK } }); } catch (error) { console.error('Error completing batch:', error); } }} onCancel={async (batch) => { try { await updateBatchStatusMutation.mutateAsync({ batchId: batch.id, updates: { status: ProductionStatusEnum.CANCELLED } }); } catch (error) { console.error('Error cancelling batch:', error); } }} onQualityCheck={(batch) => { setSelectedBatch(batch); setShowQualityModal(true); }} onViewHistory={(batch) => { setSelectedBatch(batch); setModalMode('view'); setShowBatchModal(true); }} showDetailedProgress={true} /> ))}
{/* Empty State */} {filteredBatches.length === 0 && (

No se encontraron lotes de producción

{batches.length === 0 ? 'No hay lotes de producción activos. Crea el primer lote para comenzar.' : 'Intenta ajustar la búsqueda o crear un nuevo lote de producción' }

)} {/* Production Batch Modal */} {showBatchModal && selectedBatch && ( { setShowBatchModal(false); setSelectedBatch(null); setModalMode('view'); }} mode={modalMode} onModeChange={setModalMode} title={selectedBatch.product_name} subtitle={`Lote de Producción #${selectedBatch.batch_number}`} statusIndicator={{ color: statusColors.inProgress.primary, text: t(`production:status.${selectedBatch.status.toLowerCase()}`), icon: Package }} size="lg" sections={[ { title: 'Información General', icon: Package, fields: [ { label: 'Cantidad Planificada', value: `${selectedBatch.planned_quantity} unidades`, highlight: true }, { label: 'Cantidad Real', value: selectedBatch.actual_quantity ? `${selectedBatch.actual_quantity} unidades` : 'Pendiente', editable: modalMode === 'edit', type: 'number' }, { label: 'Prioridad', value: selectedBatch.priority, type: 'select', editable: modalMode === 'edit', options: Object.values(ProductionPriorityEnum).map(value => ({ value, label: t(`production:priority.${value.toLowerCase()}`) })) }, { label: 'Estado', value: selectedBatch.status, type: 'select', editable: modalMode === 'edit', options: Object.values(ProductionStatusEnum).map(value => ({ value, label: t(`production:status.${value.toLowerCase()}`) })) }, { label: 'Personal Asignado', value: selectedBatch.staff_assigned?.join(', ') || 'No asignado', editable: modalMode === 'edit', type: 'text' } ] }, { title: 'Cronograma', icon: Clock, fields: [ { label: 'Inicio Planificado', value: selectedBatch.planned_start_time, type: 'datetime' }, { label: 'Fin Planificado', value: selectedBatch.planned_end_time, type: 'datetime' }, { label: 'Inicio Real', value: selectedBatch.actual_start_time || 'Pendiente', type: 'datetime' }, { label: 'Fin Real', value: selectedBatch.actual_end_time || 'Pendiente', type: 'datetime' } ] }, { title: 'Seguimiento de Proceso', icon: Timer, fields: [ { label: '', value: ( handleStageAdvance(selectedBatch.id, currentStage)} onQualityCheck={(checkId) => { setShowQualityModal(true); console.log('Opening quality check:', checkId); }} className="w-full" /> ), span: 2 } ] }, { title: 'Calidad y Costos', icon: CheckCircle, fields: [ { label: 'Puntuación de Calidad', value: selectedBatch.quality_score ? `${selectedBatch.quality_score}/10` : 'Pendiente' }, { label: 'Rendimiento', value: selectedBatch.yield_percentage ? `${selectedBatch.yield_percentage}%` : 'Calculando...' }, { label: 'Costo Estimado', value: selectedBatch.estimated_cost || 0, type: 'currency' }, { label: 'Costo Real', value: selectedBatch.actual_cost || 0, type: 'currency' } ] } ]} onSave={async () => { try { // Implementation would depend on specific fields changed console.log('Saving batch changes:', selectedBatch.id); // await updateBatchStatusMutation.mutateAsync({ // batchId: selectedBatch.id, // updates: selectedBatch // }); setShowBatchModal(false); setSelectedBatch(null); setModalMode('view'); } catch (error) { console.error('Error saving batch:', error); } }} onFieldChange={(sectionIndex, fieldIndex, value) => { if (!selectedBatch) return; const sections = [ ['planned_quantity', 'actual_quantity', 'priority', 'status', 'staff_assigned'], ['planned_start_time', 'planned_end_time', 'actual_start_time', 'actual_end_time'], ['quality_score', 'yield_percentage', 'estimated_cost', 'actual_cost'] ]; // Get the field names from modal sections const sectionFields = [ { fields: ['planned_quantity', 'actual_quantity', 'priority', 'status', 'staff_assigned'] }, { fields: ['planned_start_time', 'planned_end_time', 'actual_start_time', 'actual_end_time'] }, { fields: ['quality_score', 'yield_percentage', 'estimated_cost', 'actual_cost'] } ]; const fieldMapping: Record = { 'Cantidad Real': 'actual_quantity', 'Prioridad': 'priority', 'Estado': 'status', 'Personal Asignado': 'staff_assigned' }; // Get section labels to map back to field names const sectionLabels = [ ['Cantidad Planificada', 'Cantidad Real', 'Prioridad', 'Estado', 'Personal Asignado'], ['Inicio Planificado', 'Fin Planificado', 'Inicio Real', 'Fin Real'], ['Puntuación de Calidad', 'Rendimiento', 'Costo Estimado', 'Costo Real'] ]; const fieldLabel = sectionLabels[sectionIndex]?.[fieldIndex]; const propertyName = fieldMapping[fieldLabel] || sectionFields[sectionIndex]?.fields[fieldIndex]; if (propertyName) { let processedValue: any = value; if (propertyName === 'staff_assigned' && typeof value === 'string') { processedValue = value.split(',').map(s => s.trim()).filter(s => s.length > 0); } else if (propertyName === 'actual_quantity') { processedValue = parseFloat(value as string) || 0; } setSelectedBatch({ ...selectedBatch, [propertyName]: processedValue }); } }} /> )} {/* Create Production Batch Modal */} setShowCreateModal(false)} onCreateBatch={handleCreateBatch} /> {/* Quality Check Modal */} {showQualityModal && selectedBatch && ( { setShowQualityModal(false); setSelectedBatch(null); }} batch={selectedBatch} onComplete={async (result) => { console.log('Quality check completed:', result); // Optionally update batch status to completed or quality_passed try { await updateBatchStatusMutation.mutateAsync({ batchId: selectedBatch.id, updates: { status: result.overallPass ? ProductionStatusEnum.COMPLETED : ProductionStatusEnum.ON_HOLD } }); } catch (error) { console.error('Error updating batch status after quality check:', error); } }} /> )}
); }; export default ProductionPage;