import { useParams } from 'common' import { AlertTriangle, Info } from 'lucide-react' import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' import { Alert, AlertDescription, AlertTitle, Button } from 'ui' import OrganizationPicker from '@/components/interfaces/Integrations/Vercel/OrganizationPicker' import { Markdown } from '@/components/interfaces/Markdown' import { getHasInstalledObject } from '@/components/layouts/IntegrationsLayout/Integrations.utils' import VercelIntegrationWindowLayout from '@/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout' import { ScaffoldColumn, ScaffoldContainer } from '@/components/layouts/Scaffold' import { useIntegrationsQuery } from '@/data/integrations/integrations-query' import { useVercelIntegrationCreateMutation } from '@/data/integrations/vercel-integration-create-mutation' import { useOrganizationsQuery } from '@/data/organizations/organizations-query' import { useIntegrationInstallationSnapshot } from '@/state/integration-installation' import type { NextPageWithLayout, Organization } from '@/types' /** * Variations of the Vercel integration flow. * They require different UI and logic. * * Deploy Button - the flow that starts from the Deploy Button - https://vercel.com/docs/integrations#deploy-button * Marketplace - the flow that starts from the Marketplace - https://vercel.com/integrations * */ export type VercelIntegrationFlow = 'deploy-button' | 'marketing' const VercelIntegration: NextPageWithLayout = () => { const router = useRouter() const { code, configurationId, teamId, source } = useParams() const [selectedOrg, setSelectedOrg] = useState(null) const snapshot = useIntegrationInstallationSnapshot() /** * Fetch the list of organization based integration installations for Vercel. * * Array of integrations installed on all */ const { data: integrationData } = useIntegrationsQuery() const { data: organizationsData, isPending: isLoadingOrganizationsQuery, isSuccess: isOrganizationsDataSuccess, } = useOrganizationsQuery() useEffect(() => { if (organizationsData !== undefined && integrationData !== undefined) { const firstOrg = organizationsData[0] if (firstOrg && selectedOrg === null) { setSelectedOrg(firstOrg) router.query.organizationSlug = firstOrg.slug } } }, [organizationsData, integrationData]) /** * Organizations with extra `installationInstalled` attribute * * Used to show label/badge and allow/disallow installing * */ const installed = useMemo( () => integrationData && organizationsData ? getHasInstalledObject({ integrationName: 'Vercel', integrationData, organizationsData, installationId: configurationId, }) : {}, [configurationId, integrationData, organizationsData] ) /** * Handle the correct route change based on whether the vercel integration * is following the 'marketplace/external' flow or 'deploy button' flow. * See: * - https://vercel.com/docs/integrations/create-integration/submit-integration#query-parameters-for-marketplace * - https://vercel.com/docs/integrations/create-integration/submit-integration#query-parameters-for-external-flow * - https://vercel.com/docs/integrations/create-integration/submit-integration#query-parameters-for-deploy-button */ function handleRouteChange() { const orgSlug = selectedOrg?.slug switch (source) { case 'deploy-button': { router.push({ pathname: `/integrations/vercel/${orgSlug}/deploy-button/new-project`, query: router.query, }) break } case 'marketplace': case 'external': { router.push({ pathname: `/integrations/vercel/${orgSlug}/marketplace/choose-project`, query: router.query, }) break } default: toast.error( `Unsupported Vercel installation source: ${source}. Please contact support if this error persists.` ) } } const { mutate, isPending: isLoadingVercelIntegrationCreateMutation } = useVercelIntegrationCreateMutation({ onMutate() { snapshot.setLoading(true) }, onSuccess() { handleRouteChange() snapshot.setLoading(false) }, onError(error) { toast.error(`Creating Vercel integration failed: ${error.message}`) }, }) function onInstall() { const orgSlug = selectedOrg?.slug const isIntegrationInstalled = orgSlug ? installed[orgSlug] : false if (!orgSlug) { return toast.error('Please select an organization') } if (!code) { return toast.error('Vercel code missing') } if (!configurationId) { return toast.error('Vercel Configuration ID missing') } if (!source) { return toast.error('Vercel Configuration source missing') } /** * Only install if integration hasn't already been installed */ if (!isIntegrationInstalled) { mutate({ code, configurationId, orgSlug, metadata: {}, source, teamId: teamId, }) } else { handleRouteChange() } } const dataLoading = isLoadingVercelIntegrationCreateMutation || isLoadingOrganizationsQuery const noOrganizations = useMemo(() => { return isOrganizationsDataSuccess && organizationsData?.length === 0 ? true : false }, [isOrganizationsDataSuccess, organizationsData]) const alreadyInstalled = useMemo(() => { return selectedOrg && installed[selectedOrg.slug] && source === 'marketplace' && !dataLoading ? true : false }, [installed, selectedOrg, source, dataLoading]) const disableInstallationForm = (isLoadingVercelIntegrationCreateMutation && !dataLoading) || // disables installation button if integration is already installed and it is Marketplace flow alreadyInstalled || noOrganizations const isLoading = useMemo(() => { return isLoadingVercelIntegrationCreateMutation || isLoadingOrganizationsQuery }, [isLoadingVercelIntegrationCreateMutation, isLoadingOrganizationsQuery]) return ( <>

Choose organization

<> { setSelectedOrg(org) router.query.organizationSlug = org.slug }} configurationId={configurationId} /> {alreadyInstalled && ( Vercel Integration is already installed. You will need to choose another organization to install the integration. )} {noOrganizations && ( No Briven Organizations to install Integration. You will need to create a Briven Organization before you can install the Vercel Integration. You can create a new organization{' '} here . )}
You can uninstall this Integration at any time. Remove this integration at any time from Vercel or the Briven dashboard. ) } VercelIntegration.getLayout = (page) => ( {page} ) export default VercelIntegration