import { useParams } from 'common' import { partition } from 'lodash' import { AlertCircle } from 'lucide-react' import { useMemo } from 'react' import { Card, Loading, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from 'ui' import { Admonition } from 'ui-patterns' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { MemberRow } from './MemberRow' import AlertError from '@/components/ui/AlertError' import { useOrganizationRolesV2Query } from '@/data/organization-members/organization-roles-query' import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query' import { useProfile } from '@/lib/profile' export interface MembersViewProps { searchString: string } const MembersView = ({ searchString }: MembersViewProps) => { const { slug } = useParams() const { profile } = useProfile() const { data: members = [], error: membersError, isPending: isLoadingMembers, isError: isErrorMembers, isSuccess: isSuccessMembers, } = useOrganizationMembersQuery({ slug }) const { data: roles, error: rolesError, isSuccess: isSuccessRoles, isError: isErrorRoles, } = useOrganizationRolesV2Query({ slug, }) const filteredMembers = useMemo(() => { return !searchString ? members : members.filter((member) => { if (member.invited_at) { return member.primary_email?.includes(searchString) } if (member.gotrue_id) { return ( member.username.includes(searchString) || member.primary_email?.includes(searchString) ) } return false }) }, [members, searchString]) const [[user], _otherMembers] = partition( filteredMembers, (m) => m.gotrue_id === profile?.gotrue_id ) const userMember = members.find((m) => m.gotrue_id === profile?.gotrue_id) const orgScopedRoleIds = (roles?.org_scoped_roles ?? []).map((r) => r.id) const isOrgScopedRole = orgScopedRoleIds.includes(userMember?.role_ids?.[0] ?? -1) // [Joshen] Temp wait on API level changes but I think it makes sense to hide invites for // project scoped users since they can't see other members to begin with. Not a security issue nonetheless const otherMembers = isOrgScopedRole ? _otherMembers : _otherMembers.filter((x) => !('invited_id' in x)) const sortedMembers = otherMembers.sort((a, b) => (a.primary_email ?? '').localeCompare(b.primary_email ?? '') ) return ( <> {isLoadingMembers && } {isErrorMembers && ( )} {isErrorRoles && ( )} {isSuccessMembers && (
Member MFA Role {[ ...(isSuccessRoles && isSuccessMembers && !isOrgScopedRole ? [ , ] : []), ...(!!user ? [] : []), ...sortedMembers.map((member) => ( )), ...(searchString.length > 0 && filteredMembers.length === 0 ? [

No members matched the search query "{searchString}"

, ] : []), ]}
{searchString ? `${filteredMembers.length} of ${members.length} ${members.length === 1 ? 'member' : 'members'}` : `${members.length || 0} ${members.length === 1 ? 'member' : 'members'}`}
)} ) } export default MembersView