diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx
index 71a8894a17d..64a67c980db 100644
--- a/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx
@@ -11,12 +11,9 @@ import {
PipelineStatusRequestStatus,
usePipelineRequestStatus,
} from 'state/replication-pipeline-request-status'
-import { getStatusName } from './Pipeline.utils'
+import { getStatusName, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { useParams } from 'common'
-import {
- ReplicationPipelineStatusData,
- useReplicationPipelineStatusQuery,
-} from 'data/replication/pipeline-status-query'
+import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query'
import { useState, useEffect } from 'react'
import { toast } from 'sonner'
import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation'
@@ -96,14 +93,14 @@ const DestinationRow = ({
return
}
if (!pipeline) {
- toast.error('No pipeline found')
+ toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND)
return
}
try {
await startPipeline({ projectRef, pipelineId: pipeline.id })
} catch (error) {
- toast.error('Failed to enable destination')
+ toast.error(PIPELINE_ERROR_MESSAGES.ENABLE_DESTINATION)
}
setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.EnableRequested)
}
@@ -113,14 +110,14 @@ const DestinationRow = ({
return
}
if (!pipeline) {
- toast.error('No pipeline found')
+ toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND)
return
}
try {
await stopPipeline({ projectRef, pipelineId: pipeline.id })
} catch (error) {
- toast.error('Failed to disable destination')
+ toast.error(PIPELINE_ERROR_MESSAGES.DISABLE_DESTINATION)
}
setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.DisableRequested)
}
@@ -132,7 +129,7 @@ const DestinationRow = ({
return
}
if (!pipeline) {
- toast.error('No pipeline found')
+ toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND)
return
}
@@ -142,14 +139,14 @@ const DestinationRow = ({
// so we don't need to call deletePipeline explicitly
await deleteDestination({ projectRef, destinationId: destinationId })
} catch (error) {
- toast.error('Failed to delete destination')
+ toast.error(PIPELINE_ERROR_MESSAGES.DELETE_DESTINATION)
}
}
return (
<>
{isPipelineError && (
-
+
)}
{isPipelineSuccess && (
diff --git a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx
index 0ee44a0ba7a..09de2bfae34 100644
--- a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx
@@ -11,6 +11,7 @@ import { useState } from 'react'
import NewDestinationPanel from './DestinationPanel'
import { useReplicationSourcesQuery } from 'data/replication/sources-query'
import { noop } from 'lodash'
+import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
interface DestinationsProps {
onSelectPipeline?: (pipelineId: number, destinationName: string) => void
@@ -92,7 +93,7 @@ const Destinations = ({ onSelectPipeline = noop }: DestinationsProps) => {
{(isSourcesError || isDestinationsError) && (
)}
diff --git a/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts b/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts
index d63da196471..89e15496e19 100644
--- a/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts
+++ b/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts
@@ -1,6 +1,19 @@
import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query'
import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status'
+// Shared error messages for consistency
+export const PIPELINE_ERROR_MESSAGES = {
+ RETRIEVE_PIPELINE: 'Failed to retrieve pipeline information',
+ RETRIEVE_PIPELINE_STATUS: 'Failed to retrieve pipeline status',
+ RETRIEVE_REPLICATION_STATUS: 'Failed to retrieve table replication status',
+ RETRIEVE_DESTINATIONS: 'Failed to retrieve destinations',
+ ENABLE_DESTINATION: 'Failed to enable destination',
+ DISABLE_DESTINATION: 'Failed to disable destination',
+ DELETE_DESTINATION: 'Failed to delete destination',
+ NO_PIPELINE_FOUND: 'No pipeline found',
+ COPY_TABLE_STATUS: 'Failed to copy table status',
+} as const
+
export const getStatusName = (
status: ReplicationPipelineStatusData['status'] | undefined
): string | undefined => {
@@ -37,6 +50,11 @@ export const PIPELINE_STATE_MESSAGES = {
message: 'Initializing replication. Table status will be available once running.',
badge: 'Starting',
},
+ running: {
+ title: 'Pipeline Running',
+ message: 'Replication is active and processing data',
+ badge: 'Running',
+ },
unknown: {
title: 'Pipeline Status Unknown',
message: 'Unable to determine replication status. Check the logs for more information.',
@@ -49,7 +67,6 @@ export const PIPELINE_STATE_MESSAGES = {
},
} as const
-// Helper to get consistent state messages based on request status and pipeline status
export const getPipelineStateMessages = (
requestStatus: PipelineStatusRequestStatus | undefined,
statusName: string | undefined
@@ -71,6 +88,8 @@ export const getPipelineStateMessages = (
return PIPELINE_STATE_MESSAGES.stopped
case 'starting':
return PIPELINE_STATE_MESSAGES.starting
+ case 'started':
+ return PIPELINE_STATE_MESSAGES.running
case 'unknown':
return PIPELINE_STATE_MESSAGES.unknown
default:
diff --git a/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx
index 66629a919c8..3181a5abace 100644
--- a/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx
@@ -6,7 +6,11 @@ import { Loader2, AlertTriangle } from 'lucide-react'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'ui'
import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query'
import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status'
-import { PIPELINE_STATE_MESSAGES } from './Pipeline.utils'
+import {
+ getPipelineStateMessages,
+ PIPELINE_STATE_MESSAGES,
+ PIPELINE_ERROR_MESSAGES,
+} from './Pipeline.utils'
export enum PipelineStatusName {
FAILED = 'failed',
@@ -58,7 +62,7 @@ const PipelineStatus = ({
- {PIPELINE_STATE_MESSAGES.failed.message}
+ {getPipelineStateMessages(requestStatus, 'failed').message}
@@ -66,12 +70,20 @@ const PipelineStatus = ({
}
// Map backend statuses to UX-friendly display
const getStatusConfig = () => {
+ const statusName =
+ pipelineStatus && typeof pipelineStatus === 'object' && 'name' in pipelineStatus
+ ? pipelineStatus.name
+ : undefined
+
+ // Get consistent tooltip message using the same logic as other components
+ const stateMessages = getPipelineStateMessages(requestStatus, statusName)
+
if (requestStatus === PipelineStatusRequestStatus.EnableRequested) {
return {
label: 'Enabling...',
dot: ,
color: 'text-brand-600',
- tooltip: PIPELINE_STATE_MESSAGES.enabling.message,
+ tooltip: stateMessages.message,
}
}
@@ -80,7 +92,7 @@ const PipelineStatus = ({
label: 'Disabling...',
dot: ,
color: 'text-warning-600',
- tooltip: PIPELINE_STATE_MESSAGES.disabling.message,
+ tooltip: stateMessages.message,
}
}
@@ -98,35 +110,35 @@ const PipelineStatus = ({
label: 'Starting',
dot: ,
color: 'text-warning-600',
- tooltip: PIPELINE_STATE_MESSAGES.starting.message,
+ tooltip: stateMessages.message,
}
case PipelineStatusName.STARTED:
return {
label: 'Running',
dot: ,
color: 'text-brand-600',
- tooltip: 'Replication is active and processing data',
+ tooltip: stateMessages.message,
}
case PipelineStatusName.STOPPED:
return {
label: 'Stopped',
dot: ,
color: 'text-foreground-light',
- tooltip: PIPELINE_STATE_MESSAGES.stopped.message,
+ tooltip: stateMessages.message,
}
case PipelineStatusName.UNKNOWN:
return {
label: 'Unknown',
dot: ,
color: 'text-warning-600',
- tooltip: PIPELINE_STATE_MESSAGES.unknown.message,
+ tooltip: stateMessages.message,
}
default:
return {
label: 'Unknown',
dot: ,
color: 'text-destructive-600',
- tooltip: PIPELINE_STATE_MESSAGES.unknown.message,
+ tooltip: stateMessages.message,
}
}
}
@@ -145,7 +157,9 @@ const PipelineStatus = ({
return (
<>
{isLoading && }
- {isError && }
+ {isError && (
+
+ )}
{isSuccess && (
<>
{statusConfig.isFailedStatus ? (
diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx
index 98bade929f8..495a1945934 100644
--- a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx
@@ -9,7 +9,7 @@ import {
usePipelineRequestStatus,
PipelineStatusRequestStatus,
} from 'state/replication-pipeline-request-status'
-import { getStatusName, getPipelineStateMessages } from './Pipeline.utils'
+import { getStatusName, getPipelineStateMessages, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { useState, useEffect } from 'react'
import {
Button,
@@ -116,7 +116,7 @@ const ReplicationPipelineStatus = ({
await copyToClipboard(statusText)
toast.success('Table status copied to clipboard')
} catch {
- toast.error('Failed to copy table status')
+ toast.error(PIPELINE_ERROR_MESSAGES.COPY_TABLE_STATUS)
}
}
@@ -255,7 +255,7 @@ const ReplicationPipelineStatus = ({
Pipeline Status
-
+
)
}
@@ -305,7 +305,10 @@ const ReplicationPipelineStatus = ({
{(isPipelineLoading || isStatusLoading) && }
{isStatusError && (
-
+
)}
{/* Error alert */}
diff --git a/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx b/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx
index f09cc703093..f2467075c43 100644
--- a/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx
+++ b/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx
@@ -1,4 +1,5 @@
import AlertError from 'components/ui/AlertError'
+import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { Edit, MoreVertical, Pause, Play, Trash } from 'lucide-react'
import { ResponseError } from 'types'
import {
@@ -46,7 +47,9 @@ const RowMenu = ({
return (
{isLoading &&
}
- {isError &&
}
+ {isError && (
+
+ )}