Skip to content

feat(teams): Team Management v0.8.0 Step 5 — Control Center UI - #35

Merged
man4ish merged 1 commit into
mainfrom
feature/team-management-v0.8.0-step5-teams-ui
Aug 11, 2026
Merged

feat(teams): Team Management v0.8.0 Step 5 — Control Center UI#35
man4ish merged 1 commit into
mainfrom
feature/team-management-v0.8.0-step5-teams-ui

Conversation

@man4ish

@man4ish man4ish commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Steps 1–4 of Team Management v0.8.0 are already merged in omnibioai-auth (PR #41). This PR implements Step 5 only: the Control Center UI catch-up for the per-member endpoints those steps shipped.

  • Granular team-member management APIs (fetchTeamMembers, inviteTeamMember, updateTeamMemberRole, removeTeamMember, leaveTeam, updateTeam) are now wired into frontend/cc-ui/src/teams.ts, matching omnibioai-auth's current routes_teams.py/schemas/teams.py contract exactly.
  • The old bulk-replace membership UI (TeamMemberSelector.tsx, updateTeamMembers) is removed — no remaining references anywhere in the app.
  • New TeamMembersPanel.tsx provides the per-member roster: role changes, invite, remove (two-step confirm), and self-service leave, plus a rename/description edit form on TeamRow.tsx.
  • Fix found during review: backend/src/control_center/api/routes_team_proxy.py had never been extended for Steps 1–4's per-member endpoints — only the original 4 Phase 3 PR3C routes were proxied. Without the 6 routes added here (PATCH /{team_id}, GET .../members, POST .../invite, PUT .../members/{user_id}/role, DELETE .../members/{user_id}, POST .../leave), the new frontend controls would 404 in the real deployed app. Same thin-relay pattern as every existing proxy route — no authorization decision made in this layer.

Security

  • No team_role (or any authorization flag) is ever sent from the client — the backend's require_team_manage_permission (Step 4) is authoritative; every mutating control renders unconditionally regardless of the viewer's guessed role, same "frontend hiding is not authorization" posture this app already established (PR3B/PR3C).
  • No X-Team-Id/X-Workspace-Id header introduced anywhere.
  • Team IDs used in API calls come only from server-provided Team/TeamMember data, never free-typed.
  • No workspace-isolation or collaboration work is included in this PR.

Tests

  • Frontend: 436/436 passing (vitest), tsc -b --noEmit clean.
  • Backend: 949/949 passing (pytest), 99.80% coverage (98% gate).
  • No lint command configured in this repo.

🤖 Generated with Claude Code

Team Management v0.8.0 Step 5 -- wires Control Center's Teams UI up to
the per-member endpoints omnibioai-auth Steps 1-4 already shipped
(invite/role-change/remove/leave, plus PATCH rename), replacing the old
full-replace membership picker.

Frontend (frontend/cc-ui):
- teams.ts: drop updateTeamMembers (old bulk PUT .../members), add
  fetchTeamMembers/inviteTeamMember/updateTeamMemberRole/
  removeTeamMember/leaveTeam/updateTeam, matching omnibioai-auth's
  TeamMemberOut/TeamUpdate schemas exactly.
- TeamMembersPanel.tsx (new): per-member roster with role select,
  invite form, remove (two-step confirm), and self-service leave.
  Replaces TeamMemberSelector.tsx (removed) -- no remaining references.
- TeamRow.tsx / TeamsCard.tsx: wire in TeamMembersPanel, add the
  rename/description edit form, description on the create form.
- Every mutating control renders unconditionally regardless of the
  viewer's guessed role; the backend's require_team_manage_permission
  is what actually decides, same "frontend hiding is not authorization"
  posture this app already established (PR3B/PR3C). No team_role is
  ever sent to the backend; no X-Team-Id/X-Workspace-Id header.
- OrganizationDetailPage.test.tsx: mocks TeamsCard instead of
  re-asserting its internals (TeamsCard/TeamRow/TeamMembersPanel now
  carry that coverage directly), mirroring TeamsPage.test.tsx's
  existing precedent for this same component.

Backend (backend/src/control_center):
- routes_team_proxy.py had never been extended for Steps 1-4's
  per-member endpoints -- only the original 4 Phase 3 PR3C routes were
  proxied. Adds the 6 missing routes (PATCH /{team_id}, GET .../members,
  POST .../invite, PUT .../members/{user_id}/role,
  DELETE .../members/{user_id}, POST .../leave) so this UI is actually
  reachable; without this the new controls above 404 in the real app.
  Same thin-relay pattern as every existing proxy route -- no
  authorization decision made here.

Tests: 436/436 frontend (vitest), 949/949 backend (pytest, 99.80% cov).
tsc -b --noEmit clean. No lint config in this repo.

No workspace-isolation or collaboration work included.
@man4ish
man4ish merged commit f9b46f0 into main Aug 11, 2026
2 checks passed
man4ish added a commit that referenced this pull request Aug 12, 2026
Mode B Phase 2: adds a top-bar TeamSwitcher that lets a user move
between their personal workspace (team_id: null) and any team they
belong to, backed by the existing auth-service POST /auth/switch-team.

- backend: routes_auth_proxy.py gains a pure relay for
  /auth/switch-team (same _proxy_to_auth pattern as every other route
  in this file; this service never inspects or trusts team_id).
- frontend: auth.ts adds SessionUser.teamId/teamRole and switchTeam(),
  which persists the reissued access token and re-validates the
  session before the caller may reload -- never a locally-computed
  team_id, no X-Team-Id/X-Workspace-Id header.
- frontend: new TeamSwitcher.tsx/test.tsx, wired into TopAppBar.

Rebased onto main (PR #35, Team Management v0.8.0 Step 5); two test
fixtures (TeamSwitcher.test.tsx's Team mocks, TeamMembersPanel.test.tsx's
SessionUser mock) updated to match the interfaces both branches now
share -- no functional changes to Step 5's own implementation.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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