Implement 10 platform features: evaluation UX, admin tools, AI summaries, applicant portal
Batch 1 - Quick Wins: - F1: Evaluation progress indicator with touch tracking in sticky status bar - F2: Export filtering results as CSV with dynamic AI column flattening - F3: Observer access to analytics dashboards (8 procedures changed to observerProcedure) Batch 2 - Jury Experience: - F4: Countdown timer component with urgency colors + email reminder service with cron endpoint - F5: Conflict of interest declaration system (dialog, admin management, review workflow) Batch 3 - Admin & AI Enhancements: - F6: Bulk status update UI with selection checkboxes, floating toolbar, status history recording - F7: AI-powered evaluation summary with anonymized data, OpenAI integration, scoring patterns - F8: Smart assignment improvements (geo diversity penalty, round familiarity bonus, COI blocking) Batch 4 - Form Flexibility & Applicant Portal: - F9: Evaluation form flexibility (text, boolean, section_header types, conditional visibility) - F10: Applicant portal (status timeline, per-round documents, mentor messaging) Schema: 5 new models (ReminderLog, ConflictOfInterest, EvaluationSummary, ProjectStatusHistory, MentorMessage), ProjectFile extended with roundId + isLate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { z } from 'zod'
|
||||
import { router, protectedProcedure, adminProcedure } from '../trpc'
|
||||
import { router, protectedProcedure, adminProcedure, observerProcedure } from '../trpc'
|
||||
|
||||
export const analyticsRouter = router({
|
||||
/**
|
||||
* Get score distribution for a round (histogram data)
|
||||
*/
|
||||
getScoreDistribution: adminProcedure
|
||||
getScoreDistribution: observerProcedure
|
||||
.input(z.object({ roundId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const evaluations = await ctx.prisma.evaluation.findMany({
|
||||
@@ -50,7 +50,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get evaluation completion over time (timeline data)
|
||||
*/
|
||||
getEvaluationTimeline: adminProcedure
|
||||
getEvaluationTimeline: observerProcedure
|
||||
.input(z.object({ roundId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const evaluations = await ctx.prisma.evaluation.findMany({
|
||||
@@ -96,7 +96,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get juror workload distribution
|
||||
*/
|
||||
getJurorWorkload: adminProcedure
|
||||
getJurorWorkload: observerProcedure
|
||||
.input(z.object({ roundId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const assignments = await ctx.prisma.assignment.findMany({
|
||||
@@ -145,7 +145,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get project rankings with average scores
|
||||
*/
|
||||
getProjectRankings: adminProcedure
|
||||
getProjectRankings: observerProcedure
|
||||
.input(z.object({ roundId: z.string(), limit: z.number().optional() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const projects = await ctx.prisma.project.findMany({
|
||||
@@ -213,7 +213,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get status breakdown (pie chart data)
|
||||
*/
|
||||
getStatusBreakdown: adminProcedure
|
||||
getStatusBreakdown: observerProcedure
|
||||
.input(z.object({ roundId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const projects = await ctx.prisma.project.groupBy({
|
||||
@@ -231,7 +231,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get overview stats for dashboard
|
||||
*/
|
||||
getOverviewStats: adminProcedure
|
||||
getOverviewStats: observerProcedure
|
||||
.input(z.object({ roundId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const [
|
||||
@@ -281,7 +281,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get criteria-level score distribution
|
||||
*/
|
||||
getCriteriaScores: adminProcedure
|
||||
getCriteriaScores: observerProcedure
|
||||
.input(z.object({ roundId: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
// Get active evaluation form for this round
|
||||
@@ -343,7 +343,7 @@ export const analyticsRouter = router({
|
||||
/**
|
||||
* Get geographic distribution of projects by country
|
||||
*/
|
||||
getGeographicDistribution: adminProcedure
|
||||
getGeographicDistribution: observerProcedure
|
||||
.input(
|
||||
z.object({
|
||||
programId: z.string(),
|
||||
|
||||
Reference in New Issue
Block a user