Files
dashboard-cpsp/components/iotPanel/IotPanelChillFactorCard.tsx
T
2026-08-26 13:46:05 +07:00

132 lines
4.5 KiB
TypeScript

import React from 'react';
import {
CartesianGrid,
Line,
LineChart,
ReferenceDot,
ReferenceLine,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import {
CHICKEN_CHILL_FACTOR_POINTS,
chickenChillFactorFromAge,
formatChillFactor,
} from './chillFactor.ts';
const CHILL_FACTOR_BASE_MAX_DAY =
CHICKEN_CHILL_FACTOR_POINTS[CHICKEN_CHILL_FACTOR_POINTS.length - 1]!.age;
export interface IotPanelChillFactorCardProps {
dayAge: number | null | undefined;
chillFactor: number | null | undefined;
}
const IotPanelChillFactorCard: React.FC<IotPanelChillFactorCardProps> = ({
dayAge,
chillFactor,
}) => {
const dayAgeValue =
dayAge != null && Number.isFinite(Number(dayAge)) ? Math.max(0, Number(dayAge)) : null;
const chartMinDay = CHICKEN_CHILL_FACTOR_POINTS[0]!.age;
const chartMaxDay = Math.max(CHILL_FACTOR_BASE_MAX_DAY, Math.ceil(dayAgeValue ?? 0));
const chillFactorChartData = Array.from({ length: chartMaxDay - chartMinDay + 1 }, (_, idx) => {
const day = chartMinDay + idx;
return { day, factor: chickenChillFactorFromAge(day) ?? 0 };
});
const chillFactorXAxisTicks = Array.from(new Set([0, 7, 14, 21, 28, 35, 42, chartMaxDay])).filter(
(day) => day <= chartMaxDay
);
const currentChillPoint =
dayAgeValue !== null && chillFactor != null && Number.isFinite(Number(chillFactor))
? { day: dayAgeValue, factor: Number(chillFactor) }
: null;
const chillFactorChartLabel = currentChillPoint
? `Grafik chill factor. Umur ayam hari ${currentChillPoint.day}, chill factor ${formatChillFactor(currentChillPoint.factor)}.`
: 'Grafik chill factor belum tersedia karena umur ayam tidak tersedia.';
return (
<article className="rounded-xl border border-slate-200 bg-gradient-to-br from-sky-50 via-cyan-50 to-blue-50 p-4 shadow-sm">
<div className="mb-3 flex items-start justify-between gap-3">
<div>
<h2 className="font-semibold text-slate-800">Grafik Chill Factor</h2>
<p className="mt-1 text-xs text-slate-500">
Grafik berdasarkan umur ayam untuk estimasi efek angin pada suhu terasa.
</p>
</div>
<span className="rounded-full bg-white px-3 py-1 text-lg font-bold text-sky-700 shadow-sm">
{formatChillFactor(chillFactor)}
</span>
</div>
<div
className="h-56 rounded-lg border border-sky-100 bg-white/75 p-2"
role="img"
aria-label={chillFactorChartLabel}
>
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={chillFactorChartData}
margin={{ top: 12, right: 16, left: -18, bottom: 4 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis
dataKey="day"
type="number"
domain={[chartMinDay, chartMaxDay]}
ticks={chillFactorXAxisTicks}
tickFormatter={(value) => `${Number(value)}`}
stroke="#64748b"
fontSize={11}
label={{
value: 'Umur (hari)',
position: 'insideBottom',
offset: -2,
fill: '#64748b',
fontSize: 11,
}}
/>
<YAxis
domain={[3, 8]}
ticks={[3, 3.5, 4.5, 6, 7, 8]}
tickFormatter={(value) => `${Number(value).toString().replace('.', ',')}`}
stroke="#64748b"
fontSize={11}
width={44}
/>
<Tooltip
labelFormatter={(day) => `Hari ${Number(day).toFixed(0)}`}
formatter={(value) => [formatChillFactor(Number(value)), 'Chill factor']}
/>
<Line
type="linear"
dataKey="factor"
name="Chill factor"
stroke="#0284c7"
strokeWidth={3}
dot={{ r: 2 }}
activeDot={{ r: 5 }}
/>
{currentChillPoint && (
<>
<ReferenceLine x={currentChillPoint.day} stroke="#0f766e" strokeDasharray="4 4" />
<ReferenceDot
x={currentChillPoint.day}
y={currentChillPoint.factor}
r={6}
fill="#0f766e"
stroke="#ffffff"
strokeWidth={2}
/>
</>
)}
</LineChart>
</ResponsiveContainer>
</div>
</article>
);
};
export default IotPanelChillFactorCard;