update sync data from site dashboard and testing closing cycles
This commit is contained in:
1 parent
36639d8c38
commit
f49e72592c
42 files changed
+1966
-688
No files matched your search
@@ -1,7 +1,5 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { api, errorMessage } from '../../services/apiClient.ts';
|
||||
import type { Cycle, InitialBalanceResult } from '../../types/api.ts';
|
||||
import { formatNumber } from '../../utils/format.ts';
|
||||
import React from 'react';
|
||||
import type { Cycle } from '../../types/api.ts';
|
||||
import InitialBalanceComparison from './InitialBalanceComparison.tsx';
|
||||
|
||||
interface InitialBalanceCardProps {
|
||||
@@ -9,53 +7,9 @@ interface InitialBalanceCardProps {
|
||||
}
|
||||
|
||||
const InitialBalanceCard: React.FC<InitialBalanceCardProps> = ({ cycle }) => {
|
||||
const savedBalanceDate = cycle.feed_initial_balance_date ?? cycle.start_date;
|
||||
const [balanceDate, setBalanceDate] = useState(savedBalanceDate);
|
||||
const [manualEntry, setManualEntry] = useState<string>(
|
||||
cycle.feed_initial_balance > 0 ? String(cycle.feed_initial_balance) : ''
|
||||
);
|
||||
const [comparing, setComparing] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [comparison, setComparison] = useState<InitialBalanceResult | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setBalanceDate(cycle.feed_initial_balance_date ?? cycle.start_date);
|
||||
setManualEntry(cycle.feed_initial_balance > 0 ? String(cycle.feed_initial_balance) : '');
|
||||
setComparison(null);
|
||||
setError(null);
|
||||
}, [cycle.id, cycle.feed_initial_balance, cycle.feed_initial_balance_date, cycle.start_date]);
|
||||
|
||||
const manualValue = manualEntry === '' ? null : Number(manualEntry);
|
||||
|
||||
const loadComparison = useCallback(async () => {
|
||||
if (balanceDate === '' || manualValue == null || Number.isNaN(manualValue) || manualValue < 0) {
|
||||
setComparison(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setComparing(true);
|
||||
setError(null);
|
||||
try {
|
||||
const compared = await api.cycles.initialBalanceCompare(cycle.id, {
|
||||
date: balanceDate,
|
||||
manual: manualValue,
|
||||
});
|
||||
setComparison(compared);
|
||||
} catch (err) {
|
||||
setComparison(null);
|
||||
setError(errorMessage(err));
|
||||
} finally {
|
||||
setComparing(false);
|
||||
}
|
||||
}, [balanceDate, cycle.id, manualValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (manualValue == null || manualValue < 0) {
|
||||
setComparison(null);
|
||||
return;
|
||||
}
|
||||
void loadComparison();
|
||||
}, [loadComparison, manualValue]);
|
||||
const hasBalance =
|
||||
cycle.feed_initial_balance > 0 || Boolean(cycle.feed_initial_balance_date);
|
||||
const balanceDate = cycle.feed_initial_balance_date ?? cycle.start_date;
|
||||
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 p-6 rounded-xl shadow-sm border-2 border-blue-200">
|
||||
@@ -66,64 +20,29 @@ const InitialBalanceCard: React.FC<InitialBalanceCardProps> = ({ cycle }) => {
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-800">Saldo Awal</h2>
|
||||
<p className="text-sm text-gray-600">
|
||||
Masukkan saldo awal karung pakan pada tanggal tertentu, lalu bandingkan dengan deteksi
|
||||
IoT.
|
||||
Saldo awal karung pakan dari site dashboard (mirror sync).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg p-6 border border-blue-200">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="initial-balance-date"
|
||||
className="text-sm font-semibold text-gray-700 block mb-1"
|
||||
>
|
||||
Tanggal
|
||||
</label>
|
||||
<input
|
||||
id="initial-balance-date"
|
||||
type="date"
|
||||
value={balanceDate}
|
||||
onChange={(event) => setBalanceDate(event.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 text-sm"
|
||||
{!hasBalance ? (
|
||||
<p className="text-sm text-gray-600">Belum ada saldo awal tersimpan untuk siklus ini.</p>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-sm text-gray-600 mb-1">
|
||||
Tanggal:{' '}
|
||||
<strong className="text-gray-800">{balanceDate}</strong>
|
||||
</p>
|
||||
<InitialBalanceComparison
|
||||
date={balanceDate}
|
||||
manual={cycle.feed_initial_balance}
|
||||
iot={cycle.feed_initial_balance_iot ?? null}
|
||||
accuracy={cycle.feed_initial_balance_accuracy ?? null}
|
||||
syncError={cycle.feed_initial_balance_sync_error ?? null}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="initial-balance-manual"
|
||||
className="text-sm font-semibold text-gray-700 block mb-1"
|
||||
>
|
||||
Feed In Manual (karung)
|
||||
</label>
|
||||
<input
|
||||
id="initial-balance-manual"
|
||||
type="number"
|
||||
min={0}
|
||||
value={manualEntry}
|
||||
onChange={(event) => setManualEntry(event.target.value)}
|
||||
placeholder="Contoh: 120"
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="mt-3 text-sm font-semibold text-red-700 bg-red-50 border border-red-200 rounded-lg p-3">
|
||||
{error}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{cycle.feed_initial_balance > 0 && comparison == null && (
|
||||
<p className="mt-4 text-sm text-gray-600">
|
||||
Saldo awal tersimpan:{' '}
|
||||
<strong className="text-blue-700">
|
||||
{formatNumber(cycle.feed_initial_balance)} karung
|
||||
</strong>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{comparison && <InitialBalanceComparison result={comparison} loading={comparing} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user