Files
Alaister Young ca2b50a0a7 chore(ui-patterns): collapse the admonition shim into ui-patterns/Admonition (#48377)
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>
2026-07-29 00:48:56 +08:00

171 lines
5.7 KiB
TypeScript

import { useParams } from 'common'
import { noop } from 'lodash'
import { memo, useMemo } from 'react'
import {
Card,
CardContent,
CardHeader,
cn,
Table,
TableBody,
TableHead,
TableHeader,
TableRow,
} from 'ui'
import { Admonition } from 'ui-patterns/Admonition'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { usePoliciesData } from '../PoliciesDataContext'
import { PolicyRow } from './PolicyRow'
import type { PolicyTable } from './PolicyTableRow.types'
import type { Policy } from './PolicyTableRow.utils'
import { getTableAdmonitionMessage, getTableDataApiStatus } from './PolicyTableRow.utils'
import { PolicyTableRowHeader } from './PolicyTableRowHeader'
import { AlertError } from '@/components/ui/AlertError'
import { useTableApiAccessQuery } from '@/data/privileges/table-api-access-query'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
export interface PolicyTableRowProps {
table: PolicyTable
isLocked: boolean
onSelectToggleRLS: (table: PolicyTable) => void
onSelectCreatePolicy: (table: PolicyTable) => void
onSelectEditPolicy: (policy: Policy) => void
onSelectDeletePolicy: (policy: Policy) => void
}
const PolicyTableRowComponent = ({
table,
isLocked,
onSelectToggleRLS = noop,
onSelectCreatePolicy = noop,
onSelectEditPolicy = noop,
onSelectDeletePolicy = noop,
}: PolicyTableRowProps) => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
const { getPoliciesForTable, isPoliciesLoading, isPoliciesError, policiesError } =
usePoliciesData()
const policies = useMemo(
() => getPoliciesForTable(table.schema, table.name),
[getPoliciesForTable, table.schema, table.name]
)
// [Joshen] Classification is more granular than "RLS on/off" alone — it also considers
// schema exposure and whether anon/authenticated/service_role actually have grants.
// Ideally we'd rely on the security lints, but they only look at the public schema and
// ignore roles. Once the lints cover both, we can switch to them as the source of truth.
const tableNames = useMemo(() => [table.name], [table.name])
const {
data: apiAccessMap,
isPending: isLoadingRolesAccess,
isError: isRolesAccessError,
} = useTableApiAccessQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
schemaName: table.schema,
tableNames,
})
const status = useMemo(
() =>
getTableDataApiStatus({
apiAccessData: apiAccessMap?.[table.name],
isRLSEnabled: table.rls_enabled,
policiesCount: policies.length,
}),
[apiAccessMap, table.name, table.rls_enabled, policies.length]
)
const hasApiAccess =
status === 'publicly-readable' || status === 'locked-by-rls' || status === 'secured'
const isPubliclyReadable = status === 'publicly-readable'
const isRealtimeSchema = table.schema === 'realtime'
const isRealtimeMessagesTable = isRealtimeSchema && table.name === 'messages'
const isTableLocked = isRealtimeSchema ? !isRealtimeMessagesTable : isLocked
const showPolicies = !isPoliciesLoading && !isPoliciesError && !isLoadingRolesAccess
const admonitionMessage = useMemo(() => getTableAdmonitionMessage({ status, ref }), [status, ref])
return (
<Card className={cn(isPubliclyReadable && 'border-warning-500')}>
<CardHeader
className={cn('py-3 px-4', status !== 'secured' && status !== 'unknown' && 'border-b-0')}
>
<PolicyTableRowHeader
table={table}
isLocked={isLocked}
hasApiAccess={hasApiAccess}
isLoadingApiAccess={isLoadingRolesAccess}
onSelectToggleRLS={onSelectToggleRLS}
onSelectCreatePolicy={onSelectCreatePolicy}
/>
</CardHeader>
{!isLoadingRolesAccess && !isRolesAccessError && admonitionMessage !== null && (
<Admonition
showIcon={false}
type={isPubliclyReadable ? 'warning' : 'default'}
className="border-0 border-y rounded-none min-h-12 flex items-center"
>
{admonitionMessage}
</Admonition>
)}
{(isPoliciesLoading || isLoadingRolesAccess) && (
<CardContent>
<ShimmeringLoader />
</CardContent>
)}
{isPoliciesError && (
<CardContent>
<AlertError
className="border-0 rounded-none"
error={policiesError}
subject="Failed to retrieve policies"
/>
</CardContent>
)}
{showPolicies && (
<CardContent className="p-0">
{policies.length === 0 ? (
<p className="text-foreground-lighter text-sm p-4">No policies created yet</p>
) : (
<Table className="table-fixed">
<TableHeader>
<TableRow>
<TableHead className="w-[40%]">Name</TableHead>
<TableHead className="w-[20%]">Command</TableHead>
<TableHead className="w-[30%]">Applied to</TableHead>
<TableHead className="text-right">
<span className="sr-only">Actions</span>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{policies.map((policy) => (
<PolicyRow
key={policy.id}
policy={policy}
isLocked={isTableLocked}
onSelectEditPolicy={onSelectEditPolicy}
onSelectDeletePolicy={onSelectDeletePolicy}
/>
))}
</TableBody>
</Table>
)}
</CardContent>
)}
</Card>
)
}
export const PolicyTableRow = memo(PolicyTableRowComponent)
PolicyTableRow.displayName = 'PolicyTableRow'