diff --git a/src/components/Dashboard/CollectionOverview.jsx b/src/components/Dashboard/CollectionOverview.jsx new file mode 100644 index 00000000..448072b4 --- /dev/null +++ b/src/components/Dashboard/CollectionOverview.jsx @@ -0,0 +1,345 @@ +import React from "react"; +import Chart from "react-apexcharts"; +import { useGetCollectionOverview } from "../../hooks/useDashboard_Data"; +import { formatFigure } from "../../utils/appUtils"; + +const CollectionOverview = ({ data }) => { + const borderColor = "#ddd"; + const labelColor = "#6c757d"; + + // Extract bucket values + const labels = ["0–30 Days", "30–60 Days", "60–90 Days", "90+ Days"]; + + const amounts = [ + data.bucket0To30Amount, + data.bucket30To60Amount, + data.bucket60To90Amount, + data.bucket90PlusAmount, + ]; + + // Colors (Zoho-style distributed) + const colors = ["#7367F0", "#00cfe8", "#28c76f", "#ea5455"]; + + const options = { + chart: { + type: "bar", + height: 260, + toolbar: { show: false }, + }, + + plotOptions: { + bar: { + horizontal: true, + barHeight: "65%", + distributed: true, + borderRadius: 8, + startingShape: "rounded", + }, + }, + + colors: colors, + + grid: { + borderColor: borderColor, + strokeDashArray: 6, + padding: { top: -10, bottom: -10 }, + xaxis: { lines: { show: true } }, + }, + + dataLabels: { + enabled: true, + formatter: (_, opts) => labels[opts.dataPointIndex], + style: { + colors: ["#fff"], + fontSize: "13px", + fontWeight: 500, + }, + offsetX: 0, + }, + + xaxis: { + categories: amounts.map((a) => a), + labels: { + style: { colors: labelColor, fontSize: "12px" }, + formatter: (val) => `₹${val.toLocaleString()}`, + }, + }, + + yaxis: { + labels: { + style: { + colors: labelColor, + fontSize: "13px", + }, + formatter: () => "", // hide duplicate labels + }, + }, + + tooltip: { + custom: ({ series, seriesIndex, dataPointIndex }) => { + return ` +
+ ${labels[dataPointIndex]}
+ ₹${series[seriesIndex][dataPointIndex].toLocaleString()} +
+ `; + }, + }, + + legend: { show: false }, + }; + + const series = [ + { + name: "Amount", + data: amounts, + }, + ]; + + return ( +
+ +
+ ); +}; + +export default CollectionOverview; +export const TopicBarChart = ({ data }) => { + const data1 = { + totalDueAmount: 213590, + totalCollectedAmount: 5000, + totalValue: 218590, + pendingPercentage: 97.71, + collectedPercentage: 2.29, + + bucket0To30Invoices: 10, + bucket30To60Invoices: 4, + bucket60To90Invoices: 2, + bucket90PlusInvoices: 1, + + bucket0To30Amount: 2130, + bucket30To60Amount: 2003, + bucket60To90Amount: 4500, + bucket90PlusAmount: 8800, + + topClientBalance: 55300, + topClient: { + id: "4e3a6d31-c640-40f7-8d67-6c109fcdb9ea", + name: "Marco Secure Solutions Ltd.", + email: "admin@marcoaiot.com", + contactPerson: "Admin", + address: + "2nd Floor, Fullora Building, Tejas CHS, behind Kothrud Stand, Tejas Society, Dahanukar Colony, Kothrud, Pune, Maharashtra 411038", + gstNumber: null, + contactNumber: "123456789", + sprid: 5400, + }, + }; + + const borderColor = "#ddd"; + const labelColor = "#6c757d"; + + // COLORS + const config = { + colors: { + b0: "#7367F0", + b30: "#00cfe8", + b60: "#28c76f", + b90: "#ea5455", + }, + }; + + // NEW LABELS (BUCKETS) + const chartLabels = ["0–30 Days", "30–60 Days", "60–90 Days", "90+ Days"]; + + // NEW VALUES (BUCKET AMOUNT) + const chartValues = [ + data.bucket0To30Amount, + data.bucket30To60Amount, + data.bucket60To90Amount, + data.bucket90PlusAmount, + ]; + + const options = { + chart: { + height: 300, + type: "bar", + toolbar: { show: false }, + }, + + plotOptions: { + bar: { + horizontal: true, + barHeight: "40%", + distributed: true, + startingShape: "rounded", + borderRadius: 7, + }, + }, + + grid: { + strokeDashArray: 10, + borderColor, + xaxis: { lines: { show: true } }, + yaxis: { lines: { show: false } }, + padding: { top: -35, bottom: -12 }, + }, + + colors: [ + config.colors.b0, + config.colors.b30, + config.colors.b60, + config.colors.b90, + ], + + labels: chartLabels, + + fill: { opacity: 1 }, + + dataLabels: { + enabled: true, + style: { + colors: ["#fff"], + fontWeight: 400, + fontSize: "13px", + fontFamily: "Public Sans", + }, + formatter: (_, opts) => chartLabels[opts.dataPointIndex], + }, + + xaxis: { + categories: chartValues.map((x) => formatFigure(x, { type: "currency" })), + axisBorder: { show: false }, + axisTicks: { show: false }, + labels: { + style: { + colors: labelColor, + fontFamily: "Public Sans", + fontSize: "13px", + }, + formatter: (val) => `₹${Number(val).toLocaleString()}`, + }, + }, + + yaxis: { + labels: { + style: { + colors: labelColor, + fontFamily: "Public Sans", + fontSize: "13px", + }, + }, + }, + + tooltip: { + enabled: true, + custom: ({ series, seriesIndex, dataPointIndex }) => { + return ` +
+ ₹${series[seriesIndex][ + dataPointIndex + ].toLocaleString()} +
+ `; + }, + }, + + legend: { show: false }, + }; + + const series = [ + { + data: chartValues, + }, + ]; + + return ( +
+
+
+
Collection Overview
+
+
+

Due Amount

+ + {formatFigure(data.totalDueAmount, { type: "currency" })} + +

Collected Amount

+ + {formatFigure(data.totalCollectedAmount, { type: "currency" })} + +
+ + +
+ +
+
+
+ {/*

Top Client

+ {data.topClient.name} */} +
+ + {/* 0–30 Days */} +
+
+ {formatFigure(data.bucket0To30Amount, { type: "currency" })} +
+

0–30 Days

+
+ + {/* 30–60 Days */} +
+
+ {formatFigure(data.bucket30To60Amount, { type: "currency" })} +
+

30–60 Days

+
+ + {/* 60–90 Days */} +
+
+ {formatFigure(data.bucket60To90Amount, { type: "currency" })} +
+

60–90 Days

+
+ + {/* 90+ Days */} +
+
+ {formatFigure(data.bucket90PlusAmount, { type: "currency" })} +
+

90+ Days

+
+
+
+ ); +}; diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index e918629d..a400d7a8 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -1,10 +1,11 @@ import React from "react"; import { useSelector } from "react-redux"; import { - useDashboardProjectsCardData, - useDashboardTeamsCardData, - useDashboardTasksCardData, - useAttendanceOverviewData + useDashboardProjectsCardData, + useDashboardTeamsCardData, + useDashboardTasksCardData, + useAttendanceOverviewData, + useGetCollectionOverview, } from "../../hooks/useDashboard_Data"; import Projects from "./Projects"; @@ -19,77 +20,171 @@ import ExpenseByProject from "./ExpenseByProject"; import ProjectStatistics from "../Project/ProjectStatistics"; import ServiceJobs from "./ServiceJobs"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; -import { REGULARIZE_ATTENDANCE, SELF_ATTENDANCE, TEAM_ATTENDANCE } from "../../utils/constants"; +import { + REGULARIZE_ATTENDANCE, + SELF_ATTENDANCE, + TEAM_ATTENDANCE, +} from "../../utils/constants"; +import CollectionOverview, { TopicBarChart } from "./CollectionOverview"; const Dashboard = () => { + // Get the selected project ID from Redux store + const projectId = useSelector((store) => store.localVariables.projectId); + const isAllProjectsSelected = projectId === null; + const canRegularize = useHasUserPermission(REGULARIZE_ATTENDANCE); + const canTeamAttendance = useHasUserPermission(TEAM_ATTENDANCE); + const canSelfAttendance = useHasUserPermission(SELF_ATTENDANCE); - // Get the selected project ID from Redux store - const projectId = useSelector((store) => store.localVariables.projectId); - const isAllProjectsSelected = projectId === null; - const canRegularize = useHasUserPermission(REGULARIZE_ATTENDANCE); - const canTeamAttendance = useHasUserPermission(TEAM_ATTENDANCE); - const canSelfAttendance = useHasUserPermission(SELF_ATTENDANCE); + const { data } = useGetCollectionOverview(); + console.log("data-->", data); + return ( +
+
+ {isAllProjectsSelected && ( +
+ +
+ )} - - return ( -
-
- {isAllProjectsSelected && ( -
- -
- )} - -
- -
- -
- -
-
-
- -
-
- -
-
- -
-
-
- -
- - {isAllProjectsSelected && ( -
- -
- )} -
- -
- {!isAllProjectsSelected && (canRegularize || canTeamAttendance || canSelfAttendance) && ( -
- -
- )} - - {!isAllProjectsSelected && ( -
-
- -
-
- )} - - {/*
- -
*/} -
+
+
- ); + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + {isAllProjectsSelected && ( +
+ +
+ )} +
+ +
+ {!isAllProjectsSelected && + (canRegularize || canTeamAttendance || canSelfAttendance) && ( +
+ +
+ )} + + {!isAllProjectsSelected && ( +
+
+ +
+
+ )} +
+ {data &&
} + +
+ + +
+ +
+ ); }; -export default Dashboard; \ No newline at end of file +export default Dashboard; + +//
+//
+//
+//
Topic you are interested in
+// +//
+//
+//
+//
+//
+//
+//
+//
+// +//
+//

UI Design

+//
35%
+//
+//
+//
+// +//
+//

Music

+//
14%
+//
+//
+//
+// +//
+//

React

+//
10%
+//
+//
+//
+ +//
+//
+// +//
+//

UX Design

+//
20%
+//
+//
+//
+// +//
+//

Animation

+//
12%
+//
+//
+//
+// +//
+//

SEO

+//
9%
+//
+//
+//
+//
+//
+//
+//
diff --git a/src/hooks/useDashboard_Data.jsx b/src/hooks/useDashboard_Data.jsx index 1a9d724a..8b3863df 100644 --- a/src/hooks/useDashboard_Data.jsx +++ b/src/hooks/useDashboard_Data.jsx @@ -192,3 +192,13 @@ export const useExpenseDataByProject = (projectId, categoryId, months) => { }, }); }; + +export const useGetCollectionOverview = (projectId) => { + return useQuery({ + queryKey: ["collection_overview", projectId], + queryFn: async () => { + const resp = await GlobalRepository.getCollectionOverview(projectId); + return resp.data; + }, + }); +}; diff --git a/src/repositories/GlobalRepository.jsx b/src/repositories/GlobalRepository.jsx index 2516020a..e95a109a 100644 --- a/src/repositories/GlobalRepository.jsx +++ b/src/repositories/GlobalRepository.jsx @@ -82,9 +82,9 @@ 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}`), + getCollectionOverview:(projectId) =>api.get(`/api/Dashboard/collection-overview`) };