Files
EmployeeManagementSystem/src/pages/UsersPage.tsx

1228 lines
46 KiB
TypeScript

import React, { useState } from "react";
import {
AlertTriangle,
Edit,
Plus,
RefreshCw,
Save,
Search,
Trash2,
UserX,
X,
} from "lucide-react";
import { Card, CardContent } from "../components/ui/Card.tsx";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../components/ui/Table.tsx";
import { Button } from "../components/ui/Button.tsx";
import { Input, PasswordInput, Select } from "../components/ui/Input.tsx";
import { useEmployees } from "../hooks/useEmployees.ts";
import { useDepartments } from "../hooks/useDepartments.ts";
import { useAuth } from "../contexts/authContext.ts";
import { api } from "../services/api.ts";
export const UsersPage: React.FC = () => {
const [activeTab, setActiveTab] = useState<
"list" | "add" | "edit" | "delete"
>("list");
const [filterRole, setFilterRole] = useState("");
const [filterDept, setFilterDept] = useState("");
const [searchQuery, setSearchQuery] = useState("");
const {
employees,
loading,
error,
refresh,
createEmployee,
deleteEmployee,
updateEmployee,
} = useEmployees();
const { departments } = useDepartments();
const { user: currentUser } = useAuth();
// Form state
const [formData, setFormData] = useState({
username: "",
password: "",
confirmPassword: "",
name: "",
email: "",
role: "Employee",
departmentId: "",
contractorId: "",
isActive: true,
// New fields
phoneNumber: "",
aadharNumber: "",
bankAccountNumber: "",
bankName: "",
bankIfsc: "",
// Contractor-specific
contractorAgreementNumber: "",
pfNumber: "",
esicNumber: "",
});
const [formError, setFormError] = useState("");
const [formLoading, setFormLoading] = useState(false);
const [contractors, setContractors] = useState<any[]>([]);
const [editingUserId, setEditingUserId] = useState<number | null>(null);
// Load contractors when role is Employee
React.useEffect(() => {
if (formData.role === "Employee") {
api.getUsers({ role: "Contractor" }).then(setContractors).catch(
console.error,
);
}
}, [formData.role]);
// Check if current user can manage users
const canManageUsers = currentUser?.role === "SuperAdmin" ||
currentUser?.role === "Supervisor";
const isSupervisor = currentUser?.role === "Supervisor";
// Filter departments for supervisors (only show their department)
const filteredDepartments = isSupervisor
? departments.filter((d) => d.id === currentUser?.department_id)
: departments;
const roleOptions = [
{ value: "", label: "All Roles" },
{ value: "SuperAdmin", label: "Super Admin" },
{ value: "Supervisor", label: "Supervisor" },
{ value: "Contractor", label: "Contractor" },
{ value: "Employee", label: "Employee" },
];
const deptOptions = isSupervisor
? [{
value: String(currentUser?.department_id),
label: filteredDepartments[0]?.name || "My Department",
}]
: [
{ value: "", label: "All Departments" },
...departments.map((d) => ({ value: String(d.id), label: d.name })),
];
const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>,
) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
setFormError("");
};
const handleCreateUser = async () => {
// Validation
if (
!formData.username || !formData.password || !formData.name ||
!formData.email
) {
setFormError("Please fill in all required fields");
return;
}
if (formData.password !== formData.confirmPassword) {
setFormError("Passwords do not match");
return;
}
if (formData.password.length < 6) {
setFormError("Password must be at least 6 characters");
return;
}
setFormLoading(true);
setFormError("");
try {
await createEmployee({
username: formData.username,
password: formData.password,
name: formData.name,
email: formData.email,
role: formData.role,
departmentId: formData.departmentId
? parseInt(formData.departmentId)
: null,
contractorId: formData.contractorId
? parseInt(formData.contractorId)
: null,
// New fields
phoneNumber: formData.phoneNumber || null,
aadharNumber: formData.aadharNumber || null,
bankAccountNumber: formData.bankAccountNumber || null,
bankName: formData.bankName || null,
bankIfsc: formData.bankIfsc || null,
// Contractor-specific
contractorAgreementNumber: formData.contractorAgreementNumber || null,
pfNumber: formData.pfNumber || null,
esicNumber: formData.esicNumber || null,
});
// Reset form and switch to list
setFormData({
username: "",
password: "",
confirmPassword: "",
name: "",
email: "",
role: "Employee",
departmentId: "",
contractorId: "",
isActive: true,
phoneNumber: "",
aadharNumber: "",
bankAccountNumber: "",
bankName: "",
bankIfsc: "",
contractorAgreementNumber: "",
pfNumber: "",
esicNumber: "",
});
setActiveTab("list");
refresh();
} catch (err: any) {
setFormError(err.message || "Failed to create user");
} finally {
setFormLoading(false);
}
};
const handleDeleteUser = async (id: number, username: string) => {
if (!confirm(`Are you sure you want to delete user "${username}"?`)) return;
try {
await deleteEmployee(id);
refresh();
} catch (err: any) {
alert(err.message || "Failed to delete user");
}
};
const handleEditUser = (user: any) => {
setFormData({
username: user.username,
password: "",
confirmPassword: "",
name: user.name,
email: user.email,
role: user.role,
departmentId: user.department_id ? String(user.department_id) : "",
contractorId: user.contractor_id ? String(user.contractor_id) : "",
isActive: user.is_active,
phoneNumber: user.phone_number || "",
aadharNumber: user.aadhar_number || "",
bankAccountNumber: user.bank_account_number || "",
bankName: user.bank_name || "",
bankIfsc: user.bank_ifsc || "",
contractorAgreementNumber: user.contractor_agreement_number || "",
pfNumber: user.pf_number || "",
esicNumber: user.esic_number || "",
});
setEditingUserId(user.id);
setActiveTab("edit");
setFormError("");
};
const handleUpdateUser = async () => {
if (!formData.name || !formData.email) {
setFormError("Please fill in all required fields");
return;
}
setFormLoading(true);
setFormError("");
try {
await updateEmployee(editingUserId!, {
name: formData.name,
email: formData.email,
role: formData.role,
departmentId: formData.departmentId
? parseInt(formData.departmentId)
: null,
contractorId: formData.contractorId
? parseInt(formData.contractorId)
: null,
isActive: formData.isActive,
// New fields
phoneNumber: formData.phoneNumber || null,
aadharNumber: formData.aadharNumber || null,
bankAccountNumber: formData.bankAccountNumber || null,
bankName: formData.bankName || null,
bankIfsc: formData.bankIfsc || null,
contractorAgreementNumber: formData.contractorAgreementNumber || null,
pfNumber: formData.pfNumber || null,
esicNumber: formData.esicNumber || null,
});
resetForm();
setActiveTab("list");
refresh();
} catch (err: any) {
setFormError(err.message || "Failed to update user");
} finally {
setFormLoading(false);
}
};
const resetForm = () => {
setFormData({
username: "",
password: "",
confirmPassword: "",
name: "",
email: "",
role: "Employee",
departmentId: "",
contractorId: "",
isActive: true,
phoneNumber: "",
aadharNumber: "",
bankAccountNumber: "",
bankName: "",
bankIfsc: "",
contractorAgreementNumber: "",
pfNumber: "",
esicNumber: "",
});
setEditingUserId(null);
setFormError("");
};
// Auto-set filter for supervisors
React.useEffect(() => {
if (isSupervisor && currentUser?.department_id) {
setFilterDept(String(currentUser.department_id));
}
}, [isSupervisor, currentUser?.department_id]);
// Filter employees
const filteredEmployees = employees.filter((emp) => {
// Search filter
if (searchQuery) {
const query = searchQuery.toLowerCase();
const matchesSearch = emp.name?.toLowerCase().includes(query) ||
emp.username?.toLowerCase().includes(query) ||
emp.email?.toLowerCase().includes(query) ||
emp.role?.toLowerCase().includes(query);
if (!matchesSearch) return false;
}
if (filterRole && emp.role !== filterRole) return false;
// For supervisors, always filter by their department
if (isSupervisor && currentUser?.department_id) {
if (emp.department_id !== currentUser.department_id) return false;
} else if (filterDept && emp.department_id !== parseInt(filterDept)) {
return false;
}
return true;
});
return (
<div className="p-6">
<Card>
<div className="border-b border-gray-200">
<div className="flex space-x-8 px-6">
<button
onClick={() => {
setActiveTab("list");
resetForm();
}}
className={`py-4 px-2 border-b-2 font-medium text-sm ${
activeTab === "list"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
User List
</button>
{canManageUsers && (
<>
<button
onClick={() => {
setActiveTab("add");
resetForm();
}}
className={`py-4 px-2 border-b-2 font-medium text-sm ${
activeTab === "add"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
Add User
</button>
<button
onClick={() => setActiveTab("edit")}
className={`py-4 px-2 border-b-2 font-medium text-sm ${
activeTab === "edit"
? "border-blue-500 text-blue-600"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
Edit User
</button>
<button
onClick={() => setActiveTab("delete")}
className={`py-4 px-2 border-b-2 font-medium text-sm ${
activeTab === "delete"
? "border-red-500 text-red-600"
: "border-transparent text-gray-500 hover:text-gray-700"
}`}
>
Delete Users
</button>
</>
)}
</div>
</div>
<CardContent>
{activeTab === "list" && (
<div>
<div className="flex gap-4 mb-6">
<div className="relative min-w-[300px] flex-1">
<Search
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
size={18}
/>
<input
type="text"
placeholder="Search users by name, username, email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<Select
options={deptOptions}
className="w-48 flex-shrink-0"
value={filterDept}
onChange={(e) => setFilterDept(e.target.value)}
disabled={isSupervisor}
/>
<Select
options={roleOptions}
className="w-48 flex-shrink-0"
value={filterRole}
onChange={(e) => setFilterRole(e.target.value)}
/>
<Button variant="ghost" onClick={refresh}>
<RefreshCw size={16} className="mr-2" />
Refresh
</Button>
</div>
<div className="mb-4 text-sm text-gray-600">
Total Users: {filteredEmployees.length}
</div>
{error && (
<div className="text-center py-8 text-red-600">
Error: {error}
</div>
)}
{loading
? <div className="text-center py-8">Loading...</div>
: filteredEmployees.length > 0
? (
<Table>
<TableHeader>
<TableHead>ID</TableHead>
<TableHead>USERNAME</TableHead>
<TableHead>FULL NAME</TableHead>
<TableHead>EMAIL</TableHead>
<TableHead>ROLE</TableHead>
<TableHead>DEPARTMENT</TableHead>
<TableHead>REPORTS TO</TableHead>
<TableHead>STATUS</TableHead>
<TableHead>ACTIONS</TableHead>
</TableHeader>
<TableBody>
{filteredEmployees.map((user) => {
// Find supervisor for contractors (supervisor in same department)
const getSupervisorName = () => {
if (user.role !== "Contractor") return null;
const supervisor = employees.find(
(e) =>
e.role === "Supervisor" &&
e.department_id === user.department_id,
);
return supervisor?.name || null;
};
// Get reports to info based on role
const getReportsTo = () => {
if (user.role === "Employee") {
return user.contractor_name
? (
<span className="text-orange-600">
{user.contractor_name}
</span>
)
: "-";
}
if (user.role === "Contractor") {
const supervisorName = getSupervisorName();
return supervisorName
? (
<span className="text-blue-600">
{supervisorName}
</span>
)
: "-";
}
return "-";
};
return (
<TableRow key={user.id}>
<TableCell>{user.id}</TableCell>
<TableCell className="text-blue-600">
{user.username}
</TableCell>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>
<span
className={`px-2 py-1 rounded text-xs font-medium ${
user.role === "SuperAdmin"
? "bg-purple-100 text-purple-700"
: user.role === "Supervisor"
? "bg-blue-100 text-blue-700"
: user.role === "Contractor"
? "bg-orange-100 text-orange-700"
: "bg-gray-100 text-gray-700"
}`}
>
{user.role}
</span>
</TableCell>
<TableCell>{user.department_name || "-"}</TableCell>
<TableCell>{getReportsTo()}</TableCell>
<TableCell>
<span
className={`px-2 py-1 rounded text-xs font-medium ${
user.is_active
? "bg-green-100 text-green-700"
: "bg-red-100 text-red-700"
}`}
>
{user.is_active ? "Active" : "Inactive"}
</span>
</TableCell>
<TableCell>
{canManageUsers && (
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={() => handleEditUser(user)}
className="text-blue-600 hover:text-blue-800"
title="Edit"
>
<Edit size={14} />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() =>
handleDeleteUser(user.id, user.username)}
className="text-red-600 hover:text-red-800"
title="Delete"
>
<Trash2 size={14} />
</Button>
</div>
)}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
)
: !loading && (
<div className="text-center py-8 text-gray-500">
No users found
</div>
)}
</div>
)}
{activeTab === "add" && (
<div className="max-w-3xl">
{formError && (
<div className="mb-4 p-3 bg-red-100 text-red-700 rounded-md">
{formError}
</div>
)}
<h3 className="text-lg font-semibold text-gray-800 mb-6">
User Information
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Username"
name="username"
value={formData.username}
onChange={handleInputChange}
required
/>
<PasswordInput
label="Password"
name="password"
value={formData.password}
onChange={handleInputChange}
required
/>
<Input
label="Full Name"
name="name"
value={formData.name}
onChange={handleInputChange}
required
/>
<PasswordInput
label="Confirm Password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleInputChange}
required
/>
<div className="col-span-2">
<Input
label="Email"
name="email"
type="email"
value={formData.email}
onChange={handleInputChange}
required
/>
</div>
</div>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Role & Department
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Select
label="Role"
name="role"
value={formData.role}
onChange={handleInputChange}
required
options={roleOptions.slice(1)}
/>
<Select
label="Department"
name="departmentId"
value={formData.departmentId}
onChange={handleInputChange}
options={deptOptions.slice(1)}
/>
{formData.role === "Employee" && (
<Select
label="Contractor (for Employees)"
name="contractorId"
value={formData.contractorId}
onChange={handleInputChange}
options={[
{ value: "", label: "Select Contractor" },
...contractors.map((c) => ({
value: String(c.id),
label: c.name,
})),
]}
/>
)}
</div>
{/* Personal & Bank Details - for Employee and Contractor */}
{(formData.role === "Employee" ||
formData.role === "Contractor") && (
<>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Personal Details
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Phone Number"
name="phoneNumber"
value={formData.phoneNumber}
onChange={handleInputChange}
placeholder="e.g., 9876543210"
/>
<Input
label="Aadhar Card Number"
name="aadharNumber"
value={formData.aadharNumber}
onChange={handleInputChange}
placeholder="12-digit Aadhar number"
maxLength={12}
/>
</div>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Bank Details
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Bank Account Number"
name="bankAccountNumber"
value={formData.bankAccountNumber}
onChange={handleInputChange}
/>
<Input
label="Bank Name"
name="bankName"
value={formData.bankName}
onChange={handleInputChange}
/>
<Input
label="IFSC Code"
name="bankIfsc"
value={formData.bankIfsc}
onChange={handleInputChange}
placeholder="e.g., SBIN0001234"
/>
</div>
</>
)}
{/* Contractor-specific fields */}
{formData.role === "Contractor" && (
<>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Contractor Details
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Contractor Agreement Number"
name="contractorAgreementNumber"
value={formData.contractorAgreementNumber}
onChange={handleInputChange}
/>
<Input
label="PF Number"
name="pfNumber"
value={formData.pfNumber}
onChange={handleInputChange}
placeholder="Provident Fund number"
/>
<Input
label="ESIC Number"
name="esicNumber"
value={formData.esicNumber}
onChange={handleInputChange}
placeholder="ESIC registration number"
/>
</div>
</>
)}
<div className="flex justify-end gap-4">
<Button
variant="outline"
onClick={() => {
setActiveTab("list");
resetForm();
}}
>
Cancel
</Button>
<Button
size="lg"
onClick={handleCreateUser}
disabled={formLoading}
>
{formLoading
? (
"Creating..."
)
: (
<>
<Plus size={16} className="mr-2" />
Create User
</>
)}
</Button>
</div>
</div>
)}
{activeTab === "edit" && (
<div className="max-w-3xl">
{formError && (
<div className="mb-4 p-3 bg-red-100 text-red-700 rounded-md">
{formError}
</div>
)}
{!editingUserId
? (
<div>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Select User to Edit
</h3>
<Select
label="Select User"
value=""
onChange={(e) => {
const user = employees.find((emp) =>
emp.id === parseInt(e.target.value)
);
if (user) handleEditUser(user);
}}
options={[
{ value: "", label: "Choose a user to edit..." },
...employees.map((emp) => ({
value: String(emp.id),
label: `${emp.name} (${emp.username}) - ${emp.role}`,
})),
]}
/>
</div>
)
: (
<>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Edit User: {formData.username}
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Username"
name="username"
value={formData.username}
disabled
/>
<Input
label="Full Name"
name="name"
value={formData.name}
onChange={handleInputChange}
required
/>
<Input
label="Email"
name="email"
type="email"
value={formData.email}
onChange={handleInputChange}
required
/>
<Select
label="Status"
name="isActive"
value={formData.isActive ? "true" : "false"}
onChange={(e) =>
setFormData((prev) => ({
...prev,
isActive: e.target.value === "true",
}))}
options={[
{ value: "true", label: "Active" },
{ value: "false", label: "Inactive" },
]}
/>
</div>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Role & Department
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Select
label="Role"
name="role"
value={formData.role}
onChange={handleInputChange}
required
options={roleOptions.slice(1)}
/>
<Select
label="Department"
name="departmentId"
value={formData.departmentId}
onChange={handleInputChange}
options={[
{ value: "", label: "No Department" },
...departments.map((d) => ({
value: String(d.id),
label: d.name,
})),
]}
/>
{formData.role === "Employee" && (
<Select
label="Contractor (for Employees)"
name="contractorId"
value={formData.contractorId}
onChange={handleInputChange}
options={[
{ value: "", label: "Select Contractor" },
...contractors.map((c) => ({
value: String(c.id),
label: c.name,
})),
]}
/>
)}
</div>
{/* Personal & Bank Details - for Employee and Contractor */}
{(formData.role === "Employee" ||
formData.role === "Contractor") && (
<>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Personal Details
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Phone Number"
name="phoneNumber"
value={formData.phoneNumber}
onChange={handleInputChange}
placeholder="e.g., 9876543210"
/>
<Input
label="Aadhar Card Number"
name="aadharNumber"
value={formData.aadharNumber}
onChange={handleInputChange}
placeholder="12-digit Aadhar number"
maxLength={12}
/>
</div>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Bank Details
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Bank Account Number"
name="bankAccountNumber"
value={formData.bankAccountNumber}
onChange={handleInputChange}
/>
<Input
label="Bank Name"
name="bankName"
value={formData.bankName}
onChange={handleInputChange}
/>
<Input
label="IFSC Code"
name="bankIfsc"
value={formData.bankIfsc}
onChange={handleInputChange}
placeholder="e.g., SBIN0001234"
/>
</div>
</>
)}
{/* Contractor-specific fields */}
{formData.role === "Contractor" && (
<>
<h3 className="text-lg font-semibold text-gray-800 mb-6">
Contractor Details
</h3>
<div className="grid grid-cols-2 gap-6 mb-8">
<Input
label="Contractor Agreement Number"
name="contractorAgreementNumber"
value={formData.contractorAgreementNumber}
onChange={handleInputChange}
/>
<Input
label="PF Number"
name="pfNumber"
value={formData.pfNumber}
onChange={handleInputChange}
placeholder="Provident Fund number"
/>
<Input
label="ESIC Number"
name="esicNumber"
value={formData.esicNumber}
onChange={handleInputChange}
placeholder="ESIC registration number"
/>
</div>
</>
)}
<div className="flex justify-end gap-4">
<Button
variant="ghost"
onClick={() => resetForm()}
>
<X size={16} className="mr-2" />
Clear Selection
</Button>
<Button
onClick={handleUpdateUser}
disabled={formLoading}
>
{formLoading
? (
"Saving..."
)
: (
<>
<Save size={16} className="mr-2" />
Save Changes
</>
)}
</Button>
</div>
</>
)}
</div>
)}
{activeTab === "delete" && canManageUsers && (
<div>
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div className="flex items-start gap-3">
<AlertTriangle
className="text-red-500 flex-shrink-0 mt-0.5"
size={20}
/>
<div>
<h4 className="font-semibold text-red-800">
Warning: Permanent Action
</h4>
<p className="text-sm text-red-700 mt-1">
Deleting a user is permanent and cannot be undone. All
associated data will be removed.
{isSupervisor &&
" As a Supervisor, you can only delete Employees and Contractors in your department."}
</p>
</div>
</div>
</div>
<div className="flex gap-4 mb-6">
<div className="relative min-w-[300px] flex-1">
<Search
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"
size={18}
/>
<input
type="text"
placeholder="Search users to delete..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
{!isSupervisor && (
<Select
options={deptOptions}
className="w-48 flex-shrink-0"
value={filterDept}
onChange={(e) => setFilterDept(e.target.value)}
/>
)}
<Select
options={[
{ value: "", label: "All Deletable Roles" },
{ value: "Employee", label: "Employee" },
{ value: "Contractor", label: "Contractor" },
]}
className="w-48 flex-shrink-0"
value={filterRole}
onChange={(e) => setFilterRole(e.target.value)}
/>
</div>
{(() => {
// Filter users that can be deleted
const deletableUsers = employees.filter((emp) => {
// SuperAdmins and Supervisors cannot be deleted from this tab
if (emp.role === "SuperAdmin" || emp.role === "Supervisor") {
return false;
}
// Only Employees and Contractors can be deleted
if (emp.role !== "Employee" && emp.role !== "Contractor") {
return false;
}
// Supervisors can only delete users in their department
if (
isSupervisor &&
emp.department_id !== currentUser?.department_id
) return false;
// Apply search filter
if (searchQuery) {
const query = searchQuery.toLowerCase();
const matchesSearch =
emp.name?.toLowerCase().includes(query) ||
emp.username?.toLowerCase().includes(query) ||
emp.email?.toLowerCase().includes(query);
if (!matchesSearch) return false;
}
// Apply role filter
if (filterRole && emp.role !== filterRole) return false;
// Apply department filter (for SuperAdmin)
if (
!isSupervisor && filterDept &&
emp.department_id !== parseInt(filterDept)
) return false;
return true;
});
return (
<>
<div className="mb-4 text-sm text-gray-600">
Deletable Users: {deletableUsers.length}
</div>
{deletableUsers.length > 0
? (
<Table>
<TableHeader>
<TableHead>ID</TableHead>
<TableHead>USERNAME</TableHead>
<TableHead>FULL NAME</TableHead>
<TableHead>EMAIL</TableHead>
<TableHead>ROLE</TableHead>
<TableHead>DEPARTMENT</TableHead>
<TableHead>REPORTS TO</TableHead>
<TableHead>STATUS</TableHead>
<TableHead>ACTION</TableHead>
</TableHeader>
<TableBody>
{deletableUsers.map((user) => {
// Find supervisor for contractors (supervisor in same department)
const getSupervisorName = () => {
if (user.role !== "Contractor") return null;
const supervisor = employees.find(
(e) =>
e.role === "Supervisor" &&
e.department_id === user.department_id,
);
return supervisor?.name || null;
};
// Get reports to info based on role
const getReportsTo = () => {
if (user.role === "Employee") {
return user.contractor_name
? (
<span className="text-orange-600">
{user.contractor_name}
</span>
)
: "-";
}
if (user.role === "Contractor") {
const supervisorName = getSupervisorName();
return supervisorName
? (
<span className="text-blue-600">
{supervisorName}
</span>
)
: "-";
}
return "-";
};
return (
<TableRow
key={user.id}
className="hover:bg-red-50"
>
<TableCell>{user.id}</TableCell>
<TableCell className="text-blue-600">
{user.username}
</TableCell>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>
<span
className={`px-2 py-1 rounded text-xs font-medium ${
user.role === "Contractor"
? "bg-orange-100 text-orange-700"
: "bg-gray-100 text-gray-700"
}`}
>
{user.role}
</span>
</TableCell>
<TableCell>
{user.department_name || "-"}
</TableCell>
<TableCell>{getReportsTo()}</TableCell>
<TableCell>
<span
className={`px-2 py-1 rounded text-xs font-medium ${
user.is_active
? "bg-green-100 text-green-700"
: "bg-red-100 text-red-700"
}`}
>
{user.is_active ? "Active" : "Inactive"}
</span>
</TableCell>
<TableCell>
<Button
variant="outline"
size="sm"
onClick={() => {
if (
confirm(
`Are you sure you want to permanently delete "${user.name}" (${user.username})?\n\nThis action cannot be undone!`,
)
) {
deleteEmployee(user.id);
}
}}
className="text-red-600 border-red-300 hover:bg-red-50 hover:border-red-400"
>
<UserX size={14} className="mr-1" />
Delete
</Button>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
)
: (
<div className="text-center py-8 text-gray-500">
<UserX
size={48}
className="mx-auto mb-4 text-gray-300"
/>
<p>No deletable users found</p>
<p className="text-sm mt-1">
{isSupervisor
? "Only Employees and Contractors in your department can be deleted."
: "Only Employees and Contractors can be deleted from this tab."}
</p>
</div>
)}
</>
);
})()}
</div>
)}
</CardContent>
</Card>
</div>
);
};