From 68192f5dfef569aed4546e938ee44d483fa18253 Mon Sep 17 00:00:00 2001 From: Riccardo Busetti Date: Wed, 23 Jul 2025 08:16:25 +0200 Subject: [PATCH] Fix --- .../Replication/ReplicationPipelineStatus.tsx | 212 ++++++++++++------ 1 file changed, 138 insertions(+), 74 deletions(-) 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.icon}
+
+
+

+ {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 && (