SyedFouzan's picture
{
ffb4c35 verified
Raw History Blame Contribute Delete
8.09 kB
import React, { useState } from 'react';
import { motion } from 'framer-motion';
import {
Search,
Filter,
MoreVertical,
Edit,
Trash2,
Mail,
Phone,
MapPin
} from 'lucide-react';
import { usersAPI } from '../services/api';
import toast from 'react-hot-toast';
const UserTable = ({ users, onEdit, onDelete, onRefresh }) => {
const [searchTerm, setSearchTerm] = useState('');
const [filterRole, setFilterRole] = useState('all');
const filteredUsers = users?.filter(user => {
const matchesSearch = user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.toLowerCase());
const matchesRole = filterRole === 'all' || user.role === filterRole;
return matchesSearch && matchesRole;
});
const getRoleColor = (role) => {
switch (role) {
case 'admin':
return 'bg-purple-100 text-purple-800 dark:bg-purple-900/20 dark:text-purple-400';
case 'manager':
return 'bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-400';
case 'employee':
return 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400';
default:
return 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400';
}
};
return (
<div className="space-y-4">
<div className="flex flex-col sm:flex-row gap-4">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
<input
type="text"
placeholder="Search users..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700"
/>
</div>
<div className="flex items-center gap-2">
<Filter className="w-4 h-4 text-gray-400" />
<select
value={filterRole}
onChange={(e) => setFilterRole(e.target.value)}
className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700"
>
<option value="all">All Roles</option>
<option value="admin">Admin</option>
<option value="manager">Manager</option>
<option value="employee">Employee</option>
</select>
</div>
</div>
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 dark:bg-gray-700">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
User
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Role
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Contact
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Joined
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
{filteredUsers?.map((user, index) => (
<motion.tr
key={user._id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.05 }}
className="hover:bg-gray-50 dark:hover:bg-gray-700/50"
>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center text-white font-medium">
{user.name.charAt(0).toUpperCase()}
</div>
<div className="ml-4">
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">
{user.name}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
ID: {user._id.slice(-6)}
</div>
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getRoleColor(user.role)}`}>
{user.role}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-900 dark:text-gray-100">
<div className="flex items-center gap-1">
<Mail className="w-3 h-3" />
{user.email}
</div>
{user.phone && (
<div className="flex items-center gap-1 text-gray-500 dark:text-gray-400">
<Phone className="w-3 h-3" />
{user.phone}
</div>
)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
user.active
? 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400'
: 'bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400'
}`}>
{user.active ? 'Active' : 'Inactive'}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{new Date(user.createdAt).toLocaleDateString()}
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => onEdit(user)}
className="p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<Edit className="w-4 h-4 text-gray-500" />
</button>
<button
onClick={() => onDelete(user._id)}
className="p-1 rounded-lg hover:bg-red-100 dark:hover:bg-red-900/20 transition-colors"
>
<Trash2 className="w-4 h-4 text-red-500" />
</button>
</div>
</td>
</motion.tr>
))}
</tbody>
</table>
</div>
{filteredUsers?.length === 0 && (
<div className="text-center py-12">
<p className="text-gray-500 dark:text-gray-400">No users found</p>
</div>
)}
</div>
</div>
);
};
export default UserTable;