|
1 | 1 | import React, { useState } from 'react';
|
2 |
| -import { |
3 |
| - Button, |
4 |
| - ButtonGroup, |
5 |
| - Content, |
6 |
| - Dialog, |
7 |
| - DialogTrigger, |
8 |
| - Divider, |
9 |
| - Heading, |
10 |
| - Text |
11 |
| -} from '@adobe/react-spectrum'; |
12 |
| -import { toastRequest } from '../utils/api'; |
| 2 | +import { Button, Text } from '@adobe/react-spectrum'; |
13 | 3 | import Cancel from '@spectrum-icons/workflow/Cancel';
|
14 |
| -import Checkmark from '@spectrum-icons/workflow/Checkmark'; |
15 |
| -import {QueueOutput} from "../utils/api.types.ts"; |
| 4 | +import { ToastQueue } from '@react-spectrum/toast'; |
| 5 | +import { apiRequest } from '../utils/api'; |
| 6 | +import {Execution, ExecutionStatus, isExecutionPending, QueueOutput} from '../utils/api.types'; |
16 | 7 |
|
17 |
| -type ExecutionAbortButtonProps = { |
18 |
| - selectedKeys: string[]; |
19 |
| - onAbort?: () => void; |
20 |
| -}; |
21 |
| - |
22 |
| -const ExecutionAbortButton: React.FC<ExecutionAbortButtonProps> = ({ selectedKeys, onAbort }) => { |
23 |
| - const [abortDialogOpen, setAbortDialogOpen] = useState(false); |
24 |
| - const [isLoading, setIsLoading] = useState(false); |
| 8 | +const executionPollInterval = 1000; |
| 9 | +const toastTimeout = 3000; |
25 | 10 |
|
26 |
| - const handleConfirm = async () => { |
27 |
| - setIsLoading(true); |
28 |
| - const ids = Array.from(selectedKeys); |
| 11 | +interface ExecutionAbortButtonProps { |
| 12 | + execution: Execution | null; |
| 13 | + onComplete: (execution: Execution | null) => void; |
| 14 | +} |
29 | 15 |
|
30 |
| - const params = new URLSearchParams(); |
31 |
| - ids.forEach((id) => params.append('jobId', id)); |
| 16 | +const ExecutionAbortButton: React.FC<ExecutionAbortButtonProps> = ({ execution, onComplete }) => { |
| 17 | + const [isAborting, setIsAborting] = useState(false); |
32 | 18 |
|
| 19 | + const onAbort = async () => { |
| 20 | + if (!execution?.id) { |
| 21 | + console.warn('Code execution cannot be aborted as it is not running!'); |
| 22 | + return; |
| 23 | + } |
| 24 | + setIsAborting(true); |
33 | 25 | try {
|
34 |
| - await toastRequest<QueueOutput>({ |
35 |
| - method: 'DELETE', |
36 |
| - url: `/apps/acm/api/queue-code.json?${params.toString()}`, |
37 |
| - operation: 'Abort executions', |
| 26 | + await apiRequest<QueueOutput>({ |
| 27 | + operation: 'Code execution aborting', |
| 28 | + url: `/apps/acm/api/queue-code.json?jobId=${execution.id}`, |
| 29 | + method: 'delete', |
38 | 30 | });
|
39 |
| - if (onAbort) onAbort(); |
| 31 | + |
| 32 | + let queuedExecution: Execution | null = null; |
| 33 | + while (queuedExecution === null || isExecutionPending(queuedExecution.status)) { |
| 34 | + const response = await apiRequest<QueueOutput>({ |
| 35 | + operation: 'Code execution state', |
| 36 | + url: `/apps/acm/api/queue-code.json?jobId=${execution.id}`, |
| 37 | + method: 'get', |
| 38 | + }); |
| 39 | + queuedExecution = response.data.data.executions[0]!; |
| 40 | + onComplete(queuedExecution); |
| 41 | + await new Promise((resolve) => setTimeout(resolve, executionPollInterval)); |
| 42 | + } |
| 43 | + if (queuedExecution.status === ExecutionStatus.ABORTED) { |
| 44 | + ToastQueue.positive('Code execution aborted successfully!', { |
| 45 | + timeout: toastTimeout, |
| 46 | + }); |
| 47 | + } else { |
| 48 | + console.warn('Code execution aborting failed!'); |
| 49 | + ToastQueue.negative('Code execution aborting failed!', { |
| 50 | + timeout: toastTimeout, |
| 51 | + }); |
| 52 | + } |
40 | 53 | } catch (error) {
|
41 |
| - console.error('Abort executions error:', error); |
| 54 | + console.error('Code execution aborting error:', error); |
| 55 | + ToastQueue.negative('Code execution aborting failed!', { |
| 56 | + timeout: toastTimeout, |
| 57 | + }); |
42 | 58 | } finally {
|
43 |
| - setIsLoading(false); |
44 |
| - setAbortDialogOpen(false); |
| 59 | + setIsAborting(false); |
45 | 60 | }
|
46 | 61 | };
|
47 | 62 |
|
48 |
| - const renderAbortDialog = () => ( |
49 |
| - <> |
50 |
| - <Heading> |
51 |
| - <Text>Confirmation</Text> |
52 |
| - </Heading> |
53 |
| - <Divider /> |
54 |
| - <Content> |
55 |
| - <Text>Are you sure you want to abort the selected executions? This action cannot be undone.</Text> |
56 |
| - </Content> |
57 |
| - <ButtonGroup> |
58 |
| - <Button variant="secondary" onPress={() => setAbortDialogOpen(false)} isDisabled={isLoading}> |
59 |
| - <Cancel /> |
60 |
| - <Text>Cancel</Text> |
61 |
| - </Button> |
62 |
| - <Button variant="negative" style="fill" onPress={handleConfirm} isPending={isLoading}> |
63 |
| - <Checkmark /> |
64 |
| - <Text>Confirm</Text> |
65 |
| - </Button> |
66 |
| - </ButtonGroup> |
67 |
| - </> |
68 |
| - ); |
69 |
| - |
70 | 63 | return (
|
71 |
| - <DialogTrigger isOpen={abortDialogOpen} onOpenChange={setAbortDialogOpen}> |
72 |
| - <Button variant="negative" style="fill" isDisabled={selectedKeys.length === 0} onPress={() => setAbortDialogOpen(true)}> |
73 |
| - <Cancel /> |
74 |
| - <Text>Abort</Text> |
75 |
| - </Button> |
76 |
| - <Dialog>{renderAbortDialog()}</Dialog> |
77 |
| - </DialogTrigger> |
| 64 | + <Button variant="negative" isDisabled={!execution || !isExecutionPending(execution.status) || isAborting} onPress={onAbort}> |
| 65 | + <Cancel /> |
| 66 | + <Text>Abort</Text> |
| 67 | + </Button> |
78 | 68 | );
|
79 | 69 | };
|
80 | 70 |
|
|
0 commit comments