Appearance
Manage Users
User management interface for inviting new users, assigning roles, and administering existing organisation members. Supports filtering by billing entity and searching by name. Has two versions controlled by a feature flag.
Route
/organisations/:organisationId/company-settings/manage-users
Roles
CompanyAdmin, CompanyOwner, TenantAdmin
TeamAdmin users can access this page but with reduced permissions: they can only invite other TeamAdmin users and cannot change roles of existing users.
Version Router
The page entry point (ManageUsers/Index.svelte) renders either V1 or V2 based on the useManageUsersV2 feature flag.
useManageUsersV2 = false-- RendersManageUsers/V1/Index.svelte(default)useManageUsersV2 = true-- RendersManageUsers/V2/Index.svelte
Both versions receive the params object containing organisationId.
V1 Layout
Rendered inside the Company Settings tab container (tab index 2, "Manage Users") using the vertical sidebar tab layout. Contains three sections laid out horizontally with flex-wrap:
- Invite New Users (left, 30rem wide)
- Pending Users (centre)
- Existing Users (right)
Billing Entity Filter
A dropdown at the top allows filtering users by billing entity:
- Default selection: "All Billing Entities"
- CompanyOwner sees all billing entities
- CompanyAdmin associated with the home organisation sees all billing entities
- Other roles see only their associated entities (derived from
$user.remundoIdentity.associatedEntities) - A search
InputGroupbeside the dropdown filters the displayed users by name
Invite Form (V1)
The V1 invite form (ManageUsers/V1/InviteForm.svelte) is displayed inline when the "Invite" button is clicked. The form is rendered within the invitation section.
Pending Users (V1)
Lists users who have been invited but have not yet accepted. Filtered by the selected billing entity and search query.
- Each pending user card shows invitation details.
- Invitations can be voided (removed).
- TeamAdmin users cannot void invitations (disabled state).
Existing Users (V1)
Lists active organisation members. Filtered by the selected billing entity and search query.
- Role changes dispatch
AddRoleand (if necessary)RemoveRolecommands with polling until confirmed. - When changing from CompanyAdmin to TeamAdmin (or vice versa), the old role is removed first via
removeRoleIfNecessary. - Associated entities can be changed per user; dispatches
addAssociatedEntitieswith polling until confirmed. - TeamAdmin users cannot change roles or associated entities (disabled state).
V2 Layout
Rendered with a page header (PageHeader) above the tab container, then uses the horizontal tab layout (useHorizontalTabs). Contains:
- Page Header -- Title and description
- Invitation Filter (
InvitationFilter) -- billing entity dropdown and search input - Invite Form (V2,
ManageUsers/Components/InviteForm.svelte) -- collapsible invite card - Card View -- Two accordion sections for pending and accepted users
Invitation Filter (V2)
The InvitationFilter component provides:
- A billing entity dropdown with an "All Billing Entities" default option
- A search input bound to
searchQuery - Events:
all-billing-entities-selected,entity-selected-from-dropdown
Card View (V2)
The CardView component (ManageUsers/V2/CardView/Index.svelte) splits users into two InvitationAccordion sections:
- Pending -- Invitations where
isPendingis true, mapped toUserCardobjects showing email, role, job title, associated entities, and status text. - Accepted -- Active users mapped to
UserCardobjects showing display name, email, role, job title, associated entities, and "Accepted" status.
Filtering logic:
- Invitations filtered by
selectedEntity(associated entities include the entity ID) andsearchQuery(case-insensitive match on invitee email). - Users filtered by
selectedEntity(exact match: user has exactly one associated entity matching the selection) andsearchQuery(case-insensitive match on display name or email). - Accepted users are sorted with CompanyOwner, TenantAdmin, and TenantContributor roles appearing first.
Each card provides action icons dispatching events:
edit-invitation-icon-click/edit-user-icon-click-- Opens theEditInvitationModalorEditUserModal.delete-invitation-icon-click/delete-user-icon-click-- Opens theDeleteInvitationModalorDeleteUserModal.resend-invitation-icon-click-- Opens theResendInvitationModal.
A table view placeholder exists ($manageUsersListViewSelected store) but is not yet implemented in V2 (commented out as <!-- <TableView /> -->).
Invite Form (Shared, V2 Component)
The V2 invite form (ManageUsers/Components/InviteForm.svelte) is used by both V2 and the expanded details view. It is a collapsible card with the following fields:
- Email (required; valid email format; must not already exist in the organisation via
doesEmailExistInOrganisation; forced lowercase on input; allowed characters:a-zA-Z0-9@\-\s'.; duplicate and invalid email errors shown contextually) - Job Title (required; max 50 characters; allowed characters:
a-zA-Z'\-) - Role (required; dropdown via
RoleSelector)- CompanyAdmin and above can select: CompanyAdmin, TeamAdmin
- TeamAdmin can only select: TeamAdmin
- Team (conditional; shown only when TeamAdmin role is selected; multi-select via
TeamSelector; toggle individual teams on/off) - Create Team inline (available within team selector via
TeamNameAndColourField; team name + colour picker; team name max 30 characters; adds team viacreateTeamAndAddToStore) - Billing Entity (required; at least one must be selected via
BillingEntitySelector; auto-selected if only one entity exists)
Each field has an InfoTip tooltip icon with contextual help text.
The Invite button is disabled until:
- Email is valid and not a duplicate
- Job title is within range (0-50 characters)
- No inline team creation is in progress (
addingTeamis false) - Palette is not open (
showPalleteis false) - Team name is under 30 characters
- At least one billing entity is selected
Submission Process
- A new UUID is generated for the invitation ID.
- If teams are selected,
addPendingAdminsToTeamAndAddToStoreis called for each selected team. - Polls via
pollingGetConditionongetTeamsuntil all selected teams include the invitation ID in theirpendingAdmins. - The invitation is sent via
sendInvitationAndUpdateStore. - Invitation expiry is set to
inviteExpiryTimeminutes from now (viaaddMinutes). - Form fields are reset after successful submission.
- An
invite-addedevent is dispatched with the invitation object.
Data Loading
- On initialisation, a single
getManageUsersPageAPI call returns users, teams, and invitations. - Teams are processed to convert
pendingAdmins,admins, andmemberstoSetobjects. - Users, teams, and invitations are loaded into their respective stores (
usersStore,teamsStore,invitationsStore).
Behavior notes
- On initialisation failure, the user is redirected to
/pages-404. - All polling uses
pollingGetConditionfor eventual consistency. - The TeamAdmin disabled state is determined by
pickPrincipalRoleat component initialisation. - V2 modals (Edit, Delete, Resend) are rendered conditionally at the page level and receive the selected invitation or user as a prop.