feat(admin): streamline group management
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import Link from "next/link";
|
||||
import { UserGroupSelect } from "./user-group-select";
|
||||
|
||||
export interface AdminUserRow {
|
||||
id: string;
|
||||
firstName: string | null;
|
||||
discordUsername: string;
|
||||
discordGlobalName: string | null;
|
||||
discordUserId: string;
|
||||
onboardingCompletedAt: Date | null;
|
||||
primaryUsername: string | null;
|
||||
accountCount: number;
|
||||
}
|
||||
|
||||
export function AdminUserTable({
|
||||
action,
|
||||
assignmentByUser,
|
||||
emptyMessage,
|
||||
groups,
|
||||
returnTo,
|
||||
users,
|
||||
}: {
|
||||
action: (formData: FormData) => Promise<void>;
|
||||
assignmentByUser: Record<string, string>;
|
||||
emptyMessage: string;
|
||||
groups: Array<{ id: string; name: string; isDefault: boolean }>;
|
||||
returnTo: string;
|
||||
users: AdminUserRow[];
|
||||
}) {
|
||||
const defaultGroup = groups.find((group) => group.isDefault);
|
||||
return (
|
||||
<div className="overflow-x-auto border border-line bg-panel shadow-[8px_8px_0_var(--color-shadow)]">
|
||||
<table className="w-full min-w-[900px] border-collapse text-left">
|
||||
<caption className="sr-only">Registered portal users and effective groups</caption>
|
||||
<thead className="border-b border-line font-mono text-[10px] uppercase tracking-widest text-muted">
|
||||
<tr><th className="p-4" scope="col">User</th><th className="p-4" scope="col">Discord</th><th className="p-4" scope="col">Primary</th><th className="p-4" scope="col">Accounts</th><th className="p-4" scope="col">Group</th><th className="p-4" scope="col">Status</th></tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-line">
|
||||
{users.map((user) => (
|
||||
<tr className="transition-colors hover:bg-canvas/60" key={user.id}>
|
||||
<th className="p-4 text-left" scope="row"><Link className="font-display text-lg font-black underline decoration-line underline-offset-4 hover:text-accent" href={`/admin/users/${user.id}`}>{user.firstName ?? "Name needed"}</Link></th>
|
||||
<td className="p-4"><div className="font-mono text-xs font-bold">{user.discordGlobalName ?? user.discordUsername}</div><div className="mt-1 font-mono text-[10px] text-muted">@{user.discordUsername}</div><div className="mt-1 font-mono text-[9px] text-muted">{user.discordUserId}</div></td>
|
||||
<td className="p-4 font-mono text-xs">{user.primaryUsername ?? "—"}</td>
|
||||
<td className="p-4 font-mono text-xs">{user.accountCount}</td>
|
||||
<td className="p-4">{defaultGroup ? <UserGroupSelect action={action} effectiveGroupId={assignmentByUser[user.id] ?? defaultGroup.id} groups={groups} returnTo={returnTo} userId={user.id} userLabel={user.firstName ?? user.discordUsername} /> : <span className="text-xs text-accent">Default group missing</span>}</td>
|
||||
<td className="p-4"><span className={`border px-2 py-1 font-mono text-[9px] uppercase tracking-wider ${user.onboardingCompletedAt ? "border-line text-muted" : "border-accent text-accent"}`}>{user.onboardingCompletedAt ? "Ready" : "Onboarding"}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
{!users.length && <tr><td className="p-8 text-muted" colSpan={6}>{emptyMessage}</td></tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user