This commit is contained in:
Riccardo Busetti committed 2025-07-22 15:44:46 +02:00
1 parent cacf7f93b4
commit 07dbaa5b50
4 files changed
+86 -65

No files matched your search

@@ -8,12 +8,13 @@ import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
import RowMenu from './RowMenu'
import PipelineStatus, { PipelineStatusName } from './PipelineStatus'
import { PipelineStatusRequestStatus, usePipelineRequestStatus } from './PipelineRequestStatusContext'
import { getStatusName } from './Pipeline.utils'
import { useParams } from 'common'
import {
ReplicationPipelineStatusData,
useReplicationPipelineStatusQuery,
} from 'data/replication/pipeline-status-query'
import { useState } from 'react'
import { useState, useEffect } from 'react'
import { toast } from 'sonner'
import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation'
import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation'
@@ -67,31 +68,19 @@ const DestinationRow = ({
},
{ refetchInterval: refreshFrequencyMs }
)
const { getRequestStatus, setRequestStatus: setGlobalRequestStatus } = usePipelineRequestStatus()
const { getRequestStatus, setRequestStatus: setGlobalRequestStatus, updatePipelineStatus } = usePipelineRequestStatus()
const requestStatus = pipeline?.id ? getRequestStatus(pipeline.id) : PipelineStatusRequestStatus.None
const { mutateAsync: startPipeline } = useStartPipelineMutation()
const { mutateAsync: stopPipeline } = useStopPipelineMutation()
const pipelineStatus = pipelineStatusData?.status
const getStatusName = (
status: ReplicationPipelineStatusData['status'] | undefined
): string | undefined => {
if (status && typeof status === 'object' && 'name' in status) {
return status.name
}
return undefined
}
const statusName = getStatusName(pipelineStatus)
if (
pipeline?.id &&
((requestStatus === PipelineStatusRequestStatus.EnableRequested &&
(statusName === PipelineStatusName.STARTED || statusName === PipelineStatusName.FAILED)) ||
(requestStatus === PipelineStatusRequestStatus.DisableRequested &&
(statusName === PipelineStatusName.STOPPED || statusName === PipelineStatusName.FAILED)))
) {
setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.None)
}
// Update request status based on backend status using centralized logic
useEffect(() => {
if (pipeline?.id) {
updatePipelineStatus(pipeline.id, statusName)
}
}, [pipeline?.id, statusName, updatePipelineStatus])
const onEnableClick = async () => {
if (!projectRef) {
@@ -0,0 +1,10 @@
import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query'
export const getStatusName = (
status: ReplicationPipelineStatusData['status'] | undefined
): string | undefined => {
if (status && typeof status === 'object' && 'name' in status) {
return status.name
}
return undefined
}
@@ -1,4 +1,4 @@
import { createContext, useContext, useState, ReactNode } from 'react'
import { createContext, useContext, useState, ReactNode, useEffect } from 'react'
export enum PipelineStatusRequestStatus {
None = 'None',
@@ -10,6 +10,7 @@ interface PipelineRequestStatusContextType {
requestStatus: Record<number, PipelineStatusRequestStatus>
setRequestStatus: (pipelineId: number, status: PipelineStatusRequestStatus) => void
getRequestStatus: (pipelineId: number) => PipelineStatusRequestStatus
updatePipelineStatus: (pipelineId: number, backendStatus: string | undefined) => void
}
const PipelineRequestStatusContext = createContext<PipelineRequestStatusContextType | undefined>(undefined)
@@ -32,12 +33,27 @@ export const PipelineRequestStatusProvider = ({ children }: PipelineRequestStatu
return requestStatus[pipelineId] || PipelineStatusRequestStatus.None
}
// Centralized logic to update request status based on backend status
const updatePipelineStatus = (pipelineId: number, backendStatus: string | undefined) => {
const currentRequestStatus = getRequestStatus(pipelineId)
if (
(currentRequestStatus === PipelineStatusRequestStatus.EnableRequested &&
(backendStatus === 'started' || backendStatus === 'failed')) ||
(currentRequestStatus === PipelineStatusRequestStatus.DisableRequested &&
(backendStatus === 'stopped' || backendStatus === 'failed'))
) {
setRequestStatus(pipelineId, PipelineStatusRequestStatus.None)
}
}
return (
<PipelineRequestStatusContext.Provider
value={{
requestStatus,
setRequestStatus,
getRequestStatus,
updatePipelineStatus,
}}
>
{children}
@@ -4,9 +4,10 @@ import AlertError from 'components/ui/AlertError'
import { useReplicationPipelineReplicationStatusQuery } from 'data/replication/pipeline-replication-status-query'
import { useReplicationPipelineByIdQuery } from 'data/replication/pipeline-by-id-query'
import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query'
import PipelineStatus, { PipelineStatusName } from './PipelineStatus'
import { PipelineStatusRequestStatus, usePipelineRequestStatus } from './PipelineRequestStatusContext'
import { useState } from 'react'
import PipelineStatus from './PipelineStatus'
import { usePipelineRequestStatus } from './PipelineRequestStatusContext'
import { getStatusName } from './Pipeline.utils'
import { useState, useEffect } from 'react'
import {
Button,
Badge,
@@ -53,7 +54,7 @@ const ReplicationPipelineStatus = ({
}: ReplicationPipelineStatusProps) => {
const { ref: projectRef } = useParams()
const [filterString, setFilterString] = useState<string>('')
const { getRequestStatus, setRequestStatus } = usePipelineRequestStatus()
const { getRequestStatus, updatePipelineStatus } = usePipelineRequestStatus()
const requestStatus = getRequestStatus(pipelineId)
const {
@@ -93,25 +94,12 @@ const ReplicationPipelineStatus = ({
}
)
// Reset request status when backend status changes appropriately
const getStatusName = (
status: typeof pipelineStatusData.status | undefined
): string | undefined => {
if (status && typeof status === 'object' && 'name' in status) {
return status.name
}
return undefined
}
// Update request status based on backend status using centralized logic
const statusName = getStatusName(pipelineStatusData?.status)
if (
(requestStatus === PipelineStatusRequestStatus.EnableRequested &&
(statusName === PipelineStatusName.STARTED || statusName === PipelineStatusName.FAILED)) ||
(requestStatus === PipelineStatusRequestStatus.DisableRequested &&
(statusName === PipelineStatusName.STOPPED || statusName === PipelineStatusName.FAILED))
) {
setRequestStatus(pipelineId, PipelineStatusRequestStatus.None)
}
useEffect(() => {
updatePipelineStatus(pipelineId, statusName)
}, [pipelineId, statusName, updatePipelineStatus])
const handleCopyTableStatus = async (tableName: string, state: TableState['state']) => {
@@ -177,6 +165,33 @@ 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 getDisabledStateMessage = (status: string | undefined) => {
switch (status) {
case 'failed': return 'Replication has failed. Check the logs for details. Table status is disabled.'
case 'stopped': return 'Replication is paused. Enable the pipeline to resume data synchronization.'
case 'starting': return 'Pipeline is initializing. Table status will be available once started.'
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'
}
}
const tableStatuses = replicationStatusData?.table_statuses || []
const filteredTableStatuses = filterString.length === 0
? tableStatuses
@@ -186,8 +201,8 @@ const ReplicationPipelineStatus = ({
const errorTables = tableStatuses.filter((table: TableState) => table.state.name === 'error')
const hasErrors = errorTables.length > 0
const isPipelineRunning = pipelineStatusData?.status?.name === 'started'
const isPipelineNotRunning = !isPipelineRunning // Any state other than 'started' is not running
const isPipelineRunning = statusName === 'started'
const hasTableData = tableStatuses.length > 0 // Any state other than 'started' is not running
@@ -289,7 +304,7 @@ const ReplicationPipelineStatus = ({
)}
{/* Pipeline not running state - generic disabled state */}
{isPipelineNotRunning && tableStatuses.length > 0 && (
{!isPipelineRunning && hasTableData && (
<div className="space-y-4">
<div className="p-6 border border-border-muted bg-surface-75 rounded-lg">
<div className="text-center space-y-3">
@@ -298,16 +313,10 @@ const ReplicationPipelineStatus = ({
</div>
<div className="space-y-2">
<h4 className="text-base font-medium text-foreground-light">
{pipelineStatusData?.status?.name === 'failed' ? 'Pipeline Failed' :
pipelineStatusData?.status?.name === 'stopped' ? 'Pipeline Stopped' :
pipelineStatusData?.status?.name === 'starting' ? 'Pipeline Starting' :
'Pipeline Not Running'}
{getDisabledStateTitle(statusName)}
</h4>
<p className="text-sm text-foreground-light max-w-md mx-auto">
{pipelineStatusData?.status?.name === 'failed' ? 'Replication has failed. Check the logs for details. Table status is disabled.' :
pipelineStatusData?.status?.name === 'stopped' ? 'Replication is paused. Enable the pipeline to resume data synchronization.' :
pipelineStatusData?.status?.name === 'starting' ? 'Pipeline is initializing. Table status will be available once started.' :
'Pipeline is not actively running. Table status information is disabled.'}
{getDisabledStateMessage(statusName)}
</p>
</div>
</div>
@@ -334,15 +343,12 @@ const ReplicationPipelineStatus = ({
</Table.td>
<Table.td>
<Badge variant="secondary" className="text-foreground-lighter bg-surface-100 border-border-muted">
{pipelineStatusData?.status?.name === 'failed' ? 'Failed' :
pipelineStatusData?.status?.name === 'stopped' ? 'Stopped' :
pipelineStatusData?.status?.name === 'starting' ? 'Starting' :
'Disabled'}
{getDisabledStateBadge(statusName)}
</Badge>
</Table.td>
<Table.td>
<div className="text-sm text-foreground-lighter">
Status unavailable while pipeline is {pipelineStatusData?.status?.name || 'not running'}
Status unavailable while pipeline is {statusName || 'not running'}
</div>
</Table.td>
<Table.td className="text-center">
@@ -372,7 +378,7 @@ const ReplicationPipelineStatus = ({
)}
{/* Tables list - active/running pipeline */}
{isPipelineRunning && !isStatusLoading && tableStatuses.length > 0 && (
{isPipelineRunning && !isStatusLoading && hasTableData && (
<div className="w-full overflow-hidden overflow-x-auto">
<Table
head={[
@@ -435,14 +441,14 @@ const ReplicationPipelineStatus = ({
{/* No results - running pipeline */}
{isPipelineRunning && !isStatusLoading && filteredTableStatuses.length === 0 && tableStatuses.length > 0 && (
{isPipelineRunning && !isStatusLoading && filteredTableStatuses.length === 0 && hasTableData && (
<div className="text-center py-8 text-foreground-light">
<p>No tables match "{filterString}"</p>
</div>
)}
{/* No results - not running pipeline */}
{isPipelineNotRunning && !isStatusLoading && filteredTableStatuses.length === 0 && tableStatuses.length > 0 && (
{!isPipelineRunning && !isStatusLoading && filteredTableStatuses.length === 0 && hasTableData && (
<div className="text-center py-8 text-foreground-light">
<p>No tables match "{filterString}"</p>
</div>
@@ -470,7 +476,7 @@ const ReplicationPipelineStatus = ({
)}
{/* Empty state - not running pipeline */}
{isPipelineNotRunning && !isStatusLoading && tableStatuses.length === 0 && (
{!isPipelineRunning && !isStatusLoading && tableStatuses.length === 0 && (
<div className="flex flex-col items-center justify-center py-16 px-4">
<div className="w-full max-w-sm mx-auto text-center space-y-4">
<div className="w-16 h-16 bg-surface-200 rounded-full flex items-center justify-center mx-auto">
@@ -479,7 +485,7 @@ const ReplicationPipelineStatus = ({
<div className="space-y-2">
<h4 className="text-lg font-semibold text-foreground-light">Pipeline Not Running</h4>
<p className="text-sm text-foreground-light leading-relaxed">
The replication pipeline is currently {pipelineStatusData?.status?.name || 'not active'}.
The replication pipeline is currently {statusName || 'not active'}.
Table status information is not available while the pipeline is in this state.
</p>
</div>