import { useEffect, useState } from 'react' import { toast } from 'sonner' import { ActionButtons, ForeignProjectSelector, Panel, SupabaseProjectSelector, } from './ProjectLinkerComponents' import { Project, ProjectLinkerProps } from './VercelGithub.types' import { InterstitialActionError } from '@/components/layouts/InterstitialLayout' import ShimmerLine from '@/components/ui/ShimmerLine' import { useOrgProjectsInfiniteQuery } from '@/data/projects/org-projects-infinite-query' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { BASE_PATH } from '@/lib/constants' import { EMPTY_ARR } from '@/lib/void' export const ProjectLinker = ({ slug, organizationIntegrationId, foreignProjects, onCreateConnections: _onCreateConnections, installedConnections = EMPTY_ARR, isLoading, integrationIcon, getForeignProjectIcon, choosePrompt = 'Choose a project', onSkip, loadingForeignProjects, showNoEntitiesState = true, defaultSupabaseProject, defaultForeignProjectId, mode, variant = 'default', actionError, onSelectionChange, }: ProjectLinkerProps) => { const [openProjectsDropdown, setOpenProjectsDropdown] = useState(false) const [openForeignProjectsComboBox, setOpenForeignProjectsComboBox] = useState(false) const [foreignProjectId, setForeignProjectId] = useState( defaultForeignProjectId ) const [selectedSupabaseProject, setSelectedSupabaseProject] = useState() const [validationError, setValidationError] = useState() const { data: selectedOrganization } = useSelectedOrganizationQuery() const { data: orgProjects, isPending: loadingSupabaseProjects } = useOrgProjectsInfiniteQuery({ slug, }) const numProjects = orgProjects?.pages[0].pagination.count ?? 0 // create a flat array of foreign project ids. ie, ["prj_MlkO6AiLG5ofS9ojKrkS3PhhlY3f", ..] const flatInstalledConnectionsIds = new Set(installedConnections.map((x) => x.foreign_project_id)) const selectedForeignProject = foreignProjectId ? foreignProjects.find((x) => x.id?.toLowerCase() === foreignProjectId?.toLowerCase()) : undefined function onCreateConnections() { const projectDetails = selectedForeignProject 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) { const message = `Unable to connect to ${selectedForeignProject.name}: Selected repository already has an installed connection to a project` if (variant === 'interstitial') { setValidationError(message) return } return toast.error(message) } setValidationError(undefined) _onCreateConnections({ organizationIntegrationId: organizationIntegrationId!, connection: { foreign_project_id: selectedForeignProject?.id, supabase_project_ref: selectedSupabaseProject?.ref, integration_id: '0', metadata: { ...projectDetails, }, }, orgSlug: selectedOrganization?.slug, new: { installation_id: selectedForeignProject.installation_id!, project_ref: selectedSupabaseProject.ref, repository_id: Number(selectedForeignProject.id), }, }) } const noSupabaseProjects = numProjects === 0 const noForeignProjects = foreignProjects.length === 0 const missingEntity = noSupabaseProjects ? 'Supabase' : mode const oppositeMissingEntity = noSupabaseProjects ? mode : 'Supabase' const connectDisabled = loadingForeignProjects || loadingSupabaseProjects || isLoading || !selectedSupabaseProject || !selectedForeignProject const displayedActionError = actionError ?? validationError const setForeignProjectSelection: typeof setForeignProjectId = (value) => { setForeignProjectId(value) setValidationError(undefined) onSelectionChange?.() } const setSupabaseProjectSelection: typeof setSelectedSupabaseProject = (value) => { setSelectedSupabaseProject(value) setValidationError(undefined) onSelectionChange?.() } useEffect(() => { if (defaultSupabaseProject !== undefined && selectedSupabaseProject === undefined) setSelectedSupabaseProject(defaultSupabaseProject) }, [defaultSupabaseProject, selectedSupabaseProject]) useEffect(() => { if (defaultForeignProjectId !== undefined && foreignProjectId === undefined) setForeignProjectId(defaultForeignProjectId) }, [defaultForeignProjectId, foreignProjectId]) if (variant === 'interstitial') { return (
{loadingForeignProjects || loadingSupabaseProjects ? (

Loading projects

) : showNoEntitiesState && (noSupabaseProjects || noForeignProjects) ? (
No {missingEntity} projects found. Create a {missingEntity} project to link to a{' '} {oppositeMissingEntity} project {onSkip !== undefined ? ', or skip and connect later.' : '.'}
) : ( <>

Supabase project

Vercel project

)}
) } return (
{loadingForeignProjects ? (

Loading projects

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

You will need to create a {missingEntity} Project to link to a {oppositeMissingEntity}{' '} Project. {onSkip !== undefined && ( <>
You can skip this and create a Project Connection later. )}

) : (
Supabase
{integrationIcon}
)}
) }