From 7bfd1728c09a92a2904b13249767897af31d913d Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Mon, 12 May 2025 11:33:04 +0530 Subject: [PATCH 1/3] updated regularization logic based on 48-hour checkout threshold for attendanceLogs. --- src/components/Activities/AttendLogs.jsx | 66 ++++++++++++++++++------ 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/src/components/Activities/AttendLogs.jsx b/src/components/Activities/AttendLogs.jsx index b246768b..0ae97b8f 100644 --- a/src/components/Activities/AttendLogs.jsx +++ b/src/components/Activities/AttendLogs.jsx @@ -2,12 +2,27 @@ import React, { useEffect, useState } from "react"; import { useEmployeeAttendacesLog } from "../../hooks/useAttendance"; import { convertShortTime } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; +import {THRESH_HOLD} from "../../utils/constants"; const AttendLogs = ({ Id }) => { const { logs, loading } = useEmployeeAttendacesLog(Id); const navigate = useNavigate(); - const whichActivityPerform = (actvity) => { + const isCheckoutRegularized = ( + activityTimeStr, + checkoutTimeStr, + threshHours + )=> { + if (!activityTimeStr || !checkoutTimeStr) return false; + + const activityTime = new Date(activityTimeStr); + const checkoutTime = new Date(checkoutTimeStr); + const threshTimeMs = threshHours * 60 * 60 * 1000; + + return checkoutTime - activityTime > threshTimeMs; + } + + const whichActivityPerform = (actvity,checkOutTime) => { switch (actvity) { case 1: return ( @@ -24,12 +39,12 @@ const AttendLogs = ({ Id }) => { case 2: return ( ); break; @@ -41,27 +56,43 @@ const AttendLogs = ({ Id }) => { data-bs-offset="0,8" data-bs-placement="top" data-bs-custom-class="tooltip" - title="Regularized" + title="Request Deleted!" > ); break; case 4: - return ( - - ); + if ( + checkOutTime && + isCheckoutRegularized(logs[0]?.activityTime, checkOutTime, THRESH_HOLD) + ) { + return ( + + ); + } else { + return ( + + ); + } break; case 5: return ( { const url = `https://www.google.com/maps?q=${lat},${lng}`; window.open(url, "_blank"); // Open in new tab }; + useEffect(() => { const tooltipTriggerList = Array.from( document.querySelectorAll('[data-bs-toggle="tooltip"]') @@ -122,11 +154,11 @@ const AttendLogs = ({ Id }) => { {log.activityTime.slice(0, 10)} {convertShortTime(log.activityTime)} - {whichActivityPerform(log.activity)} + {whichActivityPerform(log.activity,log.activityTime)} {log?.latitude != 0 ? ( Date: Sat, 31 May 2025 17:02:36 +0530 Subject: [PATCH 2/3] conditionally render Actions column based on permissions and project status --- .../Project/Infrastructure/WorkArea.jsx | 21 +- .../Project/Infrastructure/WorkItem.jsx | 184 +++++++++--------- 2 files changed, 113 insertions(+), 92 deletions(-) diff --git a/src/components/Project/Infrastructure/WorkArea.jsx b/src/components/Project/Infrastructure/WorkArea.jsx index cbe34dc5..69663630 100644 --- a/src/components/Project/Infrastructure/WorkArea.jsx +++ b/src/components/Project/Infrastructure/WorkArea.jsx @@ -6,11 +6,20 @@ import { useDispatch } from "react-redux"; import { refreshData } from "../../../slices/localVariablesSlice"; import ProjectRepository from "../../../repositories/ProjectRepository"; import showToast from "../../../services/toastService"; +import {useHasUserPermission} from "../../../hooks/useHasUserPermission"; +import {ASSIGN_REPORT_TASK, MANAGE_PROJECT_INFRA, MANAGE_TASK} from "../../../utils/constants"; +import {useParams} from "react-router-dom"; const WorkArea = ({ workArea, floor, forBuilding }) => { const [workItems, setWorkItems] = useState([]); const dispatch = useDispatch(); - const [Project, setProject] = useState(); + const [ Project, setProject ] = useState(); + const {projectId} = useParams(); + + const ManageTasks = useHasUserPermission(MANAGE_TASK); + const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA); + const ManageAndAssignTak = useHasUserPermission( ASSIGN_REPORT_TASK ); + useEffect(() => { const project = getCachedData("projectInfo"); @@ -170,7 +179,7 @@ const WorkArea = ({ workArea, floor, forBuilding }) => { className="accordion-collapse collapse" aria-labelledby={`heading-${workArea.id}`} > -
+
@@ -189,9 +198,11 @@ const WorkArea = ({ workArea, floor, forBuilding }) => { - + {( ManageInfra || ( !projectId && ManageAndAssignTak ) ) && ( + + )} diff --git a/src/components/Project/Infrastructure/WorkItem.jsx b/src/components/Project/Infrastructure/WorkItem.jsx index 9a694729..06303908 100644 --- a/src/components/Project/Infrastructure/WorkItem.jsx +++ b/src/components/Project/Infrastructure/WorkItem.jsx @@ -148,7 +148,7 @@ const WorkItem = ({ {hasWorkItem ? NewWorkItem?.workItem?.activityMaster?.activityName || - workItem.activityMaster?.activityName + workItem.activityMaster?.activityName : "NA"} @@ -157,8 +157,8 @@ const WorkItem = ({ {/* Progress Bar - always visible */} - + + + {/* Mobile (sm only): dropdown with icons */} +
+ + + +
+ + )} ); From c91682a8717f3f2634fc075e95f7d7d9a7f53d8e Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 31 May 2025 23:34:55 +0530 Subject: [PATCH 3/3] fixed attendance status & logs logic with proper activity handling --- src/components/Activities/AttendLogs.jsx | 24 ++-- src/hooks/useAttendanceStatus.js | 168 +++++++++-------------- 2 files changed, 83 insertions(+), 109 deletions(-) diff --git a/src/components/Activities/AttendLogs.jsx b/src/components/Activities/AttendLogs.jsx index 0ae97b8f..b94c80c6 100644 --- a/src/components/Activities/AttendLogs.jsx +++ b/src/components/Activities/AttendLogs.jsx @@ -2,17 +2,17 @@ import React, { useEffect, useState } from "react"; import { useEmployeeAttendacesLog } from "../../hooks/useAttendance"; import { convertShortTime } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; -import {THRESH_HOLD} from "../../utils/constants"; +import { THRESH_HOLD } from "../../utils/constants"; const AttendLogs = ({ Id }) => { const { logs, loading } = useEmployeeAttendacesLog(Id); const navigate = useNavigate(); - const isCheckoutRegularized = ( + const isCheckoutRegularized = ( activityTimeStr, checkoutTimeStr, threshHours - )=> { + ) => { if (!activityTimeStr || !checkoutTimeStr) return false; const activityTime = new Date(activityTimeStr); @@ -20,9 +20,9 @@ const AttendLogs = ({ Id }) => { const threshTimeMs = threshHours * 60 * 60 * 1000; return checkoutTime - activityTime > threshTimeMs; - } + }; - const whichActivityPerform = (actvity,checkOutTime) => { + const whichActivityPerform = (actvity, checkOutTime) => { switch (actvity) { case 1: return ( @@ -63,7 +63,11 @@ const AttendLogs = ({ Id }) => { case 4: if ( checkOutTime && - isCheckoutRegularized(logs[0]?.activityTime, checkOutTime, THRESH_HOLD) + isCheckoutRegularized( + logs[0]?.activityTime, + checkOutTime, + THRESH_HOLD + ) ) { return ( { - + ))} diff --git a/src/hooks/useAttendanceStatus.js b/src/hooks/useAttendanceStatus.js index b1b6180b..4085fba2 100644 --- a/src/hooks/useAttendanceStatus.js +++ b/src/hooks/useAttendanceStatus.js @@ -1,17 +1,16 @@ -import { useEffect, useState } from "react"; -import { timeElapsed } from "../utils/dateUtils"; -import { useSelector } from "react-redux"; -import { THRESH_HOLD } from "../utils/constants"; +import { useEffect, useState } from 'react'; +import { timeElapsed } from '../utils/dateUtils'; // Make sure it calculates in hours +import { THRESH_HOLD } from '../utils/constants'; +// THRESH_HOLD in hours (e.g., 12, 48, or 60) export const ACTIONS = { CHECK_IN: 0, CHECK_OUT: 1, REGULARIZATION: 2, REQUESTED: 3, APPROVED: 4, - REJECTED: 5, + REJECTED: 5 }; -const now = new Date(); const useAttendanceStatus = (attendanceData) => { const [status, setStatus] = useState({ @@ -19,133 +18,100 @@ const useAttendanceStatus = (attendanceData) => { action: null, disabled: true, text: "Unknown", - color: "btn-secondary", + color: 'btn-secondary', }); useEffect(() => { const { checkInTime, checkOutTime, activity } = attendanceData; + const now = new Date(); - if (activity === 0 && checkInTime === null && checkOutTime === null) { - setStatus({ + const isSameDay = (date1, date2) => + new Date(date1).toDateString() === new Date(date2).toDateString(); + + // 1. No check-in/check-out yet → Allow Check-In + if (activity === 0 && !checkInTime && !checkOutTime) { + return setStatus({ status: "Check-In", action: ACTIONS.CHECK_IN, disabled: false, text: "Check In", - color: "btn-primary", + color: 'btn-primary', }); - } else if (activity === 4 && new Date(checkOutTime) < now) { - setStatus({ - status: "Approved", - action: ACTIONS.APPROVED, - disabled: true, - text: "Approved", - color: "btn-success", - }); - } else if ( - activity === 0 && - 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 === 0 && - checkInTime !== null && - checkOutTime === null && - timeElapsed(checkInTime, THRESH_HOLD) - ) { - setStatus({ - status: "Request Regularize", - action: ACTIONS.REGULARIZATION, - disabled: false, - text: "Regularizes", - color: "btn-warning", - }); - } else if ( - activity === 1 && - checkInTime !== null && - checkOutTime === null && - !timeElapsed(checkInTime, THRESH_HOLD) - ) { - setStatus({ - status: "Check-Out", - action: ACTIONS.CHECK_OUT, - disabled: false, - text: "Check Out", - color: "btn-primary", - }); - } else if ( - activity === 1 && - checkInTime !== null && - checkOutTime === null && - timeElapsed(checkInTime, THRESH_HOLD) - ) { - setStatus({ - status: "Request Regularize", - action: ACTIONS.REGULARIZATION, - disabled: false, - text: "Regularize", - color: "btn-warning", - }); - } 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({ + } + + // 2. Checked in, no checkout yet + if (checkInTime && !checkOutTime) { + if (timeElapsed(checkInTime, THRESH_HOLD)) { + return setStatus({ + status: "Request Regularize", + action: ACTIONS.REGULARIZATION, + disabled: false, + text: "Regularize", + color: 'btn-warning', + }); + } else { + return setStatus({ + status: "Check-Out", + action: ACTIONS.CHECK_OUT, + disabled: false, + text: "Check Out", + color: 'btn-primary', + }); + } + } + + // 3. Already checked in and out → Handle activity === 4 (Approved) + if (checkInTime && checkOutTime && activity === 4) { + if (!isSameDay(checkOutTime, now) && !timeElapsed(checkOutTime, THRESH_HOLD)) { + return setStatus({ status: "Approved", action: ACTIONS.APPROVED, disabled: true, text: "Approved", - color: "btn-success", + color: 'btn-success', }); - } else { - setStatus({ + } else if (isSameDay(checkOutTime, now)) { + return setStatus({ status: "Check-In", action: ACTIONS.CHECK_IN, disabled: false, text: "Check In", - color: "btn-primary", + color: 'btn-primary', }); } - } else if (activity === 2 && checkInTime !== null) { - setStatus({ + } + + // 4. Regularization Requested + if (activity === 2) { + return setStatus({ status: "Requested", action: ACTIONS.REQUESTED, disabled: true, text: "Requested", - color: "btn-info", + color: 'btn-info', }); - } else if (activity === 5 && checkInTime !== null) { - setStatus({ + } + + // 5. Rejected Regularization + if (activity === 5) { + return setStatus({ status: "Rejected", action: ACTIONS.REJECTED, disabled: true, text: "Rejected", - color: "btn-danger", - }); - } else { - setStatus({ - status: "Approved", - action: ACTIONS.APPROVED, - disabled: true, - text: "Approved", - color: "btn-success", + color: 'btn-danger', }); } + + // Default to Approved if none of the above apply + return setStatus({ + status: "Approved", + action: ACTIONS.APPROVED, + disabled: true, + text: "Approved", + color: 'btn-success', + }); + }, [attendanceData]); return status;
Progress - Actions - + Actions +
{hasWorkItem ? NewWorkItem?.workItem?.completedWork ?? - workItem?.completedWork ?? - "NA" + workItem?.completedWork ?? + "NA" : "NA"} /{" "} {hasWorkItem @@ -171,26 +171,28 @@ const WorkItem = ({ {hasWorkItem ? NewWorkItem?.workItem?.workCategoryMaster?.name || - workItem.workCategoryMaster?.name || - "NA" + workItem.workCategoryMaster?.name || + "NA" : "NA"} - {hasWorkItem ? ( - `${NewWorkItem?.workItem?.completedWork ?? - workItem?.completedWork ?? - "0"}/${NewWorkItem?.workItem?.plannedWork ?? - workItem?.plannedWork ?? - "0"}` - ) : ( - "NA" - )} + {hasWorkItem + ? `${ + NewWorkItem?.workItem?.completedWork ?? + workItem?.completedWork ?? + "0" + }/${ + NewWorkItem?.workItem?.plannedWork ?? + workItem?.plannedWork ?? + "0" + }` + : "NA"} +
{/* Actions - always visible */} -
- {/* Desktop (md and up): inline icons */} -
- {!projectId && - ManageAndAssignTak && - PlannedWork !== CompletedWork && ( - - )} - - {ManageInfra && ( - <> - - - - )} -
- - {/* Mobile (sm only): dropdown with icons */} -
- - - -
-
{log.activityTime.slice(0, 10)} {convertShortTime(log.activityTime)}{whichActivityPerform(log.activity,log.activityTime)} + {whichActivityPerform(log.activity, log.activityTime)} + {log?.latitude != 0 ? ( { }`} - {log?.comment || "--"} + {log?.comment?.length > 50 + ? `${log.comment.slice(0, 50)}...` + : log.comment || "--"}