Skip to content

Commit db4ae7e

Browse files
SaxonFjoshenlim
andauthored
Refine the policies page (supabase#38204)
* refine the policies page * minor fixes * Fix TS * Do not transform table name in policy row table header --------- Co-authored-by: Joshen Lim <[email protected]>
1 parent 39df774 commit db4ae7e

File tree

9 files changed

+287
-301
lines changed

9 files changed

+287
-301
lines changed

apps/studio/components/interfaces/Auth/Policies/Policies.tsx

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { PostgresPolicy } from '@supabase/postgres-meta'
22
import { isEmpty } from 'lodash'
3-
import { HelpCircle } from 'lucide-react'
4-
import { useRouter } from 'next/router'
3+
import Link from 'next/link'
54
import { useState } from 'react'
65
import { toast } from 'sonner'
76

@@ -11,32 +10,34 @@ import {
1110
PolicyTableRowProps,
1211
} from 'components/interfaces/Auth/Policies/PolicyTableRow'
1312
import { ProtectedSchemaWarning } from 'components/interfaces/Database/ProtectedSchemaWarning'
14-
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
15-
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
16-
import InformationBox from 'components/ui/InformationBox'
13+
import { NoSearchResults } from 'components/ui/NoSearchResults'
1714
import { useDatabasePolicyDeleteMutation } from 'data/database-policies/database-policy-delete-mutation'
1815
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
1916
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
17+
import { Button, Card, CardContent } from 'ui'
2018
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
2119

2220
interface PoliciesProps {
21+
search?: string
2322
schema: string
2423
tables: PolicyTableRowProps['table'][]
2524
hasTables: boolean
2625
isLocked: boolean
2726
onSelectCreatePolicy: (table: string) => void
2827
onSelectEditPolicy: (policy: PostgresPolicy) => void
28+
onResetSearch?: () => void
2929
}
3030

31-
const Policies = ({
31+
export const Policies = ({
32+
search,
3233
schema,
3334
tables,
3435
hasTables,
3536
isLocked,
3637
onSelectCreatePolicy,
3738
onSelectEditPolicy: onSelectEditPolicyAI,
39+
onResetSearch,
3840
}: PoliciesProps) => {
39-
const router = useRouter()
4041
const { ref } = useParams()
4142
const { data: project } = useSelectedProjectQuery()
4243

@@ -115,40 +116,21 @@ const Policies = ({
115116
})
116117
}
117118

118-
if (tables.length === 0) {
119+
if (!hasTables) {
119120
return (
120-
<div className="flex-grow flex items-center justify-center">
121-
<ProductEmptyState
122-
size="large"
123-
title="Row-Level Security (RLS) Policies"
124-
ctaButtonLabel="Create a table"
125-
infoButtonLabel="What is RLS?"
126-
infoButtonUrl="https://supabase.com/docs/guides/auth/row-level-security"
127-
onClickCta={() => router.push(`/project/${ref}/editor`)}
128-
>
129-
<div className="space-y-4">
130-
<InformationBox
131-
title="What are policies?"
132-
icon={<HelpCircle size={14} strokeWidth={2} />}
133-
description={
134-
<div className="space-y-2">
135-
<p className="text-sm">
136-
Policies restrict, on a per-user basis, which rows can be returned by normal
137-
queries, or inserted, updated, or deleted by data modification commands.
138-
</p>
139-
<p className="text-sm">
140-
This is also known as Row-Level Security (RLS). Each policy is attached to a
141-
table, and the policy is executed each time its accessed.
142-
</p>
143-
</div>
144-
}
145-
/>
146-
<p className="text-sm text-foreground-light">
147-
Create a table in this schema first before creating a policy.
148-
</p>
149-
</div>
150-
</ProductEmptyState>
151-
</div>
121+
<Card className="w-full bg-transparent">
122+
<CardContent className="flex flex-col items-center justify-center p-8">
123+
<h2 className="heading-default">No tables to create policies for</h2>
124+
125+
<p className="text-sm text-foreground-light text-center mb-4">
126+
RLS Policies control per-user access to table rows. Create a table in this schema first
127+
before creating a policy.
128+
</p>
129+
<Button asChild type="default">
130+
<Link href={`/project/${ref}/editor`}>Create a table</Link>
131+
</Button>
132+
</CardContent>
133+
</Card>
152134
)
153135
}
154136

@@ -170,7 +152,7 @@ const Policies = ({
170152
</section>
171153
))
172154
) : hasTables ? (
173-
<NoSearchResults />
155+
<NoSearchResults searchString={search ?? ''} onResetFilter={onResetSearch} />
174156
) : null}
175157
</div>
176158

@@ -202,5 +184,3 @@ const Policies = ({
202184
</>
203185
)
204186
}
205-
206-
export default Policies

apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyRow.tsx

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { noop } from 'lodash'
44
import { Edit, MoreVertical, Trash } from 'lucide-react'
55

66
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
7-
import Panel from 'components/ui/Panel'
87
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
98
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
109
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
@@ -13,6 +12,8 @@ import {
1312
Badge,
1413
Button,
1514
cn,
15+
TableRow,
16+
TableCell,
1617
DropdownMenu,
1718
DropdownMenuContent,
1819
DropdownMenuItem,
@@ -57,58 +58,50 @@ const PolicyRow = ({
5758
(policy.roles.includes('authenticated') || policy.roles.includes('public'))
5859

5960
return (
60-
<Panel.Content
61-
className={cn(
62-
'flex border-overlay',
63-
'w-full last:border-0 space-x-4 border-b py-4 lg:items-center'
64-
)}
65-
>
66-
<div className="flex grow flex-col gap-y-1">
67-
<div className="flex items-start gap-x-4">
68-
<p className="font-mono text-xs text-foreground-light translate-y-[2px] min-w-12">
69-
{policy.command}
70-
</p>
71-
72-
<div className="flex flex-col gap-y-1">
73-
<Button
74-
type="text"
75-
className="h-auto text-foreground text-sm border-none p-0 hover:bg-transparent justify-start"
76-
onClick={() => onSelectEditPolicy(policy)}
77-
>
78-
{policy.name}
79-
</Button>
80-
<div className="flex items-center gap-x-1">
81-
<div className="text-foreground-lighter text-sm">
82-
Applied to:{' '}
83-
{policy.roles.slice(0, 3).map((role, i) => (
84-
<span key={`policy-${role}-${i}`}>
85-
<code className="text-foreground-light text-xs">{role}</code>
86-
{i < Math.min(policy.roles.length, 3) - 1 ? ', ' : ' '}
87-
</span>
88-
))}
89-
{policy.roles.length > 1 ? 'roles' : 'role'}
90-
</div>
91-
{policy.roles.length > 3 && (
92-
<Tooltip>
93-
<TooltipTrigger asChild>
94-
<code key="policy-etc" className="text-foreground-light text-xs">
95-
+ {policy.roles.length - 3} more roles
96-
</code>
97-
</TooltipTrigger>
98-
<TooltipContent side="bottom" align="center">
99-
{policy.roles.slice(3).join(', ')}
100-
</TooltipContent>
101-
</Tooltip>
102-
)}
103-
</div>
104-
</div>
105-
61+
<TableRow>
62+
<TableCell className="w-[40%] truncate">
63+
<div className="flex items-center gap-x-2 min-w-0">
64+
<Button
65+
type="text"
66+
className="text-foreground text-sm p-0 hover:bg-transparent w-full truncate justify-start"
67+
onClick={() => onSelectEditPolicy(policy)}
68+
>
69+
{policy.name}
70+
</Button>
10671
{appliesToAnonymousUsers ? (
10772
<Badge color="yellow">Applies to anonymous users</Badge>
10873
) : null}
10974
</div>
110-
</div>
111-
<div>
75+
</TableCell>
76+
<TableCell className="w-[20%] truncate">
77+
<code className="text-foreground-light text-xs">{policy.command}</code>
78+
</TableCell>
79+
<TableCell className="w-[30%] truncate">
80+
<div className="flex items-center gap-x-1">
81+
<div className="text-foreground-lighter text-sm">
82+
{policy.roles.slice(0, 3).map((role, i) => (
83+
<span key={`policy-${role}-${i}`}>
84+
<code className="text-foreground-light text-xs">{role}</code>
85+
{i < Math.min(policy.roles.length, 3) - 1 ? ', ' : ' '}
86+
</span>
87+
))}
88+
{policy.roles.length > 1 ? 'roles' : 'role'}
89+
</div>
90+
{policy.roles.length > 3 && (
91+
<Tooltip>
92+
<TooltipTrigger asChild>
93+
<code key="policy-etc" className="text-foreground-light text-xs">
94+
+ {policy.roles.length - 3} more roles
95+
</code>
96+
</TooltipTrigger>
97+
<TooltipContent side="bottom" align="center">
98+
{policy.roles.slice(3).join(', ')}
99+
</TooltipContent>
100+
</Tooltip>
101+
)}
102+
</div>
103+
</TableCell>
104+
<TableCell className="w-0 text-right whitespace-nowrap">
112105
{!isLocked && (
113106
<DropdownMenu>
114107
<DropdownMenuTrigger asChild>
@@ -127,9 +120,9 @@ const PolicyRow = ({
127120
name: `Update policy ${policy.name}`,
128121
open: true,
129122
sqlSnippets: [sql],
130-
initialInput: `Update the policy with name "${policy.name}" in the ${policy.schema} schema on the ${policy.table} table. It should...`,
123+
initialInput: `Update the policy with name \"${policy.name}\" in the ${policy.schema} schema on the ${policy.table} table. It should...`,
131124
suggestions: {
132-
title: `I can help you make a change to the policy "${policy.name}" in the ${policy.schema} schema on the ${policy.table} table, here are a few example prompts to get you started:`,
125+
title: `I can help you make a change to the policy \"${policy.name}\" in the ${policy.schema} schema on the ${policy.table} table, here are a few example prompts to get you started:`,
133126
prompts: [
134127
{
135128
label: 'Improve Policy',
@@ -169,8 +162,8 @@ const PolicyRow = ({
169162
</DropdownMenuContent>
170163
</DropdownMenu>
171164
)}
172-
</div>
173-
</Panel.Content>
165+
</TableCell>
166+
</TableRow>
174167
)
175168
}
176169

apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRowHeader.tsx

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
22
import { noop } from 'lodash'
3-
import { Lock, Unlock } from 'lucide-react'
3+
import { Lock, Table } from 'lucide-react'
44

55
import { useParams } from 'common'
66
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
77
import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id'
88
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
99
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
10-
import { AiIconAnimation, Badge } from 'ui'
10+
import { AiIconAnimation, Badge, CardTitle } from 'ui'
1111

1212
interface PolicyTableRowHeaderProps {
1313
table: {
@@ -54,28 +54,19 @@ const PolicyTableRowHeader = ({
5454
<EditorTablePageLink
5555
projectRef={ref}
5656
id={String(table.id)}
57-
className="flex items-center gap-x-2"
57+
className="flex items-center gap-x-3"
5858
>
59-
{table.rls_enabled ? (
60-
<div className="flex items-center gap-x-1 text-xs">
61-
<Lock size={14} strokeWidth={2} className="text-brand" />
62-
</div>
63-
) : (
64-
<div className="flex items-center gap-x-1 text-xs">
65-
<Unlock size={14} strokeWidth={2} className="text-warning-600" />
66-
</div>
67-
)}
68-
<h4 className="m-0">{table.name}</h4>
59+
<Table strokeWidth={1.5} size={16} className="text-foreground-muted" />
60+
<CardTitle className="m-0 normal-case">{table.name}</CardTitle>
61+
{!table.rls_enabled && <Badge variant="warning">RLS Disabled</Badge>}
6962
</EditorTablePageLink>
70-
<div className="flex items-center gap-x-2">
71-
{isTableLocked && (
72-
<Badge>
73-
<span className="flex gap-2 items-center text-xs uppercase text-foreground-lighter">
74-
<Lock size={12} /> Locked
75-
</span>
76-
</Badge>
77-
)}
78-
</div>
63+
{isTableLocked && (
64+
<Badge>
65+
<span className="flex gap-2 items-center text-xs uppercase text-foreground-lighter">
66+
<Lock size={12} /> Locked
67+
</span>
68+
</Badge>
69+
)}
7970
</div>
8071
{!isTableLocked && (
8172
<div className="flex-1">

0 commit comments

Comments
 (0)