useAvailableIntegrations.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // @ts-nocheck
  2. import { useQuery } from '@tanstack/react-query'
  3. import { FeatureFlagContext, IS_PLATFORM } from 'common'
  4. import { fullImageUrl } from 'common/marketplace-client'
  5. import { Boxes } from 'lucide-react'
  6. import dynamic from 'next/dynamic'
  7. import Image from 'next/image'
  8. import { useContext, useMemo } from 'react'
  9. import { cn } from 'ui'
  10. import { INTEGRATIONS, Loading, type IntegrationDefinition } from './Integrations.constants'
  11. import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
  12. import { useMarketplaceIntegrationsQuery } from '@/data/marketplace/integrations-query'
  13. import { useCLIReleaseVersionQuery } from '@/data/misc/cli-release-version-query'
  14. import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
  15. /**
  16. * [Joshen] Returns a combination of
  17. * - Marketplace integrations retrieved remotely (Only if feature flag enabled)
  18. * - Existing integrations that are defined within studio
  19. */
  20. export const useAvailableIntegrations = () => {
  21. const { hasLoaded } = useContext(FeatureFlagContext)
  22. const isMarketplaceEnabled = useIsMarketplaceEnabled()
  23. const { integrationsWrappers } = useIsFeatureEnabled(['integrations:wrappers'])
  24. const { data: cliData } = useCLIReleaseVersionQuery()
  25. const isCLI = !!cliData?.current
  26. const { data, error } = useQuery({
  27. ...useMarketplaceIntegrationsQuery(),
  28. enabled: isMarketplaceEnabled,
  29. })
  30. const isPending = IS_PLATFORM && (!hasLoaded || (isMarketplaceEnabled && !data && !error))
  31. const isSuccess = !IS_PLATFORM || (hasLoaded && (!isMarketplaceEnabled || (!!data && !error)))
  32. const isError = IS_PLATFORM && isMarketplaceEnabled && !!error
  33. // [Joshen] Format marketplace integrations into existing ones for now
  34. // Likely that we might need to change, but can look into separately
  35. const marketplaceIntegrations: IntegrationDefinition[] = useMemo(
  36. () =>
  37. (data ?? [])?.map((integration) => {
  38. const {
  39. id: listingId,
  40. slug,
  41. categories,
  42. featured,
  43. title,
  44. description,
  45. documentation_url: docsUrl,
  46. website_url: siteUrl,
  47. installation_url: installUrl,
  48. installation_url_type: installUrlType,
  49. installation_identification_method: installMethod,
  50. secret_key_prefix: secretKeyPrefix,
  51. edge_function_secret_name: edgeFunctionSecretName,
  52. images,
  53. content,
  54. partner_name: authorName,
  55. listing_logo: listingLogo,
  56. } = integration
  57. const status = undefined
  58. const author = { name: authorName ?? '', websiteUrl: '' }
  59. return {
  60. id: slug ?? '',
  61. name: title ?? '',
  62. status,
  63. featured: !!featured,
  64. type: 'oauth' as const, // Currently marketplace only supports oauth apps
  65. source: 'Partner' as const,
  66. categories: Array.isArray(categories)
  67. ? (categories as Array<{ slug: string }>).map((x) => x.slug)
  68. : [],
  69. content,
  70. files: images?.map((image) => fullImageUrl(image)),
  71. description,
  72. docsUrl,
  73. siteUrl,
  74. installUrl,
  75. installUrlType: installUrlType ?? undefined,
  76. installIdentificationMethod: installMethod ?? undefined,
  77. secretKeyPrefix: secretKeyPrefix ?? undefined,
  78. edgeFunctionSecretName: edgeFunctionSecretName ?? undefined,
  79. listingId: listingId ?? undefined,
  80. author,
  81. requiredExtensions: [],
  82. icon: ({ className, ...props } = {}) => (
  83. <div className="relative w-full h-full">
  84. {listingLogo ? (
  85. <Image
  86. fill
  87. src={fullImageUrl(listingLogo)}
  88. alt=""
  89. className={cn('p-2', className)}
  90. {...props}
  91. />
  92. ) : (
  93. <Boxes
  94. className={cn('inset-0 p-2 text-black w-full h-full', className)}
  95. {...props}
  96. />
  97. )}
  98. </div>
  99. ),
  100. navigation: [
  101. {
  102. route: 'overview',
  103. label: 'Overview',
  104. },
  105. ],
  106. navigate: ({ pageId = 'overview' }) => {
  107. switch (pageId) {
  108. case 'overview':
  109. return dynamic(
  110. () =>
  111. import('@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/index').then(
  112. (mod) => mod.IntegrationOverviewTabV2
  113. ),
  114. {
  115. loading: Loading,
  116. }
  117. )
  118. }
  119. return null
  120. },
  121. }
  122. }),
  123. [data]
  124. )
  125. // [Joshen] Existing integrations that are defined within studio
  126. // Available integrations are all integrations that can be installed. If an integration can't be installed (needed
  127. // extensions are not available on this DB image), the UI will provide a tooltip explaining why.
  128. const allIntegrations = useMemo(() => {
  129. return INTEGRATIONS.filter((integration) => {
  130. if (
  131. !integrationsWrappers &&
  132. (integration.type === 'wrapper' || integration.id.endsWith('_wrapper'))
  133. ) {
  134. return false
  135. }
  136. if (integration.id === 'stripe_sync_engine' && isCLI) {
  137. return false
  138. }
  139. return true
  140. })
  141. }, [integrationsWrappers, isCLI])
  142. const dataWithMarketplace = useMemo(() => {
  143. return [...marketplaceIntegrations, ...allIntegrations].sort((a, b) =>
  144. a.name.localeCompare(b.name)
  145. )
  146. }, [marketplaceIntegrations, allIntegrations])
  147. return {
  148. data: dataWithMarketplace,
  149. error,
  150. isPending,
  151. isSuccess,
  152. isError,
  153. }
  154. }