diff --git a/src/components/Charts/Circle.jsx b/src/components/Charts/Circle.jsx
new file mode 100644
index 00000000..6b0669bf
--- /dev/null
+++ b/src/components/Charts/Circle.jsx
@@ -0,0 +1,80 @@
+import React from "react";
+import ReactApexChart from "react-apexcharts";
+
+const ApexChart = ({ completed = 0, planned = 1 }) => {
+const percentage = planned > 0 ? Math.round((completed / planned) * 100) : 0;
+
+const options = {
+chart: {
+height: 200,
+type: "radialBar",
+toolbar: { show: false },
+},
+plotOptions: {
+radialBar: {
+startAngle: -135,
+endAngle: 225,
+hollow: {
+margin: 0,
+size: "60%",
+background: "#fff",
+dropShadow: {
+enabled: true,
+top: 2,
+left: 0,
+blur: 3,
+opacity: 0.45,
+},
+},
+track: {
+background: "#f5f5f5",
+strokeWidth: "67%",
+dropShadow: { enabled: false },
+},
+dataLabels: {
+show: true,
+name: {
+offsetY: -10,
+color: "#888",
+fontSize: "14px",
+},
+value: {
+formatter: (val) => `${val}%`,
+color: "#111",
+fontSize: "24px",
+show: true,
+},
+},
+},
+},
+fill: {
+type: "gradient",
+gradient: {
+shade: "dark",
+type: "horizontal",
+shadeIntensity: 0.5,
+gradientToColors: ["#ABE5A1"],
+opacityFrom: 1,
+opacityTo: 1,
+stops: [0, 100],
+},
+},
+stroke: {
+lineCap: "round",
+},
+labels: ["Progress"],
+};
+
+return (
+
+
+
+);
+};
+
+export default ApexChart;
\ No newline at end of file
diff --git a/src/components/Dashboard/Attendance.jsx b/src/components/Dashboard/Attendance.jsx
new file mode 100644
index 00000000..b7e5a3b2
--- /dev/null
+++ b/src/components/Dashboard/Attendance.jsx
@@ -0,0 +1,188 @@
+import React, { useState, useEffect } from "react";
+import LineChart from "../Charts/LineChart";
+import { useProjects } from "../../hooks/useProjects";
+import { useDashboard_AttendanceData } from "../../hooks/useDashboard_Data";
+import ApexChart from "../Charts/Circle";
+
+const LOCAL_STORAGE_PROJECT_KEY = "selectedAttendanceProjectId";
+
+const Attendance = () => {
+ const { projects } = useProjects();
+ const today = new Date().toISOString().split("T")[0]; // Format: YYYY-MM-DD
+ const [selectedDate, setSelectedDate] = useState(today);
+ const storedProjectId = localStorage.getItem(LOCAL_STORAGE_PROJECT_KEY);
+ const initialProjectId = storedProjectId || "all";
+ const [selectedProjectId, setSelectedProjectId] = useState(initialProjectId);
+ const [displayedProjectName, setDisplayedProjectName] = useState("Select Project");
+ const [activeTab, setActiveTab] = useState("all");
+
+ const { dashboard_Attendancedata: AttendanceData, isLoading, error: isError } =
+ useDashboard_AttendanceData(selectedDate, selectedProjectId);
+
+ useEffect(() => {
+ if (selectedProjectId === "all") {
+ setDisplayedProjectName("All Projects");
+ } else if (projects) {
+ const foundProject = projects.find((p) => p.id === selectedProjectId);
+ setDisplayedProjectName(foundProject ? foundProject.name : "Select Project");
+ } else {
+ setDisplayedProjectName("Select Project");
+ }
+
+ localStorage.setItem(LOCAL_STORAGE_PROJECT_KEY, selectedProjectId);
+ }, [selectedProjectId, projects]);
+
+ const handleProjectSelect = (projectId) => {
+ setSelectedProjectId(projectId);
+ };
+
+ const handleDateChange = (e) => {
+ setSelectedDate(e.target.value);
+ };
+
+ return (
+
+
+
+
+
Attendance
+
Attendance Progress Chart
+
+
+
+
+
+ -
+
+
+ {projects?.map((project) => (
+ -
+
+
+ ))}
+
+
+
+
+
+ {/* ✅ Date Picker Aligned Left with Padding */}
+
+
+
+ {/* Tabs */}
+
+ -
+
+
+ -
+
+
+
+
+
+ {activeTab === "Summary" && (
+
+
+ {isLoading ? (
+
Loading Attendance data...
+ ) : isError ? (
+
No data available.
+ ) : (
+ AttendanceData && (
+ <>
+
+ Attendance
+
+
+ {AttendanceData.checkedInEmployee?.toLocaleString()}/
+ {AttendanceData.assignedEmployee?.toLocaleString()}
+
+
Checked-In / Assigned
+
+ >
+ )
+ )}
+
+
+
+ )}
+
+ {activeTab === "Details" && (
+
+
+
+
+ Name |
+ Checkin |
+ Checkout |
+
+
+
+ {AttendanceData?.attendanceTable && AttendanceData.attendanceTable.length > 0 ? (
+ AttendanceData.attendanceTable.map((record, index) => (
+
+ {record.firstName} {record.lastName} |
+ {new Date(record.inTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} |
+ {new Date(record.outTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} |
+
+
+ ))
+ ) : (
+
+ No attendance data available |
+
+ )}
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default Attendance;
diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx
index 1e4cceaf..6bbdd11f 100644
--- a/src/components/Dashboard/Dashboard.jsx
+++ b/src/components/Dashboard/Dashboard.jsx
@@ -9,6 +9,7 @@ import Teams from "./Teams";
import TasksCard from "./Tasks";
import ProjectCompletionChart from "./ProjectCompletionChart";
import ProjectProgressChart from "./ProjectProgressChart";
+import Attendance from "./Attendance";
const Dashboard = () => {
const { projectsCardData } = useDashboardProjectsCardData();
@@ -42,6 +43,10 @@ const Dashboard = () => {
+
+
);
diff --git a/src/hooks/useDashboard_Data.jsx b/src/hooks/useDashboard_Data.jsx
index 5cf03f91..a46aa0c8 100644
--- a/src/hooks/useDashboard_Data.jsx
+++ b/src/hooks/useDashboard_Data.jsx
@@ -37,6 +37,37 @@ export const useDashboard_Data = ({ days, FromDate, projectId }) => {
return { dashboard_data, loading: isLineChartLoading, error };
};
+
+export const useDashboard_AttendanceData = (date, projectId) => {
+ const [dashboard_Attendancedata, setDashboard_AttendanceData] = useState([]);
+ const [isLineChartLoading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ const fetchData = async () => {
+ setLoading(true);
+ setError("");
+
+ try {
+ const response = await GlobalRepository.getDashboardAttendanceData(date,projectId); // date in 2nd param
+ setDashboard_AttendanceData(response.data);
+ } catch (err) {
+ setError("Failed to fetch dashboard data.");
+ console.error(err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (date && projectId !== null) {
+ fetchData();
+ }
+ }, [date, projectId]);
+
+ return { dashboard_Attendancedata, isLineChartLoading: isLineChartLoading, error };
+};
+
+
// 🔹 Dashboard Projects Card Data Hook
export const useDashboardProjectsCardData = () => {
const [projectsCardData, setProjectsData] = useState([]);
diff --git a/src/repositories/GlobalRepository.jsx b/src/repositories/GlobalRepository.jsx
index 31468b66..eda5312e 100644
--- a/src/repositories/GlobalRepository.jsx
+++ b/src/repositories/GlobalRepository.jsx
@@ -18,6 +18,11 @@ const GlobalRepository = {
return api.get(`/api/Dashboard/Progression?${params.toString()}`);
},
+
+ getDashboardAttendanceData: ( date,projectId ) => {
+
+ return api.get(`/api/Dashboard/project-attendance/${projectId}?date=${date}`);
+},
getDashboardProjectsCardData: () => {
return api.get(`/api/Dashboard/projects`);
},