fix(members): role tabs/filter include users with secondary roles

user.list and user.listInvitableIds filtered on the singular User.role column,
so the type tabs (Jury/Mentor/…) omitted users holding that role as a secondary
role (User.roles[]). Match the role as primary OR secondary (roles hasSome),
combined with search via AND, mirroring userHasRole / hasRole middleware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-01 18:51:11 +02:00
parent 040e5ff9a9
commit d4a77f63d3
2 changed files with 117 additions and 12 deletions

View File

@@ -232,19 +232,26 @@ export const userRouter = router({
const skip = (page - 1) * perPage
const where: Record<string, unknown> = {}
const and: Record<string, unknown>[] = []
// Match the role as EITHER the primary role (User.role) or a secondary
// role (User.roles[]) so the type tabs include multi-role users, mirroring
// the multi-role checks used elsewhere (userHasRole / hasRole middleware).
if (roles && roles.length > 0) {
where.role = { in: roles }
and.push({ OR: [{ role: { in: roles } }, { roles: { hasSome: roles } }] })
} else if (role) {
where.role = role
and.push({ OR: [{ role }, { roles: { has: role } }] })
}
if (status) where.status = status
if (search) {
where.OR = [
{ email: { contains: search, mode: 'insensitive' } },
{ name: { contains: search, mode: 'insensitive' } },
]
and.push({
OR: [
{ email: { contains: search, mode: 'insensitive' } },
{ name: { contains: search, mode: 'insensitive' } },
],
})
}
if (and.length > 0) where.AND = and
const dir = sortDir ?? 'asc'
const orderBy: Record<string, string> = sortBy
@@ -373,19 +380,24 @@ export const userRouter = router({
const where: Record<string, unknown> = {
status: { in: ['NONE', 'INVITED'] },
}
const and: Record<string, unknown>[] = []
// Match primary OR secondary role (see user.list for rationale).
if (input.roles && input.roles.length > 0) {
where.role = { in: input.roles }
and.push({ OR: [{ role: { in: input.roles } }, { roles: { hasSome: input.roles } }] })
} else if (input.role) {
where.role = input.role
and.push({ OR: [{ role: input.role }, { roles: { has: input.role } }] })
}
if (input.search) {
where.OR = [
{ email: { contains: input.search, mode: 'insensitive' } },
{ name: { contains: input.search, mode: 'insensitive' } },
]
and.push({
OR: [
{ email: { contains: input.search, mode: 'insensitive' } },
{ name: { contains: input.search, mode: 'insensitive' } },
],
})
}
if (and.length > 0) where.AND = and
const users = await ctx.prisma.user.findMany({
where,