mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +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,
|
||||
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',
|
||||
updated_at: '2025-01-10T00:00:00Z',
|
||||
@@ -184,7 +245,6 @@ const dummyPurchaseData: Purchase = {
|
||||
warehouse: dummyWarehouses[0],
|
||||
};
|
||||
|
||||
// Goods Receipt data - using items from PurchaseItem with received data
|
||||
const dummyGoodsReceiptItems: PurchaseItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
@@ -356,6 +416,10 @@ const PurchaseOrderDetail = ({
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
const [selectedItem, setSelectedItem] = useState<PurchaseItem | null>(null);
|
||||
|
||||
const selectedRowIds = Object.keys(rowSelection).map((item) =>
|
||||
parseInt(item)
|
||||
);
|
||||
|
||||
// ===== STATIC DATA =====
|
||||
const purchaseData = data || dummyPurchaseData;
|
||||
const purchaseOrderItems = useMemo(
|
||||
@@ -394,7 +458,7 @@ const PurchaseOrderDetail = ({
|
||||
);
|
||||
|
||||
// ===== DELETE HANDLER =====
|
||||
const deleteItemHandler = useCallback(async () => {
|
||||
const deleteItemsHandler = useCallback(async () => {
|
||||
const purchaseRequestId = searchParams.get('purchaseId')
|
||||
? parseInt(searchParams.get('purchaseId')!)
|
||||
: purchaseData?.id || 1;
|
||||
@@ -404,8 +468,10 @@ const PurchaseOrderDetail = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedItem) {
|
||||
toast.error('Item tidak valid');
|
||||
const itemIdsToDelete = selectedItem ? [selectedItem.id] : selectedRowIds;
|
||||
|
||||
if (itemIdsToDelete.length === 0) {
|
||||
toast.error('Pilih minimal 1 item untuk dihapus');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -413,7 +479,7 @@ const PurchaseOrderDetail = ({
|
||||
|
||||
try {
|
||||
const res = await PurchaseDeleteItemsApi.deleteItems(purchaseRequestId, {
|
||||
item_ids: [selectedItem.id],
|
||||
item_ids: itemIdsToDelete,
|
||||
});
|
||||
|
||||
if (isResponseError(res)) {
|
||||
@@ -421,16 +487,21 @@ const PurchaseOrderDetail = ({
|
||||
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();
|
||||
setSelectedItem(null);
|
||||
setRowSelection({});
|
||||
} catch (error) {
|
||||
toast.error('Terjadi kesalahan saat menghapus item pembelian');
|
||||
} finally {
|
||||
setIsDeleteLoading(false);
|
||||
}
|
||||
}, [purchaseData?.id, searchParams, selectedItem]);
|
||||
}, [purchaseData?.id, searchParams, selectedItem, selectedRowIds]);
|
||||
|
||||
// ===== COMPUTED VALUES =====
|
||||
const approvalSteps = useMemo(() => {
|
||||
@@ -522,6 +593,7 @@ const PurchaseOrderDetail = ({
|
||||
cell: (props) => {
|
||||
const deleteClickHandler = () => {
|
||||
setSelectedItem(props.row.original);
|
||||
setRowSelection({}); // Clear row selection when doing single delete
|
||||
deleteModal.openModal();
|
||||
};
|
||||
|
||||
@@ -857,6 +929,25 @@ const PurchaseOrderDetail = ({
|
||||
/>
|
||||
</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 */}
|
||||
<div className='border-t border-gray-200 grid grid-cols-1 lg:grid-cols-5 divide-x divide-gray-200'>
|
||||
{/* Catatan Section */}
|
||||
@@ -1024,14 +1115,18 @@ const PurchaseOrderDetail = ({
|
||||
<ConfirmationModal
|
||||
ref={deleteModal.ref}
|
||||
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
|
||||
primaryButton={{
|
||||
text: 'Ya, Hapus',
|
||||
color: 'error',
|
||||
isLoading: isDeleteLoading,
|
||||
onClick: async () => {
|
||||
await deleteItemHandler();
|
||||
await deleteItemsHandler();
|
||||
},
|
||||
}}
|
||||
secondaryButton={{
|
||||
|
||||
Reference in New Issue
Block a user