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

@@ -50,7 +50,7 @@ interface RequirementUploadSlotProps {
requirement: FileRequirement
existingFile?: UploadedFile | null
projectId: string
roundId: string
stageId: string
onFileChange?: () => void
disabled?: boolean
}
@@ -59,7 +59,7 @@ export function RequirementUploadSlot({
requirement,
existingFile,
projectId,
roundId,
stageId,
onFileChange,
disabled = false,
}: RequirementUploadSlotProps) {
@@ -110,13 +110,13 @@ export function RequirementUploadSlot({
try {
// Get presigned URL
const { url, bucket, objectKey, isLate, roundId: uploadRoundId } =
const { url, bucket, objectKey, isLate, stageId: uploadStageId } =
await getUploadUrl.mutateAsync({
projectId,
fileName: file.name,
mimeType: file.type,
fileType: 'OTHER',
roundId,
stageId,
requirementId: requirement.id,
})
@@ -150,7 +150,7 @@ export function RequirementUploadSlot({
fileType: 'OTHER',
bucket,
objectKey,
roundId: uploadRoundId || roundId,
stageId: uploadStageId || stageId,
isLate: isLate || false,
requirementId: requirement.id,
})
@@ -164,7 +164,7 @@ export function RequirementUploadSlot({
setProgress(0)
}
},
[projectId, roundId, requirement, acceptsMime, getUploadUrl, saveFileMetadata, onFileChange]
[projectId, stageId, requirement, acceptsMime, getUploadUrl, saveFileMetadata, onFileChange]
)
const handleDelete = useCallback(async () => {
@@ -309,20 +309,22 @@ export function RequirementUploadSlot({
interface RequirementUploadListProps {
projectId: string
roundId: string
stageId: string
disabled?: boolean
}
export function RequirementUploadList({ projectId, roundId, disabled }: RequirementUploadListProps) {
export function RequirementUploadList({ projectId, stageId, disabled }: RequirementUploadListProps) {
const utils = trpc.useUtils()
const { data: requirements = [] } = trpc.file.listRequirements.useQuery({ roundId })
const { data: files = [] } = trpc.file.listByProject.useQuery({ projectId, roundId })
const { data: requirements = [] } = trpc.file.listRequirements.useQuery({
stageId,
})
const { data: files = [] } = trpc.file.listByProject.useQuery({ projectId, stageId })
if (requirements.length === 0) return null
const handleFileChange = () => {
utils.file.listByProject.invalidate({ projectId, roundId })
utils.file.listByProject.invalidate({ projectId, stageId })
}
return (
@@ -351,7 +353,7 @@ export function RequirementUploadList({ projectId, roundId, disabled }: Requirem
: null
}
projectId={projectId}
roundId={roundId}
stageId={stageId}
onFileChange={handleFileChange}
disabled={disabled}
/>