Adding Delete and Restore functionality in Purchase Invoice

This commit is contained in:
Kartik Sharma 2025-12-01 15:03:13 +05:30
parent 35b3384dac
commit 311c74587a
4 changed files with 216 additions and 119 deletions

View File

@ -1,21 +1,28 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { usePurchasesList } from "../../hooks/usePurchase"; import { useDeletePurchaseInvoice, usePurchasesList } from "../../hooks/usePurchase";
import { ITEMS_PER_PAGE } from "../../utils/constants"; import { ITEMS_PER_PAGE } from "../../utils/constants";
import Pagination from "../common/Pagination"; import Pagination from "../common/Pagination";
import { PurchaseColumn } from "./Purchasetable"; import { PurchaseColumn } from "./Purchasetable";
import { SpinnerLoader } from "../common/Loader"; import { SpinnerLoader } from "../common/Loader";
import { useDebounce } from "../../utils/appUtils"; import { useDebounce } from "../../utils/appUtils";
import { usePurchaseContext } from "../../pages/purchase/PurchasePage"; import { usePurchaseContext } from "../../pages/purchase/PurchasePage";
import ConfirmModal from "../common/ConfirmModal";
const PurchaseList = ({ searchString }) => { const PurchaseList = ({ searchString, isActive }) => {
const { setViewPurchase, setManagePurchase, setChallan } = const { setViewPurchase, setManagePurchase, setChallan } =
usePurchaseContext(); usePurchaseContext();
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const { mutate: DeletePurchaseInvoice, isPending } = useDeletePurchaseInvoice();
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deletingId, setDeletingId] = useState(null);
const debounceSearch = useDebounce(searchString, 300); const debounceSearch = useDebounce(searchString, 300);
const { data, isLoading } = usePurchasesList( const { data, isLoading } = usePurchasesList(
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
currentPage, currentPage,
true, // true,
!isActive,
{}, {},
debounceSearch debounceSearch
); );
@ -28,129 +35,172 @@ const PurchaseList = ({ searchString }) => {
const visibleColumns = PurchaseColumn.filter((col) => !col.hidden); const visibleColumns = PurchaseColumn.filter((col) => !col.hidden);
const handleDeleteRestore = (id) => {
DeletePurchaseInvoice(
{ id, isActive: isActive }, // delete if active, restore if deleted
{
onSettled: () => {
setDeletingId(null);
setIsDeleteModalOpen(false);
},
}
);
};
return ( return (
<div className="card mt-2 page-min-h px-sm-4"> <>
<div className="table-responsive px-2"> {IsDeleteModalOpen && (
<table className="datatables-users table border-top text-nowrap"> <ConfirmModal
<thead> isOpen={IsDeleteModalOpen}
<tr> type={!isActive ? "delete" : "undo"}
{visibleColumns.map((col) => ( header={!isActive ? "Delete Invoice" : "Restore Invoice"}
<th key={col.key} colSpan={col.colSpan || 1}> message={!isActive ? "Are you sure you want to delete?" : "Are you sure you want to restore?"}
<div className={col.className || ""}>{col.label}</div> onSubmit={handleDeleteRestore}
</th> onClose={() => setIsDeleteModalOpen(false)}
))} loading={isPending}
<th>Action</th> paramData={deletingId}
</tr> />
</thead> )}
<div className="card mt-2 page-min-h px-sm-4">
<tbody> <div className="table-responsive px-2">
{/* LOADING */} <table className="datatables-users table border-top text-nowrap">
{isLoading && ( <thead>
<tr> <tr>
<td colSpan={visibleColumns.length + 1} className="border-0"> {visibleColumns.map((col) => (
<div className="py-6 py-12"> <th key={col.key} colSpan={col.colSpan || 1}>
<SpinnerLoader /> <div className={col.className || ""}>{col.label}</div>
</div> </th>
</td> ))}
<th>Action</th>
</tr> </tr>
)} </thead>
{!isLoading && data?.data?.length === 0 && (
<tr>
<td
colSpan={visibleColumns.length}
className="text-center py-4 border-0"
>
No Data Found
</td>
</tr>
)}
{!isLoading && <tbody>
data?.data?.map((item, index) => ( {/* LOADING */}
<tr key={item?.id || index}> {isLoading && (
{visibleColumns.map((col) => ( <tr>
<td key={col.key} className={col.className || ""}> <td
{col.render ? col.render(item) : item[col.key] || "NA"} colSpan={visibleColumns.length + 1}
</td> className="border-0"
))} style={{ height: "300px", verticalAlign: "middle" }}
<td > >
<div className="dropdown z-2"> <div className="d-flex justify-content-center align-items-center w-100 h-100">
<button <SpinnerLoader />
type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setViewPurchase({
isOpen: true,
purchaseId: item.id,
})
}
>
<i className="bx bx-show me-2"></i>
<span className="align-left">view</span>
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setManagePurchase({
isOpen: true,
purchaseId: item.id,
})
}
>
<i className="bx bx-edit me-2"></i>
<span className="align-left">Edit</span>
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setChallan({
isOpen: true,
purchaseId: item.id,
})
}
>
<i className="bx bx-file bx-plus me-2"></i>
<span className="align-left">Add Delivery Challan</span>
</a>
</li>
</ul>
</div> </div>
</td> </td>
</tr> </tr>
))} )}
</tbody>
</table>
</div>
{data?.data?.length > 0 && ( {!isLoading && data?.data?.length === 0 && (
<Pagination <tr>
currentPage={currentPage} <td
totalPages={data.totalPages} colSpan={visibleColumns.length + 1}
onPageChange={paginate} className="text-center border-0"
/> style={{ height: "400px", verticalAlign: "middle" }}
)} >
</div> No Data Found
</td>
</tr>
)}
{!isLoading &&
data?.data?.map((item, index) => (
<tr key={item?.id || index}>
{visibleColumns.map((col) => (
<td key={col.key} className={col.className || ""}>
{col.render ? col.render(item) : item[col.key] || "NA"}
</td>
))}
<td >
<div className="dropdown z-2">
<button
type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => setViewPurchase({ isOpen: true, purchaseId: item.id })}
>
<i className="bx bx-show me-2"></i> View
</a>
</li>
{!isActive ? (
<>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => setManagePurchase({ isOpen: true, purchaseId: item.id })}
>
<i className="bx bx-edit me-2"></i> Edit
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => {
setDeletingId(item.id);
setIsDeleteModalOpen(true);
}}
>
<i className="bx bx-trash me-2"></i> Delete
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => setChallan({ isOpen: true, purchaseId: item.id })}
>
<i className="bx bx-file bx-plus me-2"></i> Add Delivery Challan
</a>
</li>
</>
) : (
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() => {
setDeletingId(item.id);
setIsDeleteModalOpen(true);
}}
>
<i className="bx bx-undo me-2"></i> Restore
</a>
</li>
)}
</ul>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
{data?.data?.length > 0 && (
<Pagination
currentPage={currentPage}
totalPages={data.totalPages}
onPageChange={paginate}
/>
)}
</div>
</>
); );
}; };

View File

@ -120,3 +120,30 @@ export const useAddDeliverChallan = (onSuccessCallback) => {
}, },
}); });
}; };
export const useDeletePurchaseInvoice = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ id, isActive }) =>
await PurchaseRepository.deletePurchase(id, isActive),
onSuccess: (_, variable) => {
queryClient.invalidateQueries({ queryKey: ["purchase_list"] });
showToast(
`Purchase Invoice ${variable.isActive ? "restored" : "deleted"} successfully`,
"success"
);
},
onError: (error) => {
showToast(
error?.response?.data?.message ||
error.message ||
"Failed to delete branch",
"error"
);
},
});
};

View File

@ -19,6 +19,8 @@ export const usePurchaseContext = () => {
}; };
const PurchasePage = () => { const PurchasePage = () => {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [showDelete, setShowDelete] = useState(false);
const [showInactive, setShowInactive] = useState(false);
const [addChallan, setChallan] = useState({ const [addChallan, setChallan] = useState({
isOpen: false, isOpen: false,
purchaseId: null, purchaseId: null,
@ -61,6 +63,21 @@ const PurchasePage = () => {
aria-controls="DataTables_Table_0" aria-controls="DataTables_Table_0"
/> />
</label> </label>
<div className="form-check form-switch d-inline-flex align-items-center ms-3">
<input
type="checkbox"
className="form-check-input"
id="inactiveEmployeesCheckbox"
checked={showInactive}
onChange={() => setShowInactive(!showInactive)}
/>
<label
htmlFor="inactiveEmployeesCheckbox"
className="ms-2 text-xs"
>
{!showInactive ? "Show Deleted" : "Hide Deleted"}
</label>
</div>
</div> </div>
<di className="col-sm-6 text-end"> <di className="col-sm-6 text-end">
<button <button
@ -78,7 +95,7 @@ const PurchasePage = () => {
</div> </div>
</div> </div>
<PurchaseList searchString={searchText} /> <PurchaseList searchString={searchText} isActive={showInactive} />
{managePurchase.isOpen && ( {managePurchase.isOpen && (
<GlobalModel <GlobalModel
isOpen={managePurchase.isOpen} isOpen={managePurchase.isOpen}

View File

@ -14,6 +14,9 @@ export const PurchaseRepository = {
api.get(`/api/PurchaseInvoice/delivery-challan/list/${purchaseInvoiceId}`), api.get(`/api/PurchaseInvoice/delivery-challan/list/${purchaseInvoiceId}`),
addDelievryChallan: (data) => addDelievryChallan: (data) =>
api.post(`/api/PurchaseInvoice/delivery-challan/create`, data), api.post(`/api/PurchaseInvoice/delivery-challan/create`, data),
deletePurchase: (id, isActive = false) =>
api.delete(`/api/PurchaseInvoice/delete/${id}?isActive=${isActive}`),
}; };
// const filterPayload = JSON.stringify({ // const filterPayload = JSON.stringify({