146 lines
4.8 KiB
TypeScript
146 lines
4.8 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import type { Karung, ManualInput } from '../../types/api.ts';
|
|
import { formatNumber } from '../../utils/format.ts';
|
|
|
|
const StatItem: React.FC<{
|
|
icon: React.ReactNode;
|
|
label: string;
|
|
value: number | null;
|
|
colorOverride?: string;
|
|
}> = ({ icon, label, value, colorOverride }) => (
|
|
<div className="border border-gray-200 rounded-lg p-3 hover:border-gray-300 transition-colors">
|
|
<div className="flex items-start gap-2 mb-3 min-w-0">
|
|
<div className="mt-0.5 shrink-0 text-gray-500">{icon}</div>
|
|
<p className="min-w-0 text-[11px] sm:text-xs font-medium leading-tight text-gray-700 break-words">
|
|
{label}
|
|
</p>
|
|
</div>
|
|
<div className="rounded-md bg-gray-50/70 p-3">
|
|
<p
|
|
className="text-xl sm:text-2xl font-bold leading-none"
|
|
style={{ color: colorOverride ?? '#374151' }}
|
|
>
|
|
{value == null ? formatNumber(0) : formatNumber(value)}
|
|
</p>
|
|
<p className="text-xs text-gray-400 mt-0.5">karung</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
interface FeedStatsProps {
|
|
latest: Karung | null;
|
|
latestManual: ManualInput | null;
|
|
cycleStartDate: string;
|
|
displayDate: string;
|
|
}
|
|
|
|
const FeedStats: React.FC<FeedStatsProps> = ({
|
|
latest,
|
|
latestManual,
|
|
cycleStartDate,
|
|
displayDate,
|
|
}) => {
|
|
const formatDisplayDate = (iso: string) =>
|
|
new Intl.DateTimeFormat('id-ID', {
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric',
|
|
}).format(new Date(`${iso}T00:00:00`));
|
|
|
|
const todayLabel = useMemo(() => formatDisplayDate(displayDate), [displayDate]);
|
|
|
|
const totalCycleLabel = useMemo(() => {
|
|
const start = new Intl.DateTimeFormat('id-ID', {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
}).format(new Date(`${cycleStartDate}T00:00:00`));
|
|
const end = new Intl.DateTimeFormat('id-ID', {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
}).format(new Date(`${displayDate}T00:00:00`));
|
|
return `Total (${start} - ${end})`;
|
|
}, [cycleStartDate, displayDate]);
|
|
|
|
const columns = useMemo(
|
|
() => [
|
|
{
|
|
key: 'masuk',
|
|
title: 'Karung Masuk',
|
|
color: '#2563eb',
|
|
icon: <i className="fa-solid fa-arrow-down text-blue-600"></i>,
|
|
iotToday: latest?.in_today ?? 0,
|
|
iotTotal: latest?.in_total ?? 0,
|
|
manualToday: latestManual?.feed_in_manual ?? 0,
|
|
manualTotal: latestManual?.feed_in_manual_total ?? 0,
|
|
},
|
|
{
|
|
key: 'dituang',
|
|
title: 'Karung Dituang',
|
|
color: '#ea580c',
|
|
icon: <i className="fa-solid fa-arrow-up text-orange-600"></i>,
|
|
iotToday: latest?.feed_use_today ?? 0,
|
|
iotTotal: latest?.feed_use_total ?? 0,
|
|
manualToday: latestManual?.feed_use_manual ?? 0,
|
|
manualTotal: latestManual?.feed_use_manual_total ?? 0,
|
|
},
|
|
{
|
|
key: 'keluar',
|
|
title: 'Karung Keluar',
|
|
color: '#dc2626',
|
|
icon: <i className="fa-solid fa-arrow-right text-red-600"></i>,
|
|
iotToday: latest?.out_today ?? 0,
|
|
iotTotal: latest?.out_total ?? 0,
|
|
manualToday: latestManual?.feed_out_manual ?? 0,
|
|
manualTotal: latestManual?.feed_out_manual_total ?? 0,
|
|
},
|
|
],
|
|
[latest, latestManual]
|
|
);
|
|
|
|
return (
|
|
<div className="bg-white p-4 sm:p-6 rounded-xl shadow-sm">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 sm:gap-6">
|
|
{columns.map((col) => (
|
|
<div key={col.key}>
|
|
<h3 className="font-semibold text-sm sm:text-base mb-2" style={{ color: col.color }}>
|
|
{col.title}
|
|
</h3>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div className="space-y-3">
|
|
<StatItem
|
|
icon={<i className="fa-solid fa-microchip text-purple-600"></i>}
|
|
label={`IOT · ${todayLabel}`}
|
|
value={col.iotToday}
|
|
colorOverride={col.color}
|
|
/>
|
|
<StatItem
|
|
icon={<i className="fa-solid fa-microchip text-purple-600"></i>}
|
|
label={`IOT · ${totalCycleLabel}`}
|
|
value={col.iotTotal}
|
|
colorOverride={col.color}
|
|
/>
|
|
</div>
|
|
<div className="space-y-3">
|
|
<StatItem
|
|
icon={<i className="fa-solid fa-user-pen text-gray-500"></i>}
|
|
label={`Manual · ${todayLabel}`}
|
|
value={col.manualToday}
|
|
colorOverride="#6b7280"
|
|
/>
|
|
<StatItem
|
|
icon={<i className="fa-solid fa-user-pen text-gray-500"></i>}
|
|
label={`Manual · ${totalCycleLabel}`}
|
|
value={col.manualTotal}
|
|
colorOverride="#6b7280"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FeedStats;
|