import { useParams } from 'common' import { useMemo } from 'react' import { toast } from 'sonner' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from 'ui' import { PipelineStatusName } from './Replication.constants' import { RestartCostEstimate } from './RestartCostEstimate' import { getTableCopyTargets, type TableSyncCopyConfig } from './TableSyncCopy.utils' import { ReplicationPipelineTableStatus } from '@/data/replication/pipeline-replication-status-query' import { useRollbackTablesMutation } from '@/data/replication/rollback-tables-mutation' interface BatchRestartDialogProps { open: boolean onOpenChange: (open: boolean) => void mode: 'all' | 'errored' tables: ReplicationPipelineTableStatus[] sourceId?: number publicationName?: string tableSyncCopy?: TableSyncCopyConfig pipelineStatusName?: PipelineStatusName onRestartStart?: (tableIds: number[]) => void onRestartComplete?: (tableIds: number[]) => void } export const BatchRestartDialog = ({ open, onOpenChange, mode, tables, sourceId, publicationName, tableSyncCopy, pipelineStatusName, onRestartStart, onRestartComplete, }: BatchRestartDialogProps) => { const { ref: projectRef, pipelineId: _pipelineId } = useParams() const pipelineId = Number(_pipelineId) const affectedTables = useMemo(() => { if (mode === 'all') { return tables } else { return tables.filter((table) => table.state.name === 'error') } }, [mode, tables]) const affectedTableIds = useMemo(() => affectedTables.map((table) => table.id), [affectedTables]) const copiedTables = useMemo( () => getTableCopyTargets(affectedTables, tableSyncCopy), [affectedTables, tableSyncCopy] ) const initialSyncDescription = copiedTables.length === 0 ? (
  • No table will run an initial sync. Replication will resume with new changes only, without syncing existing source rows. There is no additional initial sync charge.
  • ) : copiedTables.length === affectedTables.length ? (
  • {copiedTables.length === 1 ? 'The table will run its initial sync again.' : `All ${copiedTables.length} tables will run initial sync again.`} {' '} Existing source rows will be synced again. Data successfully processed during this initial sync is billed again.
  • ) : (
  • {copiedTables.length} of {affectedTables.length} tables will run initial sync again. {' '} Existing source rows for those tables will be synced again and billed again. The remaining tables will resume replication with new changes only.
  • ) const { mutateAsync: rollbackTables, isPending: isResetting } = useRollbackTablesMutation({ onSuccess: (data) => { const count = data.tables.length toast.success( `Restarting replication for ${count} table${count > 1 ? 's' : ''}. Pipeline will restart automatically.` ) }, onSettled: () => { onRestartComplete?.(affectedTableIds) onOpenChange(false) }, onError: (error) => { toast.error(`Failed to restart replication: ${error.message}`) }, }) const handleReset = async () => { if (!projectRef) return toast.error('Project ref is required') onRestartStart?.(affectedTableIds) try { await rollbackTables({ projectRef, pipelineId, target: mode === 'all' ? { type: 'all_tables' } : { type: 'all_errored_tables' }, rollbackType: 'full', pipelineStatusName, }) } catch (error) {} } const dialogContent = mode === 'all' ? { title: 'Restart all tables', description: (

    This will restart replication for all {affectedTables.length} table {affectedTables.length === 1 ? '' : 's'} in this pipeline from scratch:

    ), action: 'Restart all tables', } : { title: 'Restart failed tables', description: (

    This will restart replication for all{' '} {affectedTables.length} currently failed tables from scratch:

    ), action: 'Restart failed tables', } return ( {dialogContent.title} {dialogContent.description} Cancel {isResetting ? 'Restarting replication...' : dialogContent.action} ) }