Con marca y dashboards
This commit is contained in:
80
components/dashboard/piramide-demografica.tsx
Normal file
80
components/dashboard/piramide-demografica.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user