/** * ProductionStatusBlock - Block 4: "Estado de Produccion" * * Displays today's production overview: * - Late to start batches (should have started but haven't) * - Currently running batches (IN_PROGRESS) * - Pending batches for today * - AI reasoning for batch scheduling */ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { AlertTriangle, Brain, CheckCircle2, ChevronDown, ChevronUp, Clock, Factory, Play, Timer, } from 'lucide-react'; interface ProductionStatusBlockProps { lateToStartBatches?: any[]; runningBatches?: any[]; pendingBatches?: any[]; onStartBatch?: (batchId: string) => Promise; onViewBatch?: (batchId: string) => void; loading?: boolean; } export function ProductionStatusBlock({ lateToStartBatches = [], runningBatches = [], pendingBatches = [], onStartBatch, onViewBatch, loading, }: ProductionStatusBlockProps) { const { t } = useTranslation(['dashboard', 'common', 'production']); const [expandedReasoningId, setExpandedReasoningId] = useState(null); const [processingId, setProcessingId] = useState(null); if (loading) { return (
); } const hasLate = lateToStartBatches.length > 0; const hasRunning = runningBatches.length > 0; const hasPending = pendingBatches.length > 0; const hasAnyProduction = hasLate || hasRunning || hasPending; const totalCount = lateToStartBatches.length + runningBatches.length + pendingBatches.length; // Determine header status const status = hasLate ? 'error' : hasRunning ? 'info' : hasPending ? 'warning' : 'success'; const statusStyles = { success: { iconBg: 'bg-[var(--color-success-100)]', iconColor: 'text-[var(--color-success-600)]', }, warning: { iconBg: 'bg-[var(--color-warning-100)]', iconColor: 'text-[var(--color-warning-600)]', }, info: { iconBg: 'bg-[var(--color-info-100)]', iconColor: 'text-[var(--color-info-600)]', }, error: { iconBg: 'bg-[var(--color-error-100)]', iconColor: 'text-[var(--color-error-600)]', }, }; const styles = statusStyles[status]; // Handle start batch const handleStartBatch = async (batchId: string) => { if (!onStartBatch || processingId) return; setProcessingId(batchId); try { await onStartBatch(batchId); } finally { setProcessingId(null); } }; // Toggle reasoning expansion const toggleReasoning = (batchId: string) => { setExpandedReasoningId(expandedReasoningId === batchId ? null : batchId); }; // Format AI reasoning const formatReasoning = (batch: any): string | null => { const reasoningData = batch.reasoning_data; if (!reasoningData) return null; if (typeof reasoningData === 'string') return reasoningData; if (reasoningData.type === 'forecast_demand') { return t('dashboard:new_dashboard.production_status.reasoning.forecast_demand', { product: reasoningData.parameters?.product_name || batch.product_name, demand: reasoningData.parameters?.predicted_demand || batch.planned_quantity, }); } if (reasoningData.type === 'customer_order') { return t('dashboard:new_dashboard.production_status.reasoning.customer_order', { customer: reasoningData.parameters?.customer_name || 'customer', }); } if (reasoningData.summary) return reasoningData.summary; return null; }; // Format time const formatTime = (isoString: string | null | undefined) => { if (!isoString) return '--:--'; const date = new Date(isoString); return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }; // Calculate progress percentage for running batches const calculateProgress = (batch: any): number => { if (!batch.actual_start_time || !batch.planned_end_time) return 0; const start = new Date(batch.actual_start_time).getTime(); const end = new Date(batch.planned_end_time).getTime(); const now = Date.now(); if (now >= end) return 100; if (now <= start) return 0; return Math.round(((now - start) / (end - start)) * 100); }; // Render a batch item const renderBatchItem = (batch: any, type: 'late' | 'running' | 'pending', index: number, total: number) => { const batchId = batch.id || batch.batch_id; const isProcessing = processingId === batchId; const isExpanded = expandedReasoningId === batchId; const reasoning = formatReasoning(batch); const progress = type === 'running' ? calculateProgress(batch) : 0; const typeStyles = { late: { timeBg: 'bg-[var(--color-error-100)]', timeColor: 'text-[var(--color-error-700)]', icon: , }, running: { timeBg: 'bg-[var(--color-info-100)]', timeColor: 'text-[var(--color-info-700)]', icon: , }, pending: { timeBg: 'bg-[var(--color-warning-100)]', timeColor: 'text-[var(--color-warning-700)]', icon: , }, }; const batchStyles = typeStyles[type]; return (
{/* Batch Info */}
{batchStyles.icon} {batch.product_name || 'Unknown Product'} {reasoning && ( )}

{t('dashboard:new_dashboard.production_status.batch_info', { number: batch.batch_number || batchId?.slice(0, 8), quantity: batch.planned_quantity || 0, })}

{/* Time/Status Badge */}
{type === 'late' && (
{t('dashboard:new_dashboard.production_status.should_have_started', { time: formatTime(batch.planned_start_time), })}
)} {type === 'running' && ( <>
{t('dashboard:new_dashboard.production_status.started_at', { time: formatTime(batch.actual_start_time), })}
{/* Progress Bar */}
{progress}%
)} {type === 'pending' && batch.planned_start_time && (
{t('dashboard:new_dashboard.production_status.starts_at', { time: formatTime(batch.planned_start_time), })}
)}
{/* Actions */}
{(type === 'late' || type === 'pending') && onStartBatch && ( )} {type === 'running' && onViewBatch && ( )}
{/* AI Reasoning (Expanded) */} {isExpanded && reasoning && (

{t('dashboard:new_dashboard.production_status.ai_reasoning')}

{reasoning}

)}
); }; return (
{/* Header */}
{/* Icon */}
{hasLate ? ( ) : hasAnyProduction ? ( ) : ( )}
{/* Title & Count */}

{t('dashboard:new_dashboard.production_status.title')}

{hasAnyProduction ? t('dashboard:new_dashboard.production_status.count', { count: totalCount }) : t('dashboard:new_dashboard.production_status.no_production')}

{/* Count Badges */}
{hasLate && (
{lateToStartBatches.length} {t('dashboard:new_dashboard.production_status.late_badge')}
)} {hasRunning && (
{runningBatches.length} {t('dashboard:new_dashboard.production_status.running_badge')}
)} {hasPending && (
{pendingBatches.length}
)}
{/* Content */} {hasAnyProduction ? (
{/* Late to Start Section */} {hasLate && (

{t('dashboard:new_dashboard.production_status.late_section')}

{lateToStartBatches.map((batch, index) => renderBatchItem(batch, 'late', index, lateToStartBatches.length) )}
)} {/* Running Section */} {hasRunning && (

{t('dashboard:new_dashboard.production_status.running_section')}

{runningBatches.map((batch, index) => renderBatchItem(batch, 'running', index, runningBatches.length) )}
)} {/* Pending Section */} {hasPending && (

{t('dashboard:new_dashboard.production_status.pending_section')}

{pendingBatches.map((batch, index) => renderBatchItem(batch, 'pending', index, pendingBatches.length) )}
)}
) : ( /* Empty State */

{t('dashboard:new_dashboard.production_status.all_clear')}

)}
); } export default ProductionStatusBlock;