PublicationsTableItem.tsx110 lines · main
1import type { PGPublication, PGTable } from '@supabase/pg-meta'
2import { PermissionAction } from '@supabase/shared-types/out/constants'
3import { useState } from 'react'
4import { toast } from 'sonner'
5import { Badge, Switch, TableCell, TableRow, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
6
7import { useDatabasePublicationUpdateMutation } from '@/data/database-publications/database-publications-update-mutation'
8import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
9import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
10import { useProtectedSchemas } from '@/hooks/useProtectedSchemas'
11
12interface PublicationsTableItemProps {
13 table: PGTable
14 selectedPublication: PGPublication
15}
16
17export const PublicationsTableItem = ({
18 table,
19 selectedPublication,
20}: PublicationsTableItemProps) => {
21 const { data: project } = useSelectedProjectQuery()
22 const { data: protectedSchemas } = useProtectedSchemas()
23 const enabledForAllTables = selectedPublication.tables == null
24
25 const isProtected = protectedSchemas.map((x) => x.name).includes(table.schema)
26
27 const [checked, setChecked] = useState(
28 selectedPublication.tables?.find((x: any) => x.id == table.id) != undefined
29 )
30
31 const { can: canUpdatePublications } = useAsyncCheckPermissions(
32 PermissionAction.TENANT_SQL_ADMIN_WRITE,
33 'publications'
34 )
35
36 const { mutate: updatePublications, isPending } = useDatabasePublicationUpdateMutation()
37
38 const toggleReplicationForTable = async (table: PGTable, publication: PGPublication) => {
39 if (project === undefined) return console.error('Project is required')
40
41 const originalChecked = checked
42 setChecked(!checked)
43
44 const publicationTables = publication?.tables ?? []
45 const exists = publicationTables.some((x: any) => x.id == table.id)
46 const tables = !exists
47 ? [`${table.schema}.${table.name}`].concat(
48 publicationTables.map((t: any) => `${t.schema}.${t.name}`)
49 )
50 : publicationTables
51 .filter((x: any) => x.id != table.id)
52 .map((x: any) => `${x.schema}.${x.name}`)
53
54 updatePublications(
55 {
56 projectRef: project?.ref,
57 connectionString: project?.connectionString,
58 id: publication.id,
59 tables,
60 },
61 {
62 onSuccess: () => {
63 toast.success(
64 `Successfully ${checked ? 'disabled' : 'enabled'} replication for ${table.name}`
65 )
66 },
67 onError: (error) => {
68 toast.error(`Failed to toggle replication for ${table.name}: ${error.message}`)
69 setChecked(originalChecked)
70 },
71 }
72 )
73 }
74
75 return (
76 <TableRow key={table.id}>
77 <TableCell className="py-3 whitespace-nowrap">{table.name}</TableCell>
78 <TableCell className="py-3 whitespace-nowrap text-foreground-light">{table.schema}</TableCell>
79 <TableCell className="py-3 whitespace-nowrap hidden lg:table-cell max-w-sm truncate text-foreground-light">
80 {table.comment}
81 </TableCell>
82 <TableCell className="py-3">
83 <div className="flex justify-end gap-2">
84 {enabledForAllTables ? (
85 <Badge>
86 <span>Enabled</span>
87 <span className="hidden lg:inline-block">&nbsp;for all tables</span>
88 </Badge>
89 ) : (
90 <Tooltip>
91 <TooltipTrigger>
92 <Switch
93 size="small"
94 disabled={!canUpdatePublications || isPending || isProtected}
95 checked={checked}
96 onClick={() => toggleReplicationForTable(table, selectedPublication)}
97 />
98 </TooltipTrigger>
99 {isProtected && (
100 <TooltipContent side="bottom" className="w-64 text-center">
101 This table belongs to a protected schema, and its publication cannot be toggled
102 </TooltipContent>
103 )}
104 </Tooltip>
105 )}
106 </div>
107 </TableCell>
108 </TableRow>
109 )
110}