mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 10:25:06 +03:00
Follow-up to #48344: collapses the two resolution paths for the Admonition module into one. `src/admonition.tsx` was a back-compat shim re-exporting `src/Admonition/`. Two ways to resolve one module is exactly what produced the macOS self-import bug fixed in #48344, and the local typecheck errors that #48374 worked around. This removes the shim and standardizes on the PascalCase subpath, matching every other export in the package. **Changed:** - Codemodded all 246 `ui-patterns/admonition` imports to `ui-patterns/Admonition` (240 `.tsx`, 5 `.mdx`, 1 `.ts` across studio, docs, www, design-system, and lite-studio) - Pointed the 5 internal `'../admonition'` imports back at the `'../Admonition'` directory **Removed:** - `packages/ui-patterns/src/admonition.tsx`, and its `./admonition` entry in the exports map (regenerated with `pnpm gen:exports`) ## To test - `grep -r "ui-patterns/admonition" --include='*.ts*'` → no hits - `pnpm test:case-hazards` → passes - `pnpm typecheck` → all 15 tasks green - `pnpm --filter studio run lint:ratchet` → passes - `pnpm --filter ui-patterns vitest run src/Admonition` → 11 tests pass <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Standardized Admonition component imports across the application and documentation. * Improved compatibility with case-sensitive environments by using the canonical component path. * Removed the legacy Admonition import entry point. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
164 lines
5.9 KiB
TypeScript
164 lines
5.9 KiB
TypeScript
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/Admonition'
|
||
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 && <GenericSkeletonLoader />}
|
||
|
||
{isErrorMembers && (
|
||
<AlertError error={membersError} subject="Failed to retrieve organization members" />
|
||
)}
|
||
|
||
{isErrorRoles && (
|
||
<AlertError error={rolesError} subject="Failed to retrieve organization roles" />
|
||
)}
|
||
|
||
{isSuccessMembers && (
|
||
<div className="rounded-sm w-full overflow-hidden overflow-x-scroll">
|
||
<Card>
|
||
<Loading active={!filteredMembers}>
|
||
<Table>
|
||
<TableHeader>
|
||
<TableRow>
|
||
<TableHead key="header-user">Member</TableHead>
|
||
<TableHead key="header-mfa">MFA</TableHead>
|
||
<TableHead key="header-role">Role</TableHead>
|
||
<TableHead key="header-action" />
|
||
</TableRow>
|
||
</TableHeader>
|
||
|
||
<TableBody>
|
||
{[
|
||
...(isSuccessRoles && isSuccessMembers && !isOrgScopedRole
|
||
? [
|
||
<TableRow key="project-scope-notice">
|
||
<TableCell colSpan={12} className="p-0!">
|
||
<Admonition
|
||
type="note"
|
||
title="You have limited visibility in this organization"
|
||
description="Your access is limited to specific projects, so you can’t see all members or settings."
|
||
className="border-0 rounded-none"
|
||
/>
|
||
</TableCell>
|
||
</TableRow>,
|
||
]
|
||
: []),
|
||
...(!!user ? [<MemberRow key={user.gotrue_id} member={user} />] : []),
|
||
...sortedMembers.map((member) => (
|
||
<MemberRow key={member.gotrue_id} member={member} />
|
||
)),
|
||
...(searchString.length > 0 && filteredMembers.length === 0
|
||
? [
|
||
<TableRow key="no-results" className="bg-panel-secondary-light">
|
||
<TableCell colSpan={12}>
|
||
<div className="flex items-center space-x-3 opacity-75">
|
||
<AlertCircle size={16} strokeWidth={2} />
|
||
<p className="text-foreground-light">
|
||
No members matched the search query "{searchString}"
|
||
</p>
|
||
</div>
|
||
</TableCell>
|
||
</TableRow>,
|
||
]
|
||
: []),
|
||
]}
|
||
</TableBody>
|
||
<TableFooter className="font-normal">
|
||
<TableRow className="border-b-0 [&>td]:hover:bg-inherit">
|
||
<TableCell colSpan={4} className="text-foreground-muted">
|
||
{searchString
|
||
? `${filteredMembers.length} of ${members.length} ${members.length === 1 ? 'member' : 'members'}`
|
||
: `${members.length || 0} ${members.length === 1 ? 'member' : 'members'}`}
|
||
</TableCell>
|
||
</TableRow>
|
||
</TableFooter>
|
||
</Table>
|
||
</Loading>
|
||
</Card>
|
||
</div>
|
||
)}
|
||
</>
|
||
)
|
||
}
|
||
|
||
export default MembersView
|