BatchRestartDialog.tsx166 lines · main
| 1 | import { useParams } from 'common' |
| 2 | import { useMemo } from 'react' |
| 3 | import { toast } from 'sonner' |
| 4 | import { |
| 5 | AlertDialog, |
| 6 | AlertDialogAction, |
| 7 | AlertDialogCancel, |
| 8 | AlertDialogContent, |
| 9 | AlertDialogDescription, |
| 10 | AlertDialogFooter, |
| 11 | AlertDialogHeader, |
| 12 | AlertDialogTitle, |
| 13 | } from 'ui' |
| 14 | |
| 15 | import { PipelineStatusName } from './Replication.constants' |
| 16 | import { ReplicationPipelineTableStatus } from '@/data/replication/pipeline-replication-status-query' |
| 17 | import { useRollbackTablesMutation } from '@/data/replication/rollback-tables-mutation' |
| 18 | |
| 19 | interface BatchRestartDialogProps { |
| 20 | open: boolean |
| 21 | onOpenChange: (open: boolean) => void |
| 22 | mode: 'all' | 'errored' |
| 23 | totalTables: number |
| 24 | erroredTablesCount: number |
| 25 | tables: ReplicationPipelineTableStatus[] |
| 26 | pipelineStatusName?: PipelineStatusName |
| 27 | onRestartStart?: (tableIds: number[]) => void |
| 28 | onRestartComplete?: (tableIds: number[]) => void |
| 29 | } |
| 30 | |
| 31 | export const BatchRestartDialog = ({ |
| 32 | open, |
| 33 | onOpenChange, |
| 34 | mode, |
| 35 | totalTables, |
| 36 | erroredTablesCount, |
| 37 | tables, |
| 38 | pipelineStatusName, |
| 39 | onRestartStart, |
| 40 | onRestartComplete, |
| 41 | }: BatchRestartDialogProps) => { |
| 42 | const { ref: projectRef, pipelineId: _pipelineId } = useParams() |
| 43 | const pipelineId = Number(_pipelineId) |
| 44 | // Calculate which table IDs will be restarted based on mode (memoized) |
| 45 | const affectedTableIds = useMemo(() => { |
| 46 | if (mode === 'all') { |
| 47 | return tables.map((t) => t.table_id) |
| 48 | } else { |
| 49 | return tables |
| 50 | .filter( |
| 51 | (t) => |
| 52 | t.state.name === 'error' && |
| 53 | 'retry_policy' in t.state && |
| 54 | t.state.retry_policy?.policy === 'manual_retry' |
| 55 | ) |
| 56 | .map((t) => t.table_id) |
| 57 | } |
| 58 | }, [mode, tables]) |
| 59 | |
| 60 | const { mutate: rollbackTables, isPending: isResetting } = useRollbackTablesMutation({ |
| 61 | onSuccess: (data) => { |
| 62 | const count = data.tables.length |
| 63 | toast.success( |
| 64 | `Restarting replication for ${count} table${count > 1 ? 's' : ''}. Pipeline will restart automatically.` |
| 65 | ) |
| 66 | }, |
| 67 | onSettled: () => { |
| 68 | onRestartComplete?.(affectedTableIds) |
| 69 | onOpenChange(false) |
| 70 | }, |
| 71 | onError: (error) => { |
| 72 | toast.error(`Failed to restart replication: ${error.message}`) |
| 73 | }, |
| 74 | }) |
| 75 | |
| 76 | const handleReset = () => { |
| 77 | if (!projectRef) return toast.error('Project ref is required') |
| 78 | |
| 79 | onRestartStart?.(affectedTableIds) |
| 80 | |
| 81 | rollbackTables({ |
| 82 | projectRef, |
| 83 | pipelineId, |
| 84 | target: mode === 'all' ? { type: 'all_tables' } : { type: 'all_errored_tables' }, |
| 85 | rollbackType: 'full', |
| 86 | pipelineStatusName, |
| 87 | }) |
| 88 | } |
| 89 | |
| 90 | const dialogContent = |
| 91 | mode === 'all' |
| 92 | ? { |
| 93 | title: 'Restart all tables', |
| 94 | description: ( |
| 95 | <div className="space-y-3 text-sm"> |
| 96 | <p> |
| 97 | This will restart replication for all |
| 98 | {totalTables === 0 ? '' : totalTables} table{totalTables > 1 ? 's' : ''} in this |
| 99 | pipeline from scratch: |
| 100 | </p> |
| 101 | <ul className="list-disc list-inside space-y-1.5 pl-2"> |
| 102 | <li> |
| 103 | <strong>All table copies will be re-initialized.</strong> Every table will be |
| 104 | copied again from the source. |
| 105 | </li> |
| 106 | <li> |
| 107 | <strong>All downstream data will be deleted.</strong> All replicated data will be |
| 108 | removed. |
| 109 | </li> |
| 110 | <li> |
| 111 | <strong>The pipeline will restart automatically.</strong> This is required to |
| 112 | apply this change. |
| 113 | </li> |
| 114 | </ul> |
| 115 | </div> |
| 116 | ), |
| 117 | action: 'Restart all tables', |
| 118 | } |
| 119 | : { |
| 120 | title: 'Restart failed tables', |
| 121 | description: ( |
| 122 | <div className="space-y-3 text-sm"> |
| 123 | <p> |
| 124 | This will restart replication for{' '} |
| 125 | <strong>all {erroredTablesCount} failed tables</strong> from scratch: |
| 126 | </p> |
| 127 | <ul className="list-disc list-inside space-y-1.5 pl-2"> |
| 128 | <li> |
| 129 | <strong>Failed table copies will be re-initialized.</strong> These tables will be |
| 130 | copied again from the source. |
| 131 | </li> |
| 132 | <li> |
| 133 | <strong>Existing downstream data will be deleted.</strong> Replicated data for |
| 134 | these tables will be removed. |
| 135 | </li> |
| 136 | <li> |
| 137 | <strong>All other tables remain untouched.</strong> Only failed tables are |
| 138 | affected. |
| 139 | </li> |
| 140 | <li> |
| 141 | <strong>The pipeline will restart automatically.</strong> This is required to |
| 142 | apply this change. |
| 143 | </li> |
| 144 | </ul> |
| 145 | </div> |
| 146 | ), |
| 147 | action: 'Restart failed tables', |
| 148 | } |
| 149 | |
| 150 | return ( |
| 151 | <AlertDialog open={open} onOpenChange={onOpenChange}> |
| 152 | <AlertDialogContent> |
| 153 | <AlertDialogHeader> |
| 154 | <AlertDialogTitle>{dialogContent.title}</AlertDialogTitle> |
| 155 | <AlertDialogDescription asChild>{dialogContent.description}</AlertDialogDescription> |
| 156 | </AlertDialogHeader> |
| 157 | <AlertDialogFooter> |
| 158 | <AlertDialogCancel disabled={isResetting}>Cancel</AlertDialogCancel> |
| 159 | <AlertDialogAction disabled={isResetting} onClick={handleReset} variant="warning"> |
| 160 | {isResetting ? 'Restarting replication...' : dialogContent.action} |
| 161 | </AlertDialogAction> |
| 162 | </AlertDialogFooter> |
| 163 | </AlertDialogContent> |
| 164 | </AlertDialog> |
| 165 | ) |
| 166 | } |