MemberActions() — supabase Function Reference
Architecture documentation for the MemberActions() function in MemberActions.tsx from the supabase codebase.
Entity Profile
Dependency Diagram
graph TD 0fcab92a_2321_8135_7b0a_0636fbbbac51["MemberActions()"] f6ac5702_c235_901f_8a41_8cfe80ab3f6f["useGetRolesManagementPermissions()"] 0fcab92a_2321_8135_7b0a_0636fbbbac51 -->|calls| f6ac5702_c235_901f_8a41_8cfe80ab3f6f style 0fcab92a_2321_8135_7b0a_0636fbbbac51 fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx lines 38–302
export const MemberActions = ({ member }: MemberActionsProps) => {
const { slug } = useParams()
const { profile } = useProfile()
const [showAccessModal, setShowAccessModal] = useState(false)
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
const organizationMembersDeletionEnabled = useIsFeatureEnabled('organization_members:delete')
const { data: selectedOrganization } = useSelectedOrganizationQuery()
const { data: permissions } = usePermissionsQuery()
const { data: allRoles } = useOrganizationRolesV2Query({ slug })
const { data: members } = useOrganizationMembersQuery({ slug })
const memberIsUser = member.gotrue_id == profile?.gotrue_id
const orgScopedRoles = allRoles?.org_scoped_roles ?? []
const projectScopedRoles = allRoles?.project_scoped_roles ?? []
const isPendingInviteAcceptance = !!member.invited_id
const userMemberData = members?.find((m) => m.gotrue_id === profile?.gotrue_id)
const hasOrgRole =
(userMemberData?.role_ids ?? []).length === 1 &&
orgScopedRoles.some((r) => r.id === userMemberData?.role_ids[0])
const { rolesRemovable } = useGetRolesManagementPermissions(
selectedOrganization?.slug,
orgScopedRoles.concat(projectScopedRoles),
permissions ?? []
)
const roleId = member.role_ids?.[0] ?? -1
const canRemoveMember = member.role_ids.every((id) => rolesRemovable.includes(id))
const { can: canCreateUserInvites } = useAsyncCheckPermissions(
PermissionAction.CREATE,
'user_invites',
{ resource: { role_id: roleId } }
)
const canResendInvite = canCreateUserInvites && hasOrgRole
const { can: canDeleteUserInvites } = useAsyncCheckPermissions(
PermissionAction.DELETE,
'user_invites',
{ resource: { role_id: roleId } }
)
const canRevokeInvite = canDeleteUserInvites && hasOrgRole
const { mutate: deleteOrganizationMember, isPending: isDeletingMember } =
useOrganizationMemberDeleteMutation({
onSuccess: () => {
toast.success(`Successfully removed ${member.primary_email}`)
setIsDeleteModalOpen(false)
},
})
const { mutate: inviteMember, isPending: isCreatingInvite } =
useOrganizationCreateInvitationMutation({
onSuccess: () => {
toast.success('Resent the invitation.')
},
onError: (error) => {
toast.error(`Failed to resend invitation: ${error.message}`)
},
})
const { mutate: deleteInvitation, isPending: isDeletingInvite } =
useOrganizationDeleteInvitationMutation()
const isLoading = isDeletingMember || isDeletingInvite || isCreatingInvite
const handleMemberDelete = () => {
if (!slug) return console.error('slug is required')
if (!member.gotrue_id) return console.error('gotrue_id is required')
deleteOrganizationMember({ slug, gotrueId: member.gotrue_id })
}
const handleResendInvite = (member: OrganizationMember) => {
const roleId = (member?.role_ids ?? [])[0]
const invitedId = member.invited_id
if (!slug) return console.error('Slug is required')
if (!invitedId) return console.error('Member invited ID is required')
deleteInvitation(
{ slug, id: invitedId, skipInvalidation: true },
{
onSuccess: () => {
if (!member.primary_email) return toast.error('Email is required')
const projectScopedRole = projectScopedRoles.find((role) => role.id === roleId)
if (projectScopedRole !== undefined) {
const projects = projectScopedRole.projects.map(({ ref }) => ref)
inviteMember({
slug,
email: member.primary_email,
roleId: projectScopedRole.base_role_id,
projects,
})
} else {
inviteMember({ slug, email: member.primary_email, roleId })
}
},
}
)
}
const handleRevokeInvitation = (member: OrganizationMember) => {
const invitedId = member.invited_id
if (!slug) return console.error('Slug is required')
if (!invitedId) return console.error('Member invited ID is required')
deleteInvitation(
{ slug, id: invitedId },
{ onSuccess: () => toast.success('Successfully revoked the invitation.') }
)
}
if (memberIsUser) {
return (
<div className="flex items-center justify-end">
<LeaveTeamButton />
</div>
)
}
return (
<>
<div className="flex items-center justify-end gap-x-2">
<ButtonTooltip
type="default"
disabled={isPendingInviteAcceptance || !canRemoveMember}
onClick={() => setShowAccessModal(true)}
tooltip={{
content: {
side: 'bottom',
text: isPendingInviteAcceptance
? 'Role can only be changed after the user has accepted the invite'
: !canRemoveMember
? 'You need additional permissions to manage this team member'
: undefined,
},
}}
>
Manage access
</ButtonTooltip>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="text"
className="px-1.5"
disabled={isLoading}
loading={isLoading}
icon={<MoreVertical />}
/>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end" className="w-40">
<>
{isPendingInviteAcceptance ? (
<>
<DropdownMenuItemTooltip
className="gap-x-2"
disabled={!canResendInvite}
onClick={() => handleResendInvite(member)}
tooltip={{
content: {
side: 'left',
text: 'Additional permissions required to resend invitation',
},
}}
>
<Redo2 size={14} />
<p>Resend invitation</p>
</DropdownMenuItemTooltip>
<DropdownMenuSeparator />
<DropdownMenuItemTooltip
className="gap-x-2"
disabled={!canRevokeInvite}
onClick={() => handleRevokeInvitation(member)}
tooltip={{
content: {
side: 'left',
text: 'Additional permissions required to cancel invitation',
},
}}
>
<Trash size={14} />
<p>Cancel invitation</p>
</DropdownMenuItemTooltip>
</>
) : (
organizationMembersDeletionEnabled && (
<DropdownMenuItemTooltip
className="gap-x-2"
disabled={!canRemoveMember}
onClick={() => setIsDeleteModalOpen(true)}
tooltip={{
content: {
side: 'left',
text: 'Additional permissions required to remove member',
},
}}
>
<Trash size={12} />
<p>Remove member</p>
</DropdownMenuItemTooltip>
)
)}
</>
</DropdownMenuContent>
</DropdownMenu>
</div>
<ConfirmationModal
size="large"
visible={isDeleteModalOpen}
loading={isDeletingMember}
title="Confirm to remove member"
confirmLabel="Remove"
variant="warning"
alert={{
title: 'All user content from this member will be permanently removed.',
description: (
<div>
Removing a member will delete all of the user's saved content in all projects of this
organization, which includes:
<ul className="list-disc pl-4 my-2">
<li>
SQL snippets{' '}
<span className="text-foreground">
(both <span className="underline">private</span> and{' '}
<span className="underline">shared</span> snippets)
</span>
</li>
<li>Custom reports</li>
<li>Log Explorer queries</li>
</ul>
<p className="mt-4 text-foreground-lighter">
If you'd like to retain the member's shared SQL snippets, right click on them and
"Duplicate query" in the SQL Editor before removing this member.
</p>
</div>
),
}}
onCancel={() => setIsDeleteModalOpen(false)}
onConfirm={() => {
handleMemberDelete()
}}
>
<p className="text-sm text-foreground-light">
Are you sure you want to remove{' '}
<span className="text-foreground">{member.primary_email}</span> from{' '}
<span className="text-foreground">{selectedOrganization?.name}</span>?
</p>
</ConfirmationModal>
<UpdateRolesPanel
visible={showAccessModal}
member={member}
onClose={() => setShowAccessModal(false)}
/>
</>
)
}
Domain
Subdomains
Source
Frequently Asked Questions
What does MemberActions() do?
MemberActions() is a function in the supabase codebase.
What does MemberActions() call?
MemberActions() calls 1 function(s): useGetRolesManagementPermissions.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free