merge: PR10 — applicant nationality stats card
This commit is contained in:
@@ -2403,4 +2403,67 @@ export const analyticsRouter = router({
|
||||
prisma: ctx.prisma,
|
||||
})
|
||||
}),
|
||||
|
||||
/**
|
||||
* Nationality breakdown for the applicants (team members) of projects in
|
||||
* the selected scope. Counts UNIQUE users so a single applicant on
|
||||
* multiple teams isn't double-counted.
|
||||
*
|
||||
* Scope:
|
||||
* - roundId set → projects with a ProjectRoundState in that round
|
||||
* - programId set → projects in that program
|
||||
* - neither → all team members across all projects (global)
|
||||
*/
|
||||
getApplicantNationalities: adminProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
roundId: z.string().optional(),
|
||||
programId: z.string().optional(),
|
||||
})
|
||||
.optional()
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const roundId = input?.roundId
|
||||
const programId = input?.programId
|
||||
|
||||
const projectFilter = roundId
|
||||
? { projectRoundStates: { some: { roundId } } }
|
||||
: programId
|
||||
? { programId }
|
||||
: {}
|
||||
|
||||
// Pull all distinct team-member userIds + their nationality in one query.
|
||||
// `distinct: ['userId']` collapses a user appearing on multiple teams in
|
||||
// the same scope to a single row.
|
||||
const teamMembers = await ctx.prisma.teamMember.findMany({
|
||||
where: { project: projectFilter },
|
||||
select: { userId: true, user: { select: { nationality: true } } },
|
||||
distinct: ['userId'],
|
||||
})
|
||||
|
||||
const total = teamMembers.length
|
||||
const declaredEntries = teamMembers.filter(
|
||||
(tm) => tm.user?.nationality && tm.user.nationality.trim().length > 0
|
||||
)
|
||||
const declared = declaredEntries.length
|
||||
const notDeclared = total - declared
|
||||
|
||||
const counts = new Map<string, number>()
|
||||
for (const tm of declaredEntries) {
|
||||
const code = (tm.user!.nationality as string).trim()
|
||||
counts.set(code, (counts.get(code) ?? 0) + 1)
|
||||
}
|
||||
|
||||
const byCountry = Array.from(counts.entries())
|
||||
.map(([country, count]) => ({ country, count }))
|
||||
.sort((a, b) => b.count - a.count || a.country.localeCompare(b.country))
|
||||
|
||||
return {
|
||||
total,
|
||||
declared,
|
||||
notDeclared,
|
||||
byCountry,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user