Skip to content

Commit 04a71b1

Browse files
[ML] Improving model snapshot revert UI experience (#88588)
* [ML] Improving model snapshot revert UI experience * removing button disabling * updating component is mounted check Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent 8df4d67 commit 04a71b1

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { FC, useEffect, useCallback, useState } from 'react';
7+
import React, { FC, useEffect, useCallback, useState, useRef } from 'react';
88
import { i18n } from '@kbn/i18n';
99

1010
import {
@@ -50,15 +50,24 @@ export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => {
5050
const [closeJobModalVisible, setCloseJobModalVisible] = useState<ModelSnapshot | null>(null);
5151
const [combinedJobState, setCombinedJobState] = useState<COMBINED_JOB_STATE | null>(null);
5252

53+
const isMounted = useRef(true);
5354
useEffect(() => {
5455
loadModelSnapshots();
56+
return () => {
57+
isMounted.current = false;
58+
};
5559
}, []);
5660

57-
const loadModelSnapshots = useCallback(async () => {
61+
async function loadModelSnapshots() {
62+
if (isMounted.current === false) {
63+
// table refresh can be triggered a while after a snapshot revert has been triggered.
64+
// ensure the table is still visible before attempted to refresh it.
65+
return;
66+
}
5867
const { model_snapshots: ms } = await ml.getModelSnapshots(job.job_id);
5968
setSnapshots(ms);
6069
setSnapshotsLoaded(true);
61-
}, [job]);
70+
}
6271

6372
const checkJobIsClosed = useCallback(
6473
async (snapshot: ModelSnapshot) => {
@@ -107,13 +116,14 @@ export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => {
107116
}
108117
}, []);
109118

110-
const closeRevertFlyout = useCallback((reload: boolean) => {
119+
const closeRevertFlyout = useCallback(() => {
111120
setRevertSnapshot(null);
112-
if (reload) {
113-
loadModelSnapshots();
114-
// wait half a second before refreshing the jobs list
115-
setTimeout(refreshJobList, 500);
116-
}
121+
}, []);
122+
123+
const refresh = useCallback(() => {
124+
loadModelSnapshots();
125+
// wait half a second before refreshing the jobs list
126+
setTimeout(refreshJobList, 500);
117127
}, []);
118128

119129
const columns: Array<EuiBasicTableColumn<any>> = [
@@ -231,6 +241,7 @@ export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => {
231241
snapshots={snapshots}
232242
job={job}
233243
closeFlyout={closeRevertFlyout}
244+
refresh={refresh}
234245
/>
235246
)}
236247

x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,17 @@ interface Props {
5353
snapshot: ModelSnapshot;
5454
snapshots: ModelSnapshot[];
5555
job: CombinedJobWithStats;
56-
closeFlyout(reload: boolean): void;
56+
closeFlyout(): void;
57+
refresh(): void;
5758
}
5859

59-
export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job, closeFlyout }) => {
60+
export const RevertModelSnapshotFlyout: FC<Props> = ({
61+
snapshot,
62+
snapshots,
63+
job,
64+
closeFlyout,
65+
refresh,
66+
}) => {
6067
const { toasts } = useNotifications();
6168
const { loadAnomalyDataForJob, loadEventRateForJob } = useMemo(
6269
() => chartLoaderProvider(mlResultsService),
@@ -73,7 +80,6 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
7380
const [eventRateData, setEventRateData] = useState<LineChartPoint[]>([]);
7481
const [anomalies, setAnomalies] = useState<Anomaly[]>([]);
7582
const [chartReady, setChartReady] = useState(false);
76-
const [applying, setApplying] = useState(false);
7783

7884
useEffect(() => {
7985
createChartData();
@@ -110,13 +116,6 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
110116
setChartReady(true);
111117
}, [job]);
112118

113-
function closeWithReload() {
114-
closeFlyout(true);
115-
}
116-
function closeWithoutReload() {
117-
closeFlyout(false);
118-
}
119-
120119
function showRevertModal() {
121120
setRevertModalVisible(true);
122121
}
@@ -125,7 +124,6 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
125124
}
126125

127126
async function applyRevert() {
128-
setApplying(true);
129127
const end =
130128
replay && runInRealTime === false ? job.data_counts.latest_record_timestamp : undefined;
131129
try {
@@ -138,17 +136,19 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
138136
}))
139137
: undefined;
140138

141-
await ml.jobs.revertModelSnapshot(
142-
job.job_id,
143-
currentSnapshot.snapshot_id,
144-
replay,
145-
end,
146-
events
147-
);
139+
ml.jobs
140+
.revertModelSnapshot(job.job_id, currentSnapshot.snapshot_id, replay, end, events)
141+
.then(() => {
142+
toasts.addSuccess(
143+
i18n.translate('xpack.ml.revertModelSnapshotFlyout.revertSuccessTitle', {
144+
defaultMessage: 'Model snapshot revert successful',
145+
})
146+
);
147+
refresh();
148+
});
148149
hideRevertModal();
149-
closeWithReload();
150+
closeFlyout();
150151
} catch (error) {
151-
setApplying(false);
152152
toasts.addError(new Error(error.body.message), {
153153
title: i18n.translate('xpack.ml.revertModelSnapshotFlyout.revertErrorTitle', {
154154
defaultMessage: 'Model snapshot revert failed',
@@ -166,7 +166,7 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
166166

167167
return (
168168
<>
169-
<EuiFlyout onClose={closeWithoutReload} hideCloseButton size="m">
169+
<EuiFlyout onClose={closeFlyout} hideCloseButton size="m">
170170
<EuiFlyoutHeader hasBorder>
171171
<EuiTitle size="s">
172172
<h5>
@@ -347,7 +347,7 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
347347
<EuiFlyoutFooter>
348348
<EuiFlexGroup justifyContent="spaceBetween">
349349
<EuiFlexItem grow={false}>
350-
<EuiButtonEmpty iconType="cross" onClick={closeWithoutReload} flush="left">
350+
<EuiButtonEmpty iconType="cross" onClick={closeFlyout} flush="left">
351351
<FormattedMessage
352352
id="xpack.ml.newJob.wizard.revertModelSnapshotFlyout.closeButton"
353353
defaultMessage="Close"
@@ -392,10 +392,14 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job,
392392
defaultMessage: 'Apply',
393393
}
394394
)}
395-
confirmButtonDisabled={applying}
396395
buttonColor="danger"
397396
defaultFocusedButton="confirm"
398-
/>
397+
>
398+
<FormattedMessage
399+
id="xpack.ml.newJob.wizard.revertModelSnapshotFlyout.modalBody"
400+
defaultMessage="The snapshot revert will be carried out in the background and may take some time."
401+
/>
402+
</EuiConfirmModal>
399403
</EuiOverlayMask>
400404
)}
401405
</>

0 commit comments

Comments
 (0)