mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 01:18:42 +00:00
UI: Warn before saving script contents (#29026)
## For #28699 auxiliary feature  - [x] Changes file added for user-visible changes in `changes/` - [x] A detailed QA plan exists on the associated ticket (if it isn't there, work with the product group's QA engineer to add it) - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
parent
a7b1e6358b
commit
262832bfff
4 changed files with 93 additions and 10 deletions
1
changes/warn-on-save-script
Normal file
1
changes/warn-on-save-script
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Warn users of consequences when updating script contents
|
||||
|
|
@ -6,6 +6,7 @@ import Icon from "components/Icon/Icon";
|
|||
const baseClass = "modal";
|
||||
|
||||
type ModalWidth = "medium" | "large" | "xlarge" | "auto";
|
||||
// 650px 800px 850px auto
|
||||
|
||||
export interface IModalProps {
|
||||
title: string | JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React, { useContext, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
|
||||
import classnames from "classnames";
|
||||
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { AppContext } from "context/app";
|
||||
import { getPathWithQueryParams } from "utilities/url";
|
||||
|
|
@ -21,6 +23,52 @@ import { getErrorMessage } from "../ScriptUploader/helpers";
|
|||
|
||||
const baseClass = "edit-script-modal";
|
||||
|
||||
interface IWarningModal {
|
||||
onExit: () => void;
|
||||
onSave: () => void;
|
||||
scriptName: string;
|
||||
isSubmitting: boolean;
|
||||
}
|
||||
const WarningModal = ({
|
||||
onExit,
|
||||
onSave,
|
||||
scriptName,
|
||||
isSubmitting,
|
||||
}: IWarningModal) => {
|
||||
return (
|
||||
<Modal
|
||||
className={`${baseClass}__warning`}
|
||||
title="Save changes?"
|
||||
onExit={onExit}
|
||||
>
|
||||
<>
|
||||
<p>
|
||||
The changes you are making will cancel any pending script runs for{" "}
|
||||
<b>{scriptName}</b>.<br />
|
||||
<br />
|
||||
If this script is currently running on a host, it will complete, but
|
||||
results won't appear in Fleet. <br />
|
||||
<br />
|
||||
You cannot undo this action.
|
||||
</p>
|
||||
<div className="modal-cta-wrap">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={onSave}
|
||||
className="save-loading"
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button onClick={onExit} variant="inverse">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
interface IEditScriptModal {
|
||||
onExit: () => void;
|
||||
scriptId: number;
|
||||
|
|
@ -47,7 +95,10 @@ const EditScriptModal = ({
|
|||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formError, setFormError] = useState<string | null>(null);
|
||||
|
||||
const [showConfirmChanges, setShowConfirmChanges] = useState(false);
|
||||
|
||||
const {
|
||||
data: curScriptContent,
|
||||
error: isSelectedScriptContentError,
|
||||
isLoading: isLoadingSelectedScriptContent,
|
||||
} = useQuery<ScriptContent, Error>(
|
||||
|
|
@ -55,8 +106,8 @@ const EditScriptModal = ({
|
|||
() => scriptAPI.downloadScript(scriptId),
|
||||
{
|
||||
...DEFAULT_USE_QUERY_OPTIONS,
|
||||
onSuccess: (scriptContent) => {
|
||||
setScriptFormData(scriptContent);
|
||||
onSuccess: (curScriptContent_) => {
|
||||
setScriptFormData(curScriptContent_);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -79,6 +130,12 @@ const EditScriptModal = ({
|
|||
if (err || isSubmitting) {
|
||||
return;
|
||||
}
|
||||
// if contents have changed and not already showing the warning modal
|
||||
if (curScriptContent !== scriptFormData && !showConfirmChanges) {
|
||||
setShowConfirmChanges(true);
|
||||
return;
|
||||
}
|
||||
// show warning modal and abort this call
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
await scriptAPI.updateScript(scriptId, scriptFormData, scriptName);
|
||||
|
|
@ -88,6 +145,7 @@ const EditScriptModal = ({
|
|||
renderFlash("error", getErrorMessage(e));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
setShowConfirmChanges(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -161,15 +219,28 @@ const EditScriptModal = ({
|
|||
);
|
||||
};
|
||||
|
||||
const classes = classnames(baseClass, {
|
||||
[`${baseClass}__hide-main`]: !!showConfirmChanges,
|
||||
});
|
||||
return (
|
||||
<Modal
|
||||
className={baseClass}
|
||||
title={scriptName}
|
||||
width="large"
|
||||
onExit={onExit}
|
||||
>
|
||||
{renderContent()}
|
||||
</Modal>
|
||||
<>
|
||||
<Modal
|
||||
className={classes}
|
||||
title={scriptName}
|
||||
width="large"
|
||||
onExit={onExit}
|
||||
>
|
||||
{renderContent()}
|
||||
</Modal>
|
||||
{!!showConfirmChanges && (
|
||||
<WarningModal
|
||||
onExit={() => setShowConfirmChanges(false)}
|
||||
onSave={onSave}
|
||||
scriptName={scriptName}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
.edit-script-modal {
|
||||
&__hide-main {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
// since this modal shows only while anotheris also showing, suppress background for one of them to
|
||||
// prevent "double-darkening"
|
||||
.modal__background:has(.edit-script-modal.edit-script-modal__hide-main) {
|
||||
visibility: hidden;
|
||||
}
|
||||
Loading…
Reference in a new issue