62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
'use client'
|
||
import { useEffect, useState } from 'react'
|
||
import { isSuppressed, type ScoreData } from '@/lib/dashboard-types'
|
||
import { queries } from '@/lib/dashboard-queries'
|
||
import type { DashboardFilters } from '@/lib/dashboard-types'
|
||
import { WidgetShell } from './widget-shell'
|
||
|
||
const ZONA_COLOR = { baja: '#ef4444', desarrollo: '#F8AD13', consciente: '#43BBC8' }
|
||
const ZONA_LABEL = { baja: 'Baja madurez', desarrollo: 'En desarrollo', consciente: 'Consciente' }
|
||
|
||
export function GaugeOrganizacional({ surveyId, filters }: { surveyId: string; filters: DashboardFilters }) {
|
||
const [data, setData] = useState<ScoreData | null>(null)
|
||
const [sup, setSup] = useState(false)
|
||
|
||
useEffect(() => {
|
||
setData(null); setSup(false)
|
||
queries.score(surveyId, filters)
|
||
.then(d => { if (isSuppressed(d)) setSup(true); else setData(d as ScoreData) })
|
||
.catch(() => {})
|
||
}, [surveyId, JSON.stringify(filters)])
|
||
|
||
const status = !data && !sup ? 'loading' : sup ? 'suppressed' : 'ok'
|
||
|
||
// SVG semicircle gauge
|
||
const score = data?.score ?? 0
|
||
const angle = (score / 100) * 180 // 0° = left, 180° = right
|
||
const rad = (angle - 90) * Math.PI / 180
|
||
const r = 60; const cx = 75; const cy = 80
|
||
const needleX = cx + r * Math.cos(rad)
|
||
const needleY = cy + r * Math.sin(rad)
|
||
const color = data ? ZONA_COLOR[data.zona] : '#43BBC8'
|
||
|
||
return (
|
||
<WidgetShell title="🎯 Madurez organizacional" status={status} n={data?.n} colSpan={3}>
|
||
{data && (
|
||
<div className="db-gauge-wrapper">
|
||
<svg viewBox="0 0 150 100" className="db-gauge-svg" aria-label={`Score: ${score}/100`}>
|
||
{/* Fondo semicírculo */}
|
||
<path d="M 15 80 A 60 60 0 0 1 135 80" fill="none" stroke="#2a3547" strokeWidth="18" strokeLinecap="round" />
|
||
{/* Arco coloreado proporcional al score */}
|
||
<path d="M 15 80 A 60 60 0 0 1 135 80" fill="none"
|
||
stroke={color} strokeWidth="18" strokeLinecap="round"
|
||
strokeDasharray={`${(score/100)*188.5} 188.5`} />
|
||
{/* Aguja */}
|
||
<line x1={cx} y1={cy} x2={needleX} y2={needleY} stroke="white" strokeWidth="2" strokeLinecap="round" />
|
||
<circle cx={cx} cy={cy} r={4} fill="white" />
|
||
{/* Score */}
|
||
<text x={cx} y={cy-10} textAnchor="middle" fill="white" fontSize="18" fontWeight="bold">{score}</text>
|
||
<text x={cx} y={cy+6} textAnchor="middle" fill="#9ca3af" fontSize="7">/ 100</text>
|
||
</svg>
|
||
<p className="db-gauge-zona" style={{ color }}>
|
||
{ZONA_LABEL[data.zona]}
|
||
</p>
|
||
<p className="db-gauge-hint">
|
||
0–33 baja · 34–66 desarrollo · 67–100 consciente
|
||
</p>
|
||
</div>
|
||
)}
|
||
</WidgetShell>
|
||
)
|
||
}
|