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

98 lines
3.3 KiB
TypeScript

import React from 'react';
import { formatNumber } from '../../utils/format.ts';
import { formatChillFactor, type ExperienceCalc } from './chillFactor.ts';
const DEGREE_C = '\u00B0C';
const oneDecimal = (value: number | null): string =>
value === null ? '--' : formatNumber(value, 1);
const formatSigned = (value: number | null): string => {
if (value === null) return '--';
const sign = value > 0 ? '+' : '';
return `${sign}${oneDecimal(value)} ${DEGREE_C}`;
};
export interface IotPanelExperienceTempCardProps {
calc: ExperienceCalc;
apiExperienceTemp: number | null | undefined;
}
const IotPanelExperienceTempCard: React.FC<IotPanelExperienceTempCardProps> = ({
calc,
apiExperienceTemp,
}) => {
const calculationRows = [
{
label: 'Kecepatan angin',
value: calc.windSpeed === null ? '--' : `${oneDecimal(calc.windSpeed)} m/s`,
note: 'Dari sensor angin kandang',
},
{
label: 'Chill factor',
value: formatChillFactor(calc.chillFactor),
note: 'Berdasarkan grafik chill factor',
},
{
label: 'Koreksi RH',
value: formatSigned(calc.humidityAdjustment),
note: '(RH - 70) / 5',
},
{
label: 'Suhu rata-rata',
value: calc.averageTemp === null ? '--' : `${oneDecimal(calc.averageTemp)} ${DEGREE_C}`,
note: 'Suhu rata-rata dari Panel',
},
{
label: 'Chill effect',
value: formatSigned(calc.chillEffect),
note: '(Angin x chill factor) - koreksi RH',
},
];
const apiDiffers =
apiExperienceTemp != null &&
calc.experienceTemp != null &&
Math.abs(apiExperienceTemp - calc.experienceTemp) > 0.05;
return (
<article className="rounded-xl border border-sky-200 bg-gradient-to-br from-sky-50 to-blue-50 p-4 shadow-sm">
<div className="flex items-start justify-between gap-4">
<div>
<h2 className="text-base font-semibold text-slate-800">
<i className="fa-solid fa-temperature-half mr-2 text-sky-600" />
Experience Suhu
</h2>
<p className="mt-1 text-xs text-slate-500">Suhu yang dirasakan oleh ayam.</p>
</div>
<p className="shrink-0 text-3xl font-bold text-sky-600">
{calc.experienceTemp === null ? 'N/A' : `${oneDecimal(calc.experienceTemp)} ${DEGREE_C}`}
</p>
</div>
<div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2">
{calculationRows.map((item) => (
<div key={item.label} className="rounded-lg border border-sky-100 bg-white/70 p-2">
<div className="flex items-center justify-between gap-2">
<span className="text-xs font-medium text-slate-500">{item.label}</span>
<strong className="text-sm text-slate-800">{item.value}</strong>
</div>
<p className="mt-1 text-[11px] text-slate-400">{item.note}</p>
</div>
))}
</div>
<div className="mt-3 rounded-lg bg-white/60 px-3 py-2 text-xs text-slate-500">
Experience suhu = Suhu rata-rata - chill effect.
{apiDiffers && apiExperienceTemp != null && (
<span className="mt-1 block">
Nilai tercatat Panel: {oneDecimal(apiExperienceTemp)} {DEGREE_C}.
</span>
)}
</div>
</article>
);
};
export default IotPanelExperienceTempCard;