feat(projects): pick each member's project role in the staff picker - #374
Merged
Conversation
The projects API has always accepted `[{ user_id, role }]` for a project's
roster, but the form only ever posted bare ids, so every assignment landed on
the API's `Student` default and there was no way to make anyone a Director or
project Admin from the UI. Editing a project was worse than that: seeding
dropped `member.role`, so a save re-sent ids with no role and silently relied
on the backend's "keep the role they already held" fallback.
The staff picker now carries an assignment per person instead of an id. The
selected chips become rows with a role dropdown, defaulting new picks to the
same `DEFAULT_PROJECT_ROLE` the API would have applied, and edit mode seeds
each row with the role that member already holds. The project page shows the
role beside the name, using the slot `StaffCard` already had for it.
Roles come from `@branch/rbac` rather than the copy that had grown in
`types/project.ts` — the same re-export the projects lambda does, so the
picker's options and the values `validateMembers` accepts cannot drift.
`DropdownSelector` gains `disabled` and `ariaLabel`, which a control repeated
per row needs: the visible label is the row, not the field.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nourshoreibah
force-pushed
the
worktree-project-member-roles
branch
from
August 25, 2026 02:23
448e35c to
8ce2e31
Compare
Contributor
|
🌿 ⏳ Creating preview environment… (logs) |
Contributor
🌿 Preview environment — ready ✅Open: https://d3nmtjoh6ir9ym.cloudfront.net/pr-374/ Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the |
Contributor
|
🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed. |
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.
Problem
The UI gave no way to say what role someone holds on a project. The projects API has accepted
[{ user_id, role }]since roles were introduced (validateMembers,apps/backend/lambdas/projects/validation-utils.ts), butProjectFormModalpostedmembers: number[], so every assignment fell through to the API'sStudentdefault.Editing was the sharper edge: seeding the form dropped
member.roleentirely, so a save re-sent bare ids and relied onsyncMemberships' "keep the role they already held" fallback. Correct by accident, and impossible to change a role through.Change
StaffPickercarries aMemberAssignmentper person instead of a user id. The selected chips become rows: name + email, a role dropdown, and the remove button. New picks start atDEFAULT_PROJECT_ROLE, which is exactly what the API would have applied on its own.ProjectFormModalseeds each row from the role that member already holds and posts the assignments as-is.ProjectDetailViewrenders the role beside the name via thetitleslotStaffCardalready had, so a role you set is visible without reopening the form.types/project.tsre-exportsPROJECT_ROLES/ProjectRole/DEFAULT_PROJECT_ROLEfrom@branch/rbacinstead of keeping the second copy that had grown there — the same re-export the projects lambda does, so the picker's options and the values the API accepts cannot drift.DropdownSelectorgainsdisabledandariaLabel. Its own doc comment already anticipated "callers that repeat the control per row, where the visible label is the row rather than the field"; this is that caller.No backend, schema, or
@branch/rbacchanges — the API side was already there.Tests
New
test/components/ProjectFormModal.test.tsx(4 cases): existing members seed with the role they hold, a changed role reaches the request body, a newly picked member defaults toStudentand posts whatever it is changed to, and a removed member drops out of the roster.Verified locally in
apps/frontend:tsc --noEmitclean,next lintclean,npm run buildgreen, full jest suite 38/38 suites and 366 tests passing.🤖 Generated with Claude Code