PublicationsTables.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { PermissionAction } from '@supabase/shared-types/out/constants'
  2. import { useParams } from 'common'
  3. import { Search } from 'lucide-react'
  4. import { useMemo, useRef, useState } from 'react'
  5. import { Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
  6. import { Admonition } from 'ui-patterns'
  7. import { Input } from 'ui-patterns/DataInputs/Input'
  8. import { PublicationTablesSkeleton } from './PublicationSkeleton'
  9. import { PublicationsTableItem } from './PublicationsTableItem'
  10. import { AlertError } from '@/components/ui/AlertError'
  11. import { NoSearchResults } from '@/components/ui/NoSearchResults'
  12. import { useDatabasePublicationsQuery } from '@/data/database-publications/database-publications-query'
  13. import { useTablesQuery } from '@/data/tables/tables-query'
  14. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  15. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  16. import { onSearchInputEscape } from '@/lib/keyboard'
  17. import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
  18. import { useShortcut } from '@/state/shortcuts/useShortcut'
  19. export const PublicationsTables = () => {
  20. const { id } = useParams()
  21. const { data: project } = useSelectedProjectQuery()
  22. const [filterString, setFilterString] = useState<string>('')
  23. const searchInputRef = useRef<HTMLInputElement>(null)
  24. const { can: canUpdatePublications, isLoading: isLoadingPermissions } = useAsyncCheckPermissions(
  25. PermissionAction.TENANT_SQL_ADMIN_WRITE,
  26. 'publications'
  27. )
  28. useShortcut(
  29. SHORTCUT_IDS.LIST_PAGE_FOCUS_SEARCH,
  30. () => {
  31. searchInputRef.current?.focus()
  32. searchInputRef.current?.select()
  33. },
  34. { label: 'Search publications' }
  35. )
  36. const { data: publications = [] } = useDatabasePublicationsQuery({
  37. projectRef: project?.ref,
  38. connectionString: project?.connectionString,
  39. })
  40. const selectedPublication = publications.find((pub) => pub.id === Number(id))
  41. const {
  42. data: tablesData = [],
  43. isPending: isLoading,
  44. isSuccess,
  45. isError,
  46. error,
  47. } = useTablesQuery({
  48. projectRef: project?.ref,
  49. connectionString: project?.connectionString,
  50. })
  51. const tables = useMemo(() => {
  52. return tablesData.filter((table) =>
  53. filterString.length === 0 ? table : table.name.includes(filterString)
  54. )
  55. }, [tablesData, filterString])
  56. return (
  57. <>
  58. <div className="flex items-center justify-between mb-4">
  59. <Input
  60. size="tiny"
  61. ref={searchInputRef}
  62. icon={<Search />}
  63. className="w-48"
  64. placeholder="Search for a table"
  65. value={filterString}
  66. onChange={(e) => setFilterString(e.target.value)}
  67. onKeyDown={onSearchInputEscape(filterString, setFilterString)}
  68. />
  69. </div>
  70. {!isLoadingPermissions && !canUpdatePublications && (
  71. <Admonition
  72. type="warning"
  73. className="mb-4 w-full"
  74. description="You need additional permissions to update database replications."
  75. />
  76. )}
  77. <Card>
  78. <Table>
  79. <TableHeader>
  80. <TableRow>
  81. <TableHead>Name</TableHead>
  82. <TableHead>Schema</TableHead>
  83. <TableHead className="hidden lg:table-cell">Description</TableHead>
  84. {/*
  85. We've disabled All tables toggle for publications.
  86. See https://github.com/briven/briven/pull/7233.
  87. */}
  88. <TableHead />
  89. </TableRow>
  90. </TableHeader>
  91. <TableBody>
  92. {(isLoading || isLoadingPermissions) &&
  93. Array.from({ length: 2 }).map((_, i) => (
  94. <PublicationTablesSkeleton key={i} index={i} />
  95. ))}
  96. {isError && (
  97. <TableRow>
  98. <TableCell colSpan={4}>
  99. <AlertError error={error} subject="Failed to retrieve tables" />
  100. </TableCell>
  101. </TableRow>
  102. )}
  103. {!isLoading && !isLoadingPermissions && tables.length === 0 && (
  104. <TableRow>
  105. <TableCell colSpan={4}>
  106. <NoSearchResults
  107. className="border-none !p-0"
  108. searchString={filterString}
  109. onResetFilter={() => setFilterString('')}
  110. />
  111. </TableCell>
  112. </TableRow>
  113. )}
  114. {isSuccess ? (
  115. !!selectedPublication ? (
  116. tables.map((table) => (
  117. <PublicationsTableItem
  118. key={table.id}
  119. table={table}
  120. selectedPublication={selectedPublication}
  121. />
  122. ))
  123. ) : (
  124. <TableRow>
  125. <TableCell colSpan={4}>
  126. <p>The selected publication with ID {id} cannot be found</p>
  127. <p className="text-foreground-light">
  128. Head back to the list of publications to select one from there
  129. </p>
  130. </TableCell>
  131. </TableRow>
  132. )
  133. ) : null}
  134. </TableBody>
  135. </Table>
  136. </Card>
  137. </>
  138. )
  139. }