/** * ProteccionPersonas.jsx — v3 CORREGIDA */ const VERDE = '#2D7A4F' const ROJO = '#C0392B' const GRIS = '#4A4E6A' const NARANJA = '#E8881A' const CON_HABITACULO = new Set([ 'Automóvil', 'Camioneta/Utilitario', 'Transporte De Pasajeros', 'Transporte De Carga', ]) const SIN_HABITACULO = new Set([ 'Motocicleta', 'Peatón', 'Bicicleta', 'Tracción A Sangre', 'Tracción a Sangre', ]) function pct(n, base) { return base > 0 ? `${((n / base) * 100).toFixed(1)}%` : '—' } function IndicadorChip({ titulo, icon, base, baseLabel, si, no, sd, colorSi = VERDE, }) { return (
{icon && {icon}} {titulo}
base: {base.toLocaleString('es-AR')} {baseLabel}
{base > 0 ? ( <>
{si > 0 && (
)} {no > 0 && (
)} {sd > 0 && (
)}
{[ { label: 'Sí', v: si, color: colorSi }, { label: 'No', v: no, color: ROJO }, { label: 'Sin dato', v: sd, color: GRIS }, ] .filter((item) => item.v > 0) .map((item) => (
{item.v.toLocaleString('es-AR')} {` ${item.label} (${pct(item.v, base)})`}
))}
) : (
Sin datos para esta categoría en el período seleccionado
)}
) } function SeccionAlcohol({ personas }) { const total = personas.length const pos = personas.filter((p) => p.prueba_alcohol === 'Positivo').length const neg = personas.filter((p) => p.prueba_alcohol === 'Negativo').length const nr = personas.filter((p) => p.prueba_alcohol === 'No se Realizó').length const sd = total - pos - neg - nr const realizadas = pos + neg const items = [ { label: 'Positivo', v: pos, color: ROJO }, { label: 'Negativo', v: neg, color: VERDE }, { label: 'No realizada', v: nr, color: NARANJA }, { label: 'Sin dato', v: sd, color: GRIS }, ].filter((i) => i.v > 0) return (
Prueba de alcohol base: {total.toLocaleString('es-AR')} personas totales
{items.map((item) => (
{item.v.toLocaleString('es-AR')}
{item.label}
{pct(item.v, total)}
))}
{realizadas > 0 && (
Tasa de positividad sobre pruebas realizadas ( {realizadas.toLocaleString('es-AR')} ): {' '} 0 ? ROJO : VERDE }} > {((pos / realizadas) * 100).toFixed(1)}%
)}
) } export default function ProteccionPersonas({ personas, involucrados }) { if (!personas?.length) return null const tipoMap = new Map() if (involucrados?.length) { involucrados.forEach((i) => { if (i.id_involucrado != null && i.tipo_involucrado) { tipoMap.set(String(i.id_involucrado), i.tipo_involucrado) } }) } const personasConTipo = personas.map((p) => ({ ...p, _tipo: tipoMap.get(String(p.id_involucrado)) || null, })) const motociclistas = personasConTipo.filter( (p) => p._tipo === 'Motocicleta' ) const cascaSi = motociclistas.filter((p) => p.casco === 'Si').length const cascaNo = motociclistas.filter((p) => p.casco === 'No').length const cascaSD = motociclistas.length - cascaSi - cascaNo const enHabitaculo = personasConTipo.filter((p) => { if (!p._tipo) return false if (SIN_HABITACULO.has(p._tipo)) return false if (CON_HABITACULO.has(p._tipo)) return true return true }) const cinSi = enHabitaculo.filter( (p) => p.cinturon_seguridad === 'Si' ).length const cinNo = enHabitaculo.filter( (p) => p.cinturon_seguridad === 'No' ).length const cinSD = enHabitaculo.length - cinSi - cinNo const airSi = enHabitaculo.filter((p) => p.airbag === 'Si').length const airNo = enHabitaculo.filter((p) => p.airbag === 'No').length const airSD = enHabitaculo.length - airSi - airNo const sinMapa = tipoMap.size === 0 return (
{sinMapa && (
⚠ No se recibieron datos de Involucrados — los cálculos de casco/cinturón/airbag no pueden segmentarse por tipo de vehículo. Verificá que el componente reciba la prop involucrados.
)}
) }