Files
supabase/apps/studio/components/interfaces/Settings/General/ProjectAccessSection.tsx
Joshen Lim 7f5865872a Enforce noUnusedLocals and noUnusedParameters in tsconfig.json + fix all related issues (#45264)
## Context

Enforce `noUnusedLocals` and `noUnusedParameters` in tsconfig.json + fix
all related issues
2026-04-27 17:42:34 +08:00

196 lines
7.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link'
import {
Badge,
Button,
Card,
CardContent,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from 'ui'
import {
PageSection,
PageSectionContent,
PageSectionMeta,
PageSectionSummary,
PageSectionTitle,
} from 'ui-patterns/PageSection'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { summarizeProjectAccess } from './General.utils'
import AlertError from '@/components/ui/AlertError'
import { useOrganizationRolesV2Query } from '@/data/organization-members/organization-roles-query'
import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { useProfile } from '@/lib/profile'
export const ProjectAccessSection = () => {
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
const { profile } = useProfile()
const isBranch = Boolean(project?.parent_project_ref)
const projectRef = project?.parent_project_ref ?? project?.ref
const {
data: organizationMembers = [],
error: organizationMembersError,
isPending: isLoadingOrganizationMembers,
isError: isErrorOrganizationMembers,
} = useOrganizationMembersQuery(
{ slug: organization?.slug },
{
enabled: !!organization?.slug,
}
)
const {
data: organizationRoles,
error: organizationRolesError,
isPending: isLoadingOrganizationRoles,
isError: isErrorOrganizationRoles,
} = useOrganizationRolesV2Query(
{ slug: organization?.slug },
{
enabled: !!organization?.slug,
}
)
const userMemberData = organizationMembers.find(
(member) => member.gotrue_id === profile?.gotrue_id
)
const orgScopedRoleIds = new Set(
(organizationRoles?.org_scoped_roles ?? []).map((role) => role.id)
)
const hasProjectScopedRoles = (organizationRoles?.project_scoped_roles ?? []).length > 0
const isOrgScopedRole = (userMemberData?.role_ids ?? []).some((roleId) =>
orgScopedRoleIds.has(roleId)
)
const hasLimitedVisibility = hasProjectScopedRoles && !isOrgScopedRole
const {
visibleMembers,
hiddenMembersCount,
projectMemberCount,
organizationMemberCount,
shouldShowOrgComparison,
hasOrganizationWideAccess,
} = summarizeProjectAccess({
organizationMembers,
roles: organizationRoles,
projectRef,
hasLimitedVisibility,
currentUserId: profile?.gotrue_id,
})
const isLoadingProjectAccess = isLoadingOrganizationMembers || isLoadingOrganizationRoles
const isErrorProjectAccess = isErrorOrganizationMembers || isErrorOrganizationRoles
const projectAccessError = organizationMembersError ?? organizationRolesError
if (isBranch) return null
const projectAccessTitle = hasLimitedVisibility
? 'You have limited visibility in this organization'
: shouldShowOrgComparison && hasOrganizationWideAccess
? 'Organization-wide access'
: 'Restricted project access'
const projectAccessDescription = hasLimitedVisibility
? 'Your access is limited to specific projects, so you cant see all members or settings.'
: shouldShowOrgComparison
? hasOrganizationWideAccess
? `All ${organizationMemberCount} organization members can access this project.`
: `${projectMemberCount} of ${organizationMemberCount} organization members can access this project.`
: `${projectMemberCount} project member${projectMemberCount === 1 ? '' : 's'} currently ${projectMemberCount === 1 ? 'has' : 'have'} access.`
return (
<PageSection>
<PageSectionMeta>
<PageSectionSummary>
<PageSectionTitle>Project access</PageSectionTitle>
</PageSectionSummary>
</PageSectionMeta>
<PageSectionContent>
{isErrorProjectAccess ? (
<AlertError error={projectAccessError} subject="Failed to retrieve project members" />
) : (
<Card>
{isLoadingProjectAccess ? (
<CardContent>
<GenericSkeletonLoader />
</CardContent>
) : (
<>
<CardContent className="flex flex-col gap-4">
<div className="flex flex-col @lg:flex-row @lg:items-center @lg:justify-between gap-3">
<div>
<p className="text-sm">{projectAccessTitle}</p>
<p className="text-sm text-foreground-light">{projectAccessDescription}</p>
</div>
{!!organization?.slug && (
<Button asChild type="default">
<Link href={`/org/${organization.slug}/team`}>
{hasLimitedVisibility ? 'View team' : 'Manage members'}
</Link>
</Button>
)}
</div>
</CardContent>
{visibleMembers.length > 0 ? (
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow>
<TableHead>Member</TableHead>
<TableHead className="w-[180px]">Role</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{visibleMembers.map((member) => (
<TableRow key={member.id}>
<TableCell className="align-top">
<div className="flex items-center gap-2">
<p className="text-sm text-foreground break-all">{member.email}</p>
{member.id === profile?.gotrue_id && (
<Badge variant="default">You</Badge>
)}
</div>
</TableCell>
<TableCell className="align-top text-sm text-foreground-light w-[180px]">
{member.role ?? ''}
</TableCell>
</TableRow>
))}
{hiddenMembersCount > 0 && (
<TableRow className="[&>td]:hover:bg-inherit">
<TableCell colSpan={2}>
<p className="text-sm text-foreground-lighter">
+{hiddenMembersCount} more project member
{hiddenMembersCount === 1 ? '' : 's'}
</p>
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</CardContent>
) : (
<CardContent className="pt-0">
<p className="text-sm text-foreground-lighter">
No visible project members in your current access scope.
</p>
</CardContent>
)}
</>
)}
</Card>
)}
</PageSectionContent>
</PageSection>
)
}