Repository navigation
feat(members): enrolment tokens and join requests for Business teams - #828
Merged
Merged
Conversation
…ttern bytesToBase64Url/base64UrlToBytes live in utils/base64 and replace the hand-rolled copies in enrollmentToken and passkeyRoundTrip. The token parser reuses GRANT_SECRET_RE, and buildEnrollmentToken refuses a server URL that is not http(s) instead of encoding it anyway.
useServerCapability(flag) carries the refresh effect and the selector; the named hooks stay as one-line wrappers because many callers and test mocks use them.
The select-on-focus input with its Copy button moves out of JoinLinksTab into CopySecretField so the enrolment token can use it too.
Join requests: Approve stays enabled on seat-limit rows so an admin can add seats and approve again; below Business it is disabled with the reason as its title. A pending answer to approve means the email is still unverified and says so. On a phone the row stacks the person above the status and actions instead of overflowing. Enrolment token: a failed load shows the error with a retry and never the create form, so Create cannot silently rotate a live token; a 404 (older server) or TEAM_INVITES_DISABLED hides the section. Stale loads for a previous team are dropped. An expired token says so and points at Rotate, and rotating it keeps its lifetime counted from now. Rotate and Revoke are disabled while busy, rotate toasts say rotated, domains keep the part after the last @ and must be hostname-shaped, and bare 400/403 read as sentences. Refusal mapping and the lapsed-plan refresh live in one place (enrollmentRefusal, enrollmentErrorMessage, runEnrollmentAction), toast punctuation follows the rest of members, and the duplicate error.unavailable string is gone.
The share popover's outside-press handler (useAnchoredPopover) only counted DOM descendants of the panel, so a press in the Rotate/Revoke ConfirmModal, portalled to body, closed the popover. Rotation still went through but the section unmounted and the new secret was lost. The hook now also counts presses React routes through the panel (onMouseDownCapture, wired in PickerSurface), so any dialog the panel opens stays inside it. The token card refetches its counts when the team's join requests reload (the join_requests SSE signal) and keeps showing the fresh token. BusinessLockLine gains a compact size, used in the 11 px enrolment section; its upgrade link now takes the line's size.
33 tasks done
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.
Adds the admin side of enrolment tokens for Business teams: a team admin creates one company-wide token and decides who joins through it. Part of #757. The server part shipped in VoltiusApp/server#128 and is deployed (sha-e5af0d4).
voltius-cli enrolland the first-launch screen that use the token come in a later PR.What changes
voltius-cli enroll <token>example, and is never stored. The live token shows its guards, who created it, when it was last used, and "N joined · M waiting", which update as people join. Rotate and Revoke ask first; rotating keeps the guards and a future expiry, and an expired token says so. On a server without email delivery the approval switch is forced on. Below Business the section shows the lock line and keeps Revoke. An older server without these routes shows nothing.join_requests:<team>event refreshes only this list. The list is cleared and guarded across account switches.vte1.<server>.<id>.<secret>: one TS helper builds and parses it from the account's canonical server URL, with a shared fixture insrc-tauri/src/policy/fixtures/enrollment_token.jsonfor the CLI's Rust twin later.useAnchoredPopover, so everyPickerSurfacegets it.useServerCapabilityhook shape for server capability flags, shared base64url helpers (passkeys use them too, byte-identical output), a shared copy field for one-time secrets used by join links and enrolment, andBusinessLockLinegains a compact size.Screenshots
Desktop and phone layout from the built branch against a private sha-e5af0d4 server, including the rotate flow with real clicks: https://claude.ai/artifact/NMdGCyWfsatGHf7tdJiVsm. The "no seat left" request, the lapsed team and the admin's confirmed email were set by hand, since a self-hosted test server can't reach them by itself.
Tests
tscclean; 916 test files / 8269 tests pass.Known gaps, left for later
Docs: VoltiusApp/docs PR to follow.