diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 505c7c61..268115fe 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -6,28 +6,36 @@ import RenderAttendanceStatus from "./RenderAttendanceStatus"; import { useSelector, useDispatch } from "react-redux"; import { fetchAttendanceData } from "../../slices/apiSlice/attedanceLogsSlice"; import DateRangePicker from "../common/DateRangePicker"; -import { clearCacheKey, getCachedData } from "../../slices/apiDataManager"; import eventBus from "../../services/eventBus"; -import AttendanceRepository from "../../repositories/AttendanceRepository"; const usePagination = (data, itemsPerPage) => { const [currentPage, setCurrentPage] = useState(1); - const maxPage = Math.ceil(data.length / itemsPerPage); + const totalItems = Array.isArray(data) ? data.length : 0; + const maxPage = Math.ceil(totalItems / itemsPerPage); + const currentItems = useMemo(() => { + if (!Array.isArray(data) || data.length === 0) { + return []; + } const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; return data.slice(startIndex, endIndex); }, [data, currentPage, itemsPerPage]); - const paginate = useCallback((pageNumber) => setCurrentPage(pageNumber), []); - const resetPage = useCallback(() => setCurrentPage(1), []); + const paginate = useCallback((pageNumber) => { + if (pageNumber > 0 && pageNumber <= maxPage) { + setCurrentPage(pageNumber); + } + }, [maxPage]); + + const resetPage = useCallback(() => setCurrentPage(1), []); // This is returned by the hook return { currentPage, totalPages: maxPage, currentItems, paginate, - resetPage, + resetPage, // Ensure resetPage is returned here }; }; @@ -36,38 +44,44 @@ const AttendanceLog = ({ projectId, setshowOnlyCheckout, showOnlyCheckout, + searchQuery, // Prop for search query }) => { const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const dispatch = useDispatch(); const { data, loading, error } = useSelector((store) => store.attendanceLogs); const [isRefreshing, setIsRefreshing] = useState(false); - const [processedData, setProcessedData] = useState([]); - const today = new Date(); - today.setHours(0, 0, 0, 0); + const today = useMemo(() => { + const d = new Date(); + d.setHours(0, 0, 0, 0); + return d; + }, []); - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); + const yesterday = useMemo(() => { + const d = new Date(); + d.setDate(d.getDate() - 1); + return d; + }, []); - const isSameDay = (dateStr) => { + const isSameDay = useCallback((dateStr) => { if (!dateStr) return false; const d = new Date(dateStr); d.setHours(0, 0, 0, 0); return d.getTime() === today.getTime(); - }; + }, [today]); - const isBeforeToday = (dateStr) => { + const isBeforeToday = useCallback((dateStr) => { if (!dateStr) return false; const d = new Date(dateStr); d.setHours(0, 0, 0, 0); return d.getTime() < today.getTime(); - }; + }, [today]); - 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 sortByName = useCallback((a, b) => { + const nameA = `${a.firstName || ""} ${a.lastName || ""}`.toLowerCase(); + const nameB = `${b.firstName || ""} ${b.lastName || ""}`.toLowerCase(); + return nameA.localeCompare(nameB); + }, []); useEffect(() => { const { startDate, endDate } = dateRange; @@ -81,11 +95,29 @@ const AttendanceLog = ({ setIsRefreshing(false); }, [dateRange, projectId, dispatch, isRefreshing]); - const filtering = (data) => { - const filteredData = showOnlyCheckout + const processedData = useMemo(() => { + let filteredData = showOnlyCheckout ? data.filter((item) => item.checkOutTime === null) : data; + // Apply search query filter + if (searchQuery) { + const lowerCaseSearchQuery = searchQuery.toLowerCase(); + filteredData = filteredData.filter((att) => { + // Construct a full name from available parts, filtering out null/undefined + const fullName = [att.firstName, att.middleName, att.lastName] + .filter(Boolean) // This removes null, undefined, or empty string parts + .join(" ") + .toLowerCase(); + + return ( + att.employeeName?.toLowerCase().includes(lowerCaseSearchQuery) || + att.employeeId?.toLowerCase().includes(lowerCaseSearchQuery) || + fullName.includes(lowerCaseSearchQuery) + ); + }); + } + const group1 = filteredData .filter((d) => d.activity === 1 && isSameDay(d.checkInTime)) .sort(sortByName); @@ -130,71 +162,65 @@ const AttendanceLog = ({ ); // Create the final sorted array - const finalData = sortedDates.flatMap((date) => groupedByDate[date]); - setProcessedData(finalData); - } - - useEffect(() => { - filtering(data) - }, [data, showOnlyCheckout]); + return sortedDates.flatMap((date) => groupedByDate[date]); + }, [data, showOnlyCheckout, searchQuery, isSameDay, isBeforeToday, sortByName]); const { currentPage, totalPages, currentItems: paginatedAttendances, paginate, - resetPage, + resetPage, // Destructure resetPage here } = usePagination(processedData, 20); - // Reset to the first page whenever processedData changes (due to switch on/off) + // Reset page when processedData changes (due to filters/search) useEffect(() => { resetPage(); - }, [processedData, resetPage]); + }, [processedData, resetPage]); // Add resetPage to dependency array 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 ? msg.response.checkInTime.substring(0, 10) : null; - filtering(updatedAttendance); - resetPage(); - } - }, - [projectId, dateRange, data, filtering, resetPage] -); + if ( + projectId === msg.projectId && + checkIn && + startDate <= checkIn && + checkIn <= endDate + ) { + dispatch( + fetchAttendanceData({ + projectId, + fromDate: startDate, + toDate: endDate, + }) + ); + } + }, + [projectId, dateRange, dispatch] + ); - 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({ + projectId, + fromDate: startDate, + toDate: endDate, + }) + ); }, - [projectId, dateRange,data] + [projectId, dateRange, dispatch] ); - useEffect(() => { + useEffect(() => { eventBus.on("employee", employeeHandler); return () => eventBus.off("employee", employeeHandler); }, [employeeHandler]); @@ -224,9 +250,8 @@ const AttendanceLog = ({
setIsRefreshing(true)} /> @@ -234,9 +259,9 @@ const AttendanceLog = ({
- {data && data.length > 0 && ( + {processedData && processedData.length > 0 ? ( @@ -269,9 +294,9 @@ const AttendanceLog = ({ const previousAttendance = arr[index - 1]; const previousDate = previousAttendance ? moment( - previousAttendance.checkInTime || - previousAttendance.checkOutTime - ).format("YYYY-MM-DD") + previousAttendance.checkInTime || + previousAttendance.checkOutTime + ).format("YYYY-MM-DD") : null; if (!previousDate || currentDate !== previousDate) { @@ -289,7 +314,7 @@ const AttendanceLog = ({ ); } acc.push( - +
- )} - {!loading && !isRefreshing && data.length === 0 && ( - No employee logs - )} - {/* {error && !loading && !isRefreshing && ( - - {error} - - )} */} + ) : ( + !loading && + !isRefreshing && ( +
+ No employee logs. +
+ ) + ) + }
- {!loading && !isRefreshing && processedData.length > 10 && ( + {!loading && !isRefreshing && processedData.length > 20 && (