'use client' import { useEffect, useState } from 'react' import { isSuppressed, type IntensidadData } from '@/lib/dashboard-types' import { queries } from '@/lib/dashboard-queries' import type { DashboardFilters } from '@/lib/dashboard-types' import { WidgetShell } from './widget-shell' import { HORAS_ORDER } from '@/lib/dashboard-utils' // Hexbin implementado como CSS Grid con celdas coloreadas por densidad. // NO es scatter de individuos — cada celda es un agregado con N≥5. const BIENESTAR_BINS = ['1','2','3','4','5'] export function IntensidadProductividad({ surveyId, filters }: { surveyId: string; filters: DashboardFilters }) { const [data, setData] = useState(null) const [sup, setSup] = useState(false) useEffect(() => { setData(null); setSup(false) queries.intensidad(surveyId, filters) .then(d => { if (isSuppressed(d)) setSup(true); else setData(d as IntensidadData) }) .catch(() => {}) }, [surveyId, JSON.stringify(filters)]) const status = !data && !sup ? 'loading' : sup ? 'suppressed' : 'ok' const maxN = data ? Math.max(...data.data.map(c => c.n ?? 0), 1) : 1 const cellMap = new Map((data?.data ?? []).map(c => [`${c.horas}|${c.bienestar}`, c])) function cellColor(n: number | null): string { if (n === null) return '#1e2d42' const t = n / maxN return `rgba(67, 187, 200, ${0.1 + t * 0.9})` } return ( {data && (

Cada celda = respondentes agregados. Celdas sin color: N < 5 (suprimido).

{BIENESTAR_BINS.map(b => (
Bienestar {b}
))} {HORAS_ORDER.map(horas => ( <>
{horas}
{BIENESTAR_BINS.map(bien => { const cell = cellMap.get(`${horas}|${bien}`) const n = cell?.n ?? null const suppressed = cell?.suppressed ?? true return (
{!suppressed && n !== null && {n}}
) })} ))}
)} ) }