import { groupAccessWindows, groups, minecraftAccounts, userGroupMemberships, users } from "@minecraft-account-manager/database"; import { and, asc, desc, eq, isNull, sql } from "drizzle-orm"; import type { ReactNode } from "react"; import Link from "next/link"; import { notFound } from "next/navigation"; import { AdminModalForm } from "@/components/admin-modal-form"; import { AdminUserTable } from "@/components/admin-user-table"; import { GroupPolicyControl } from "@/components/group-policy-control"; import { GroupScheduleEditor, GroupScheduleSummary } from "@/components/group-schedule-editor"; import { db } from "@/lib/database"; import { isEffectiveGroupMember } from "@/lib/group-management"; import { assignUserGroupFromRegistry } from "../../users/actions"; import { deleteGroup, replaceGroupSchedule, setGroupAccess, setGroupAnonymizedNetworkAccess, updateGroupDetails } from "../actions"; const savedMessages: Record = { created: "Group created.", details: "Group details updated.", access: "Minecraft access policy updated.", "network-access": "VPN, proxy, and Tor policy updated.", schedule: "Weekly access schedule updated.", group: "Member group updated.", }; const errorMessages: Record = { "invalid-group": "Enter a valid name and a description of no more than 500 characters.", "duplicate-group": "A group with that name already exists.", "invalid-group-assignment": "The user or destination group no longer exists. No membership change was applied.", "invalid-schedule": "Use valid, non-overlapping weekly access windows. Start and end cannot be identical.", }; export const dynamic = "force-dynamic"; export default async function GroupPage({ params, searchParams, }: { params: Promise<{ groupId: string }>; searchParams: Promise<{ error?: string; saved?: string }>; }) { const { groupId } = await params; const query = await searchParams; const [group] = await db.select().from(groups).where(eq(groups.id, groupId)).limit(1); if (!group) notFound(); const [allUsers, allGroups, memberships, accessWindows] = await Promise.all([ db.select({ id: users.id, firstName: users.firstName, discordUsername: users.discordUsername, discordGlobalName: users.discordGlobalName, discordUserId: users.discordUserId, onboardingCompletedAt: users.onboardingCompletedAt, primaryUsername: minecraftAccounts.username, accountCount: sql`( select count(*)::int from ${minecraftAccounts} account_count where account_count.user_id = ${users.id} and account_count.deleted_at is null )`, }) .from(users) .leftJoin(minecraftAccounts, and( eq(minecraftAccounts.userId, users.id), eq(minecraftAccounts.isPrimary, true), isNull(minecraftAccounts.deletedAt), )) .orderBy(users.firstName, users.discordUsername), db.select({ id: groups.id, name: groups.name, isDefault: groups.isDefault }) .from(groups).orderBy(desc(groups.isDefault), asc(groups.name)), db.select({ userId: userGroupMemberships.userId, groupId: userGroupMemberships.groupId }) .from(userGroupMemberships), db.select({ startMinuteOfWeek: groupAccessWindows.startMinuteOfWeek, endMinuteOfWeek: groupAccessWindows.endMinuteOfWeek, }).from(groupAccessWindows).where(eq(groupAccessWindows.groupId, group.id)) .orderBy(groupAccessWindows.startMinuteOfWeek), ]); const assignmentByUser = Object.fromEntries(memberships.map((membership) => [membership.userId, membership.groupId])); const memberUsers = allUsers.filter((user) => isEffectiveGroupMember(user.id, assignmentByUser, group)); const returnTo = `/admin/groups/${group.id}`; return (
← All groups

Access group

{group.name}

{group.isDefault && Default}

{group.description ?? "No description."}