- {/* Initial loading: show full loader */}
- {isLoading && (
-
- Loading...
-
- )}
-
- {/* Data display */}
- {!isLoading && report.length === 0 && (
-
No data found
- )}
-
- {!isLoading && report.length > 0 && (
- <>
- {/* Overlay spinner for refetch */}
- {isFetching && (
-
- Loading...
-
- )}
-
-
-
item.totalApprovedAmount || 0)}
- type="donut"
- width="320"
- />
+
+ {/* Initial loading: show full loader */}
+ {isLoading && (
+
+ Loading...
+ )}
-
-
- {report.map((item, idx) => (
-
-
-
-
-
-
-
- {item.projectName}
- {item.totalApprovedAmount}
-
-
- ))}
+ {/* Data display */}
+ {!isLoading && report.length === 0 && (
+
No data found
+ )}
+
+ {!isLoading && report.length > 0 && (
+ <>
+ {/* Overlay spinner for refetch */}
+ {isFetching && (
+
+ Loading...
+
+ )}
+
+
+ item.totalApprovedAmount || 0)}
+ type="donut"
+ width="320"
+ />
-
- >
- )}
-
-
+
+
+ {report.map((item, idx) => (
+
+
+
+
+
+
+
+ {item.projectName}
+
+ {formatCurrency(item.totalApprovedAmount)}
+
+
+
+ ))}
+
+
+ >
+ )}
+
+ >
);
};
diff --git a/src/components/Dashboard/ExpenseStatus.jsx b/src/components/Dashboard/ExpenseStatus.jsx
new file mode 100644
index 00000000..3fdddf51
--- /dev/null
+++ b/src/components/Dashboard/ExpenseStatus.jsx
@@ -0,0 +1,116 @@
+import React, { useEffect, useState } from "react";
+import { useExpense } from "../../hooks/useExpense";
+import { useExpenseStatus } from "../../hooks/useDashboard_Data";
+import { useSelectedProject } from "../../slices/apiDataManager";
+import { useProjectName } from "../../hooks/useProjects";
+import { formatCurrency } from "../../utils/appUtils";
+import { EXPENSE_STATUS } from "../../utils/constants";
+import { useNavigate } from "react-router-dom";
+
+const ExpenseStatus = () => {
+ const [projectName, setProjectName] = useState("All Project");
+ const selectedProject = useSelectedProject();
+ const { projectNames, loading } = useProjectName();
+ const { data, isPending, error } = useExpenseStatus(selectedProject);
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (selectedProject && projectNames?.length) {
+ const project = projectNames.find((p) => p.id === selectedProject);
+ setProjectName(project?.name || "All Project");
+ } else {
+ setProjectName("All Project");
+ }
+ }, [projectNames, selectedProject]);
+ return (
+ <>
+
+
+
Expense - By Status
+
{projectName}
+
+
+
+
+
+
+ Project Spendings:{" "}
+
+ {formatCurrency(data?.totalAmount)}
+
+
+
{`(All Processed Payments)`}
+
+
+ {[
+ {
+ title: "Pending Payment",
+ count: data?.approvePending?.count,
+ amount: data?.approvePending?.totalAmount,
+ icon: "bx bx-rupee",
+ iconColor: "text-primary",
+ status: EXPENSE_STATUS.payment_pending,
+ },
+ {
+ title: "Pending Approver",
+ count: data?.processPending?.count,
+ amount: data?.processPending?.totalAmount,
+ icon: "fa-solid fa-check",
+ iconColor: "text-warning",
+ status: EXPENSE_STATUS.approve_pending,
+ },
+ {
+ title: "Pending Reviewer",
+ count: data?.reviewPending?.count,
+ amount: data?.reviewPending?.totalAmount,
+ icon: "bx bx-file",
+ iconColor: "text-secondary",
+ status: EXPENSE_STATUS.review_pending,
+ },
+ {
+ title: "Draft",
+ count: data?.submited?.count,
+ amount: data?.submited?.totalAmount,
+ icon: "bx bx-file-blank",
+ iconColor: "text-info",
+ status: EXPENSE_STATUS.daft,
+ },
+ ].map((item, idx) => (
+
navigate(`/expenses/${item.status}`)}
+ >
+
+
+
+
+
+
+
+
+ {item.title}
+
+ {formatCurrency(item.amount)}
+
+
+
+ {" "}
+
+ {item.count}{" "}
+
+
+
+
+
+
+
+
+ ))}
+
+
+ >
+ );
+};
+
+export default ExpenseStatus;
diff --git a/src/components/Expenses/ExpenseFilterPanel.jsx b/src/components/Expenses/ExpenseFilterPanel.jsx
index c04a0981..c3e2db23 100644
--- a/src/components/Expenses/ExpenseFilterPanel.jsx
+++ b/src/components/Expenses/ExpenseFilterPanel.jsx
@@ -13,9 +13,11 @@ import { useSelector } from "react-redux";
import moment from "moment";
import { useExpenseFilter } from "../../hooks/useExpense";
import { ExpenseFilterSkeleton } from "./ExpenseSkeleton";
-import { useLocation } from "react-router-dom";
+import { useLocation, useNavigate, useParams } from "react-router-dom";
const ExpenseFilterPanel = ({ onApply, handleGroupBy }) => {
+ const { status } = useParams();
+ const navigate = useNavigate();
const selectedProjectId = useSelector(
(store) => store.localVariables.projectId
);
@@ -37,9 +39,22 @@ const ExpenseFilterPanel = ({ onApply, handleGroupBy }) => {
const [selectedGroup, setSelectedGroup] = useState(groupByList[0]);
const [resetKey, setResetKey] = useState(0);
+ const dynamicDefaultFilter = useMemo(() => {
+ return {
+ ...defaultFilter,
+ statusIds: status ? [status] : defaultFilter.statusIds || [],
+ projectIds: defaultFilter.projectIds || [],
+ createdByIds: defaultFilter.createdByIds || [],
+ paidById: defaultFilter.paidById || [],
+ isTransactionDate: defaultFilter.isTransactionDate ?? true,
+ startDate: defaultFilter.startDate,
+ endDate: defaultFilter.endDate,
+ };
+ }, [status]);
+
const methods = useForm({
resolver: zodResolver(SearchSchema),
- defaultValues: defaultFilter,
+ defaultValues: dynamicDefaultFilter,
});
const { control, handleSubmit, reset, setValue, watch } = methods;
@@ -71,14 +86,49 @@ const ExpenseFilterPanel = ({ onApply, handleGroupBy }) => {
onApply(defaultFilter);
handleGroupBy(groupByList[0].id);
closePanel();
+ if (status) {
+ navigate("/expenses", { replace: true });
+ }
};
- // Close popup when navigating to another component
const location = useLocation();
useEffect(() => {
closePanel();
}, [location]);
+ const [appliedStatusId, setAppliedStatusId] = useState(null);
+
+ useEffect(() => {
+ if (!status) return;
+
+ if (status !== appliedStatusId && data) {
+ const filterWithStatus = {
+ ...dynamicDefaultFilter,
+ startDate: dynamicDefaultFilter.startDate
+ ? moment
+ .utc(dynamicDefaultFilter.startDate, "DD-MM-YYYY")
+ .toISOString()
+ : undefined,
+ endDate: dynamicDefaultFilter.endDate
+ ? moment.utc(dynamicDefaultFilter.endDate, "DD-MM-YYYY").toISOString()
+ : undefined,
+ };
+
+ onApply(filterWithStatus);
+ handleGroupBy(selectedGroup.id);
+
+ setAppliedStatusId(status);
+ }
+ }, [
+ status,
+ data,
+ dynamicDefaultFilter,
+ onApply,
+ handleGroupBy,
+ selectedGroup.id,
+ appliedStatusId,
+ ]);
+
if (isLoading || isFetching) return
;
if (isError && isFetched)
return
Something went wrong Here- {error.message}
;
diff --git a/src/components/Expenses/ExpenseList.jsx b/src/components/Expenses/ExpenseList.jsx
index 35085588..affb44a0 100644
--- a/src/components/Expenses/ExpenseList.jsx
+++ b/src/components/Expenses/ExpenseList.jsx
@@ -10,7 +10,7 @@ import {
EXPENSE_REJECTEDBY,
ITEMS_PER_PAGE,
} from "../../utils/constants";
-import { getColorNameFromHex, useDebounce } from "../../utils/appUtils";
+import { formatCurrency, getColorNameFromHex, useDebounce } from "../../utils/appUtils";
import { ExpenseTableSkeleton } from "./ExpenseSkeleton";
import ConfirmModal from "../common/ConfirmModal";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
@@ -140,7 +140,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
label: "Amount",
getValue: (e) => (
<>
-
{e?.amount}
+ {formatCurrency(e?.amount)}
>
),
isAlwaysVisible: true,
diff --git a/src/components/Expenses/ViewExpense.jsx b/src/components/Expenses/ViewExpense.jsx
index 7db45afc..a59c445f 100644
--- a/src/components/Expenses/ViewExpense.jsx
+++ b/src/components/Expenses/ViewExpense.jsx
@@ -111,9 +111,6 @@ const ViewExpense = ({ ExpenseId }) => {
Expense Details