| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- 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 can’t 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>
- )
- }
|