169 lines
5.3 KiB
TypeScript
169 lines
5.3 KiB
TypeScript
'use client';
|
|
|
|
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
|
|
import { AdminUsersViewData } from '@/lib/view-data/AdminUsersViewData';
|
|
import { Button } from '@/ui/Button';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { Stack } from '@/ui/primitives/Stack';
|
|
import { SimpleCheckbox } from '@/ui/SimpleCheckbox';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow
|
|
} from '@/ui/Table';
|
|
import { Text } from '@/ui/Text';
|
|
import { MoreVertical, Shield, Trash2 } from 'lucide-react';
|
|
import { UserStatusTag } from './UserStatusTag';
|
|
|
|
interface AdminUsersTableProps {
|
|
users: AdminUsersViewData['users'];
|
|
selectedUserIds: string[];
|
|
onSelectUser: (userId: string) => void;
|
|
onSelectAll: () => void;
|
|
onUpdateStatus: (userId: string, status: string) => void;
|
|
onDeleteUser: (userId: string) => void;
|
|
deletingUserId?: string | null;
|
|
}
|
|
|
|
/**
|
|
* AdminUsersTable
|
|
*
|
|
* Semantic table for managing users.
|
|
* High-density, instrument-grade UI.
|
|
*/
|
|
export function AdminUsersTable({
|
|
users,
|
|
selectedUserIds,
|
|
onSelectUser,
|
|
onSelectAll,
|
|
onUpdateStatus,
|
|
onDeleteUser,
|
|
deletingUserId
|
|
}: AdminUsersTableProps) {
|
|
const allSelected = users.length > 0 && selectedUserIds.length === users.length;
|
|
|
|
return (
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableHeader width="10">
|
|
<SimpleCheckbox
|
|
checked={allSelected}
|
|
onChange={onSelectAll}
|
|
aria-label="Select all users"
|
|
/>
|
|
</TableHeader>
|
|
<TableHeader>User</TableHeader>
|
|
<TableHeader>Roles</TableHeader>
|
|
<TableHeader>Status</TableHeader>
|
|
<TableHeader>Last Login</TableHeader>
|
|
<TableHeader textAlign="right">Actions</TableHeader>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{users.map((user) => (
|
|
<TableRow key={user.id} variant={selectedUserIds.includes(user.id) ? 'highlight' : 'default'}>
|
|
<TableCell>
|
|
<SimpleCheckbox
|
|
checked={selectedUserIds.includes(user.id)}
|
|
onChange={() => onSelectUser(user.id)}
|
|
aria-label={`Select user ${user.displayName}`}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Stack direction="row" align="center" gap={3}>
|
|
<Stack
|
|
bg="bg-primary-blue/10"
|
|
rounded="full"
|
|
p={2}
|
|
border
|
|
borderColor="border-primary-blue/20"
|
|
>
|
|
<Icon icon={Shield} size={4} color="#198CFF" />
|
|
</Stack>
|
|
<Stack>
|
|
<Text weight="semibold" color="text-white" block>
|
|
{user.displayName}
|
|
</Text>
|
|
<Text size="xs" color="text-gray-500" block>
|
|
{user.email}
|
|
</Text>
|
|
</Stack>
|
|
</Stack>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Stack direction="row" gap={1.5} wrap>
|
|
{user.roles.map((role) => (
|
|
<Stack
|
|
key={role}
|
|
px={2}
|
|
py={0.5}
|
|
rounded="full"
|
|
bg="bg-charcoal-outline/30"
|
|
border
|
|
borderColor="border-charcoal-outline"
|
|
>
|
|
<Text size="xs" weight="medium" color="text-gray-300">
|
|
{role}
|
|
</Text>
|
|
</Stack>
|
|
))}
|
|
</Stack>
|
|
</TableCell>
|
|
<TableCell>
|
|
<UserStatusTag status={user.status} />
|
|
</TableCell>
|
|
<TableCell>
|
|
<Text size="sm" color="text-gray-400">
|
|
{user.lastLoginAt ? DateDisplay.formatShort(user.lastLoginAt) : 'Never'}
|
|
</Text>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Stack direction="row" align="center" justify="end" gap={2}>
|
|
{user.status === 'active' ? (
|
|
<Button
|
|
size="sm"
|
|
variant="secondary"
|
|
onClick={() => onUpdateStatus(user.id, 'suspended')}
|
|
>
|
|
Suspend
|
|
</Button>
|
|
) : user.status === 'suspended' ? (
|
|
<Button
|
|
size="sm"
|
|
variant="secondary"
|
|
onClick={() => onUpdateStatus(user.id, 'active')}
|
|
>
|
|
Activate
|
|
</Button>
|
|
) : null}
|
|
|
|
<Button
|
|
size="sm"
|
|
variant="secondary"
|
|
onClick={() => onDeleteUser(user.id)}
|
|
disabled={deletingUserId === user.id}
|
|
icon={<Icon icon={Trash2} size={3} />}
|
|
>
|
|
{deletingUserId === user.id ? '...' : ''}
|
|
</Button>
|
|
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
icon={<Icon icon={MoreVertical} size={4} />}
|
|
>
|
|
{''}
|
|
</Button>
|
|
</Stack>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
);
|
|
}
|