BatchRestartDialog.tsx166 lines · main
1import { useParams } from 'common'
2import { useMemo } from 'react'
3import { toast } from 'sonner'
4import {
5 AlertDialog,
6 AlertDialogAction,
7 AlertDialogCancel,
8 AlertDialogContent,
9 AlertDialogDescription,
10 AlertDialogFooter,
11 AlertDialogHeader,
12 AlertDialogTitle,
13} from 'ui'
14
15import { PipelineStatusName } from './Replication.constants'
16import { ReplicationPipelineTableStatus } from '@/data/replication/pipeline-replication-status-query'
17import { useRollbackTablesMutation } from '@/data/replication/rollback-tables-mutation'
18
19interface 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
31export 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}