Etapa completada (local) — widgets de Mirada Empresa
This commit is contained in:
126
components/dashboard/senales-impacto-bar.tsx
Normal file
126
components/dashboard/senales-impacto-bar.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import {
|
||||
isSuppressed,
|
||||
type RiesgoRotacionData,
|
||||
type AusentismoData,
|
||||
type PresentismoData,
|
||||
type CarreraCongeladaData,
|
||||
type SuppressedPayload,
|
||||
type DashboardFilters,
|
||||
} from '@/lib/dashboard-types'
|
||||
import { queries } from '@/lib/dashboard-queries'
|
||||
import { WidgetShell } from './widget-shell'
|
||||
|
||||
type Riesgo = RiesgoRotacionData | SuppressedPayload
|
||||
type Ausentismo = AusentismoData | SuppressedPayload
|
||||
type Presentismo = PresentismoData | SuppressedPayload
|
||||
type Carrera = CarreraCongeladaData | SuppressedPayload
|
||||
|
||||
interface Senal {
|
||||
label: string
|
||||
pct: number | null
|
||||
color: 'rose' | 'amber'
|
||||
}
|
||||
|
||||
// Combina E2 (riesgo) + E3 (ausentismo) + E4 (presentismo) — núcleo — y
|
||||
// E5 (carrera congelada) — [SEC] — en una sola card de 4 barras, igual al
|
||||
// mockup ("Señales de impacto laboral"). Cada señal se suprime POR FILA
|
||||
// (n<5 individual) sin tumbar las demás; la card entera solo se suprime
|
||||
// si las 4 vienen suprimidas a la vez.
|
||||
export function SenalesImpactoBar({ surveyId, filters }: { surveyId: string; filters: DashboardFilters }) {
|
||||
const [riesgo, setRiesgo] = useState<Riesgo | null>(null)
|
||||
const [ausentismo, setAusentismo] = useState<Ausentismo | null>(null)
|
||||
const [presentismo, setPresentismo] = useState<Presentismo | null>(null)
|
||||
const [carrera, setCarrera] = useState<Carrera | null>(null)
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setRiesgo(null); setAusentismo(null); setPresentismo(null); setCarrera(null); setError(false)
|
||||
Promise.all([
|
||||
queries.meRiesgoRotacion(surveyId, filters),
|
||||
queries.meAusentismo(surveyId, filters),
|
||||
queries.mePresentismo(surveyId, filters),
|
||||
queries.meCarreraCongelada(surveyId, filters),
|
||||
])
|
||||
.then(([r, a, p, c]) => {
|
||||
setRiesgo(r as Riesgo)
|
||||
setAusentismo(a as Ausentismo)
|
||||
setPresentismo(p as Presentismo)
|
||||
setCarrera(c as Carrera)
|
||||
})
|
||||
.catch(() => setError(true))
|
||||
}, [surveyId, JSON.stringify(filters)])
|
||||
|
||||
const loading = riesgo === null || ausentismo === null || presentismo === null || carrera === null
|
||||
const allSuppressed =
|
||||
!loading &&
|
||||
isSuppressed(riesgo) && isSuppressed(ausentismo) && isSuppressed(presentismo) && isSuppressed(carrera)
|
||||
|
||||
const status = error ? 'error' : loading ? 'loading' : allSuppressed ? 'suppressed' : 'ok'
|
||||
|
||||
const rows: Senal[] = !loading
|
||||
? [
|
||||
{
|
||||
label: 'Presentismo',
|
||||
pct: !isSuppressed(presentismo) ? presentismo.pct_presentismo : null,
|
||||
color: 'amber',
|
||||
},
|
||||
{
|
||||
label: 'Ausentismo frecuente',
|
||||
pct: !isSuppressed(ausentismo) ? ausentismo.pct_ausentismo_recurrente : null,
|
||||
color: 'amber',
|
||||
},
|
||||
{
|
||||
label: 'Riesgo de salida',
|
||||
pct: !isSuppressed(riesgo) ? riesgo.pct_riesgo : null,
|
||||
color: 'rose',
|
||||
},
|
||||
{
|
||||
label: 'Carrera congelada',
|
||||
pct: !isSuppressed(carrera) ? carrera.pct_carrera_congelada : null,
|
||||
color: 'rose',
|
||||
},
|
||||
]
|
||||
: []
|
||||
|
||||
const repN = !loading && !isSuppressed(riesgo) ? riesgo.n_cuidadores : undefined
|
||||
|
||||
return (
|
||||
<WidgetShell
|
||||
title="Señales de impacto laboral"
|
||||
subtitle="7.2 · 7.3 · 7.6 · 7.5 — sobre la población cuidadora"
|
||||
status={status}
|
||||
n={repN}
|
||||
colSpan={6}
|
||||
>
|
||||
{!loading && (
|
||||
<ul className="db-bar-list" aria-label="Señales de impacto laboral">
|
||||
{rows.map((row) => (
|
||||
<li key={row.label} className="db-bar-item">
|
||||
<span className="db-bar-label">{row.label}</span>
|
||||
{row.pct !== null ? (
|
||||
<>
|
||||
<div className="db-bar-track">
|
||||
<div
|
||||
className="db-bar-fill"
|
||||
style={{ width: `${row.pct}%`, background: row.color === 'rose' ? '#f06b6b' : '#f0b429' }}
|
||||
/>
|
||||
</div>
|
||||
<span className="db-bar-pct">{row.pct}%</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="db-bar-track" />
|
||||
<span className="db-bar-pct" style={{ color: '#6b7280' }}>
|
||||
n<5
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</WidgetShell>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user