Round system redesign: Phases 1-7 complete

Full pipeline/track/stage architecture replacing the legacy round system.

Schema: 11 new models (Pipeline, Track, Stage, StageTransition,
ProjectStageState, RoutingRule, Cohort, CohortProject, LiveProgressCursor,
OverrideAction, AudienceVoter) + 8 new enums.

Backend: 9 new routers (pipeline, stage, routing, stageFiltering,
stageAssignment, cohort, live, decision, award) + 6 new services
(stage-engine, routing-engine, stage-filtering, stage-assignment,
stage-notifications, live-control).

Frontend: Pipeline wizard (17 components), jury stage pages (7),
applicant pipeline pages (3), public stage pages (2), admin pipeline
pages (5), shared stage components (3), SSE route, live hook.

Phase 6 refit: 23 routers/services migrated from roundId to stageId,
all frontend components refitted. Deleted round.ts (985 lines),
roundTemplate.ts, round-helpers.ts, round-settings.ts, round-type-settings.tsx,
10 legacy admin pages, 7 legacy jury pages, 3 legacy dialogs.

Phase 7 validation: 36 tests (10 unit + 8 integration files) all passing,
TypeScript 0 errors, Next.js build succeeds, 13 integrity checks,
legacy symbol sweep clean, auto-seed on first Docker startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 13:57:09 +01:00
parent 8a328357e3
commit 331b67dae0
256 changed files with 29117 additions and 21424 deletions

View File

@@ -22,28 +22,28 @@ export const dashboardRouter = router({
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
const [
activeRoundCount,
totalRoundCount,
activeStageCount,
totalStageCount,
projectCount,
newProjectsThisWeek,
totalJurors,
activeJurors,
evaluationStats,
totalAssignments,
recentRounds,
recentStages,
latestProjects,
categoryBreakdown,
oceanIssueBreakdown,
recentActivity,
pendingCOIs,
draftRounds,
draftStages,
unassignedProjects,
] = await Promise.all([
ctx.prisma.round.count({
where: { programId: editionId, status: 'ACTIVE' },
ctx.prisma.stage.count({
where: { track: { pipeline: { programId: editionId } }, status: 'STAGE_ACTIVE' },
}),
ctx.prisma.round.count({
where: { programId: editionId },
ctx.prisma.stage.count({
where: { track: { pipeline: { programId: editionId } } },
}),
ctx.prisma.project.count({
where: { programId: editionId },
@@ -58,38 +58,38 @@ export const dashboardRouter = router({
where: {
role: 'JURY_MEMBER',
status: { in: ['ACTIVE', 'INVITED', 'NONE'] },
assignments: { some: { round: { programId: editionId } } },
assignments: { some: { stage: { track: { pipeline: { programId: editionId } } } } },
},
}),
ctx.prisma.user.count({
where: {
role: 'JURY_MEMBER',
status: 'ACTIVE',
assignments: { some: { round: { programId: editionId } } },
assignments: { some: { stage: { track: { pipeline: { programId: editionId } } } } },
},
}),
ctx.prisma.evaluation.groupBy({
by: ['status'],
where: { assignment: { round: { programId: editionId } } },
where: { assignment: { stage: { track: { pipeline: { programId: editionId } } } } },
_count: true,
}),
ctx.prisma.assignment.count({
where: { round: { programId: editionId } },
where: { stage: { track: { pipeline: { programId: editionId } } } },
}),
ctx.prisma.round.findMany({
where: { programId: editionId },
ctx.prisma.stage.findMany({
where: { track: { pipeline: { programId: editionId } } },
orderBy: { createdAt: 'desc' },
take: 5,
select: {
id: true,
name: true,
status: true,
votingStartAt: true,
votingEndAt: true,
submissionEndDate: true,
stageType: true,
windowOpenAt: true,
windowCloseAt: true,
_count: {
select: {
projects: true,
projectStageStates: true,
assignments: true,
},
},
@@ -115,7 +115,6 @@ export const dashboardRouter = router({
createdAt: true,
submittedAt: true,
status: true,
round: { select: { name: true } },
},
}),
ctx.prisma.project.groupBy({
@@ -146,16 +145,20 @@ export const dashboardRouter = router({
where: {
hasConflict: true,
reviewedAt: null,
assignment: { round: { programId: editionId } },
assignment: { stage: { track: { pipeline: { programId: editionId } } } },
},
}),
ctx.prisma.round.count({
where: { programId: editionId, status: 'DRAFT' },
ctx.prisma.stage.count({
where: { track: { pipeline: { programId: editionId } }, status: 'STAGE_DRAFT' },
}),
ctx.prisma.project.count({
where: {
programId: editionId,
round: { status: 'ACTIVE' },
projectStageStates: {
some: {
stage: { status: 'STAGE_ACTIVE' },
},
},
assignments: { none: {} },
},
}),
@@ -163,21 +166,21 @@ export const dashboardRouter = router({
return {
edition,
activeRoundCount,
totalRoundCount,
activeStageCount,
totalStageCount,
projectCount,
newProjectsThisWeek,
totalJurors,
activeJurors,
evaluationStats,
totalAssignments,
recentRounds,
recentStages,
latestProjects,
categoryBreakdown,
oceanIssueBreakdown,
recentActivity,
pendingCOIs,
draftRounds,
draftStages,
unassignedProjects,
}
}),