import React from 'react'; import { useTranslation } from 'react-i18next'; import { Scale, Clock, Target, Calendar, AlertTriangle, CheckCircle, XCircle, HelpCircle, Lightbulb, FileText, TrendingUp, } from 'lucide-react'; import type { AnalysisData, HeatmapDataPoint, DrilldownDataPoint } from '../../types'; import { Card, Badge, Stat, } from '../ui'; import { cn, STATUS_CLASSES, formatCurrency, formatNumber, } from '../../config/designSystem'; // ============================================ // TIPOS Y CONSTANTES // ============================================ type ComplianceStatus = 'CUMPLE' | 'PARCIAL' | 'NO_CUMPLE' | 'SIN_DATOS'; interface ComplianceResult { status: ComplianceStatus; score: number; // 0-100 gap: string; details: string[]; } const LAW_10_2025 = { deadline: new Date('2026-12-28'), requirements: { LAW_07: { name: 'Cobertura Horaria', maxOffHoursPct: 15, }, LAW_01: { name: 'Velocidad de Respuesta', maxHoldTimeSeconds: 180, }, LAW_02: { name: 'Calidad de Resolucion', minFCR: 75, maxTransfer: 15, }, LAW_09: { name: 'Cobertura Linguistica', languages: ['es', 'ca', 'eu', 'gl', 'va'], }, }, }; // ============================================ // FUNCIONES DE EVALUACION DE COMPLIANCE // ============================================ function evaluateLaw07Compliance(data: AnalysisData, t: (key: string, options?: any) => string): ComplianceResult { // Evaluar cobertura horaria basado en off_hours_pct const volumetryDim = data.dimensions.find(d => d.name === 'volumetry_distribution'); const offHoursPct = volumetryDim?.distribution_data?.off_hours_pct ?? null; if (offHoursPct === null) { return { status: 'SIN_DATOS', score: 0, gap: t('law10.compliance.law07.noData'), details: [t('law10.compliance.law07.noDataDetails')], }; } const details: string[] = []; details.push(t('law10.compliance.law07.offHoursPercent', { percent: offHoursPct.toFixed(1) })); if (offHoursPct < 5) { return { status: 'CUMPLE', score: 100, gap: '-', details: [...details, t('law10.compliance.law07.adequateCoverage')], }; } else if (offHoursPct <= 15) { return { status: 'PARCIAL', score: Math.round(100 - ((offHoursPct - 5) / 10) * 50), gap: t('law10.compliance.law07.gapOverOptimal', { gap: (offHoursPct - 5).toFixed(1) }), details: [...details, t('law10.compliance.law07.improvableCoverage')], }; } else { return { status: 'NO_CUMPLE', score: Math.max(0, Math.round(50 - ((offHoursPct - 15) / 10) * 50)), gap: t('law10.compliance.law07.gapOverLimit', { gap: (offHoursPct - 15).toFixed(1) }), details: [...details, t('law10.compliance.law07.insufficientCoverage')], }; } } function evaluateLaw01Compliance(data: AnalysisData, t: (key: string, options?: any) => string): ComplianceResult { // Evaluar tiempo de espera (hold_time) vs limite de 180 segundos const totalVolume = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); if (totalVolume === 0) { return { status: 'SIN_DATOS', score: 0, gap: t('law10.compliance.law01.noData'), details: [t('law10.compliance.law01.noDataDetails')], }; } // Calcular hold_time promedio ponderado por volumen const avgHoldTime = data.heatmapData.reduce( (sum, h) => sum + h.metrics.hold_time * h.volume, 0 ) / totalVolume; // Contar colas que exceden el limite const colasExceden = data.heatmapData.filter(h => h.metrics.hold_time > 180); const pctColasExceden = (colasExceden.length / data.heatmapData.length) * 100; // Calcular % de interacciones dentro del limite const volDentroLimite = data.heatmapData .filter(h => h.metrics.hold_time <= 180) .reduce((sum, h) => sum + h.volume, 0); const pctDentroLimite = (volDentroLimite / totalVolume) * 100; const details: string[] = []; details.push(t('law10.compliance.law01.avgHoldTime', { time: Math.round(avgHoldTime) })); details.push(t('law10.compliance.law01.withinLimit', { percent: pctDentroLimite.toFixed(1) })); details.push(t('law10.compliance.law01.queuesExceedLimit', { count: colasExceden.length, total: data.heatmapData.length })); if (avgHoldTime < 180 && pctColasExceden < 10) { return { status: 'CUMPLE', score: 100, gap: t('law10.compliance.law01.gapNegative', { gap: Math.round(180 - avgHoldTime) }), details, }; } else if (avgHoldTime < 180) { return { status: 'PARCIAL', score: Math.round(90 - pctColasExceden), gap: t('law10.compliance.law01.queuesOutside', { count: colasExceden.length }), details, }; } else { return { status: 'NO_CUMPLE', score: Math.max(0, Math.round(50 - ((avgHoldTime - 180) / 60) * 25)), gap: t('law10.compliance.law01.gapPositive', { gap: Math.round(avgHoldTime - 180) }), details, }; } } function evaluateLaw02Compliance(data: AnalysisData, t: (key: string, options?: any) => string): ComplianceResult { // Evaluar FCR y tasa de transferencia const totalVolume = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); if (totalVolume === 0) { return { status: 'SIN_DATOS', score: 0, gap: t('law10.compliance.law02.noData'), details: [t('law10.compliance.law02.noDataDetails')], }; } // FCR Tecnico ponderado (comparable con benchmarks) const avgFCR = data.heatmapData.reduce( (sum, h) => sum + (h.metrics.fcr_tecnico ?? (100 - h.metrics.transfer_rate)) * h.volume, 0 ) / totalVolume; // Transfer rate ponderado const avgTransfer = data.heatmapData.reduce( (sum, h) => sum + h.metrics.transfer_rate * h.volume, 0 ) / totalVolume; const details: string[] = []; details.push(t('law10.compliance.law02.fcrTechnical', { fcr: avgFCR.toFixed(1) })); details.push(t('law10.compliance.law02.transferRate', { rate: avgTransfer.toFixed(1) })); // Colas con alto transfer const colasAltoTransfer = data.heatmapData.filter(h => h.metrics.transfer_rate > 25); if (colasAltoTransfer.length > 0) { details.push(t('law10.compliance.law02.highTransferQueues', { count: colasAltoTransfer.length })); } const cumpleFCR = avgFCR >= 75; const cumpleTransfer = avgTransfer <= 15; const parcialFCR = avgFCR >= 60; const parcialTransfer = avgTransfer <= 25; if (cumpleFCR && cumpleTransfer) { return { status: 'CUMPLE', score: 100, gap: t('law10.compliance.law02.gapDash'), details, }; } else if (parcialFCR && parcialTransfer) { const score = Math.round( (Math.min(avgFCR, 75) / 75 * 50) + (Math.max(0, 25 - avgTransfer) / 25 * 50) ); const fcrGap = avgFCR < 75 ? t('law10.compliance.law02.gapNegative', { gap: (75 - avgFCR).toFixed(0) }) : t('law10.compliance.law02.gapOk'); const transferGap = avgTransfer > 15 ? t('law10.compliance.law02.gapPositive', { gap: (avgTransfer - 15).toFixed(0) }) : t('law10.compliance.law02.gapOk'); return { status: 'PARCIAL', score, gap: t('law10.compliance.law02.gapFcr', { fcrGap, transferGap }), details, }; } else { return { status: 'NO_CUMPLE', score: Math.max(0, Math.round((avgFCR / 75 * 30) + ((30 - avgTransfer) / 30 * 20))), gap: `FCR ${t('law10.compliance.law02.gapNegative', { gap: (75 - avgFCR).toFixed(0) })}, Transfer ${t('law10.compliance.law02.gapPositive', { gap: (avgTransfer - 15).toFixed(0) })}`, details, }; } } function evaluateLaw09Compliance(_data: AnalysisData, t: (key: string, options?: any) => string): ComplianceResult { // Los datos de idioma no estan disponibles en el modelo actual return { status: 'SIN_DATOS', score: 0, gap: t('law10.compliance.law09.noData'), details: [ t('law10.compliance.law09.noLanguageData'), t('law10.compliance.law09.needsLanguageField'), ], }; } // ============================================ // COMPONENTES DE SECCION // ============================================ interface Law10TabProps { data: AnalysisData; } // Status Icon Component function StatusIcon({ status }: { status: ComplianceStatus }) { switch (status) { case 'CUMPLE': return ; case 'PARCIAL': return ; case 'NO_CUMPLE': return ; default: return ; } } function getStatusBadgeVariant(status: ComplianceStatus): 'success' | 'warning' | 'critical' | 'default' { switch (status) { case 'CUMPLE': return 'success'; case 'PARCIAL': return 'warning'; case 'NO_CUMPLE': return 'critical'; default: return 'default'; } } function getStatusLabel(status: ComplianceStatus, t: (key: string) => string): string { switch (status) { case 'CUMPLE': return t('law10.status.CUMPLE'); case 'PARCIAL': return t('law10.status.PARCIAL'); case 'NO_CUMPLE': return t('law10.status.NO_CUMPLE'); default: return t('law10.status.SIN_DATOS'); } } // Header con descripcion del analisis function Law10HeaderCountdown({ complianceResults, }: { complianceResults: { law07: ComplianceResult; law01: ComplianceResult; law02: ComplianceResult; law09: ComplianceResult }; }) { const { t } = useTranslation(); const now = new Date(); const deadline = LAW_10_2025.deadline; const diffTime = deadline.getTime() - now.getTime(); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); // Contar requisitos cumplidos const results = [complianceResults.law07, complianceResults.law01, complianceResults.law02]; const cumplidos = results.filter(r => r.status === 'CUMPLE').length; const total = results.length; // Determinar estado general const getOverallStatus = () => { if (results.every(r => r.status === 'CUMPLE')) return 'CUMPLE'; if (results.some(r => r.status === 'NO_CUMPLE')) return 'NO_CUMPLE'; return 'PARCIAL'; }; const overallStatus = getOverallStatus(); return ( {/* Header */}

{t('law10.header.aboutThisAnalysis')}

{t('law10.header.lawTitle')}

{/* Descripcion */}

{/* Metricas de estado */}
{/* Deadline */}

{t('law10.header.complianceDeadline')}

{t('law10.header.december282026')}

{t('law10.header.daysRemaining', { days: diffDays })}

{/* Requisitos evaluados */}

{t('law10.header.requirementsEvaluated')}

{t('law10.header.requirementsMet', { met: cumplidos, total })}

{t('law10.header.basedOnData')}

{/* Estado general */}

{t('law10.header.overallStatus')}

{getStatusLabel(overallStatus, t)}

{overallStatus === 'CUMPLE' ? t('law10.header.goodState') : overallStatus === 'PARCIAL' ? t('law10.header.requiresAttention') : t('law10.header.urgentAction')}

); } // Seccion: Cobertura Horaria (LAW-07) function TimeCoverageSection({ data, result }: { data: AnalysisData; result: ComplianceResult }) { const { t } = useTranslation(); const volumetryDim = data.dimensions.find(d => d.name === 'volumetry_distribution'); const hourlyData = volumetryDim?.distribution_data?.hourly || []; const dailyData = volumetryDim?.distribution_data?.daily || []; const totalVolume = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); // Calcular metricas detalladas const hourlyTotal = hourlyData.reduce((sum, v) => sum + v, 0); const nightVolume = hourlyData.slice(22).concat(hourlyData.slice(0, 8)).reduce((sum, v) => sum + v, 0); const nightPct = hourlyTotal > 0 ? (nightVolume / hourlyTotal) * 100 : 0; const earlyMorningVolume = hourlyData.slice(0, 6).reduce((sum, v) => sum + v, 0); const earlyMorningPct = hourlyTotal > 0 ? (earlyMorningVolume / hourlyTotal) * 100 : 0; // Encontrar hora pico const maxHourIndex = hourlyData.indexOf(Math.max(...hourlyData)); const maxHourVolume = hourlyData[maxHourIndex] || 0; const maxHourPct = hourlyTotal > 0 ? (maxHourVolume / hourlyTotal) * 100 : 0; // Dias de la semana const dayNames = ['Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa', 'Do']; // Generar datos de heatmap 7x24 (simulado basado en hourly y daily) const generateHeatmapData = () => { const heatmap: number[][] = []; const maxHourly = Math.max(...hourlyData, 1); for (let day = 0; day < 7; day++) { const dayRow: number[] = []; const dayMultiplier = dailyData[day] ? dailyData[day] / Math.max(...dailyData, 1) : (day < 5 ? 1 : 0.6); for (let hour = 0; hour < 24; hour++) { const hourValue = hourlyData[hour] || 0; const normalizedValue = (hourValue / maxHourly) * dayMultiplier; dayRow.push(normalizedValue); } heatmap.push(dayRow); } return heatmap; }; const heatmapData = generateHeatmapData(); // Funcion para obtener el caracter de barra segun intensidad const getBarChar = (value: number): string => { if (value < 0.1) return '▁'; if (value < 0.25) return '▂'; if (value < 0.4) return '▃'; if (value < 0.55) return '▄'; if (value < 0.7) return '▅'; if (value < 0.85) return '▆'; if (value < 0.95) return '▇'; return '█'; }; // Funcion para obtener color segun intensidad const getBarColor = (value: number): string => { if (value < 0.2) return 'text-blue-200'; if (value < 0.4) return 'text-blue-300'; if (value < 0.6) return 'text-blue-400'; if (value < 0.8) return 'text-blue-500'; return 'text-blue-600'; }; return ( {/* Header */}

{t('law10.timeCoverage.title')}

{t('law10.timeCoverage.article')}

{/* Lo que sabemos */}

{t('law10.timeCoverage.whatWeKnow')}

{/* Heatmap 24x7 */}

{t('law10.timeCoverage.heatmap247')}

{/* Header de horas */}
{[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22].map(h => (
{h.toString().padStart(2, '0')}
))}
{/* Filas por dia */} {heatmapData.map((dayRow, dayIdx) => (
{dayNames[dayIdx]}
{dayRow.map((value, hourIdx) => ( {getBarChar(value)} ))}
))} {/* Leyenda */}
{t('law10.timeCoverage.intensity')} ▁ {t('law10.timeCoverage.intensityLow')} ▄ {t('law10.timeCoverage.intensityMedium')} █ {t('law10.timeCoverage.intensityHigh')}
{/* Hallazgos operacionales */}

{t('law10.timeCoverage.operationalFindings')}

{/* Implicacion Ley 10/2025 */}

{t('law10.timeCoverage.lawImplication')}

{t('law10.timeCoverage.basicServiceRequirement')}
{t('law10.timeCoverage.article14Requirement')}

{t('law10.timeCoverage.gapIdentified')}

{/* Accion sugerida */}

{t('law10.timeCoverage.suggestedAction')}

{t('law10.timeCoverage.classifyNightVolume')}

  • • {t('law10.timeCoverage.criticalIncidents')}
  • • {t('law10.timeCoverage.generalQueries')}

{t('law10.timeCoverage.coverageOptions')}

{t('law10.timeCoverage.optionAChatbot')} {t('law10.timeCoverage.costPerYear', { cost: '65K' })}
{t('law10.timeCoverage.optionBExternal')} {t('law10.timeCoverage.costPerYear', { cost: '95K' })}
{t('law10.timeCoverage.optionCNight')} {t('law10.timeCoverage.costPerYear', { cost: '180K' })}
); } // Seccion: Velocidad de Respuesta (LAW-01) function ResponseSpeedSection({ data, result }: { data: AnalysisData; result: ComplianceResult }) { const { t } = useTranslation(); const totalVolume = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); const volumetryDim = data.dimensions.find(d => d.name === 'volumetry_distribution'); const hourlyData = volumetryDim?.distribution_data?.hourly || []; // Metricas de AHT - usar aht_seconds (limpio, sin noise/zombie) const avgAHT = totalVolume > 0 ? data.heatmapData.reduce((sum, h) => sum + h.aht_seconds * h.volume, 0) / totalVolume : 0; // Calcular AHT P50 y P90 aproximados desde drilldown let ahtP50 = avgAHT; let ahtP90 = avgAHT * 1.8; if (data.drilldownData && data.drilldownData.length > 0) { const allAHTs = data.drilldownData.flatMap(d => d.originalQueues?.map(q => q.aht_mean) || [] ).filter(v => v > 0); if (allAHTs.length > 0) { allAHTs.sort((a, b) => a - b); ahtP50 = allAHTs[Math.floor(allAHTs.length * 0.5)] || avgAHT; ahtP90 = allAHTs[Math.floor(allAHTs.length * 0.9)] || avgAHT * 1.8; } } const ahtRatio = ahtP50 > 0 ? ahtP90 / ahtP50 : 1; // Tasa de abandono - usar abandonment_rate (campo correcto) const abandonRate = totalVolume > 0 ? data.heatmapData.reduce((sum, h) => sum + (h.metrics.abandonment_rate || 0) * h.volume, 0) / totalVolume : 0; // Generar datos de abandono por hora (simulado basado en volumetria) const hourlyAbandonment = hourlyData.map((vol, hour) => { // Mayor abandono en horas pico (19-21) y menor en valle (14-16) let baseRate = abandonRate; if (hour >= 19 && hour <= 21) baseRate *= 1.5; else if (hour >= 14 && hour <= 16) baseRate *= 0.6; else if (hour >= 9 && hour <= 11) baseRate *= 1.2; return { hour, volume: vol, abandonRate: Math.min(baseRate, 35) }; }); // Encontrar patrones const maxAbandonHour = hourlyAbandonment.reduce((max, h) => h.abandonRate > max.abandonRate ? h : max, hourlyAbandonment[0]); const minAbandonHour = hourlyAbandonment.reduce((min, h) => h.abandonRate < min.abandonRate && h.volume > 0 ? h : min, hourlyAbandonment[0]); // Funcion para obtener el caracter de barra segun tasa de abandono const getBarChar = (rate: number): string => { if (rate < 5) return '▁'; if (rate < 10) return '▂'; if (rate < 15) return '▃'; if (rate < 20) return '▅'; if (rate < 25) return '▆'; return '█'; }; // Funcion para obtener color segun tasa de abandono const getAbandonColor = (rate: number): string => { if (rate < 8) return 'text-emerald-500'; if (rate < 12) return 'text-amber-400'; if (rate < 18) return 'text-orange-500'; return 'text-red-500'; }; // Estimacion conservadora const estimatedFastResponse = Math.max(0, 100 - abandonRate - 7); const gapVs95 = 95 - estimatedFastResponse; return ( {/* Header */}

{t('law10.responseSpeed.title')}

{t('law10.responseSpeed.article')}

{/* Lo que sabemos */}

{t('law10.responseSpeed.whatWeKnow')}

{/* Metricas principales */}

{abandonRate.toFixed(1)}%

{t('law10.responseSpeed.abandonmentRate')}

{Math.round(ahtP50)}s

{t('law10.responseSpeed.ahtP50', { min: Math.floor(ahtP50 / 60), sec: Math.round(ahtP50 % 60) })}

{Math.round(ahtP90)}s

{t('law10.responseSpeed.ahtP90', { min: Math.floor(ahtP90 / 60), sec: Math.round(ahtP90 % 60) })}

2 ? 'bg-amber-50' : 'bg-gray-50' )}>

2 ? 'text-amber-600' : 'text-gray-900' )}>{ahtRatio.toFixed(1)}

{t('law10.responseSpeed.ratioP90P50', { elevated: ahtRatio > 2 ? t('law10.responseSpeed.elevated') : '' })}

{/* Grafico de abandonos por hora */}

{t('law10.responseSpeed.abandonmentByHour')}

{hourlyAbandonment.map((h, idx) => (
{getBarChar(h.abandonRate)}
))}
00:00 06:00 12:00 18:00 24:00
{t('law10.responseSpeed.abandonmentLegend')}
{/* Patrones observados */}

{t('law10.responseSpeed.patternsObserved')}

{/* Implicacion Ley 10/2025 */}

{t('law10.responseSpeed.lawImplication')}

{t('law10.responseSpeed.dataLimitation')}

{t('law10.responseSpeed.noAsaData')}

{t('law10.responseSpeed.butWeKnow')}

  • {t('law10.responseSpeed.highVariability', { ratio: ahtRatio.toFixed(1) })}
  • {t('law10.responseSpeed.peaksCoincide')}

{t('law10.responseSpeed.conservativeEstimate')}

0 ? 'text-red-600' : 'text-emerald-600' )} dangerouslySetInnerHTML={{ __html: t('law10.responseSpeed.gapVs95', { operator: gapVs95 > 0 ? '-' : '+', gap: Math.abs(gapVs95).toFixed(0) }) }} />

{/* Accion sugerida */}

{t('law10.responseSpeed.suggestedAction')}

{t('law10.responseSpeed.shortTerm')}

  • • {t('law10.responseSpeed.dimension2Identifies')}
  • {t('law10.responseSpeed.highAht', { aht: Math.round(ahtP50) })}
  • {t('law10.responseSpeed.copilotOpportunity')}
  • • {t('law10.responseSpeed.dualBenefit')}

{t('law10.responseSpeed.mediumTerm')}

{t('law10.responseSpeed.platformConfig')} 5-8K
{t('law10.responseSpeed.implementationTimeline')} {t('law10.responseSpeed.implementationWeeks')}

{t('law10.responseSpeed.benefit')}

); } // Seccion: Calidad de Resolucion (LAW-02) function ResolutionQualitySection({ data, result }: { data: AnalysisData; result: ComplianceResult }) { const { t } = useTranslation(); const totalVolume = data.heatmapData.reduce((sum, h) => sum + h.volume, 0); // FCR Tecnico y Real const avgFCRTecnico = totalVolume > 0 ? data.heatmapData.reduce((sum, h) => sum + (h.metrics.fcr_tecnico ?? (100 - h.metrics.transfer_rate)) * h.volume, 0) / totalVolume : 0; const avgFCRReal = totalVolume > 0 ? data.heatmapData.reduce((sum, h) => sum + h.metrics.fcr * h.volume, 0) / totalVolume : 0; // Recontactos (diferencia entre FCR Tecnico y Real) const recontactRate7d = 100 - avgFCRReal; // Calcular llamadas repetidas const repeatCallsPct = Math.min(recontactRate7d * 0.8, 35); // Datos por skill para el grafico const skillFCRData = data.heatmapData .map(h => ({ skill: h.skill, fcrReal: h.metrics.fcr, fcrTecnico: h.metrics.fcr_tecnico ?? (100 - h.metrics.transfer_rate), volume: h.volume, })) .sort((a, b) => a.fcrReal - b.fcrReal); // Top skills con FCR bajo const lowFCRSkills = skillFCRData .filter(s => s.fcrReal < 60) .slice(0, 5); // Funcion para obtener caracter de barra segun FCR const getFCRBarChar = (fcr: number): string => { if (fcr >= 80) return '█'; if (fcr >= 70) return '▇'; if (fcr >= 60) return '▅'; if (fcr >= 50) return '▃'; if (fcr >= 40) return '▂'; return '▁'; }; // Funcion para obtener color segun FCR const getFCRColor = (fcr: number): string => { if (fcr >= 75) return 'text-emerald-500'; if (fcr >= 60) return 'text-amber-400'; if (fcr >= 45) return 'text-orange-500'; return 'text-red-500'; }; return ( {/* Header */}

{t('law10.resolutionQuality.title')}

{t('law10.resolutionQuality.article')}

{/* Lo que sabemos */}

{t('law10.resolutionQuality.whatWeKnow')}

{/* Metricas principales */}
= 60 ? 'bg-gray-50' : 'bg-red-50' )}>

= 60 ? 'text-gray-900' : 'text-red-600' )}>{avgFCRReal.toFixed(0)}%

{t('law10.resolutionQuality.fcrReal')}

{recontactRate7d.toFixed(0)}%

{t('law10.resolutionQuality.recontactRate7d')}

{repeatCallsPct.toFixed(0)}%

{t('law10.resolutionQuality.repeatCalls')}

{/* Grafico FCR por skill */}

{t('law10.resolutionQuality.fcrBySkill')}

{skillFCRData.slice(0, 8).map((s, idx) => (
{s.skill}
{Array.from({ length: 10 }).map((_, i) => ( {i < Math.round(s.fcrReal / 10) ? getFCRBarChar(s.fcrReal) : '▁'} ))}
{s.fcrReal.toFixed(0)}%
))}
{t('law10.resolutionQuality.fcrLegend')}
{/* Top skills con FCR bajo */} {lowFCRSkills.length > 0 && (

{t('law10.resolutionQuality.topLowFcr')}

    {lowFCRSkills.map((s, idx) => (
  • {idx + 1}. {s.skill}: {t('law10.resolutionQuality.fcrValue', { fcr: s.fcrReal.toFixed(0) })}
  • ))}
)}
{/* Implicacion Ley 10/2025 */}

{t('law10.resolutionQuality.lawImplication')}

{t('law10.resolutionQuality.dataLimitation')}

{t('law10.resolutionQuality.noCaseTracking')}

{t('law10.resolutionQuality.butWeKnow')}

  • {t('law10.resolutionQuality.fcrGap', { fcr: avgFCRReal.toFixed(0), gap: recontactRate7d.toFixed(0) })}
  • {t('law10.resolutionQuality.complexProcesses')}

{t('law10.resolutionQuality.alertSignal')}

{t('law10.resolutionQuality.resolutionTimeRisk')}

{/* Accion sugerida */}

{t('law10.resolutionQuality.suggestedAction')}

{t('law10.resolutionQuality.diagnosis')}

  • • {t('law10.resolutionQuality.registerOpenClose')}
  • • {t('law10.resolutionQuality.linkInteractions')}
  • • {t('law10.resolutionQuality.typology')}
{t('law10.resolutionQuality.crmInvestment')} 15-25K

{t('law10.resolutionQuality.operationalImprovement')}

  • • {t('law10.resolutionQuality.dimension3Identifies')}
  • {t('law10.resolutionQuality.rootCauses')}
  • {t('law10.resolutionQuality.solution')}
  • • {t('law10.resolutionQuality.fcrBenefit')}
); } // Seccion: Resumen de Cumplimiento function Law10SummaryRoadmap({ complianceResults, data, }: { complianceResults: { law07: ComplianceResult; law01: ComplianceResult; law02: ComplianceResult; law09: ComplianceResult }; data: AnalysisData; }) { const { t } = useTranslation(); // Resultado por defecto para requisitos sin datos const sinDatos: ComplianceResult = { status: 'SIN_DATOS', score: 0, gap: t('law10.common.requiredData'), details: [t('law10.common.noData')], }; // Todos los requisitos de la Ley 10/2025 con descripciones const allRequirements = [ { id: 'LAW-01', name: t('law10.summary.requirements.LAW-01.name'), description: t('law10.summary.requirements.LAW-01.description'), result: complianceResults.law01, }, { id: 'LAW-02', name: t('law10.summary.requirements.LAW-02.name'), description: t('law10.summary.requirements.LAW-02.description'), result: complianceResults.law02, }, { id: 'LAW-03', name: t('law10.summary.requirements.LAW-03.name'), description: t('law10.summary.requirements.LAW-03.description'), result: sinDatos, }, { id: 'LAW-04', name: t('law10.summary.requirements.LAW-04.name'), description: t('law10.summary.requirements.LAW-04.description'), result: sinDatos, }, { id: 'LAW-05', name: t('law10.summary.requirements.LAW-05.name'), description: t('law10.summary.requirements.LAW-05.description'), result: sinDatos, }, { id: 'LAW-06', name: t('law10.summary.requirements.LAW-06.name'), description: t('law10.summary.requirements.LAW-06.description'), result: sinDatos, }, { id: 'LAW-07', name: t('law10.summary.requirements.LAW-07.name'), description: t('law10.summary.requirements.LAW-07.description'), result: complianceResults.law07, }, { id: 'LAW-08', name: t('law10.summary.requirements.LAW-08.name'), description: t('law10.summary.requirements.LAW-08.description'), result: sinDatos, }, { id: 'LAW-09', name: t('law10.summary.requirements.LAW-09.name'), description: t('law10.summary.requirements.LAW-09.description'), result: complianceResults.law09, }, { id: 'LAW-10', name: t('law10.summary.requirements.LAW-10.name'), description: t('law10.summary.requirements.LAW-10.description'), result: sinDatos, }, { id: 'LAW-11', name: t('law10.summary.requirements.LAW-11.name'), description: t('law10.summary.requirements.LAW-11.description'), result: sinDatos, }, { id: 'LAW-12', name: t('law10.summary.requirements.LAW-12.name'), description: t('law10.summary.requirements.LAW-12.description'), result: sinDatos, }, ]; // Calcular inversion estimada basada en datos reales const estimatedInvestment = () => { // Base: 3% del coste anual actual o minimo 15K const currentCost = data.economicModel?.currentAnnualCost || 0; let base = currentCost > 0 ? Math.max(15000, currentCost * 0.03) : 15000; // Incrementos por gaps de compliance if (complianceResults.law01.status === 'NO_CUMPLE') base += currentCost > 0 ? currentCost * 0.01 : 25000; if (complianceResults.law02.status === 'NO_CUMPLE') base += currentCost > 0 ? currentCost * 0.008 : 20000; if (complianceResults.law07.status === 'NO_CUMPLE') base += currentCost > 0 ? currentCost * 0.015 : 35000; return Math.round(base); }; return (

{t('law10.summary.title')}

{/* Scorecard con todos los requisitos */}
{allRequirements.map((req) => ( ))}
{t('law10.summaryTable.requirement')} {t('law10.summaryTable.description')} {t('law10.summaryTable.status')} {t('law10.summaryTable.score')} {t('law10.summaryTable.gap')}
{req.id} {req.name} {req.description}
{req.result.status !== 'SIN_DATOS' ? ( = 80 ? 'text-emerald-600' : req.result.score >= 50 ? 'text-amber-600' : 'text-red-600' )}> {req.result.score} ) : ( - )} {req.result.gap}
{/* Leyenda */}
{t('law10.summaryTable.legend.complies')}
{t('law10.summaryTable.legend.partial')}
{t('law10.summaryTable.legend.notComply')}
{t('law10.summaryTable.legend.noData')}
{/* Inversion Estimada */}

{t('law10.summaryTable.investment.nonComplianceCost')}

{t('law10.summaryTable.investment.upTo100k')}

{t('law10.summaryTable.investment.potentialFines')}

{t('law10.summaryTable.investment.recommendedInvestment')}

{formatCurrency(estimatedInvestment())}

{t('law10.summaryTable.investment.basedOnOperation')}

{t('law10.summaryTable.investment.complianceRoi')}

{data.economicModel?.roi3yr ? `${Math.round(data.economicModel.roi3yr / 2)}%` : 'Alto'}

{t('law10.summaryTable.investment.avoidSanctions')}

); } // Seccion: Resumen de Madurez de Datos function DataMaturitySummary({ data }: { data: AnalysisData }) { const { t } = useTranslation(); // Usar datos economicos reales cuando esten disponibles const currentAnnualCost = data.economicModel?.currentAnnualCost || 0; const annualSavings = data.economicModel?.annualSavings || 0; // Datos disponibles const availableData = [ { name: t('law10.dataMaturity.items.coverage247'), article: t('law10.dataMaturity.article', { number: '14' }) }, { name: t('law10.dataMaturity.items.geoDistribution'), article: t('law10.dataMaturity.articlePartial', { number: '15' }) }, { name: t('law10.dataMaturity.items.resolutionQuality'), article: t('law10.dataMaturity.articleIndirect', { number: '17' }) }, ]; // Datos estimables const estimableData = [ { name: t('law10.dataMaturity.items.asa3min'), article: t('law10.dataMaturity.article', { number: '8.2' }), error: t('law10.dataMaturity.errorMargin', { margin: '10' }) }, { name: t('law10.dataMaturity.items.officialLanguages'), article: t('law10.dataMaturity.article', { number: '15' }), error: t('law10.dataMaturity.noDetail') }, ]; // Datos no disponibles const missingData = [ { name: t('law10.dataMaturity.items.caseResolutionTime'), article: t('law10.dataMaturity.article', { number: '17' }) }, { name: t('law10.dataMaturity.items.undueBilling'), article: t('law10.dataMaturity.article', { number: '17' }) }, { name: t('law10.dataMaturity.items.supervisorTransfer'), article: t('law10.dataMaturity.article', { number: '8' }) }, { name: t('law10.dataMaturity.items.incidentInfo'), article: t('law10.dataMaturity.article', { number: '17' }) }, { name: t('law10.dataMaturity.items.enacAudit'), article: t('law10.dataMaturity.article', { number: '22' }), note: t('law10.dataMaturity.items.externalContractRequired') }, ]; return (

{t('law10.dataMaturity.title')}

{t('law10.dataMaturity.currentLevel')}

{/* Datos disponibles */}

{t('law10.dataMaturity.availableData')}

    {availableData.map((item, idx) => (
  • {item.name} ({item.article})
  • ))}
{/* Datos estimables */}

{t('law10.dataMaturity.estimableData')}

    {estimableData.map((item, idx) => (
  • {item.name} ({item.article}) - {item.error}
  • ))}
{/* Datos no disponibles */}

{t('law10.dataMaturity.unavailableData')}

    {missingData.map((item, idx) => (
  • {item.name} ({item.article}) {item.note && - {item.note}}
  • ))}
{/* Inversion sugerida */}

{t('law10.dataMaturity.investment.title')}

{/* Fase 1 */}

{t('law10.dataMaturity.investment.phase1.title')}

  • {t('law10.dataMaturity.investment.phase1.realAsaTracking')} 5-8K
  • {t('law10.dataMaturity.investment.phase1.ticketingSystem')} 15-25K
  • {t('law10.dataMaturity.investment.phase1.languageEnrichment')} 2K
  • {t('law10.dataMaturity.investment.phase1.subtotal')} 22-35K
{/* Fase 2 */}

{t('law10.dataMaturity.investment.phase2.title')}

  • {t('law10.dataMaturity.investment.phase2.coverage247')} 65K/yr
  • {t('law10.dataMaturity.investment.phase2.aiCopilot')} 35K + 8K/mo
  • {t('law10.dataMaturity.investment.phase2.enacAuditor')} 12-18K/yr
  • {t('law10.dataMaturity.investment.phase2.subtotalYear1')} 112-118K
{/* Totales - usar datos reales cuando disponibles */}

{t('law10.dataMaturity.investment.totals.totalInvestment')}

{currentAnnualCost > 0 ? formatCurrency(Math.round(currentAnnualCost * 0.05)) : '134-153K'}

{t('law10.dataMaturity.investment.totals.percentAnnualCost')}

{t('law10.dataMaturity.investment.totals.riskAvoided')}

{currentAnnualCost > 0 ? formatCurrency(Math.min(1000000, currentAnnualCost * 0.3)) : '750K-1M'}

{t('law10.dataMaturity.investment.totals.potentialSanctions')}

{t('law10.dataMaturity.investment.totals.complianceRoi')}

{data.economicModel?.roi3yr ? `${data.economicModel.roi3yr}%` : '490-650%'}

); } // ============================================ // COMPONENTE PRINCIPAL // ============================================ export function Law10Tab({ data }: Law10TabProps) { const { t } = useTranslation(); // Evaluar compliance para cada requisito const complianceResults = { law07: evaluateLaw07Compliance(data, t), law01: evaluateLaw01Compliance(data, t), law02: evaluateLaw02Compliance(data, t), law09: evaluateLaw09Compliance(data, t), }; return (
{/* Header con Countdown */} {/* Secciones de Analisis - Formato horizontal sin columnas */}
{/* LAW-01: Velocidad de Respuesta */} {/* LAW-02: Calidad de Resolucion */} {/* LAW-07: Cobertura Horaria */}
{/* Resumen de Cumplimiento */} {/* Madurez de Datos para Compliance */}
); } export default Law10Tab;