Platform-wide visual overhaul, team invites, analytics improvements, and deployment hardening

UI overhaul applying jury dashboard design patterns across all pages:
- Stat cards with border-l-4 accent + icon pills on admin, observer, mentor, applicant dashboards and reports
- Card section headers with color-coded icon pills throughout
- Hover lift effects (translate-y + shadow) on cards and list items
- Gradient progress bars (brand-teal to brand-blue) platform-wide
- AnimatedCard stagger animations on all dashboard sections
- Auth pages with gradient accent strip and polished icon containers
- EmptyState component upgraded with rounded icon pill containers
- Replaced AI-looking icons (Brain/Sparkles/Bot/Wand2/Cpu) with descriptive alternatives across 12 files
- Removed gradient overlay from jury dashboard header
- Quick actions restyled as card links with group hover effects

Backend improvements:
- Team member invite emails with account setup flow and notification logging
- Analytics routers accept edition-wide queries (programId) in addition to roundId
- Round detail endpoint returns inline progress data (eliminates extra getProgress call)
- Award voting endpoints parallelized with Promise.all
- Bulk invite supports optional sendInvitation flag
- AwardVote composite index migration for query performance

Infrastructure:
- Docker entrypoint with migration retry loop (configurable retries/delay)
- docker-compose pull_policy: always for automatic image refresh
- Simplified deploy/update scripts using docker compose up -d --pull always
- Updated deployment documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 13:20:52 +01:00
parent 98f4a957cc
commit ce4069bf92
59 changed files with 1949 additions and 913 deletions

View File

@@ -87,18 +87,44 @@ export const roundRouter = router({
},
})
// Get evaluation stats
const evaluationStats = await ctx.prisma.evaluation.groupBy({
by: ['status'],
where: {
assignment: { roundId: input.id },
// Get evaluation stats + progress in parallel (avoids duplicate groupBy in getProgress)
const [evaluationStats, totalAssignments, completedAssignments] =
await Promise.all([
ctx.prisma.evaluation.groupBy({
by: ['status'],
where: {
assignment: { roundId: input.id },
},
_count: true,
}),
ctx.prisma.assignment.count({ where: { roundId: input.id } }),
ctx.prisma.assignment.count({
where: { roundId: input.id, isCompleted: true },
}),
])
const evaluationsByStatus = evaluationStats.reduce(
(acc, curr) => {
acc[curr.status] = curr._count
return acc
},
_count: true,
})
{} as Record<string, number>
)
return {
...round,
evaluationStats,
// Inline progress data (eliminates need for separate getProgress call)
progress: {
totalProjects: round._count.projects,
totalAssignments,
completedAssignments,
completionPercentage:
totalAssignments > 0
? Math.round((completedAssignments / totalAssignments) * 100)
: 0,
evaluationsByStatus,
},
}
}),