feat(portal): add SSR operations and exclusive groups
CI / validate (push) Successful in 5m20s
Release / release (push) Successful in 6m56s

This commit is contained in:
dmg
2026-08-01 19:21:23 -04:00
parent b88097c15a
commit b7c0083647
45 changed files with 2245 additions and 363 deletions
@@ -10,9 +10,13 @@ const errors: Record<string, string> = {
"create-failed": "The group could not be created.",
"unknown-group": "That group no longer exists.",
"invalid-membership": "That membership change was invalid.",
"invalid-delete": "Confirm the group deletion before continuing.",
"protected-group": "The protected default group cannot be deleted.",
};
export default async function GroupsPage({ searchParams }: { searchParams: Promise<{ error?: string }> }) {
export const dynamic = "force-dynamic";
export default async function GroupsPage({ searchParams }: { searchParams: Promise<{ error?: string; saved?: string }> }) {
const query = await searchParams;
const [allGroups, memberships, registeredUsers] = await Promise.all([
db.select().from(groups).orderBy(desc(groups.isDefault), asc(groups.name)),
@@ -23,20 +27,22 @@ export default async function GroupsPage({ searchParams }: { searchParams: Promi
for (const membership of memberships) {
membershipCounts.set(membership.groupId, (membershipCounts.get(membership.groupId) ?? 0) + 1);
}
const explicitlyAssignedUsers = memberships.length;
return (
<main className="mx-auto max-w-6xl px-6 py-14">
<header className="border-b border-line pb-8">
<p className="font-mono text-xs font-bold uppercase tracking-[0.25em] text-accent">Admission policy</p>
<h1 className="mt-4 font-display text-5xl font-black uppercase">Access groups</h1>
<p className="mt-5 max-w-2xl leading-7 text-muted">Every registered user belongs to <strong className="text-ink">everyone</strong>. A player can join only when at least one of their groups has access enabled.</p>
<p className="mt-5 max-w-2xl leading-7 text-muted">Each user has one effective group. Users without an explicit assignment fall back to <strong className="text-ink">everyone</strong>; Minecraft admission follows only that groups access setting.</p>
</header>
{query.error && <p className="mt-7 border-l-2 border-accent bg-panel px-5 py-4 text-sm text-accent">{errors[query.error] ?? "The group operation failed."}</p>}
{query.error && <p className="mt-7 border-l-2 border-accent bg-panel px-5 py-4 text-sm text-accent" role="alert">{errors[query.error] ?? "The group operation failed."}</p>}
{query.saved === "deleted" && <p className="mt-7 border-l-2 border-signal bg-panel px-5 py-4 text-sm" role="status">Group deleted. Its former members now use the default group.</p>}
<section className="mt-10 grid gap-5 md:grid-cols-2">
{allGroups.map((group) => {
const memberCount = group.isDefault ? registeredUsers.length : membershipCounts.get(group.id) ?? 0;
const memberCount = group.isDefault ? registeredUsers.length - explicitlyAssignedUsers : membershipCounts.get(group.id) ?? 0;
return (
<article className="border border-line bg-panel p-6 shadow-[5px_5px_0_var(--color-shadow)]" key={group.id}>
<div className="flex items-start justify-between gap-4">