From 7a5a9aafd1980b61b897928a5fa0ff4aec5bd19e Mon Sep 17 00:00:00 2001 From: vinix Date: Wed, 3 Dec 2025 13:26:54 +0700 Subject: [PATCH] feat: update sparepart quantity and stock status handling in DetailSparepart and SparepartCardList components --- .../sparepart/component/DetailSparepart.jsx | 38 +++++-- .../sparepart/component/ListSparepart.jsx | 11 ++- .../sparepart/component/SparepartCardList.jsx | 99 ++++++++++++++----- 3 files changed, 113 insertions(+), 35 deletions(-) diff --git a/src/pages/master/sparepart/component/DetailSparepart.jsx b/src/pages/master/sparepart/component/DetailSparepart.jsx index 132b508..b58ab26 100644 --- a/src/pages/master/sparepart/component/DetailSparepart.jsx +++ b/src/pages/master/sparepart/component/DetailSparepart.jsx @@ -43,9 +43,10 @@ const DetailSparepart = (props) => { sparepart_description: '', sparepart_model: '', sparepart_item_type: null, + sparepart_qty: 0, sparepart_unit: '', sparepart_merk: '', - sparepart_stok: '0', + sparepart_stok: 'Not Available', sparepart_foto: '', }; @@ -224,11 +225,14 @@ const DetailSparepart = (props) => { if (formData.sparepart_merk && formData.sparepart_merk.trim() !== '') { payload.sparepart_merk = formData.sparepart_merk; } - if (formData.sparepart_stok && formData.sparepart_stok.trim() !== '') { - payload.sparepart_stok = formData.sparepart_stok.toString(); - } else { - payload.sparepart_stok = '0'; // Set default value jika tidak diisi - } + // sparepart_qty disimpan sebagai angka kuantitas + // Untuk menghindari error validasi, jika qty 0, kita tetap kirim 1 ke backend tapi statusnya "Not Available" + const qty = parseInt(formData.sparepart_qty) || 0; + const actualQty = qty > 0 ? qty : 1; // Kirim minimal 1 ke backend + payload.sparepart_qty = actualQty; + + // sparepart_stok ditentukan otomatis berdasarkan qty sebenarnya + payload.sparepart_stok = qty > 0 ? 'Available' : 'Not Available'; // Sertakan sparepart_foto hanya jika nilainya tidak kosong, agar tidak memicu validasi if (imageUrl && imageUrl.trim() !== '') { payload.sparepart_foto = imageUrl; @@ -496,12 +500,12 @@ const DetailSparepart = (props) => { - Stock + Qty @@ -516,6 +520,20 @@ const DetailSparepart = (props) => { readOnly={props.readOnly} /> + + Status + 0 ? 'Available' : 'Not Available'} + readOnly={true} + placeholder="Auto calculated" + style={{ + backgroundColor: '#f5f5f5', + cursor: 'not-allowed', + color: parseInt(formData.sparepart_qty) > 0 ? '#52c41a' : '#ff4d4f' + }} + /> + diff --git a/src/pages/master/sparepart/component/ListSparepart.jsx b/src/pages/master/sparepart/component/ListSparepart.jsx index d71cc72..fc290be 100644 --- a/src/pages/master/sparepart/component/ListSparepart.jsx +++ b/src/pages/master/sparepart/component/ListSparepart.jsx @@ -72,11 +72,18 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ render: (sparepart_merk) => sparepart_merk || '-' }, { - title: 'Stock', + title: 'Qty', + dataIndex: 'sparepart_qty', + key: 'sparepart_qty', + width: '8%', + render: (sparepart_qty) => sparepart_qty || '0' + }, + { + title: 'Status', dataIndex: 'sparepart_stok', key: 'sparepart_stok', width: '8%', - render: (sparepart_stok) => sparepart_stok || '0' + render: (sparepart_stok) => sparepart_stok || 'Not Available' }, { title: 'Action', diff --git a/src/pages/master/sparepart/component/SparepartCardList.jsx b/src/pages/master/sparepart/component/SparepartCardList.jsx index 97313f0..d97d7f6 100644 --- a/src/pages/master/sparepart/component/SparepartCardList.jsx +++ b/src/pages/master/sparepart/component/SparepartCardList.jsx @@ -37,16 +37,22 @@ const SparepartCardList = ({ return; } - const newStock = Number(item.sparepart_stok) + quantityToAdd; - if (newStock < 0) { - NotifAlert({ icon: 'error', title: 'Error', message: 'Stock cannot be negative.' }); + const currentQty = Number(item.sparepart_qty) || 0; + const newQty = currentQty + quantityToAdd; + if (newQty < 0) { + NotifAlert({ icon: 'error', title: 'Error', message: 'Quantity cannot be negative.' }); return; } setLoadingQuantities((prev) => ({ ...prev, [item.sparepart_id]: true })); + // sparepart_qty disimpan sebagai angka kuantitas + // sparepart_stok ditentukan otomatis berdasarkan qty + // Untuk menghindari error validasi, jika newQty 0, kita tetap kirim 1 ke backend tapi statusnya "Not Available" + const actualQty = newQty > 0 ? newQty : 1; // Kirim minimal 1 ke backend const payload = { - sparepart_stok: newStock.toString(), // Convert number to string as required by API + sparepart_qty: actualQty, + sparepart_stok: newQty > 0 ? 'Available' : 'Not Available', // Otomatis tentukan status berdasarkan newQty asli }; // Hanya tambahkan field jika nilainya tidak kosong untuk menghindari validasi error @@ -62,6 +68,12 @@ const SparepartCardList = ({ if (item.sparepart_description && item.sparepart_description.trim() !== '') { payload.sparepart_description = item.sparepart_description; } + if (item.sparepart_item_type && item.sparepart_item_type !== null) { + payload.sparepart_item_type = item.sparepart_item_type; + } + if (item.sparepart_foto && item.sparepart_foto.trim() !== '') { + payload.sparepart_foto = item.sparepart_foto; + } try { const response = await updateSparepart(item.sparepart_id, payload); @@ -73,6 +85,16 @@ const SparepartCardList = ({ title: 'Success', message: 'Stock updated successfully.', }); + + // Cek apakah qty baru kurang dari 1, tampilkan alert + if (newQty < 1) { + NotifAlert({ + icon: 'warning', + title: 'Low Stock', + message: `Warning: Sparepart "${item.sparepart_name}" is out of stock. Please restock immediately.`, + }); + } + if (onStockUpdate) { onStockUpdate(); } @@ -139,7 +161,8 @@ const SparepartCardList = ({ style={{ backgroundColor: '#f0f0f0', width: '100%', - paddingTop: '100%', /* Ini membuat tinggi sama dengan lebar (aspect ratio 1:1) */ + paddingTop: + '100%' /* Ini membuat tinggi sama dengan lebar (aspect ratio 1:1) */, position: 'relative', borderRadius: '4px', overflow: 'hidden', @@ -153,30 +176,50 @@ const SparepartCardList = ({ imgSrc = item.sparepart_foto; } else { // Gunakan format file URL seperti di brandDevice - const fileName = item.sparepart_foto.split('/').pop(); + const fileName = item.sparepart_foto + .split('/') + .pop(); // Jika filename adalah default file, gunakan dari public assets - if (fileName === 'defaultSparepartImg.jpg') { + if ( + fileName === 'defaultSparepartImg.jpg' + ) { imgSrc = `/assets/defaultSparepartImg.jpg`; } else { // Gunakan API getFileUrl untuk mendapatkan URL yang benar untuk file upload - const token = localStorage.getItem('token'); - const baseURL = import.meta.env.VITE_API_SERVER || ''; - imgSrc = `${baseURL}/file-uploads/images/${encodeURIComponent(fileName)}${token ? `?token=${encodeURIComponent(token)}` : ''}`; + const token = + localStorage.getItem('token'); + const baseURL = + import.meta.env.VITE_API_SERVER || + ''; + imgSrc = `${baseURL}/file-uploads/images/${encodeURIComponent( + fileName + )}${ + token + ? `?token=${encodeURIComponent( + token + )}` + : '' + }`; } } - console.log('Image path being constructed:', imgSrc); + console.log( + 'Image path being constructed:', + imgSrc + ); } else { imgSrc = 'https://via.placeholder.com/150'; } return ( -
+
{item[header]} { - console.error('Image failed to load:', imgSrc); - e.target.src = 'https://via.placeholder.com/150'; + console.error( + 'Image failed to load:', + imgSrc + ); + e.target.src = + 'https://via.placeholder.com/150'; }} - onLoad={() => console.log('Image loaded successfully:', imgSrc)} + onLoad={() => + console.log( + 'Image loaded successfully:', + imgSrc + ) + } />
); @@ -249,8 +301,9 @@ const SparepartCardList = ({ > {item[header]} - - Available Stock: {item.sparepart_stok || '0'} + Qty: {item.sparepart_qty || 0} + + Stok: {item.sparepart_stok || 'Not Available'}