Files
Gildas Garcia cabe14e5ca chore: remove _Shadcn_ suffix from ui tabs components (#47628)
## Problem

Now that we migrated all usages of the deprecated `Tabs` component, we
don't need the `_Shadcn_` suffix anymore.

## Solution

Remove `_Shadcn_` suffix from `ui` tabs components. That's all this PR
does, no visual nor functional changes

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Standardized tab components across the app so pages and dialogs now
use the same consistent tab UI.
* Improved tab-based views in design, docs, studio, learn, and website
experiences for a more uniform interface.

* **Chores**
* Updated shared UI exports to expose tab components directly,
simplifying future usage across the product.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-06 15:29:16 +02:00

198 lines
8.0 KiB
TypeScript

import { Badge, cn, Tabs, TabsContent, TabsList, TabsTrigger } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Results } from '../../SQLEditor/UtilityPanel/Results'
import { RLSTableCard } from './RLSTableCard'
import { ParseQueryResults } from './RLSTester.types'
import { deriveRLSTestState } from './RLSTesterResults.utils'
import { useTestQueryRLS } from './useTestQueryRLS'
import type { Policy } from '@/components/interfaces/Database/Policies/PolicyTableRow/PolicyTableRow.utils'
import { type QueryResponseError } from '@/data/sql/execute-sql-mutation'
interface RLSTesterResultsProps {
results: Object[]
autoLimit: boolean
parseQueryResults: ParseQueryResults
executeSqlError: Error | QueryResponseError | null | undefined
handleSelectEditPolicy: (policy: Policy) => void
}
export const RLSTesterResults = ({
results,
autoLimit,
parseQueryResults,
executeSqlError,
handleSelectEditPolicy,
}: RLSTesterResultsProps) => {
const { limit } = useTestQueryRLS()
const {
isServiceRole,
tableWithRLSEnabledButNoPolicies,
tableWithRLSEnabledWithPolicyFalse,
tableWithRLSEnabledWithPoliciesDontApply,
noAccessToData,
} = deriveRLSTestState(parseQueryResults)
const { operation, role } = parseQueryResults
const rlsBlockInsert = executeSqlError && operation === 'INSERT'
const noAccess = noAccessToData || rlsBlockInsert
return (
<div className="p-5 pt-4">
<div className="flex items-center gap-x-2 mb-2">
<p className="text-sm">Summary</p>
{noAccess ? (
<Badge variant="destructive">No access</Badge>
) : (
<Badge variant="success">{results.length > 0 ? 'Can access' : 'Has access'}</Badge>
)}
</div>
<Tabs defaultValue="policies">
<TabsList className="gap-x-3">
<TabsTrigger value="policies" className="px-2">
Policies applied
</TabsTrigger>
<TabsTrigger value="data" className="px-2" disabled={operation !== 'SELECT'}>
Data preview
</TabsTrigger>
</TabsList>
{!!parseQueryResults && (
<div className="border rounded-sm flex items-center justify-between px-3 py-1.5 mt-3">
<div className="flex items-center gap-x-2">
<p className="text-xs text-foreground-light">Ran as</p>
{!parseQueryResults.role ? (
<code className="text-code-inline">postgres</code>
) : parseQueryResults.user ? (
<p className="text-sm truncate max-w-52">{parseQueryResults.user.email}</p>
) : parseQueryResults.role === 'anon' ? (
<p className="text-xs">an Anonymous user</p>
) : null}
</div>
{parseQueryResults.role === 'anon' && (
<p className="text-foreground-light text-xs">Not logged in user</p>
)}
{!!parseQueryResults.user && (
<code className="text-code-inline">ID: {parseQueryResults.user.id}</code>
)}
</div>
)}
<TabsContent value="policies" className="mt-0">
{!isServiceRole &&
(!!tableWithRLSEnabledButNoPolicies ? (
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
<p className="mb-0.5! text-foreground">
This user{' '}
{operation === 'SELECT'
? 'has no access to any rows'
: `is unable to ${operation?.toLowerCase()} any rows`}{' '}
from this query
</p>
<p className="text-foreground-light">
The table{' '}
<code className="text-code-inline">
{tableWithRLSEnabledButNoPolicies.schema}.
{tableWithRLSEnabledButNoPolicies.table}
</code>{' '}
has RLS enabled but no policies set up for the{' '}
<code className="text-code-inline break-keep!">{parseQueryResults.role}</code>{' '}
role.
</p>
</Admonition>
) : tableWithRLSEnabledWithPolicyFalse ? (
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
<p className="mb-0.5! text-foreground">
This user has no access to any rows from this query
</p>
<p className="text-foreground-light">
The table{' '}
<code className="text-code-inline">
{tableWithRLSEnabledWithPolicyFalse.schema}.
{tableWithRLSEnabledWithPolicyFalse.table}
</code>{' '}
has a policy that evaluates to
<code className="text-code-inline break-keep!">false</code> for the{' '}
<code className="text-code-inline break-keep!">{parseQueryResults.role}</code>{' '}
role.
</p>
</Admonition>
) : rlsBlockInsert &&
parseQueryResults.user &&
tableWithRLSEnabledWithPoliciesDontApply ? (
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
<p className="mb-0.5! text-foreground">
This user is unable to {operation?.toLowerCase()} any rows from this query
</p>
<p className="text-foreground-light">
The table{' '}
<code className="text-code-inline">
{tableWithRLSEnabledWithPoliciesDontApply.schema}.
{tableWithRLSEnabledWithPoliciesDontApply.table}
</code>{' '}
has a policy for the{' '}
<code className="text-code-inline break-keep!">{parseQueryResults.role}</code>{' '}
role, but its condition wasn't satisfied for this specific request.
</p>
</Admonition>
) : null)}
{isServiceRole && (
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
<p className="mb-0.5! text-foreground">
The <code className="text-code-inline">postgres</code> role has access to all rows
for this query
</p>
<p className="text-foreground-light">
The <code className="text-code-inline">postgres</code> role has admin privileges and
bypasses all RLS policies.
</p>
</Admonition>
)}
<div className="flex flex-col gap-y-2 mt-4">
<p className="text-sm">Table access</p>
{!isServiceRole && (
<div className="flex flex-col gap-y-2">
{parseQueryResults?.tables.map((x) => {
const { schema, table, tablePolicies, isRLSEnabled } = x
return (
<RLSTableCard
key={`${schema}.${table}`}
table={{ schema, name: table, isRLSEnabled }}
role={role}
operation={operation}
policies={tablePolicies}
hasError={!!executeSqlError}
handleSelectEditPolicy={handleSelectEditPolicy}
/>
)
})}
</div>
)}
</div>
</TabsContent>
<TabsContent value="data" className="mt-2">
<div
className={cn(
'grow flex flex-col border overflow-hidden',
results.length === 0 ? 'rounded-sm h-32' : 'rounded-t h-56'
)}
>
<Results rows={results} />
</div>
{results.length > 0 && (
<p className="border border-t-0 rounded-b font-mono text-xs text-foreground-light p-2">
{results.length} row{results.length > 1 ? 's' : ''}
{autoLimit && results.length >= limit && ` (Limited to only ${limit} rows)`}
</p>
)}
</TabsContent>
</Tabs>
</div>
)
}