189 lines
6.2 KiB
TypeScript
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;
|