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 || "", amount: data.amount || "",
noOfPersons: data.noOfPersons || "", noOfPersons: data.noOfPersons || "",
gstNumber: data.gstNumber || "", gstNumber: data.gstNumber || "",
currencyId: data.currencyId || DEFAULT_CURRENCY, currencyId: data.currency.id || DEFAULT_CURRENCY,
billAttachments: data.documents billAttachments: data.documents
? data.documents.map((doc) => ({ ? data.documents.map((doc) => ({
fileName: doc.fileName, fileName: doc.fileName,

View File

@ -16,11 +16,13 @@ import Error from "../common/Error";
import { useRecurringExpenseContext } from "../../pages/RecurringExpense/RecurringExpensePage"; import { useRecurringExpenseContext } from "../../pages/RecurringExpense/RecurringExpensePage";
import { useRecurringExpenseList } from "../../hooks/useExpense"; import { useRecurringExpenseList } from "../../hooks/useExpense";
import Pagination from "../common/Pagination"; import Pagination from "../common/Pagination";
import { SpinnerLoader } from "../common/Loader";
const RecurringExpenseList = ({ search, filterStatuses }) => { const RecurringExpenseList = ({ search, filterStatuses }) => {
const { setManageRequest, setVieRequest, setViewRecurring } = useRecurringExpenseContext(); const { setManageRequest, setVieRequest, setViewRecurring } =
useRecurringExpenseContext();
const navigate = useNavigate(); const navigate = useNavigate();
const [IsDeleteModalOpen, setIsDeleteModalOpen,] = useState(false); const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deletingId, setDeletingId] = useState(null); const [deletingId, setDeletingId] = useState(null);
const SelfId = useSelector( const SelfId = useSelector(
@ -28,10 +30,10 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
); );
const statusColorMap = { const statusColorMap = {
"da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active "da462422-13b2-45cc-a175-910a225f6fc8": "primary", // Active
"306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed "306856fb-5655-42eb-bf8b-808bb5e84725": "success", // Completed
"3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated "3ec864d2-8bf5-42fb-ba70-5090301dd816": "danger", // De-Activated
"8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused "8bfc9346-e092-4a80-acbf-515ae1ef6868": "warning", // Paused
}; };
const recurringExpenseColumns = [ const recurringExpenseColumns = [
@ -68,7 +70,9 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
align: "text-end", align: "text-end",
getValue: (e) => getValue: (e) =>
e?.amount e?.amount
? `${e?.currency?.symbol ? e.currency.symbol + " " : ""}${e.amount.toLocaleString()}` ? `${
e?.currency?.symbol ? e.currency.symbol + " " : ""
}${e.amount.toLocaleString()}`
: "N/A", : "N/A",
}, },
{ {
@ -108,7 +112,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => {
debouncedSearch debouncedSearch
); );
const paginate = (page) => { const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) { if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page); 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"> <div className="card-datatable" id="payment-request-table">
<table className="table border-top dataTable text-nowrap align-middle"> {Array.isArray(filteredData) && filteredData.length > 0 && (
<thead> <table className="table border-top dataTable text-nowrap align-middle">
<tr> <thead>
{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>
))
) : (
<tr> <tr>
<td {recurringExpenseColumns.map((col) => (
colSpan={recurringExpenseColumns?.length + 1} <th key={col.key} className={`sorting ${col.align}`}>
className="text-center border-0 py-8" {col.label}
> </th>
<p>No Recurring Expense Found</p> ))}
</td> <th className="text-center">Action</th>
</tr> </tr>
)} </thead>
</tbody>
</table> <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> </div>
{/* Pagination */} {/* Pagination */}
<Pagination <Pagination
currentPage={currentPage} currentPage={currentPage}
totalPages={data?.totalPages} totalPages={data?.totalPages}
e
onPageChange={paginate} onPageChange={paginate}
/> />
</div> </div>

View File

@ -147,32 +147,36 @@ export const useAuthModal = () => {
export const useLogout = () => { export const useLogout = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const naviget = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
return useMutation({ return useMutation({
mutationFn: async () => { mutationFn: async () => {
let payload = { const refreshToken =
refreshToken: localStorage.getItem("refreshToken") ||
localStorage.getItem("refreshToken") || sessionStorage.getItem("refreshToken");
sessionStorage.getItem("refreshToken"),
}; if (!refreshToken) return; // no call if already removed
return await AuthRepository.logout(payload);
return await AuthRepository.logout({ refreshToken });
}, },
onSuccess: (data) => { onMutate: async () => {
// Cancel all ongoing queries
await queryClient.cancelQueries();
},
onSuccess: () => {
queryClient.clear(); queryClient.clear();
removeSession(); removeSession();
dispatch(cacheProfileData(null)); dispatch(cacheProfileData(null));
navigate("/auth/login", { replace: true });
// window.location.href = "/auth/login";
naviget("/auth/login", { replace: true });
if (onSuccessCallBack) onSuccessCallBack();
}, },
onError: (error) => { onError: () => {
showToast(error.message || "Error while creating project", "error");
removeSession(); 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 RecurringExpenseContext = createContext();
export const useRecurringExpenseContext = () => { export const useRecurringExpenseContext = () => {
const context = useContext(RecurringExpenseContext); const context = useContext(RecurringExpenseContext);
if (!context) { if (!context) {
throw new Error("useRecurringExpenseContext must be used within an ExpenseProvider"); throw new Error(
} "useRecurringExpenseContext must be used within an ExpenseProvider"
return context; );
}
return context;
}; };
const RecurringExpensePage = () => { const RecurringExpensePage = () => {
const [ManageRequest, setManageRequest] = useState({ const [ManageRequest, setManageRequest] = useState({
IsOpen: null, IsOpen: null,
RecurringId: null, RecurringId: null,
}); });
const [viewRecurring, setViewRecurring] = useState({ view: false, recurringId: null }) const [viewRecurring, setViewRecurring] = useState({
view: false,
recurringId: null,
});
const [selectedStatuses, setSelectedStatuses] = useState( const [selectedStatuses, setSelectedStatuses] = useState(
PAYEE_RECURRING_EXPENSE.map((s) => s.id) 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 = { <div className="dropdown">
setManageRequest, <a
setViewRecurring className="dropdown-toggle hide-arrow cursor-pointer p-1"
}; data-bs-toggle="dropdown"
aria-expanded="false"
const handleStatusChange = (id) => { title="Filter"
setSelectedStatuses((prev) => >
prev.includes(id) <i className="bx bx-slider-alt ms-1"></i>
? prev.filter((s) => s !== id) </a>
: [...prev, id] <ul className="dropdown-menu p-2 text-capitalize">
); {PAYEE_RECURRING_EXPENSE.map(({ id, label }) => (
}; <li key={id}>
return ( <div className="form-check">
<RecurringExpenseContext.Provider value={contextValue}> <input
<div className="container-fluid"> className="form-check-input"
{/* Breadcrumb */} type="checkbox"
<Breadcrumb checked={selectedStatuses.includes(id)}
data={[ onChange={() => handleStatusChange(id)}
{ label: "Home", link: "/dashboard" }, />
{ label: "Recurring Expense", link: null }, <label className="form-check-label">{label}</label>
]} </div>
/> </li>
))}
{/* Top Bar */} </ul>
<div className="card my-3 px-sm-4 px-0"> </div>
<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> </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 && ( <RecurringExpenseList
<GlobalModel filterStatuses={selectedStatuses}
isOpen search={search}
size="lg" />
closeModal={() =>
setManageRequest({ IsOpen: null, expenseId: null }) {ManageRequest.IsOpen && (
} <GlobalModel
> isOpen
<ManageRecurringExpense size="lg"
key={ManageRequest.RecurringId ?? "new"} closeModal={() =>
closeModal={() => setManageRequest({ IsOpen: null, expenseId: null })
setManageRequest({ IsOpen: null, RecurringId: null }) }
} >
requestToEdit={ManageRequest.RecurringId} <ManageRecurringExpense
/> key={ManageRequest.RecurringId ?? "new"}
</GlobalModel> closeModal={() =>
)} setManageRequest({ IsOpen: null, RecurringId: null })
{viewRecurring.view && ( }
<GlobalModel requestToEdit={ManageRequest.RecurringId}
isOpen />
size="lg" </GlobalModel>
closeModal={() => )}
setViewRecurring({ IsOpen: null, recurringId: null }) {viewRecurring.view && (
} <GlobalModel
> isOpen
{/* <viewRecurring size="lg"
closeModal={() =>
setViewRecurring({ IsOpen: null, recurringId: null })
}
>
{/* <viewRecurring
key={viewRecurring.RecurringId ?? "new"} key={viewRecurring.RecurringId ?? "new"}
closeModal={() => closeModal={() =>
setViewRecurring({ IsOpen: null, recurringId: null }) setViewRecurring({ IsOpen: null, recurringId: null })
} }
RecurringId={viewRecurring.recurringId} RecurringId={viewRecurring.recurringId}
/> */} /> */}
<ViewRecurringExpense RecurringId={viewRecurring.recurringId} /> <ViewRecurringExpense RecurringId={viewRecurring.recurringId} />
</GlobalModel> </GlobalModel>
)} )}
</div>
</RecurringExpenseContext.Provider>
</div> );
</RecurringExpenseContext.Provider>
);
}; };
export default RecurringExpensePage; export default RecurringExpensePage;