diff --git a/CHANGELOG.md b/CHANGELOG.md index 066e6266..7d353b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,9 @@ written while it was being built. See [RELEASING.md](RELEASING.md). ### Added - Manage Teams fixes the teams once a team has a submission or teams are - published: a notice says why, and uploading, adding and deleting teams are - disabled. People can still be moved between teams or to Unassigned by hand, - and teams renamed. + published: a notice says why, and uploading and deleting teams are disabled. + People can still be moved between teams or to Unassigned by hand, teams + renamed, and new teams added. - Manage Teams can filter the Unassigned column by registration answers: click an answer (or type into a free-text question's box) to bring the people who gave it to the top; everyone else stays below, greyed out. Each answer shows diff --git a/components/frontend/src/lib/server/hackathon/teamAssignmentLock.ts b/components/frontend/src/lib/server/hackathon/teamAssignmentLock.ts index 595cfb08..7154f8e4 100644 --- a/components/frontend/src/lib/server/hackathon/teamAssignmentLock.ts +++ b/components/frontend/src/lib/server/hackathon/teamAssignmentLock.ts @@ -11,9 +11,9 @@ import { enabledCapabilities } from "./phaseForm" /** * Why the teams themselves are fixed — empty while they are not. * - * Fixed means no team may be added or deleted, and so no upload, which - * replaces every team. People may still be moved by hand and teams renamed: - * that is how an organizer handles someone dropping out late. Two things fix + * Fixed means no team may be deleted, and so no upload, which replaces every + * team. People may still be moved by hand, teams renamed and new teams added: + * that is how an organizer handles a late dropout or a late joiner. Two things fix * the teams, each because rebuilding them would pull the ground from under * somebody: * @@ -25,8 +25,8 @@ import { enabledCapabilities } from "./phaseForm" * Either one is enough, and the reasons are listed so the page can say which. * * TODO(backend: team-assignment-lock): this is a frontend rule only. The page - * stops offering the controls and its save action refuses a plan that adds or - * deletes a team, but `TeamService` still accepts that from any other caller. + * stops offering the controls and its save action refuses a plan that deletes + * a team, but `TeamService` still accepts that from any other caller. * Planned as a backend check in a follow-up pull request; the frontend can * then keep this for the explanation and rely on the error for the rule. */ diff --git a/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.server.ts b/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.server.ts index e0b86ab8..18d1ecac 100644 --- a/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.server.ts +++ b/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.server.ts @@ -249,19 +249,18 @@ export const actions: Actions = { const before = all.filter((t) => approved.has(t.projectId)) if (locked.length > 0) { - // Locked, the teams themselves stand: people may move between them or - // to Unassigned and a team may be renamed, but none may be added or - // deleted — deleting one takes its submissions with it, and published - // teams are what participants have been told. An emptied team is kept. + // Locked, no team may be deleted: deleting one takes its submissions + // with it, and published teams are what participants have been told. + // Everything else stays open — people may move between teams or to + // Unassigned, teams may be renamed, and a new team may be added. An + // emptied team is kept; a new one nobody was put on is not created. const planned = new Set(plan.map((t) => t.id)) - if ( - plan.some((t) => t.id === null) || - before.some((t) => !planned.has(t.id)) - ) { + if (before.some((t) => !planned.has(t.id))) { return fail(409, { - message: `Teams cannot be added or deleted now: ${locked.join(" and ")}.`, + message: `Teams cannot be deleted now: ${locked.join(" and ")}.`, }) } + plan = plan.filter((t) => t.id !== null || t.memberIds.length > 0) } else { // A team with nobody in it is not part of the assignment: dropped, so // a new one is never created and a saved one is deleted below like any diff --git a/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.svelte b/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.svelte index a48e1635..82cd9981 100644 --- a/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.svelte +++ b/components/frontend/src/routes/(app)/my/hackathon/[id]/teams/manage/+page.svelte @@ -366,9 +366,12 @@ const base = fromServer(data.projectRows); const nameBefore = new Map(base.map((t) => [t.key, t.name])); // Save drops a team with nobody in it, so an empty one counts as - // deleted, or as not added — the summary says what Save will do. Not - // while the teams are fixed: then an emptied team is kept. - const kept = locked ? teams : teams.filter((t) => t.memberIds.length > 0); + // deleted, or as not added — the summary says what Save will do. While + // the teams are fixed, an emptied saved team is kept; an empty new one + // is still not created. + const kept = teams.filter( + (t) => t.memberIds.length > 0 || (locked && t.id !== null) + ); const alive = new Set(kept.map((t) => t.key)); const added = kept.filter((t) => t.id === null).length; @@ -438,10 +441,10 @@ dropTarget = null; } - // Once a submission exists or teams are published, the teams themselves - // stand: no upload (it replaces every team), no adding, deleting or Clear - // all. People can still be moved by hand — between teams or to Unassigned — - // and teams renamed, and the save action enforces the same line. The + // Once a submission exists or teams are published, no team may go: no + // upload (it replaces every team), no deleting, no Clear all. People can + // still be moved by hand — between teams or to Unassigned — teams renamed + // and new teams added, and the save action enforces the same line. The // reason is said once at the top and again on hover over what is off. See // `assignmentLockReasons`. const lockReasons = $derived(data.lockReasons); @@ -692,8 +695,8 @@

Teams are fixed: - {lockReasons.join(' and ')}. Upload and adding or deleting teams are off; you can - still move people between teams or to Unassigned, and rename teams. + {lockReasons.join(' and ')}. Upload and deleting teams are off; you can still + move people between teams or to Unassigned, rename teams and add new ones.

{/if} @@ -819,7 +822,7 @@

Once a team has a submission or teams are published, teams are fixed: no - upload, no adding or deleting teams. Moving people and renaming still work. + upload, no deleting teams. Moving people, renaming and adding teams still work.

@@ -1190,7 +1193,9 @@ > - {#if !locked} + + {#if !locked || t.id === null} - {/if} + {/each}