useIntegrationDetail.ts122 lines · main
| 1 | import { useParams } from 'common' |
| 2 | import { useRouter } from 'next/router' |
| 3 | import { useEffect, useMemo } from 'react' |
| 4 | |
| 5 | import { useAvailableIntegrations } from './useAvailableIntegrations' |
| 6 | import { useInstalledIntegrations } from './useInstalledIntegrations' |
| 7 | import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query' |
| 8 | import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' |
| 9 | import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' |
| 10 | |
| 11 | export type IntegrationTab = { |
| 12 | label: string |
| 13 | href: string |
| 14 | active: boolean |
| 15 | } |
| 16 | |
| 17 | export const useIntegrationDetail = () => { |
| 18 | const router = useRouter() |
| 19 | const { ref, id, pageId, childId } = useParams() |
| 20 | |
| 21 | const { integrationsWrappers } = useIsFeatureEnabled(['integrations:wrappers']) |
| 22 | |
| 23 | const { data: project } = useSelectedProjectQuery() |
| 24 | const { data: extensions } = useDatabaseExtensionsQuery({ |
| 25 | projectRef: project?.ref, |
| 26 | connectionString: project?.connectionString, |
| 27 | }) |
| 28 | |
| 29 | const { data: allIntegrations, isPending: isAvailableLoading } = useAvailableIntegrations() |
| 30 | const { installedIntegrations, isLoading: isInstalledLoading } = useInstalledIntegrations() |
| 31 | |
| 32 | // wrapped in useMemo to avoid UI resets when installing additional extensions like pg_net |
| 33 | const integration = useMemo(() => allIntegrations.find((i) => i.id === id), [allIntegrations, id]) |
| 34 | const installation = useMemo( |
| 35 | () => installedIntegrations.find((i) => i.id === id), |
| 36 | [installedIntegrations, id] |
| 37 | ) |
| 38 | |
| 39 | const installableExtensions = (extensions ?? []).filter((ext) => |
| 40 | (integration?.requiredExtensions ?? []).includes(ext.name) |
| 41 | ) |
| 42 | const extensionsInstalled = installableExtensions.every((x) => x.installed_version) |
| 43 | |
| 44 | // installedIntegrations doesn't return wrappers unless there's a wrapper created |
| 45 | const isInstalled = |
| 46 | !!integration && (!!installation || (integration.type === 'wrapper' && extensionsInstalled)) |
| 47 | |
| 48 | const navItems = useMemo(() => { |
| 49 | if (!integration?.navigation) return [] |
| 50 | return isInstalled |
| 51 | ? integration.navigation |
| 52 | : integration.navigation.filter((nav) => nav.route === 'overview') |
| 53 | }, [integration, isInstalled]) |
| 54 | |
| 55 | const activeRoute = pageId ?? 'overview' |
| 56 | |
| 57 | const activeNav = useMemo( |
| 58 | () => navItems.find((nav) => nav.route === activeRoute), |
| 59 | [navItems, activeRoute] |
| 60 | ) |
| 61 | |
| 62 | const isKnownRoute = !!activeNav |
| 63 | const layout = (activeNav?.layout ?? 'full') as 'full' | 'constrained' |
| 64 | |
| 65 | const tabs: IntegrationTab[] = useMemo( |
| 66 | () => |
| 67 | navItems.map((nav) => ({ |
| 68 | label: nav.label, |
| 69 | href: `/project/${ref}/integrations/${id}/${nav.route}`, |
| 70 | active: nav.route === activeRoute, |
| 71 | })), |
| 72 | [navItems, ref, id, activeRoute] |
| 73 | ) |
| 74 | |
| 75 | const Component = useMemo( |
| 76 | () => integration?.navigate({ id, pageId, childId }), |
| 77 | [integration, id, pageId, childId] |
| 78 | ) |
| 79 | |
| 80 | const isReady = !!router?.isReady |
| 81 | const isWrapperBlocked = !integrationsWrappers && !!id?.endsWith('_wrapper') |
| 82 | |
| 83 | const pageTitle = integration?.name ?? 'Integration not found' |
| 84 | const pageSubTitle = |
| 85 | integration?.description ?? 'If you think this is an error, please contact support' |
| 86 | |
| 87 | useEffect(() => { |
| 88 | if ( |
| 89 | router?.isReady && |
| 90 | !isAvailableLoading && |
| 91 | !isInstalledLoading && |
| 92 | !!integration && |
| 93 | pageId && |
| 94 | !isKnownRoute |
| 95 | ) { |
| 96 | router.replace(`/project/${ref}/integrations/${id}/overview`) |
| 97 | } |
| 98 | }, [router, pageId, ref, id, integration, isKnownRoute, isAvailableLoading, isInstalledLoading]) |
| 99 | |
| 100 | return { |
| 101 | ref, |
| 102 | id, |
| 103 | pageId, |
| 104 | childId, |
| 105 | activeRoute, |
| 106 | activeNav, |
| 107 | isKnownRoute, |
| 108 | layout, |
| 109 | tabs, |
| 110 | isReady, |
| 111 | isWrapperBlocked, |
| 112 | pageTitle, |
| 113 | pageSubTitle, |
| 114 | integrationsWrappers, |
| 115 | integration, |
| 116 | installation, |
| 117 | isInstalled, |
| 118 | isAvailableLoading, |
| 119 | isInstalledLoading, |
| 120 | Component, |
| 121 | } |
| 122 | } |