Esta página aún no está traducida al español — mostrando la versión en inglés.

Members

See who's on your workspace, invite new teammates, and remove members. Admin actions are gated by the `is_admin` prop.

The Members page lives at /app/members. It shows the current workspace's members and any pending invitations. Admin actions are gated.

Page structure

Top of the page:

  • — the current workspace's name (from current_account.name on shared props)

  • Subtitle: "Manage members and invitations."

Sections that follow (in order):

  1. Invite a member card — only if is_admin is true
  2. A horizontal separator
  3. Members ({count}) section — always present
  4. A second horizontal separator + Pending Invitations ({count}) section — only if there is at least one invitation

Invite a member (admin-only)

Shown only when is_admin === true. Card title: Invite a member. Description: "Send an invitation to join this account."

┌─ Invite a member ─────────────────────────────────────────┐
│  Send an invitation to join this account.                 │
│                                                           │
│  Email                     Name (optional)                │
│  [ colleague@example.com ] [ Jane              ] [Invite] │
└───────────────────────────────────────────────────────────┘

Fields:

  • Emailtype="email", required, placeholder colleague@example.com
  • Name (optional) — placeholder Jane

Submit button: Invite. While the request is in flight: "Sending...". Posts to POST /app/members with an invitation payload.

Server responses (verified from MemberController.create/2):

  • Success: flash "Invitation sent!", redirect to /app/members
  • Already a member of this account: flash "This user is already a member of this account.", redirect to /app/members
  • Any other changeset error: flash "Could not send invitation.", redirect to /app/members

Members list

Section heading: Members ({count}). One card per member:

  • Primary label: {first_name} {last_name}
  • Below: {email} muted
  • A rounded pill — Admin when member.roles?.admin is truthy, Member otherwise
  • A Remove button (ghost, destructive-colored) — shown only when is_admin === true

Clicking Remove calls confirm("Remove this member from the account?"). Confirming fires DELETE /app/members/{member.user_id}.

Pending Invitations

Only rendered when invitations.length > 0. Heading: Pending Invitations ({count}). One card per invitation:

  • Primary label: inv.name if present, otherwise inv.email
  • Below (only when inv.name is present): inv.email muted
  • A muted Pending pill
  • A Cancel button (ghost) — shown only when is_admin === true

Clicking Cancel calls confirm("Cancel this invitation?"). Confirming fires DELETE /app/members/{inv.id}.

Delete endpoint (shared by members and invitations)

Verified from MemberController.delete/2: there is one DELETE /app/members/:id route. The controller passes the ID straight to Accounts.remove_account_member(account.id, id) — the same function resolves both user IDs and invitation IDs. After removal, the flash is always the same:

Member removed.

followed by a redirect to /app/members. So even when you cancel an invitation, the confirmation flash reads "Member removed.".

Roles

The JSX reads member.roles?.admin. When truthy it shows Admin, otherwise Member. There are no other role names surfaced on this page.

  • Invitations — the invited user's side of the flow
  • Workspaces — how switching between workspaces works