From 5138dbaee3b7170231b0f0f05940e1d18c3db202 Mon Sep 17 00:00:00 2001 From: RachelElysia Date: Fri, 6 Sep 2024 10:49:24 -0700 Subject: [PATCH] Proof of concept of save modal, need work on uploader refactor --- .../components/FileDetails/FileDetails.tsx | 1 + frontend/components/FileDetails/_styles.scss | 1 + frontend/components/FileDetails/index.ts | 1 + frontend/components/FileUploader/_styles.scss | 92 ------------------- .../ConfirmSaveChangesModal.tsx | 57 ++++++++++++ .../ConfirmSaveChangesModal/index.ts | 1 + .../EditSoftwareModal/EditSoftwareModal.tsx | 17 +++- .../EditSoftwareModal/_styles.scss | 4 + .../SoftwarePackageCard.tsx | 24 ++++- .../SoftwarePackageCard/helpers.ts | 2 +- .../AddPackageForm/AddPackageForm.tsx | 31 ++++++- frontend/services/entities/software.ts | 3 +- 12 files changed, 132 insertions(+), 102 deletions(-) create mode 100644 frontend/components/FileDetails/FileDetails.tsx create mode 100644 frontend/components/FileDetails/_styles.scss create mode 100644 frontend/components/FileDetails/index.ts create mode 100644 frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx create mode 100644 frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/index.ts diff --git a/frontend/components/FileDetails/FileDetails.tsx b/frontend/components/FileDetails/FileDetails.tsx new file mode 100644 index 000000000000..600cfe6919be --- /dev/null +++ b/frontend/components/FileDetails/FileDetails.tsx @@ -0,0 +1 @@ +// TODO: Move to a separate file diff --git a/frontend/components/FileDetails/_styles.scss b/frontend/components/FileDetails/_styles.scss new file mode 100644 index 000000000000..70b786d12ed0 --- /dev/null +++ b/frontend/components/FileDetails/_styles.scss @@ -0,0 +1 @@ +// TODO diff --git a/frontend/components/FileDetails/index.ts b/frontend/components/FileDetails/index.ts new file mode 100644 index 000000000000..434185761720 --- /dev/null +++ b/frontend/components/FileDetails/index.ts @@ -0,0 +1 @@ +export { default } from "./FileDetails"; diff --git a/frontend/components/FileUploader/_styles.scss b/frontend/components/FileUploader/_styles.scss index 2959b66d243a..e69de29bb2d1 100644 --- a/frontend/components/FileUploader/_styles.scss +++ b/frontend/components/FileUploader/_styles.scss @@ -1,92 +0,0 @@ -.file-uploader { - display: flex; - flex-direction: column; - align-items: center; - border-radius: $border-radius-medium; - background-color: $ui-fleet-blue-10; - border: 1px solid $ui-fleet-black-10; - padding: $pad-xlarge $pad-large; - font-size: $x-small; - text-align: center; - gap: $pad-small; - - // when the file preview is showing, we want the padding to be - // slightly smaller on the top and bottom. - &__file-preview { - padding: $pad-medium $pad-large; - } - - &__file { - display: flex; - justify-content: space-between; - width: 100%; - - &-info { - display: flex; - gap: $pad-medium; - align-items: center; - width: 100%; - text-align: left; - } - - &-name { - font-size: $x-small; - font-weight: $bold; - } - - &-platform { - font-size: $xx-small; - color: $ui-fleet-black-75; - } - - &-edit { - display: flex; - align-items: center; // Center the button vertically - margin-right: -$pad-medium; // Adjust for button padding - } - - label { - display: flex; - - &:hover { - cursor: pointer; - } - } - } - - &__graphics { - display: flex; - align-items: center; - gap: $pad-medium; - } - &__message { - margin: 0; - color: $ui-fleet-black-75; - } - - &__additional-info { - margin: 0; - color: $ui-fleet-black-50; - } - - input { - display: none; - } - - &__upload-button { - // we handle the padding in the label so the entire button is clickable - padding: 0; - - label { - padding: $pad-small $pad-medium; - display: flex; - align-items: center; - justify-content: center; - gap: $pad-small; - - &:hover { - cursor: pointer; - } - } - } -} diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx new file mode 100644 index 000000000000..11ee5da1c3e8 --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx @@ -0,0 +1,57 @@ +import React from "react"; + +import Button from "components/buttons/Button"; +import Modal from "components/Modal"; + +const baseClass = "save-changes-modal"; + +export interface IConfirmSaveChangesModalProps { + onSaveChanges: (evt: React.MouseEvent) => void; + softwarePackageName?: string; + onClose: () => void; +} + +const ConfirmSaveChangesModal = ({ + onSaveChanges, + softwarePackageName, + onClose, +}: IConfirmSaveChangesModalProps) => { + const warningText = ( + <> + The changes you are making will cancel any pending installs and uninstalls + {softwarePackageName ? ( + <> + {" "} + for {softwarePackageName} + + ) : ( + "" + )} + . + + ); + return ( + +
+

{warningText}

+

You cannot undo this action.

+
+ + +
+
+
+ ); +}; + +export default ConfirmSaveChangesModal; diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/index.ts b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/index.ts new file mode 100644 index 000000000000..c8c31da396be --- /dev/null +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/index.ts @@ -0,0 +1 @@ +export { default } from "./ConfirmSaveChangesModal"; diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx index 4450239887fe..908fd044b94a 100644 --- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx @@ -1,5 +1,6 @@ import React, { useContext, useState } from "react"; import { InjectedRouter } from "react-router"; +import classnames from "classnames"; import { getErrorReason } from "interfaces/errors"; @@ -38,6 +39,8 @@ interface IEditSoftwareModalProps { selfService?: boolean; onExit: () => void; setAddedSoftwareToken: (token: string) => void; + showConfirmSaveChangesModal: boolean; + toggleConfirmSaveChangesModal: () => void; } const EditSoftwareModal = ({ @@ -52,7 +55,14 @@ const EditSoftwareModal = ({ selfService, onExit, setAddedSoftwareToken, + showConfirmSaveChangesModal, + toggleConfirmSaveChangesModal, }: IEditSoftwareModalProps) => { + // Add class to hide modal based on showConfirmSaveChangesModal, + const editSoftwareModalClasses = classnames(baseClass, { + [`${baseClass}--hidden`]: !!showConfirmSaveChangesModal, + }); + const { renderFlash } = useContext(NotificationContext); const [isUpdatingSoftware, setIsUpdatingSoftware] = useState(false); @@ -121,7 +131,11 @@ const EditSoftwareModal = ({ }; return ( - + ); diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/_styles.scss b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/_styles.scss index d3e223b09f1d..6dd125240355 100644 --- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/_styles.scss +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/_styles.scss @@ -10,4 +10,8 @@ flex-direction: column; gap: $pad-medium; } + + &--hidden { + display: none; + } } diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/SoftwarePackageCard.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/SoftwarePackageCard.tsx index 37b8b449327d..4406489d831b 100644 --- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/SoftwarePackageCard.tsx +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/SoftwarePackageCard.tsx @@ -33,9 +33,10 @@ import DeleteSoftwareModal from "../DeleteSoftwareModal"; import EditSoftwareModal from "../EditSoftwareModal"; import { APP_STORE_APP_DROPDOWN_OPTIONS, - SOFTWARE_PACAKGE_DROPDOWN_OPTIONS, + SOFTWARE_PACKAGE_DROPDOWN_OPTIONS, downloadFile, } from "./helpers"; +import ConfirmSaveChangesModal from "../ConfirmSaveChangesModal"; const baseClass = "software-package-card"; @@ -232,7 +233,7 @@ const ActionsDropdown = ({ searchable={false} options={ isSoftwarePackage - ? SOFTWARE_PACAKGE_DROPDOWN_OPTIONS + ? SOFTWARE_PACKAGE_DROPDOWN_OPTIONS : APP_STORE_APP_DROPDOWN_OPTIONS } /> @@ -282,8 +283,18 @@ const SoftwarePackageCard = ({ const { renderFlash } = useContext(NotificationContext); const [showEditSoftwareModal, setShowEditSoftwareModal] = useState(false); + const [ + showConfirmSaveChangesModal, + setShowConfirmSaveChangesModal, + ] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); + const toggleConfirmSaveChangesModal = () => { + debugger; + // open and closes save changes modal + setShowConfirmSaveChangesModal(!showConfirmSaveChangesModal); + }; + const onEditSoftwareClick = () => { setShowEditSoftwareModal(true); }; @@ -410,6 +421,15 @@ const SoftwarePackageCard = ({ onExit={() => setShowEditSoftwareModal(false)} router={router} setAddedSoftwareToken={noop} + showConfirmSaveChangesModal={showConfirmSaveChangesModal} + toggleConfirmSaveChangesModal={toggleConfirmSaveChangesModal} + /> + )} + {showConfirmSaveChangesModal && ( + )} {showDeleteModal && ( diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/helpers.ts b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/helpers.ts index 0f2c4e5af66e..e1bf20690154 100644 --- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/helpers.ts +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwarePackageCard/helpers.ts @@ -1,4 +1,4 @@ -export const SOFTWARE_PACAKGE_DROPDOWN_OPTIONS = [ +export const SOFTWARE_PACKAGE_DROPDOWN_OPTIONS = [ { label: "Download", value: "download", diff --git a/frontend/pages/SoftwarePage/components/AddPackageForm/AddPackageForm.tsx b/frontend/pages/SoftwarePage/components/AddPackageForm/AddPackageForm.tsx index 8408186540ce..f0c7cba9595d 100644 --- a/frontend/pages/SoftwarePage/components/AddPackageForm/AddPackageForm.tsx +++ b/frontend/pages/SoftwarePage/components/AddPackageForm/AddPackageForm.tsx @@ -3,6 +3,7 @@ import React, { useContext, useState } from "react"; import { NotificationContext } from "context/notification"; import { getFileDetails } from "utilities/file/fileUtils"; +import deepDifference from "utilities/deep_difference"; import getDefaultInstallScript from "utilities/software_install_scripts"; import getDefaultUninstallScript from "utilities/software_uninstall_scripts"; @@ -56,6 +57,7 @@ interface IAddPackageFormProps { defaultPostInstallScript?: string; defaultUninstallScript?: string; defaultSelfService?: boolean; + toggleSaveChangesForEditModal?: () => void; } const ACCEPTED_EXTENSIONS = ".pkg,.msi,.exe,.deb"; @@ -71,17 +73,21 @@ const AddPackageForm = ({ defaultPostInstallScript, defaultUninstallScript, defaultSelfService, + toggleSaveChangesForEditModal, }: IAddPackageFormProps) => { const { renderFlash } = useContext(NotificationContext); - const [formData, setFormData] = useState({ + const initialFormData = { software: defaultSoftware || null, installScript: defaultInstallScript || "", preInstallQuery: defaultPreInstallQuery || undefined, postInstallScript: defaultPostInstallScript || undefined, uninstallScript: defaultUninstallScript || undefined, selfService: defaultSelfService || false, - }); + }; + const [formData, setFormData] = useState( + initialFormData + ); const [formValidation, setFormValidation] = useState({ isValid: false, software: { isValid: false }, @@ -119,8 +125,22 @@ const AddPackageForm = ({ }; const onFormSubmit = (evt: React.FormEvent) => { - evt.preventDefault(); - onSubmit(formData); + // When editing software, we prompt a save changes modal for all changes except to self-service + const updates = deepDifference(initialFormData, formData); + const onlySelfServiceUpdated = + Object.keys(updates).length === 1 && "selfService" in updates; + + const promptSaveChangesForEditModal = + isEditingSoftware && !onlySelfServiceUpdated; + + if (promptSaveChangesForEditModal && !!toggleSaveChangesForEditModal) { + evt.preventDefault(); + toggleSaveChangesForEditModal(); + } else { + evt.preventDefault(); + + onSubmit(formData); + } }; const onChangeInstallScript = (value: string) => { @@ -216,4 +236,5 @@ const AddPackageForm = ({ ); }; -export default AddPackageForm; +// Allows form not to re-render as long as it's props don't change +export default React.memo(AddPackageForm); diff --git a/frontend/services/entities/software.ts b/frontend/services/entities/software.ts index 96f50a61de8b..c5dda4c84a9b 100644 --- a/frontend/services/entities/software.ts +++ b/frontend/services/entities/software.ts @@ -254,10 +254,11 @@ export default { formData.append("pre_install_query", data.preInstallQuery); data.postInstallScript && formData.append("post_install_script", data.postInstallScript); + // TODO: uninstall script once uninstall is merged teamId && formData.append("team_id", teamId.toString()); return sendRequest( - "POST", + "PATCH", SOFTWARE_TITLE(softwareId), formData, undefined,