From 627d4f898e28db3ed933670c09d01757d01a923d Mon Sep 17 00:00:00 2001 From: "kartik.sharma" Date: Wed, 28 May 2025 13:06:03 +0530 Subject: [PATCH 1/4] Adding new Weidget in Dashboadr name is "Pending Attendance". --- src/components/Dashboard/Dashboard.jsx | 15 +++++++-- .../Dashboard/PendingAttendance.jsx | 32 +++++++++++++++++++ src/hooks/useDashboard_Data.jsx | 28 ++++++++++++++++ src/repositories/GlobalRepository.jsx | 3 ++ 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/components/Dashboard/PendingAttendance.jsx diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index 1e4cceaf..cb8e7e74 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -3,36 +3,45 @@ import { useDashboardProjectsCardData, useDashboardTeamsCardData, useDashboardTasksCardData, + useDashboardPendingAttendenceData, } from "../../hooks/useDashboard_Data"; import Projects from "./Projects"; import Teams from "./Teams"; import TasksCard from "./Tasks"; import ProjectCompletionChart from "./ProjectCompletionChart"; import ProjectProgressChart from "./ProjectProgressChart"; +import PendingAttendance from "./PendingAttendance"; const Dashboard = () => { const { projectsCardData } = useDashboardProjectsCardData(); const { teamsCardData } = useDashboardTeamsCardData(); const { tasksCardData } = useDashboardTasksCardData(); + const {PendingAttendenceData} = useDashboardPendingAttendenceData(); + return (
{/* Projects Card */} -
+
{/* Teams Card */} -
+
{/* Tasks Card */} -
+
+ {/* Pending Attendance */} +
+ +
+ {/* Bar Chart (Project Completion) */}
diff --git a/src/components/Dashboard/PendingAttendance.jsx b/src/components/Dashboard/PendingAttendance.jsx new file mode 100644 index 00000000..f3f1ec04 --- /dev/null +++ b/src/components/Dashboard/PendingAttendance.jsx @@ -0,0 +1,32 @@ +import React from "react"; +import { useDashboardPendingAttendenceData } from "../../hooks/useDashboard_Data"; + +const PendingAttendance = () => { + const { PendingAttendenceData } = useDashboardPendingAttendenceData(); + + return ( +
+
+
+ Pending Attendence +
+
+
+
+

+ {PendingAttendenceData.pendingCheckOut?.toLocaleString()} +

+ Checkout +
+
+

+ {PendingAttendenceData.pendingRegularization?.toLocaleString()} +

+ Regularization +
+
+
+ ); +}; + +export default PendingAttendance; \ No newline at end of file diff --git a/src/hooks/useDashboard_Data.jsx b/src/hooks/useDashboard_Data.jsx index 5cf03f91..e3e86dac 100644 --- a/src/hooks/useDashboard_Data.jsx +++ b/src/hooks/useDashboard_Data.jsx @@ -120,3 +120,31 @@ export const useDashboardTasksCardData = () => { return { tasksCardData, loading, error }; }; + +export const useDashboardPendingAttendenceData = () => { + const [PendingAttendenceData, setPendingAttendence] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + useEffect(() => { + const fetchPendingAttendence = async () => { + setLoading(true); + setError(""); + + try { + const response = await GlobalRepository.getDashboardPendingAttendence(); + setPendingAttendence(response.data); + } catch (err) { + setError("Failed to fetch Pending Attendence card data."); + console.error(err); + } finally { + setLoading(false); + } + }; + + fetchPendingAttendence(); + }, []); + + return { PendingAttendenceData, loading, error }; +}; + diff --git a/src/repositories/GlobalRepository.jsx b/src/repositories/GlobalRepository.jsx index 31468b66..5ee67f01 100644 --- a/src/repositories/GlobalRepository.jsx +++ b/src/repositories/GlobalRepository.jsx @@ -27,6 +27,9 @@ const GlobalRepository = { getDashboardTasksCardData: () => { return api.get(`/api/Dashboard/tasks`); }, +getDashboardPendingAttendence: () => { + return api.get(`/api/dashboard/pending-attendance`); + }, }; From d16675bf71667ed13eb5bb192ba8f3effac2410f Mon Sep 17 00:00:00 2001 From: "kartik.sharma" Date: Fri, 30 May 2025 16:19:20 +0530 Subject: [PATCH 2/4] Adding New Weidge in Dashboard Name is "Activity". --- src/components/Charts/Circlechart.jsx | 85 +++++++++++ src/components/Dashboard/Activity.jsx | 194 +++++++++++++++++++++++++ src/components/Dashboard/Dashboard.jsx | 8 +- src/components/common/DatePicker.jsx | 4 +- src/hooks/useDashboard_Data.jsx | 58 ++++++++ src/repositories/GlobalRepository.jsx | 17 +++ 6 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 src/components/Charts/Circlechart.jsx create mode 100644 src/components/Dashboard/Activity.jsx diff --git a/src/components/Charts/Circlechart.jsx b/src/components/Charts/Circlechart.jsx new file mode 100644 index 00000000..ab1c1116 --- /dev/null +++ b/src/components/Charts/Circlechart.jsx @@ -0,0 +1,85 @@ +import React from "react"; +import ReactApexChart from "react-apexcharts"; + +const ApexChart = () => { + const [state] = React.useState({ + series: [75], // Replace this with dynamic value if needed + options: { + chart: { + height: 200, // Smaller height + type: "radialBar", + toolbar: { + show: false, // Hide toolbar + }, + }, + plotOptions: { + radialBar: { + startAngle: -135, + endAngle: 225, + hollow: { + margin: 0, + size: "60%", // Smaller inner circle + 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) => parseInt(val), + color: "#111", + fontSize: "24px", // Smaller number + 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: ["Percent"], + }, + }); + + return ( +
+ +
+ ); +}; + +export default ApexChart; diff --git a/src/components/Dashboard/Activity.jsx b/src/components/Dashboard/Activity.jsx new file mode 100644 index 00000000..a1f9c5fc --- /dev/null +++ b/src/components/Dashboard/Activity.jsx @@ -0,0 +1,194 @@ +import React, { useState, useEffect } from "react"; +import LineChart from "../Charts/LineChart"; +import { useProjects } from "../../hooks/useProjects"; +import { useDashboard_ActivityData } from "../../hooks/useDashboard_Data"; +import ApexChart from "../Charts/Circlechart"; + +const LOCAL_STORAGE_PROJECT_KEY = "selectedActivityProjectId"; + +const Activity = () => { + 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_Activitydata: ActivityData, isLoading, error: isError } = + useDashboard_ActivityData(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 ( +
+
+
+
+
Activity
+

Activity Progress Chart

+
+ +
+ +
    +
  • + +
  • + {projects?.map((project) => ( +
  • + +
  • + ))} +
+
+
+
+ + {/* ✅ Date Picker Aligned Left with Padding */} +
+
+ +
+
+ + {/* Tabs */} +
    +
  • + +
  • +
  • + +
  • +
+ +
+ {activeTab === "all" && ( +
+
+ {isLoading ? ( +

Loading activity data...

+ ) : isError ? ( +

No data available.

+ ) : ( + ActivityData && ( + <> +
+ Allocated Task +
+

+ {ActivityData.totalCompletedWork?.toLocaleString()}/ + {ActivityData.totalPlannedWork?.toLocaleString()} +

+ Completed / Assigned +
+ +
+ + ) + )} +
+ +
+ {!isLoading && !isError && ActivityData && ( + <> +
+ Activities +
+

+ {ActivityData.totalCompletedWork?.toLocaleString()}/ + {ActivityData.totalPlannedWork?.toLocaleString()} +

+ Pending / Assigned +
+ +
+ + )} +
+
+ )} + + {activeTab === "logs" && ( +
+ + + + + + + + + {[{ + activity: "Code Review / Remote", + assignedToday: 3, + completed: 2 + }].map((log, index) => ( + + + + + ))} + +
Activity / LocationAssigned / Completed
{log.activity}{log.assignedToday} / {log.completed}
+
+ )} +
+
+ ); +}; + +export default Activity; diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index 1e4cceaf..93e9f18e 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -9,12 +9,14 @@ import Teams from "./Teams"; import TasksCard from "./Tasks"; import ProjectCompletionChart from "./ProjectCompletionChart"; import ProjectProgressChart from "./ProjectProgressChart"; +import Activity from "./Activity"; const Dashboard = () => { const { projectsCardData } = useDashboardProjectsCardData(); const { teamsCardData } = useDashboardTeamsCardData(); const { tasksCardData } = useDashboardTasksCardData(); - + // const { ActivityData } = useDashboardActivityData(); + return (
@@ -42,6 +44,10 @@ const Dashboard = () => {
+ +
+ +
); diff --git a/src/components/common/DatePicker.jsx b/src/components/common/DatePicker.jsx index 7003ab78..728b14fb 100644 --- a/src/components/common/DatePicker.jsx +++ b/src/components/common/DatePicker.jsx @@ -23,9 +23,9 @@ const DatePicker = ({ onDateChange }) => { return (
- */} { return { dashboard_data, loading: isLineChartLoading, error }; }; +export const useDashboard_ActivityData = (date, projectId) => { + const [dashboard_Activitydata, setDashboard_ActivityData] = useState([]); + const [isLineChartLoading, setLoading] = useState(false); + const [error, setError] = useState(""); + + useEffect(() => { + const fetchData = async () => { + setLoading(true); + setError(""); + + try { + const response = await GlobalRepository.getDashboardActivityData(date,projectId); // date in 2nd param + setDashboard_ActivityData(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_Activitydata, isLineChartLoading: isLineChartLoading, error }; +}; + + // 🔹 Dashboard Projects Card Data Hook export const useDashboardProjectsCardData = () => { const [projectsCardData, setProjectsData] = useState([]); @@ -120,3 +150,31 @@ export const useDashboardTasksCardData = () => { return { tasksCardData, loading, error }; }; + + +// export const useDashboardActivityData = () => { +// const [ActivityData, setActivityData] = useState([]); +// const [loading, setLoading] = useState(false); +// const [error, setError] = useState(""); + +// useEffect(() => { +// const fetchTasksData = async () => { +// setLoading(true); +// setError(""); + +// try { +// const response = await GlobalRepository.getDashboardActivityData(); +// setActivityData(response.data); +// } catch (err) { +// setError("Failed to fetch tasks card data."); +// console.error(err); +// } finally { +// setLoading(false); +// } +// }; + +// fetchTasksData(); +// }, []); + +// return { ActivityData, loading, error }; +// }; diff --git a/src/repositories/GlobalRepository.jsx b/src/repositories/GlobalRepository.jsx index 31468b66..54034d7c 100644 --- a/src/repositories/GlobalRepository.jsx +++ b/src/repositories/GlobalRepository.jsx @@ -18,6 +18,23 @@ const GlobalRepository = { return api.get(`/api/Dashboard/Progression?${params.toString()}`); }, + + + getDashboardActivityData: ( date,projectId ) => { + + return api.get(`/api/Dashboard/activities/${projectId}?date=${date}`); +}, + +// getDashboardActivityData: (date, projectId) => { +// let url = `/api/Dashboard/activities`; +// if (projectId) { +// url += `/${projectId}?date=${date}`; +// } else { +// url += `?date=${date}`; // Fetch for all projects +// } +// return api.get(url); +// }, + getDashboardProjectsCardData: () => { return api.get(`/api/Dashboard/projects`); }, From dbc9bbffa2afa9a3e74137163d8ccd89fd1f4b50 Mon Sep 17 00:00:00 2001 From: "kartik.sharma" Date: Sat, 31 May 2025 11:39:15 +0530 Subject: [PATCH 3/4] 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`); }, From d80082bc9b438ff0df1eaf69e184a16c0534bb10 Mon Sep 17 00:00:00 2001 From: "kartik.sharma" Date: Wed, 28 May 2025 16:03:35 +0530 Subject: [PATCH 4/4] Status code 400 showing on Logs tab and Daily Progress Report datepicker issue. --- src/components/common/DateRangePicker.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/common/DateRangePicker.jsx b/src/components/common/DateRangePicker.jsx index c56e742e..36fa61d8 100644 --- a/src/components/common/DateRangePicker.jsx +++ b/src/components/common/DateRangePicker.jsx @@ -12,7 +12,9 @@ const DateRangePicker = ({ onRangeChange, DateDifference = 7, defaultStartDate = const fp = flatpickr(inputRef.current, { mode: "range", - dateFormat: "d-m-Y", + dateFormat: "Y-m-d", // Format for backend (actual input value) + altInput: true, // Enables a visually different field + altFormat: "d-m-Y", defaultDate: [fifteenDaysAgo, today], static: true, clickOpens: true,