167 lines
10 KiB
TypeScript
167 lines
10 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { useCRM } from '../context/CRMContext';
|
|
import { Shield, ShieldAlert, User as UserIcon, Check, X, MoreVertical, Edit2 } from 'lucide-react';
|
|
import { User } from '../types';
|
|
|
|
const Users: React.FC = () => {
|
|
const { users, isAdmin, updateUserRole } = useCRM();
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [showRoleModal, setShowRoleModal] = useState(false);
|
|
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
|
const [newRole, setNewRole] = useState<'admin' | 'user'>('user');
|
|
|
|
// Filter users
|
|
const filteredUsers = users.filter(user =>
|
|
(user.name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
user.email.toLowerCase().includes(searchTerm.toLowerCase()))
|
|
);
|
|
|
|
const handleRoleUpdateClick = (user: User) => {
|
|
setSelectedUser(user);
|
|
setNewRole(user.role === 'admin' ? 'user' : 'admin');
|
|
setShowRoleModal(true);
|
|
};
|
|
|
|
const confirmRoleUpdate = async () => {
|
|
if (selectedUser) {
|
|
await updateUserRole(selectedUser.id, newRole);
|
|
setShowRoleModal(false);
|
|
setSelectedUser(null);
|
|
}
|
|
};
|
|
|
|
if (!isAdmin) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center h-[calc(100vh-100px)] text-center animate-in fade-in zoom-in duration-500">
|
|
<div className="w-24 h-24 bg-rose-500/10 rounded-full flex items-center justify-center mb-6 border border-rose-500/20 shadow-[0_0_30px_rgba(244,63,94,0.2)]">
|
|
<ShieldAlert size={48} className="text-rose-500" />
|
|
</div>
|
|
<h2 className="text-3xl font-bold text-white mb-3 tracking-tight">Acesso Restrito</h2>
|
|
<p className="text-slate-400 max-w-md leading-relaxed">
|
|
Você precisa de privilégios de administrador para gerenciar usuários e permissões do sistema.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="animate-in fade-in duration-500 space-y-6">
|
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 bg-white/5 p-8 rounded-[32px] border border-white/5 backdrop-blur-xl">
|
|
<div>
|
|
<h2 className="text-2xl font-bold text-white tracking-tight mb-1">Gerenciamento de Usuários</h2>
|
|
<p className="text-slate-400">Controle total sobre acesso e permissões da equipe.</p>
|
|
</div>
|
|
<div className="relative w-full md:w-auto">
|
|
<input
|
|
type="text"
|
|
placeholder="Buscar por nome ou email..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className="w-full md:w-80 bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-sm text-slate-300 focus:border-indigo-500 outline-none transition-all placeholder:text-slate-600"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="glass-card rounded-[32px] border border-white/5 overflow-hidden shadow-2xl">
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-left">
|
|
<thead className="bg-black/40 text-[10px] font-bold text-slate-500 uppercase tracking-widest border-b border-white/5">
|
|
<tr>
|
|
<th className="px-8 py-5">Usuário</th>
|
|
<th className="px-8 py-5">Email</th>
|
|
<th className="px-8 py-5">Status</th>
|
|
<th className="px-8 py-5 text-center">Permissão</th>
|
|
<th className="px-8 py-5 text-center">Último Acesso</th>
|
|
<th className="px-8 py-5 text-right">Ações</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-white/5">
|
|
{filteredUsers.length > 0 ? filteredUsers.map(user => (
|
|
<tr key={user.id} className="hover:bg-white/[0.02] transition-colors group">
|
|
<td className="px-8 py-5">
|
|
<div className="flex items-center gap-4">
|
|
{user.avatar ? (
|
|
<img src={user.avatar} alt={user.name} className="w-10 h-10 rounded-full border border-white/10 object-cover" />
|
|
) : (
|
|
<div className="w-10 h-10 rounded-full bg-slate-800 flex items-center justify-center text-slate-400 border border-white/5">
|
|
<UserIcon size={18} />
|
|
</div>
|
|
)}
|
|
<div>
|
|
<div className="font-bold text-slate-200">{user.name || 'Sem Nome'}</div>
|
|
<div className="text-[10px] text-slate-500 font-mono hidden sm:block">ID: {user.id.substring(0, 8)}</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td className="px-8 py-5 text-slate-400 font-mono text-xs">{user.email}</td>
|
|
<td className="px-8 py-5">
|
|
<span className={`inline-flex items-center px-2.5 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider border ${user.status === 'Active' ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' : 'bg-slate-500/10 text-slate-400 border-slate-500/20'
|
|
}`}>
|
|
{user.status || 'Active'}
|
|
</span>
|
|
</td>
|
|
<td className="px-8 py-5 text-center">
|
|
<span className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider border ${user.role === 'admin' ? 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20' : 'bg-slate-500/10 text-slate-400 border-slate-500/20'
|
|
}`}>
|
|
{user.role === 'admin' && <Shield size={10} />}
|
|
{user.role || 'User'}
|
|
</span>
|
|
</td>
|
|
<td className="px-8 py-5 text-center text-xs text-slate-500">
|
|
{user.lastAccess ? new Date(user.lastAccess).toLocaleDateString() : '-'}
|
|
</td>
|
|
<td className="px-8 py-5 text-right">
|
|
<button
|
|
onClick={() => handleRoleUpdateClick(user)}
|
|
className="text-xs font-bold text-slate-500 hover:text-white px-4 py-2 rounded-lg hover:bg-white/10 transition-colors border border-transparent hover:border-white/5"
|
|
>
|
|
{user.role === 'admin' ? 'Rebaixar' : 'Promover'}
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
)) : (
|
|
<tr>
|
|
<td colSpan={6} className="py-20 text-center opacity-30">
|
|
<UserIcon size={48} className="mx-auto mb-4 text-slate-400" />
|
|
<p className="text-sm font-bold text-slate-400 uppercase tracking-widest">Nenhum usuário encontrado</p>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Role Update Modal */}
|
|
{showRoleModal && selectedUser && (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm animate-in fade-in duration-200">
|
|
<div className="bg-[#0F1115] border border-white/10 rounded-[32px] w-full max-w-sm shadow-2xl p-8 relative overflow-hidden">
|
|
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-indigo-500 to-purple-500"></div>
|
|
|
|
<h3 className="text-xl font-bold text-white mb-2">Alterar Permissão</h3>
|
|
<p className="text-sm text-slate-400 mb-6 leading-relaxed">
|
|
Você tem certeza que deseja alterar o nível de acesso de <strong className="text-white">{selectedUser.name}</strong> para <strong className="text-indigo-400 uppercase">{newRole}</strong>?
|
|
</p>
|
|
|
|
<div className="flex gap-3">
|
|
<button
|
|
onClick={() => setShowRoleModal(false)}
|
|
className="flex-1 py-3 text-sm font-bold text-slate-400 hover:text-white transition-colors hover:bg-white/5 rounded-xl"
|
|
>
|
|
Cancelar
|
|
</button>
|
|
<button
|
|
onClick={confirmRoleUpdate}
|
|
className="flex-[2] bg-indigo-600 hover:bg-indigo-500 text-white rounded-xl font-bold text-sm shadow-lg shadow-indigo-900/20 transition-all flex items-center justify-center gap-2 active:scale-95"
|
|
>
|
|
Confirmar Alteração
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Users;
|