Skip to content

feat(teams): several owners per team, and a primary owner who can hand over (#652) - #657

Merged
kipavy merged 1 commit into
devfrom
feat/multi-owner-652
Oct 18, 2026
Merged

kipavy merged 1 commit into
devfrom
feat/multi-owner-652

Conversation

@kipavy

@kipavy kipavy commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Closes #652. Needs VoltiusApp/server#114.

What changes

  • Owner role in the member panel. Owners now see the Owner chip in a member's roles and can grant or remove it. Both ask for confirmation first. Managers still never see it.
  • Co-owners. An Owner can remove another Owner and edit their roles. A co-owner can leave the team.
  • Primary owner. The creator (teams.owner_id) is shown as the primary owner in an "Ownership" section. Billing is attached to that account, and only it can delete the team.
    • It can't be removed or leave. The panel tells it to hand over first.
    • Another Owner's panel offers "Make primary owner". Self-hosted, any Owner sees it. On the cloud only the primary owner does, and the confirmation says the new owner's own subscription pays from then on.
    • Server refusals map to messages: 402 needs a plan with enough seats, 409 make them an Owner first, 403 only the primary owner.
  • Old servers. All of it is gated on multi_owner from /v1/meta. Against an older server the panel behaves as before.

Code

  • services/teamOwnership.ts: pure memberOwnership(ctx, target). Decides protected / leave / grant Owner / make primary. The roster and the panel both use it.
  • hooks/useOwnershipContext.ts: reads the server capabilities, the team's owner_id and the viewer's roles into that context.
  • teamService.transferBillingOwner + teamStore.transferBillingOwner, then reload the teams list.
  • isTeamOwner is renamed isPrimaryOwner. Existing callers (delete team, make private, business-lock banner) already meant that.
  • New strings in all six locales.

Tests

  • teamOwnership.test.ts: the rules, including the cloud vs self-hosted transfer and old-server fallback.
  • MemberDetailPanel.ownership.test.tsx: Owner chip shown to Owners only, grant asks first, primary owner protected, transfer offered per mode, leave rules.
  • tsc clean. Members, permissions and i18n suites pass (298).

Checked live

The app ran headless against a local build of server#114 (self-hosted). On it I granted Owner to a Manager, made another Owner primary, and checked the old primary owner then gets "Leave team". The server state matched after each step. I also took phone-width captures with the Android shell forced in the desktop build; those are layout only, not a device run.

Not here

  • Inviting someone directly as Owner from the invite screen. The server allows it for Owners; promoting after they join works today.
  • The cloud wording of the transfer dialog was not captured in screenshots.

…d over (#652)

Owners now see the Owner role in a member's roles and can grant or remove
it, after a confirmation. A co-owner is no longer out of an Owner's reach:
they can be removed, and can leave on their own.

The team's creator becomes the primary owner, the account billing sits
on. The member panel says so, explains that they must hand it over
before leaving, and offers "Make primary owner" on another Owner. On a
self-hosted server any Owner may do that. On the cloud only the primary
owner may, and the confirmation says the new owner's own subscription
pays from then on.

The controls appear only when the server advertises multi_owner, so an
older server keeps the single-owner behaviour. The ownership rules live
in a pure memberOwnership model shared by the roster and the panel.
isTeamOwner is renamed isPrimaryOwner, since owner_id now means exactly
that.
@kipavy
kipavy merged commit bd2c9dd into dev Oct 18, 2026
5 checks passed
@kipavy
kipavy deleted the feat/multi-owner-652 branch October 18, 2026 22:24
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