wulf-pulse/components/kiosk/gauge-chart.tsx
lorentz 3c13defacb feat: kiosk UI updates - new cards, gauge chart, performance improvements
- Updated kiosk stats API with expanded metrics
- New components: company-tickets-card, gauge-chart, service-desk-card, ticket-leaders-card
- Updated cycling-display, kpi-card, and ticker components
- Added performance.css for kiosk optimizations
- Added Wulf logo asset
2026-02-19 15:31:23 -05:00

122 lines
3.4 KiB
TypeScript

'use client';
interface GaugeChartProps {
value: number;
max: number;
label: string;
breakpoints: {
green: number;
yellow: number;
red: number;
};
}
export function GaugeChart({ value, max, label, breakpoints }: GaugeChartProps) {
// Calculate percentages for color segments
const greenPercent = (breakpoints.green / max) * 100;
const yellowPercent = ((breakpoints.yellow - breakpoints.green) / max) * 100;
const redPercent = ((max - breakpoints.yellow) / max) * 100;
// Calculate needle angle (gauge goes from -90 to 90 degrees, 180 total)
const percentage = Math.min((value / max) * 100, 100);
const angle = -90 + (percentage / 100) * 180;
// SVG arc path helper
const createArc = (startAngle: number, endAngle: number, radius: number) => {
const start = polarToCartesian(100, 100, radius, endAngle);
const end = polarToCartesian(100, 100, radius, startAngle);
const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
return `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x} ${end.y}`;
};
const polarToCartesian = (centerX: number, centerY: number, radius: number, angleInDegrees: number) => {
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
return {
x: centerX + radius * Math.cos(angleInRadians),
y: centerY + radius * Math.sin(angleInRadians),
};
};
// Calculate segment angles
const greenEndAngle = -90 + (greenPercent / 100) * 180;
const yellowEndAngle = greenEndAngle + (yellowPercent / 100) * 180;
return (
<div className="flex flex-col items-center">
<svg viewBox="0 0 200 140" className="w-full max-w-[300px]">
{/* Background arc */}
<path
d={createArc(-90, 90, 70)}
fill="none"
stroke="rgba(59, 130, 246, 0.2)"
strokeWidth="20"
strokeLinecap="round"
/>
{/* Green segment */}
<path
d={createArc(-90, greenEndAngle, 70)}
fill="none"
stroke="#22c55e"
strokeWidth="20"
strokeLinecap="round"
/>
{/* Yellow segment */}
<path
d={createArc(greenEndAngle, yellowEndAngle, 70)}
fill="none"
stroke="#eab308"
strokeWidth="20"
strokeLinecap="round"
/>
{/* Red segment */}
<path
d={createArc(yellowEndAngle, 90, 70)}
fill="none"
stroke="#ef4444"
strokeWidth="20"
strokeLinecap="round"
/>
{/* Needle */}
<g transform={`rotate(${angle} 100 100)`}>
<line
x1="100"
y1="100"
x2="100"
y2="35"
stroke="#1e293b"
strokeWidth="4"
strokeLinecap="round"
/>
<circle cx="100" cy="100" r="8" fill="#1e293b" />
</g>
{/* Center value */}
<text
x="100"
y="105"
textAnchor="middle"
className="text-4xl font-bold fill-blue-500"
>
{value}
</text>
{/* Min/Max labels */}
<text x="20" y="130" textAnchor="start" className="text-sm fill-gray-400">
0
</text>
<text x="180" y="130" textAnchor="end" className="text-sm fill-gray-400">
{max}
</text>
</svg>
{/* Label */}
<div className="text-xl font-semibold text-gray-300 text-center mt-2">
{label}
</div>
</div>
);
}