diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx
index 970bb195668..33eb3d2b734 100644
--- a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx
@@ -5,7 +5,7 @@ import { useReplicationPipelineReplicationStatusQuery } from 'data/replication/p
import { useReplicationPipelineByIdQuery } from 'data/replication/pipeline-by-id-query'
import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query'
import PipelineStatus from './PipelineStatus'
-import { usePipelineRequestStatus } from 'state/replication-pipeline-request-status'
+import { usePipelineRequestStatus, PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status'
import { getStatusName } from './Pipeline.utils'
import { useState, useEffect } from 'react'
import {
@@ -18,7 +18,18 @@ import {
Input_Shadcn_,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
-import { Copy, ExternalLink, ChevronLeft, Search, AlertTriangle, Activity } from 'lucide-react'
+import {
+ Copy,
+ ExternalLink,
+ ChevronLeft,
+ Search,
+ AlertTriangle,
+ Activity,
+ Loader2,
+ Clock,
+ XCircle,
+ HelpCircle,
+} from 'lucide-react'
import Link from 'next/link'
import { toast } from 'sonner'
import { copyToClipboard } from 'ui'
@@ -129,7 +140,7 @@ const ReplicationPipelineStatus = ({
case 'following_wal':
return {
badge: Live,
- description: `Replicating live changes (${state.lag}ms lag)`,
+ description: `Replicating live changes`,
color: 'text-success-600',
}
case 'error':
@@ -154,42 +165,91 @@ const ReplicationPipelineStatus = ({
}
}
- const getDisabledStateTitle = (status: string | undefined) => {
- switch (status) {
- case 'failed':
- return 'Pipeline Failed'
- case 'stopped':
- return 'Pipeline Stopped'
- case 'starting':
- return 'Pipeline Starting'
- default:
- return 'Pipeline Not Running'
+ const getDisabledStateConfig = () => {
+ if (isEnablingDisabling) {
+ const isEnabling = requestStatus === PipelineStatusRequestStatus.EnableRequested
+ return {
+ title: isEnabling ? 'Pipeline Enabling' : 'Pipeline Disabling',
+ message: isEnabling
+ ? 'Pipeline is being enabled and will start shortly.'
+ : 'Pipeline is being disabled and will stop shortly.',
+ badge: isEnabling ? 'Enabling' : 'Disabling',
+ icon: ,
+ colors: {
+ bg: isEnabling ? 'bg-brand-50 border-brand-200' : 'bg-warning-50 border-warning-200',
+ text: isEnabling ? 'text-brand-900' : 'text-warning-900',
+ subtext: isEnabling ? 'text-brand-700' : 'text-warning-700',
+ icon: isEnabling ? 'text-brand-600' : 'text-warning-600',
+ },
+ }
}
- }
- const getDisabledStateMessage = (status: string | undefined) => {
- switch (status) {
+ switch (statusName) {
case 'failed':
- return 'Replication has failed. Check the logs for details. Table status is disabled.'
+ return {
+ title: 'Pipeline Failed',
+ message: 'Replication has failed. Check the logs for details.',
+ badge: 'Failed',
+ icon: ,
+ colors: {
+ bg: 'bg-destructive-50 border-destructive-200',
+ text: 'text-destructive-900',
+ subtext: 'text-destructive-700',
+ icon: 'text-destructive-600',
+ },
+ }
case 'stopped':
- return 'Replication is paused. Enable the pipeline to resume data synchronization.'
+ return {
+ title: 'Pipeline Stopped',
+ message: 'Enable the pipeline to resume data synchronization.',
+ badge: 'Stopped',
+ icon: ,
+ colors: {
+ bg: 'bg-surface-100 border-border-muted',
+ text: 'text-foreground',
+ subtext: 'text-foreground-light',
+ icon: 'text-foreground-lighter',
+ },
+ }
case 'starting':
- return 'Pipeline is initializing. Table status will be available once started.'
+ return {
+ title: 'Pipeline Starting',
+ message: 'Pipeline is initializing. Table status will be available once started.',
+ badge: 'Starting',
+ icon: ,
+ colors: {
+ bg: 'bg-warning-50 border-warning-200',
+ text: 'text-warning-900',
+ subtext: 'text-warning-700',
+ icon: 'text-warning-600',
+ },
+ }
+ case 'unknown':
+ return {
+ title: 'Pipeline Status Unknown',
+ message: 'Unable to determine pipeline status. Check the logs for more information.',
+ badge: 'Unknown',
+ icon: ,
+ colors: {
+ bg: 'bg-warning-50 border-warning-200',
+ text: 'text-warning-900',
+ subtext: 'text-warning-700',
+ icon: 'text-warning-600',
+ },
+ }
default:
- return 'Pipeline is not actively running. Table status information is disabled.'
- }
- }
-
- const getDisabledStateBadge = (status: string | undefined) => {
- switch (status) {
- case 'failed':
- return 'Failed'
- case 'stopped':
- return 'Stopped'
- case 'starting':
- return 'Starting'
- default:
- return 'Disabled'
+ return {
+ title: 'Pipeline Not Running',
+ message: 'Pipeline is not actively running.',
+ badge: 'Disabled',
+ icon: ,
+ colors: {
+ bg: 'bg-surface-100 border-border-muted',
+ text: 'text-foreground',
+ subtext: 'text-foreground-light',
+ icon: 'text-foreground-lighter',
+ },
+ }
}
}
@@ -204,7 +264,12 @@ const ReplicationPipelineStatus = ({
const errorTables = tableStatuses.filter((table: TableState) => table.state.name === 'error')
const hasErrors = errorTables.length > 0
const isPipelineRunning = statusName === 'started'
- const hasTableData = tableStatuses.length > 0 // Any state other than 'started' is not running
+ const hasTableData = tableStatuses.length > 0
+ const isEnablingDisabling =
+ requestStatus === PipelineStatusRequestStatus.EnableRequested ||
+ requestStatus === PipelineStatusRequestStatus.DisableRequested
+ const showDisabledState = !isPipelineRunning || isEnablingDisabling
+ const showActiveTable = isPipelineRunning && !isEnablingDisabling
if (isPipelineError) {
return (
@@ -300,37 +365,36 @@ const ReplicationPipelineStatus = ({
)}
- {/* Pipeline not running state - generic disabled state */}
- {!isPipelineRunning && hasTableData && (
-
-
-
-
-
-
- {getDisabledStateTitle(statusName)}
-
-
- {getDisabledStateMessage(statusName)}
-
+ {/* Pipeline not running state */}
+ {showDisabledState && hasTableData && (() => {
+ const config = getDisabledStateConfig()
+ return (
+
+
+
+
+
+
+ {config.title}
+
+
+ {config.message}
+
+
-
-
-
Table,
- Status,
- Details,
-
- Actions
- ,
- ]}
- body={filteredTableStatuses.map((table: TableState, index: number) => {
- return (
+
+
Table,
+ Status,
+ Details,
+ Actions,
+ ]}
+ body={filteredTableStatuses.map((table: TableState, index: number) => (
- {getDisabledStateBadge(statusName)}
+ Not Available
@@ -361,7 +425,7 @@ const ReplicationPipelineStatus = ({
type="text"
size="tiny"
icon={}
- disabled={true}
+ disabled
className="h-auto p-2 opacity-40 cursor-not-allowed"
/>
@@ -372,15 +436,15 @@ const ReplicationPipelineStatus = ({
- )
- })}
- />
+ ))}
+ />
+
-
- )}
+ )
+ })()}
{/* Tables list - active/running pipeline */}
- {isPipelineRunning && !isStatusLoading && hasTableData && (
+ {showActiveTable && !isStatusLoading && hasTableData && (
@@ -484,7 +548,7 @@ const ReplicationPipelineStatus = ({
)}
{/* Empty state - not running pipeline */}
- {!isPipelineRunning && !isStatusLoading && tableStatuses.length === 0 && (
+ {showDisabledState && !isStatusLoading && tableStatuses.length === 0 && (