Repository navigation
fix(assignments): send each assignment edit as one all-or-nothing request - #120
Merged
Merged
Conversation
…ows in one transaction
…efault role change
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PATCHper row afterwards, and only moves bars whose sole role is the old default.Closes #118
What changed
Backend
Schemas and routes
PUT /tournaments/{tournament_id}/assignments/placements/(manage_members) takes a list ofAssignmentPlacement, each{membership_id, tournament_event_id, tournament_track_id, cells: [{tournament_shift_id, role_id}]}, and returns the resultingAssignmentReadrows.cells: only the difference is inserted or deleted, unchanged rows keep their ids, and emptycellsclears the placement.cellsis an explicit list rather thanshift_ids × role_ids, because a pinned and an unpinned bar can share a track with different roles, and a product would rewrite one when editing the other.TournamentTrackUpdategainsreassign_assignments: bool; with a newdefault_role_id,PATCH .../tracks/{id}/also moves the track's bars whose only role is the old default to the new one in the same commit.reassign_assignmentsadditionally requiresmanage_members, and is a 422 without adefault_role_id.Logic
set_placementsruns every check (get_scoped_or_404,validate_shift_on_event,resolve_assignment_track,authorize_role_grant) before changing any row, so a delete earlier in the request can't change whether a later grant counts as already held.reassign_track_roleincore/tournament/assignments.pyrank-checks every affected member throughauthorize_role_grantbefore moving any row.reassign_track_rolechecks per bar (one member on one event): a bar holding any other role alongside the old default was set up by hand and is left alone, so no row is ever deleted.Frontend
assignmentsApi.setPlacementsandplacementKeyOf(inlib/assignments/board.ts) are new;TournamentTrackUpdategainsreassign_assignments.assignments/page.tsx):createAssignmentRow/deleteAssignmentRoware replaced bysyncPlacements, which sends every placement a gesture touched as one request and swaps those placements' rows for the response.handleResizeCommit,setLaneRoles,handleRemoveand bothhandleDragEndbranches now go throughsyncPlacements; on error the whole gesture rolls back.syncPlacementsapplies only the newer one's response.applyDefaultRoleChangesends one trackPATCHwithreassign_assignmentsinstead of aPATCHper row; the confirm dialog's count and the local update use the newsoleRoleBarshelper inlib/assignments/board.ts, which applies the same per-bar rule.applyDefaultRoleChangenow also updates each event's embedded copy of the track (event.tracks), which is what the track headers render and whatrequestDefaultRoleChangecompares against.buildLanesinlib/assignments/lanes.tstakes a comparator and sorts lanes, instead of leaving them in row order, which followed write history; the board sorts by first name, last name, then membership id, and the member panel sorts its event bars by event name.byNameinlib/memberSort.tsis now exported for the board's comparator.TrackShiftGrid.tsx) and member panel resize (EventBarinAssignmentsSection.tsx) round the dragged edge to the nearest column boundary instead of flooring it; the existing clamp keeps at least the bar's anchor shift.AssignmentsSection.tsx):syncLane,removeLaneandhandleDragEndeach send onesetPlacementsrequest; a drag between tracks clears the source and fills the target in that same request.runWrite(which re-read the member after a failure to find out what half-landed) is removed, andonChangednow fires only after a successful write.Out of scope
POST/PATCH/DELETEassignment routes stay;assignmentsApi.create/update/deleteare now unused by the frontend but left in place.PATCH .../members/{id}/roles/).Test plan
pytestpasses locallyAutomated
tests/api/tournament/test_assignments.py:TestPlacementsRoutecovers adding, removing and keeping rows, stable ids, clearing, a move between events, rows outside the placement, rollback on a 403, a 409 and a 422, a repeated placement, an archived tournament, and a cross-tournament event.tests/api/tournament/test_tracks.py:reassign_assignmentsmoves only bars whose sole role is the old default on that track (per event, across shifts), leaves a bar that also holds the new role, needsmanage_members, is rank-checked before anything changes, and needs adefault_role_id; without the flag rows are untouched.Manual
PUT .../placements/in the Network tab.*/assignments/placements/*in DevTools and drag a bar to another event; the bar returns to the source event and an error toast shows.