import { useFeatureFlags, useParams } from 'common' import { ArrowUpRight } from 'lucide-react' import Link from 'next/link' import { parseAsInteger, parseAsStringEnum, useQueryState } from 'nuqs' import { useEffect, useRef } from 'react' import { toast } from 'sonner' import { Button, DialogSectionSeparator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetSection, SheetTitle, } from 'ui' import { Admonition } from 'ui-patterns/Admonition' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { EnablePipelinesCallout } from '../EnablePipelinesCallout' import { PipelineStatusName } from '../Replication.constants' import { useDestinationInformation } from '../useDestinationInformation' import { useIsETLPrivateAlpha } from '../useIsETLPrivateAlpha' import { DestinationForm } from './DestinationForm' import { DestinationType } from './DestinationPanel.types' import { DestinationTypeSelection } from './DestinationTypeSelection' import { DiscardChangesConfirmationDialog } from '@/components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog' import { DocsButton } from '@/components/ui/DocsButton' import { useReplicationDestinationsQuery } from '@/data/replication/destinations-query' import { checkLocalETLNotSetUp } from '@/data/replication/utils' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { useConfirmOnClose } from '@/hooks/ui/useConfirmOnClose' import { DOCS_URL, IS_PLATFORM } from '@/lib/constants' const DESTINATION_DOCS_PATHS: Partial> = { BigQuery: '/guides/database/replication/pipelines/bigquery#configure-bigquery-as-a-destination', ClickHouse: '/guides/database/replication/pipelines/clickhouse#configure-clickhouse-as-a-destination', DuckLake: '/guides/database/replication/pipelines/ducklake#choose-a-configuration-mode', Snowflake: '/guides/database/replication/pipelines/snowflake#prepare-snowflake-resources', } export const DestinationPanel = () => { const { ref: projectRef } = useParams() const { hasLoaded: flagsLoaded } = useFeatureFlags() const { isPending: isOrganizationPending } = useSelectedOrganizationQuery() const enablePgReplicate = useIsETLPrivateAlpha() const isAccessLoading = IS_PLATFORM && (!flagsLoaded || isOrganizationPending) const { error: destinationsError } = useReplicationDestinationsQuery({ projectRef }) const isLocalETLNotSetUp = checkLocalETLNotSetUp(destinationsError) const [urlDestinationType, setDestinationType] = useQueryState( 'destinationType', parseAsStringEnum([ 'BigQuery', 'Analytics Bucket', 'DuckLake', 'Snowflake', 'ClickHouse', ]).withOptions({ history: 'push', clearOnDefault: true, }) ) const [edit, setEdit] = useQueryState( 'edit', parseAsInteger.withOptions({ history: 'push', clearOnDefault: true, }) ) const visible = urlDestinationType !== null || edit !== null const editMode = edit !== null const { sourceId, pipeline, statusName, replicationNotEnabled, type: existingDestinationType, destinationFetcher, } = useDestinationInformation({ id: edit }) const destinationType = existingDestinationType ?? urlDestinationType const invalidExistingDestination = destinationFetcher.error?.code === 404 const showAccessRequest = !isAccessLoading && !enablePgReplicate const showEnablement = !isAccessLoading && enablePgReplicate && replicationNotEnabled const showDestinationForm = !isAccessLoading && enablePgReplicate && !replicationNotEnabled const existingDestination = editMode ? { sourceId, destinationId: edit, pipelineId: pipeline?.id, statusName, enabled: statusName === PipelineStatusName.STARTED || statusName === PipelineStatusName.FAILED, } : undefined const checkIsDirtyRef = useRef<() => boolean>(() => false) const onClose = () => { checkIsDirtyRef.current = () => false setDestinationType(null) setEdit(null) } const { confirmOnClose, handleOpenChange, modalProps } = useConfirmOnClose({ checkIsDirty: () => checkIsDirtyRef.current(), onClose, }) const docsUrl = `${DOCS_URL}${ (destinationType && DESTINATION_DOCS_PATHS[destinationType]) ?? '/guides/database/replication/pipelines#step-3-configure-a-destination' }` useEffect(() => { if (edit !== null && invalidExistingDestination) { toast(`Unable to find destination ID ${edit}`) setEdit(null) } }, [edit, invalidExistingDestination, setEdit]) const typeSelection = ( <> ) const pipelinesTypeSelection = ( <> {typeSelection} {destinationType != null && isLocalETLNotSetUp && ( )} ) return ( <>
{editMode ? 'Edit pipeline' : 'Add pipeline'} {editMode ? 'Update how this pipeline sends data to its destination.' : 'Send tables to an external destination for analytics workloads.'}
{isAccessLoading && ( )} {showAccessRequest && (
{pipelinesTypeSelection} } > Request access } />
)} {showEnablement && (
{pipelinesTypeSelection}
)} {showDestinationForm && ( )}
) }