mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
feat(FE-208,212): implement bulk delete functionality for purchase order items with confirmation modal
This commit is contained in:
@@ -175,6 +175,67 @@ const dummyPurchaseData: Purchase = {
|
|||||||
vehicle_number: null,
|
vehicle_number: null,
|
||||||
warehouse: dummyWarehouses[0],
|
warehouse: dummyWarehouses[0],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
purchase_id: 2,
|
||||||
|
product: {
|
||||||
|
id: 1,
|
||||||
|
name: 'CP Vaksin',
|
||||||
|
brand: '',
|
||||||
|
sku: '',
|
||||||
|
product_price: 0,
|
||||||
|
selling_price: 0,
|
||||||
|
tax: 0,
|
||||||
|
expiry_period: 0,
|
||||||
|
uom: {
|
||||||
|
id: 1,
|
||||||
|
name: 'Ekor',
|
||||||
|
created_user: {
|
||||||
|
id: 1,
|
||||||
|
id_user: 1,
|
||||||
|
email: 'hello@gmail.com',
|
||||||
|
name: 'Admin',
|
||||||
|
},
|
||||||
|
created_at: '2025-01-01T00:00:00Z',
|
||||||
|
updated_at: '2025-01-01T00:00:00Z',
|
||||||
|
},
|
||||||
|
product_category: {
|
||||||
|
id: 1,
|
||||||
|
code: 'DOC',
|
||||||
|
name: 'DOC',
|
||||||
|
created_user: {
|
||||||
|
id: 1,
|
||||||
|
id_user: 1,
|
||||||
|
email: 'hello@gmail.com',
|
||||||
|
name: 'Admin',
|
||||||
|
},
|
||||||
|
created_at: '2025-01-01T00:00:00Z',
|
||||||
|
updated_at: '2025-01-01T00:00:00Z',
|
||||||
|
},
|
||||||
|
suppliers: [],
|
||||||
|
flags: [],
|
||||||
|
created_user: {
|
||||||
|
id: 1,
|
||||||
|
id_user: 1,
|
||||||
|
email: 'hello@gmail.com',
|
||||||
|
name: 'Admin',
|
||||||
|
},
|
||||||
|
created_at: '2025-01-01T00:00:00Z',
|
||||||
|
updated_at: '2025-01-01T00:00:00Z',
|
||||||
|
},
|
||||||
|
product_warehouse: dummyProductWarehouses[0],
|
||||||
|
quantity: 10000,
|
||||||
|
sub_qty: 10000,
|
||||||
|
total_qty: 10000,
|
||||||
|
total_used: 0,
|
||||||
|
price: 6500,
|
||||||
|
total_price: 65000000,
|
||||||
|
received_date: null,
|
||||||
|
travel_number: null,
|
||||||
|
travel_number_docs: null,
|
||||||
|
vehicle_number: null,
|
||||||
|
warehouse: dummyWarehouses[0],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
created_at: '2025-01-10T00:00:00Z',
|
created_at: '2025-01-10T00:00:00Z',
|
||||||
updated_at: '2025-01-10T00:00:00Z',
|
updated_at: '2025-01-10T00:00:00Z',
|
||||||
@@ -184,7 +245,6 @@ const dummyPurchaseData: Purchase = {
|
|||||||
warehouse: dummyWarehouses[0],
|
warehouse: dummyWarehouses[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Goods Receipt data - using items from PurchaseItem with received data
|
|
||||||
const dummyGoodsReceiptItems: PurchaseItem[] = [
|
const dummyGoodsReceiptItems: PurchaseItem[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -356,6 +416,10 @@ const PurchaseOrderDetail = ({
|
|||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
const [selectedItem, setSelectedItem] = useState<PurchaseItem | null>(null);
|
const [selectedItem, setSelectedItem] = useState<PurchaseItem | null>(null);
|
||||||
|
|
||||||
|
const selectedRowIds = Object.keys(rowSelection).map((item) =>
|
||||||
|
parseInt(item)
|
||||||
|
);
|
||||||
|
|
||||||
// ===== STATIC DATA =====
|
// ===== STATIC DATA =====
|
||||||
const purchaseData = data || dummyPurchaseData;
|
const purchaseData = data || dummyPurchaseData;
|
||||||
const purchaseOrderItems = useMemo(
|
const purchaseOrderItems = useMemo(
|
||||||
@@ -394,7 +458,7 @@ const PurchaseOrderDetail = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ===== DELETE HANDLER =====
|
// ===== DELETE HANDLER =====
|
||||||
const deleteItemHandler = useCallback(async () => {
|
const deleteItemsHandler = useCallback(async () => {
|
||||||
const purchaseRequestId = searchParams.get('purchaseId')
|
const purchaseRequestId = searchParams.get('purchaseId')
|
||||||
? parseInt(searchParams.get('purchaseId')!)
|
? parseInt(searchParams.get('purchaseId')!)
|
||||||
: purchaseData?.id || 1;
|
: purchaseData?.id || 1;
|
||||||
@@ -404,8 +468,10 @@ const PurchaseOrderDetail = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedItem) {
|
const itemIdsToDelete = selectedItem ? [selectedItem.id] : selectedRowIds;
|
||||||
toast.error('Item tidak valid');
|
|
||||||
|
if (itemIdsToDelete.length === 0) {
|
||||||
|
toast.error('Pilih minimal 1 item untuk dihapus');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +479,7 @@ const PurchaseOrderDetail = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await PurchaseDeleteItemsApi.deleteItems(purchaseRequestId, {
|
const res = await PurchaseDeleteItemsApi.deleteItems(purchaseRequestId, {
|
||||||
item_ids: [selectedItem.id],
|
item_ids: itemIdsToDelete,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isResponseError(res)) {
|
if (isResponseError(res)) {
|
||||||
@@ -421,16 +487,21 @@ const PurchaseOrderDetail = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success('Berhasil menghapus item pembelian');
|
const successMessage = selectedItem
|
||||||
|
? 'Berhasil menghapus item pembelian'
|
||||||
|
: `Berhasil menghapus ${itemIdsToDelete.length} item pembelian`;
|
||||||
|
|
||||||
|
toast.success(successMessage);
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
setSelectedItem(null);
|
setSelectedItem(null);
|
||||||
|
setRowSelection({});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Terjadi kesalahan saat menghapus item pembelian');
|
toast.error('Terjadi kesalahan saat menghapus item pembelian');
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
}
|
}
|
||||||
}, [purchaseData?.id, searchParams, selectedItem]);
|
}, [purchaseData?.id, searchParams, selectedItem, selectedRowIds]);
|
||||||
|
|
||||||
// ===== COMPUTED VALUES =====
|
// ===== COMPUTED VALUES =====
|
||||||
const approvalSteps = useMemo(() => {
|
const approvalSteps = useMemo(() => {
|
||||||
@@ -522,6 +593,7 @@ const PurchaseOrderDetail = ({
|
|||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const deleteClickHandler = () => {
|
const deleteClickHandler = () => {
|
||||||
setSelectedItem(props.row.original);
|
setSelectedItem(props.row.original);
|
||||||
|
setRowSelection({}); // Clear row selection when doing single delete
|
||||||
deleteModal.openModal();
|
deleteModal.openModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -857,6 +929,25 @@ const PurchaseOrderDetail = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bulk Action Buttons */}
|
||||||
|
{selectedRowIds.length > 0 && (
|
||||||
|
<div className='flex justify-center items-center mt-4 gap-4 px-6 py-4 bg-gray-50 border-t border-gray-200'>
|
||||||
|
<Button
|
||||||
|
type='button'
|
||||||
|
color='error'
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedItem(null);
|
||||||
|
deleteModal.openModal();
|
||||||
|
}}
|
||||||
|
disabled={selectedRowIds.length === 0}
|
||||||
|
className='w-fit text-sm'
|
||||||
|
>
|
||||||
|
<Icon icon='mdi:trash-can' width={16} height={16} />
|
||||||
|
Hapus Terpilih ({selectedRowIds.length})
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Bottom Section - Catatan dan Total */}
|
{/* Bottom Section - Catatan dan Total */}
|
||||||
<div className='border-t border-gray-200 grid grid-cols-1 lg:grid-cols-5 divide-x divide-gray-200'>
|
<div className='border-t border-gray-200 grid grid-cols-1 lg:grid-cols-5 divide-x divide-gray-200'>
|
||||||
{/* Catatan Section */}
|
{/* Catatan Section */}
|
||||||
@@ -1024,14 +1115,18 @@ const PurchaseOrderDetail = ({
|
|||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
ref={deleteModal.ref}
|
ref={deleteModal.ref}
|
||||||
type='error'
|
type='error'
|
||||||
text='Apakah Anda yakin ingin menghapus item pembelian ini?'
|
text={`Apakah Anda yakin ingin menghapus ${
|
||||||
|
selectedItem
|
||||||
|
? 'item pembelian ini?'
|
||||||
|
: `${selectedRowIds.length} item pembelian yang dipilih?`
|
||||||
|
}`}
|
||||||
closeOnBackdrop
|
closeOnBackdrop
|
||||||
primaryButton={{
|
primaryButton={{
|
||||||
text: 'Ya, Hapus',
|
text: 'Ya, Hapus',
|
||||||
color: 'error',
|
color: 'error',
|
||||||
isLoading: isDeleteLoading,
|
isLoading: isDeleteLoading,
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await deleteItemHandler();
|
await deleteItemsHandler();
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
|
|||||||
Reference in New Issue
Block a user