import { ReactNode, useEffect, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import { Button, cn, CommandEmpty_Shadcn_, CommandGroup_Shadcn_, CommandInput_Shadcn_, CommandItem_Shadcn_, CommandList_Shadcn_, Command_Shadcn_, IconChevronDown, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, } from 'ui' import ShimmerLine from 'components/ui/ShimmerLine' import { IntegrationConnectionsCreateVariables, IntegrationProjectConnection, } from 'data/integrations/integrations.types' import { useSelectedOrganization } from 'hooks' import { BASE_PATH } from 'lib/constants' export interface Project { id: string name: string ref: string } export interface ForeignProject { id: string name: string } export interface ProjectLinkerProps { organizationIntegrationId: string | undefined foreignProjects: ForeignProject[] supabaseProjects: Project[] onCreateConnections: (variables: IntegrationConnectionsCreateVariables) => void installedConnections: IntegrationProjectConnection[] | undefined isLoading?: boolean integrationIcon: ReactNode getForeignProjectIcon?: (project: ForeignProject) => ReactNode choosePrompt?: string onSkip?: () => void loadingForeignProjects?: boolean loadingSupabaseProjects?: boolean defaultSupabaseProjectRef?: string defaultForeignProjectId?: string } const ProjectLinker = ({ organizationIntegrationId, foreignProjects, supabaseProjects, onCreateConnections: _onCreateConnections, installedConnections = [], isLoading, integrationIcon, getForeignProjectIcon, choosePrompt = 'Choose a project', onSkip, loadingForeignProjects, loadingSupabaseProjects, defaultSupabaseProjectRef, defaultForeignProjectId, }: ProjectLinkerProps) => { const [supabaseProjectsComboBoxOpen, setSupabaseProjectsComboboxOpen] = useState(false) const [foreignProjectsComboBoxOpen, setForeignProjectsComboboxOpen] = useState(false) const supabaseProjectsComboBoxRef = useRef(null) const foreignProjectsComboBoxRef = useRef(null) const selectedOrganization = useSelectedOrganization() const [supabaseProjectRef, setSupabaseProjectRef] = useState( defaultSupabaseProjectRef ) useEffect(() => { if (defaultSupabaseProjectRef !== undefined && supabaseProjectRef === undefined) setSupabaseProjectRef(defaultSupabaseProjectRef) }, [defaultSupabaseProjectRef, supabaseProjectRef]) const [foreignProjectId, setForeignProjectId] = useState( defaultForeignProjectId ) useEffect(() => { if (defaultForeignProjectId !== undefined && foreignProjectId === undefined) setForeignProjectId(defaultForeignProjectId) }, [defaultForeignProjectId, foreignProjectId]) // create a flat array of foreign project ids. ie, ["prj_MlkO6AiLG5ofS9ojKrkS3PhhlY3f", ..] const flatInstalledConnectionsIds = new Set(installedConnections.map((x) => x.foreign_project_id)) const selectedSupabaseProject = supabaseProjectRef ? supabaseProjects.find((x) => x.ref?.toLowerCase() === supabaseProjectRef?.toLowerCase()) : undefined const selectedForeignProject = foreignProjectId ? foreignProjects.find((x) => x.id?.toLowerCase() === foreignProjectId?.toLowerCase()) : undefined function onCreateConnections() { const projectDetails = selectedForeignProject if (!organizationIntegrationId) return console.error('No integration ID set') if (!selectedForeignProject?.id) return console.error('No Foreign project ID set') if (!selectedSupabaseProject?.ref) return console.error('No Supabase project ref set') const alreadyInstalled = flatInstalledConnectionsIds.has(foreignProjectId ?? '') if (alreadyInstalled) { return toast.error( `Unable to connect to ${selectedForeignProject.name}: Selected repository already has an installed connection to a project` ) } _onCreateConnections({ organizationIntegrationId, connection: { foreign_project_id: selectedForeignProject?.id, supabase_project_ref: selectedSupabaseProject?.ref, metadata: { ...projectDetails, }, }, orgSlug: selectedOrganization?.slug, }) } const Panel = ({ children, className, ...props }: React.HTMLAttributes) => { return (
{children}
) } const noSupabaseProjects = supabaseProjects.length === 0 const noForeignProjects = foreignProjects.length === 0 const missingEntity = noSupabaseProjects ? 'Supabase' : 'Vercel' const oppositeMissingEntity = noSupabaseProjects ? 'Vercel' : 'Supabase' return (
{loadingForeignProjects || loadingSupabaseProjects ? (

Loading projects

) : noSupabaseProjects || noForeignProjects ? (
No {missingEntity} Projects found

You will need to create a {missingEntity} Project to link to a {oppositeMissingEntity}{' '} Project.
You can skip this and create a Project Connection later.

) : (
Supabase
} iconRight={ defaultSupabaseProjectRef === undefined ? ( ) : null } > {selectedSupabaseProject ? selectedSupabaseProject.name : 'Choose Project'} No results found. {supabaseProjects.map((project, i) => { return ( { if (project.ref) setSupabaseProjectRef(project.ref) setSupabaseProjectsComboboxOpen(false) }} >
Supabase
{project.name}
) })}
{integrationIcon}
No results found. {foreignProjects.map((project, i) => { return ( { if (project.id) setForeignProjectId(project.id) setForeignProjectsComboboxOpen(false) }} > {getForeignProjectIcon?.(project) ?? integrationIcon} {project.name} ) })}
)}
{onSkip !== undefined && ( )}
) } export default ProjectLinker