81 lines
3.3 KiB
TypeScript
81 lines
3.3 KiB
TypeScript
'use client'
|
|
import { useEffect, useState } from 'react'
|
|
import { isSuppressed, type PiramideData } from '@/lib/dashboard-types'
|
|
import { queries } from '@/lib/dashboard-queries'
|
|
import type { DashboardFilters } from '@/lib/dashboard-types'
|
|
import { WidgetShell } from './widget-shell'
|
|
import { ETARIO_ORDER } from '@/lib/dashboard-utils'
|
|
|
|
const GENERO_COLOR: Record<string, string> = {
|
|
'Femenino': '#EE761A',
|
|
'Masculino': '#43BBC8',
|
|
'No binario/Otro': '#F8AD13',
|
|
'Prefiero no decir':'#6b7280',
|
|
}
|
|
|
|
export function PiramideDemografica({ surveyId, filters }: { surveyId: string; filters: DashboardFilters }) {
|
|
const [data, setData] = useState<PiramideData | null>(null)
|
|
const [sup, setSup] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setData(null); setSup(false)
|
|
queries.piramide(surveyId, filters)
|
|
.then(d => { if (isSuppressed(d)) setSup(true); else setData(d as PiramideData) })
|
|
.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 generos = ['Femenino','Masculino','No binario/Otro']
|
|
|
|
return (
|
|
<WidgetShell title="👥 Pirámide demográfica" status={status} n={data?.n_total} colSpan={6}>
|
|
{data && (
|
|
<div className="db-piramide">
|
|
<div className="db-piramide-legend" aria-label="Leyenda">
|
|
{generos.map(g => (
|
|
<span key={g} className="db-legend-item">
|
|
<span className="db-legend-dot" style={{ background: GENERO_COLOR[g] }} />
|
|
{g}
|
|
</span>
|
|
))}
|
|
</div>
|
|
{ETARIO_ORDER.filter(e => e !== 'Prefiero no decir').map(etario => {
|
|
const row = generos.map(gen => {
|
|
const cell = data.data.find(c => c.etario === etario && c.genero === gen)
|
|
return { gen, n: cell?.n ?? null, suppressed: cell?.suppressed ?? true }
|
|
})
|
|
return (
|
|
<div key={etario} className="db-piramide-row" role="row" aria-label={etario}>
|
|
<span className="db-piramide-label">{etario}</span>
|
|
<div className="db-piramide-bars">
|
|
{row.map(({ gen, n, suppressed }) => (
|
|
<div key={gen} className="db-piramide-bar-wrap"
|
|
style={{ flex: `0 0 ${Math.max(10, (n ?? 0) / maxN * 100)}%` }}>
|
|
<div className="db-piramide-bar"
|
|
style={{
|
|
width: suppressed ? '100%' : `${(n ?? 0) / maxN * 100}%`,
|
|
background: suppressed ? '#2a3547' : GENERO_COLOR[gen],
|
|
opacity: suppressed ? 0.4 : 1,
|
|
}}
|
|
title={suppressed ? 'Datos insuficientes' : `${gen}: n=${n}`}
|
|
role="cell"
|
|
aria-label={suppressed ? `${gen}: suprimido` : `${gen}: ${n}`}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<span className="db-piramide-n">
|
|
{row.every(r => r.suppressed) ? '🔒' : row.reduce((s, r) => s + (r.n ?? 0), 0)}
|
|
</span>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)}
|
|
</WidgetShell>
|
|
)
|
|
}
|