From bd91f60e085f493c90d6769871598b95a9852f97 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sun, 4 May 2025 12:11:50 +0530 Subject: [PATCH 1/6] passed checkout time to timepicker as props --- src/components/Activities/CheckCheckOutForm.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Activities/CheckCheckOutForm.jsx b/src/components/Activities/CheckCheckOutForm.jsx index 3766ee51..419e7b12 100644 --- a/src/components/Activities/CheckCheckOutForm.jsx +++ b/src/components/Activities/CheckCheckOutForm.jsx @@ -73,7 +73,7 @@ const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => { label="Choose a time" onChange={(e) => setValue("markTime", e)} interval={10} - activity={modeldata?.action} + checkOutTime={modeldata?.checkOutTime} checkInTime={modeldata?.checkInTime} /> {errors. markTime &&

{errors.markTime.message}

} From 292f80c9a8770e9dcf98186692259096d7782da2 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sun, 4 May 2025 12:12:53 +0530 Subject: [PATCH 2/6] config checkIn and checkout in handleModalData for which slots show to user --- src/components/Activities/RenderAttendanceStatus.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Activities/RenderAttendanceStatus.jsx b/src/components/Activities/RenderAttendanceStatus.jsx index 63d65709..637575a4 100644 --- a/src/components/Activities/RenderAttendanceStatus.jsx +++ b/src/components/Activities/RenderAttendanceStatus.jsx @@ -18,7 +18,8 @@ const RenderAttendanceStatus = ({ attendanceData, handleModalData,Tab,currentDat employeeId: attendanceData?.employeeId, id: attendanceData?.id, currentDate, - checkInTime:attendanceData.checkInTime + checkInTime: attendanceData.checkInTime, + checkOutTime:attendanceData.checkOutTime }); } }; From d5cbe67d3e7ec4f5e2070296969925eac323ab1e Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sun, 4 May 2025 12:20:16 +0530 Subject: [PATCH 3/6] update TimePicker slot logic based on check-in and check-out time conditions --- src/components/common/TimePicker.jsx | 79 +++++++++++----------------- 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/src/components/common/TimePicker.jsx b/src/components/common/TimePicker.jsx index ee912551..56a0e426 100644 --- a/src/components/common/TimePicker.jsx +++ b/src/components/common/TimePicker.jsx @@ -1,6 +1,7 @@ import React, { useState, useRef, useEffect } from "react"; +import {THRESH_HOLD} from "../../utils/constants" -const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,activity }) => { +const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,checkOutTime }) => { const [time, setTime] = useState(value || ""); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); @@ -41,59 +42,50 @@ const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,activity const checkInDate = checkInTime ? new Date(checkInTime) : null; - - const fullStart = checkInDate ? new Date(checkInDate) : new Date(); - fullStart.setHours(0, 0, 0, 0); - - const fullEnd = checkInDate ? new Date(checkInDate) : new Date(); - fullEnd.setHours(23, 59, 59, 999); - + const checkOutDate = checkOutTime ? new Date(checkOutTime) : null; + + const dayStart = new Date(); + const dayEnd = new Date(); + if (checkInDate) { + dayStart.setTime(checkInDate.getTime()); + dayEnd.setTime(checkInDate.getTime()); + } + dayStart.setHours(0, 0, 0, 0); + dayEnd.setHours(23, 59, 59, 999); let minSelectable = null; let maxSelectable = null; // Activity 0: Time slots based on current time - if (activity === 0 && !checkInDate) { - minSelectable = new Date(currentSlot.getTime() - 60 * 60000); // one hour before current time + if (!checkInDate) { + // Case 1: No check-in time (new check-in) + minSelectable = new Date(dayStart); maxSelectable = new Date(currentSlot); } // Activity 1: Time slots based on check-in time today - else if (activity === 1 && checkInDate) { - if (checkInDate.toDateString() === now.toDateString()) { + else if (checkInDate && !checkOutDate) { + const hoursDiff = (now - checkInDate) / (1000 * 60 * 60); + if (hoursDiff < THRESH_HOLD) { + // Case 2: Check-in present, no checkout, within THRESH_HOLD hours minSelectable = new Date(checkInDate); maxSelectable = new Date(currentSlot); + } else { + // Case 4: Check-in present, no checkout, more than THRESH_HOLD hours + minSelectable = new Date(checkInDate); + maxSelectable = new Date(dayEnd); } - } - // Activity 2: Time slots from check-in time to 12:00 AM of the same day - else if (activity === 2 && checkInDate) { - // Set minSelectable to the exact check-in time - minSelectable = new Date(checkInDate); - - // Set maxSelectable to midnight (12:00 AM) of the same day - maxSelectable = new Date(checkInDate); - maxSelectable.setHours(23, 59, 59, 999); // Set to 23:59:59.999 (end of the day) + } else if (checkInDate && checkOutDate) { + // Case 3: Both check-in and checkout present + minSelectable = new Date(checkOutDate); + maxSelectable = new Date(currentSlot); } - // Loop through every slot in the day to check which ones are selectable - const slot = new Date(fullStart.getTime()); - while (slot <= fullEnd) { + const slot = new Date(dayStart.getTime()); + while (slot <= dayEnd) { const formatted = formatTime(slot); - let isSelectable = false; - - // For activity 2, allow only slots from the check-in time up to 12:00 AM of the same day - if (minSelectable && maxSelectable) { - if (activity === 2 && checkInDate) { - // Ensure slot is greater than or equal to minSelectable and less than or equal to maxSelectable - isSelectable = slot >= minSelectable && slot <= maxSelectable; - } else { - isSelectable = slot >= minSelectable && slot <= maxSelectable; - } - } - slots.push({ - time: formatted, - isSelectable, - }); + const isSelectable = slot >= minSelectable && slot <= maxSelectable; + slots.push({ time: formatted, isSelectable }); slot.setMinutes(slot.getMinutes() + interval); } @@ -137,13 +129,6 @@ const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,activity } }, [isOpen, time]); - useEffect(() => { - if (activity === 2 && checkInTime) { - const slots = generateTimeSlots(); - const selectableSlots = slots.filter(slot => slot.isSelectable); - console.log("Selectable Slots for Activity 2:", selectableSlots); - } - }, [activity, checkInTime]); return (
@@ -205,4 +190,4 @@ const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,activity ); }; -export default TimePicker; +export default TimePicker; \ No newline at end of file From 213ca4067e0baf2c060e397e2777381af98f6893 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sun, 4 May 2025 12:23:04 +0530 Subject: [PATCH 4/6] added one more codition if emp checkIn and checkOut and checkout day is current , so ee can able to checkIn otherwise their attendance approved of that day --- src/hooks/useAttendanceStatus.js | 34 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/hooks/useAttendanceStatus.js b/src/hooks/useAttendanceStatus.js index a1ba9a22..e0dd8e6c 100644 --- a/src/hooks/useAttendanceStatus.js +++ b/src/hooks/useAttendanceStatus.js @@ -74,15 +74,31 @@ const useAttendanceStatus = (attendanceData) => { text: "Regularize", color: 'btn-warning', }); - }else if(activity === 4 && checkInTime !== null && checkOutTime !== null && !timeElapsed(checkInTime,THRESH_HOLD) ){ - setStatus({ - status: "Check-In", - action: ACTIONS.CHECK_IN, - disabled: false, - text: "Check In", - color: 'btn-primary', - }) - }else if(activity === 2 && checkInTime !== null ){ + } else if ( activity === 4 && checkInTime !== null && checkOutTime !== null && !timeElapsed( checkInTime, THRESH_HOLD ) ) + { + + if ( activity === 4 && checkInTime !== null && checkOutTime !== null && new Date(checkOutTime).toDateString() !== new Date().toDateString()) + { + setStatus( { + status: "Approved", + action: ACTIONS.APPROVED, + disabled: true, + text: "Approved", + color: 'btn-success', + } ); + } else + { + setStatus( { + status: "Check-In", + action: ACTIONS.CHECK_IN, + disabled: false, + text: "Check In", + color: 'btn-primary', + } ) + } + } + else if ( activity === 2 && checkInTime !== null ) + { setStatus({ status: "Requested", action: ACTIONS.REQUESTED, From f1e22e944e592236df23209941cfdf6908715dba Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sun, 4 May 2025 12:24:27 +0530 Subject: [PATCH 5/6] added on more condition checIn and checkOut are present, and same day, so display slots from chekout to end of that day. --- src/components/common/TimePicker.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/common/TimePicker.jsx b/src/components/common/TimePicker.jsx index 56a0e426..cb76a5d1 100644 --- a/src/components/common/TimePicker.jsx +++ b/src/components/common/TimePicker.jsx @@ -76,8 +76,9 @@ const TimePicker = ({ label, onChange, interval = 10, value,checkInTime,checkOut } } else if (checkInDate && checkOutDate) { // Case 3: Both check-in and checkout present + const isSameDay = new Date(checkOutDate).toDateString() === new Date().toDateString(); minSelectable = new Date(checkOutDate); - maxSelectable = new Date(currentSlot); + maxSelectable = isSameDay ? new Date(dayEnd) : new Date(currentSlot); } const slot = new Date(dayStart.getTime()); From 2fb08a004430c0777972fbff2637c9d24c264a02 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Sun, 4 May 2025 12:30:35 +0530 Subject: [PATCH 6/6] added sorting group by name, checkIn , checkOut, regularize, approved and reject --- src/components/Activities/Attendance.jsx | 37 +++++++++--- src/components/Activities/AttendcesLogs.jsx | 35 +++++++++++- src/components/Activities/Regularization.jsx | 59 ++++++++++++++++++++ 3 files changed, 121 insertions(+), 10 deletions(-) diff --git a/src/components/Activities/Attendance.jsx b/src/components/Activities/Attendance.jsx index 0ee7fc97..69f3de10 100644 --- a/src/components/Activities/Attendance.jsx +++ b/src/components/Activities/Attendance.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState,useEffect } from "react"; import moment from "moment"; import Avatar from "../common/Avatar"; import { convertShortTime } from "../../utils/dateUtils"; @@ -7,17 +7,37 @@ import usePagination from "../../hooks/usePagination"; import { useNavigate } from "react-router-dom"; const Attendance = ({ attendance, getRole, handleModalData }) => { - const { currentPage, totalPages, currentItems, paginate } = usePagination( - attendance, - 5 - ); const [loading, setLoading] = useState(false); const navigate = useNavigate(); + // Ensure attendance is an array + const attendanceList = Array.isArray(attendance) ? attendance : []; + + // 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); + const group2 = attendanceList + .filter((d) => d.activity === 0) + .sort(sortByName); + + const filteredData = [...group1, ...group2]; + + const { currentPage, totalPages, currentItems, paginate } = usePagination( + filteredData, + 5 + ); return ( <>
- {attendance && attendance.length > 0 ? ( + {attendance && attendance.length > 0 && ( <> @@ -96,6 +116,9 @@ const Attendance = ({ attendance, getRole, handleModalData }) => { ))} + {!attendance && ( + No employees assigned to the project + )}
@@ -145,8 +168,6 @@ const Attendance = ({ attendance, getRole, handleModalData }) => { )} - ) : ( - No employees assigned to the project )}
diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 6d3dc32c..fd6e0779 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -16,9 +16,41 @@ const AttendanceLog = ({ handleModalData, projectId }) => { const dispatch = useDispatch(); const { data, loading, error } = useSelector((store) => store.attendanceLogs); const [isRefreshing, setIsRefreshing] = useState(true); + + const today = new Date(); + today.setHours(0, 0, 0, 0); // Strip time to compare dates only + + const isSameDay = (dateStr) => { + if (!dateStr) return false; + const d = new Date(dateStr); + d.setHours(0, 0, 0, 0); + return d.getTime() === today.getTime(); + }; + + const isBeforeToday = (dateStr) => { + if (!dateStr) return false; + const d = new Date(dateStr); + d.setHours(0, 0, 0, 0); + return d.getTime() < today.getTime(); + }; + + const sortByName = (a, b) => { + const nameA = a.firstName.toLowerCase() + a.lastName.toLowerCase(); + const nameB = b.firstName.toLowerCase() + b.lastName.toLowerCase(); + return nameA.localeCompare(nameB); + }; + + const group1 = data.filter(d => d.activity === 1 && isSameDay(d.checkInTime)).sort(sortByName); + const group2 = data.filter(d => d.activity === 4 && isSameDay(d.checkOutTime)).sort(sortByName); + const group3 = data.filter(d => d.activity === 1 && isBeforeToday(d.checkInTime)).sort(sortByName); + const group4 = data.filter(d => d.activity === 4 && isBeforeToday(d.checkOutTime)).sort(sortByName); + const group5 = data.filter(d => d.activity === 5).sort(sortByName); + + const sortedFinalList = [...group1, ...group2, ...group3, ...group4, ...group5]; + const currentDate = new Date().toLocaleDateString( "en-CA" ); const { currentPage, totalPages, currentItems, paginate } = usePagination( - data, + sortedFinalList, 5 ); @@ -35,7 +67,6 @@ const AttendanceLog = ({ handleModalData, projectId }) => { } }, [dateRange, projectId, isRefreshing]); - return ( <>
{ var selectedProject = useSelector((store) => store.localVariables.projectId); @@ -16,6 +17,19 @@ const Regularization = ({ handleRequest }) => { setregularizedList(regularizes); }, [regularizes]); + const sortByName = (a, b) => { + const nameA = a.firstName.toLowerCase() + a.lastName.toLowerCase(); + const nameB = b.firstName.toLowerCase() + b.lastName.toLowerCase(); + return nameA.localeCompare(nameB); + }; + + const filteredData = regularizesList.sort(sortByName) + + const { currentPage, totalPages, currentItems, paginate } = usePagination( + filteredData, + 5 + ); + return (
@@ -79,6 +93,51 @@ const Regularization = ({ handleRequest }) => { ))}
+ {!loading && ( + + )}
); };