Adding Switch button in attendence component. #129

Merged
vikas.nale merged 3 commits from Kartik_Enhancement#274 into Issue_May_4W 2025-05-24 10:17:21 +00:00
2 changed files with 100 additions and 34 deletions

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState, useMemo, useCallback } from "react";
import moment from "moment"; import moment from "moment";
import Avatar from "../common/Avatar"; import Avatar from "../common/Avatar";
import { convertShortTime } from "../../utils/dateUtils"; import { convertShortTime } from "../../utils/dateUtils";
@ -7,13 +7,28 @@ import { useSelector, useDispatch } from "react-redux";
import { fetchAttendanceData } from "../../slices/apiSlice/attedanceLogsSlice"; import { fetchAttendanceData } from "../../slices/apiSlice/attedanceLogsSlice";
import DateRangePicker from "../common/DateRangePicker"; import DateRangePicker from "../common/DateRangePicker";
import { getCachedData } from "../../slices/apiDataManager"; import { getCachedData } from "../../slices/apiDataManager";
import usePagination from "../../hooks/usePagination";
const AttendanceLog = ({ handleModalData, projectId }) => { const usePagination = (data, itemsPerPage) => {
const [currentPage, setCurrentPage] = useState(1);
const maxPage = Math.ceil(data.length / itemsPerPage);
const currentItems = useMemo(() => {
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), []);
return { currentPage, totalPages: maxPage, currentItems, paginate, resetPage };
};
const AttendanceLog = ({ handleModalData, projectId, showOnlyCheckout }) => {
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
const dispatch = useDispatch(); const dispatch = useDispatch();
const { data, loading, error } = useSelector((store) => store.attendanceLogs); const { data, loading, error } = useSelector((store) => store.attendanceLogs);
const [isRefreshing, setIsRefreshing] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false);
const [processedData, setProcessedData] = useState([]); const [processedData, setProcessedData] = useState([]);
const today = new Date(); const today = new Date();
@ -44,7 +59,6 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
useEffect(() => { useEffect(() => {
const { startDate, endDate } = dateRange; const { startDate, endDate } = dateRange;
if (startDate && endDate) {
dispatch( dispatch(
fetchAttendanceData({ fetchAttendanceData({
projectId, projectId,
@ -52,25 +66,29 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
toDate: endDate, toDate: endDate,
}) })
); );
} setIsRefreshing(false);
}, [dateRange, projectId, isRefreshing]); }, [dateRange, projectId, dispatch, isRefreshing]);
useEffect(() => { useEffect(() => {
const group1 = data const filteredData = showOnlyCheckout
? data.filter((item) => item.checkOutTime === null)
: data;
const group1 = filteredData
.filter((d) => d.activity === 1 && isSameDay(d.checkInTime)) .filter((d) => d.activity === 1 && isSameDay(d.checkInTime))
.sort(sortByName); .sort(sortByName);
const group2 = data const group2 = filteredData
.filter((d) => d.activity === 4 && isSameDay(d.checkOutTime)) .filter((d) => d.activity === 4 && isSameDay(d.checkOutTime))
.sort(sortByName); .sort(sortByName);
const group3 = data const group3 = filteredData
.filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime)) .filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime))
.sort(sortByName); .sort(sortByName);
const group4 = data const group4 = filteredData
.filter((d) => d.activity === 4 && isBeforeToday(d.checkOutTime)); .filter((d) => d.activity === 4 && isBeforeToday(d.checkOutTime));
const group5 = data const group5 = filteredData
.filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime)) .filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime))
.sort(sortByName); .sort(sortByName);
const group6 = data.filter((d) => d.activity === 5).sort(sortByName); const group6 = filteredData.filter((d) => d.activity === 5).sort(sortByName);
const sortedList = [...group1, ...group2, ...group3, ...group4, ...group5, ...group6]; const sortedList = [...group1, ...group2, ...group3, ...group4, ...group5, ...group6];
@ -90,13 +108,18 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
// Create the final sorted array // Create the final sorted array
const finalData = sortedDates.flatMap((date) => groupedByDate[date]); const finalData = sortedDates.flatMap((date) => groupedByDate[date]);
setProcessedData(finalData); setProcessedData(finalData);
}, [data]); }, [data, showOnlyCheckout]);
const { currentPage, totalPages, currentItems: paginatedAttendances, paginate } = usePagination( const { currentPage, totalPages, currentItems: paginatedAttendances, paginate, resetPage } = usePagination(
processedData, processedData,
10 10
); );
// Reset to the first page whenever processedData changes (due to switch on/off)
useEffect(() => {
resetPage();
}, [processedData, resetPage]);
return ( return (
<> <>
<div <div
@ -108,10 +131,10 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
</div> </div>
<div className="col-md-2 m-0 text-end"> <div className="col-md-2 m-0 text-end">
<i <i
className={`bx bx-refresh cursor-pointer fs-4 ${loading ? "spin" : "" className={`bx bx-refresh cursor-pointer fs-4 ${loading || isRefreshing ? "spin" : ""
}`} }`}
title="Refresh" title="Refresh"
onClick={() => setIsRefreshing(!isRefreshing)} onClick={() => setIsRefreshing(true)}
/> />
</div> </div>
</div> </div>
@ -134,8 +157,12 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{loading && <td colSpan={5}>Loading...</td>} {(loading || isRefreshing) && (
{paginatedAttendances.reduce((acc, attendance, index, arr) => { <tr>
<td colSpan={6}>Loading...</td>
</tr>
)}
{!loading && !isRefreshing && paginatedAttendances.reduce((acc, attendance, index, arr) => {
const currentDate = moment(attendance.checkInTime || attendance.checkOutTime).format("YYYY-MM-DD"); const currentDate = moment(attendance.checkInTime || attendance.checkOutTime).format("YYYY-MM-DD");
const previousAttendance = arr[index - 1]; const previousAttendance = arr[index - 1];
const previousDate = previousAttendance ? moment(previousAttendance.checkInTime || previousAttendance.checkOutTime).format("YYYY-MM-DD") : null; const previousDate = previousAttendance ? moment(previousAttendance.checkInTime || previousAttendance.checkOutTime).format("YYYY-MM-DD") : null;
@ -191,10 +218,14 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
</tbody> </tbody>
</table> </table>
)} )}
{!loading && data.length === 0 && <span>No employee logs</span>} {!loading && !isRefreshing && data.length === 0 && <span>No employee logs</span>}
{error && <td colSpan={6}>{error}</td>} {error && !loading && !isRefreshing && (
<tr>
<td colSpan={6}>{error}</td>
</tr>
)}
</div> </div>
{!loading && processedData.length > 10 && ( {!loading && !isRefreshing && processedData.length > 10 && (
<nav aria-label="Page "> <nav aria-label="Page ">
<ul className="pagination pagination-sm justify-content-end py-1"> <ul className="pagination pagination-sm justify-content-end py-1">
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}> <li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>

View File

@ -21,7 +21,7 @@ import { REGULARIZE_ATTENDANCE } from "../../utils/constants";
const AttendancePage = () => { const AttendancePage = () => {
const [activeTab, setActiveTab] = useState("all"); const [activeTab, setActiveTab] = useState("all");
const [showOnlyCheckout, setShowOnlyCheckout] = useState(false);
const loginUser = getCachedProfileData(); const loginUser = getCachedProfileData();
var selectedProject = useSelector((store) => store.localVariables.projectId); var selectedProject = useSelector((store) => store.localVariables.projectId);
const { projects, loading: projectLoading } = useProjects(); const { projects, loading: projectLoading } = useProjects();
@ -86,6 +86,10 @@ const AttendancePage = () => {
}); });
}; };
const handleToggle = (event) => {
setShowOnlyCheckout(event.target.checked);
};
useEffect(() => { useEffect(() => {
if (modelConfig !== null) { if (modelConfig !== null) {
openModel(); openModel();
@ -100,6 +104,17 @@ const AttendancePage = () => {
dispatch(setProjectId(loginUser?.projects[0])); dispatch(setProjectId(loginUser?.projects[0]));
} }
}, [selectedProject, loginUser?.projects]); }, [selectedProject, loginUser?.projects]);
// Filter attendance data based on the toggle
// const filteredAttendance = showOnlyCheckout
// ? attendances?.filter(
// (att) => att?.checkOutTime !== null && att?.checkInTime !== null
// )
// : attendances;
const filteredAttendance = showOnlyCheckout
? attendances?.filter((att) => att?.checkOutTime === null)
: attendances;
return ( return (
<> <>
{isCreateModalOpen && modelConfig && ( {isCreateModalOpen && modelConfig && (
@ -198,6 +213,26 @@ const AttendancePage = () => {
Regularization Regularization
</button> </button>
</li> </li>
<li
className={`nav-item ms-auto ${
activeTab === "regularization" ? "d-none" : ""
}`}
>
<label className="switch switch-primary">
<input
type="checkbox"
className="switch-input"
checked={showOnlyCheckout}
onChange={handleToggle}
/>
<span className="switch-toggle-slider">
<span className="switch-on"></span>
<span className="switch-off"></span>
</span>
<span className="switch-label m-2">Pending Actions</span>
</label>
</li>
</ul> </ul>
<div className="tab-content attedanceTabs py-2"> <div className="tab-content attedanceTabs py-2">
{projectLoading && <span>Loading..</span>} {projectLoading && <span>Loading..</span>}
@ -205,12 +240,12 @@ const AttendancePage = () => {
{activeTab === "all" && ( {activeTab === "all" && (
<> <>
{!projectLoading && attendances.length === 0 && ( {!projectLoading && filteredAttendance?.length === 0 && (
<p>No Employee assigned yet.</p> <p>No Employee assigned yet.</p>
)} )}
<div className="tab-pane fade show active py-0"> <div className="tab-pane fade show active py-0">
<Attendance <Attendance
attendance={attendances} attendance={filteredAttendance}
handleModalData={handleModalData} handleModalData={handleModalData}
getRole={getRole} getRole={getRole}
/> />
@ -218,12 +253,12 @@ const AttendancePage = () => {
</> </>
)} )}
{activeTab === "logs" && ( {activeTab === "logs" && (
<div className="tab-pane fade show active py-0"> <div className="tab-pane fade show active py-0">
<AttendanceLog <AttendanceLog
handleModalData={handleModalData} handleModalData={handleModalData}
projectId={selectedProject} projectId={selectedProject}
showOnlyCheckout={showOnlyCheckout}
/> />
</div> </div>
)} )}