Compare commits
4 Commits
820e8b4887
...
a48aca0a26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a48aca0a26 | ||
|
|
20e9d213bb | ||
|
|
c5c88f6f21 | ||
|
|
cbea968776 |
@@ -311,6 +311,7 @@ function generateCausalAnalysis(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'economy_cpi':
|
case 'economy_cpi':
|
||||||
|
case 'economy_costs': // También manejar el ID del backend
|
||||||
// Análisis de CPI
|
// Análisis de CPI
|
||||||
if (CPI > 3.5) {
|
if (CPI > 3.5) {
|
||||||
const excessCPI = CPI - CPI_TCO;
|
const excessCPI = CPI - CPI_TCO;
|
||||||
@@ -573,6 +574,29 @@ function DimensionCard({
|
|||||||
// ========== v3.16: COMPONENTE PRINCIPAL ==========
|
// ========== v3.16: COMPONENTE PRINCIPAL ==========
|
||||||
|
|
||||||
export function DimensionAnalysisTab({ data }: DimensionAnalysisTabProps) {
|
export function DimensionAnalysisTab({ data }: DimensionAnalysisTabProps) {
|
||||||
|
// DEBUG: Verificar CPI en dimensión vs heatmapData
|
||||||
|
const economyDim = data.dimensions.find(d =>
|
||||||
|
d.id === 'economy_costs' || d.name === 'economy_costs' ||
|
||||||
|
d.id === 'economy_cpi' || d.name === 'economy_cpi'
|
||||||
|
);
|
||||||
|
const heatmapData = data.heatmapData;
|
||||||
|
const totalCostVolume = heatmapData.reduce((sum, h) => sum + (h.cost_volume || h.volume), 0);
|
||||||
|
const hasCpiField = heatmapData.some(h => h.cpi !== undefined && h.cpi > 0);
|
||||||
|
const calculatedCPI = hasCpiField
|
||||||
|
? (totalCostVolume > 0
|
||||||
|
? heatmapData.reduce((sum, h) => sum + (h.cpi || 0) * (h.cost_volume || h.volume), 0) / totalCostVolume
|
||||||
|
: 0)
|
||||||
|
: (totalCostVolume > 0
|
||||||
|
? heatmapData.reduce((sum, h) => sum + (h.annual_cost || 0), 0) / totalCostVolume
|
||||||
|
: 0);
|
||||||
|
|
||||||
|
console.log('🔍 DimensionAnalysisTab DEBUG:');
|
||||||
|
console.log(' - economyDim found:', !!economyDim, economyDim?.id || economyDim?.name);
|
||||||
|
console.log(' - economyDim.kpi.value:', economyDim?.kpi?.value);
|
||||||
|
console.log(' - calculatedCPI from heatmapData:', `€${calculatedCPI.toFixed(2)}`);
|
||||||
|
console.log(' - hasCpiField:', hasCpiField);
|
||||||
|
console.log(' - MATCH:', economyDim?.kpi?.value === `€${calculatedCPI.toFixed(2)}`);
|
||||||
|
|
||||||
// Filter out agentic_readiness (has its own tab)
|
// Filter out agentic_readiness (has its own tab)
|
||||||
const coreDimensions = data.dimensions.filter(d => d.name !== 'agentic_readiness');
|
const coreDimensions = data.dimensions.filter(d => d.name !== 'agentic_readiness');
|
||||||
|
|
||||||
|
|||||||
@@ -427,6 +427,9 @@ function UnifiedKPIBenchmark({ heatmapData }: { heatmapData: HeatmapDataPoint[]
|
|||||||
: 0)
|
: 0)
|
||||||
: (totalCostVolume > 0 ? totalAnnualCost / totalCostVolume : 0);
|
: (totalCostVolume > 0 ? totalAnnualCost / totalCostVolume : 0);
|
||||||
|
|
||||||
|
// DEBUG: Log CPI calculation
|
||||||
|
console.log('🔍 ExecutiveSummaryTab CPI:', `€${cpi.toFixed(2)}`, { hasCpiField, totalCostVolume });
|
||||||
|
|
||||||
// Volume-weighted metrics
|
// Volume-weighted metrics
|
||||||
const operacion = {
|
const operacion = {
|
||||||
aht: aht,
|
aht: aht,
|
||||||
|
|||||||
@@ -811,6 +811,48 @@ export const generateAnalysis = async (
|
|||||||
console.log('📊 Heatmap generado desde backend (fallback - sin parsedInteractions)');
|
console.log('📊 Heatmap generado desde backend (fallback - sin parsedInteractions)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v4.5: SINCRONIZAR CPI de dimensión economía con heatmapData para consistencia entre tabs
|
||||||
|
// El heatmapData contiene el CPI calculado correctamente (con cost_volume ponderado)
|
||||||
|
// La dimensión economía fue calculada en mapBackendResultsToAnalysisData con otra fórmula
|
||||||
|
// Actualizamos la dimensión para que muestre el mismo valor que Executive Summary
|
||||||
|
if (mapped.heatmapData && mapped.heatmapData.length > 0) {
|
||||||
|
const heatmapData = mapped.heatmapData;
|
||||||
|
const totalCostVolume = heatmapData.reduce((sum, h) => sum + (h.cost_volume || h.volume), 0);
|
||||||
|
const hasCpiField = heatmapData.some(h => h.cpi !== undefined && h.cpi > 0);
|
||||||
|
|
||||||
|
let globalCPI: number;
|
||||||
|
if (hasCpiField) {
|
||||||
|
// CPI real disponible: promedio ponderado por cost_volume
|
||||||
|
globalCPI = totalCostVolume > 0
|
||||||
|
? heatmapData.reduce((sum, h) => sum + (h.cpi || 0) * (h.cost_volume || h.volume), 0) / totalCostVolume
|
||||||
|
: 0;
|
||||||
|
} else {
|
||||||
|
// Fallback: annual_cost / cost_volume
|
||||||
|
const totalAnnualCost = heatmapData.reduce((sum, h) => sum + (h.annual_cost || 0), 0);
|
||||||
|
globalCPI = totalCostVolume > 0 ? totalAnnualCost / totalCostVolume : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar la dimensión de economía con el CPI calculado desde heatmap
|
||||||
|
// Buscar tanto economy_costs (backend) como economy_cpi (frontend fallback)
|
||||||
|
const economyDimIdx = mapped.dimensions.findIndex(d =>
|
||||||
|
d.id === 'economy_costs' || d.name === 'economy_costs' ||
|
||||||
|
d.id === 'economy_cpi' || d.name === 'economy_cpi'
|
||||||
|
);
|
||||||
|
if (economyDimIdx >= 0 && globalCPI > 0) {
|
||||||
|
const CPI_BENCHMARK = 5.00;
|
||||||
|
const cpiDiff = globalCPI - CPI_BENCHMARK;
|
||||||
|
const cpiStatus = cpiDiff <= 0 ? 'positive' : cpiDiff <= 0.5 ? 'neutral' : 'negative';
|
||||||
|
|
||||||
|
mapped.dimensions[economyDimIdx].kpi = {
|
||||||
|
label: 'Coste por Interacción',
|
||||||
|
value: `€${globalCPI.toFixed(2)}`,
|
||||||
|
change: `vs benchmark €${CPI_BENCHMARK.toFixed(2)}`,
|
||||||
|
changeType: cpiStatus as 'positive' | 'neutral' | 'negative'
|
||||||
|
};
|
||||||
|
console.log(`💰 CPI sincronizado: €${globalCPI.toFixed(2)} (desde heatmapData, consistente con Executive Summary)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// v3.5: Calcular drilldownData PRIMERO (necesario para opportunities y roadmap)
|
// v3.5: Calcular drilldownData PRIMERO (necesario para opportunities y roadmap)
|
||||||
if (parsedInteractions && parsedInteractions.length > 0) {
|
if (parsedInteractions && parsedInteractions.length > 0) {
|
||||||
mapped.drilldownData = calculateDrilldownMetrics(parsedInteractions, costPerHour);
|
mapped.drilldownData = calculateDrilldownMetrics(parsedInteractions, costPerHour);
|
||||||
@@ -1020,6 +1062,65 @@ export const generateAnalysisFromCache = async (
|
|||||||
);
|
);
|
||||||
console.log('📊 Heatmap data points:', mapped.heatmapData?.length || 0);
|
console.log('📊 Heatmap data points:', mapped.heatmapData?.length || 0);
|
||||||
|
|
||||||
|
// v4.6: SINCRONIZAR CPI de dimensión economía con heatmapData para consistencia entre tabs
|
||||||
|
// (Mismo fix que en generateAnalysis - necesario para path de cache)
|
||||||
|
if (mapped.heatmapData && mapped.heatmapData.length > 0) {
|
||||||
|
const heatmapData = mapped.heatmapData;
|
||||||
|
const totalCostVolume = heatmapData.reduce((sum, h) => sum + (h.cost_volume || h.volume), 0);
|
||||||
|
const hasCpiField = heatmapData.some(h => h.cpi !== undefined && h.cpi > 0);
|
||||||
|
|
||||||
|
// DEBUG: Log CPI calculation details
|
||||||
|
console.log('🔍 CPI SYNC DEBUG (cache):');
|
||||||
|
console.log(' - heatmapData length:', heatmapData.length);
|
||||||
|
console.log(' - hasCpiField:', hasCpiField);
|
||||||
|
console.log(' - totalCostVolume:', totalCostVolume);
|
||||||
|
if (hasCpiField) {
|
||||||
|
console.log(' - Sample CPIs:', heatmapData.slice(0, 3).map(h => ({ skill: h.skill, cpi: h.cpi, cost_volume: h.cost_volume })));
|
||||||
|
}
|
||||||
|
|
||||||
|
let globalCPI: number;
|
||||||
|
if (hasCpiField) {
|
||||||
|
globalCPI = totalCostVolume > 0
|
||||||
|
? heatmapData.reduce((sum, h) => sum + (h.cpi || 0) * (h.cost_volume || h.volume), 0) / totalCostVolume
|
||||||
|
: 0;
|
||||||
|
} else {
|
||||||
|
const totalAnnualCost = heatmapData.reduce((sum, h) => sum + (h.annual_cost || 0), 0);
|
||||||
|
console.log(' - totalAnnualCost (fallback):', totalAnnualCost);
|
||||||
|
globalCPI = totalCostVolume > 0 ? totalAnnualCost / totalCostVolume : 0;
|
||||||
|
}
|
||||||
|
console.log(' - globalCPI calculated:', globalCPI.toFixed(4));
|
||||||
|
|
||||||
|
// Buscar tanto economy_costs (backend) como economy_cpi (frontend fallback)
|
||||||
|
const dimensionIds = mapped.dimensions.map(d => ({ id: d.id, name: d.name }));
|
||||||
|
console.log(' - Available dimensions:', dimensionIds);
|
||||||
|
|
||||||
|
const economyDimIdx = mapped.dimensions.findIndex(d =>
|
||||||
|
d.id === 'economy_costs' || d.name === 'economy_costs' ||
|
||||||
|
d.id === 'economy_cpi' || d.name === 'economy_cpi'
|
||||||
|
);
|
||||||
|
console.log(' - economyDimIdx:', economyDimIdx);
|
||||||
|
|
||||||
|
if (economyDimIdx >= 0 && globalCPI > 0) {
|
||||||
|
const oldKpi = mapped.dimensions[economyDimIdx].kpi;
|
||||||
|
console.log(' - OLD KPI value:', oldKpi?.value);
|
||||||
|
|
||||||
|
const CPI_BENCHMARK = 5.00;
|
||||||
|
const cpiDiff = globalCPI - CPI_BENCHMARK;
|
||||||
|
const cpiStatus = cpiDiff <= 0 ? 'positive' : cpiDiff <= 0.5 ? 'neutral' : 'negative';
|
||||||
|
|
||||||
|
mapped.dimensions[economyDimIdx].kpi = {
|
||||||
|
label: 'Coste por Interacción',
|
||||||
|
value: `€${globalCPI.toFixed(2)}`,
|
||||||
|
change: `vs benchmark €${CPI_BENCHMARK.toFixed(2)}`,
|
||||||
|
changeType: cpiStatus as 'positive' | 'neutral' | 'negative'
|
||||||
|
};
|
||||||
|
console.log(' - NEW KPI value:', mapped.dimensions[economyDimIdx].kpi.value);
|
||||||
|
console.log(`💰 CPI sincronizado (cache): €${globalCPI.toFixed(2)}`);
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ CPI sync skipped: economyDimIdx=', economyDimIdx, 'globalCPI=', globalCPI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === DrilldownData: usar cacheado (rápido) o fallback a heatmap ===
|
// === DrilldownData: usar cacheado (rápido) o fallback a heatmap ===
|
||||||
if (cachedDrilldownData && cachedDrilldownData.length > 0) {
|
if (cachedDrilldownData && cachedDrilldownData.length > 0) {
|
||||||
// Usar drilldownData cacheado directamente (ya calculado al subir archivo)
|
// Usar drilldownData cacheado directamente (ya calculado al subir archivo)
|
||||||
|
|||||||
Reference in New Issue
Block a user