feat: translate AgenticReadinessTab and add translation infrastructure
Translate AgenticReadinessTab from Spanish to English with i18next support: - Replaced ~150 hardcoded Spanish strings with translation keys - Added comprehensive translation keys to en.json and es.json - Organized translations under agenticReadiness.* namespace - Includes: methodology, tier explanations, factor descriptions, UI labels Translation structure: - agenticReadiness.methodology: Index definition and categories - agenticReadiness.tiers: AUTOMATE, ASSIST, AUGMENT, HUMAN-ONLY - agenticReadiness.factors: Predictability, simplicity, volume, ROI - agenticReadiness.redFlags: CV, transfer, volume, data quality - agenticReadiness.table: Headers, filters, sorting - agenticReadiness.summary: Volume metrics and interpretations All UI strings now support EN/ES language switching. Frontend compiles successfully with no errors. https://claude.ai/code/session_01GNbnkFoESkRcnPr3bLCYDg
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
formatNumber,
|
||||
formatPercent,
|
||||
} from '../../config/designSystem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ============================================
|
||||
// RED FLAGS CONFIGURATION AND DETECTION
|
||||
@@ -38,50 +39,51 @@ interface RedFlagConfig {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const RED_FLAG_CONFIGS: RedFlagConfig[] = [
|
||||
// Note: These configs will be translated in the component using t() function
|
||||
const getRedFlagConfigs = (t: any): RedFlagConfig[] => [
|
||||
{
|
||||
id: 'cv_high',
|
||||
label: 'Critical AHT CV',
|
||||
shortLabel: 'CV',
|
||||
label: t('agenticReadiness.redFlags.cvCritical'),
|
||||
shortLabel: t('agenticReadiness.redFlags.cvCriticalShort'),
|
||||
threshold: 120,
|
||||
operator: '>',
|
||||
getValue: (q) => q.cv_aht,
|
||||
format: (v) => `${v.toFixed(0)}%`,
|
||||
color: 'red',
|
||||
description: 'Extreme variability - unpredictable processes'
|
||||
description: t('agenticReadiness.redFlags.cvCriticalDesc')
|
||||
},
|
||||
{
|
||||
id: 'transfer_high',
|
||||
label: 'Excessive Transfer',
|
||||
shortLabel: 'Transfer',
|
||||
label: t('agenticReadiness.redFlags.transferExcessive'),
|
||||
shortLabel: t('agenticReadiness.redFlags.transferExcessiveShort'),
|
||||
threshold: 50,
|
||||
operator: '>',
|
||||
getValue: (q) => q.transfer_rate,
|
||||
format: (v) => `${v.toFixed(0)}%`,
|
||||
color: 'orange',
|
||||
description: 'High complexity - requires frequent escalation'
|
||||
description: t('agenticReadiness.redFlags.transferExcessiveDesc')
|
||||
},
|
||||
{
|
||||
id: 'volume_low',
|
||||
label: 'Insufficient Volume',
|
||||
shortLabel: 'Vol',
|
||||
label: t('agenticReadiness.redFlags.volumeInsufficient'),
|
||||
shortLabel: t('agenticReadiness.redFlags.volumeInsufficientShort'),
|
||||
threshold: 50,
|
||||
operator: '<',
|
||||
getValue: (q) => q.volume,
|
||||
format: (v) => v.toLocaleString(),
|
||||
color: 'slate',
|
||||
description: 'Negative ROI - volume doesn\'t justify investment'
|
||||
description: t('agenticReadiness.redFlags.volumeInsufficientDesc')
|
||||
},
|
||||
{
|
||||
id: 'valid_low',
|
||||
label: 'Low Data Quality',
|
||||
shortLabel: 'Valid',
|
||||
label: t('agenticReadiness.redFlags.dataQualityLow'),
|
||||
shortLabel: t('agenticReadiness.redFlags.dataQualityLowShort'),
|
||||
threshold: 30,
|
||||
operator: '<',
|
||||
getValue: (q) => q.volume > 0 ? (q.volumeValid / q.volume) * 100 : 0,
|
||||
format: (v) => `${v.toFixed(0)}%`,
|
||||
color: 'amber',
|
||||
description: 'Unreliable data - distorted metrics'
|
||||
description: t('agenticReadiness.redFlags.dataQualityLowDesc')
|
||||
}
|
||||
];
|
||||
|
||||
@@ -91,10 +93,10 @@ interface DetectedRedFlag {
|
||||
value: number;
|
||||
}
|
||||
|
||||
function detectRedFlags(queue: OriginalQueueMetrics): DetectedRedFlag[] {
|
||||
function detectRedFlags(queue: OriginalQueueMetrics, configs: RedFlagConfig[]): DetectedRedFlag[] {
|
||||
const flags: DetectedRedFlag[] = [];
|
||||
|
||||
for (const config of RED_FLAG_CONFIGS) {
|
||||
for (const config of configs) {
|
||||
const value = config.getValue(queue);
|
||||
const hasFlag = config.operator === '>'
|
||||
? value > config.threshold
|
||||
@@ -109,13 +111,13 @@ function detectRedFlags(queue: OriginalQueueMetrics): DetectedRedFlag[] {
|
||||
}
|
||||
|
||||
// v3.5: Individual Red Flag badge component
|
||||
function RedFlagBadge({ flag, size = 'sm' }: { flag: DetectedRedFlag; size?: 'sm' | 'md' }) {
|
||||
function RedFlagBadge({ flag, size = 'sm', t }: { flag: DetectedRedFlag; size?: 'sm' | 'md'; t: any }) {
|
||||
const sizeClasses = size === 'md' ? 'px-2 py-1 text-xs' : 'px-1.5 py-0.5 text-[10px]';
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center gap-1 ${sizeClasses} rounded bg-red-100 text-red-700 font-medium`}
|
||||
title={`${flag.config.label}: ${flag.config.format(flag.value)} (umbral: ${flag.config.operator}${flag.config.threshold})`}
|
||||
title={`${flag.config.label}: ${flag.config.format(flag.value)} ${t('agenticReadiness.redFlags.threshold', { operator: flag.config.operator, value: flag.config.threshold })}`}
|
||||
>
|
||||
<XCircle className="w-3 h-3" />
|
||||
{flag.config.shortLabel}: {flag.config.format(flag.value)}
|
||||
@@ -124,8 +126,8 @@ function RedFlagBadge({ flag, size = 'sm' }: { flag: DetectedRedFlag; size?: 'sm
|
||||
}
|
||||
|
||||
// v3.5: Componente de lista de Red Flags de una cola
|
||||
function RedFlagsList({ queue, compact = false }: { queue: OriginalQueueMetrics; compact?: boolean }) {
|
||||
const flags = detectRedFlags(queue);
|
||||
function RedFlagsList({ queue, compact = false, configs, t }: { queue: OriginalQueueMetrics; compact?: boolean; configs: RedFlagConfig[]; t: any }) {
|
||||
const flags = detectRedFlags(queue, configs);
|
||||
|
||||
if (flags.length === 0) return null;
|
||||
|
||||
@@ -133,7 +135,7 @@ function RedFlagsList({ queue, compact = false }: { queue: OriginalQueueMetrics;
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{flags.map(flag => (
|
||||
<RedFlagBadge key={flag.config.id} flag={flag} size="sm" />
|
||||
<RedFlagBadge key={flag.config.id} flag={flag} size="sm" t={t} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -146,7 +148,7 @@ function RedFlagsList({ queue, compact = false }: { queue: OriginalQueueMetrics;
|
||||
<XCircle className="w-3 h-3 text-red-500 flex-shrink-0" />
|
||||
<span className="text-red-700 font-medium">{flag.config.label}:</span>
|
||||
<span className="text-red-600">{flag.config.format(flag.value)}</span>
|
||||
<span className="text-gray-400">(umbral: {flag.config.operator}{flag.config.threshold})</span>
|
||||
<span className="text-gray-400">({t('agenticReadiness.redFlags.threshold', { operator: flag.config.operator, value: flag.config.threshold })})</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -173,46 +175,46 @@ interface TierExplanation {
|
||||
recommendation: string;
|
||||
}
|
||||
|
||||
const TIER_EXPLANATIONS: TierExplanation[] = [
|
||||
const getTierExplanations = (t: any): TierExplanation[] => [
|
||||
{
|
||||
tier: 'AUTOMATE',
|
||||
label: 'Automatizable',
|
||||
label: t('agenticReadiness.automatable'),
|
||||
emoji: '🤖',
|
||||
color: '#10b981',
|
||||
bgColor: '#d1fae5',
|
||||
description: 'Procesos maduros listos para automatización completa con agente virtual.',
|
||||
criteria: 'Score ≥7.5: CV AHT <75%, Transfer <15%, Volumen >500/mes',
|
||||
recommendation: 'Desplegar agente virtual con resolución autónoma'
|
||||
description: t('agenticReadiness.automatableDesc'),
|
||||
criteria: t('agenticReadiness.automatableCriteria'),
|
||||
recommendation: t('agenticReadiness.automatableAction')
|
||||
},
|
||||
{
|
||||
tier: 'ASSIST',
|
||||
label: 'Asistible',
|
||||
label: t('agenticReadiness.assistable'),
|
||||
emoji: '🤝',
|
||||
color: '#3b82f6',
|
||||
bgColor: '#dbeafe',
|
||||
description: 'Candidatos a Copilot: IA asiste al agente humano en tiempo real.',
|
||||
criteria: 'Score 5.5-7.5: Procesos semiestructurados con variabilidad moderada',
|
||||
recommendation: 'Implementar Copilot con sugerencias y búsqueda inteligente'
|
||||
description: t('agenticReadiness.assistableDesc'),
|
||||
criteria: t('agenticReadiness.assistableCriteria'),
|
||||
recommendation: t('agenticReadiness.assistableAction')
|
||||
},
|
||||
{
|
||||
tier: 'AUGMENT',
|
||||
label: 'Optimizable',
|
||||
label: t('agenticReadiness.optimizable'),
|
||||
emoji: '📚',
|
||||
color: '#f59e0b',
|
||||
bgColor: '#fef3c7',
|
||||
description: 'Requiere herramientas y estandarización antes de automatizar.',
|
||||
criteria: 'Score 3.5-5.5: Alta variabilidad o complejidad, necesita optimización',
|
||||
recommendation: 'Desplegar KB mejorada, scripts guiados, herramientas de soporte'
|
||||
description: t('agenticReadiness.optimizableDesc'),
|
||||
criteria: t('agenticReadiness.optimizableCriteria'),
|
||||
recommendation: t('agenticReadiness.optimizableAction')
|
||||
},
|
||||
{
|
||||
tier: 'HUMAN-ONLY',
|
||||
label: 'Solo Humano',
|
||||
label: t('agenticReadiness.humanOnly'),
|
||||
emoji: '👤',
|
||||
color: '#6b7280',
|
||||
bgColor: '#f3f4f6',
|
||||
description: 'No apto para automatización: volumen insuficiente o complejidad extrema.',
|
||||
criteria: 'Score <3.5 o Red Flags: CV >120%, Transfer >50%, Vol <50',
|
||||
recommendation: 'Mantener gestión humana, evaluar periódicamente'
|
||||
description: t('agenticReadiness.humanOnlyDesc'),
|
||||
criteria: t('agenticReadiness.humanOnlyCriteria'),
|
||||
recommendation: t('agenticReadiness.humanOnlyAction')
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user