Merge branch 'pramod_Task#185' into Issue_May_2W
This commit is contained in:
commit
b885c9a8b9
@ -1,54 +1,116 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from "react";
|
||||||
import { useEmployeeAttendacesLog } from '../../hooks/useAttendance';
|
import { useEmployeeAttendacesLog } from "../../hooks/useAttendance";
|
||||||
import { convertShortTime } from '../../utils/dateUtils';
|
import { convertShortTime } from "../../utils/dateUtils";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
const AttendLogs = ({ Id }) => {
|
const AttendLogs = ({ Id }) => {
|
||||||
|
const { logs, loading } = useEmployeeAttendacesLog(Id);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const {logs, loading} = useEmployeeAttendacesLog( Id )
|
const whichActivityPerform = (actvity) => {
|
||||||
|
|
||||||
const whichActivityPerform = ( actvity ) =>
|
|
||||||
{
|
|
||||||
switch (actvity) {
|
switch (actvity) {
|
||||||
case 1:
|
case 1:
|
||||||
return "IN"
|
return (
|
||||||
|
<i
|
||||||
|
className="bx bx-right-arrow-circle text-success"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="Check-In"
|
||||||
|
></i>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
return "Requested"
|
return (
|
||||||
|
<i
|
||||||
|
className="bx bx-help-circle text-secondary"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="regularize Requested"
|
||||||
|
></i>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
return "Deleted"
|
return (
|
||||||
|
<i
|
||||||
|
className="bx bx-x-circle text-danger"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="Request Rejected"
|
||||||
|
></i>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
return "OUT"
|
return (
|
||||||
|
<i
|
||||||
|
className="bx bx-left-arrow-circle text-danger "
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="Check-Out"
|
||||||
|
></i>
|
||||||
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
return "Regularized"
|
return (
|
||||||
|
<i
|
||||||
|
className="bx bx-check-circle text-success"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="Regularized"
|
||||||
|
></i>
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
const LocationLink = (lat, lng) => {
|
||||||
|
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"]')
|
||||||
|
);
|
||||||
|
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className="table-responsive">
|
<div className="table-responsive">
|
||||||
|
<div className="text-start">
|
||||||
|
{logs && !loading && (
|
||||||
|
<p>
|
||||||
|
Attendance for{" "}
|
||||||
|
{logs[0]?.employee?.firstName + " " + logs[0]?.employee?.lastName}{" "}
|
||||||
|
on {logs[0]?.activityTime.slice(0, 10)}{" "}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{loading && <p>Loading..</p>}
|
{loading && <p>Loading..</p>}
|
||||||
{logs && logs.length > 0 && (
|
{logs && logs.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="d-flex justify-content-start align-items-center gap-2 my-1">
|
<div className="d-flex justify-content-start align-items-center gap-2 my-1">
|
||||||
<div className="align-item-center">
|
<div className="align-item-center"></div>
|
||||||
<p className="p-0 m-0">Date : {logs[0].activityTime.slice(0,10)} </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table className="table table-sm mb-0">
|
<table className="table table-sm mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style={{width: '15%'}}>Time</th>
|
<th style={{ width: "20%" }}>Date</th>
|
||||||
<th style={{ width: '20%' }}>Activity</th>
|
<th style={{ width: "15%" }}>Time</th>
|
||||||
<th style={{ width: '20%' }}>Date</th>
|
<th style={{ width: "20%" }}>Activity</th>
|
||||||
<th style={{ width: '45%' }}>Description</th>
|
<th style={{ width: "20%" }}>Location</th>
|
||||||
|
<th style={{ width: "45%" }}>Description</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -57,9 +119,26 @@ const AttendLogs = ({ Id }) => {
|
|||||||
.sort((a, b) => b.id - a.id)
|
.sort((a, b) => b.id - a.id)
|
||||||
.map((log, index) => (
|
.map((log, index) => (
|
||||||
<tr key={index}>
|
<tr key={index}>
|
||||||
|
<td>{log.activityTime.slice(0, 10)}</td>
|
||||||
<td>{convertShortTime(log.activityTime)}</td>
|
<td>{convertShortTime(log.activityTime)}</td>
|
||||||
<td>{whichActivityPerform(log.activity)}</td>
|
<td>{whichActivityPerform(log.activity)}</td>
|
||||||
<td>{log.activityTime.slice(0, 10)}</td>
|
<td>
|
||||||
|
{log?.latitude != 0 ? (
|
||||||
|
<i
|
||||||
|
class="bx bx-location-plus text-warning cursor-pointer"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="Location"
|
||||||
|
onClick={() =>
|
||||||
|
LocationLink(log?.latitude, log?.longitude)
|
||||||
|
}
|
||||||
|
></i>
|
||||||
|
) : (
|
||||||
|
"--"
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td className="text-wrap" colSpan={3}>
|
<td className="text-wrap" colSpan={3}>
|
||||||
{log?.comment}
|
{log?.comment}
|
||||||
</td>
|
</td>
|
||||||
@ -68,13 +147,9 @@ const AttendLogs = ({ Id }) => {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</>
|
</>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AttendLogs;
|
export default AttendLogs;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -67,6 +67,7 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
|
|||||||
}
|
}
|
||||||
}, [ dateRange, projectId, isRefreshing ] );
|
}, [ dateRange, projectId, isRefreshing ] );
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@ -83,11 +84,12 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
|
|||||||
}`}
|
}`}
|
||||||
title="Refresh"
|
title="Refresh"
|
||||||
onClick={() => setIsRefreshing( !isRefreshing )}
|
onClick={() => setIsRefreshing( !isRefreshing )}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="table-responsive text-nowrap">
|
<div className="table-responsive text-nowrap">
|
||||||
{data && data.length > 0 ? (
|
{(data && data.length > 0 ) && (
|
||||||
<table className="table mb-0">
|
<table className="table mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -107,13 +109,6 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{loading && <td colSpan={5}>Loading...</td>}
|
{loading && <td colSpan={5}>Loading...</td>}
|
||||||
{error && <td colSpan={5}>{error}</td>}
|
|
||||||
{data && data.length === 0 && (
|
|
||||||
<tr>
|
|
||||||
<td colSpan={5}>No Data Found</td>
|
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{currentItems?.map( ( attendance, index ) => (
|
{currentItems?.map( ( attendance, index ) => (
|
||||||
<tr key={index}>
|
<tr key={index}>
|
||||||
<td colSpan={2}>
|
<td colSpan={2}>
|
||||||
@ -153,9 +148,11 @@ const AttendanceLog = ({ handleModalData, projectId }) => {
|
|||||||
) )}
|
) )}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
) : (
|
|
||||||
<span>No employee logs</span>
|
|
||||||
) }
|
) }
|
||||||
|
{(!loading && data.length === 0) &&
|
||||||
|
<span>No employee logs</span>
|
||||||
|
}
|
||||||
|
{error && <td colSpan={5}>{error}</td>}
|
||||||
</div>
|
</div>
|
||||||
{!loading && (
|
{!loading && (
|
||||||
<nav aria-label="Page ">
|
<nav aria-label="Page ">
|
||||||
|
|||||||
@ -5,20 +5,7 @@ const EmployeeNav = ({ onPillClick, activePill }) => {
|
|||||||
<div className="col-md-12">
|
<div className="col-md-12">
|
||||||
<div className="nav-align-top ">
|
<div className="nav-align-top ">
|
||||||
<ul className="nav nav-tabs">
|
<ul className="nav nav-tabs">
|
||||||
<li className="nav-item">
|
|
||||||
<a
|
|
||||||
className={`nav-link py-1 px-2 small ${
|
|
||||||
activePill === "account" ? "active" : ""
|
|
||||||
}`}
|
|
||||||
href="#"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault(); // Prevent page reload
|
|
||||||
onPillClick("account");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="bx bx-user bx-sm me-1_5"></i> Documents
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<a
|
<a
|
||||||
className={`nav-link py-1 px-2 small ${
|
className={`nav-link py-1 px-2 small ${
|
||||||
@ -33,6 +20,21 @@ const EmployeeNav = ({ onPillClick, activePill }) => {
|
|||||||
<i className="bx bx-group bx-sm me-1_5"></i> Attendances
|
<i className="bx bx-group bx-sm me-1_5"></i> Attendances
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li className="nav-item">
|
||||||
|
<a
|
||||||
|
className={`nav-link py-1 px-2 small ${
|
||||||
|
activePill === "account" ? "active" : ""
|
||||||
|
}`}
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault(); // Prevent page reload
|
||||||
|
onPillClick("account");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="bx bx-user bx-sm me-1_5"></i> Documents
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<a
|
<a
|
||||||
className={`nav-link py-1 px-2 small ${
|
className={`nav-link py-1 px-2 small ${
|
||||||
|
|||||||
@ -38,7 +38,6 @@ const Teams = ({ project }) => {
|
|||||||
ProjectRepository.getProjectAllocation(project.id)
|
ProjectRepository.getProjectAllocation(project.id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setEmployees(response.data);
|
setEmployees(response.data);
|
||||||
console.log(response.data);
|
|
||||||
setFilteredEmployees(response.data.filter((emp) => emp.isActive));
|
setFilteredEmployees(response.data.filter((emp) => emp.isActive));
|
||||||
setEmployeeLoading(false);
|
setEmployeeLoading(false);
|
||||||
})
|
})
|
||||||
|
|||||||
262
src/pages/employee/AttendancesEmployeeRecords.jsx
Normal file
262
src/pages/employee/AttendancesEmployeeRecords.jsx
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import moment from "moment";
|
||||||
|
import DateRangePicker from "../../components/common/DateRangePicker";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { fetchEmployeeAttendanceData } from "../../slices/apiSlice/employeeAttendanceSlice";
|
||||||
|
import usePagination from "../../hooks/usePagination";
|
||||||
|
import Avatar from "../../components/common/Avatar";
|
||||||
|
import { convertShortTime } from "../../utils/dateUtils";
|
||||||
|
import RenderAttendanceStatus from "../../components/Activities/RenderAttendanceStatus";
|
||||||
|
import AttendLogs from "../../components/Activities/AttendLogs";
|
||||||
|
|
||||||
|
const AttendancesEmployeeRecords = ({ employee }) => {
|
||||||
|
const [attendances, setAttendnaces] = useState([]);
|
||||||
|
const [selectedDate, setSelectedDate] = useState("");
|
||||||
|
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [attendanceId, setAttendanecId] = useState();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { data, loading, error } = useSelector(
|
||||||
|
(store) => store.employeeAttendance
|
||||||
|
);
|
||||||
|
|
||||||
|
const [isRefreshing, setIsRefreshing] = useState(true);
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
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(
|
||||||
|
sortedFinalList,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { startDate, endDate } = dateRange;
|
||||||
|
if (startDate && endDate) {
|
||||||
|
dispatch(
|
||||||
|
fetchEmployeeAttendanceData({
|
||||||
|
employeeId: employee,
|
||||||
|
fromDate: startDate,
|
||||||
|
toDate: endDate,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [dateRange, employee,isRefreshing]);
|
||||||
|
|
||||||
|
const openModal = (id) => {
|
||||||
|
setAttendanecId(id);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
const closeModal = () => setIsModalOpen(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`modal fade ${isModalOpen ? "show" : ""}`}
|
||||||
|
tabIndex="-1"
|
||||||
|
role="dialog"
|
||||||
|
style={{ display: isModalOpen ? "block" : "none" }}
|
||||||
|
aria-hidden={!isModalOpen}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<div
|
||||||
|
className="modal-dialog modal-md modal-simple attendance-log-modal mx-sm-auto mx-1"
|
||||||
|
role="document"
|
||||||
|
>
|
||||||
|
<div className="modal-content">
|
||||||
|
<div className="modal-body p-sm-4 p-0">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-close"
|
||||||
|
onClick={closeModal}
|
||||||
|
aria-label="Close"
|
||||||
|
></button>
|
||||||
|
|
||||||
|
<AttendLogs Id={attendanceId} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-4 py-2 " style={{ minHeight: "500px" }}>
|
||||||
|
<div
|
||||||
|
className="dataTables_length text-start py-2 d-flex justify-content-between "
|
||||||
|
id="DataTables_Table_0_length"
|
||||||
|
>
|
||||||
|
<div className="col-md-3 my-0 ">
|
||||||
|
<DateRangePicker onRangeChange={setDateRange} />
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2 m-0 text-end">
|
||||||
|
<i
|
||||||
|
className={`bx bx-refresh cursor-pointer fs-4 ${
|
||||||
|
loading ? "spin" : ""
|
||||||
|
}`}
|
||||||
|
data-toggle="tooltip"
|
||||||
|
title="Refresh"
|
||||||
|
onClick={() => setIsRefreshing(!isRefreshing)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="table-responsive text-nowrap">
|
||||||
|
{(!loading && data.length === 0) &&
|
||||||
|
<span>No employee logs</span>
|
||||||
|
}
|
||||||
|
{error && <div className="text-center">{error }</div>}
|
||||||
|
{(loading && !data ) && <div className="text-center">Loading...</div>}
|
||||||
|
{(data && data.length > 0 ) && (
|
||||||
|
<table className="table mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="border-top-1" colSpan={2}>
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th className="border-top-1">Date</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>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{currentItems?.map( ( attendance, index ) => (
|
||||||
|
<tr key={index}>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<div className="d-flex justify-content-start align-items-center">
|
||||||
|
<Avatar
|
||||||
|
firstName={attendance.firstName}
|
||||||
|
lastName={attendance.lastName}
|
||||||
|
/>
|
||||||
|
<div className="d-flex flex-column">
|
||||||
|
<a href="#" className="text-heading text-truncate">
|
||||||
|
<span className="fw-normal">
|
||||||
|
{attendance.firstName} {attendance.lastName}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{" "}
|
||||||
|
{moment( attendance.checkInTime ).format( "DD-MMM-YYYY" )}
|
||||||
|
</td>
|
||||||
|
<td>{convertShortTime( attendance.checkInTime )}</td>
|
||||||
|
<td>
|
||||||
|
{attendance.checkOutTime
|
||||||
|
? convertShortTime( attendance.checkOutTime )
|
||||||
|
: "--"}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="text-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-xs btn-secondary ms-2"
|
||||||
|
tabIndex="0"
|
||||||
|
aria-controls="DataTables_Table_0"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
onClick={() => openModal( attendance.id )}
|
||||||
|
>
|
||||||
|
View
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) )}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
{(!loading && data.length > 5) && (
|
||||||
|
<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)}
|
||||||
|
>
|
||||||
|
«
|
||||||
|
</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)}
|
||||||
|
>
|
||||||
|
»
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AttendancesEmployeeRecords;
|
||||||
@ -448,7 +448,7 @@ const EmployeeList = () => {
|
|||||||
<div className="d-flex flex-column">
|
<div className="d-flex flex-column">
|
||||||
<a
|
<a
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate(`/employee/${item.id}?for=account`)
|
navigate(`/employee/${item.id}?for=attendance`)
|
||||||
}
|
}
|
||||||
className="text-heading text-truncate cursor-pointer"
|
className="text-heading text-truncate cursor-pointer"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import EmployeeRepository from "../../repositories/EmployeeRepository";
|
|||||||
import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Avatar from "../../components/common/Avatar";
|
import Avatar from "../../components/common/Avatar";
|
||||||
|
import AttendancesEmployeeRecords from "./AttendancesEmployeeRecords";
|
||||||
const EmployeeProfile = () => {
|
const EmployeeProfile = () => {
|
||||||
|
|
||||||
const projectID = useSelector((store)=>store.localVariables.projectId)
|
const projectID = useSelector((store)=>store.localVariables.projectId)
|
||||||
@ -54,14 +55,15 @@ const EmployeeProfile = () => {
|
|||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
if (loading) return <div>Loading</div>;
|
if (loading) return <div>Loading</div>;
|
||||||
switch (activePill) {
|
switch (activePill) {
|
||||||
case "account": {
|
case "attendance": {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ComingSoonPage/>
|
<AttendancesEmployeeRecords employee={employeeId } />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case "attendance": {
|
case "dcoument": {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ComingSoonPage/>
|
<ComingSoonPage/>
|
||||||
|
|||||||
@ -143,7 +143,7 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
{!loading && (
|
{!loading && safeData.length > 20 && (
|
||||||
<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" : ""}`}>
|
||||||
|
|||||||
@ -19,7 +19,21 @@ const AttendanceRepository = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getAttendanceLogs: ( id ) => api.get( `api/attendance/log/attendance/${ id }` ),
|
getAttendanceLogs: ( id ) => api.get( `api/attendance/log/attendance/${ id }` ),
|
||||||
getRegularizeList: (id)=> api.get(`api/attendance/regularize?projectId=${id}`)
|
getRegularizeList: ( id ) => api.get( `api/attendance/regularize?projectId=${ id }` ),
|
||||||
|
|
||||||
|
getAttendanceByEmployee: ( employeeId, fromDate, toDate ) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
let url = `api/Attendance/log/employee/${ employeeId }?`
|
||||||
|
if (fromDate) {
|
||||||
|
url += `&dateFrom=${fromDate}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toDate) {
|
||||||
|
url += `&dateTo=${toDate}`;
|
||||||
|
}
|
||||||
|
return api.get(url)
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
56
src/slices/apiSlice/employeeAttendanceSlice.js
Normal file
56
src/slices/apiSlice/employeeAttendanceSlice.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
|
import AttendanceRepository from '../../repositories/AttendanceRepository';
|
||||||
|
import { markAttendance } from './attedanceLogsSlice';
|
||||||
|
|
||||||
|
export const fetchEmployeeAttendanceData = createAsyncThunk(
|
||||||
|
'employeeAttendance/fetchEmployeeAttendanceData',
|
||||||
|
async ( {employeeId, fromDate, toDate}, thunkAPI ) =>
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const response = await AttendanceRepository.getAttendanceByEmployee( employeeId, fromDate, toDate );
|
||||||
|
// return response?.data?.filter((log) => log.checkInTime !== null && log.activity !== 0);
|
||||||
|
return response.data
|
||||||
|
} catch (error) {
|
||||||
|
return thunkAPI.rejectWithValue(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const employeeAttendancesSlice = createSlice({
|
||||||
|
name: 'employeeAttendance', // Updated slice name
|
||||||
|
initialState: {
|
||||||
|
data: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
reducers: {
|
||||||
|
setEmployeeAttendanceData: (state, action) => {
|
||||||
|
state.data = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
// Fetch attendance data
|
||||||
|
.addCase(fetchEmployeeAttendanceData.pending, (state) => {
|
||||||
|
state.loading = true;
|
||||||
|
})
|
||||||
|
.addCase(fetchEmployeeAttendanceData.fulfilled, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.data = action.payload;
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
.addCase(fetchEmployeeAttendanceData.rejected, (state, action) => {
|
||||||
|
state.loading = false;
|
||||||
|
state.error = action.payload;
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setEmployeeAttendanceData } = employeeAttendancesSlice.actions;
|
||||||
|
export default employeeAttendancesSlice.reducer;
|
||||||
@ -3,6 +3,7 @@ import apiCacheReducer from "../slices/apiCacheSlice";
|
|||||||
import globalVariablesReducer from "../slices/globalVariablesSlice";
|
import globalVariablesReducer from "../slices/globalVariablesSlice";
|
||||||
import localVariableRducer from "../slices/localVariablesSlice"
|
import localVariableRducer from "../slices/localVariablesSlice"
|
||||||
import attendanceReducer from "../slices/apiSlice/attedanceLogsSlice"
|
import attendanceReducer from "../slices/apiSlice/attedanceLogsSlice"
|
||||||
|
import employeeAttendanceReducer from "../slices/apiSlice/employeeAttendanceSlice"
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@ -10,5 +11,6 @@ export const store = configureStore({
|
|||||||
globalVariables: globalVariablesReducer,
|
globalVariables: globalVariablesReducer,
|
||||||
localVariables:localVariableRducer,
|
localVariables:localVariableRducer,
|
||||||
attendanceLogs: attendanceReducer,
|
attendanceLogs: attendanceReducer,
|
||||||
|
employeeAttendance: employeeAttendanceReducer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user