(Feat): More changes

This commit is contained in:
2025-11-28 19:04:35 +00:00
parent 25ed1d5c56
commit 8ac2eb1944
42 changed files with 3291 additions and 3407 deletions

View File

@@ -1,9 +1,9 @@
import React, { useState, useMemo } from 'react';
import React, { useState } from 'react';
import { RefreshCw, Plus, Trash2, Edit, Save, X, Search, AlertTriangle, UserX } from 'lucide-react';
import { Card, CardHeader, CardContent } from '../components/ui/Card';
import { Card, CardContent } from '../components/ui/Card';
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '../components/ui/Table';
import { Button } from '../components/ui/Button';
import { Input, Select } from '../components/ui/Input';
import { Input, Select, PasswordInput } from '../components/ui/Input';
import { useEmployees } from '../hooks/useEmployees';
import { useDepartments } from '../hooks/useDepartments';
import { useAuth } from '../contexts/AuthContext';
@@ -29,6 +29,16 @@ export const UsersPage: React.FC = () => {
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);
@@ -99,6 +109,16 @@ export const UsersPage: React.FC = () => {
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
@@ -111,6 +131,15 @@ export const UsersPage: React.FC = () => {
role: 'Employee',
departmentId: '',
contractorId: '',
isActive: true,
phoneNumber: '',
aadharNumber: '',
bankAccountNumber: '',
bankName: '',
bankIfsc: '',
contractorAgreementNumber: '',
pfNumber: '',
esicNumber: '',
});
setActiveTab('list');
refresh();
@@ -143,6 +172,15 @@ export const UsersPage: React.FC = () => {
departmentId: user.department_id ? String(user.department_id) : '',
contractorId: user.contractor_id ? String(user.contractor_id) : '',
isActive: user.is_active,
// New fields
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');
@@ -166,6 +204,15 @@ export const UsersPage: React.FC = () => {
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();
@@ -189,6 +236,14 @@ export const UsersPage: React.FC = () => {
departmentId: '',
contractorId: '',
isActive: true,
phoneNumber: '',
aadharNumber: '',
bankAccountNumber: '',
bankName: '',
bankIfsc: '',
contractorAgreementNumber: '',
pfNumber: '',
esicNumber: '',
});
setEditingUserId(null);
setFormError('');
@@ -329,60 +384,89 @@ export const UsersPage: React.FC = () => {
<TableHead>EMAIL</TableHead>
<TableHead>ROLE</TableHead>
<TableHead>DEPARTMENT</TableHead>
<TableHead>REPORTS TO</TableHead>
<TableHead>STATUS</TableHead>
<TableHead>ACTIONS</TableHead>
</TableHeader>
<TableBody>
{filteredEmployees.map((user) => (
<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>
<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>
))}
{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 && (
@@ -410,10 +494,9 @@ export const UsersPage: React.FC = () => {
onChange={handleInputChange}
required
/>
<Input
<PasswordInput
label="Password"
name="password"
type="password"
value={formData.password}
onChange={handleInputChange}
required
@@ -425,10 +508,9 @@ export const UsersPage: React.FC = () => {
onChange={handleInputChange}
required
/>
<Input
<PasswordInput
label="Confirm Password"
name="confirmPassword"
type="password"
value={formData.confirmPassword}
onChange={handleInputChange}
required
@@ -476,6 +558,82 @@ export const UsersPage: React.FC = () => {
)}
</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"
@@ -599,6 +757,82 @@ export const UsersPage: React.FC = () => {
)}
</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"
@@ -718,49 +952,78 @@ export const UsersPage: React.FC = () => {
<TableHead>EMAIL</TableHead>
<TableHead>ROLE</TableHead>
<TableHead>DEPARTMENT</TableHead>
<TableHead>REPORTS TO</TableHead>
<TableHead>STATUS</TableHead>
<TableHead>ACTION</TableHead>
</TableHeader>
<TableBody>
{deletableUsers.map((user) => (
<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>
<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>
))}
{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>
) : (