Decouple projects from rounds with RoundProject join table

Projects now exist at the program level instead of being locked to a
single round. A new RoundProject join table enables many-to-many
relationships with per-round status tracking. Rounds have sortOrder
for configurable progression paths.

- Add RoundProject model, programId on Project, sortOrder on Round
- Migration preserves existing data (roundId -> RoundProject entries)
- Update all routers to query through RoundProject join
- Add assign/remove/advance/reorder round endpoints
- Add Assign, Advance, Remove Projects dialogs on round detail page
- Add round reorder controls (up/down arrows) on rounds list
- Show all rounds on project detail page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 22:33:55 +01:00
parent 0d2bc4db7e
commit fd5e5222da
52 changed files with 1892 additions and 326 deletions

View File

@@ -333,7 +333,7 @@ export const mentorRouter = router({
// Get projects without mentors
const projects = await ctx.prisma.project.findMany({
where: {
roundId: input.roundId,
roundProjects: { some: { roundId: input.roundId } },
mentorAssignment: null,
wantsMentorship: true,
},
@@ -431,10 +431,17 @@ export const mentorRouter = router({
include: {
project: {
include: {
round: {
program: { select: { name: true, year: true } },
roundProjects: {
include: {
program: { select: { name: true, year: true } },
round: {
include: {
program: { select: { name: true, year: true } },
},
},
},
orderBy: { addedAt: 'desc' },
take: 1,
},
teamMembers: {
include: {
@@ -477,10 +484,17 @@ export const mentorRouter = router({
const project = await ctx.prisma.project.findUniqueOrThrow({
where: { id: input.projectId },
include: {
round: {
program: { select: { id: true, name: true, year: true } },
roundProjects: {
include: {
program: { select: { id: true, name: true, year: true } },
round: {
include: {
program: { select: { id: true, name: true, year: true } },
},
},
},
orderBy: { addedAt: 'desc' },
take: 1,
},
teamMembers: {
include: {
@@ -528,7 +542,7 @@ export const mentorRouter = router({
)
.query(async ({ ctx, input }) => {
const where = {
...(input.roundId && { project: { roundId: input.roundId } }),
...(input.roundId && { project: { roundProjects: { some: { roundId: input.roundId } } } }),
...(input.mentorId && { mentorId: input.mentorId }),
}
@@ -541,9 +555,12 @@ export const mentorRouter = router({
id: true,
title: true,
teamName: true,
status: true,
oceanIssue: true,
competitionCategory: true,
roundProjects: {
select: { status: true },
take: 1,
},
},
},
mentor: {