// ================================================================ // frontend/src/components/dashboard/ExecutionProgressTracker.tsx // ================================================================ /** * Execution Progress Tracker - Plan vs Actual * * Shows how today's execution is progressing vs the plan. * Helps identify bottlenecks early (e.g., deliveries running late). * * Features: * - Production progress (plan vs actual batches) * - Delivery status (received, pending, overdue) * - Approval tracking * - "What's next" preview * - Status indicators (on_track, at_risk, completed) */ import React from 'react'; import { Package, Truck, CheckCircle, Clock, AlertCircle, TrendingUp, Calendar, } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { formatTime as formatTimeUtil } from '../../utils/date'; // ============================================================ // Types // ============================================================ export interface ProductionProgress { status: 'no_plan' | 'completed' | 'on_track' | 'at_risk'; total: number; completed: number; inProgress: number; pending: number; inProgressBatches?: Array<{ id: string; batchNumber: string; productName: string; quantity: number; actualStartTime: string; estimatedCompletion: string; }>; nextBatch?: { productName: string; plannedStart: string; // ISO datetime batchNumber: string; }; } export interface DeliveryProgress { status: 'no_deliveries' | 'completed' | 'on_track' | 'at_risk'; total: number; received: number; pending: number; overdue: number; } export interface ApprovalProgress { status: 'completed' | 'on_track' | 'at_risk'; pending: number; } export interface ExecutionProgress { production: ProductionProgress; deliveries: DeliveryProgress; approvals: ApprovalProgress; } interface ExecutionProgressTrackerProps { progress: ExecutionProgress | null | undefined; loading?: boolean; } // ============================================================ // Helper Functions // ============================================================ function getStatusColor(status: string): { bg: string; border: string; text: string; icon: string; } { switch (status) { case 'completed': return { bg: 'var(--color-success-50)', border: 'var(--color-success-300)', text: 'var(--color-success-900)', icon: 'var(--color-success-600)', }; case 'on_track': return { bg: 'var(--color-info-50)', border: 'var(--color-info-300)', text: 'var(--color-info-900)', icon: 'var(--color-info-600)', }; case 'at_risk': return { bg: 'var(--color-error-50)', border: 'var(--color-error-300)', text: 'var(--color-error-900)', icon: 'var(--color-error-600)', }; case 'no_plan': case 'no_deliveries': return { bg: 'var(--bg-secondary)', border: 'var(--border-secondary)', text: 'var(--text-secondary)', icon: 'var(--text-tertiary)', }; default: return { bg: 'var(--bg-secondary)', border: 'var(--border-secondary)', text: 'var(--text-primary)', icon: 'var(--text-secondary)', }; } } function formatTime(isoDate: string): string { return formatTimeUtil(isoDate, 'HH:mm'); } // ============================================================ // Sub-Components // ============================================================ interface SectionProps { title: string; icon: React.ElementType; status: string; statusLabel: string; children: React.ReactNode; } function Section({ title, icon: Icon, status, statusLabel, children }: SectionProps) { const colors = getStatusColor(status); return (
{/* Section Header */}

{title}

{statusLabel}
{/* Section Content */} {children}
); } // ============================================================ // Main Component // ============================================================ export function ExecutionProgressTracker({ progress, loading, }: ExecutionProgressTrackerProps) { const { t } = useTranslation(['dashboard', 'common']); if (loading) { return (
); } if (!progress) { return null; } return (
{/* Header with Hero Icon */}
{/* Hero Icon */}
{/* Title */}

{t('dashboard:execution_progress.title')}

{t('dashboard:execution_progress.subtitle')}

{/* Production Section */}
{progress.production.status === 'no_plan' ? (

{t('dashboard:execution_progress.no_production_plan')}

) : ( <> {/* Progress Bar */}
{progress.production.completed} / {progress.production.total} {t('dashboard:execution_progress.batches_complete')} {Math.round((progress.production.completed / progress.production.total) * 100)}%
{/* Status Breakdown */}
{t('dashboard:execution_progress.completed')}: {progress.production.completed}
{t('dashboard:execution_progress.in_progress')}: {progress.production.inProgress} {progress.production.inProgressBatches && progress.production.inProgressBatches.length > 0 && (
{progress.production.inProgressBatches.map((batch) => (
• {batch.productName} ({batch.batchNumber})
))}
)}
{t('dashboard:execution_progress.pending')}: {progress.production.pending}
{/* Next Batch */} {progress.production.nextBatch && (
{t('dashboard:execution_progress.whats_next')}

{progress.production.nextBatch.productName}

{progress.production.nextBatch.batchNumber} · {t('dashboard:execution_progress.starts_at')}{' '} {formatTime(progress.production.nextBatch.plannedStart)}

)} )}
{/* Deliveries Section */}
{progress.deliveries.status === 'no_deliveries' ? (

{t('dashboard:execution_progress.no_deliveries_today')}

) : (
{progress.deliveries.received}
{t('dashboard:execution_progress.received')}
{progress.deliveries.pending}
{t('dashboard:execution_progress.pending')}
{progress.deliveries.overdue}
{t('dashboard:execution_progress.overdue')}
)}
{/* Approvals Section */}
{t('dashboard:execution_progress.pending_approvals')} {progress.approvals.pending}
); }