import { Activity, ArrowUpCircle, Ban, ChevronLeft, ExternalLink, Pause, Play, RotateCcw, Search, X, } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { useParams } from 'common' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { useReplicationPipelineByIdQuery } from 'data/replication/pipeline-by-id-query' import { useReplicationPipelineReplicationStatusQuery } from 'data/replication/pipeline-replication-status-query' import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query' import { useReplicationPipelineVersionQuery } from 'data/replication/pipeline-version-query' import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation' import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation' import { PipelineStatusRequestStatus, usePipelineRequestStatus, } from 'state/replication-pipeline-request-status' import { Badge, Button, cn } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns' import { Input } from 'ui-patterns/DataInputs/Input' import { ErroredTableDetails } from './ErroredTableDetails' import { PIPELINE_ACTIONABLE_STATES, PIPELINE_ERROR_MESSAGES, getStatusName, } from './Pipeline.utils' import { PipelineStatus, PipelineStatusName } from './PipelineStatus' import { STATUS_REFRESH_FREQUENCY_MS } from './Replication.constants' import { TableState } from './ReplicationPipelineStatus.types' import { getDisabledStateConfig, getStatusConfig } from './ReplicationPipelineStatus.utils' import { UpdateVersionModal } from './UpdateVersionModal' /** * Component for displaying replication pipeline status and table replication details. * Supports both legacy 'error' state and new 'errored' state with retry policies. */ export const ReplicationPipelineStatus = () => { const { ref: projectRef, pipelineId: _pipelineId } = useParams() const [filterString, setFilterString] = useState('') const [showUpdateVersionModal, setShowUpdateVersionModal] = useState(false) const pipelineId = Number(_pipelineId) const { getRequestStatus, updatePipelineStatus, setRequestStatus } = usePipelineRequestStatus() const requestStatus = getRequestStatus(pipelineId) const { data: pipeline, error: pipelineError, isLoading: isPipelineLoading, isError: isPipelineError, } = useReplicationPipelineByIdQuery({ projectRef, pipelineId, }) const { data: pipelineStatusData, error: pipelineStatusError, isLoading: isPipelineStatusLoading, isError: isPipelineStatusError, isSuccess: isPipelineStatusSuccess, } = useReplicationPipelineStatusQuery( { projectRef, pipelineId }, { enabled: !!pipelineId, refetchInterval: STATUS_REFRESH_FREQUENCY_MS, } ) const { data: replicationStatusData, error: statusError, isLoading: isStatusLoading, isError: isStatusError, } = useReplicationPipelineReplicationStatusQuery( { projectRef, pipelineId }, { enabled: !!pipelineId, refetchInterval: STATUS_REFRESH_FREQUENCY_MS, } ) const { data: versionData } = useReplicationPipelineVersionQuery({ projectRef, pipelineId: pipeline?.id, }) const hasUpdate = Boolean(versionData?.new_version) const { mutateAsync: startPipeline, isLoading: isStartingPipeline } = useStartPipelineMutation() const { mutateAsync: stopPipeline, isLoading: isStoppingPipeline } = useStopPipelineMutation() const destinationName = pipeline?.destination_name const statusName = getStatusName(pipelineStatusData?.status) const config = getDisabledStateConfig({ requestStatus, statusName }) const tableStatuses = replicationStatusData?.table_statuses || [] const filteredTableStatuses = filterString.length === 0 ? tableStatuses : tableStatuses.filter((table: TableState) => table.table_name.toLowerCase().includes(filterString.toLowerCase()) ) const isPipelineRunning = statusName === 'started' const hasTableData = tableStatuses.length > 0 const isEnablingDisabling = requestStatus === PipelineStatusRequestStatus.StartRequested || requestStatus === PipelineStatusRequestStatus.StopRequested || requestStatus === PipelineStatusRequestStatus.RestartRequested const showDisabledState = !isPipelineRunning || isEnablingDisabling const logsUrl = `/project/${projectRef}/logs/etl-replication-logs${ pipelineId ? `?f=${encodeURIComponent(JSON.stringify({ pipeline_id: pipelineId }))}` : '' }` const label = statusName === 'stopped' ? 'Start' : statusName === 'started' ? 'Stop' : statusName === 'failed' ? 'Restart' : 'Action unavailable' const icon = statusName === 'stopped' ? ( ) : statusName === 'started' ? ( ) : statusName === 'failed' ? ( ) : ( ) const onPrimaryAction = async () => { if (!projectRef) return console.error('Project ref is required') if (!pipeline) return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) try { if (statusName === 'stopped') { setRequestStatus(pipeline.id, PipelineStatusRequestStatus.StartRequested, statusName) await startPipeline({ projectRef, pipelineId: pipeline.id }) } else if (statusName === 'started') { setRequestStatus(pipeline.id, PipelineStatusRequestStatus.StopRequested, statusName) await stopPipeline({ projectRef, pipelineId: pipeline.id }) } else if (statusName === 'failed') { setRequestStatus(pipeline.id, PipelineStatusRequestStatus.RestartRequested, statusName) await startPipeline({ projectRef, pipelineId: pipeline.id }) } } catch (error) { toast.error(PIPELINE_ERROR_MESSAGES.ENABLE_DESTINATION) } } useEffect(() => { updatePipelineStatus(pipelineId, statusName) }, [pipelineId, statusName, updatePipelineStatus]) return ( <>

{destinationName || 'Pipeline'}

{hasUpdate && ( )} } className="pl-7 h-[26px] text-xs" placeholder="Search for tables" value={filterString} disabled={isPipelineError} onChange={(e) => setFilterString(e.target.value)} actions={ filterString.length > 0 ? [ setFilterString('')} />, ] : undefined } />
{(isPipelineLoading || isStatusLoading) && } {isPipelineError && ( )} {isStatusError && ( )} {hasTableData && (
{showDisabledState && (
{config.icon}

{config.title}

{config.message}

)}
{/* [Joshen] Should update to use new Table components next time */} Table, Status, Details, ]} body={ <> {filteredTableStatuses.length === 0 && hasTableData && (

No results found

Your search for "{filterString}" did not return any results

)} {filteredTableStatuses.map((table: TableState, index: number) => { const statusConfig = getStatusConfig(table.state) return (

{table.table_name}

} tooltip={{ content: { side: 'bottom', text: 'Open in Table Editor' }, }} >
{showDisabledState ? ( Not Available ) : ( statusConfig.badge )} {showDisabledState ? (

Status unavailable while pipeline is {config.badge.toLowerCase()}

) : (
{statusConfig.description}
{table.state.name === 'error' && ( )}
)}
) })} } /> )} {!isStatusLoading && tableStatuses.length === 0 && (

{showDisabledState ? 'Pipeline not running' : 'No table status information'}

{showDisabledState ? `The replication pipeline is currently ${statusName || 'not active'}. Table status information is not available while the pipeline is in this state.` : `This pipeline doesn't have any table replication status data available yet. The status will appear here once replication begins.`}

Data refreshes automatically every 2 seconds

)} setShowUpdateVersionModal(false)} confirmLabel={ statusName === PipelineStatusName.STARTED || statusName === PipelineStatusName.FAILED ? 'Update and restart' : 'Update version' } /> ) }