Download src/components/UserTable.js from SyedFouzan/taskflow-titan-workflow-wizardry: direct link, hf CLI and curl.
- Browser
- Download file 8.09 kB
-
https://huggingface.co/spaces/SyedFouzan/taskflow-titan-workflow-wizardry/resolve/main/src/components/UserTable.js
- Command line
-
hf download hf://spaces/SyedFouzan/taskflow-titan-workflow-wizardry/src/components/UserTable.js
-
curl -L -o UserTable.js https://huggingface.co/spaces/SyedFouzan/taskflow-titan-workflow-wizardry/resolve/main/src/components/UserTable.js
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; |