Files
dashboard/components/feedSack/InitialBalanceCard.tsx
T
Alberto-Audrix 32a36cceff
CI / lint-and-test (push) Canceled after 0s
first commit
2026-07-28 08:55:05 +07:00

189 lines
6.2 KiB
TypeScript

import React, { useState } from 'react';
import { CycleFeedInitialBalance } from '../../types/counting.ts';
import { formatNumber } from '../../utils/formatters.ts';
interface InitialBalanceCardProps {
cycleId: string;
initialBalance: CycleFeedInitialBalance | null;
onSave: (saldoAwal: number) => Promise<void>;
}
const InitialBalanceCard: React.FC<InitialBalanceCardProps> = ({
cycleId: _cycleId,
initialBalance,
onSave,
}) => {
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState<number>(initialBalance?.saldoAwal || 0);
const [validationError, setValidationError] = useState<string>('');
const [isSaving, setIsSaving] = useState(false);
const handleEdit = () => {
setEditValue(initialBalance?.saldoAwal || 0);
setValidationError('');
setIsEditing(true);
};
const handleCancel = () => {
setIsEditing(false);
setEditValue(initialBalance?.saldoAwal || 0);
setValidationError('');
};
const handleSave = async () => {
// Validation
if (editValue < 0) {
setValidationError('Saldo awal tidak boleh negatif');
return;
}
if (!Number.isInteger(editValue)) {
setValidationError('Saldo awal harus bilangan bulat');
return;
}
setIsSaving(true);
try {
await onSave(editValue);
setIsEditing(false);
setValidationError('');
} catch (error) {
console.error('Failed to save initial balance:', error);
setValidationError('Gagal menyimpan. Coba lagi.');
} finally {
setIsSaving(false);
}
};
const formatLastUpdate = () => {
if (!initialBalance) return null;
const date = new Date(initialBalance.updatedAt);
// Check if date is valid
if (isNaN(date.getTime())) {
return null; // Return null if date is invalid, won't show the update info
}
const formattedDate = date.toLocaleDateString('id-ID', {
day: 'numeric',
month: 'short',
year: 'numeric',
});
const formattedTime = date.toLocaleTimeString('id-ID', {
hour: '2-digit',
minute: '2-digit',
});
return `${formattedDate} ${formattedTime}${initialBalance.createdBy ? ` oleh ${initialBalance.createdBy}` : ''}`;
};
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-box-archive text-2xl"></i>
</div>
<div>
<h2 className="text-xl font-bold text-gray-800">Saldo Karung Awal</h2>
<p className="text-sm text-gray-600">Stok awal karung pakan untuk siklus ini</p>
</div>
</div>
{!isEditing && (
<button
onClick={handleEdit}
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors flex items-center gap-2"
>
<i className="fa-solid fa-edit"></i>
<span>Edit</span>
</button>
)}
</div>
<div className="bg-white rounded-lg p-6 border border-blue-200">
{isEditing ? (
<div className="space-y-4">
<div>
<div className="flex items-center gap-3">
<input
type="number"
value={editValue}
onChange={(e) => setEditValue(parseInt(e.target.value) || 0)}
className="flex-1 px-4 py-3 border-2 border-blue-300 rounded-lg text-2xl font-bold text-gray-800 focus:outline-none focus:border-blue-500"
placeholder="0"
min="0"
step="1"
/>
<span className="text-gray-600 font-semibold">karung</span>
</div>
{validationError && (
<p className="text-sm text-red-600 mt-2">
<i className="fa-solid fa-exclamation-circle mr-1"></i>
{validationError}
</p>
)}
</div>
<div className="flex gap-2">
<button
onClick={handleSave}
disabled={isSaving}
className="flex-1 px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:opacity-50 transition-colors"
>
{isSaving ? (
<>
<i className="fa-solid fa-spinner fa-spin mr-2"></i>
Menyimpan...
</>
) : (
<>
<i className="fa-solid fa-check mr-2"></i>
Simpan
</>
)}
</button>
<button
onClick={handleCancel}
disabled={isSaving}
className="flex-1 px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 disabled:opacity-50 transition-colors"
>
<i className="fa-solid fa-times mr-2"></i>
Batal
</button>
</div>
</div>
) : (
<div className="text-center">
{initialBalance && initialBalance.saldoAwal > 0 ? (
<>
<p className="text-5xl font-bold text-blue-600 mb-2">
{formatNumber(initialBalance.saldoAwal)}
</p>
<p className="text-lg text-gray-600 mb-3">karung</p>
{formatLastUpdate() && (
<p className="text-xs text-gray-500">
<i className="fa-solid fa-clock mr-1"></i>
Terakhir diperbarui: {formatLastUpdate()}
</p>
)}
</>
) : (
<div className="py-4">
<i className="fa-solid fa-inbox text-5xl text-gray-300 mb-3"></i>
<p className="text-lg font-semibold text-gray-600 mb-1">
Saldo karung awal belum diatur
</p>
<p className="text-sm text-gray-500">Klik Edit untuk menambahkan</p>
</div>
)}
</div>
)}
</div>
</div>
);
};
export default InitialBalanceCard;