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:
@@ -86,17 +86,12 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
// Fetch files (flat list for backward compatibility)
|
||||
const { data: files } = trpc.file.listByProject.useQuery({ projectId })
|
||||
|
||||
// Fetch grouped files by round (if project has a roundId)
|
||||
const { data: groupedFiles } = trpc.file.listByProjectForRound.useQuery(
|
||||
{ projectId, roundId: project?.roundId || '' },
|
||||
{ enabled: !!project?.roundId }
|
||||
)
|
||||
|
||||
// Fetch available rounds for upload selector (if project has a programId)
|
||||
const { data: rounds } = trpc.round.listByProgram.useQuery(
|
||||
{ programId: project?.programId || '' },
|
||||
// Fetch available stages for upload selector (if project has a programId)
|
||||
const { data: programData } = trpc.program.get.useQuery(
|
||||
{ id: project?.programId || '' },
|
||||
{ enabled: !!project?.programId }
|
||||
)
|
||||
const availableStages = (programData?.stages as Array<{ id: string; name: string }>) || []
|
||||
|
||||
const utils = trpc.useUtils()
|
||||
|
||||
@@ -148,15 +143,15 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-wrap items-center gap-1 text-sm text-muted-foreground">
|
||||
{project.roundId ? (
|
||||
{project.programId ? (
|
||||
<Link
|
||||
href={`/admin/rounds/${project.roundId}`}
|
||||
href={`/admin/programs/${project.programId}`}
|
||||
className="hover:underline"
|
||||
>
|
||||
{project.round?.name ?? 'Round'}
|
||||
{programData?.name ?? 'Program'}
|
||||
</Link>
|
||||
) : (
|
||||
<span>No round</span>
|
||||
<span>No program</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -526,9 +521,7 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{groupedFiles && groupedFiles.length > 0 ? (
|
||||
<FileViewer groupedFiles={groupedFiles} />
|
||||
) : files && files.length > 0 ? (
|
||||
{files && files.length > 0 ? (
|
||||
<FileViewer
|
||||
projectId={projectId}
|
||||
files={files.map((f) => ({
|
||||
@@ -551,13 +544,9 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
<p className="text-sm font-medium mb-3">Upload New Files</p>
|
||||
<FileUpload
|
||||
projectId={projectId}
|
||||
roundId={project.roundId || undefined}
|
||||
availableRounds={rounds?.map((r: { id: string; name: string }) => ({ id: r.id, name: r.name }))}
|
||||
availableStages={availableStages?.map((s: { id: string; name: string }) => ({ id: s.id, name: s.name }))}
|
||||
onUploadComplete={() => {
|
||||
utils.file.listByProject.invalidate({ projectId })
|
||||
if (project.roundId) {
|
||||
utils.file.listByProjectForRound.invalidate({ projectId, roundId: project.roundId })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -585,7 +574,7 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link href={`/admin/rounds/${project.roundId}/assignments`}>
|
||||
<Link href={`/admin/members`}>
|
||||
Manage
|
||||
</Link>
|
||||
</Button>
|
||||
@@ -688,10 +677,10 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
)}
|
||||
|
||||
{/* AI Evaluation Summary */}
|
||||
{project.roundId && stats && stats.totalEvaluations > 0 && (
|
||||
{assignments && assignments.length > 0 && stats && stats.totalEvaluations > 0 && (
|
||||
<EvaluationSummaryCard
|
||||
projectId={projectId}
|
||||
roundId={project.roundId}
|
||||
stageId={assignments[0].stageId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user