Org-scoped data layer for the admin organization overview surface.
Provides the member roster and the set of pending invitations for a single
organization. Both surfaces are inherently per-org: there is no global-scope
behavior. Every query authorizes the admin scope against the requested
organization and filters by organization_id, so an org-admin can never see
another organization's members or invitations (fails closed).
Host schemas are resolved by namespace inference (optional_schema/2),
mirroring Sigra.Admin.Users.Detail. When a membership or invitation schema
is absent (legacy installs), the corresponding function returns [] rather
than raising.
Summary
Functions
Returns the member roster for the admin scope's organization.
Returns only the pending invitations for the admin scope's organization.
Types
@type invitation_row() :: %{ email: String.t() | nil, role: atom() | String.t() | nil, expires_at: DateTime.t() | nil, expired?: boolean() }
Functions
@spec member_roster(map(), Sigra.Admin.Scope.t()) :: [member_row()]
Returns the member roster for the admin scope's organization.
Each row is a map with the full user struct, the membership role, derived
confirmed?/locked? status flags, and a display label. Returns [] when no
membership schema can be resolved. Rows are ordered owners -> admins ->
members, then by downcased display name (falling back to email).
Fails closed: authorizes the org scope and filters by organization_id.
@spec pending_invitations(map(), Sigra.Admin.Scope.t()) :: [invitation_row()]
Returns only the pending invitations for the admin scope's organization.
Pending means accepted_at IS NULL AND revoked_at IS NULL. Each row carries
an expired? flag computed in Elixir against the current time (expires_at < now) to avoid DB-time skew. Returns [] when no invitation schema can be
resolved.
Fails closed: authorizes the org scope and filters by organization_id.