1616 * specific language governing permissions and limitations
1717 * under the License.
1818 */
19- import { Box , Heading , Text } from "@chakra-ui/react" ;
19+ import { Box , Button , Flex , Heading , Text , type ButtonProps } from "@chakra-ui/react" ;
20+ import { useQueryClient } from "@tanstack/react-query" ;
2021import type { ColumnDef } from "@tanstack/react-table" ;
22+ import { useCallback } from "react" ;
2123import { useTranslation } from "react-i18next" ;
24+ import { MdPause , MdPlayArrow , MdStop } from "react-icons/md" ;
2225import { useParams } from "react-router-dom" ;
2326
24- import { useBackfillServiceListBackfillsUi } from "openapi/queries" ;
27+ import {
28+ useBackfillServiceCancelBackfill ,
29+ useBackfillServiceListBackfillsUi ,
30+ useBackfillServiceListBackfillsUiKey ,
31+ useBackfillServicePauseBackfill ,
32+ useBackfillServiceUnpauseBackfill ,
33+ } from "openapi/queries" ;
2534import type { BackfillResponse } from "openapi/requests/types.gen" ;
2635import { DataTable } from "src/components/DataTable" ;
2736import { useTableURLState } from "src/components/DataTable/useTableUrlState" ;
2837import { ErrorAlert } from "src/components/ErrorAlert" ;
2938import Time from "src/components/Time" ;
30- import { getDuration } from "src/utils" ;
39+ import { getDuration , useAutoRefresh } from "src/utils" ;
3140
32- const getColumns = ( translate : ( key : string ) => string ) : Array < ColumnDef < BackfillResponse > > => [
41+ const buttonProps = {
42+ rounded : "full" ,
43+ size : "xs" ,
44+ variant : "outline" ,
45+ } satisfies ButtonProps ;
46+
47+ type GetColumnsParams = {
48+ readonly isCancelPending : boolean ;
49+ readonly isPausePending : boolean ;
50+ readonly isUnpausePending : boolean ;
51+ readonly onCancel : ( backfill : BackfillResponse ) => void ;
52+ readonly onPause : ( backfill : BackfillResponse ) => void ;
53+ readonly translate : ( key : string ) => string ;
54+ } ;
55+
56+ const getColumns = ( {
57+ isCancelPending,
58+ isPausePending,
59+ isUnpausePending,
60+ onCancel,
61+ onPause,
62+ translate,
63+ } : GetColumnsParams ) : Array < ColumnDef < BackfillResponse > > => [
3364 {
3465 accessorKey : "date_from" ,
3566 cell : ( { row } ) => (
@@ -101,6 +132,46 @@ const getColumns = (translate: (key: string) => string): Array<ColumnDef<Backfil
101132 enableSorting : false ,
102133 header : translate ( "table.maxActiveRuns" ) ,
103134 } ,
135+ {
136+ accessorKey : "actions" ,
137+ cell : ( { row } ) => {
138+ const backfill = row . original ;
139+
140+ if ( backfill . completed_at !== null ) {
141+ return null ;
142+ }
143+
144+ return (
145+ < Flex gap = { 2 } justifyContent = "end" >
146+ < Button
147+ aria-label = {
148+ backfill . is_paused
149+ ? translate ( "components:banner.unpause" )
150+ : translate ( "components:banner.pause" )
151+ }
152+ loading = { isPausePending || isUnpausePending }
153+ onClick = { ( ) => onPause ( backfill ) }
154+ { ...buttonProps }
155+ >
156+ { backfill . is_paused ? < MdPlayArrow /> : < MdPause /> }
157+ </ Button >
158+ < Button
159+ aria-label = { translate ( "components:banner.cancel" ) }
160+ loading = { isCancelPending }
161+ onClick = { ( ) => onCancel ( backfill ) }
162+ { ...buttonProps }
163+ >
164+ < MdStop />
165+ </ Button >
166+ </ Flex >
167+ ) ;
168+ } ,
169+ enableSorting : false ,
170+ header : "" ,
171+ meta : {
172+ skeletonWidth : 10 ,
173+ } ,
174+ } ,
104175] ;
105176
106177export const Backfills = ( ) => {
@@ -110,14 +181,64 @@ export const Backfills = () => {
110181 const { pagination } = tableURLState ;
111182
112183 const { dagId = "" } = useParams ( ) ;
184+ const refetchInterval = useAutoRefresh ( { dagId } ) ;
113185
114- const { data, error, isFetching, isLoading } = useBackfillServiceListBackfillsUi ( {
115- dagId,
116- limit : pagination . pageSize ,
117- offset : pagination . pageIndex * pagination . pageSize ,
186+ const { data, error, isFetching, isLoading } = useBackfillServiceListBackfillsUi (
187+ {
188+ dagId,
189+ limit : pagination . pageSize ,
190+ offset : pagination . pageIndex * pagination . pageSize ,
191+ } ,
192+ undefined ,
193+ {
194+ refetchInterval : ( query ) =>
195+ query . state . data ?. backfills . some ( ( bf : BackfillResponse ) => bf . completed_at === null && ! bf . is_paused )
196+ ? refetchInterval
197+ : false ,
198+ } ,
199+ ) ;
200+
201+ const queryClient = useQueryClient ( ) ;
202+ const onSuccess = async ( ) => {
203+ await queryClient . invalidateQueries ( {
204+ queryKey : [ useBackfillServiceListBackfillsUiKey ] ,
205+ } ) ;
206+ } ;
207+
208+ const { isPending : isPausePending , mutate : pauseMutate } = useBackfillServicePauseBackfill ( { onSuccess } ) ;
209+ const { isPending : isUnpausePending , mutate : unpauseMutate } = useBackfillServiceUnpauseBackfill ( {
210+ onSuccess,
118211 } ) ;
212+ const { isPending : isCancelPending , mutate : cancelMutate } = useBackfillServiceCancelBackfill ( {
213+ onSuccess,
214+ } ) ;
215+
216+ const handlePause = useCallback (
217+ ( backfill : BackfillResponse ) => {
218+ if ( backfill . is_paused ) {
219+ unpauseMutate ( { backfillId : backfill . id } ) ;
220+ } else {
221+ pauseMutate ( { backfillId : backfill . id } ) ;
222+ }
223+ } ,
224+ [ pauseMutate , unpauseMutate ] ,
225+ ) ;
226+
227+ const handleCancel = useCallback (
228+ ( backfill : BackfillResponse ) => {
229+ cancelMutate ( { backfillId : backfill . id } ) ;
230+ } ,
231+ [ cancelMutate ] ,
232+ ) ;
119233
120- const columns = getColumns ( translate ) ;
234+ const columns = getColumns ( {
235+ isCancelPending,
236+ isPausePending,
237+ isUnpausePending,
238+ onCancel : handleCancel ,
239+ onPause : handlePause ,
240+ translate,
241+ } ) ;
121242
122243 return (
123244 < Box >
0 commit comments