feat(sprint-5/etapa-3): heatmap gradiente 4 stops + modo percentil + localStorage + fix zoom
- colors.ts: gradiente continuo verde→amarillo→naranja→rojo (interpolación N-stops genérica) - Renombra modos: relative→percentile (rank-based), absolute→minmax - Hook useHeatmapMode(): persiste selección en localStorage con clave inq-roi:heatmap-mode - HeatmapLegend: barra de 4 stops, labels P0/P50/P100 en modo percentil - HeatmapModeToggle: etiquetas "Percentil" / "Min-max" - pdf-sections.ts: leyenda PDF con 4 stops interpolados - BpmnCanvas: controles de zoom movidos a esquina inferior izquierda (evita logo bpmn.io) - Tests: 528 unitarios pasan; E2E recalibrados con vecino más cercano (distancia euclidiana) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,7 @@ describe('ActivitiesTable', () => {
|
||||
render(
|
||||
<ActivitiesTable
|
||||
perActivity={mockActivities}
|
||||
mode="relative"
|
||||
mode="percentile"
|
||||
currency="USD"
|
||||
highlightedId={null}
|
||||
onRowClick={vi.fn()}
|
||||
@@ -161,7 +161,7 @@ describe('ActivitiesTable', () => {
|
||||
render(
|
||||
<ActivitiesTable
|
||||
perActivity={mockActivities}
|
||||
mode="relative"
|
||||
mode="percentile"
|
||||
currency="USD"
|
||||
highlightedId={null}
|
||||
onRowClick={vi.fn()}
|
||||
@@ -177,7 +177,7 @@ describe('ActivitiesTable', () => {
|
||||
render(
|
||||
<ActivitiesTable
|
||||
perActivity={mockActivities}
|
||||
mode="relative"
|
||||
mode="percentile"
|
||||
currency="USD"
|
||||
highlightedId={null}
|
||||
onRowClick={onRowClick}
|
||||
@@ -193,7 +193,7 @@ describe('ActivitiesTable', () => {
|
||||
render(
|
||||
<ActivitiesTable
|
||||
perActivity={mockActivities}
|
||||
mode="relative"
|
||||
mode="percentile"
|
||||
currency="USD"
|
||||
highlightedId="task1"
|
||||
onRowClick={vi.fn()}
|
||||
@@ -208,7 +208,7 @@ describe('ActivitiesTable', () => {
|
||||
render(
|
||||
<ActivitiesTable
|
||||
perActivity={[]}
|
||||
mode="relative"
|
||||
mode="percentile"
|
||||
currency="USD"
|
||||
highlightedId={null}
|
||||
onRowClick={vi.fn()}
|
||||
|
||||
@@ -213,7 +213,7 @@ describe('buildTopActivitiesOption — estructura', () => {
|
||||
it('serie de tipo bar con hasta 10 items', () => {
|
||||
const xml = loadBpmn('medium-with-gateways.bpmn')
|
||||
const result = runSimulation(buildSimInput(xml, 100, 0.2))
|
||||
const option = buildTopActivitiesOption(result.perActivity, 'relative', 'USD')
|
||||
const option = buildTopActivitiesOption(result.perActivity, 'percentile', 'USD')
|
||||
const series = option.series as any[]
|
||||
expect(series[0].type).toBe('bar')
|
||||
expect(series[0].data.length).toBeLessThanOrEqual(10)
|
||||
@@ -225,7 +225,7 @@ describe('buildTopActivitiesOption — estructura', () => {
|
||||
const result = runSimulation(buildSimInput(xml, 200, 0))
|
||||
// En modo relativo con varianza real, la 1ra actividad → t=1 → rojo
|
||||
// En simple-linear todos cuestan igual → NEUTRAL_HEATMAP_COLOR
|
||||
const option = buildTopActivitiesOption(result.perActivity, 'relative', 'USD')
|
||||
const option = buildTopActivitiesOption(result.perActivity, 'percentile', 'USD')
|
||||
const series = option.series as any[]
|
||||
// Verifica que itemStyle.color existe en cada barra
|
||||
series[0].data.forEach((item: any) => {
|
||||
@@ -237,7 +237,7 @@ describe('buildTopActivitiesOption — estructura', () => {
|
||||
it('yAxis data contiene los nombres de actividades (truncados)', () => {
|
||||
const xml = loadBpmn('simple-linear.bpmn')
|
||||
const result = runSimulation(buildSimInput(xml, 100, 0))
|
||||
const option = buildTopActivitiesOption(result.perActivity, 'relative', 'USD')
|
||||
const option = buildTopActivitiesOption(result.perActivity, 'percentile', 'USD')
|
||||
const yAxis = option.yAxis as any
|
||||
expect(yAxis.data).toHaveLength(result.perActivity.length)
|
||||
// Los nombres deben existir (no vacíos)
|
||||
|
||||
Reference in New Issue
Block a user