From dbc9bbffa2afa9a3e74137163d8ccd89fd1f4b50 Mon Sep 17 00:00:00 2001 From: "kartik.sharma" Date: Sat, 31 May 2025 11:39:15 +0530 Subject: [PATCH] Create a new Weidget in Dashboard name "Attendance" and Adding a features to show chart and Table on it. --- src/components/Charts/Circle.jsx | 80 ++++++++++ src/components/Dashboard/Attendance.jsx | 188 ++++++++++++++++++++++++ src/components/Dashboard/Dashboard.jsx | 5 + src/hooks/useDashboard_Data.jsx | 31 ++++ src/repositories/GlobalRepository.jsx | 5 + 5 files changed, 309 insertions(+) create mode 100644 src/components/Charts/Circle.jsx create mode 100644 src/components/Dashboard/Attendance.jsx 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" && ( +
+ + + + + + + + + + {AttendanceData?.attendanceTable && AttendanceData.attendanceTable.length > 0 ? ( + AttendanceData.attendanceTable.map((record, index) => ( + + + + + + + )) + ) : ( + + + + )} + +
NameCheckinCheckout
{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`); },