diff --git a/src/components/Dashboard/CollectionOverview.jsx b/src/components/Dashboard/CollectionOverview.jsx
new file mode 100644
index 00000000..34f0504b
--- /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, isLoading }) => {
+ 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,isLoading }) => {
+ 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 (
+
+
+
+
+
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/CollectionOverviewSkeleton.jsx b/src/components/Dashboard/CollectionOverviewSkeleton.jsx
new file mode 100644
index 00000000..b4e85709
--- /dev/null
+++ b/src/components/Dashboard/CollectionOverviewSkeleton.jsx
@@ -0,0 +1,40 @@
+
+const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => (
+
+);
+export const CollectionOverviewSkeleton = () => {
+ return (
+
+
+ {/* LEFT SIDE */}
+
+
+
+ {/* Header */}
+
+
+
+
+ {/* Due & Collected summary */}
+
+
+
+
+
+
+
+ {/* Chart Skeleton */}
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx
index 2de8d444..0f856000 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,78 +20,179 @@ 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";
+import { CollectionOverviewSkeleton } from "./CollectionOverviewSkeleton";
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, isLoading, isError } = useGetCollectionOverview();
+ console.log("data-->", data);
+ return (
+
+
+ {isAllProjectsSelected && (
+
+ )}
-
- return (
-
-
- {isAllProjectsSelected && (
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {isAllProjectsSelected && (
-
- )}
-
- {!isAllProjectsSelected && (canRegularize || canTeamAttendance || canSelfAttendance) && (
-
- )}
-
- {!isAllProjectsSelected && (
-
- )}
- {isAllProjectsSelected && (
-
-
-
- )}
-
+
+
- );
+
+
+
+
+
+
+
+
+
+
+
+ {isAllProjectsSelected && (
+
+ )}
+
+ {!isAllProjectsSelected &&
+ (canRegularize || canTeamAttendance || canSelfAttendance) && (
+
+ )}
+
+ {!isAllProjectsSelected && (
+
+ )}
+ {isAllProjectsSelected && (
+
+
+
+ )}
+
+ {isLoading ? (
+
+ ) : (
+ data && (
+
+
+
+ )
+ )}
+
+
+
+ );
};
-export default Dashboard;
\ No newline at end of file
+export default Dashboard;
+
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
UI Design
+//
35%
+//
+//
+//
+//
+//
+
+//
+//
+//
+//
+//
UX Design
+//
20%
+//
+//
+//
+//
+//
+//
Animation
+//
12%
+//
+//
+//
+//
+//
+//
+//
+//
diff --git a/src/components/Expenses/PreviewDocument.jsx b/src/components/Expenses/PreviewDocument.jsx
index 5d5b1b59..e819cd44 100644
--- a/src/components/Expenses/PreviewDocument.jsx
+++ b/src/components/Expenses/PreviewDocument.jsx
@@ -1,3 +1,4 @@
+import { error } from "pdf-lib";
import { useState, useRef, useEffect } from "react";
const PreviewDocument = ({ files = [] }) => {
@@ -15,9 +16,11 @@ const PreviewDocument = ({ files = [] }) => {
const MIN_ZOOM = 0.4;
const MAX_ZOOM = 3;
- const currentImage = images[index];
+ const currentFile = images[index];
+ const fileUrl = currentFile?.preSignedUrl;
+
+ const isPDF = fileUrl?.toLowerCase().endsWith(".pdf");
- // Reset on image change
useEffect(() => {
setRotation(0);
setScale(1);
@@ -25,8 +28,8 @@ const PreviewDocument = ({ files = [] }) => {
setLoading(true);
}, [index]);
- const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, MAX_ZOOM));
- const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, MIN_ZOOM));
+ const zoomIn = () => !isPDF && setScale((prev) => Math.min(prev + 0.2, MAX_ZOOM));
+ const zoomOut = () => !isPDF && setScale((prev) => Math.max(prev - 0.2, MIN_ZOOM));
const resetAll = () => {
setRotation(0);
@@ -42,24 +45,8 @@ const PreviewDocument = ({ files = [] }) => {
if (index > 0) setIndex((i) => i - 1);
};
- const handleWheel = (e) => {
- e.preventDefault();
-
- if (e.ctrlKey) {
- const delta = e.deltaY > 0 ? -0.1 : 0.1;
- setScale((prev) => {
- let next = prev + delta;
- if (next < MIN_ZOOM) next = MIN_ZOOM;
- if (next > MAX_ZOOM) next = MAX_ZOOM;
- return next;
- });
- } else {
- if (e.deltaY > 0) nextImage();
- else prevImage();
- }
- };
-
const handleMouseDown = (e) => {
+ if (isPDF) return;
setDragging(true);
startPos.current = {
x: e.clientX - position.x,
@@ -68,7 +55,7 @@ const PreviewDocument = ({ files = [] }) => {
};
const handleMouseMove = (e) => {
- if (!dragging) return;
+ if (!dragging || isPDF) return;
setPosition({
x: e.clientX - startPos.current.x,
@@ -78,39 +65,41 @@ const PreviewDocument = ({ files = [] }) => {
const handleMouseUp = () => setDragging(false);
- const handleDoubleClick = () => resetAll();
+ const handleDoubleClick = () => !isPDF && resetAll();
return (
<>
- {/* Top Controls */}
+ {/* Controls */}
- {/* Left */}
- setRotation((prev) => prev + 90)}
- title="Rotate"
- />
-
-
-
+ {!isPDF && (
+ <>
+ setRotation((prev) => prev + 90)}
+ title="Rotate"
+ />
+
+
+
+ >
+ )}
{
className="position-relative d-flex justify-content-center align-items-center bg-light-secondary overflow-hidden"
style={{
minHeight: "70vh",
+
userSelect: "none",
borderRadius: "10px",
}}
>
{loading &&
Loading...
}
-

setLoading(false)}
- />
+ {/* PDF VIEW */}
+ {isPDF ? (
+
+
+
- Scroll = change image | Double click = reset
+ Scroll = change file | Double click = reset (images only)
{
- if (isImage) {
setDocumentView({
IsOpen: true,
Images: data?.documents,
});
- }
}}
>
{
>
)}
{data &&
- data?.data.map((section) => (
+ data?.data?.map((section) => (
diff --git a/src/components/Project/Team/TeamEmployeeList.jsx b/src/components/Project/Team/TeamEmployeeList.jsx
index 64d5f605..491c0448 100644
--- a/src/components/Project/Team/TeamEmployeeList.jsx
+++ b/src/components/Project/Team/TeamEmployeeList.jsx
@@ -10,11 +10,13 @@ import {
import useMaster, { useServices } from "../../../hooks/masterHook/useMaster";
import showToast from "../../../services/toastService";
import { useOrganizationEmployees } from "../../../hooks/useOrganization";
+import { useDispatch } from "react-redux";
+import { changeMaster } from "../../../slices/localVariablesSlice";
const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
const selectedProject = useSelectedProject();
const debounceSearchTerm = useDebounce(searchTerm, 500);
-
+ const dispatch = useDispatch();
const {
data: employeesData = [],
isLoading,
@@ -45,6 +47,7 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
});
useEffect(() => {
+ dispatch(changeMaster("Job Role"));
if (employeesData?.data?.length > 0) {
const available = employeesData.data.filter((emp) => {
const projEmp = projectEmployees.find((pe) => pe.employeeId === emp.id);
@@ -119,7 +122,7 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
status: true,
}));
- handleAssignEmployee({ payload,actionType:"assign"} );
+ handleAssignEmployee({ payload, actionType: "assign" });
setEmployees((prev) =>
prev.map((emp) => ({
@@ -132,26 +135,26 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
);
};
-if (isLoading) {
- return ( ) ;
-}
+ if (isLoading) {
+ return ();
+ }
-if (isError) {
- return (
-
- {error?.status === 400 ? (
-
Enter employee you want to find.
- ) : (
-
Something went wrong. Please try again later.
- )}
-
+ if (isError) {
+ return (
+
+ {error?.status === 400 ? (
+
Enter employee you want to find.
+ ) : (
+
Something went wrong. Please try again later.
+ )}
+
- );
-}
+ );
+ }
-if (employees.length === 0) {
- return(No available employees to assign.
) ;
-}
+ if (employees.length === 0) {
+ return (No available employees to assign.
);
+ }
return (
@@ -183,9 +186,8 @@ if (employees.length === 0) {
onChange={(e) =>
handleSelectChange(index, "serviceId", e.target.value)
}
- className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
- emp.errors.serviceId ? "is-invalid" : ""
- }`}
+ className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${emp.errors.serviceId ? "is-invalid" : ""
+ }`}
>
{services?.map((s) => (
@@ -205,9 +207,8 @@ if (employees.length === 0) {
onChange={(e) =>
handleSelectChange(index, "jobRole", e.target.value)
}
- className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
- emp.errors.jobRole ? "is-invalid" : ""
- }`}
+ className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${emp.errors.jobRole ? "is-invalid" : ""
+ }`}
>
{jobRoles?.map((r) => (
diff --git a/src/components/ServiceProject/ServiceProjectJob/JobStatusLog.jsx b/src/components/ServiceProject/ServiceProjectJob/JobStatusLog.jsx
index bf13b7e5..b2fce0d7 100644
--- a/src/components/ServiceProject/ServiceProjectJob/JobStatusLog.jsx
+++ b/src/components/ServiceProject/ServiceProjectJob/JobStatusLog.jsx
@@ -1,56 +1,38 @@
-import React from "react";
-import Avatar from "../../common/Avatar";
-import { formatUTCToLocalTime } from "../../../utils/dateUtils";
+import React, { useMemo } from "react";
+import { getColorNameFromHex } from "../../../utils/appUtils";
+import Timeline from "../../common/TimeLine";
const JobStatusLog = ({ data }) => {
+ // Prepare timeline items
+ const timelineData = useMemo(() => {
+ if (!data) return [];
+ return data
+ .sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt))
+ .map((log) => ({
+ id: log.id,
+ title: log.nextStatus?.displayName || log.status?.displayName || "Status Updated",
+ description: log.nextStatus?.description || "",
+ timeAgo: log.updatedAt,
+ color: getColorNameFromHex(log.nextStatus?.color) || "primary",
+ userComment: log.comment,
+ users: log.updatedBy
+ ? [
+ {
+ firstName: log.updatedBy.firstName || "",
+ lastName: log.updatedBy.lastName || "",
+ role: log.updatedBy.jobRoleName || "",
+ avatar: log.updatedBy.photo || null,
+ },
+ ]
+ : [],
+ }));
+ }, [data]);
+
return (
-
-
- {data?.map((item) => (
-
-
-
-
- {item.nextStatus?.displayName ??
- item.status?.displayName ??
- "Status"}
-
-
-
-
- {formatUTCToLocalTime(item?.updatedAt,true)}
-
-
-
-
-
-
-
- {item.updatedBy?.firstName} {item.updatedBy?.lastName}
-
-
- {/* {formatUTCToLocalTime(item?.createdAt, true)} */}
-
-
-
- {item?.updatedBy?.jobRoleName}
-
-
-
-
-
- ))}
-
+
+
);
diff --git a/src/components/ServiceProject/ServiceProjectJob/ManageJobTicket.jsx b/src/components/ServiceProject/ServiceProjectJob/ManageJobTicket.jsx
index d2647649..b05e85fe 100644
--- a/src/components/ServiceProject/ServiceProjectJob/ManageJobTicket.jsx
+++ b/src/components/ServiceProject/ServiceProjectJob/ManageJobTicket.jsx
@@ -23,20 +23,21 @@ const ManageJobTicket = ({ Job }) => {
);
const drawerRef = useRef();
const tabsData = [
+ {
+ id: "history",
+ title: "History",
+ icon: "bx bx-time me-2",
+ active: true,
+ content:
,
+ },
{
id: "comment",
title: "Comments",
icon: "bx bx-comment me-2",
- active: true,
+ active: false,
content:
,
},
- {
- id: "history",
- title: "History",
- icon: "bx bx-time me-2",
- active: false,
- content:
,
- },
+
];
if (isLoading) return
;
diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx
index a87ee7ec..83d7d879 100644
--- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx
+++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx
@@ -66,7 +66,7 @@ const ServiceProjectTeamList = () => {
{data?.length > 0 ? (
data.map((row) => (
-
+
{servceProjectColmen.map((col) => (
| {col.getValue(row)} |
))}
diff --git a/src/hooks/useDashboard_Data.jsx b/src/hooks/useDashboard_Data.jsx
index 68b914d7..6f1bae69 100644
--- a/src/hooks/useDashboard_Data.jsx
+++ b/src/hooks/useDashboard_Data.jsx
@@ -201,3 +201,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/pages/Activities/AttendancePage.jsx b/src/pages/Activities/AttendancePage.jsx
index 5b07002e..0c8791f5 100644
--- a/src/pages/Activities/AttendancePage.jsx
+++ b/src/pages/Activities/AttendancePage.jsx
@@ -59,7 +59,7 @@ const AttendancePage = () => {
if (selectedProject == null) {
dispatch(setProjectId(projectNames[0]?.id));
}
- dispatch(setOrganization(appliedFilters?.selectedOrganization))
+ dispatch(setOrganization(appliedFilters?.selectedOrganization));
}, [appliedFilters?.selectedOrganization]);
const getRole = (roleId) => {
@@ -94,6 +94,49 @@ const AttendancePage = () => {
setSearchTerm(""); // Reset search term when tab changes
};
+ // --- START: Tab Configuration Array
+ const tabsData = [
+ {
+ id: "all",
+ title: "Today's",
+ content: (
+
+ ),
+ },
+ {
+ id: "logs",
+ title: "Logs",
+ content: (
+
+ ),
+ },
+ // Conditionally include Regularization tab based on permission
+ ...(DoRegularized
+ ? [
+ {
+ id: "regularization",
+ title: "Regularization",
+ content: (
+
+ ),
+ },
+ ]
+ : []),
+ ];
+ // --- END: Tab Configuration Array
+
return (
<>
{isCreateModalOpen && modelConfig && (
@@ -114,9 +157,7 @@ const AttendancePage = () => {
{modelConfig?.action === 6 && (
)}
- {modelConfig?.action === 7 && (
-
- )}
+ {modelConfig?.action === 7 && }
)}
@@ -129,57 +170,39 @@ const AttendancePage = () => {
>
- {/* Tabs */}
-
+ {/* Tabs header with search and filter */}
+
- {/* Tabs */}
-
+ {/* Tabs Buttons */}
+
- -
-
-
- -
-
-
-
- -
-
-
+
+
+ ))}
{/* Search + Organization filter */}
-
+ {/* Tab Content */}
{selectedProject ? (
<>
- {activeTab === "all" && (
-
-
+ {tabsData.map((tab) => (
+
+ {activeTab === tab.id && tab.content}
- )}
- {activeTab === "logs" && (
-
- )}
- {activeTab === "regularization" && DoRegularized && (
-
-
-
- )}
+ ))}
>
) : (
@@ -259,4 +267,4 @@ const AttendancePage = () => {
);
};
-export default AttendancePage;
+export default AttendancePage;
\ No newline at end of file
diff --git a/src/repositories/GlobalRepository.jsx b/src/repositories/GlobalRepository.jsx
index 7bee2d1c..e4258f6e 100644
--- a/src/repositories/GlobalRepository.jsx
+++ b/src/repositories/GlobalRepository.jsx
@@ -84,9 +84,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`)
};
diff --git a/src/utils/appUtils.js b/src/utils/appUtils.js
index 0a3fc50c..7b983d2b 100644
--- a/src/utils/appUtils.js
+++ b/src/utils/appUtils.js
@@ -43,7 +43,7 @@ export const getJobStatusBadge = (statusId) => {
"32d76a02-8f44-4aa0-9b66-c3716c45a918": "bg-label-primary", // New
"cfa1886d-055f-4ded-84c6-42a2a8a14a66": "bg-label-info", // Assigned
"5a6873a5-fed7-4745-a52f-8f61bf3bd72d": "bg-label-warning", // In Progress
- "aab71020-2fb8-44d9-9430-c9a7e9bf33b0": "bg-label-label-dark", // Review
+ "aab71020-2fb8-44d9-9430-c9a7e9bf33b0": "bg-label-dark", // Review
"ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7": "bg-label-success", // Done
"3ddeefb5-ae3c-4e10-a922-35e0a452bb69": "bg-label-secondary", // Closed
"75a0c8b8-9c6a-41af-80bf-b35bab722eb2": "bg-label-danger", // On Hold