Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 253x 253x 2x 1x 1x 414x 16x | import { Link } from "@tanstack/react-router";
import { Eye, Ban, CheckCircle } from "lucide-react";
import { Button } from "../ui/button";
import type { AdminUser } from "../../lib/api";
import { useSortable } from "../../hooks/useSortable";
import { SortableHeader } from "../ui/sortable-header";
interface UserTableProps {
users: AdminUser[];
onToggleBan: (user: AdminUser) => void;
}
export function UserTable({ users, onToggleBan }: UserTableProps) {
const { sorted: sortedUsers, sortKey, sortOrder, toggleSort } = useSortable(
users,
"name" as keyof AdminUser,
"asc",
);
return (
<div className="overflow-x-auto -mx-6 px-6">
<table className="w-full min-w-[650px]">
<thead>
<tr className="border-b border-border-default text-left text-sm text-foreground-muted">
<SortableHeader
label="Name"
sortKey="name"
currentSortKey={sortKey as string}
sortOrder={sortOrder}
onSort={(key) => toggleSort(key as keyof AdminUser)}
/>
<SortableHeader
label="Email"
sortKey="email"
currentSortKey={sortKey as string}
sortOrder={sortOrder}
onSort={(key) => toggleSort(key as keyof AdminUser)}
/>
<th className="pb-3 font-medium">Status</th>
<th className="pb-3 font-medium">Phone</th>
<SortableHeader
label="Created"
sortKey="createdAt"
currentSortKey={sortKey as string}
sortOrder={sortOrder}
onSort={(key) => toggleSort(key as keyof AdminUser)}
/>
<th className="pb-3 font-medium">Actions</th>
</tr>
</thead>
<tbody>
{sortedUsers.map((user) => (
<tr
key={user.id}
className="border-b border-border-default last:border-0 hover:bg-background-muted"
>
<td className="py-4">
<Link
to={`/admin/users/${user.id}`}
className="font-medium text-foreground-default hover:text-primary-600"
>
{user.name}
</Link>
</td>
<td className="py-4 text-foreground-muted">{user.email}</td>
<td className="py-4">
{user.banned ? (
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded-full bg-error-light text-error">
<Ban className="h-3 w-3" />
Banned
</span>
) : user.emailVerified ? (
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded-full bg-success-light text-success">
<CheckCircle className="h-3 w-3" />
Active
</span>
) : (
<span className="inline-block px-2 py-1 text-xs font-medium rounded-full bg-warning-light text-warning">
Unverified
</span>
)}
</td>
<td className="py-4 text-foreground-muted">
{user.phoneNumber || "-"}
</td>
<td className="py-4 text-foreground-muted">
{new Date(user.createdAt).toLocaleDateString()}
</td>
<td className="py-4">
<div className="flex items-center gap-2">
<Link to={`/admin/users/${user.id}`}>
<Button variant="ghost" size="sm">
<Eye className="h-4 w-4" />
</Button>
</Link>
<Button
variant="ghost"
size="sm"
onClick={() => onToggleBan(user)}
title={user.banned ? "Unban user" : "Ban user"}
>
{user.banned ? (
<CheckCircle className="h-4 w-4 text-success" />
) : (
<Ban className="h-4 w-4 text-error" />
)}
</Button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
|