--- import { Icon } from 'astro-icon/components' import { z } from 'astro:content' import { orderBy as lodashOrderBy } from 'lodash-es' import Button from '../../../components/Button.astro' import SortArrowIcon from '../../../components/SortArrowIcon.astro' import TimeFormatted from '../../../components/TimeFormatted.astro' import Tooltip from '../../../components/Tooltip.astro' import UserBadge from '../../../components/UserBadge.astro' import BaseLayout from '../../../layouts/BaseLayout.astro' import { zodParseQueryParamsStoringErrors } from '../../../lib/parseUrlFilters' import { pluralize } from '../../../lib/pluralize' import { prisma } from '../../../lib/prisma' import { formatDateShort } from '../../../lib/timeAgo' import { urlWithParams } from '../../../lib/urls' import type { Prisma } from '@prisma/client' const { data: filters } = zodParseQueryParamsStoringErrors( { 'sort-by': z.enum(['name', 'role', 'lastLoginAt', 'karma', 'createdAt']).default('createdAt'), 'sort-order': z.enum(['asc', 'desc']).default('desc'), search: z.string().optional(), role: z.enum(['user', 'admin', 'moderator', 'verified', 'spammer']).optional(), }, Astro ) // Set up Prisma orderBy with correct typing const prismaOrderBy = filters['sort-by'] === 'name' || filters['sort-by'] === 'createdAt' || filters['sort-by'] === 'lastLoginAt' || filters['sort-by'] === 'karma' ? { [filters['sort-by'] === 'karma' ? 'totalKarma' : filters['sort-by']]: filters['sort-order'] === 'asc' ? 'asc' : 'desc', } : { createdAt: 'desc' as const } // Build where clause based on role filter const whereClause: Prisma.UserWhereInput = {} if (filters.search) { whereClause.OR = [{ name: { contains: filters.search, mode: 'insensitive' } }] } if (filters.role) { switch (filters.role) { case 'user': { whereClause.admin = false whereClause.moderator = false whereClause.verified = false whereClause.spammer = false break } case 'admin': { whereClause.admin = true break } case 'moderator': { whereClause.moderator = true break } case 'verified': { whereClause.verified = true break } case 'spammer': { whereClause.spammer = true break } } } // Retrieve users from the database const dbUsers = await prisma.user.findMany({ where: whereClause, select: { id: true, name: true, displayName: true, picture: true, verified: true, admin: true, moderator: true, spammer: true, totalKarma: true, createdAt: true, updatedAt: true, lastLoginAt: true, internalNotes: { select: { id: true, content: true, createdAt: true, }, }, _count: { select: { suggestions: true, comments: true, }, }, }, orderBy: prismaOrderBy, }) const users = filters['sort-by'] === 'role' ? lodashOrderBy(dbUsers, [(u) => (u.admin ? 'admin' : 'user')], [filters['sort-order']]) : dbUsers const makeSortUrl = (sortBy: NonNullable<(typeof filters)['sort-by']>) => { return urlWithParams(Astro.url, { 'sort-by': sortBy, 'sort-order': filters['sort-by'] === sortBy && filters['sort-order'] === 'asc' ? 'desc' : 'asc', }) } --- User Management {users.length} users Search Filter by Role/Status All Users Regular Users Admins Moderators Verified Users Spammers Users List Scroll horizontally to see more → Name Status Role Karma Login / Joined Activity Actions { users.map((user) => ( {user.internalNotes.length > 0 && ( `${formatDateShort(note.createdAt, { prefix: false, hourPrecision: true, caseType: 'sentence', })}: ${note.content}` ) .join('\n\n')} > {user.internalNotes.length} internal {pluralize('note', user.internalNotes.length)} )} {user.spammer && ( Spammer )} {user.verified && ( Verified )} {user.moderator && ( Moderator )} {user.admin ? 'Admin' : 'User'} = 100 ? 'text-green-400' : user.totalKarma >= 0 ? 'text-zinc-300' : 'text-red-400'}`} > {user.totalKarma} / Suggestions {user._count.suggestions} Comments {user._count.comments} )) }