All files / src/components/admin UserTable.tsx

100% Statements 7/7
100% Branches 10/10
100% Functions 6/6
100% Lines 7/7

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>
	);
}