97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
'use client'
|
||
import { useEffect, useState } from 'react'
|
||
import { isSuppressed, type KpiData, type SuppressedPayload } from '@/lib/dashboard-types'
|
||
import { queries } from '@/lib/dashboard-queries'
|
||
import type { DashboardFilters } from '@/lib/dashboard-types'
|
||
|
||
const KPI_DEFS = [
|
||
{
|
||
title: 'CUIDADORES ACTIVOS',
|
||
subtitle: '1.6 + 1.13 · % de la plantilla con responsabilidades de cuidado de otros',
|
||
getValue: (d: KpiData) => `${d.pct_cuidadores}%`,
|
||
getN: (d: KpiData) => `n = ${d.n}`,
|
||
},
|
||
{
|
||
title: 'FRECUENCIA DE AUSENTISMO',
|
||
subtitle: '5.2 · % con ausencias laborales recurrentes (4 o más veces al año)',
|
||
getValue: (d: KpiData) => `${d.pct_ausencias}%`,
|
||
getN: () => 'ausencias frecuentes',
|
||
},
|
||
{
|
||
title: 'BIENESTAR',
|
||
subtitle: '4.3 · autorreporte de bienestar general · escala 1–5',
|
||
getValue: (d: KpiData) => d.score_bienestar !== null ? d.score_bienestar.toFixed(1) : '—',
|
||
getN: () => '/ 5',
|
||
},
|
||
] as const
|
||
|
||
interface KpiCardProps {
|
||
title: string
|
||
subtitle: string
|
||
value: string
|
||
valueNote: string
|
||
suppressed: boolean
|
||
loading: boolean
|
||
}
|
||
|
||
function KpiCard({ title, subtitle, value, valueNote, suppressed, loading }: KpiCardProps) {
|
||
return (
|
||
<div className="db-kpi-card">
|
||
<span className="db-kpi-title">{title}</span>
|
||
<span className="db-kpi-subtitle">{subtitle}</span>
|
||
|
||
{loading && (
|
||
<div className="db-skeleton-block" style={{ height: 36, margin: '10px 0 4px', borderRadius: 6 }} />
|
||
)}
|
||
{!loading && suppressed && (
|
||
<div className="db-kpi-suppressed-block" role="status">
|
||
<span className="db-suppressed-icon" aria-hidden="true">🔒</span>
|
||
<span className="db-kpi-value db-kpi-value-suppressed">n < 5</span>
|
||
</div>
|
||
)}
|
||
{!loading && !suppressed && (
|
||
<>
|
||
<span className="db-kpi-value" aria-live="polite">{value}</span>
|
||
<span className="db-kpi-note">{valueNote}</span>
|
||
</>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
interface Props { surveyId: string; filters: DashboardFilters }
|
||
|
||
export function KpiCards({ surveyId, filters }: Props) {
|
||
const [data, setData] = useState<KpiData | SuppressedPayload | null>(null)
|
||
const [error, setError] = useState(false)
|
||
|
||
useEffect(() => {
|
||
setData(null); setError(false)
|
||
queries.kpis(surveyId, filters)
|
||
.then(d => setData(d as KpiData | SuppressedPayload))
|
||
.catch(() => setError(true))
|
||
}, [surveyId, JSON.stringify(filters)])
|
||
|
||
if (error) return <p className="db-error">Error al cargar KPIs</p>
|
||
|
||
const loading = data === null
|
||
const suppressed = !loading && isSuppressed(data)
|
||
const ok = !loading && !suppressed
|
||
|
||
return (
|
||
<div className="db-kpi-row">
|
||
{KPI_DEFS.map((def) => (
|
||
<KpiCard
|
||
key={def.title}
|
||
title={def.title}
|
||
subtitle={def.subtitle}
|
||
value={ok ? def.getValue(data as KpiData) : '—'}
|
||
valueNote={ok ? def.getN(data as KpiData) : ''}
|
||
suppressed={suppressed}
|
||
loading={loading}
|
||
/>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|