This commit is contained in:
Riccardo Busetti committed 2025-07-23 09:11:27 +02:00
1 parent 65c5cbc397
commit 67d8df25e3
6 files changed
+66 -29

No files matched your search

@@ -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 && (
<AlertError error={pipelineError} subject="Failed to retrieve pipeline" />
<AlertError error={pipelineError} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE} />
)}
{isPipelineSuccess && (
<Table.tr>
@@ -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) && (
<AlertError
error={sourcesError || destinationsError}
subject="Failed to retrieve destinations"
subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_DESTINATIONS}
/>
)}
@@ -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:
@@ -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 = ({
</div>
</TooltipTrigger>
<TooltipContent>
<p>{PIPELINE_STATE_MESSAGES.failed.message}</p>
<p>{getPipelineStateMessages(requestStatus, 'failed').message}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
@@ -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: <Loader2 className="animate-spin w-3 h-3 text-brand-600" />,
color: 'text-brand-600',
tooltip: PIPELINE_STATE_MESSAGES.enabling.message,
tooltip: stateMessages.message,
}
}
@@ -80,7 +92,7 @@ const PipelineStatus = ({
label: 'Disabling...',
dot: <Loader2 className="animate-spin w-3 h-3 text-warning-600" />,
color: 'text-warning-600',
tooltip: PIPELINE_STATE_MESSAGES.disabling.message,
tooltip: stateMessages.message,
}
}
@@ -98,35 +110,35 @@ const PipelineStatus = ({
label: 'Starting',
dot: <Loader2 className="animate-spin w-3 h-3 text-warning-600" />,
color: 'text-warning-600',
tooltip: PIPELINE_STATE_MESSAGES.starting.message,
tooltip: stateMessages.message,
}
case PipelineStatusName.STARTED:
return {
label: 'Running',
dot: <div className="w-2 h-2 bg-brand-600 rounded-full" />,
color: 'text-brand-600',
tooltip: 'Replication is active and processing data',
tooltip: stateMessages.message,
}
case PipelineStatusName.STOPPED:
return {
label: 'Stopped',
dot: <div className="w-2 h-2 bg-foreground-lighter rounded-full" />,
color: 'text-foreground-light',
tooltip: PIPELINE_STATE_MESSAGES.stopped.message,
tooltip: stateMessages.message,
}
case PipelineStatusName.UNKNOWN:
return {
label: 'Unknown',
dot: <div className="w-2 h-2 bg-warning-600 rounded-full" />,
color: 'text-warning-600',
tooltip: PIPELINE_STATE_MESSAGES.unknown.message,
tooltip: stateMessages.message,
}
default:
return {
label: 'Unknown',
dot: <div className="w-2 h-2 bg-destructive-600 rounded-full" />,
color: 'text-destructive-600',
tooltip: PIPELINE_STATE_MESSAGES.unknown.message,
tooltip: stateMessages.message,
}
}
}
@@ -145,7 +157,9 @@ const PipelineStatus = ({
return (
<>
{isLoading && <ShimmeringLoader></ShimmeringLoader>}
{isError && <AlertError error={error} subject="Failed to retrieve pipeline status" />}
{isError && (
<AlertError error={error} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE_STATUS} />
)}
{isSuccess && (
<>
{statusConfig.isFailedStatus ? (
@@ -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 = ({
<h3 className="text-xl font-semibold">Pipeline Status</h3>
</div>
</div>
<AlertError error={pipelineError} subject="Failed to retrieve pipeline details" />
<AlertError error={pipelineError} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE} />
</div>
)
}
@@ -305,7 +305,10 @@ const ReplicationPipelineStatus = ({
{(isPipelineLoading || isStatusLoading) && <GenericSkeletonLoader />}
{isStatusError && (
<AlertError error={statusError} subject="Failed to retrieve table replication status" />
<AlertError
error={statusError}
subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_REPLICATION_STATUS}
/>
)}
{/* Error alert */}
@@ -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 (
<div className="flex justify-end items-center space-x-2">
{isLoading && <ShimmeringLoader></ShimmeringLoader>}
{isError && <AlertError error={error} subject="Failed to retrieve pipeline status" />}
{isError && (
<AlertError error={error} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE_STATUS} />
)}
<DropdownMenu>
<DropdownMenuTrigger asChild>