Files
MOPC-Portal/src/server/routers/dashboard.ts

185 lines
5.0 KiB
TypeScript
Raw Normal View History

import { z } from 'zod'
import { router, adminProcedure } from '../trpc'
export const dashboardRouter = router({
/**
* Get all dashboard stats in a single query batch.
* Replaces the 16 parallel Prisma queries that were previously
* run during SSR, which blocked the event loop and caused 503s.
*/
getStats: adminProcedure
.input(z.object({ editionId: z.string() }))
.query(async ({ ctx, input }) => {
const { editionId } = input
const edition = await ctx.prisma.program.findUnique({
where: { id: editionId },
select: { name: true, year: true },
})
if (!edition) return null
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
const [
activeRoundCount,
totalRoundCount,
projectCount,
newProjectsThisWeek,
totalJurors,
activeJurors,
evaluationStats,
totalAssignments,
recentRounds,
latestProjects,
categoryBreakdown,
oceanIssueBreakdown,
recentActivity,
pendingCOIs,
draftRounds,
unassignedProjects,
] = await Promise.all([
ctx.prisma.round.count({
where: { programId: editionId, status: 'ACTIVE' },
}),
ctx.prisma.round.count({
where: { programId: editionId },
}),
ctx.prisma.project.count({
where: { programId: editionId },
}),
ctx.prisma.project.count({
where: {
programId: editionId,
createdAt: { gte: sevenDaysAgo },
},
}),
ctx.prisma.user.count({
where: {
role: 'JURY_MEMBER',
status: { in: ['ACTIVE', 'INVITED', 'NONE'] },
assignments: { some: { round: { programId: editionId } } },
},
}),
ctx.prisma.user.count({
where: {
role: 'JURY_MEMBER',
status: 'ACTIVE',
assignments: { some: { round: { programId: editionId } } },
},
}),
ctx.prisma.evaluation.groupBy({
by: ['status'],
where: { assignment: { round: { programId: editionId } } },
_count: true,
}),
ctx.prisma.assignment.count({
where: { round: { programId: editionId } },
}),
ctx.prisma.round.findMany({
where: { programId: editionId },
orderBy: { createdAt: 'desc' },
take: 5,
select: {
id: true,
name: true,
status: true,
votingStartAt: true,
votingEndAt: true,
submissionEndDate: true,
_count: {
select: {
projects: true,
assignments: true,
},
},
assignments: {
select: {
evaluation: { select: { status: true } },
},
},
},
}),
ctx.prisma.project.findMany({
where: { programId: editionId },
orderBy: { createdAt: 'desc' },
take: 8,
select: {
id: true,
title: true,
teamName: true,
country: true,
competitionCategory: true,
oceanIssue: true,
logoKey: true,
createdAt: true,
submittedAt: true,
status: true,
round: { select: { name: true } },
},
}),
ctx.prisma.project.groupBy({
by: ['competitionCategory'],
where: { programId: editionId },
_count: true,
}),
ctx.prisma.project.groupBy({
by: ['oceanIssue'],
where: { programId: editionId },
_count: true,
}),
ctx.prisma.auditLog.findMany({
where: {
timestamp: { gte: sevenDaysAgo },
},
orderBy: { timestamp: 'desc' },
take: 8,
select: {
id: true,
action: true,
entityType: true,
timestamp: true,
user: { select: { name: true } },
},
}),
ctx.prisma.conflictOfInterest.count({
where: {
hasConflict: true,
reviewedAt: null,
assignment: { round: { programId: editionId } },
},
}),
ctx.prisma.round.count({
where: { programId: editionId, status: 'DRAFT' },
}),
ctx.prisma.project.count({
where: {
programId: editionId,
round: { status: 'ACTIVE' },
assignments: { none: {} },
},
}),
])
return {
edition,
activeRoundCount,
totalRoundCount,
projectCount,
newProjectsThisWeek,
totalJurors,
activeJurors,
evaluationStats,
totalAssignments,
recentRounds,
latestProjects,
categoryBreakdown,
oceanIssueBreakdown,
recentActivity,
pendingCOIs,
draftRounds,
unassignedProjects,
}
}),
})