Admin UI audit round 2: fix 28 display bugs across 23 files
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m51s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m51s
HIGH fixes (broken features / wrong data): - H1: Fix roundAssignments → projectRoundStates in project router (7 occurrences) - H2: Fix deliberation results panel blank table (wrong field names) - H3: Fix deliberation participant names blank (wrong data path) - H4: Fix awards "Evaluated" stat duplicating "Eligible" count - H5: Fix cross-round comparison enabled at 1 round (backend requires 2) - H6: Fix setState during render anti-pattern (6 occurrences) - H7: Fix round detail jury member count always showing 0 - H8: Remove 4 invalid status values from observer dashboard filter - H9: Fix filtering progress bar always showing 100% MEDIUM fixes (misleading display): - M1: Filter special-award rounds from competition timeline - M2: Exclude special-award rounds from distinct project count - M3: Fix MENTORING pipeline node hardcoded "0 mentored" - M4: Fix DELIB_LOCKED badge using red for success state - M5: Add status label maps to deliberation session detail - M6: Humanize deliberation category + tie-break method displays - M8: Rename setStageId → setRoundId, "Select Stage" → "Select Round" - M9: Add missing INVITED/ACTIVE/SUSPENDED to members status labels - M10: Add ROUND_DRAFT/ACTIVE/CLOSED/ARCHIVED to StatusBadge - M11: Fix unsent messages showing "Scheduled" instead of "Draft" - M12: Rename misleading totalEvaluations → totalAssignments - M13: Rename "Stage" column to "Program" in projects page LOW fixes (cosmetic / edge-case): - L1: Use unfiltered rounds array for active round detection - L2: Use all rounds length for new round sort order - L3: Filter special-award rounds from header count - L4: Fix single-underscore replace in award status badges - L5: Fix score bucket boundary gaps (4.99 dropped between buckets) - L6: Title-case LIVE_FINAL pipeline metric status - L7: Fix roundType.replace only replacing first underscore - L8: Remove duplicate severity sort in smart-actions component Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -754,14 +754,14 @@ export const analyticsRouter = router({
|
||||
|
||||
const scores = evaluationScores.map((e) => e.globalScore!).filter((s) => s != null)
|
||||
const scoreDistribution = [
|
||||
{ label: '9-10', min: 9, max: 10 },
|
||||
{ label: '7-8', min: 7, max: 8.99 },
|
||||
{ label: '5-6', min: 5, max: 6.99 },
|
||||
{ label: '3-4', min: 3, max: 4.99 },
|
||||
{ label: '1-2', min: 1, max: 2.99 },
|
||||
{ label: '9-10', min: 9, max: Infinity },
|
||||
{ label: '7-8', min: 7, max: 9 },
|
||||
{ label: '5-6', min: 5, max: 7 },
|
||||
{ label: '3-4', min: 3, max: 5 },
|
||||
{ label: '1-2', min: 1, max: 3 },
|
||||
].map((b) => ({
|
||||
label: b.label,
|
||||
count: scores.filter((s) => s >= b.min && s <= b.max).length,
|
||||
count: scores.filter((s) => s >= b.min && s < b.max).length,
|
||||
}))
|
||||
|
||||
return {
|
||||
@@ -770,7 +770,7 @@ export const analyticsRouter = router({
|
||||
projectCount,
|
||||
jurorCount,
|
||||
submittedEvaluations,
|
||||
totalEvaluations: totalAssignments,
|
||||
totalAssignments,
|
||||
completionRate,
|
||||
scoreDistribution,
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ export const competitionRouter = router({
|
||||
}
|
||||
|
||||
// Count distinct projects across all rounds (not sum of per-round states)
|
||||
const roundIds = competition.rounds.map((r) => r.id)
|
||||
const roundIds = competition.rounds.filter((r) => !r.specialAwardId).map((r) => r.id)
|
||||
const distinctProjectCount = roundIds.length > 0
|
||||
? await ctx.prisma.projectRoundState.findMany({
|
||||
where: { roundId: { in: roundIds } },
|
||||
|
||||
@@ -146,7 +146,24 @@ export const deliberationRouter = router({
|
||||
aggregate: adminProcedure
|
||||
.input(z.object({ sessionId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
return aggregateVotes(input.sessionId, ctx.prisma)
|
||||
const result = await aggregateVotes(input.sessionId, ctx.prisma)
|
||||
// Enrich rankings with project titles
|
||||
const projectIds = result.rankings.map((r) => r.projectId)
|
||||
const projects = projectIds.length > 0
|
||||
? await ctx.prisma.project.findMany({
|
||||
where: { id: { in: projectIds } },
|
||||
select: { id: true, title: true, teamName: true },
|
||||
})
|
||||
: []
|
||||
const projectMap = new Map(projects.map((p) => [p.id, p]))
|
||||
return {
|
||||
...result,
|
||||
rankings: result.rankings.map((r) => ({
|
||||
...r,
|
||||
projectTitle: projectMap.get(r.projectId)?.title ?? 'Unknown Project',
|
||||
teamName: projectMap.get(r.projectId)?.teamName ?? '',
|
||||
})),
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
|
||||
@@ -89,19 +89,19 @@ export const projectRouter = router({
|
||||
// Filter by program
|
||||
if (programId) where.programId = programId
|
||||
|
||||
// Filter by round (via RoundAssignment join)
|
||||
// Filter by round (via ProjectRoundState)
|
||||
if (roundId) {
|
||||
where.roundAssignments = { some: { roundId } }
|
||||
where.projectRoundStates = { some: { roundId } }
|
||||
}
|
||||
|
||||
// Exclude projects already in a specific round
|
||||
if (excludeInRoundId) {
|
||||
where.roundAssignments = { none: { roundId: excludeInRoundId } }
|
||||
where.projectRoundStates = { none: { roundId: excludeInRoundId } }
|
||||
}
|
||||
|
||||
// Filter by unassigned (not in any round)
|
||||
if (unassignedOnly) {
|
||||
where.roundAssignments = { none: {} }
|
||||
where.projectRoundStates = { none: {} }
|
||||
}
|
||||
|
||||
// Status filter
|
||||
@@ -223,13 +223,13 @@ export const projectRouter = router({
|
||||
|
||||
if (programId) where.programId = programId
|
||||
if (roundId) {
|
||||
where.roundAssignments = { some: { roundId } }
|
||||
where.projectRoundStates = { some: { roundId } }
|
||||
}
|
||||
if (excludeInRoundId) {
|
||||
where.roundAssignments = { none: { roundId: excludeInRoundId } }
|
||||
where.projectRoundStates = { none: { roundId: excludeInRoundId } }
|
||||
}
|
||||
if (unassignedOnly) {
|
||||
where.roundAssignments = { none: {} }
|
||||
where.projectRoundStates = { none: {} }
|
||||
}
|
||||
if (statuses?.length) where.status = { in: statuses }
|
||||
if (tags && tags.length > 0) where.tags = { hasSome: tags }
|
||||
@@ -1102,7 +1102,7 @@ export const projectRouter = router({
|
||||
|
||||
const where: Record<string, unknown> = {
|
||||
programId,
|
||||
roundAssignments: { none: {} }, // Projects not assigned to any round
|
||||
projectRoundStates: { none: {} }, // Projects not assigned to any round
|
||||
}
|
||||
|
||||
if (search) {
|
||||
|
||||
@@ -102,12 +102,17 @@ export const specialAwardRouter = router({
|
||||
}
|
||||
}
|
||||
|
||||
// Count eligible projects
|
||||
const eligibleCount = await ctx.prisma.awardEligibility.count({
|
||||
where: { awardId: input.id, eligible: true },
|
||||
})
|
||||
// Count eligible projects and total assessed
|
||||
const [eligibleCount, totalAssessed] = await Promise.all([
|
||||
ctx.prisma.awardEligibility.count({
|
||||
where: { awardId: input.id, eligible: true },
|
||||
}),
|
||||
ctx.prisma.awardEligibility.count({
|
||||
where: { awardId: input.id },
|
||||
}),
|
||||
])
|
||||
|
||||
return { ...award, competition, eligibleCount }
|
||||
return { ...award, competition, eligibleCount, totalAssessed }
|
||||
}),
|
||||
|
||||
// ─── Admin Mutations ────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user