288 lines
10 KiB
JavaScript

import React, { useState, useEffect, useCallback, useMemo } from "react";
import moment from "moment";
import Avatar from "../common/Avatar";
import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
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";
import { useSelectedProject } from "../../slices/apiDataManager";
const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizationId, includeInactive, date }) => {
const queryClient = useQueryClient();
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const [todayDate, setTodayDate] = useState(new Date());
const [ShowPending, setShowPending] = useState(false);
const selectedProject = useSelectedProject();
const {
attendance,
loading: attLoading,
recall: attrecall,
isFetching
} = useAttendance(selectedProject, organizationId, includeInactive, date);
const filteredAttendance = ShowPending
? attendance?.filter(
(att) => att?.checkInTime !== null && att?.checkOutTime === null
)
: attendance;
const attendanceList = Array.isArray(filteredAttendance)
? filteredAttendance
: [];
const sortByName = (a, b) => {
const nameA = (a.firstName + a.lastName).toLowerCase();
const nameB = (b.firstName + b.lastName).toLowerCase();
return nameA?.localeCompare(nameB);
};
const group1 = attendanceList
.filter((d) => d.activity === 1 || d.activity === 4)
.sort(sortByName);
const group2 = attendanceList
.filter((d) => d.activity === 0)
.sort(sortByName);
const finalFilteredData = useMemo(() => {
const combinedData = [...group1, ...group2];
if (!searchTerm) {
return combinedData;
}
const lowercasedSearchTerm = searchTerm.toLowerCase();
return combinedData.filter((item) => {
const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
const role = item.jobRoleName?.toLowerCase() || "";
return (
fullName.includes(lowercasedSearchTerm) ||
role.includes(lowercasedSearchTerm) // also search by role
);
});
}, [group1, group2, searchTerm]);
const { currentPage, totalPages, currentItems, paginate } = usePagination(
finalFilteredData,
ITEMS_PER_PAGE
);
// Reset pagination when the filter or search term changes
useEffect(() => {
}, [finalFilteredData]);
const handler = useCallback(
(msg) => {
if (selectedProject == msg.projectId) {
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 (attendance.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 (
<>
<div
className="table-responsive text-nowrap h-100"
style={{ minHeight: "200px" }} // Ensures fixed height
>
<div className="d-flex text-start align-items-center py-2">
<strong>Date : {formatUTCToLocalTime(todayDate)}</strong>
<div className="form-check form-switch text-start m-0 ms-5">
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
disabled={isFetching}
checked={ShowPending}
onChange={(e) => setShowPending(e.target.checked)}
/>
<label className="form-check-label ms-0">Show Pending</label>
</div>
</div>
{attLoading ? (
<div>Loading...</div>
) : currentItems?.length > 0 ? (
<>
<table className="table ">
<thead>
<tr className="border-top-1">
<th colSpan={2}>Name</th>
<th>Role</th>
<th>Organization</th>
<th>
<i className="bx bxs-down-arrow-alt text-success"></i>
Check-In
</th>
<th>
<i className="bx bxs-up-arrow-alt text-danger"></i>Check-Out
</th>
<th>Actions</th>
</tr>
</thead>
<tbody className="table-border-bottom-0 ">
{currentItems &&
currentItems
.sort((a, b) => {
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;
})
.map((item) => (
<tr key={item.employeeId}>
<td colSpan={2}>
<div className="d-flex justify-content-start align-items-center">
<Avatar
firstName={item.firstName}
lastName={item.lastName}
></Avatar>
<div className="d-flex flex-column">
<a
onClick={(e) =>
navigate(
`/employee/${item.employeeId}?for=attendance`
)
}
className="text-heading text-truncate cursor-pointer"
>
<span className="fw-normal">
{item.firstName} {item.lastName}
</span>
</a>
</div>
</div>
</td>
<td>{item.jobRoleName}</td>
<td>{item.organizationName || "--"}</td>
<td>
{item.checkInTime
? convertShortTime(item.checkInTime)
: "--"}
</td>
<td>
{item.checkOutTime
? convertShortTime(item.checkOutTime)
: "--"}
</td>
<td className="text-center">
<RenderAttendanceStatus
attendanceData={item}
handleModalData={handleModalData}
Tab={1}
currentDate={null}
/>
</td>
</tr>
))}
{!attendance && (
<tr>
<td
colSpan={7}
className="text-center text-secondary"
style={{ height: "200px" }}
>
No employees assigned to the project!
</td>
</tr>
)}
</tbody>
</table>
{!loading && finalFilteredData.length > ITEMS_PER_PAGE && (
<nav aria-label="Page ">
<ul className="pagination pagination-sm justify-content-end py-1">
<li
className={`page-item ${currentPage === 1 ? "disabled" : ""
}`}
>
<button
className="page-link btn-xs"
onClick={() => paginate(currentPage - 1)}
>
&laquo;
</button>
</li>
{[...Array(totalPages)].map((_, index) => (
<li
key={index}
className={`page-item ${currentPage === index + 1 ? "active" : ""
}`}
>
<button
className="page-link "
onClick={() => paginate(index + 1)}
>
{index + 1}
</button>
</li>
))}
<li
className={`page-item ${currentPage === totalPages ? "disabled" : ""
}`}
>
<button
className="page-link "
onClick={() => paginate(currentPage + 1)}
>
&raquo;
</button>
</li>
</ul>
</nav>
)}
</>
) : (
<div
className="d-flex justify-content-center align-items-center text-muted"
style={{ height: "200px" }}
>
{searchTerm
? "No results found for your search."
: attendanceList.length === 0
? "No employees assigned to the project."
: "No pending records available."}
</div>
)}
</div>
</>
);
};
export default Attendance;