veza/apps/web/src/components/admin/UserTableRow.tsx

129 lines
4.5 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { User } from '@/types';
import { Badge } from '../ui/badge';
import { MoreVertical, Shield, Ban, Mail, Trash2 } from 'lucide-react';
interface UserTableRowProps {
user: User;
onBan: (user: User) => void;
onDelete: (user: User) => void;
onEditRole: (user: User) => void;
}
export const UserTableRow: React.FC<UserTableRowProps> = ({
user,
onBan,
onDelete,
onEditRole,
}) => {
const [showMenu, setShowMenu] = useState(false);
const statusColor: Record<string, string> = {
online: 'bg-kodo-lime',
offline: 'bg-kodo-steel',
away: 'bg-kodo-gold',
idle: 'bg-kodo-gold',
busy: 'bg-kodo-red',
dnd: 'bg-kodo-red',
};
return (
<tr className="hover:bg-white/5 transition-colors group relative">
<td className="p-4">
<div className="flex items-center gap-3">
<div className="relative">
<img
src={user.avatar}
className="w-8 h-8 rounded-full object-cover"
/>
<div
className={`absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 border-2 border-kodo-ink rounded-full ${user.status ? statusColor[user.status] : statusColor.offline}`}
></div>
</div>
<div>
<div className="font-bold text-white text-sm">{user.username}</div>
<div className="text-xs text-kodo-content-dim font-mono">{user.id}</div>
</div>
</div>
</td>
<td className="p-4 text-sm text-kodo-content-dim">{user.email}</td>
<td className="p-4">
<div className="flex gap-1">
{(user.roles || [user.role]).map((role: string) => (
<Badge
key={role}
label={role}
variant={
role === 'Admin' || role === 'admin' ? 'magenta' : 'cyan'
}
className="scale-90"
/>
))}
</div>
</td>
<td className="p-4 text-sm text-kodo-content-dim">{user.tier || 'Free'}</td>
<td className="p-4 text-sm text-kodo-content-dim font-mono">
{user.joinDate || user.created_at}
</td>
<td className="p-4 text-sm text-kodo-content-dim font-mono">
{user.lastLogin || user.last_login_at || 'Never'}
</td>
<td className="p-4 text-right">
<div className="relative">
<button
className="p-1.5 hover:bg-white/10 rounded text-kodo-content-dim hover:text-white"
onClick={(e) => {
e.stopPropagation();
setShowMenu(!showMenu);
}}
>
<MoreVertical className="w-4 h-4" />
</button>
{showMenu && (
<>
<div
className="fixed inset-0 z-10"
onClick={() => setShowMenu(false)}
></div>
<div className="absolute right-0 top-full mt-2 w-48 bg-kodo-graphite border border-kodo-steel rounded-lg shadow-xl z-20 overflow-hidden">
<button
className="w-full text-left px-4 py-2.5 text-xs text-kodo-text-main hover:bg-white/10 flex items-center gap-2"
onClick={() => {
onEditRole(user);
setShowMenu(false);
}}
>
<Shield className="w-3 h-3" /> Change Role
</button>
<button className="w-full text-left px-4 py-2.5 text-xs text-kodo-text-main hover:bg-white/10 flex items-center gap-2">
<Mail className="w-3 h-3" /> Send Email
</button>
<div className="h-px bg-kodo-steel/50 my-1"></div>
<button
className="w-full text-left px-4 py-2.5 text-xs text-kodo-gold hover:bg-white/10 flex items-center gap-2"
onClick={() => {
onBan(user);
setShowMenu(false);
}}
>
<Ban className="w-3 h-3" /> Suspend User
</button>
<button
className="w-full text-left px-4 py-2.5 text-xs text-kodo-red hover:bg-white/10 flex items-center gap-2"
onClick={() => {
onDelete(user);
setShowMenu(false);
}}
>
<Trash2 className="w-3 h-3" /> Delete Account
</button>
</div>
</>
)}
</div>
</td>
</tr>
);
};