import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
/**
* Employees Manager Component
*
* Provides a user interface for managing employees. Users can view, add, edit,
* and delete employees through a dynamic interface. The component interacts
* with a backend API for CRUD operations.
*
* @returns {JSX.Element} The rendered Employees component.
*/
const Employees = () => {
const VITE_BACKEND_URL = import.meta.env.VITE_BACKEND_URL;
const [employees, setEmployees] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false);
const [currentEmployee, setCurrentEmployee] = useState(null);
const [employeeForm, setEmployeeForm] = useState({
employee_name: '',
position: '',
hire_date: '',
});
const navigate = useNavigate();
/**
* Fetches employees from the backend and updates the state.
* Sorts employees by hire date before setting the state.
*
* @async
* @function loadEmployeesFromDatabase
*/
const loadEmployeesFromDatabase = async () => {
try {
const response = await fetch(`${VITE_BACKEND_URL}/api/employees/`);
if (!response.ok) {
throw new Error('Failed to fetch employees');
}
const data = await response.json();
setEmployees(data.sort((a, b) => new Date(a.hire_date) - new Date(b.hire_date)));
} catch (error) {
console.error('Error loading employees:', error);
}
};
/**
* Handles input changes in the employee form.
*
* @param {Object} e - The event object from the input field.
*/
const handleInputChange = (e) => {
const { name, value } = e.target;
setEmployeeForm((prevForm) => ({
...prevForm,
[name]: value,
}));
};
/**
* Opens the Add/Edit modal and initializes form data.
*
* @param {Object|null} employee - The employee to edit, or null for adding a new employee.
*/
const openModal = (employee = null) => {
setCurrentEmployee(employee);
setEmployeeForm(employee || { employee_name: '', position: '', hire_date: '' });
setIsModalOpen(true);
};
/**
* Closes the Add/Edit modal and resets relevant state.
*/
const closeModal = () => {
setIsModalOpen(false);
setCurrentEmployee(null);
};
/**
* Opens the delete confirmation modal for a specific employee.
*
* @param {Object} employee - The employee to delete.
*/
const openDeleteConfirm = (employee) => {
setCurrentEmployee(employee);
setIsDeleteConfirmOpen(true);
};
/**
* Closes the delete confirmation modal and resets relevant state.
*/
const closeDeleteConfirm = () => {
setIsDeleteConfirmOpen(false);
setCurrentEmployee(null);
};
/**
* Handels the submit button click to create or update an employee.
*
* @async
* @param {Object} e - The event object from the form submission.
*/
const handleSubmit = async (e) => {
e.preventDefault();
const method = currentEmployee ? 'PUT' : 'POST';
const url = currentEmployee
? `${VITE_BACKEND_URL}/api/employees/update-role`
: `${VITE_BACKEND_URL}/api/employees/create`;
try {
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(employeeForm),
});
if (!response.ok) {
throw new Error('Failed to save employee');
}
await loadEmployeesFromDatabase();
closeModal();
} catch (error) {
console.error('Error saving employee:', error);
}
};
/**
* Deletes the currently selected employee.
*
* @async
* @function handleDelete
*/
const handleDelete = async () => {
try {
const response = await fetch(`${VITE_BACKEND_URL}/api/employees/delete`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
employee_id: currentEmployee.employee_id,
}),
});
if (!response.ok) {
throw new Error('Failed to delete employee');
}
await loadEmployeesFromDatabase();
closeDeleteConfirm();
} catch (error) {
console.error('Error deleting employee:', error);
}
};
/**
* Fetches employees when the component is mounted.
*
* @useEffect
*/
useEffect(() => {
loadEmployeesFromDatabase();
}, []);
return (
<div className="flex flex-col items-center p-8 bg-gray-50 dark:dark:bg-slate-800 min-h-screen">
<button
className="fixed top-20 left-4 bg-gray-300 text-black font-bold text-2xl rounded-full w-12 h-12 flex items-center justify-center bg-opacity-75 hover:scale-110 hover:bg-gray-400 transition-transform duration-200 ease-in-out"
onClick={() => navigate(-1)}
>
{"<"}
</button>
<h1 className="text-4xl font-bold text-red-600 mb-8">Employees</h1>
<button
onClick={() => openModal()}
className="bg-green-600 text-white text-lg px-6 py-2 rounded shadow-lg hover:bg-green-500 transition duration-300 mb-4"
>
Add Employee
</button>
<div className="bg-white border border-gray-300 rounded-lg shadow-lg w-full max-w-4xl overflow-hidden">
<table className="min-w-full divide-y divide-gray-200 border-collapse">
<thead className="bg-gray-100">
<tr>
<th className="px-16 py-4 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Employee Name</th>
<th className="px-16 py-4 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Position</th>
<th className="px-16 py-4 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Hire Date</th>
<th className="px-16 py-4 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody className="bg-white dark:bg-slate-300 divide-y divide-gray-200">
{employees.length === 0 ? (
<tr>
<td colSpan="4" className="px-16 py-4 text-center text-gray-500">No employees found.</td>
</tr>
) : (
employees.map((employee) => (
<tr key={employee.employee_id} className="hover:bg-gray-100 transition duration-300">
<td className="px-16 py-4 whitespace-nowrap text-sm font-medium text-gray-900 border-b border-gray-200">{employee.employee_name}</td>
<td className="px-16 py-4 whitespace-nowrap text-sm text-gray-600 border-b border-gray-200">{employee.position}</td>
<td className="px-16 py-4 whitespace-nowrap text-sm text-gray-600 border-b border-gray-200">
{new Date(employee.hire_date).toLocaleDateString()}
</td>
<td className="px-16 py-4 whitespace-nowrap text-sm text-gray-600 border-b border-gray-200">
<button
onClick={() => openModal(employee)}
className="bg-blue-500 text-white px-4 py-1 rounded hover:bg-blue-400 transition duration-300 mr-2"
>
Edit
</button>
<button
onClick={() => openDeleteConfirm(employee)}
className="bg-red-500 text-white px-4 py-1 rounded hover:bg-red-400 transition duration-300"
>
Delete
</button>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
{isModalOpen && (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white p-6 rounded shadow-lg max-w-sm w-full">
<h2 className="text-xl font-bold mb-4">{currentEmployee ? 'Edit Employee' : 'Add Employee'}</h2>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label className="block text-gray-700">Employee Name</label>
<input
type="text"
name="employee_name"
value={employeeForm.employee_name}
onChange={handleInputChange}
required
className="border rounded w-full p-2"
/>
</div>
<div className="mb-4">
<label className="block text-gray-700">Position</label>
<select
name="position"
value={employeeForm.position}
onChange={handleInputChange}
required
className="border rounded w-full p-2"
>
<option value="" disabled>Select Position</option>
<option value="Cashier">Cashier</option>
<option value="Manager">Manager</option>
</select>
</div>
<div className="flex justify-between">
<button type="button" onClick={closeModal} className="bg-red-500 text-white px-4 py-1 rounded hover:bg-red-400 transition duration-300">
Cancel
</button>
<button type="submit" className="bg-blue-500 text-white px-4 py-1 rounded hover:bg-blue-400 transition duration-300">
{currentEmployee ? 'Update' : 'Add'}
</button>
</div>
</form>
</div>
</div>
)}
{isDeleteConfirmOpen && (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white p-6 rounded shadow-lg max-w-sm w-full">
<h2 className="text-xl font-bold mb-4">Confirm Deletion</h2>
<p>Are you sure you want to delete {currentEmployee?.employee_name}?</p>
<div className="flex justify-between mt-4">
<button type="button" onClick={closeDeleteConfirm} className="bg-red-500 text-white px-4 py-1 rounded hover:bg-red-400 transition duration-300">
Cancel
</button>
<button onClick={handleDelete} className="bg-blue-500 text-white px-4 py-1 rounded hover:bg-blue-400 transition duration-300">
Delete
</button>
</div>
</div>
</div>
)}
</div>
);
};
export default Employees;