121 lines
4.8 KiB
TypeScript
121 lines
4.8 KiB
TypeScript
import React, { useMemo } from 'react';
|
||
import type { Karung } from '../../types/api.ts';
|
||
import { formatNumber } from '../../utils/format.ts';
|
||
|
||
interface FeedSaldoCardProps {
|
||
latest: Karung | null;
|
||
feedInitialBalance: number;
|
||
cycleStartDate: string;
|
||
displayDate: string;
|
||
}
|
||
|
||
const FeedSaldoCard: React.FC<FeedSaldoCardProps> = ({
|
||
latest,
|
||
feedInitialBalance,
|
||
cycleStartDate,
|
||
displayDate,
|
||
}) => {
|
||
const saldoData = useMemo(() => {
|
||
const inbound = latest?.in_total ?? feedInitialBalance;
|
||
const feedUse = latest?.feed_use_total ?? 0;
|
||
const keluar = latest?.out_total ?? 0;
|
||
const saldo = inbound - feedUse - keluar;
|
||
return { inbound, feedUse, keluar, saldo };
|
||
}, [latest, feedInitialBalance]);
|
||
|
||
const formatDateRange = useMemo(() => {
|
||
const formatDate = (iso: string) =>
|
||
new Intl.DateTimeFormat('id-ID', {
|
||
day: 'numeric',
|
||
month: 'long',
|
||
year: 'numeric',
|
||
}).format(new Date(`${iso}T00:00:00`));
|
||
return `${formatDate(cycleStartDate)} - ${formatDate(displayDate)}`;
|
||
}, [cycleStartDate, displayDate]);
|
||
|
||
const getSaldoStatusColor = (saldo: number) => {
|
||
if (saldo > 0) return 'text-green-600';
|
||
if (saldo < 0) return 'text-red-600';
|
||
return 'text-gray-600';
|
||
};
|
||
|
||
return (
|
||
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 p-6 rounded-xl shadow-sm border-2 border-blue-200">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="bg-blue-500 text-white p-3 rounded-lg">
|
||
<i className="fa-solid fa-scale-balanced text-2xl"></i>
|
||
</div>
|
||
<div>
|
||
<h2 className="text-xl font-bold text-gray-800">Saldo Karung IOT</h2>
|
||
<p className="text-sm text-gray-600">{formatDateRange}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Saldo Display */}
|
||
<div className="bg-white rounded-lg p-6 border border-blue-200">
|
||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||
{/* Karung Masuk */}
|
||
<div className="text-center">
|
||
<div className="flex items-center justify-center gap-2 mb-2">
|
||
<i className="fa-solid fa-arrow-down text-green-600"></i>
|
||
<p className="text-sm font-semibold text-gray-700">Karung Masuk</p>
|
||
</div>
|
||
<p className="text-3xl font-bold text-green-600">{formatNumber(saldoData.inbound)}</p>
|
||
<p className="text-xs text-gray-400 mt-1">karung</p>
|
||
</div>
|
||
|
||
{/* Karung Dituang (feed use) */}
|
||
<div className="text-center">
|
||
<div className="flex items-center justify-center gap-2 mb-2">
|
||
<i className="fa-solid fa-arrow-up text-orange-600"></i>
|
||
<p className="text-sm font-semibold text-gray-700">Karung Dituang</p>
|
||
</div>
|
||
<p className="text-3xl font-bold text-orange-600">{formatNumber(saldoData.feedUse)}</p>
|
||
<p className="text-xs text-gray-400 mt-1">karung</p>
|
||
</div>
|
||
|
||
{/* Karung Keluar */}
|
||
<div className="text-center">
|
||
<div className="flex items-center justify-center gap-2 mb-2">
|
||
<i className="fa-solid fa-arrow-right text-red-600"></i>
|
||
<p className="text-sm font-semibold text-gray-700">Karung Keluar</p>
|
||
</div>
|
||
<p className="text-3xl font-bold text-red-600">{formatNumber(saldoData.keluar)}</p>
|
||
<p className="text-xs text-gray-400 mt-1">karung</p>
|
||
</div>
|
||
|
||
{/* Saldo */}
|
||
<div className="text-center lg:border-l-2 lg:border-blue-200 lg:pl-6">
|
||
<div className="flex items-center justify-center gap-2 mb-2">
|
||
<i className="fa-solid fa-scale-balanced text-blue-600"></i>
|
||
<p className="text-sm font-semibold text-gray-700">Saldo</p>
|
||
</div>
|
||
<p className={`text-4xl font-bold ${getSaldoStatusColor(saldoData.saldo)}`}>
|
||
{formatNumber(saldoData.saldo)}
|
||
</p>
|
||
<p className="text-xs text-gray-400 mt-1">karung</p>
|
||
</div>
|
||
</div>
|
||
<p className="text-xs text-gray-400 mt-4 text-center">
|
||
Saldo = karung masuk − karung dituang − karung keluar.
|
||
</p>
|
||
</div>
|
||
|
||
{/* Warning for negative saldo */}
|
||
{saldoData.saldo < 0 && (
|
||
<div className="mt-4 bg-yellow-50 border border-yellow-200 rounded-lg p-3 flex items-start gap-2">
|
||
<i className="fa-solid fa-triangle-exclamation text-yellow-600 mt-0.5"></i>
|
||
<p className="text-xs text-yellow-800">
|
||
<strong>Peringatan:</strong> Saldo negatif menunjukkan karung dituang lebih banyak dari
|
||
karung masuk. Periksa kembali data sinkronisasi karung-web-admin.
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default FeedSaldoCard;
|