Files
motor-de-encuestas/components/dashboard/kpi-cards.tsx
2026-06-05 07:18:10 -03:00

63 lines
2.1 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'
interface KpiCardProps { label: string; value: string; sub?: string }
function KpiCard({ label, value, sub }: KpiCardProps) {
return (
<div className="db-kpi-card">
<span className="db-kpi-value" aria-live="polite">{value}</span>
<span className="db-kpi-label">{label}</span>
{sub && <span className="db-kpi-sub">{sub}</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>
if (!data) {
return (
<div className="db-kpi-row">
{[1,2,3,4].map(i => <div key={i} className="db-kpi-card db-skeleton-block" style={{height:96}} />)}
</div>
)
}
if (isSuppressed(data)) {
return (
<div className="db-kpi-row">
{[1,2,3,4].map(i => (
<div key={i} className="db-kpi-card db-suppressed-card">
<span className="db-suppressed-icon">🔒</span>
<span className="db-kpi-sub">n &lt; 5</span>
</div>
))}
</div>
)
}
const d = data as KpiData
return (
<div className="db-kpi-row">
<KpiCard label="Cuidadores activos" value={`${d.pct_cuidadores}%`} sub={`n = ${d.n}`} />
<KpiCard label="Score bienestar (0-100)" value={d.score_bienestar !== null ? `${d.score_bienestar}` : '—'} />
<KpiCard label="Ausencias frecuentes" value={`${d.pct_ausencias}%`} />
<KpiCard label="Total respuestas" value={d.n.toLocaleString('es-PY')} sub="en este corte" />
</div>
)
}