To implement pagination similar to ngx-pagination
in Angular. Here's how you can modify your EmployeesList
component in React using react-paginate
for efficient pagination:
Steps:
- Install
react-paginate
: Install
lodash
(for efficient filtering)
npm install lodash
Update your component to include pagination.
What’s Added?
✅ Pagination using react-paginate
.
✅ Configurable items per page (change employeesPerPage
).
✅ Page navigation (next/previous buttons).
✅ Search + Pagination (works together).
✅ State Management using useState
for pagination.
✅ Lodash debounce for search optimization.
Uses lodash.debounce
(Prevents excessive re-renders).
Updated EmployeesList.js
with Pagination:
import { useEffect, useState,useCallback } from "react";
import axios from "axios";
import { useNavigate, Link } from "react-router-dom";
import { debounce } from 'lodash';
import ReactPaginate from "react-paginate"; // Pagination component
const API_URL = "http://localhost:9090/api/employees";
const EmployeesList = () => {
const [employees, setEmployees] = useState([]);
const [filteredEmployees, setFilteredEmployees] = useState([]); // Filtered results
const [search, setSearch] = useState("");
const [page, setPage] = useState(1);
const [pageNumber, setPageNumber] = useState(0);
const employeesPerPage = 5; // Items per page
const navigate = useNavigate();
// Fetch employees on page load & when search/page changes
useEffect(() => {
fetchEmployees();
}, []);
const fetchEmployees = async () => {
try {
const response = await axios.get(API_URL);
console.log("API Response:", response.data);
if (Array.isArray(response.data)) {
setEmployees(response.data);
setFilteredEmployees(response.data); // Default to all employees
}
} catch (error) {
console.error("Error fetching employees:", error);
setEmployees([]);
setFilteredEmployees([]);
}
};
// Debounced Search Handler
const handleSearchChange = useCallback(
debounce((value) => {
setSearch(value);
if (!value) {
setFilteredEmployees(employees);
} else {
setFilteredEmployees(
employees.filter((emp) =>
emp.empName.toLowerCase().includes(value.toLowerCase()) ||
emp.empPhoneNumber.includes(value) // Check phone number
)
);
}
}, 300),
[employees]
);
// Handle disabling an employee
const handleDisable = async (empId) => {
if (window.confirm("Are you sure you want to disable this employee?")) {
try {
await axios.put(`${API_URL}/disable/${empId}`, { empId });
fetchEmployees(); // Refresh the list
} catch (error) {
console.error("Error disabling employee:", error);
}
}
};
const handleToggle = async (empId, currentStatus) => {
const newStatus = !currentStatus; // Toggle active/inactive
//if (window.confirm(`Are you sure you want to ${newStatus ? "enable" : "disable"} this employee?`)) {
try {
await axios.put(`${API_URL}/disable/${empId}/${newStatus}`);
fetchEmployees(); // Refresh the list after update
} catch (error) {
console.error("Error updating employee status:", error);
}
//}
};
// Pagination logic
const pagesVisited = pageNumber * employeesPerPage;
const displayEmployees = filteredEmployees.slice(pagesVisited, pagesVisited + employeesPerPage);
const pageCount = Math.ceil(filteredEmployees.length / employeesPerPage);
const changePage = ({ selected }) => {
setPageNumber(selected);
};
return (
<div className="container-fluid mt-4">
<div className="card shadow p-4">
<h2 className="text-center mb-4 text-primary">Employee List</h2>
{/* Search & Add Button */}
<div className="d-flex justify-content-between align-items-center mb-3">
<div className="input-group w-50">
<span className="input-group-text">
<i className="fas fa-search"></i>
</span>
<input
type="text"
className="form-control"
placeholder="Search by name..."
defaultValue={search}
onChange={(e) => handleSearchChange(e.target.value)}
/>
</div>
<Link to="/add" className="btn btn-success">
<i className="fas fa-user-plus"></i> Add Employee
</Link>
</div>
{/* Employee Table */}
<table className="table table-striped table-hover">
<thead className="table-dark">
<tr>
<th>Name</th>
<th>Designation</th>
<th>Date of Join</th>
<th>Contact</th>
<th>Department</th>
<th>In Service</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{displayEmployees.length > 0 ? (
displayEmployees.map((emp) => (
<tr key={emp.empId}>
<td>{emp.empName}</td>
<td>{emp.designation}</td>
<td>
{new Date(emp.dateOfJoining).toLocaleDateString('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric',
})}
</td>
<td>{emp.empPhoneNumber}</td>
<td>{emp.department?.deptName}</td>
<td>
{/* <button
className={`btn btn-sm ${emp.isActive ? "btn-success" : "btn-danger"}`}
onClick={() => handleToggle(emp.empId, emp.isActive)} // Ensure correct property name
>
{emp.isActive ? "Active" : "Inactive"}
</button> */}
<div className="form-check form-switch">
<input
className="form-check-input"
type="checkbox"
id={`toggle-${emp.empId}`}
checked={emp.isActive}
onChange={() => handleToggle(emp.empId, emp.isActive)}
style={{
backgroundColor: emp.isActive ? "#28a745" : "#dc3545", // Green when Active, Red when Inactive
borderColor: emp.isActive ? "#28a745" : "#dc3545",
appearance: "none", // Remove default styles
width: "2.5rem", // Custom width
height: "1.4rem", // Custom height
borderRadius: "50px", // Make it rounded
position: "relative",
cursor: "pointer",
transition: "background-color 0.3s ease-in-out",
}}
/>
{/* <label className="form-check-label" htmlFor={`toggle-${emp.empId}`}>
{emp.isActive ? "Active" : "Inactive"}
</label> */}
</div>
</td>
<td>
{/* Edit Button */}
<Link to={`/edit/${emp.empId}`} className="btn btn-primary btn-sm me-2">
<i className="fas fa-edit"></i> Edit
</Link>
{/* Disable Button */}
{/* <button className="btn btn-danger btn-sm" onClick={() => handleDisable(emp.empId)}>
<i className="fas fa-user-slash"></i> Disable
</button> */}
</td>
</tr>
))
) : (
<tr>
<td colSpan="6" className="text-center text-muted">No employees found.</td>
</tr>
)}
</tbody>
</table>
{/* Pagination Controls
<div className="d-flex justify-content-between mt-3">
<button className="btn btn-secondary" onClick={() => setPage(page - 1)} disabled={page === 1}>
<i className="fas fa-arrow-left"></i> Previous
</button>
<span className="align-self-center">Page {page} of {totalPages}</span>
<button className="btn btn-secondary" onClick={() => setPage(page + 1)} disabled={page >= totalPages}>
Next <i className="fas fa-arrow-right"></i>
</button>
</div> */}
{/* Pagination Component */}
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"pagination justify-content-end"}
previousLinkClassName={"page-link"}
nextLinkClassName={"page-link"}
pageClassName={"page-item"}
pageLinkClassName={"page-link"}
activeClassName={"active"}
/>
</div>
</div>
);
};
export default EmployeesList;
No comments:
Post a Comment