UpdateRolesPanel() — supabase Function Reference
Architecture documentation for the UpdateRolesPanel() function in UpdateRolesPanel.tsx from the supabase codebase.
Entity Profile
Dependency Diagram
graph TD 26567e94_e158_8cd1_b1e4_49300baf745f["UpdateRolesPanel()"] f6ac5702_c235_901f_8a41_8cfe80ab3f6f["useGetRolesManagementPermissions()"] 26567e94_e158_8cd1_b1e4_49300baf745f -->|calls| f6ac5702_c235_901f_8a41_8cfe80ab3f6f 85313509_9893_a7f1_0325_abddf0cf38b3["formatMemberRoleToProjectRoleConfiguration()"] 26567e94_e158_8cd1_b1e4_49300baf745f -->|calls| 85313509_9893_a7f1_0325_abddf0cf38b3 style 26567e94_e158_8cd1_b1e4_49300baf745f fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesPanel.tsx lines 54–378
export const UpdateRolesPanel = ({ visible, member, onClose }: UpdateRolesPanelProps) => {
const { slug } = useParams()
const { data: organization } = useSelectedOrganizationQuery()
const isOptedIntoProjectLevelPermissions = useHasAccessToProjectLevelPermissions(slug as string)
const { data: permissions } = usePermissionsQuery()
const { data: allRoles, isSuccess: isSuccessRoles } = useOrganizationRolesV2Query({ slug })
const { data: projectsData } = useOrgProjectsInfiniteQuery({ slug })
const totalNumOrgProjects = projectsData?.pages[0].pagination.count ?? 0
const orgProjects =
useMemo(() => projectsData?.pages.flatMap((page) => page.projects), [projectsData?.pages]) || []
// [Joshen] We use the org scoped roles as the source for available roles
const orgScopedRoles = allRoles?.org_scoped_roles ?? []
const projectScopedRoles = allRoles?.project_scoped_roles ?? []
const { rolesAddable, rolesRemovable } = useGetRolesManagementPermissions(
organization?.slug,
orgScopedRoles.concat(projectScopedRoles),
permissions ?? []
)
const cannotAddAnyRoles = orgScopedRoles.every((r) => !rolesAddable.includes(r.id))
const [showConfirmation, setShowConfirmation] = useState(false)
const [showProjectDropdown, setShowProjectDropdown] = useState(false)
const [projectsRoleConfiguration, setProjectsRoleConfiguration] = useState<
ProjectRoleConfiguration[]
>([])
const originalConfiguration =
allRoles !== undefined ? formatMemberRoleToProjectRoleConfiguration(member, allRoles) : []
const originalConfigurationType =
originalConfiguration.length === 1 &&
!!orgScopedRoles.find((r) => r.id === originalConfiguration[0].roleId)
? 'org-scope'
: 'project-scope'
const isApplyingRoleToAllProjects =
projectsRoleConfiguration.length === 1 && projectsRoleConfiguration[0]?.ref === undefined
const canSaveRoles = projectsRoleConfiguration.length > 0
const lowerPermissionsRole = orgScopedRoles.find((r) => r.name === 'Developer')?.id
const noAccessProjects = orgProjects.filter((project) => {
return !projectsRoleConfiguration.some((p) => p.ref === project.ref)
})
const numberOfProjectsWithAccess = orgProjects.length - noAccessProjects.length
const hasNoChanges = isEqual(projectsRoleConfiguration, originalConfiguration)
const onSelectProject = (project: OrgProject) => {
setProjectsRoleConfiguration(
projectsRoleConfiguration.concat({
ref: project.ref,
name: project.name,
roleId: lowerPermissionsRole ?? orgScopedRoles[0].id,
})
)
setShowProjectDropdown(false)
}
const onRemoveProject = (ref?: string) => {
if (ref === undefined) return
setProjectsRoleConfiguration(projectsRoleConfiguration.filter((p) => p.ref !== ref))
}
const onSelectRole = (value: string, project: ProjectRoleConfiguration) => {
if (project.ref !== undefined) {
setProjectsRoleConfiguration(
projectsRoleConfiguration.map((p) => {
if (p.ref === project.ref) {
return { ref: p.ref, name: p.name, roleId: Number(value) }
} else {
return p
}
})
)
} else {
setProjectsRoleConfiguration([{ ref: undefined, roleId: Number(value) }])
}
}
const onToggleApplyToAllProjects = (isApplyAllProjects: boolean) => {
const roleIdToApply = lowerPermissionsRole ?? orgScopedRoles[0].id
if (isApplyAllProjects) {
if (originalConfigurationType === 'org-scope') {
setProjectsRoleConfiguration(originalConfiguration)
} else {
setProjectsRoleConfiguration([{ ref: undefined, name: undefined, roleId: roleIdToApply }])
}
} else {
if (originalConfigurationType === 'project-scope') {
setProjectsRoleConfiguration(originalConfiguration)
} else {
setProjectsRoleConfiguration(
orgProjects.map((p) => {
return { ref: p.ref, name: p.name, roleId: roleIdToApply }
})
)
}
}
}
useEffect(() => {
if (visible && isSuccessRoles) {
const roleConfiguration = formatMemberRoleToProjectRoleConfiguration(member, allRoles)
setProjectsRoleConfiguration(roleConfiguration)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible, isSuccessRoles])
return (
<>
<Sheet open={visible} onOpenChange={() => onClose()}>
<SheetContent
showClose={false}
size="default"
className="bg-surface-200 p-0 flex flex-row gap-0 md:w-[600px] lg:w-[600px] w-full"
>
<div className="flex flex-col grow w-full">
<SheetHeader className="py-3 flex flex-row justify-between gap-x-4 items-center border-b bg-transparent">
<p className="truncate" title={`Manage access for ${member.username}`}>
Manage access for {member.username}
</p>
<DocsButton href={`${DOCS_URL}/guides/platform/access-control`} />
</SheetHeader>
<SheetSection className="h-full overflow-auto flex flex-col">
{isOptedIntoProjectLevelPermissions && (
<div className="flex items-center gap-x-4 border-b border-border pb-4">
<Switch
disabled={cannotAddAnyRoles}
checked={isApplyingRoleToAllProjects}
onCheckedChange={onToggleApplyToAllProjects}
/>
<p className="text-sm">Apply roles to all projects in the organization</p>
</div>
)}
{projectsRoleConfiguration.length === 0 && (
<Alert_Shadcn_>
<WarningIcon />
<AlertTitle_Shadcn_>
Team members need to be assigned at least one role
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You may not remove all roles from a team member
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
{!isApplyingRoleToAllProjects &&
projectsRoleConfiguration.length > 0 &&
projectsRoleConfiguration.length < totalNumOrgProjects && (
<Collapsible_Shadcn_ className="bg-alternative border rounded-lg py-4 group">
<CollapsibleTrigger_Shadcn_ className="w-full text-left px-4 flex items-center justify-between">
<span className="text-sm">
{hasNoChanges
? `This member only has access to ${numberOfProjectsWithAccess} project${numberOfProjectsWithAccess > 1 ? 's' : ''}`
: `This member will only have access to ${numberOfProjectsWithAccess} project${numberOfProjectsWithAccess > 1 ? 's' : ''}`}
</span>
<ChevronDown
size={14}
className="transition group-data-[state=open]:-rotate-180"
/>
</CollapsibleTrigger_Shadcn_>
<CollapsibleContent_Shadcn_ className="text-foreground-light text-sm px-4">
<p>
{member.username} {hasNoChanges ? 'does' : 'will'} not have access to the
following {noAccessProjects.length} project
{noAccessProjects.length > 1 ? 's' : ''}:
</p>
<ul className="list-disc pl-6">
{noAccessProjects.map((project) => {
return <li key={project.ref}>{project.name}</li>
})}
</ul>
</CollapsibleContent_Shadcn_>
</Collapsible_Shadcn_>
)}
<div className="flex flex-col divide-y divide-border">
{projectsRoleConfiguration.map((project) => {
const name = project.ref === undefined ? 'All projects' : project.name
const role = orgScopedRoles.find((r) => {
if (project.baseRoleId !== undefined) return r.id === project.baseRoleId
else return r.id === project.roleId
})
const canRemoveRole = rolesRemovable.includes(role?.id ?? 0)
return (
<div
key={`${project.ref}-${project.roleId}`}
className="flex items-center justify-between py-2"
>
<p className="text-sm">{name}</p>
<div className="flex items-center gap-x-2">
{cannotAddAnyRoles ? (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center justify-between rounded-md border border-button bg-button px-3 py-2 text-sm h-10 w-56 text-foreground-light">
{role?.name ?? 'Unknown'}
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
Additional permissions required to update role
</TooltipContent>
</Tooltip>
) : (
<Select_Shadcn_
value={(project?.baseRoleId ?? project.roleId).toString()}
onValueChange={(value) => onSelectRole(value, project)}
>
<SelectTrigger_Shadcn_
className={cn(
' w-40',
role?.name === undefined && 'text-foreground-light'
)}
>
{role?.name ?? 'Please select a role'}
</SelectTrigger_Shadcn_>
<SelectContent_Shadcn_>
<SelectGroup_Shadcn_>
{(orgScopedRoles ?? []).map((role) => {
const canAssignRole = rolesAddable.includes(role.id)
return (
<SelectItem_Shadcn_
key={role.id}
value={role.id.toString()}
className="text-sm hover:bg-selection cursor-pointer"
disabled={!canAssignRole}
>
{role.name}
</SelectItem_Shadcn_>
)
})}
</SelectGroup_Shadcn_>
</SelectContent_Shadcn_>
</Select_Shadcn_>
)}
{!isApplyingRoleToAllProjects && (
<ButtonTooltip
type="text"
disabled={!canRemoveRole}
className="px-1"
icon={<X />}
onClick={() => onRemoveProject(project?.ref)}
tooltip={{
content: {
side: 'bottom',
text: !canRemoveRole
? 'Additional permission required to remove role from member'
: 'Remove access to project',
},
}}
/>
)}
</div>
</div>
)
})}
</div>
{!isApplyingRoleToAllProjects && (
<OrganizationProjectSelector
open={showProjectDropdown}
setOpen={setShowProjectDropdown}
modal={true}
onSelect={onSelectProject}
renderTrigger={() => (
<Button type="default" className="w-min">
Add project
</Button>
)}
renderRow={(project) => {
const hasRoleAssigned = projectsRoleConfiguration.some(
(p) => p.ref === project.ref
)
return (
<div className="w-full flex items-center justify-between">
<span className="truncate">{project.name}</span>
{hasRoleAssigned && <p className="w-[45%] text-right">Already assigned</p>}
</div>
)
}}
isOptionDisabled={(project) =>
projectsRoleConfiguration.some((p) => p.ref === project.ref)
}
/>
)}
</SheetSection>
<SheetFooter className="flex items-center !justify-end px-5 py-4 w-full border-t">
<Button type="default" disabled={false} onClick={() => onClose()}>
Cancel
</Button>
<Button
loading={false}
disabled={!canSaveRoles || hasNoChanges}
onClick={() => {
setShowConfirmation(true)
}}
>
Save roles
</Button>
</SheetFooter>
</div>
</SheetContent>
</Sheet>
<UpdateRolesConfirmationModal
visible={showConfirmation}
member={member}
projectsRoleConfiguration={projectsRoleConfiguration}
onClose={(success) => {
setShowConfirmation(false)
if (success) onClose()
}}
/>
</>
)
}
Domain
Subdomains
Source
Frequently Asked Questions
What does UpdateRolesPanel() do?
UpdateRolesPanel() is a function in the supabase codebase.
What does UpdateRolesPanel() call?
UpdateRolesPanel() calls 2 function(s): formatMemberRoleToProjectRoleConfiguration, useGetRolesManagementPermissions.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free