From 997971629f62cb5642cc493e66130e5177b20b6c Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 4 Oct 2025 11:08:19 +0530 Subject: [PATCH] addd Expense By project --- src/components/Dashboard/Dashboard.jsx | 42 +++-- src/components/Dashboard/ExpenseByProject.jsx | 162 ++++++++++++++++++ src/hooks/useDashboard_Data.jsx | 13 +- src/pages/Expense/ExpensePage.jsx | 4 +- src/repositories/GlobalRepository.jsx | 25 ++- src/utils/dateUtils.jsx | 14 +- 6 files changed, 234 insertions(+), 26 deletions(-) create mode 100644 src/components/Dashboard/ExpenseByProject.jsx diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index 931ad8a9..a65d2d4f 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -18,6 +18,7 @@ import { useSelectedProject } from "../../slices/apiDataManager"; import { useProjectName } from "../../hooks/useProjects"; import ExpenseAnalysis from "./ExpenseAnalysis"; import ExpenseStatus from "./ExpenseStatus"; +import ExpenseByProject from "./ExpenseByProject"; const Dashboard = () => { // const { projectsCardData } = useDashboardProjectsCardData(); @@ -29,24 +30,33 @@ const Dashboard = () => { const isAllProjectsSelected = projectId === null; return ( -
-
-
-
- -
-
- -
-
- -
-
+
+
+
+
+
- {isAllProjectsSelected && (
- -
)}
+ +
+
+ +
+
+
+ +
+ {!isAllProjectsSelected && ( +
+ +
+ )} +
+ +
+
+
+ ); }; diff --git a/src/components/Dashboard/ExpenseByProject.jsx b/src/components/Dashboard/ExpenseByProject.jsx new file mode 100644 index 00000000..e3c79113 --- /dev/null +++ b/src/components/Dashboard/ExpenseByProject.jsx @@ -0,0 +1,162 @@ +import React, { useState, useEffect } from "react"; +import Chart from "react-apexcharts"; +import { useExpenseType } from "../../hooks/masterHook/useMaster"; +import { useSelector } from "react-redux"; +import { useExpenseDataByProject } from "../../hooks/useDashboard_Data"; +import { formatCurrency } from "../../utils/appUtils"; +import { formatDate_DayMonth } from "../../utils/dateUtils"; + +const ExpenseByProject = () => { + const projectId = useSelector((store) => store.localVariables.projectId); + const [range, setRange] = useState("12M"); + const [selectedType, setSelectedType] = useState(""); + const [viewMode, setViewMode] = useState("Category"); + const [chartData, setChartData] = useState({ categories: [], data: [] }); + + const { ExpenseTypes, loading: typeLoading } = useExpenseType(); + + const { data: expenseApiData, isLoading } = useExpenseDataByProject( + projectId, + selectedType, + range === "All" ? null : parseInt(range) + ); + + useEffect(() => { + if (expenseApiData) { + const categories = expenseApiData.map( + (item) => formatDate_DayMonth(item.monthName, item.year) + ); + const data = expenseApiData.map((item) => item.total); + setChartData({ categories, data }); + } else { + setChartData({ categories: [], data: [] }); + } + }, [expenseApiData]); + + const getSelectedTypeName = () => { + if (!selectedType) return "All Types"; + const found = ExpenseTypes.find((t) => t.id === selectedType); + return found ? found.name : "All Types"; + }; + + const options = { + chart: { type: "bar", toolbar: { show: false } }, + plotOptions: { bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 } }, + dataLabels: { enabled: true, formatter: (val) => formatCurrency(val) }, + xaxis: { + categories: chartData.categories, + labels: { style: { fontSize: "12px" }, rotate: -45 }, + }, + tooltip: { + y: { + formatter: (val) => `${formatCurrency(val)} (${getSelectedTypeName()})`, + }, + }, + + annotations: { xaxis: [{ x: 0, strokeDashArray: 0, }] }, + fill: { opacity: 1 }, + colors: ["#2196f3"], + }; + + const series = [ + { + name: getSelectedTypeName(), + data: chartData.data, + }, + ]; + + return ( +
+ {/* Header */} +
+
+
+
Monthly Expense -
+

Detailed project expenses

+
+
+ +
    +
  • + +
  • +
  • + +
  • +
+
+ {viewMode === "Category" && ( + + )} +
+ + {/* Range Buttons + Expense Dropdown */} +
+ {["1M", "3M", "6M", "12M", "All"].map((item) => ( + + ))} +
+
+ + {/* Chart */} +
+ {isLoading ? ( +

Loading chart...

+ ) : ( + + )} +
+
+ ); +}; + +export default ExpenseByProject; \ No newline at end of file diff --git a/src/hooks/useDashboard_Data.jsx b/src/hooks/useDashboard_Data.jsx index 747d2f5b..48be991f 100644 --- a/src/hooks/useDashboard_Data.jsx +++ b/src/hooks/useDashboard_Data.jsx @@ -282,4 +282,15 @@ export const useExpenseStatus = (projectId)=>{ return resp.data; } }) -} \ No newline at end of file +} + +export const useExpenseDataByProject = (projectId, categoryId, months) => { + return useQuery({ + queryKey: ["expenseByProject", projectId, categoryId, months], + queryFn: async () => { + const resp = await GlobalRepository.getExpenseDataByProject(projectId, categoryId, months); + return resp.data; + }, + + }); +}; \ No newline at end of file diff --git a/src/pages/Expense/ExpensePage.jsx b/src/pages/Expense/ExpensePage.jsx index 2141af2c..1206e237 100644 --- a/src/pages/Expense/ExpensePage.jsx +++ b/src/pages/Expense/ExpensePage.jsx @@ -129,9 +129,7 @@ const ExpensePage = () => {
- + {IsCreatedAble && (