import { useRouter } from 'next/router' import { PropsWithChildren, useEffect, useRef, useState } from 'react' import { IntegrationDefinition } from 'components/interfaces/Integrations/Landing/Integrations.constants' import { useInstalledIntegrations } from 'components/interfaces/Integrations/Landing/useInstalledIntegrations' import { Header } from 'components/layouts/Integrations/header' import ProjectLayout from 'components/layouts/ProjectLayout/ProjectLayout' import { ProductMenu } from 'components/ui/ProductMenu' import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types' import { useScroll } from 'framer-motion' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' import { useFlag } from 'hooks/ui/useFlag' import { IntegrationTabs } from './tabs' import { Menu, Separator } from 'ui' import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem' import { GenericSkeletonLoader } from 'ui-patterns' import AlertError from 'components/ui/AlertError' /** * Layout component for the Integrations section * Handles scroll-based sticky header behavior and authentication */ const IntegrationsLayout = ({ ...props }: PropsWithChildren) => { const layoutSidebar = useFlag('integrationLayoutSidebar') if (layoutSidebar) { return } return } /** * Top level layout */ const IntegrationTopHeaderLayout = ({ ...props }: PropsWithChildren) => { const project = useSelectedProject() const router = useRouter() // Refs for the main scrollable area and header const mainElementRef = useRef(null) const headerRef = useRef(null) // Track if header should be in sticky state const [isSticky, setIsSticky] = useState(false) // State to hold the scrollable container element const [container, setContainer] = useState(null) // Initialize framer-motion scroll tracking // Only tracks scroll when container is available const scroll = useScroll({ container: container ? { current: container } : undefined, }) // Set up container reference once mainElementRef is mounted useEffect(() => { if (mainElementRef.current) { setContainer(mainElementRef.current) } }, [mainElementRef.current]) // Set up scroll event listener to handle sticky header behavior useEffect(() => { // Exit if scroll tracking isn't available yet if (!scroll.scrollY) return // Update sticky state based on scroll position relative to header height const handleScroll = (latest: number) => { if (headerRef.current) { setIsSticky(latest > headerRef.current.offsetHeight) } } // Subscribe to scroll position changes const unsubscribe = scroll.scrollY.on('change', handleScroll) // Clean up scroll listener on unmount return () => { unsubscribe() } }, [scroll.scrollY]) const segments = router.asPath.split('/') // construct the page url to be used to determine the active state for the sidebar const page = `${segments[3]}${segments[4] ? `/${segments[4]}` : ''}` const { installedIntegrations: integrations, error, isLoading, isSuccess, isError, } = useInstalledIntegrations() const installedIntegrationItems = integrations.map((integration) => ({ name: integration.name, label: integration.status, key: `integrations/${integration.id}`, url: `/project/${project?.ref}/integrations/${integration.id}/overview`, icon: (
{integration.icon({ className: 'p-1' })}
), items: [], })) return (
Installed integrations
} /> {isLoading && } {isError && ( )} {isSuccess && (
{installedIntegrationItems.map((item) => ( ))}
)} } >
{props.children} ) } const IntegrationsLayoutSide = ({ ...props }: PropsWithChildren) => { const router = useRouter() const page = router.pathname.split('/')[4] const project = useSelectedProject() const { installedIntegrations: integrations, error, isLoading, isError, isSuccess, } = useInstalledIntegrations() const installedIntegrationItems = integrations.map((integration) => ({ name: integration.name, label: integration.status, key: `integrations/${integration.id}`, url: `/project/${project?.ref}/integrations/${integration.id}/overview`, icon: (
{integration.icon({ className: 'p-1' })}
), items: [], })) return (
Installed integrations
} /> {isLoading && } {isError && ( )} {isSuccess && (
{installedIntegrationItems.map((item) => ( ))}
)} } > {props.children}
) } // Wrap component with authentication HOC before exporting export default withAuth(IntegrationsLayout) const generateIntegrationsMenu = ({ projectRef }: { projectRef?: string }): ProductMenuGroup[] => { return [ { title: 'All Integrations', items: [ { name: 'All Integrations', key: 'integrations', url: `/project/${projectRef}/integrations`, items: [], }, ], }, ].filter(Boolean) }