import React from 'react'; import { TrendingUp, TrendingDown, AlertTriangle, CheckCircle, Target, Activity, Clock, PhoneForwarded, Users, Bot, ChevronRight, BarChart3, Cpu, Map, Zap, ArrowRight, Calendar } from 'lucide-react'; import type { AnalysisData, Finding, DrilldownDataPoint, HeatmapDataPoint } from '../../types'; import type { TabId } from '../DashboardHeader'; import { Card, Badge, SectionHeader, DistributionBar, Stat, Button, } from '../ui'; import { cn, COLORS, STATUS_CLASSES, getStatusFromScore, formatCurrency, formatNumber, formatPercent, } from '../../config/designSystem'; interface ExecutiveSummaryTabProps { data: AnalysisData; onTabChange?: (tab: TabId) => void; } // ============================================ // BENCHMARKS DE INDUSTRIA // ============================================ type IndustryKey = 'aerolineas' | 'telecomunicaciones' | 'banca' | 'utilities' | 'retail' | 'general'; interface BenchmarkMetric { p25: number; p50: number; p75: number; p90: number; unidad: string; invertida: boolean; } interface IndustryBenchmarks { nombre: string; fuente: string; metricas: { aht: BenchmarkMetric; fcr: BenchmarkMetric; abandono: BenchmarkMetric; transfer: BenchmarkMetric; cpi: BenchmarkMetric; }; } const BENCHMARKS_INDUSTRIA: Record = { aerolineas: { nombre: 'Aerolíneas', fuente: 'COPC 2024, Dimension Data Global CX Report 2024', metricas: { aht: { p25: 320, p50: 380, p75: 450, p90: 520, unidad: 's', invertida: true }, fcr: { p25: 55, p50: 68, p75: 78, p90: 85, unidad: '%', invertida: false }, abandono: { p25: 8, p50: 5, p75: 3, p90: 2, unidad: '%', invertida: true }, transfer: { p25: 18, p50: 12, p75: 8, p90: 5, unidad: '%', invertida: true }, cpi: { p25: 4.50, p50: 3.50, p75: 2.80, p90: 2.20, unidad: '€', invertida: true } } }, telecomunicaciones: { nombre: 'Telecomunicaciones', fuente: 'Contact Babel UK Report 2024, ICMI Benchmark Study', metricas: { aht: { p25: 380, p50: 420, p75: 500, p90: 600, unidad: 's', invertida: true }, fcr: { p25: 50, p50: 65, p75: 75, p90: 82, unidad: '%', invertida: false }, abandono: { p25: 10, p50: 6, p75: 4, p90: 2, unidad: '%', invertida: true }, transfer: { p25: 22, p50: 15, p75: 10, p90: 6, unidad: '%', invertida: true }, cpi: { p25: 5.00, p50: 4.00, p75: 3.20, p90: 2.50, unidad: '€', invertida: true } } }, banca: { nombre: 'Banca & Finanzas', fuente: 'Deloitte Banking Benchmark 2024, McKinsey CX Survey', metricas: { aht: { p25: 280, p50: 340, p75: 420, p90: 500, unidad: 's', invertida: true }, fcr: { p25: 58, p50: 72, p75: 82, p90: 88, unidad: '%', invertida: false }, abandono: { p25: 6, p50: 4, p75: 2, p90: 1, unidad: '%', invertida: true }, transfer: { p25: 15, p50: 10, p75: 6, p90: 3, unidad: '%', invertida: true }, cpi: { p25: 6.00, p50: 4.50, p75: 3.50, p90: 2.80, unidad: '€', invertida: true } } }, utilities: { nombre: 'Utilities & Energía', fuente: 'Dimension Data 2024, Utilities CX Benchmark', metricas: { aht: { p25: 350, p50: 400, p75: 480, p90: 560, unidad: 's', invertida: true }, fcr: { p25: 52, p50: 67, p75: 77, p90: 84, unidad: '%', invertida: false }, abandono: { p25: 9, p50: 6, p75: 4, p90: 2, unidad: '%', invertida: true }, transfer: { p25: 20, p50: 14, p75: 9, p90: 5, unidad: '%', invertida: true }, cpi: { p25: 4.20, p50: 3.30, p75: 2.60, p90: 2.00, unidad: '€', invertida: true } } }, retail: { nombre: 'Retail & E-commerce', fuente: 'Zendesk CX Trends 2024, Salesforce State of Service', metricas: { aht: { p25: 240, p50: 300, p75: 380, p90: 450, unidad: 's', invertida: true }, fcr: { p25: 60, p50: 73, p75: 82, p90: 89, unidad: '%', invertida: false }, abandono: { p25: 7, p50: 4, p75: 2, p90: 1, unidad: '%', invertida: true }, transfer: { p25: 12, p50: 8, p75: 5, p90: 3, unidad: '%', invertida: true }, cpi: { p25: 3.80, p50: 2.80, p75: 2.10, p90: 1.60, unidad: '€', invertida: true } } }, general: { nombre: 'Cross-Industry', fuente: 'Dimension Data Global CX Benchmark 2024', metricas: { aht: { p25: 320, p50: 380, p75: 460, p90: 540, unidad: 's', invertida: true }, fcr: { p25: 55, p50: 70, p75: 80, p90: 87, unidad: '%', invertida: false }, abandono: { p25: 8, p50: 5, p75: 3, p90: 2, unidad: '%', invertida: true }, transfer: { p25: 18, p50: 12, p75: 8, p90: 5, unidad: '%', invertida: true }, cpi: { p25: 4.50, p50: 3.50, p75: 2.80, p90: 2.20, unidad: '€', invertida: true } } } }; function calcularPercentilUsuario(valor: number, bench: BenchmarkMetric): number { const { p25, p50, p75, p90, invertida } = bench; if (invertida) { if (valor <= p90) return 95; if (valor <= p75) return 82; if (valor <= p50) return 60; if (valor <= p25) return 35; return 15; } else { if (valor >= p90) return 95; if (valor >= p75) return 82; if (valor >= p50) return 60; if (valor >= p25) return 35; return 15; } } function BenchmarkTable({ heatmapData }: { heatmapData: HeatmapDataPoint[] }) { const [selectedIndustry, setSelectedIndustry] = React.useState('aerolineas'); const benchmarks = BENCHMARKS_INDUSTRIA[selectedIndustry]; const totalVolume = heatmapData.reduce((sum, h) => sum + h.volume, 0); const operacion = { aht: totalVolume > 0 ? heatmapData.reduce((sum, h) => sum + h.aht_seconds * h.volume, 0) / totalVolume : 0, fcr: totalVolume > 0 ? heatmapData.reduce((sum, h) => sum + h.metrics.fcr * h.volume, 0) / totalVolume : 0, abandono: totalVolume > 0 ? heatmapData.reduce((sum, h) => sum + (h.metrics.abandonment_rate || 0) * h.volume, 0) / totalVolume : 0, transfer: totalVolume > 0 ? heatmapData.reduce((sum, h) => sum + (h.variability?.transfer_rate || 0) * h.volume, 0) / totalVolume : 0, cpi: 2.33 }; const getPercentileBadge = (percentile: number) => { if (percentile >= 90) return { label: 'Top 10%', color: 'bg-emerald-500 text-white' }; if (percentile >= 75) return { label: 'Top 25%', color: 'bg-emerald-100 text-emerald-700' }; if (percentile >= 50) return { label: 'Avg', color: 'bg-amber-100 text-amber-700' }; if (percentile >= 25) return { label: 'Below Avg', color: 'bg-orange-100 text-orange-700' }; return { label: 'Bottom 25%', color: 'bg-red-100 text-red-700' }; }; const metricsData = [ { id: 'aht', label: 'AHT (Tiempo Medio)', valor: operacion.aht, display: `${Math.round(operacion.aht)}s`, bench: benchmarks.metricas.aht }, { id: 'fcr', label: 'FCR (Resolución 1er Contacto)', valor: operacion.fcr, display: `${Math.round(operacion.fcr)}%`, bench: benchmarks.metricas.fcr }, { id: 'abandono', label: 'Tasa de Abandono', valor: operacion.abandono, display: `${operacion.abandono.toFixed(1)}%`, bench: benchmarks.metricas.abandono }, { id: 'transfer', label: 'Tasa de Transferencia', valor: operacion.transfer, display: `${operacion.transfer.toFixed(1)}%`, bench: benchmarks.metricas.transfer }, { id: 'cpi', label: 'Coste por Interacción', valor: operacion.cpi, display: `€${operacion.cpi.toFixed(2)}`, bench: benchmarks.metricas.cpi } ]; return (

Benchmark vs Industria

Fuente: {benchmarks.fuente}

{metricsData.map((m) => { const percentil = calcularPercentilUsuario(m.valor, m.bench); const badge = getPercentileBadge(percentil); return ( ); })}
Métrica Tu Op. P50 Posición
{m.label} {m.display} {m.bench.p50}{m.bench.unidad} {badge.label}
); } // ============================================ // PRINCIPALES HALLAZGOS // ============================================ interface Hallazgo { tipo: 'critico' | 'warning' | 'info'; texto: string; metrica?: string; } function generarHallazgos(data: AnalysisData): Hallazgo[] { const hallazgos: Hallazgo[] = []; const allQueues = data.drilldownData?.flatMap(s => s.originalQueues) || []; const totalVolume = allQueues.reduce((s, q) => s + q.volume, 0); // Llamadas fuera de horario (simulado - buscar en métricas si existe) const avgAHT = data.heatmapData.length > 0 ? data.heatmapData.reduce((sum, h) => sum + h.aht_seconds, 0) / data.heatmapData.length : 0; // Alta variabilidad const colasAltaVariabilidad = allQueues.filter(q => q.cv_aht > 100); if (colasAltaVariabilidad.length > 0) { const pctVolumen = (colasAltaVariabilidad.reduce((s, q) => s + q.volume, 0) / totalVolume) * 100; hallazgos.push({ tipo: 'critico', texto: `${colasAltaVariabilidad.length} colas con variabilidad crítica (CV >100%) representan ${pctVolumen.toFixed(0)}% del volumen`, metrica: 'CV AHT' }); } // Alto transfer const colasAltoTransfer = allQueues.filter(q => q.transfer_rate > 25); if (colasAltoTransfer.length > 0) { hallazgos.push({ tipo: 'warning', texto: `${colasAltoTransfer.length} colas con tasa de transferencia >25% - posible problema de routing o formación`, metrica: 'Transfer' }); } // Bajo FCR const colasBajoFCR = allQueues.filter(q => q.fcr_rate < 50); if (colasBajoFCR.length > 0) { hallazgos.push({ tipo: 'warning', texto: `${colasBajoFCR.length} colas con FCR <50% - clientes requieren múltiples contactos`, metrica: 'FCR' }); } // AHT elevado vs benchmark if (avgAHT > 400) { hallazgos.push({ tipo: 'warning', texto: `AHT promedio de ${Math.round(avgAHT)}s supera el benchmark de industria (380s)`, metrica: 'AHT' }); } // Colas Human-Only const colasHumanOnly = allQueues.filter(q => q.tier === 'HUMAN-ONLY'); if (colasHumanOnly.length > 0) { const pctHuman = (colasHumanOnly.reduce((s, q) => s + q.volume, 0) / totalVolume) * 100; hallazgos.push({ tipo: 'info', texto: `${colasHumanOnly.length} colas (${pctHuman.toFixed(0)}% volumen) requieren intervención humana completa`, metrica: 'Tier' }); } // Oportunidad de automatización const colasAutomate = allQueues.filter(q => q.tier === 'AUTOMATE'); if (colasAutomate.length > 0) { hallazgos.push({ tipo: 'info', texto: `${colasAutomate.length} colas listas para automatización con potencial de ahorro significativo`, metrica: 'Oportunidad' }); } return hallazgos.slice(0, 5); // Máximo 5 hallazgos } function PrincipalesHallazgos({ data }: { data: AnalysisData }) { const hallazgos = generarHallazgos(data); if (hallazgos.length === 0) return null; const getIcono = (tipo: string) => { if (tipo === 'critico') return ; if (tipo === 'warning') return ; return ; }; const getClase = (tipo: string) => { if (tipo === 'critico') return 'bg-red-50 border-red-200'; if (tipo === 'warning') return 'bg-amber-50 border-amber-200'; return 'bg-blue-50 border-blue-200'; }; return (

Principales Hallazgos

{hallazgos.map((h, idx) => (
{getIcono(h.tipo)}

{h.texto}

{h.metrica && ( {h.metrica} )}
))}
); } // ============================================ // CABECERA CON PERIODO ANALIZADO // ============================================ function CabeceraPeriodo({ data }: { data: AnalysisData }) { const totalInteractions = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); // Contar colas únicas (original_queue_id) desde drilldownData const uniqueQueues = data.drilldownData ? new Set(data.drilldownData.flatMap(d => d.originalQueues.map(q => q.original_queue_id))).size : data.heatmapData.length; // Contar líneas de negocio únicas (skills en drilldownData = queue_skill agrupado) const numLineasNegocio = data.drilldownData?.length || data.heatmapData.length; // Formatear fechas del periodo const formatPeriodo = () => { if (!data.dateRange?.min || !data.dateRange?.max) { return 'Periodo no especificado'; } const formatDate = (dateStr: string) => { try { const date = new Date(dateStr); return date.toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' }); } catch { return dateStr; } }; return `${formatDate(data.dateRange.min)} - ${formatDate(data.dateRange.max)}`; }; return (
Periodo: {formatPeriodo()}
{formatNumber(totalInteractions)} int. {uniqueQueues} colas {numLineasNegocio} LN
); } // ============================================ // v3.15: HEADLINE EJECUTIVO (Situación) // ============================================ function HeadlineEjecutivo({ totalInteracciones, oportunidadTotal, eficienciaScore, resolucionScore, satisfaccionScore }: { totalInteracciones: number; oportunidadTotal: number; eficienciaScore: number; resolucionScore: number; satisfaccionScore: number; }) { const getStatusLabel = (score: number): string => { if (score >= 80) return 'Óptimo'; if (score >= 60) return 'Aceptable'; return 'Crítico'; }; const getStatusVariant = (score: number): 'success' | 'warning' | 'critical' => { if (score >= 80) return 'success'; if (score >= 60) return 'warning'; return 'critical'; }; return (
{/* Título principal */}

Tu operación procesa{' '} {formatNumber(totalInteracciones)}{' '} interacciones

con oportunidad de{' '} {formatCurrency(oportunidadTotal)} {' '} en optimización

{/* Status Bar */}
Eficiencia: {getStatusLabel(eficienciaScore)}
Resolución: {getStatusLabel(resolucionScore)}
Satisfacción: {getStatusLabel(satisfaccionScore)}
); } // v3.15: Compact KPI Row Component function KeyMetricsCard({ totalInteractions, avgAHT, avgFCR, avgTransferRate, ahtBenchmark, fcrBenchmark }: { totalInteractions: number; avgAHT: number; avgFCR: number; avgTransferRate: number; ahtBenchmark?: number; fcrBenchmark?: number; }) { const getAHTStatus = (aht: number): { variant: 'success' | 'warning' | 'critical'; label: string } => { if (aht <= 420) return { variant: 'success', label: 'Bueno' }; if (aht <= 480) return { variant: 'warning', label: 'Aceptable' }; return { variant: 'critical', label: 'Alto' }; }; const getFCRStatus = (fcr: number): { variant: 'success' | 'warning' | 'critical'; label: string } => { if (fcr >= 75) return { variant: 'success', label: 'Bueno' }; if (fcr >= 65) return { variant: 'warning', label: 'Mejorable' }; return { variant: 'critical', label: 'Crítico' }; }; const ahtStatus = getAHTStatus(avgAHT); const fcrStatus = getFCRStatus(avgFCR); const transferStatus = avgTransferRate > 20 ? { variant: 'warning' as const, label: 'Alto' } : { variant: 'success' as const, label: 'OK' }; const metrics = [ { icon: Users, label: 'Interacciones', value: formatNumber(totalInteractions), sublabel: 'en el periodo', status: null }, { icon: Clock, label: 'AHT Promedio', value: `${Math.floor(avgAHT / 60)}:${String(avgAHT % 60).padStart(2, '0')}`, sublabel: ahtBenchmark ? `Benchmark: ${Math.floor(ahtBenchmark / 60)}:${String(Math.round(ahtBenchmark) % 60).padStart(2, '0')}` : 'min:seg', status: ahtStatus }, { icon: CheckCircle, label: 'FCR', value: `${avgFCR}%`, sublabel: fcrBenchmark ? `Benchmark: ${fcrBenchmark}%` : 'Resolución 1er contacto', status: fcrStatus }, { icon: PhoneForwarded, label: 'Transferencias', value: `${avgTransferRate}%`, sublabel: avgTransferRate > 20 ? 'Requiere atención' : 'Bajo control', status: transferStatus } ]; return (
{metrics.map((metric) => { const Icon = metric.icon; return (
{metric.label}
{metric.value} {metric.status && ( )}

{metric.sublabel}

); })}
); } // v3.15: Health Score with Breakdown function HealthScoreDetailed({ score, avgFCR, avgAHT, avgTransferRate, avgCSAT }: { score: number; avgFCR: number; avgAHT: number; avgTransferRate: number; avgCSAT: number | null; // null = sin datos de CSAT }) { const getScoreColor = (s: number): string => { if (s >= 80) return COLORS.status.success; if (s >= 60) return COLORS.status.warning; return COLORS.status.critical; }; const getScoreLabel = (s: number): string => { if (s >= 80) return 'Excelente'; if (s >= 60) return 'Bueno'; if (s >= 40) return 'Regular'; return 'Crítico'; }; const color = getScoreColor(score); const circumference = 2 * Math.PI * 40; const strokeDasharray = `${(score / 100) * circumference} ${circumference}`; // Calculate individual factor scores (0-100) const fcrScore = Math.min(100, Math.round((avgFCR / 85) * 100)); const ahtScore = Math.min(100, Math.round(Math.max(0, (1 - (avgAHT - 240) / 360) * 100))); const transferScore = Math.min(100, Math.round(Math.max(0, (1 - avgTransferRate / 30) * 100))); const hasCSATData = avgCSAT !== null; const csatScore = avgCSAT ?? 0; type FactorStatus = 'success' | 'warning' | 'critical' | 'nodata'; const getFactorStatus = (s: number): FactorStatus => s >= 80 ? 'success' : s >= 60 ? 'warning' : 'critical'; // Factores sin CSAT si no hay datos const basefactors = [ { name: 'FCR', score: fcrScore, status: getFactorStatus(fcrScore) as FactorStatus, insight: fcrScore >= 80 ? 'Óptimo' : fcrScore >= 60 ? 'Mejorable' : 'Requiere acción', hasData: true }, { name: 'Eficiencia (AHT)', score: ahtScore, status: getFactorStatus(ahtScore) as FactorStatus, insight: ahtScore >= 80 ? 'Óptimo' : ahtScore >= 60 ? 'En rango' : 'Muy alto', hasData: true }, { name: 'Transferencias', score: transferScore, status: getFactorStatus(transferScore) as FactorStatus, insight: transferScore >= 80 ? 'Bajo' : transferScore >= 60 ? 'Moderado' : 'Excesivo', hasData: true }, { name: 'CSAT', score: csatScore, status: hasCSATData ? getFactorStatus(csatScore) as FactorStatus : 'nodata' as FactorStatus, insight: !hasCSATData ? 'Sin datos' : csatScore >= 80 ? 'Óptimo' : csatScore >= 60 ? 'Aceptable' : 'Bajo', hasData: hasCSATData } ]; const factors = basefactors; const statusBarColors: Record = { success: 'bg-emerald-500', warning: 'bg-amber-500', critical: 'bg-red-500', nodata: 'bg-gray-300' }; const statusTextColors: Record = { success: 'text-emerald-600', warning: 'text-amber-600', critical: 'text-red-600', nodata: 'text-gray-400' }; const getMainInsight = () => { // Solo considerar factores que tienen datos const factorsWithData = factors.filter(f => f.hasData); if (factorsWithData.length === 0) return 'No hay suficientes datos para generar insights.'; const weakest = factorsWithData.reduce((min, f) => f.score < min.score ? f : min, factorsWithData[0]); const strongest = factorsWithData.reduce((max, f) => f.score > max.score ? f : max, factorsWithData[0]); if (score >= 80) return `Rendimiento destacado en ${strongest.name}. Mantener estándares actuales.`; if (score >= 60) return `Oportunidad de mejora en ${weakest.name} (${weakest.insight.toLowerCase()}).`; return `Priorizar mejora en ${weakest.name}: impacto directo en satisfacción del cliente.`; }; return (
{/* Gauge */}
{score}

{getScoreLabel(score)}

{/* Breakdown */}

Health Score - Desglose

{factors.map((factor) => (
{factor.name}
{factor.hasData ? ( <>
{factor.score}
{factor.insight}
) : ( <>
Sin datos
)}
))}
{/* Key Insight */}

Insight: {getMainInsight()}

); } // v3.16: Potencial de Automatización - Sin gauge confuso, solo distribución clara function AgenticReadinessScore({ data }: { data: AnalysisData }) { const allQueues = data.drilldownData?.flatMap(skill => skill.originalQueues) || []; const totalQueueVolume = allQueues.reduce((sum, q) => sum + q.volume, 0); // Calcular volúmenes por tier const tierVolumes = { AUTOMATE: allQueues.filter(q => q.tier === 'AUTOMATE').reduce((s, q) => s + q.volume, 0), ASSIST: allQueues.filter(q => q.tier === 'ASSIST').reduce((s, q) => s + q.volume, 0), AUGMENT: allQueues.filter(q => q.tier === 'AUGMENT').reduce((s, q) => s + q.volume, 0), 'HUMAN-ONLY': allQueues.filter(q => q.tier === 'HUMAN-ONLY').reduce((s, q) => s + q.volume, 0) }; const tierCounts = { AUTOMATE: allQueues.filter(q => q.tier === 'AUTOMATE').length, ASSIST: allQueues.filter(q => q.tier === 'ASSIST').length, AUGMENT: allQueues.filter(q => q.tier === 'AUGMENT').length, 'HUMAN-ONLY': allQueues.filter(q => q.tier === 'HUMAN-ONLY').length }; // Porcentajes por tier const tierPcts = { AUTOMATE: totalQueueVolume > 0 ? (tierVolumes.AUTOMATE / totalQueueVolume) * 100 : 0, ASSIST: totalQueueVolume > 0 ? (tierVolumes.ASSIST / totalQueueVolume) * 100 : 0, AUGMENT: totalQueueVolume > 0 ? (tierVolumes.AUGMENT / totalQueueVolume) * 100 : 0, 'HUMAN-ONLY': totalQueueVolume > 0 ? (tierVolumes['HUMAN-ONLY'] / totalQueueVolume) * 100 : 0 }; // Datos de tiers con descripción clara const tiers = [ { key: 'AUTOMATE', label: 'AUTOMATE', bgColor: 'bg-emerald-500', desc: 'Bot autónomo' }, { key: 'ASSIST', label: 'ASSIST', bgColor: 'bg-cyan-500', desc: 'Bot + agente' }, { key: 'AUGMENT', label: 'AUGMENT', bgColor: 'bg-amber-500', desc: 'Agente asistido' }, { key: 'HUMAN-ONLY', label: 'HUMAN', bgColor: 'bg-gray-400', desc: 'Solo humano' } ]; return (

Potencial de Automatización

{/* Distribución por tier */}
{tiers.map((tier) => { const pct = tierPcts[tier.key as keyof typeof tierPcts]; const count = tierCounts[tier.key as keyof typeof tierCounts]; const vol = tierVolumes[tier.key as keyof typeof tierVolumes]; return (
{tier.label}
{tier.desc}
{Math.round(pct)}%
{count} colas
); })}
{/* Resumen */}

{Math.round(tierPcts.AUTOMATE)}%

Automatización completa

{Math.round(tierPcts.AUTOMATE + tierPcts.ASSIST)}%

Con asistencia IA

Basado en {formatNumber(totalQueueVolume)} interacciones analizadas

); } // ============================================ // v3.15: SIGUIENTE PASO RECOMENDADO (Acción) // ============================================ interface RecomendacionData { colasAutomate: number; topColasAutomate: string[]; volumenHuman: number; pctHuman: number; colasConRedFlags: number; ahorroTotal: number; } function generarRecomendacionPrincipal(datos: RecomendacionData): { texto: string; tipo: 'dual' | 'automate' | 'foundation'; prioridad: 'alta' | 'media'; } { if (datos.colasAutomate >= 3 && datos.pctHuman > 0.05) { return { texto: `Iniciar piloto de automatización con ${datos.colasAutomate} colas mientras se ejecuta Wave 1 Foundation para el ${(datos.pctHuman * 100).toFixed(0)}% del volumen que requiere estandarización.`, tipo: 'dual', prioridad: 'alta' }; } if (datos.colasAutomate >= 3) { return { texto: `${datos.colasAutomate} colas listas para automatización inmediata. Iniciar piloto con las de mayor volumen para maximizar ROI.`, tipo: 'automate', prioridad: 'alta' }; } return { texto: `Priorizar Wave 1 Foundation para resolver red flags en ${datos.colasConRedFlags} colas antes de automatizar. Esto habilitará más candidatos de automatización.`, tipo: 'foundation', prioridad: 'media' }; } function SiguientePasoRecomendado({ recomendacion, ahorroTotal, onVerRoadmap }: { recomendacion: RecomendacionData; ahorroTotal: number; onVerRoadmap?: () => void; }) { const rec = generarRecomendacionPrincipal(recomendacion); const tipoConfig = { dual: { icon: Zap, color: 'text-blue-600', bg: 'bg-blue-50', border: 'border-blue-200', label: 'Enfoque Dual' }, automate: { icon: Bot, color: 'text-emerald-600', bg: 'bg-emerald-50', border: 'border-emerald-200', label: 'Automatización' }, foundation: { icon: Target, color: 'text-amber-600', bg: 'bg-amber-50', border: 'border-amber-200', label: 'Foundation' } }; const config = tipoConfig[rec.tipo]; const Icon = config.icon; return (
Recomendación basada en el análisis

{rec.texto}

{config.label} {ahorroTotal > 0 && ( Potencial: {formatCurrency(ahorroTotal)}/año )}
{onVerRoadmap && ( )}
); } // Top Opportunities Component (legacy - kept for reference) function TopOpportunities({ findings, opportunities }: { findings: Finding[]; opportunities: { name: string; impact: number; savings: number }[]; }) { const items = [ ...findings .filter(f => f.type === 'critical' || f.type === 'warning') .slice(0, 3) .map((f, i) => ({ rank: i + 1, title: f.title || f.text.split(':')[0], metric: f.text.includes(':') ? f.text.split(':')[1].trim() : '', action: f.description || 'Acción requerida', type: f.type as 'critical' | 'warning' | 'info' })), ].slice(0, 3); if (items.length < 3) { const remaining = 3 - items.length; opportunities .sort((a, b) => b.savings - a.savings) .slice(0, remaining) .forEach(() => { const opp = opportunities[items.length]; if (opp) { items.push({ rank: items.length + 1, title: opp.name, metric: `€${opp.savings.toLocaleString()} ahorro potencial`, action: 'Implementar', type: 'info' as const }); } }); } const getIcon = (type: string) => { if (type === 'critical') return ; if (type === 'warning') return ; return ; }; return (

Top 3 Oportunidades

{items.map((item) => (
{item.rank}
{getIcon(item.type)} {item.title}
{item.metric && (

{item.metric}

)}

→ {item.action}

))}
); } // v3.15: Economic Summary Compact function EconomicSummary({ economicModel }: { economicModel: AnalysisData['economicModel'] }) { return (

Impacto Económico

ROI 3 años

{economicModel.roi3yr}%

Payback

{economicModel.paybackMonths}m

); } export function ExecutiveSummaryTab({ data, onTabChange }: ExecutiveSummaryTabProps) { // Métricas básicas const totalInteractions = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); const avgAHT = data.heatmapData.length > 0 ? Math.round(data.heatmapData.reduce((sum, h) => sum + h.aht_seconds, 0) / data.heatmapData.length) : 0; const avgFCR = data.heatmapData.length > 0 ? Math.round(data.heatmapData.reduce((sum, h) => sum + h.metrics.fcr, 0) / data.heatmapData.length) : 0; const avgTransferRate = data.heatmapData.length > 0 ? Math.round(data.heatmapData.reduce((sum, h) => sum + h.metrics.transfer_rate, 0) / data.heatmapData.length) : 0; // Verificar si hay datos reales de CSAT (no todos son 0) const hasCSATData = data.heatmapData.some(h => h.metrics.csat > 0); const avgCSAT = hasCSATData && data.heatmapData.length > 0 ? Math.round(data.heatmapData.reduce((sum, h) => sum + h.metrics.csat, 0) / data.heatmapData.length) : null; // null indica "sin datos" const ahtBenchmark = data.benchmarkData.find(b => b.kpi.toLowerCase().includes('aht')); const fcrBenchmark = data.benchmarkData.find(b => b.kpi.toLowerCase().includes('fcr')); // v3.13: Métricas para headline y recomendación const allQueues = data.drilldownData?.flatMap(s => s.originalQueues) || []; const totalVolume = allQueues.reduce((s, q) => s + q.volume, 0); const colasAutomate = allQueues.filter(q => q.tier === 'AUTOMATE'); const colasHumanOnly = allQueues.filter(q => q.tier === 'HUMAN-ONLY'); const volumenHumanOnly = colasHumanOnly.reduce((s, q) => s + q.volume, 0); const pctHumanOnly = totalVolume > 0 ? volumenHumanOnly / totalVolume : 0; // Red flags: colas con CV > 100% o FCR < 50% const colasConRedFlags = allQueues.filter(q => q.cv_aht > 100 || q.fcr_rate < 50 || q.transfer_rate > 25 ).length; const ahorroTotal = data.economicModel?.annualSavings || 0; const dimensionesConProblemas = data.dimensions.filter(d => d.score < 60).length; // Scores para status bar const eficienciaScore = Math.min(100, Math.max(0, Math.round((1 - (avgAHT - 240) / 360) * 100))); const resolucionScore = Math.min(100, Math.round((avgFCR / 85) * 100)); const satisfaccionScore = avgCSAT ?? 0; // Para cálculos que necesiten número // Datos para recomendación const recomendacionData: RecomendacionData = { colasAutomate: colasAutomate.length, topColasAutomate: colasAutomate.slice(0, 5).map(q => q.original_queue_id), volumenHuman: volumenHumanOnly, pctHuman: pctHumanOnly, colasConRedFlags, ahorroTotal }; return (
{/* ======================================== 1. CABECERA CON PERIODO ======================================== */} {/* ======================================== 2. KPIs HEADER (Métricas clave) ======================================== */} {/* ======================================== 3. HEALTH SCORE ======================================== */} {/* ======================================== 4. BENCHMARK VS INDUSTRIA ======================================== */} {/* ======================================== 5. PRINCIPALES HALLAZGOS ======================================== */} {/* ======================================== 6. SIGUIENTE PASO RECOMENDADO (Acción) ======================================== */} onTabChange('roadmap') : undefined} /> {/* ======================================== 6. NAVEGACIÓN RÁPIDA (Explorar más) ======================================== */} {onTabChange && (

Explorar análisis detallado

{/* Dimensiones */} {/* Agentic Readiness */}
)}
); } export default ExecutiveSummaryTab;