import {
Activity,
ArrowUpCircle,
Ban,
ChevronLeft,
ExternalLink,
Info,
Pause,
Play,
RotateCcw,
Search,
WifiOff,
X,
} from 'lucide-react'
import Link from 'next/link'
import { parseAsString, useQueryState } from 'nuqs'
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 { useRestartPipelineHelper } from 'data/replication/restart-pipeline-helper'
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 } from '../PipelineStatus'
import { PipelineStatusName, STATUS_REFRESH_FREQUENCY_MS } from '../Replication.constants'
import { UpdateVersionModal } from '../UpdateVersionModal'
import { SlotLagMetrics, TableState } from './ReplicationPipelineStatus.types'
import { getDisabledStateConfig, getStatusConfig } from './ReplicationPipelineStatus.utils'
import { SlotLagMetricsInline, SlotLagMetricsList } from './SlotLagMetrics'
/**
* 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 [searchString, setSearchString] = useQueryState('search', parseAsString.withDefault(''))
const [showUpdateVersionModal, setShowUpdateVersionModal] = useState(false)
const pipelineId = Number(_pipelineId)
const { getRequestStatus, updatePipelineStatus, setRequestStatus } = usePipelineRequestStatus()
const requestStatus = getRequestStatus(pipelineId)
const {
data: pipeline,
error: pipelineError,
isPending: 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,
isPending: 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, isPending: isStartingPipeline } = useStartPipelineMutation()
const { mutateAsync: stopPipeline, isPending: isStoppingPipeline } = useStopPipelineMutation()
const { restartPipeline } = useRestartPipelineHelper()
const destinationName = pipeline?.destination_name
const statusName = getStatusName(pipelineStatusData?.status)
const config = getDisabledStateConfig({ requestStatus, statusName })
const tableStatuses = replicationStatusData?.table_statuses || []
const applyLagMetrics = replicationStatusData?.apply_lag
const filteredTableStatuses =
searchString.length === 0
? tableStatuses
: tableStatuses.filter((table) =>
table.table_name.toLowerCase().includes(searchString.toLowerCase())
)
const tablesWithLag = tableStatuses.filter((table) => Boolean(table.table_sync_lag))
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 refreshIntervalLabel =
STATUS_REFRESH_FREQUENCY_MS >= 1000
? `${Math.round(STATUS_REFRESH_FREQUENCY_MS / 1000)}s`
: `${STATUS_REFRESH_FREQUENCY_MS}ms`
const logsUrl = `/project/${projectRef}/logs/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' ? (
{config.message}
Snapshot of how far this pipeline is trailing behind right now.
Updates every {refreshIntervalLabel}
Unable to refresh data. Showing the last values we received.
)}