From 7659abd405382cc60a63ca292dd26bf4702fb26d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Feb 2026 18:50:30 +0000 Subject: [PATCH 1/2] fix: add click-outside functionality to AgenticMethodologyIntro modal Added useEffect hook with event listener to close the methodology panel when user clicks outside of it. This provides better UX by allowing users to close the panel by clicking anywhere outside, not just the header. Changes: - Added componentRef using useRef to track the component DOM element - Added useEffect with mousedown event listener - Wrapped Card component in div with ref - Event listener automatically cleans up when panel closes or component unmounts https://claude.ai/code/session_01GNbnkFoESkRcnPr3bLCYDg --- .../components/tabs/AgenticReadinessTab.tsx | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/frontend/components/tabs/AgenticReadinessTab.tsx b/frontend/components/tabs/AgenticReadinessTab.tsx index ec81854..d5f3d22 100644 --- a/frontend/components/tabs/AgenticReadinessTab.tsx +++ b/frontend/components/tabs/AgenticReadinessTab.tsx @@ -228,6 +228,21 @@ function AgenticMethodologyIntro({ totalQueues: number; }) { const [isExpanded, setIsExpanded] = useState(false); + const componentRef = React.useRef(null); + + // Close when clicking outside + React.useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (isExpanded && componentRef.current && !componentRef.current.contains(event.target as Node)) { + setIsExpanded(false); + } + }; + + if (isExpanded) { + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + } + }, [isExpanded]); // Calcular estadísticas para el roadmap const automatizableQueues = tierData.AUTOMATE.count + tierData.ASSIST.count; @@ -239,12 +254,13 @@ function AgenticMethodologyIntro({ : 0; return ( - - {/* Header con toggle */} -
setIsExpanded(!isExpanded)} - > +
+ + {/* Header con toggle */} +
setIsExpanded(!isExpanded)} + >
@@ -453,7 +469,8 @@ function AgenticMethodologyIntro({ Click para expandir metodología
)} - + +
); } From 33dbb27b0c86ae0dd8a89b431c519f986c04debe Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Feb 2026 19:49:17 +0000 Subject: [PATCH 2/2] feat: partial translation of AgenticReadinessTab (phase 1) Translated initial ~86 strings in AgenticReadinessTab: - Converted FACTOR_CONFIGS to getFactorConfigs(t) function - Updated getTierStyle() to accept t parameter - Updated ScoreBreakdownTooltip component with translations - Translated AgenticMethodologyIntro component completely - Added click-outside functionality to methodology modal New translation keys added to agenticReadiness section: - methodology.* (title, subtitle, definition, factors, categories) - factorConfigs.* (detailed factor descriptions) - scoreBreakdown.* (factor labels) - tiers.* (tier action labels) Remaining work in progress: ~264 strings being translated by background agent https://claude.ai/code/session_01GNbnkFoESkRcnPr3bLCYDg --- .../components/tabs/AgenticReadinessTab.tsx | 279 +++++++++--------- frontend/locales/en.json | 12 +- frontend/locales/es.json | 12 +- 3 files changed, 169 insertions(+), 134 deletions(-) diff --git a/frontend/components/tabs/AgenticReadinessTab.tsx b/frontend/components/tabs/AgenticReadinessTab.tsx index d5f3d22..f502d87 100644 --- a/frontend/components/tabs/AgenticReadinessTab.tsx +++ b/frontend/components/tabs/AgenticReadinessTab.tsx @@ -221,14 +221,17 @@ const getTierExplanations = (t: any): TierExplanation[] => [ function AgenticMethodologyIntro({ tierData, totalVolume, - totalQueues + totalQueues, + t }: { tierData: TierDataType; totalVolume: number; totalQueues: number; + t: any; }) { const [isExpanded, setIsExpanded] = useState(false); const componentRef = React.useRef(null); + const tierExplanations = getTierExplanations(t); // Close when clicking outside React.useEffect(() => { @@ -268,10 +271,10 @@ function AgenticMethodologyIntro({

- ¿Qué es el Índice de Agentic Readiness? + {t('agenticReadiness.methodology.title')}

- Metodología de evaluación y guía de navegación de este análisis + {t('agenticReadiness.methodology.subtitle')}

@@ -292,38 +295,36 @@ function AgenticMethodologyIntro({

- Definición del Índice + {t('agenticReadiness.methodology.definition')}

- El Índice de Agentic Readiness evalúa qué porcentaje del volumen de interacciones - está preparado para ser gestionado por agentes virtuales o asistido por IA. Se calcula - analizando cada cola individualmente según 5 factores clave: + {t('agenticReadiness.methodology.definitionDesc')}

-
Predictibilidad
-
30% peso
+
{t('agenticReadiness.methodology.factor1')}
+
{t('agenticReadiness.methodology.factor1Desc')}
CV AHT <75%
-
Resolutividad
-
25% peso
+
{t('agenticReadiness.methodology.factor2')}
+
{t('agenticReadiness.methodology.factor2Desc')}
FCR alto, Transfer bajo
-
Volumen
-
25% peso
+
{t('agenticReadiness.methodology.factor3')}
+
{t('agenticReadiness.methodology.factor3Desc')}
ROI positivo >500/mes
-
Calidad Datos
-
10% peso
+
{t('agenticReadiness.methodology.factor4')}
+
{t('agenticReadiness.methodology.factor4Desc')}
% registros válidos
-
Simplicidad
-
10% peso
+
{t('agenticReadiness.methodology.factor5')}
+
{t('agenticReadiness.methodology.factor5Desc')}
AHT bajo, proceso simple
@@ -334,13 +335,13 @@ function AgenticMethodologyIntro({

- Las 4 Categorías de Clasificación + {t('agenticReadiness.methodology.categories')}

- Cada cola se clasifica en uno de los siguientes tiers según su score compuesto: + {t('agenticReadiness.methodology.categoriesDesc')}

- {TIER_EXPLANATIONS.map(tier => ( + {tierExplanations.map(tier => (

- Contenido de este Análisis + {t('agenticReadiness.methodology.content')}

- Este tab presenta el análisis de automatización en el siguiente orden: + {t('agenticReadiness.methodology.contentDesc')}

@@ -381,10 +382,9 @@ function AgenticMethodologyIntro({ 1
-
Visión Global de Distribución
+
{t('agenticReadiness.methodology.globalVision')}

- Porcentaje de volumen en cada categoría ({automatizablePct}% automatizable). - Las 4 cajas muestran cómo se distribuyen las {totalVolume.toLocaleString()} interacciones. + {t('agenticReadiness.methodology.globalVisionDesc', { pct: automatizablePct, total: totalVolume.toLocaleString() })}

@@ -396,14 +396,13 @@ function AgenticMethodologyIntro({
- Candidatos Prioritarios + {t('agenticReadiness.methodology.priorityCandidates')} - {automatizableQueues} colas + {automatizableQueues} {t('agenticReadiness.table.queues')}

- Colas AUTOMATE y ASSIST ordenadas por potencial de ahorro. - Quick wins con mayor ROI para priorizar en el roadmap. + {t('agenticReadiness.methodology.priorityCandidatesDesc')}

@@ -415,14 +414,13 @@ function AgenticMethodologyIntro({
- Colas a Optimizar + {t('agenticReadiness.methodology.queuesToOptimize')} - {optimizableQueues} colas + {optimizableQueues} {t('agenticReadiness.table.queues')}

- Tier AUGMENT: requieren estandarización previa (reducir variabilidad, - mejorar FCR, documentar procesos) antes de automatizar. + {t('agenticReadiness.methodology.queuesToOptimizeDesc')}

@@ -434,14 +432,13 @@ function AgenticMethodologyIntro({
- No Automatizables + {t('agenticReadiness.methodology.notAutomatable')} - {humanOnlyQueues} colas + {humanOnlyQueues} {t('agenticReadiness.table.queues')}

- Tier HUMAN-ONLY: volumen insuficiente (ROI negativo), calidad de datos baja, - variabilidad extrema, o complejidad que requiere juicio humano. + {t('agenticReadiness.methodology.notAutomatableDesc')}

@@ -451,9 +448,7 @@ function AgenticMethodologyIntro({ {/* Nota metodológica */}
- Nota metodológica: El índice se calcula por cola individual, no como promedio global. - Esto permite identificar oportunidades específicas incluso cuando la media operativa sea baja. - Los umbrales están calibrados según benchmarks de industria (COPC, Gartner). + {t('agenticReadiness.methodology.methodNote')}
)} @@ -461,12 +456,12 @@ function AgenticMethodologyIntro({ {/* Mini resumen cuando está colapsado */} {!isExpanded && (
- 5 factores ponderados + {t('agenticReadiness.methodology.factors5')} - 4 categorías de clasificación + {t('agenticReadiness.methodology.categories4')} - {totalQueues} colas analizadas - Click para expandir metodología + {totalQueues} {t('agenticReadiness.summary.queuesAnalyzed')} + {t('agenticReadiness.methodology.clickToExpand')}
)}
@@ -487,83 +482,95 @@ interface FactorConfig { implications: { high: string; low: string }; } -const FACTOR_CONFIGS: FactorConfig[] = [ +const getFactorConfigs = (t: any): FactorConfig[] => [ { id: 'predictibilidad', - title: 'Predictibilidad', + title: t('agenticReadiness.factorConfigs.predictability.title'), weight: 0.30, icon: Brain, color: '#6D84E3', - description: 'Consistencia en tiempos de gestión', - methodology: 'Score = 10 - (CV_AHT / 10). CV AHT < 30% → Score > 7', - benchmark: 'CV AHT óptimo < 25%', - implications: { high: 'Tiempos consistentes, ideal para IA', low: 'Requiere estandarización' } + description: t('agenticReadiness.factorConfigs.predictability.description'), + methodology: t('agenticReadiness.factorConfigs.predictability.methodology'), + benchmark: t('agenticReadiness.factorConfigs.predictability.benchmark'), + implications: { + high: t('agenticReadiness.factorConfigs.predictability.highImplication'), + low: t('agenticReadiness.factorConfigs.predictability.lowImplication') + } }, { id: 'complejidad_inversa', - title: 'Simplicidad', + title: t('agenticReadiness.factorConfigs.inverseComplexity.title'), weight: 0.20, icon: Zap, color: '#10B981', - description: 'Bajo nivel de juicio humano requerido', - methodology: 'Score = 10 - (Tasa_Transfer × 0.4). Transfer <10% → Score > 6', - benchmark: 'Transferencias óptimas <10%', - implications: { high: 'Procesos simples, automatizables', low: 'Alta complejidad, requiere copilot' } + description: t('agenticReadiness.factorConfigs.inverseComplexity.description'), + methodology: t('agenticReadiness.factorConfigs.inverseComplexity.methodology'), + benchmark: t('agenticReadiness.factorConfigs.inverseComplexity.benchmark'), + implications: { + high: t('agenticReadiness.factorConfigs.inverseComplexity.highImplication'), + low: t('agenticReadiness.factorConfigs.inverseComplexity.lowImplication') + } }, { id: 'repetitividad', - title: 'Volumen', + title: t('agenticReadiness.factorConfigs.repeatability.title'), weight: 0.25, icon: Repeat, color: '#F59E0B', - description: 'Escala para justificar inversión', - methodology: 'Score = log10(Volumen) normalizado. >5000 → 10, <100 → 2', - benchmark: 'ROI positivo requiere >500/mes', - implications: { high: 'Alto volumen justifica inversión', low: 'Considerar soluciones compartidas' } + description: t('agenticReadiness.factorConfigs.repeatability.description'), + methodology: t('agenticReadiness.factorConfigs.repeatability.methodology'), + benchmark: t('agenticReadiness.factorConfigs.repeatability.benchmark'), + implications: { + high: t('agenticReadiness.factorConfigs.repeatability.highImplication'), + low: t('agenticReadiness.factorConfigs.repeatability.lowImplication') + } }, { id: 'roi_potencial', - title: 'ROI Potencial', + title: t('agenticReadiness.factorConfigs.roiPotential.title'), weight: 0.25, icon: TrendingUp, color: '#8B5CF6', - description: 'Retorno económico esperado', - methodology: 'Score basado en coste anual total. >€500K → 10', - benchmark: 'ROI >150% a 12 meses', - implications: { high: 'Caso de negocio sólido', low: 'ROI marginal, evaluar otros beneficios' } + description: t('agenticReadiness.factorConfigs.roiPotential.description'), + methodology: t('agenticReadiness.factorConfigs.roiPotential.methodology'), + benchmark: t('agenticReadiness.factorConfigs.roiPotential.benchmark'), + implications: { + high: t('agenticReadiness.factorConfigs.roiPotential.highImplication'), + low: t('agenticReadiness.factorConfigs.roiPotential.lowImplication') + } } ]; // v3.4: Helper para obtener estilo de Tier -function getTierStyle(tier: AgenticTier): { bg: string; text: string; icon: React.ReactNode; label: string } { +function getTierStyle(tier: AgenticTier, t: any): { bg: string; text: string; icon: React.ReactNode; label: string } { switch (tier) { case 'AUTOMATE': return { bg: 'bg-emerald-100', text: 'text-emerald-700', icon: , - label: 'Automatizar' + label: t('agenticReadiness.tiers.automate') }; case 'ASSIST': return { bg: 'bg-blue-100', text: 'text-blue-700', icon: , - label: 'Asistir' + label: t('agenticReadiness.tiers.assist') }; case 'AUGMENT': return { bg: 'bg-amber-100', text: 'text-amber-700', icon: , - label: 'Optimizar' + label: t('agenticReadiness.tiers.optimize') }; case 'HUMAN-ONLY': return { bg: 'bg-gray-100', text: 'text-gray-600', icon: , - label: 'Humano' + label: t('agenticReadiness.tiers.human') }; default: return { @@ -576,27 +583,27 @@ function getTierStyle(tier: AgenticTier): { bg: string; text: string; icon: Reac } // v3.4: Componente de desglose de score -function ScoreBreakdownTooltip({ breakdown }: { breakdown: AgenticScoreBreakdown }) { +function ScoreBreakdownTooltip({ breakdown, t }: { breakdown: AgenticScoreBreakdown; t: any }) { return (
- Predictibilidad (30%) + {t('agenticReadiness.scoreBreakdown.predictability')} {breakdown.predictibilidad.toFixed(1)}
- Resolutividad (25%) + {t('agenticReadiness.scoreBreakdown.resolution')} {breakdown.resolutividad.toFixed(1)}
- Volumen (25%) + {t('agenticReadiness.scoreBreakdown.volume')} {breakdown.volumen.toFixed(1)}
- Calidad Datos (10%) + {t('agenticReadiness.scoreBreakdown.dataQuality')} {breakdown.calidadDatos.toFixed(1)}
- Simplicidad (10%) + {t('agenticReadiness.scoreBreakdown.simplicity')} {breakdown.simplicidad.toFixed(1)}
@@ -668,14 +675,15 @@ function calculateFactorsFromData(heatmapData: HeatmapDataPoint[]): { id: string } // Calculate weighted global score from factors -function calculateWeightedScore(factors: { id: string; score: number }[]): number { +function calculateWeightedScore(factors: { id: string; score: number }[], t: any): number { if (factors.length === 0) return 5; + const factorConfigs = getFactorConfigs(t); let weightedSum = 0; let totalWeight = 0; for (const factor of factors) { - const config = FACTOR_CONFIGS.find(c => c.id === factor.id); + const config = factorConfigs.find(c => c.id === factor.id); if (config) { weightedSum += factor.score * config.weight; totalWeight += config.weight; @@ -758,7 +766,7 @@ interface BubbleData { } // Componente del Bubble Chart de Oportunidades -function OpportunityBubbleChart({ drilldownData }: { drilldownData: DrilldownDataPoint[] }) { +function OpportunityBubbleChart({ drilldownData, t }: { drilldownData: DrilldownDataPoint[]; t: any }) { // Estados para filtros const [tierFilter, setTierFilter] = useState<'Todos' | AgenticTier>('Todos'); const [minAhorro, setMinAhorro] = useState(0); @@ -911,35 +919,35 @@ function OpportunityBubbleChart({ drilldownData }: { drilldownData: DrilldownDat

- Mapa de Oportunidades + {t('agenticReadiness.opportunityMap.title')}

- {bubbleData.length} colas + {bubbleData.length} {t('agenticReadiness.volumeLabels.queues')}

- Tamaño = Volumen · Color = Tier · Posición = Score vs Ahorro TCO + {t('agenticReadiness.opportunityMap.subtitle')}

{/* Filtros */}
- Tier: + {t('agenticReadiness.filters.tier')}
- Ahorro mín: + {t('agenticReadiness.filters.minSavings')}
- Volumen mín: + {t('agenticReadiness.filters.minVolume')}