Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
*
Expand All @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -692,8 +695,8 @@
<LockIcon class="mt-0.5 size-3 shrink-0" />
<p class="m-0">
<strong>Teams are fixed:</strong>
{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.
</p>
</div>
{/if}
Expand Down Expand Up @@ -819,7 +822,7 @@
</p>
<p class="m-0">
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.
</p>
</div>

Expand Down Expand Up @@ -1190,7 +1193,9 @@
>
<Pencil class="size-3" />
</button>
{#if !locked}
<!-- While fixed, only a team added here
and not yet saved may go. -->
{#if !locked || t.id === null}
<button
type="button"
class="shrink-0 text-ink-3 hover:text-danger-ink"
Expand Down Expand Up @@ -1231,18 +1236,16 @@
</p>
{/if}

{#if !locked}
<button
type="button"
class="btn btn-sm btn-ghost h-full"
title={projectTeams.length === 0
? 'Add the first team for this project'
: 'Add another team for this project'}
onclick={() => addTeam(p.id)}
>
{projectTeams.length === 0 ? '+ Add Team' : '+'}
</button>
{/if}
<button
type="button"
class="btn btn-sm btn-ghost h-full"
title={projectTeams.length === 0
? 'Add the first team for this project'
: 'Add another team for this project'}
onclick={() => addTeam(p.id)}
>
{projectTeams.length === 0 ? '+ Add Team' : '+'}
</button>
</div>
</div>
{/each}
Expand Down
Loading