@@ -88,27 +85,15 @@ const AttendanceOverview = () => {
Role-wise present count
-
@@ -127,9 +112,7 @@ const AttendanceOverview = () => {
Role |
{dates.map((date, idx) => (
-
- {date}
- |
+ {date} |
))}
diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx
index 171b8cae..9435d3bc 100644
--- a/src/components/Dashboard/Dashboard.jsx
+++ b/src/components/Dashboard/Dashboard.jsx
@@ -13,26 +13,33 @@ import { useDispatch, useSelector } from "react-redux";
// import ProjectCompletionChart from "./ProjectCompletionChart";
// import ProjectProgressChart from "./ProjectProgressChart";
// import ProjectOverview from "../Project/ProjectOverview";
-import AttendanceOverview from "./AttendanceChart";
+import AttendanceOverview from "./AttendanceOverview";
import { useSelectedProject } from "../../slices/apiDataManager";
import { useProjectName } from "../../hooks/useProjects";
+import ExpenseAnalysis from "./ExpenseAnalysis";
const Dashboard = () => {
-
// const { projectsCardData } = useDashboardProjectsCardData();
// const { teamsCardData } = useDashboardTeamsCardData();
// const { tasksCardData } = useDashboardTasksCardData();
// Get the selected project ID from Redux store
+ const projectId = useSelector((store) => store.localVariables.projectId);
+ const isAllProjectsSelected = projectId === null;
return (
- {/* {shouldShowAttendance && ( */}
+
+
+
+
+ {!isAllProjectsSelected && (
+ )}
);
diff --git a/src/components/Dashboard/ExpenseAnalysis.jsx b/src/components/Dashboard/ExpenseAnalysis.jsx
new file mode 100644
index 00000000..3cdda4ae
--- /dev/null
+++ b/src/components/Dashboard/ExpenseAnalysis.jsx
@@ -0,0 +1,142 @@
+import React, { useEffect, useState } from "react";
+import Chart from "react-apexcharts";
+import { useExpenseAnalysis } from "../../hooks/useDashboard_Data";
+import { useSelectedProject } from "../../slices/apiDataManager";
+import { DateRangePicker1 } from "../common/DateRangePicker";
+import { FormProvider, useForm } from "react-hook-form";
+import { localToUtc } from "../../utils/appUtils";
+
+const ExpenseAnalysis = () => {
+ const projectId = useSelectedProject();
+
+ const methods = useForm({
+ defaultValues: {
+ startDate: "",
+ endDate: "",
+ },
+ });
+
+ const { watch } = methods;
+
+ const [startDate, endDate] = watch(["startDate", "endDate"]);
+ console.log(startDate,endDate)
+ const { data, isLoading, isError, error, isFetching } = useExpenseAnalysis(
+ projectId,
+ localToUtc(startDate),
+ localToUtc(endDate)
+);
+
+ if (isError) return
{error.message}
;
+
+ const report = data?.report || [];
+
+ const labels = report.map((item) => item.projectName);
+ const series = report.map((item) => item.totalApprovedAmount || 0);
+ const total = data?.totalAmount || 0;
+
+ const donutOptions = {
+ chart: { type: "donut" },
+ labels,
+ legend: { show: false },
+ dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` },
+ colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"],
+ plotOptions: {
+ pie: {
+ donut: {
+ size: "70%",
+ labels: {
+ show: true,
+ total: {
+ show: true,
+ label: "Total",
+ fontSize: "16px",
+ formatter: () => `${total}`,
+ },
+ },
+ },
+ },
+ },
+ };
+
+
+ if (data?.report === 0) {
+ return
No data found
;
+ }
+
+ return (
+
+
+
+
Expense Breakdown
+
Detailed project expenses
+
+
+
+
+
+
+
+
+
+
+ {/* 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"
+ />
+
+
+
+
+ {report.map((item, idx) => (
+
+
+
+
+
+
+
+ {item.projectName}
+ {item.totalApprovedAmount}
+
+
+ ))}
+
+
+ >
+ )}
+
+
+
+ );
+};
+
+export default ExpenseAnalysis;
diff --git a/src/components/Expenses/ManageExpense.jsx b/src/components/Expenses/ManageExpense.jsx
index fa19d2c5..860a61c9 100644
--- a/src/components/Expenses/ManageExpense.jsx
+++ b/src/components/Expenses/ManageExpense.jsx
@@ -189,7 +189,9 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
const editPayload = { ...payload, id: data.id };
ExpenseUpdate({ id: data.id, payload: editPayload });
} else {
- CreateExpense(payload);
+ console.log(fromdata)
+ console.log(payload)
+ // CreateExpense(payload);
}
};
const ExpenseTypeId = watch("expensesTypeId");
diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx
index ec55b48c..870672e3 100644
--- a/src/components/Layout/Header.jsx
+++ b/src/components/Layout/Header.jsx
@@ -67,7 +67,7 @@ const Header = () => {
if (projectLoading) return "Loading...";
if (!projectNames?.length) return "No Projects Assigned";
if (projectNames.length === 1) return projectNames[0].name;
-
+ if (selectedProject === null) return "All Projects";
const selectedObj = projectNames.find((p) => p.id === selectedProject);
return selectedObj
? selectedObj.name
@@ -199,6 +199,14 @@ const Header = () => {
className="dropdown-menu"
style={{ overflow: "auto", maxHeight: "300px" }}
>
+
+
+ handleProjectChange(null)}
+ >All Project
+
+
{[...projectsForDropdown]
.sort((a, b) => a?.name?.localeCompare(b.name))
.map((project) => (
diff --git a/src/components/common/DateRangePicker.jsx b/src/components/common/DateRangePicker.jsx
index 72aad155..1fd5d2f6 100644
--- a/src/components/common/DateRangePicker.jsx
+++ b/src/components/common/DateRangePicker.jsx
@@ -85,6 +85,7 @@ export const DateRangePicker1 = ({
resetSignal,
defaultRange = true,
maxDate = null,
+ sm,md,
...rest
}) => {
const inputRef = useRef(null);
@@ -168,11 +169,12 @@ export const DateRangePicker1 = ({
const formattedValue = start && end ? `${start} To ${end}` : "";
return (
-
+
{
inputRef.current = el;
@@ -181,12 +183,10 @@ export const DateRangePicker1 = ({
readOnly={!allowText}
autoComplete="off"
/>
- inputRef.current?._flatpickr?.open()}
- >
-
-
+ >
);
};
diff --git a/src/hooks/useDashboard_Data.jsx b/src/hooks/useDashboard_Data.jsx
index 8800d48a..7c022b46 100644
--- a/src/hooks/useDashboard_Data.jsx
+++ b/src/hooks/useDashboard_Data.jsx
@@ -253,4 +253,21 @@ export const useDashboardProjectsCardData = () => {
return resp.data;
}
})
-}
\ No newline at end of file
+}
+
+export const useExpenseAnalysis = (projectId, startDate, endDate) => {
+ const hasBothDates = !!startDate && !!endDate;
+ const noDatesSelected = !startDate && !endDate;
+
+ const shouldFetch =
+ noDatesSelected ||
+ hasBothDates;
+ return useQuery({
+ queryKey: ["expenseAnalysis", projectId, startDate, endDate],
+ queryFn: async () => {
+ const resp = await GlobalRepository.getExpenseData(projectId, startDate, endDate);
+ return resp.data;
+ },
+ enabled:shouldFetch
+ });
+};
\ No newline at end of file
diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx
index e741e0f3..6f4e1130 100644
--- a/src/pages/project/ProjectDetails.jsx
+++ b/src/pages/project/ProjectDetails.jsx
@@ -15,7 +15,7 @@ import { useProjectDetails, useProjectName } from "../../hooks/useProjects";
import { ComingSoonPage } from "../Misc/ComingSoonPage";
import eventBus from "../../services/eventBus";
import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart";
-import AttendanceOverview from "../../components/Dashboard/AttendanceChart";
+import AttendanceOverview from "../../components/Dashboard/AttendanceOverview";
import { setProjectId } from "../../slices/localVariablesSlice";
import ProjectDocuments from "../../components/Project/ProjectDocuments";
import ProjectSetting from "../../components/Project/ProjectSetting";
diff --git a/src/repositories/GlobalRepository.jsx b/src/repositories/GlobalRepository.jsx
index d3367fa6..9fc8b074 100644
--- a/src/repositories/GlobalRepository.jsx
+++ b/src/repositories/GlobalRepository.jsx
@@ -41,7 +41,32 @@ const GlobalRepository = {
return api.get(url);
},
- getAttendanceOverview:(projectId,days)=>api.get(`/api/dashboard/attendance-overview/${projectId}?days=${days}`)
+ getAttendanceOverview:(projectId,days)=>api.get(`/api/dashboard/attendance-overview/${projectId}?days=${days}`),
+
+
+ getExpenseData: (projectId, startDate, endDate) => {
+ let url = `api/Dashboard/expense/type`
+
+ const queryParams = [];
+
+ if (projectId) {
+ queryParams.push(`projectId=${projectId}`);
+ }
+
+ if (startDate) {
+ queryParams.push(`startDate=${startDate}`);
+ }
+ if (endDate) {
+ queryParams.push(`endDate=${endDate}`);
+ }
+
+ if (queryParams.length > 0) {
+ url += `?${queryParams.join("&")}`;
+ }
+
+ return api.get(url );
+ },
+
};
diff --git a/src/utils/appUtils.js b/src/utils/appUtils.js
index 0766514a..0533dead 100644
--- a/src/utils/appUtils.js
+++ b/src/utils/appUtils.js
@@ -69,14 +69,24 @@ export const normalizeAllowedContentTypes = (allowedContentType) => {
return allowedContentType.split(",");
return [];
};
-export function localToUtc(localDateString) {
- if (!localDateString || typeof localDateString !== "string") return null;
+export function localToUtc(dateString) {
+ if (!dateString || typeof dateString !== "string") return null;
-
- const [year, month, day] = localDateString.trim().split("-");
- if (!year || !month || !day) return null;
+ const parts = dateString.trim().split("-");
+ if (parts.length !== 3) return null;
+
+ let day, month, year;
+
+ if (parts[0].length === 4) {
+ // Format: yyyy-mm-dd
+ [year, month, day] = parts;
+ } else {
+ // Format: dd-mm-yyyy
+ [day, month, year] = parts;
+ }
+
+ if (!day || !month || !year) return null;
const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day), 0, 0, 0));
-
return isNaN(date.getTime()) ? null : date.toISOString();
-}
\ No newline at end of file
+}