IntegrationOverviewTab.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { useParams } from 'common'
  2. import { PropsWithChildren, ReactNode } from 'react'
  3. import { Badge, Card, CardContent, cn, Separator } from 'ui'
  4. import { INTEGRATIONS } from '../Landing/Integrations.constants'
  5. import { BuiltBySection } from './BuildBySection'
  6. import { MarkdownContent } from './MarkdownContent'
  7. import { MissingExtensionAlert } from './MissingExtensionAlert'
  8. import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
  9. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  10. export interface IntegrationOverviewTabProps {
  11. actions?: ReactNode
  12. status?: string | ReactNode
  13. alert?: ReactNode
  14. hideRequiredExtensionsSection?: boolean
  15. }
  16. /** [Joshen] This will eventually get replaced by IntegrationOverviewTabV2 */
  17. export const IntegrationOverviewTab = ({
  18. actions,
  19. alert,
  20. status,
  21. children,
  22. hideRequiredExtensionsSection = false,
  23. }: PropsWithChildren<IntegrationOverviewTabProps>) => {
  24. const { id } = useParams()
  25. const { data: project } = useSelectedProjectQuery()
  26. const integration = INTEGRATIONS.find((i) => i.id === id)
  27. const { data: extensions } = useDatabaseExtensionsQuery({
  28. projectRef: project?.ref,
  29. connectionString: project?.connectionString,
  30. })
  31. if (!integration) {
  32. return <div>Unsupported integration type</div>
  33. }
  34. const dependsOnExtension = (integration.requiredExtensions ?? []).length > 0
  35. const installableExtensions = (extensions ?? []).filter((ext) =>
  36. (integration.requiredExtensions ?? []).includes(ext.name)
  37. )
  38. const hasToInstallExtensions = installableExtensions.some((x) => !x.installed_version)
  39. // The integration requires extensions that are not available to install on the current database image
  40. const hasMissingExtensions =
  41. installableExtensions.length !== integration.requiredExtensions.length
  42. return (
  43. <div className="flex flex-col gap-8 py-10">
  44. <BuiltBySection integration={integration} status={status} />
  45. {!!alert && <div className="px-10 max-w-4xl">{alert}</div>}
  46. <MarkdownContent key={integration.id} integrationId={integration.id} />
  47. <Separator />
  48. {dependsOnExtension && !hideRequiredExtensionsSection && (
  49. <div className="px-4 md:px-10 max-w-4xl flex flex-col gap-y-4">
  50. <h4>Required extensions</h4>
  51. <Card>
  52. <CardContent className="p-0">
  53. <ul className="text-foreground-light text-sm">
  54. {(integration.requiredExtensions ?? []).map((requiredExtension, idx) => {
  55. const extension = (extensions ?? []).find((ext) => ext.name === requiredExtension)
  56. const isInstalled = !!extension?.installed_version
  57. const isLastRow = idx === (integration.requiredExtensions?.length ?? 0) - 1
  58. return (
  59. <li
  60. key={requiredExtension}
  61. className={cn(
  62. 'flex items-center justify-between gap-3 py-2 px-3',
  63. !isLastRow ? 'border-b' : ''
  64. )}
  65. >
  66. <code className="text-xs">{requiredExtension}</code>
  67. <div className="shrink-0">
  68. {extension ? (
  69. isInstalled ? (
  70. <Badge>Installed</Badge>
  71. ) : (
  72. <MissingExtensionAlert extension={extension} />
  73. )
  74. ) : (
  75. <span className="text-foreground-muted">Unavailable</span>
  76. )}
  77. </div>
  78. </li>
  79. )
  80. })}
  81. </ul>
  82. {hasMissingExtensions && (
  83. <div className="py-3 px-4 border-t">{integration.missingExtensionsAlert}</div>
  84. )}
  85. </CardContent>
  86. </Card>
  87. </div>
  88. )}
  89. {!!actions && (
  90. <div
  91. aria-disabled={hasToInstallExtensions && !hideRequiredExtensionsSection}
  92. className={cn(
  93. 'px-10 max-w-4xl',
  94. hasToInstallExtensions &&
  95. !hideRequiredExtensionsSection &&
  96. 'opacity-25 [&_button]:pointer-events-none'
  97. )}
  98. >
  99. {actions}
  100. </div>
  101. )}
  102. {children}
  103. </div>
  104. )
  105. }