diff --git a/index.html b/index.html index a1531220..1b2b7575 100644 --- a/index.html +++ b/index.html @@ -52,7 +52,7 @@ - + diff --git a/public/assets/js/main.js b/public/assets/js/main.js index 4ad0cfe9..cc356fa7 100644 --- a/public/assets/js/main.js +++ b/public/assets/js/main.js @@ -115,4 +115,38 @@ function Main () { // Auto update menu collapsed/expanded based on the themeConfig window.Helpers.setCollapsed(true, false); + + + + + // perfect scrolling + const verticalExample = document.getElementById('vertical-example'), + horizontalExample = document.getElementById('horizontal-example'), + horizVertExample = document.getElementById('both-scrollbars-example'); + + // Vertical Example + // -------------------------------------------------------------------- + if (verticalExample) { + new PerfectScrollbar(verticalExample, { + wheelPropagation: false + }); + } + + // Horizontal Example + // -------------------------------------------------------------------- + if (horizontalExample) { + new PerfectScrollbar(horizontalExample, { + wheelPropagation: false, + suppressScrollY: true + }); + } + + // Both vertical and Horizontal Example + // -------------------------------------------------------------------- + if (horizVertExample) { + new PerfectScrollbar(horizVertExample, { + wheelPropagation: false + }); + } + }; diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index d95f46c2..a9930ceb 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -454,7 +454,9 @@ table { caption-side: bottom; border-collapse: collapse; } - +.tr-group{ + background-color: var(--bs-body-bg); /* apply globale color for table row, where grouping datewise*/ +} caption { padding-top: 0.782rem; padding-bottom: 0.782rem; diff --git a/src/components/Activities/Attendance.jsx b/src/components/Activities/Attendance.jsx index 01ebc105..75979e7b 100644 --- a/src/components/Activities/Attendance.jsx +++ b/src/components/Activities/Attendance.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import moment from "moment"; import Avatar from "../common/Avatar"; import { convertShortTime } from "../../utils/dateUtils"; @@ -6,29 +6,41 @@ import RenderAttendanceStatus from "./RenderAttendanceStatus"; import usePagination from "../../hooks/usePagination"; import { useNavigate } from "react-router-dom"; import { ITEMS_PER_PAGE } from "../../utils/constants"; +import { useAttendance } from "../../hooks/useAttendance"; +import { useSelector } from "react-redux"; +import { useQueryClient } from "@tanstack/react-query"; +import eventBus from "../../services/eventBus"; -const Attendance = ({ - attendance, - getRole, - handleModalData, - setshowOnlyCheckout, - showOnlyCheckout, -}) => { +const Attendance = ({ getRole, handleModalData }) => { + const queryClient = useQueryClient(); const [loading, setLoading] = useState(false); const navigate = useNavigate(); const [todayDate, setTodayDate] = useState(new Date()); + const [ShowPending, setShowPending] = useState(false); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + const { + attendance, + loading: attLoading, + recall: attrecall, + isFetching + } = useAttendance(selectedProject); + const filteredAttendance = ShowPending + ? attendance?.filter( + (att) => att?.checkInTime !== null && att?.checkOutTime === null + ) + : attendance; - // Ensure attendance is an array - const attendanceList = Array.isArray(attendance) ? attendance : []; + const attendanceList = Array.isArray(filteredAttendance) + ? filteredAttendance + : []; - // Function to sort by first and last name const sortByName = (a, b) => { const nameA = (a.firstName + a.lastName).toLowerCase(); const nameB = (b.firstName + b.lastName).toLowerCase(); return nameA?.localeCompare(nameB); }; - - // Filter employees based on activity const group1 = attendanceList .filter((d) => d.activity === 1 || d.activity === 4) .sort(sortByName); @@ -37,30 +49,69 @@ const Attendance = ({ .sort(sortByName); const filteredData = [...group1, ...group2]; - const { currentPage, totalPages, currentItems, paginate } = usePagination( filteredData, ITEMS_PER_PAGE ); + + const handler = useCallback( + (msg) => { + if (selectedProject == msg.projectId) { + // const updatedAttendance = attendances.map((item) => + // item.employeeId === msg.response.employeeId + // ? { ...item, ...msg.response } + // : item + // ); + queryClient.setQueryData(["attendance", selectedProject], (oldData) => { + if (!oldData) { + queryClient.invalidateQueries({queryKey:["attendance"]}) + }; + return oldData.map((record) => + record.employeeId === msg.response.employeeId ? { ...record, ...msg.response } : record + ); + }); + } + }, + [selectedProject, attrecall] + ); + + const employeeHandler = useCallback( + (msg) => { + if (attendances.some((item) => item.employeeId == msg.employeeId)) { + attrecall(); + } + }, + [selectedProject, attendance] + ); + useEffect(() => { + eventBus.on("attendance", handler); + return () => eventBus.off("attendance", handler); + }, [handler]); + + useEffect(() => { + eventBus.on("employee", employeeHandler); + return () => eventBus.off("employee", employeeHandler); + }, [employeeHandler]); + return ( <> -
+
Date : {todayDate.toLocaleDateString("en-GB")} -
setshowOnlyCheckout(e.target.checked)} + disabled={isFetching} + checked={ShowPending} + onChange={(e) => setShowPending(e.target.checked)} />
- {attendance && attendance.length > 0 && ( + {Array.isArray(attendance) && attendance.length > 0 ? ( <> @@ -81,14 +132,13 @@ const Attendance = ({ {currentItems && currentItems .sort((a, b) => { - // If checkInTime exists, compare it, otherwise, treat null as earlier than a date const checkInA = a?.checkInTime ? new Date(a.checkInTime) : new Date(0); const checkInB = b?.checkInTime ? new Date(b.checkInTime) : new Date(0); - return checkInB - checkInA; // Sort in descending order of checkInTime + return checkInB - checkInA; }) .map((item) => ( @@ -138,7 +188,7 @@ const Attendance = ({ ))} {!attendance && ( - No employees assigned to the project + No employees assigned to the project! )}
@@ -189,6 +239,18 @@ const Attendance = ({ )} + ) : attLoading ? ( +
Loading...
+ ) : ( +
+ {Array.isArray(attendance) + ? "No employees assigned to the project" + : "Attendance data unavailable"} +
+ )} + + {currentItems?.length == 0 && attendance.length > 0 && ( +
No Pending Record Available !
)}
diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 505c7c61..0a28ffd3 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -9,6 +9,8 @@ import DateRangePicker from "../common/DateRangePicker"; import { clearCacheKey, getCachedData } from "../../slices/apiDataManager"; import eventBus from "../../services/eventBus"; import AttendanceRepository from "../../repositories/AttendanceRepository"; +import { useAttendancesLogs } from "../../hooks/useAttendance"; +import { queryClient } from "../../layouts/AuthLayout"; const usePagination = (data, itemsPerPage) => { const [currentPage, setCurrentPage] = useState(1); @@ -33,13 +35,15 @@ const usePagination = (data, itemsPerPage) => { const AttendanceLog = ({ handleModalData, - projectId, - setshowOnlyCheckout, - showOnlyCheckout, }) => { + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const dispatch = useDispatch(); - const { data, loading, error } = useSelector((store) => store.attendanceLogs); + const [loading, setLoading] = useState(false); + const [showPending,setShowPending] = useState(false) + const [isRefreshing, setIsRefreshing] = useState(false); const [processedData, setProcessedData] = useState([]); @@ -69,20 +73,19 @@ const AttendanceLog = ({ return nameA?.localeCompare(nameB); }; - useEffect(() => { - const { startDate, endDate } = dateRange; - dispatch( - fetchAttendanceData({ - projectId, - fromDate: startDate, - toDate: endDate, - }) - ); - setIsRefreshing(false); - }, [dateRange, projectId, dispatch, isRefreshing]); - + const { + data = [], + isLoading, + error, + refetch, + isFetching, + } = useAttendancesLogs( + selectedProject, + dateRange.startDate, + dateRange.endDate + ); const filtering = (data) => { - const filteredData = showOnlyCheckout + const filteredData = showPending ? data.filter((item) => item.checkOutTime === null) : data; @@ -124,19 +127,17 @@ const AttendanceLog = ({ return acc; }, {}); - // Sort dates in descending order const sortedDates = Object.keys(groupedByDate).sort( (a, b) => new Date(b) - new Date(a) ); - // Create the final sorted array const finalData = sortedDates.flatMap((date) => groupedByDate[date]); setProcessedData(finalData); - } + }; useEffect(() => { - filtering(data) - }, [data, showOnlyCheckout]); + filtering(data); + }, [data, showPending]); const { currentPage, @@ -146,55 +147,59 @@ const AttendanceLog = ({ resetPage, } = usePagination(processedData, 20); - // Reset to the first page whenever processedData changes (due to switch on/off) useEffect(() => { resetPage(); }, [processedData, resetPage]); const handler = useCallback( - (msg) => { - const { startDate, endDate } = dateRange; - const checkIn = msg.response.checkInTime.substring(0, 10); - if ( - projectId === msg.projectId && - startDate <= checkIn && - checkIn <= endDate - ) { - const updatedAttendance = data.map((item) => - item.id === msg.response.id - ? { ...item, ...msg.response } - : item - ); + (msg) => { + const { startDate, endDate } = dateRange; + const checkIn = msg.response.checkInTime.substring(0, 10); + if ( + selectedProject === msg.projectId && + startDate <= checkIn && + checkIn <= endDate + ) { + queryClient.setQueriesData(["attendanceLogs"],(oldData)=>{ + if(!oldData) { + queryClient.invalidateQueries({queryKey:["attendanceLogs"]}) + } + return oldData.map((record) => + record.id === msg.response.id ? { ...record, ...msg.response } : record + ); + }) - filtering(updatedAttendance); - resetPage(); - } - }, - [projectId, dateRange, data, filtering, resetPage] -); + filtering(updatedAttendance); + resetPage(); + } + }, + [selectedProject, dateRange, data, filtering, resetPage] + ); - useEffect(() => { + useEffect(() => { eventBus.on("attendance_log", handler); return () => eventBus.off("attendance_log", handler); }, [handler]); - const employeeHandler = useCallback( + const employeeHandler = useCallback( (msg) => { const { startDate, endDate } = dateRange; if (data.some((item) => item.employeeId == msg.employeeId)) { - dispatch( - fetchAttendanceData({ - projectId, - fromDate: startDate, - toDate: endDate, - }) - ) + // dispatch( + // fetchAttendanceData({ + // , + // fromDate: startDate, + // toDate: endDate, + // }) + // ); + + refetch() } }, - [projectId, dateRange,data] + [selectedProject, dateRange, data] ); - useEffect(() => { + useEffect(() => { eventBus.on("employee", employeeHandler); return () => eventBus.off("employee", employeeHandler); }, [employeeHandler]); @@ -215,9 +220,10 @@ const AttendanceLog = ({ type="checkbox" className="form-check-input" role="switch" + disabled={isFetching} id="inactiveEmployeesCheckbox" - checked={showOnlyCheckout} - onChange={(e) => setshowOnlyCheckout(e.target.checked)} + checked={showPending} + onChange={(e) => setShowPending(e.target.checked)} />
@@ -225,18 +231,17 @@ const AttendanceLog = ({
setIsRefreshing(true)} + onClick={() => refetch()} />
-
- {data && data.length > 0 && ( +
+ {isLoading ? ( +

Loading...

+ ) : data?.length > 0 ? ( @@ -255,92 +260,82 @@ const AttendanceLog = ({ - {(loading || isRefreshing) && ( - - - - )} - {!loading && - !isRefreshing && - paginatedAttendances.reduce((acc, attendance, index, arr) => { - const currentDate = moment( - attendance.checkInTime || attendance.checkOutTime - ).format("YYYY-MM-DD"); - const previousAttendance = arr[index - 1]; - const previousDate = previousAttendance - ? moment( - previousAttendance.checkInTime || - previousAttendance.checkOutTime - ).format("YYYY-MM-DD") - : null; + {paginatedAttendances.reduce((acc, attendance, index, arr) => { + const currentDate = moment( + attendance.checkInTime || attendance.checkOutTime + ).format("YYYY-MM-DD"); + const previousAttendance = arr[index - 1]; + const previousDate = previousAttendance + ? moment( + previousAttendance.checkInTime || + previousAttendance.checkOutTime + ).format("YYYY-MM-DD") + : null; - if (!previousDate || currentDate !== previousDate) { - acc.push( - - - - ); - } + if (!previousDate || currentDate !== previousDate) { acc.push( - - - - - - + ); - return acc; - }, [])} + } + acc.push( + + + + + + + + ); + return acc; + }, [])}
Loading...
- - {moment(currentDate).format("DD-MM-YYYY")} - -
- - - {moment( - attendance.checkInTime || attendance.checkOutTime - ).format("DD-MMM-YYYY")} - {convertShortTime(attendance.checkInTime)} - {attendance.checkOutTime - ? convertShortTime(attendance.checkOutTime) - : "--"} - - +
+ + {moment(currentDate).format("DD-MM-YYYY")} +
+ + + {moment( + attendance.checkInTime || attendance.checkOutTime + ).format("DD-MMM-YYYY")} + {convertShortTime(attendance.checkInTime)} + {attendance.checkOutTime + ? convertShortTime(attendance.checkOutTime) + : "--"} + + +
+ ) : ( +
No Record Available !
)} - {!loading && !isRefreshing && data.length === 0 && ( - No employee logs - )} - {/* {error && !loading && !isRefreshing && ( - - {error} - - )} */}
- {!loading && !isRefreshing && processedData.length > 10 && ( + {paginatedAttendances?.length == 0 && data?.length > 0 && ( +
No Pending Record Available !
+ )} + {processedData.length > 10 && (