import React, { useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { HelpCircle, ArrowUpDown, TrendingUp, TrendingDown } from 'lucide-react'; import { HeatmapDataPoint } from '../types'; import clsx from 'clsx'; interface HeatmapEnhancedProps { data: HeatmapDataPoint[]; } type SortKey = 'skill' | 'fcr' | 'aht' | 'csat' | 'quality'; type SortOrder = 'asc' | 'desc'; interface TooltipData { skill: string; metric: string; value: number; x: number; y: number; } const getCellColor = (value: number) => { if (value >= 95) return 'bg-emerald-600 text-white'; if (value >= 90) return 'bg-emerald-500 text-white'; if (value >= 85) return 'bg-green-400 text-green-900'; if (value >= 80) return 'bg-yellow-300 text-yellow-900'; if (value >= 70) return 'bg-amber-400 text-amber-900'; return 'bg-red-400 text-red-900'; }; const getPercentile = (value: number): string => { if (value >= 95) return 'P95+'; if (value >= 90) return 'P90-P95'; if (value >= 75) return 'P75-P90'; if (value >= 50) return 'P50-P75'; return ' = ({ data }) => { const [sortKey, setSortKey] = useState('skill'); const [sortOrder, setSortOrder] = useState('asc'); const [hoveredRow, setHoveredRow] = useState(null); const [tooltip, setTooltip] = useState(null); const metrics: Array<{ key: keyof HeatmapDataPoint['metrics']; label: string }> = [ { key: 'fcr', label: 'FCR' }, { key: 'aht', label: 'AHT' }, { key: 'csat', label: 'CSAT' }, { key: 'quality', label: 'Quality' }, ]; const handleSort = (key: SortKey) => { if (sortKey === key) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortKey(key); setSortOrder('desc'); } }; const sortedData = [...data].sort((a, b) => { let aValue: number | string; let bValue: number | string; if (sortKey === 'skill') { aValue = a.skill; bValue = b.skill; } else { aValue = a.metrics[sortKey]; bValue = b.metrics[sortKey]; } if (typeof aValue === 'string' && typeof bValue === 'string') { return sortOrder === 'asc' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); } return sortOrder === 'asc' ? (aValue as number) - (bValue as number) : (bValue as number) - (aValue as number); }); const handleCellHover = ( skill: string, metric: string, value: number, event: React.MouseEvent ) => { const rect = event.currentTarget.getBoundingClientRect(); setTooltip({ skill, metric, value, x: rect.left + rect.width / 2, y: rect.top, }); }; const handleCellLeave = () => { setTooltip(null); }; return (

Beyond CX Heatmap™

Mapa de calor de Readiness Agéntico por skill. Muestra el rendimiento en métricas clave para identificar fortalezas y áreas de mejora.
Click en columnas para ordenar
{metrics.map(({ key, label }) => ( ))} {sortedData.map(({ skill, metrics: skillMetrics }, index) => ( setHoveredRow(skill)} onMouseLeave={() => setHoveredRow(null)} className={clsx( 'border-t border-slate-200 transition-colors', hoveredRow === skill && 'bg-blue-50' )} > {metrics.map(({ key }) => { const value = skillMetrics[key]; return ( ); })} ))}
handleSort('skill')} className="p-3 font-semibold text-slate-700 text-left cursor-pointer hover:bg-slate-100 transition-colors" >
Skill/Proceso
handleSort(key)} className="p-3 font-semibold text-slate-700 text-center cursor-pointer hover:bg-slate-100 transition-colors uppercase" >
{label}
{skill} handleCellHover(skill, key.toUpperCase(), value, e)} onMouseLeave={handleCellLeave} > {value}
{/* Legend */}
Leyenda:
<70 (Bajo)
70-85 (Medio)
85-90 (Bueno)
90+ (Excelente)
{/* Tooltip */} {tooltip && (
{tooltip.skill}
{tooltip.metric}: {tooltip.value}%
Percentil: {getPercentile(tooltip.value)}
{tooltip.value >= 85 ? ( <> Por encima del promedio ) : ( <> Oportunidad de mejora )}
)}
); }; export default HeatmapEnhanced;