132 lines
4.5 KiB
TypeScript
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;
|