Skip to content

feat(frontend): team assignment page - #323

Merged
sabinem merged 23 commits into
mainfrom
feat/team-assignment-page
Oct 8, 2026
Merged

sabinem merged 23 commits into
mainfrom
feat/team-assignment-page

Conversation

@sabinem

@sabinem sabinem commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Manage Teams: plan in a spreadsheet, filter by answers and preferences, lock once teams are in use

As an organizer assigning 100+ participants to teams, I can now see what everyone answered at registration and which projects they want, bring the right people to the top of the Unassigned list, and plan the whole assignment in a spreadsheet. Once teams are in use, the page protects them.

Before: the page opened with a "Suggest teams" button nobody should use. The upload rules were hidden in a tooltip. Only multiple-choice registration questions showed up, as codes like "B2" that needed a legend. Finding "beginners who want project 3" meant scrolling through every card. Nothing stopped an organizer from reshuffling teams that already had submissions.

Now:

  • "Suggest teams" is gone. Teams are built by dragging, or in the spreadsheet.
  • All registration questions are listed: multiple choice, yes/no and free text.
    • "Show on cards" puts the answer under each name as "A: XL" or "size: S". Yes/no answers show as "Yes" or "No", or as the organizer's own words, e.g. "remote" / "on site".
    • Organizers can give questions short names. These are kept in their own browser.
  • Filters: clicking an answer, or a project in the "Project preferences" box, brings matching unassigned people to the top. Everyone else stays below, greyed out but still draggable.
    • Each answer shows how many unassigned people gave it.
    • Filters show as tags with "Reset filters" and survive a reload.
  • Spreadsheet: projects appear as the numbers shown on the page, in the project and prefers columns, with a project_title column beside the number. The download now includes free-text answers.
  • Upload: the file replaces all teams.
    • A row needs a project number and a team name; any name works, even a single character.
    • Incomplete rows and people missing from the file get a warning and end up unassigned.
    • A row whose project_title no longer matches its number gets a warning too. That catches a file downloaded before a project was approved or rejected.
  • Save deletes teams with nobody in them.
  • Teams are fixed once any team has a submission or teams are published.
    • A notice says why.
    • Upload, Clear all, adding and deleting teams are off.
    • People can still be moved between teams or to Unassigned by hand, and teams renamed. Emptied teams are kept.
    • The save action re-checks and refuses any plan that adds or deletes a team.
  • "How assignment works" explains dragging, saving, locking and the spreadsheet in one place. Other explanatory text was removed from the page.
  • The Unassigned list shows its scrollbar from the start, so a long list no longer looks like it ends where the box does (Chrome, Edge and Safari; Firefox on macOS follows the system setting).

Deliberately left out

  • No backend lock yet. Fixed teams are enforced by the frontend only: the page and its save action. TeamService still accepts adding or deleting teams in that state from other callers. A backend check is planned for a follow-up PR, marked TODO(backend: team-assignment-lock).
  • Per-browser settings: short question names, filters and "Show on cards" choices are kept in the organizer's browser, not shared with co-organizers.
  • CSV formula cells: free-text answers are written to the CSV as entered, so a cell starting with = could be run as a formula by a spreadsheet. This matches how names were already exported.
  • Seed data: no dev hackathon is locked for only one of the two reasons.

@sabinem sabinem changed the title Feat/team assignment page feat(frontend): team assignment page Oct 8, 2026
sabinem added 19 commits October 8, 2026 13:12
Teams are now built from the downloaded spreadsheet or by dragging, so the
automatic suggestion only competed with that for attention. Drop the button,
its handler, and the distribution algorithm nothing else used. PlannedTeam
and initialsOf stay; the CSV import and "+ Add Team" still need them.
The upload rules lived in a tooltip on "Upload CSV", where nobody found
them. Group Download, Upload and a new "How the file works" toggle under a
Spreadsheet label; the panel lists the three steps and what each kind of
edit does on upload, mirroring applyAssignmentCsv's rules. An upload that
reports problems links straight to it.
Only multiple-choice questions reached the Manage Teams legend. Letter all
of them: a tick-box codes as Yes/No like a two-option list, and a free-text
answer is carried whole under the question's letter and shown as a
truncated line under the name, full text on hover. The panel becomes one
row per question with its codes and a "Show on cards" toggle.
Clicking an answer in the questions panel narrows the Unassigned column to
the people who gave it; a free-text question gets a "contains" box.
Answers to one question widen the filter, different questions narrow it.
Each answer shows how many unassigned people gave it. Active filters show
as removable tags above the pool with a count and "Reset filters", and are
kept per hackathon in the browser, keyed by question id and answer text so
a shifted letter cannot redirect them.
Matching, toggling, restoring saved filters and answer counts move from
the Manage Teams page into $lib/utils/teamPoolFilter with unit tests.
Behaviour is unchanged.
… rest

A filter on Unassigned now reorders rather than hides: matches come
first, everyone else follows greyed out under "Not matching" and stays
draggable. Someone dragged back into the pool no longer disappears, and
a filter left on from an earlier visit cannot hide people.
"N want in" on each project row becomes a toggle that brings the
unassigned people who picked that project to the top, shown as a
"Prefers 3" tag beside the answer filters. Projects widen among
themselves and narrow against answers, like a question. Saved project
filters for projects that no longer have a row are dropped on restore.
The spreadsheet is now where teams are planned, so every registration
question gets a column, free text included. Long answers stay in their
cell; the writer already quotes line breaks. Values are written as
stored, matching csvRow's existing choice for names.
The intro paragraph, the questions panel hint and the organizer's
"you are not here" note go; the page's controls speak for themselves
and the spreadsheet rules stay in "How the file works". The loader's
ownerMissingFromPool flag existed only for that note and goes with it.
A "Project preferences" box below the registration questions holds one
pill per approved project with how many unassigned people picked it, so
every filter on the page lives in one place and works one way. The
"N want in" toggles on the project rows go back to plain text. The box
also gets a "Show on cards" toggle for the "Prefers …" line, on by
default and remembered per hackathon like the question ticks.
Codes like "A5" sent the reader to the legend to find out what they
meant. Cards now show the question's letter with the answer itself —
"A: XL", "B: No" — in a small tag, the full question on hover, and a
free-text answer as a "C: …" line of its own. Filter tags read the same
way. The questions box keeps its letters, and its answer pills drop the
codes since the letter is already on the row. Project numbers stay as
they were.
A question's letter in the questions box becomes a button that sets a
short name, so cards and filter tags read "size: S" instead of "B: S".
A yes/no question instead gets its own words for Yes and No, and then
shows "remote" or "on site" alone, falling back to its letter for a
side left unnamed. Names are kept per hackathon in the browser like the
"Show on cards" ticks, and are display-only: filters still match the
stored answer.
A yes/no answer reads "Yes" or "No" on a card, or the organizer's own
words for them, so the letter in front added nothing. Tick-boxes no
longer take a letter in answerLegend, keeping A, B, C gap-free for the
questions that use one, and their naming button in the questions box is
just a pencil.
…ignment

The upload now treats the file as the complete assignment: a row with a
project and a team assigns, a row with neither unassigns, and a team the
file leaves empty is removed. Anything wrong — one of the two columns
missing, an unknown project or person, a duplicate, or no row for someone
on a team — cancels the whole file, so it applies completely or not at
all. Someone unassigned with no row, likely new since the download, only
warns. The page asks before an upload takes people off teams, and "How
the file works" describes the new rules.
An upload now replaces all teams with the ones the file names, each a
new team. A row assigns a person when it has a project from the page and
a team name of at least three characters; any other row, a duplicate, or
a person with no row leaves that person unassigned with a warning. Only a
file without the needed columns is refused. The info box is rewritten
around these rules and keeps only what an organizer needs to know.
…are published

Changing teams after a submission or after publishing them would either
delete a submission or reshuffle people who have been told their team.
The page now says why it is locked and disables Clear all, Upload,
Discard and Save, and the team cards go read-only; download and the
participant filters still work. The save action re-checks, and a failed
submission check locks rather than guesses open. A backend check follows
in a separate pull request.
A team with nobody in it is not part of the assignment, so Save no longer
creates an empty new team and deletes an emptied saved one. The unsaved
changes summary counts empty teams the same way, and an empty team card
says it will be removed.
@sabinem
sabinem force-pushed the feat/team-assignment-page branch from 3fb6b21 to eed5764 Compare October 8, 2026 11:13
macOS hides scrollbars until hover, so a long Unassigned list looked like
it ended at the bottom of its box. A new theme class, .scroll-visible,
styles the scrollbar, which keeps it visible in Chrome, Edge and Safari;
Firefox gets a thin themed one and still follows the system setting.
…heet

The download writes each project as the number the page shows, in the
project and prefers columns, with a project_title column beside it; the
upload reads numbers (titles still work) and warns about a row whose
title no longer matches its number, which catches a file from before the
numbering moved. Page and file share one numbering helper. Team names may
be anything non-empty, down to a single character.
Once a team has a submission or teams are published, only what would
rebuild the teams is off: upload, Clear all, adding and deleting teams.
People can still be moved between teams or to Unassigned and teams
renamed, so a late dropout can be handled; an emptied team is kept. The
save action refuses a plan that adds or deletes a team in that state.
The project_title column goes from the download and the upload: the
on-screen number is what organizers work with. A row whose project is not
one of the page's numbers — a title, a typo, an unknown number — is no
longer an assignment; that person is warned about and left unassigned.
@sabinem
sabinem merged commit fb4b133 into main Oct 8, 2026
3 checks passed
@sabinem
sabinem deleted the feat/team-assignment-page branch October 8, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant