prevent calling unnecessary call during logout and fixed currency displaying in update form

This commit is contained in:
pramod.mahajan 2025-11-08 21:32:35 +05:30
parent e7fcfdb154
commit c14dc3d765
4 changed files with 269 additions and 251 deletions

View File

@ -165,7 +165,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
amount: data.amount || "",
noOfPersons: data.noOfPersons || "",
gstNumber: data.gstNumber || "",
currencyId: data.currencyId || DEFAULT_CURRENCY,
currencyId: data.currency.id || DEFAULT_CURRENCY,
billAttachments: data.documents
? data.documents.map((doc) => ({
fileName: doc.fileName,

View File

@ -16,11 +16,13 @@ import Error from "../common/Error";
import { useRecurringExpenseContext } from "../../pages/RecurringExpense/RecurringExpensePage";
import { useRecurringExpenseList } from "../../hooks/useExpense";
import Pagination from "../common/Pagination";
import { SpinnerLoader } from "../common/Loader";
const RecurringExpenseList = ({ search, filterStatuses }) => {
const { setManageRequest, setVieRequest, setViewRecurring } = useRecurringExpenseContext();
const { setManageRequest, setVieRequest, setViewRecurring } =
useRecurringExpenseContext();
const navigate = useNavigate();
const [IsDeleteModalOpen, setIsDeleteModalOpen,] = useState(false);
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deletingId, setDeletingId] = useState(null);
const SelfId = useSelector(
@ -28,10 +30,10 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
);
const statusColorMap = {
"da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active
"306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed
"3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated
"8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused
"da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active
"306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed
"3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated
"8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused
};
const recurringExpenseColumns = [
@ -68,7 +70,9 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
align: "text-end",
getValue: (e) =>
e?.amount
? `${e?.currency?.symbol ? e.currency.symbol + " " : ""}${e.amount.toLocaleString()}`
? `${
e?.currency?.symbol ? e.currency.symbol + " " : ""
}${e.amount.toLocaleString()}`
: "N/A",
},
{
@ -108,7 +112,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
debouncedSearch
);
const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page);
@ -178,108 +181,116 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
/>
)}
<div className="card page-min-h table-responsive px-sm-4">
<div className="card page-min-h table-responsive px-sm-4 ">
<div className="card-datatable" id="payment-request-table">
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>
{recurringExpenseColumns.map((col) => (
<th key={col.key} className={`sorting ${col.align}`}>
{col.label}
</th>
))}
<th className="text-center">Action</th>
</tr>
</thead>
<tbody>
{filteredData?.length > 0 ? (
filteredData?.map((recurringExpense) => (
<tr
key={recurringExpense.id}
className="align-middle"
style={{ height: "50px" }}
>
{recurringExpenseColumns.map((col) => (
<td
key={col.key}
className={`d-table-cell ${col.align ?? ""} py-3`}
>
{col?.customRender
? col?.customRender(recurringExpense)
: col?.getValue(recurringExpense)}
</td>
))}
<td className="sticky-action-column bg-white">
<div className="d-flex flex-row gap-2 gap-0">
<i
className="bx bx-show text-primary cursor-pointer"
onClick={() =>
setViewRecurring({
recurringId: recurringExpense?.id,
view: true,
})
}
></i>
<div className="dropdown z-2">
<button
type="button"
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
data-bs-toggle="dropdown"
>
<i className="bx bx-dots-vertical-rounded text-muted p-0"></i>
</button>
<ul className="dropdown-menu dropdown-menu-end w-auto">
<li
onClick={() =>
setManageRequest({
IsOpen: true,
RecurringId: recurringExpense?.id,
})
}
>
<a className="dropdown-item px-2 cursor-pointer py-1">
<i className="bx bx-edit text-primary bx-xs me-2"></i>
Modify
</a>
</li>
<li
onClick={() => {
setIsDeleteModalOpen(true);
setDeletingId(recurringExpense.id);
}}
>
<a className="dropdown-item px-2 cursor-pointer py-1">
<i className="bx bx-trash text-danger bx-xs me-2"></i>
Delete
</a>
</li>
</ul>
</div>
</div>
</td>
</tr>
))
) : (
{Array.isArray(filteredData) && filteredData.length > 0 && (
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>
<td
colSpan={recurringExpenseColumns?.length + 1}
className="text-center border-0 py-8"
>
<p>No Recurring Expense Found</p>
</td>
{recurringExpenseColumns.map((col) => (
<th key={col.key} className={`sorting ${col.align}`}>
{col.label}
</th>
))}
<th className="text-center">Action</th>
</tr>
)}
</tbody>
</table>
</thead>
<tbody>
{filteredData?.length > 0 ? (
filteredData?.map((recurringExpense) => (
<tr
key={recurringExpense.id}
className="align-middle"
style={{ height: "50px" }}
>
{recurringExpenseColumns.map((col) => (
<td
key={col.key}
className={`d-table-cell ${col.align ?? ""} py-3`}
>
{col?.customRender
? col?.customRender(recurringExpense)
: col?.getValue(recurringExpense)}
</td>
))}
<td className="sticky-action-column bg-white">
<div className="d-flex flex-row gap-2 gap-0">
<i
className="bx bx-show text-primary cursor-pointer"
onClick={() =>
setViewRecurring({
recurringId: recurringExpense?.id,
view: true,
})
}
></i>
<div className="dropdown z-2">
<button
type="button"
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
data-bs-toggle="dropdown"
>
<i className="bx bx-dots-vertical-rounded text-muted p-0"></i>
</button>
<ul className="dropdown-menu dropdown-menu-end w-auto">
<li
onClick={() =>
setManageRequest({
IsOpen: true,
RecurringId: recurringExpense?.id,
})
}
>
<a className="dropdown-item px-2 cursor-pointer py-1">
<i className="bx bx-edit text-primary bx-xs me-2"></i>
Modify
</a>
</li>
<li
onClick={() => {
setIsDeleteModalOpen(true);
setDeletingId(recurringExpense.id);
}}
>
<a className="dropdown-item px-2 cursor-pointer py-1">
<i className="bx bx-trash text-danger bx-xs me-2"></i>
Delete
</a>
</li>
</ul>
</div>
</div>
</td>
</tr>
))
) : (
<tr>
<td
colSpan={recurringExpenseColumns?.length + 1}
className="text-center border-0 py-8"
></td>
</tr>
)}
</tbody>
</table>
)}
{!filteredData ||
filteredData.length === 0
&& (
<div className="d-flex justify-content-center align-items-center h-64">
{isError ? (<p>{error.message}</p>):(<p>No Recurring Expense Found</p>)}
</div>
)}
</div>
{/* Pagination */}
<Pagination
currentPage={currentPage}
totalPages={data?.totalPages}
e
onPageChange={paginate}
/>
</div>

View File

@ -147,32 +147,36 @@ export const useAuthModal = () => {
export const useLogout = () => {
const queryClient = useQueryClient();
const naviget = useNavigate();
const navigate = useNavigate();
const dispatch = useDispatch();
return useMutation({
mutationFn: async () => {
let payload = {
refreshToken:
localStorage.getItem("refreshToken") ||
sessionStorage.getItem("refreshToken"),
};
return await AuthRepository.logout(payload);
const refreshToken =
localStorage.getItem("refreshToken") ||
sessionStorage.getItem("refreshToken");
if (!refreshToken) return; // no call if already removed
return await AuthRepository.logout({ refreshToken });
},
onSuccess: (data) => {
onMutate: async () => {
// Cancel all ongoing queries
await queryClient.cancelQueries();
},
onSuccess: () => {
queryClient.clear();
removeSession();
dispatch(cacheProfileData(null));
// window.location.href = "/auth/login";
naviget("/auth/login", { replace: true });
if (onSuccessCallBack) onSuccessCallBack();
navigate("/auth/login", { replace: true });
},
onError: (error) => {
showToast(error.message || "Error while creating project", "error");
onError: () => {
removeSession();
queryClient.clear();
navigate("/auth/login", { replace: true });
},
});
};

View File

@ -10,156 +10,159 @@ import ViewRecurringExpense from "../../components/RecurringExpense/ViewRecurrin
export const RecurringExpenseContext = createContext();
export const useRecurringExpenseContext = () => {
const context = useContext(RecurringExpenseContext);
if (!context) {
throw new Error("useRecurringExpenseContext must be used within an ExpenseProvider");
}
return context;
const context = useContext(RecurringExpenseContext);
if (!context) {
throw new Error(
"useRecurringExpenseContext must be used within an ExpenseProvider"
);
}
return context;
};
const RecurringExpensePage = () => {
const [ManageRequest, setManageRequest] = useState({
IsOpen: null,
RecurringId: null,
});
const [viewRecurring, setViewRecurring] = useState({ view: false, recurringId: null })
const [ManageRequest, setManageRequest] = useState({
IsOpen: null,
RecurringId: null,
});
const [viewRecurring, setViewRecurring] = useState({
view: false,
recurringId: null,
});
const [selectedStatuses, setSelectedStatuses] = useState(
PAYEE_RECURRING_EXPENSE.map((s) => s.id)
const [selectedStatuses, setSelectedStatuses] = useState(
PAYEE_RECURRING_EXPENSE.map((s) => s.id)
);
const [search, setSearch] = useState("");
const contextValue = {
setManageRequest,
setViewRecurring,
};
const handleStatusChange = (id) => {
setSelectedStatuses((prev) =>
prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id]
);
};
return (
<RecurringExpenseContext.Provider value={contextValue}>
<div className="container-fluid">
{/* Breadcrumb */}
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Recurring Expense", link: null },
]}
/>
const [search, setSearch] = useState("");
{/* Top Bar */}
<div className="card my-3 px-sm-4 px-0">
<div className="card-body py-2 px-1">
<div className="row align-items-center mb-0">
{/* Left Column: Search + Filter */}
<div className="col-md-8 col-sm-12 mb-2 mb-md-0">
<div className="d-flex align-items-center flex-wrap gap-0">
<input
type="search"
className="form-control form-control-sm w-25"
placeholder="Search Recurring Expense"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
const contextValue = {
setManageRequest,
setViewRecurring
};
const handleStatusChange = (id) => {
setSelectedStatuses((prev) =>
prev.includes(id)
? prev.filter((s) => s !== id)
: [...prev, id]
);
};
return (
<RecurringExpenseContext.Provider value={contextValue}>
<div className="container-fluid">
{/* Breadcrumb */}
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Recurring Expense", link: null },
]}
/>
{/* Top Bar */}
<div className="card my-3 px-sm-4 px-0">
<div className="card-body py-2 px-1">
<div className="row align-items-center mb-0">
{/* Left Column: Search + Filter */}
<div className="col-md-8 col-sm-12 mb-2 mb-md-0">
<div className="d-flex align-items-center flex-wrap gap-0">
<input
type="search"
className="form-control form-control-sm w-25"
placeholder="Search Recurring Expense"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<div className="dropdown">
<a
className="dropdown-toggle hide-arrow cursor-pointer p-1"
data-bs-toggle="dropdown"
aria-expanded="false"
title="Filter"
>
<i className="bx bx-slider-alt ms-1"></i>
</a>
<ul className="dropdown-menu p-2 text-capitalize">
{PAYEE_RECURRING_EXPENSE.map(({ id, label }) => (
<li key={id}>
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
checked={selectedStatuses.includes(id)}
onChange={() => handleStatusChange(id)}
/>
<label className="form-check-label">{label}</label>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
{/* Right Column: Add Button */}
<div className="col-md-4 col-sm-12 text-md-end text-start">
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() =>
setManageRequest({
IsOpen: true,
RecurringId: null,
})
}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">
Add Recurring Expense
</span>
</button>
</div>
</div>
</div>
<div className="dropdown">
<a
className="dropdown-toggle hide-arrow cursor-pointer p-1"
data-bs-toggle="dropdown"
aria-expanded="false"
title="Filter"
>
<i className="bx bx-slider-alt ms-1"></i>
</a>
<ul className="dropdown-menu p-2 text-capitalize">
{PAYEE_RECURRING_EXPENSE.map(({ id, label }) => (
<li key={id}>
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
checked={selectedStatuses.includes(id)}
onChange={() => handleStatusChange(id)}
/>
<label className="form-check-label">{label}</label>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
<RecurringExpenseList filterStatuses={selectedStatuses} search={search} />
{/* Right Column: Add Button */}
<div className="col-md-4 col-sm-12 text-md-end text-start">
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() =>
setManageRequest({
IsOpen: true,
RecurringId: null,
})
}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">
Add Recurring Expense
</span>
</button>
</div>
</div>
</div>
</div>
{ManageRequest.IsOpen && (
<GlobalModel
isOpen
size="lg"
closeModal={() =>
setManageRequest({ IsOpen: null, expenseId: null })
}
>
<ManageRecurringExpense
key={ManageRequest.RecurringId ?? "new"}
closeModal={() =>
setManageRequest({ IsOpen: null, RecurringId: null })
}
requestToEdit={ManageRequest.RecurringId}
/>
</GlobalModel>
)}
{viewRecurring.view && (
<GlobalModel
isOpen
size="lg"
closeModal={() =>
setViewRecurring({ IsOpen: null, recurringId: null })
}
>
{/* <viewRecurring
<RecurringExpenseList
filterStatuses={selectedStatuses}
search={search}
/>
{ManageRequest.IsOpen && (
<GlobalModel
isOpen
size="lg"
closeModal={() =>
setManageRequest({ IsOpen: null, expenseId: null })
}
>
<ManageRecurringExpense
key={ManageRequest.RecurringId ?? "new"}
closeModal={() =>
setManageRequest({ IsOpen: null, RecurringId: null })
}
requestToEdit={ManageRequest.RecurringId}
/>
</GlobalModel>
)}
{viewRecurring.view && (
<GlobalModel
isOpen
size="lg"
closeModal={() =>
setViewRecurring({ IsOpen: null, recurringId: null })
}
>
{/* <viewRecurring
key={viewRecurring.RecurringId ?? "new"}
closeModal={() =>
setViewRecurring({ IsOpen: null, recurringId: null })
}
RecurringId={viewRecurring.recurringId}
/> */}
<ViewRecurringExpense RecurringId={viewRecurring.recurringId} />
</GlobalModel>
)}
</div>
</RecurringExpenseContext.Provider>
);
<ViewRecurringExpense RecurringId={viewRecurring.recurringId} />
</GlobalModel>
)}
</div>
</RecurringExpenseContext.Provider>
);
};
export default RecurringExpensePage;