From 391bd0a5a0406626e336be3b06aebee1cb90eacc Mon Sep 17 00:00:00 2001 From: jacobshandling <61553566+jacobshandling@users.noreply.github.com> Date: Fri, 21 Feb 2025 12:22:08 -0800 Subject: [PATCH] UI - GitOps mode, part 2 (#26509) ## For #26229 This is the 2nd iterative PR for this ticket. It includes: - tests with a new testing utility - refactored argument and class names - another batch of UI updates - [x] Added/updated automated tests - [x] Manual QA for all new/changed functionality --------- Co-authored-by: Jacob Shandling --- .../components/FileDetails/FileDetails.tsx | 48 +- frontend/components/FileDetails/_styles.scss | 3 + .../components/FileUploader/FileUploader.tsx | 4 + .../GitOpsModeTooltipWrapper.tests.tsx | 89 ++++ .../GitOpsModeTooltipWrapper.tsx | 4 +- frontend/components/YamlAce/YamlAce.jsx | 4 + frontend/components/YamlAce/_styles.scss | 3 + .../AppleOSTargetForm/AppleOSTargetForm.tsx | 11 +- .../WindowsTargetForm/WindowsTargetForm.tsx | 11 +- .../SelectSoftwareModal.tsx | 4 +- .../SelectSoftwareTableConfig.tsx | 8 +- .../SoftwareVppForm/SoftwareVppForm.tsx | 5 +- .../EditSoftwareModal/EditSoftwareModal.tsx | 1 + .../ManageSoftwareAutomationsModal.tsx | 13 +- .../components/PackageForm/PackageForm.tsx | 10 +- .../cards/Calendars/Calendars.tsx | 28 +- .../ChangeManagement/ChangeManagement.tsx | 25 +- .../AddIntegrationModal.tsx | 9 +- .../IntegrationForm/IntegrationForm.tsx | 87 ++-- .../components/content/ApplePushCertInfo.tsx | 32 +- .../cards/MdmSettings/ScepPage/ScepPage.tsx | 30 +- .../components/AddVppModal/AddVppModal.tsx | 24 +- .../WindowsMdmPage/WindowsMdmPage.tsx | 22 +- .../cards/MdmSettings/_styles.scss | 3 + .../components/IdpSection/IdpSection.tsx | 129 ++--- .../pages/admin/OrgSettingsPage/_styles.scss | 4 + .../cards/Advanced/Advanced.tsx | 445 +++++++++--------- .../OrgSettingsPage/cards/Agents/Agents.tsx | 27 +- .../cards/FleetDesktop/FleetDesktop.tsx | 29 +- .../GlobalHostStatusWebhook.tsx | 189 ++++---- .../admin/OrgSettingsPage/cards/Info/Info.tsx | 155 +++--- .../admin/OrgSettingsPage/cards/Smtp/Smtp.tsx | 159 ++++--- .../admin/OrgSettingsPage/cards/Sso/Sso.tsx | 207 ++++---- .../cards/Statistics/Statistics.tsx | 24 +- .../cards/WebAddress/WebAddress.tsx | 27 +- .../AgentOptionsPage/AgentOptionsPage.tsx | 27 +- .../ManagePoliciesPage/ManagePoliciesPage.tsx | 7 +- .../CalendarEventsModal.tests.tsx | 2 +- .../CalendarEventsModal.tsx | 14 +- .../InstallSoftwareModal.tsx | 37 +- .../InstallSoftwareModal/_styles.scss | 3 + .../OtherWorkflowsModal.tsx | 45 +- .../OtherWorkflowsModal/_styles.scss | 6 + .../PoliciesTable/PoliciesTableConfig.tsx | 16 +- .../PolicyRunScriptModal.tsx | 35 +- .../PolicyRunScriptModal/_styles.scss | 5 +- .../components/PolicyForm/PolicyForm.tsx | 32 +- .../components/PolicyForm/_styles.scss | 2 +- .../ManageQueryAutomationsModal.tsx | 9 +- .../QueriesTable/QueriesTableConfig.tsx | 16 +- .../EditQueryForm/EditQueryForm.tsx | 26 +- .../components/EditQueryForm/_styles.scss | 4 +- frontend/test/test-utils.tsx | 11 +- 53 files changed, 1303 insertions(+), 867 deletions(-) create mode 100644 frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tests.tsx diff --git a/frontend/components/FileDetails/FileDetails.tsx b/frontend/components/FileDetails/FileDetails.tsx index 913408190d..d04cbf8f8f 100644 --- a/frontend/components/FileDetails/FileDetails.tsx +++ b/frontend/components/FileDetails/FileDetails.tsx @@ -1,11 +1,14 @@ import React from "react"; +import classnames from "classnames"; + import { IFileDetails } from "utilities/file/fileUtils"; import Button from "components/buttons/Button"; import { ISupportedGraphicNames } from "components/FileUploader/FileUploader"; import Graphic from "components/Graphic"; import Icon from "components/Icon"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; export type IFileDetailsSupportedGraphicNames = | ISupportedGraphicNames @@ -20,6 +23,7 @@ interface IFileDetailsProps { onFileSelect?: (e: React.ChangeEvent) => void; accept?: string; progress?: number; + gitOpsModeEnabled?: boolean; } const baseClass = "file-details"; @@ -31,10 +35,15 @@ const FileDetails = ({ onFileSelect, accept, progress, + gitOpsModeEnabled = false, }: IFileDetailsProps) => { + const infoClasses = classnames(`${baseClass}__info`, { + [`${baseClass}__info--disabled-by-gitops-mode`]: gitOpsModeEnabled, + }); return (
-
+ {/* disabling at this level preserves funcitonality of GitOpsModeTooltipWrapper around the edit icon */} +
{!progress && canEdit && onFileSelect && ( -
- - -
+ ( +
+ + +
+ )} + /> )} {!!progress && (
diff --git a/frontend/components/FileDetails/_styles.scss b/frontend/components/FileDetails/_styles.scss index 265df6191a..20942f2157 100644 --- a/frontend/components/FileDetails/_styles.scss +++ b/frontend/components/FileDetails/_styles.scss @@ -10,6 +10,9 @@ align-items: center; width: 100%; text-align: left; + &--disabled-by-gitops-mode { + @include disabled; + } } &__name { diff --git a/frontend/components/FileUploader/FileUploader.tsx b/frontend/components/FileUploader/FileUploader.tsx index 4f9dbe17bc..c41a994264 100644 --- a/frontend/components/FileUploader/FileUploader.tsx +++ b/frontend/components/FileUploader/FileUploader.tsx @@ -57,6 +57,8 @@ interface IFileUploaderProps { }; /** Indicates that this file uploader deals with an entity that can be managed by GitOps, and so should be disabled when gitops mode is enabled */ gitopsCompatible?: boolean; + /** Whether or not GitOpsMode is enabled. Has no effect if `gitopsCompatible` is false */ + gitOpsModeEnabled?: boolean; } /** @@ -76,6 +78,7 @@ export const FileUploader = ({ canEdit = false, fileDetails, gitopsCompatible = false, + gitOpsModeEnabled = false, }: IFileUploaderProps) => { const [isFileSelected, setIsFileSelected] = useState(!!fileDetails); const fileInputRef = useRef(null); @@ -180,6 +183,7 @@ export const FileUploader = ({ canEdit={canEdit} onFileSelect={onFileSelect} accept={accept} + gitOpsModeEnabled={gitopsCompatible && gitOpsModeEnabled} /> ) : ( renderFileUploader() diff --git a/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tests.tsx b/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tests.tsx new file mode 100644 index 0000000000..f139a2b25b --- /dev/null +++ b/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tests.tsx @@ -0,0 +1,89 @@ +import React from "react"; + +import { noop } from "lodash"; +import { screen } from "@testing-library/react"; +import { createCustomRenderer } from "test/test-utils"; + +import Button from "components/buttons/Button"; + +import GitOpsModeTooltipWrapper from "./GitOpsModeTooltipWrapper"; + +describe("GitOpsModeTooltipWrapper", () => { + it("renders clickable children without a tooltip when GOM is not enabled", async () => { + const render = createCustomRenderer({ + context: { + app: { + isGlobalAdmin: true, + isTeamAdmin: false, + // thanks, DeepPartial! + config: { + gitops: { + gitops_mode_enabled: false, + repository_url: "", + }, + }, + }, + }, + }); + + const onSave = jest.fn(); + + const { user } = render( + ( + + )} + /> + ); + + const btn = screen.getByText("Save"); + expect(btn).toBeInTheDocument(); + + await user.hover(btn); + expect(screen.queryByRole("tooltip")).toBeNull(); + + await user.click(btn); + expect(onSave).toHaveBeenCalled(); + }); + + it("renders non-clickable children with the tooltip when GOM is enabled", async () => { + const render = createCustomRenderer({ + context: { + app: { + isGlobalAdmin: true, + isTeamAdmin: false, + // thanks, DeepPartial! + config: { + gitops: { + gitops_mode_enabled: true, + repository_url: "a.b.cc", + }, + }, + }, + }, + }); + + const onSave = jest.fn(); + + const { user } = render( + ( + + )} + /> + ); + + const btn = screen.getByText("Save"); + expect(btn).toBeInTheDocument(); + + await user.hover(btn); + expect(screen.getByRole("tooltip")).toBeInTheDocument(); + + await user.click(btn); + expect(onSave).not.toHaveBeenCalled(); + }); +}); diff --git a/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx b/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx index c2b054a294..1f21a19b08 100644 --- a/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx +++ b/frontend/components/GitOpsModeTooltipWrapper/GitOpsModeTooltipWrapper.tsx @@ -21,10 +21,10 @@ const GitOpsModeTooltipWrapper = ({ fixedPositionStrategy, }: IGitOpsModeTooltipWrapper) => { const { config } = useContext(AppContext); - const gomEnabled = config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; const repoURL = config?.gitops.repository_url; - if (!gomEnabled) { + if (!gitOpsModeEnabled) { return <>{renderChildren()}; } diff --git a/frontend/components/YamlAce/YamlAce.jsx b/frontend/components/YamlAce/YamlAce.jsx index b8979f99e8..11342b6c51 100644 --- a/frontend/components/YamlAce/YamlAce.jsx +++ b/frontend/components/YamlAce/YamlAce.jsx @@ -15,6 +15,7 @@ class YamlAce extends Component { onChange: PropTypes.func.isRequired, value: PropTypes.string, wrapperClassName: PropTypes.string, + disabled: PropTypes.bool, }; onLoadHandler = (editor) => { @@ -56,18 +57,21 @@ class YamlAce extends Component { value, error, wrapperClassName, + disabled, } = this.props; const { renderLabel, onLoadHandler } = this; const wrapperClass = classnames(wrapperClassName, "form-field", { [`${baseClass}__wrapper--error`]: error, + [`${baseClass}__wrapper--disabled`]: disabled, }); return (
{renderLabel()} { const { renderFlash } = useContext(NotificationContext); - const gomEnabled = useContext(AppContext).config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = useContext(AppContext).config?.gitops + .gitops_mode_enabled; const [isSaving, setIsSaving] = useState(false); const [minOsVersion, setMinOsVersion] = useState(defaultMinOsVersion); @@ -188,7 +189,7 @@ const AppleOSTargetForm = ({
@@ -205,7 +206,7 @@ const AppleOSTargetForm = ({ onChange={handleMinVersionChange} /> ( - )} diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/WindowsTargetForm/WindowsTargetForm.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/WindowsTargetForm/WindowsTargetForm.tsx index 371c54faf5..1fd3c75a14 100644 --- a/frontend/pages/ManageControlsPage/OSUpdates/components/WindowsTargetForm/WindowsTargetForm.tsx +++ b/frontend/pages/ManageControlsPage/OSUpdates/components/WindowsTargetForm/WindowsTargetForm.tsx @@ -100,7 +100,8 @@ const WindowsTargetForm = ({ refetchTeamConfig, }: IWindowsTargetFormProps) => { const { renderFlash } = useContext(NotificationContext); - const gomEnabled = useContext(AppContext).config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = useContext(AppContext).config?.gitops + .gitops_mode_enabled; const [isSaving, setIsSaving] = useState(false); const [deadlineDays, setDeadlineDays] = useState( @@ -161,7 +162,7 @@ const WindowsTargetForm = ({ return ( ( - )} diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/components/SelectSoftwareModal/SelectSoftwareModal.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/components/SelectSoftwareModal/SelectSoftwareModal.tsx index 4dc9312135..c885e1ae2c 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/components/SelectSoftwareModal/SelectSoftwareModal.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/components/SelectSoftwareModal/SelectSoftwareModal.tsx @@ -95,9 +95,9 @@ const SelectSoftwareModal = ({
( + renderChildren={(disableChildren) => ( diff --git a/frontend/pages/admin/IntegrationsPage/cards/Calendars/Calendars.tsx b/frontend/pages/admin/IntegrationsPage/cards/Calendars/Calendars.tsx index 63573337d7..a3f82ade0f 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/Calendars/Calendars.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/Calendars/Calendars.tsx @@ -19,6 +19,7 @@ import DataError from "components/DataError"; import PremiumFeatureMessage from "components/PremiumFeatureMessage/PremiumFeatureMessage"; import Icon from "components/Icon"; import Card from "components/Card"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; const CREATING_SERVICE_ACCOUNT = "https://www.fleetdm.com/learn-more-about/creating-service-accounts"; @@ -85,6 +86,7 @@ const Calendars = (): JSX.Element => { const [copyMessage, setCopyMessage] = useState(""); const { + data: config, isLoading: isLoadingAppConfig, refetch: refetchConfig, error: errorAppConfig, @@ -104,6 +106,7 @@ const Calendars = (): JSX.Element => { } }, }); + const gomEnabled = config?.gitops.gitops_mode_enabled; const { apiKeyJson, domain } = formData; @@ -312,6 +315,7 @@ const Calendars = (): JSX.Element => { ignore1password inputClassName={`${baseClass}__api-key-json`} error={formErrors.apiKeyJson} + disabled={gomEnabled} /> { } error={formErrors.domain} + disabled={gomEnabled} + /> + ( + + )} /> - diff --git a/frontend/pages/admin/IntegrationsPage/cards/ChangeManagement/ChangeManagement.tsx b/frontend/pages/admin/IntegrationsPage/cards/ChangeManagement/ChangeManagement.tsx index a97236f3d4..e2ceed8ba5 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/ChangeManagement/ChangeManagement.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/ChangeManagement/ChangeManagement.tsx @@ -23,7 +23,7 @@ import { AppContext } from "context/app"; const baseClass = "change-management"; interface IChangeManagementFormData { - gomEnabled: boolean; + gitOpsModeEnabled: boolean; repoURL: string; } @@ -33,8 +33,8 @@ interface IChangeManagementFormErrors { const validate = (formData: IChangeManagementFormData) => { const errs: IChangeManagementFormErrors = {}; - const { gomEnabled, repoURL } = formData; - if (gomEnabled) { + const { gitOpsModeEnabled, repoURL } = formData; + if (gitOpsModeEnabled) { if (!repoURL) { errs.repository_url = "Git repository URL is required when GitOps mode is enabled"; @@ -51,7 +51,7 @@ const ChangeManagement = () => { const [formData, setFormData] = useState({ // dummy 0 values, will be populated with fresh config API response - gomEnabled: false, + gitOpsModeEnabled: false, repoURL: "", }); const [formErrors, setFormErrors] = useState({}); @@ -67,15 +67,18 @@ const ChangeManagement = () => { { onSuccess: (data) => { const { - gitops: { gitops_mode_enabled: gomEnabled, repository_url: repoURL }, + gitops: { + gitops_mode_enabled: gitOpsModeEnabled, + repository_url: repoURL, + }, } = data; - setFormData({ gomEnabled, repoURL }); + setFormData({ gitOpsModeEnabled, repoURL }); setConfig(data); }, } ); - const { gomEnabled, repoURL } = formData; + const { gitOpsModeEnabled, repoURL } = formData; if (isLoadingConfig) { return ; @@ -96,7 +99,7 @@ const ChangeManagement = () => { try { await configAPI.update({ gitops: { - gitops_mode_enabled: formData.gomEnabled, + gitops_mode_enabled: formData.gitOpsModeEnabled, repository_url: formData.repoURL, }, }); @@ -147,8 +150,8 @@ const ChangeManagement = () => {
@@ -165,7 +168,7 @@ const ChangeManagement = () => { onBlur={onInputBlur} error={formErrors.repository_url} helpText="When GitOps mode is enabled, you will be directed here to make changes." - disabled={!gomEnabled} + disabled={!gitOpsModeEnabled} />
diff --git a/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx b/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx index dee63d4826..88eba6d33e 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/Integrations/components/IntegrationForm/IntegrationForm.tsx @@ -16,6 +16,7 @@ import validUrl from "components/forms/validators/valid_url"; import Spinner from "components/Spinner"; import { COLORS } from "styles/var/colors"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; const baseClass = "integration-form"; @@ -37,6 +38,7 @@ interface IIntegrationFormProps { integrationEditingType?: IIntegrationType; destination?: string; testingConnection?: boolean; + gitOpsModeEnabled?: boolean; } interface IFormField { @@ -59,6 +61,7 @@ const IntegrationForm = ({ integrationEditingType, destination, testingConnection, + gitOpsModeEnabled, }: IIntegrationFormProps): JSX.Element => { const { jira: jiraIntegrations, zendesk: zendeskIntegrations } = integrations; const [formData, setFormData] = useState({ @@ -191,6 +194,7 @@ const IntegrationForm = ({ value={url} error={urlError} onBlur={validateForm} + disabled={gitOpsModeEnabled} /> {integrationDestination === "jira" ? ( ) : ( )} {integrationDestination === "jira" ? ( To find the Jira project key, head to your project in
@@ -245,6 +253,7 @@ const IntegrationForm = ({ type="number" parseTarget value={groupId === 0 ? null : groupId} + disabled={gitOpsModeEnabled} tooltip={ <> To find the Zendesk group ID, select{" "} @@ -259,28 +268,10 @@ const IntegrationForm = ({ /> )}
-
- -
- - <> - Complete all fields to save
the integration. - -
+ formData.groupId === 0; + // TODO - refactor below to use TooltipWrapper + return ( + <> +
+ +
+ + <> + Complete all fields to save
the integration. + +
+ + ); + }} + /> diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/AppleMdmPage/components/content/ApplePushCertInfo.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/AppleMdmPage/components/content/ApplePushCertInfo.tsx index a8ee331d5a..80b26fb03a 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/AppleMdmPage/components/content/ApplePushCertInfo.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/AppleMdmPage/components/content/ApplePushCertInfo.tsx @@ -5,6 +5,7 @@ import { IMdmApple } from "interfaces/mdm"; import { readableDate } from "utilities/helpers"; import Button from "components/buttons/Button"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; interface IApplePushCertInfoProps { baseClass: string; @@ -43,12 +44,31 @@ const ApplePushCertInfo = ({
- - + ( + + )} + /> + ( + + )} + />
); diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/ScepPage/ScepPage.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/ScepPage/ScepPage.tsx index caea834d89..4bbe7a40e6 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/ScepPage/ScepPage.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/ScepPage/ScepPage.tsx @@ -22,6 +22,7 @@ import PremiumFeatureMessage from "components/PremiumFeatureMessage"; import Spinner from "components/Spinner"; import DataError from "components/DataError"; import TurnOnMdmMessage from "components/TurnOnMdmMessage"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { SCEP_SERVER_TIP_CONTENT } from "../components/ScepSection/ScepSection"; @@ -95,6 +96,8 @@ export const ScepCertificateContent = ({ ); } + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; + const disableSave = // all fields aren't empty !Object.values(formData).every((val) => val === "") && @@ -138,6 +141,7 @@ export const ScepCertificateContent = ({ parseTarget error={formErrors.scepUrl} placeholder="https://example.com/certsrv/mscep/mscep.dll" + disabled={gitOpsModeEnabled} /> onBlur("username", e.target.value)} parseTarget placeholder="username@example.microsoft.com" + disabled={gitOpsModeEnabled} /> + ( + + )} /> - diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/VppPage/components/AddVppModal/AddVppModal.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/VppPage/components/AddVppModal/AddVppModal.tsx index 520cea425f..52dfe4d0e0 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/VppPage/components/AddVppModal/AddVppModal.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/VppPage/components/AddVppModal/AddVppModal.tsx @@ -1,12 +1,12 @@ import React, { useCallback, useContext, useState } from "react"; import { NotificationContext } from "context/notification"; -import { getErrorReason } from "interfaces/errors"; import mdmAppleAPI from "services/entities/mdm_apple"; import Modal from "components/Modal"; import Button from "components/buttons/Button"; import FileUploader from "components/FileUploader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import VppSetupSteps from "../VppSetupSteps"; import { getErrorMessage } from "./helpers"; @@ -71,16 +71,22 @@ const AddVppModal = ({ onCancel, onAdded }: IAddVppModalProps) => { buttonMessage={isUploading ? "Uploading..." : "Upload"} fileDetails={tokenFile ? { name: tokenFile.name } : undefined} onFileUpload={onSelectFile} + gitopsCompatible />
- + ( + + )} + />
diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx index 1fa8ef9617..4dd0610b20 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/WindowsMdmPage/WindowsMdmPage.tsx @@ -11,6 +11,8 @@ import Button from "components/buttons/Button"; import BackLink from "components/BackLink/BackLink"; import Slider from "components/forms/fields/Slider"; import Checkbox from "components/forms/fields/Checkbox"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; + import { getErrorMessage } from "./helpers"; const baseClass = "windows-mdm-page"; @@ -60,6 +62,7 @@ interface IWindowsMdmPageProps { const WindowsMdmPage = ({ router }: IWindowsMdmPageProps) => { const { config, isPremiumTier } = useContext(AppContext); + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; const [mdmOn, setMdmOn] = useState( config?.mdm?.windows_enabled_and_configured ?? false @@ -106,10 +109,11 @@ const WindowsMdmPage = ({ router }: IWindowsMdmPageProps) => { activeText="Windows MDM on" inactiveText="Windows MDM off" onChange={onChangeMdmOn} + disabled={gitOpsModeEnabled} />

{descriptionText}

{ > Automatically migrate hosts connected to another MDM solution - - + ( + + )} + /> diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/_styles.scss b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/_styles.scss index 93d3b2c538..866d2e6a10 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/_styles.scss +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/_styles.scss @@ -2,4 +2,7 @@ display: flex; flex-direction: column; gap: $pad-xxxlarge; + .disabled-by-gitops-mode { + @include disabled; + } } diff --git a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/IdpSection/IdpSection.tsx b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/IdpSection/IdpSection.tsx index 24c79e3a29..0ec571468a 100644 --- a/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/IdpSection/IdpSection.tsx +++ b/frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/IdpSection/IdpSection.tsx @@ -12,6 +12,7 @@ import { AppContext } from "context/app"; import { expandErrorReasonRequired } from "interfaces/errors"; import { AxiosResponse } from "axios"; import TooltipWrapper from "components/TooltipWrapper"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IFormDataIdp, IFormErrorsIdp, @@ -24,6 +25,8 @@ const baseClass = "idp-section"; const IdpSection = () => { const { config } = useContext(AppContext); + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; + const { renderFlash } = useContext(NotificationContext); const [formData, setFormData] = useState( newFormDataIdp(config?.mdm?.end_user_authentication) @@ -107,64 +110,76 @@ const IdpSection = () => { newTab />

- - - - If both Metadata URL and Metadata are specified,{" "} - Metadata URL will be used. - - } - onChange={onInputChange} - onBlur={onBlur} - name="metadata_url" - value={formData.metadata_url} - parseTarget - error={formErrors?.metadata_url} - tooltip="Metadata URL provided by the identity provider." - /> - - - - + + + + If both Metadata URL and Metadata are specified,{" "} + Metadata URL will be used. + + } + onChange={onInputChange} + onBlur={onBlur} + name="metadata_url" + value={formData.metadata_url} + parseTarget + error={formErrors?.metadata_url} + tooltip="Metadata URL provided by the identity provider." + /> + +
+ ( + + + + )} + />
); diff --git a/frontend/pages/admin/OrgSettingsPage/_styles.scss b/frontend/pages/admin/OrgSettingsPage/_styles.scss index 441cab0f4a..51fa723590 100644 --- a/frontend/pages/admin/OrgSettingsPage/_styles.scss +++ b/frontend/pages/admin/OrgSettingsPage/_styles.scss @@ -77,4 +77,8 @@ } } } + + .disabled-by-gitops-mode { + @include disabled; + } } diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx index 69d7b3ccb7..22fb3719d7 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/Advanced/Advanced.tsx @@ -8,6 +8,7 @@ import InputField from "components/forms/fields/InputField"; import SectionHeader from "components/SectionHeader"; // @ts-ignore import Dropdown from "components/forms/fields/Dropdown"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { ACTIVITY_EXPIRY_WINDOW_DROPDOWN_OPTIONS } from "utilities/constants"; import { getCustomDropdownOptions } from "utilities/helpers"; @@ -71,6 +72,8 @@ const Advanced = ({ handleSubmit, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; + const [formData, setFormData] = useState({ mdmAppleServerURL: appConfig.mdm?.apple_server_url || "", domain: appConfig.smtp_settings?.domain || "", @@ -189,232 +192,244 @@ const Advanced = ({

Most users do not need to modify these options.

- {appConfig.mdm.enabled_and_configured && ( +
+ {appConfig.mdm.enabled_and_configured && ( + + )} - )} - - If you need to specify a HELO domain,
- you can do it here{" "} - - (Default: Blank) - - - } - /> - - Turn this off (not recommended)
- if you use a self-signed certificate{" "} - -
- (Default: On) -
- - } - > - Verify SSL certs -
- - Detects if STARTTLS is enabled
- in your SMTP server and starts
- to use it.{" "} - - (Default: On) - - - } - > - Enable STARTTLS -
- - When enabled, allows automatic cleanup of -
- hosts that have not communicated with Fleet in -
- the number of days specified in the{" "} - - Host expiry -
- window -
{" "} - setting.{" "} - - (Default: Off) - - - } - > - Host expiry -
- {enableHostExpiry && ( - - )} - - When enabled, allows automatic cleanup of audit logs older than - the number of days specified in the{" "} - Audit log retention window setting. - - (Default: Off) - - - } - > - Delete activities - - {deleteActivities && ( - - )} - - When enabled, disables the ability to run live queries
- (ad hoc queries executed via the UI or fleetctl).{" "} - - (Default: Off) - - - } - > - Disable live queries -
- - Disabling script execution will block access to run scripts. -
- Scripts may still be added and removed in the UI and API. -
- - (Default: Off) - - - } - helpText="Features that run scripts under-the-hood (e.g. software install, lock/wipe) will still be available." - > - Disable script execution features -
- - When enabled, disables AI features such as pre-filling forms -
- with descriptions generated by a large language model -
- (LLM).{" "} - - (Default: Off) - - - } - helpText="If enabled, only policy queries (SQL) are sent to the LLM. Fleet doesn’t use this data to train models." - > - Disable generative AI features -
- + error={formErrors.domain} + tooltip={ <> - Disabling query reports will decrease database usage,
- but will prevent you from accessing query results in + If you need to specify a HELO domain,
+ you can do it here{" "} + + (Default: Blank) + + + } + /> + + Turn this off (not recommended)
+ if you use a self-signed certificate{" "} + +
+ (Default: On) +
+ + } + > + Verify SSL certs +
+ + Detects if STARTTLS is enabled
+ in your SMTP server and starts
+ to use it.{" "} + + (Default: On) + + + } + > + Enable STARTTLS +
+ + When enabled, allows automatic cleanup of
- Fleet and will delete existing reports. This can also be{" "} + hosts that have not communicated with Fleet in +
+ the number of days specified in the{" "} + + Host expiry +
+ window +
{" "} + setting.{" "} + + (Default: Off) + + + } + > + Host expiry +
+ {enableHostExpiry && ( + + )} + + When enabled, allows automatic cleanup of audit logs older + than the number of days specified in the{" "} + Audit log retention window setting. + + (Default: Off) + + + } + > + Delete activities + + {deleteActivities && ( + + )} + + When enabled, disables the ability to run live queries
+ (ad hoc queries executed via the UI or fleetctl).{" "} + + (Default: Off) + + + } + > + Disable live queries +
+ + Disabling script execution will block access to run scripts. +
+ Scripts may still be added and removed in the UI and API.
- disabled on a per-query basis by enabling "Discard
- data".{" "} (Default: Off) - - } - helpText="Enabling this setting will delete all existing query reports in Fleet." - > - Disable query reports -
- + } + helpText="Features that run scripts under-the-hood (e.g. software install, lock/wipe) will still be available." + > + Disable script execution features +
+ + When enabled, disables AI features such as pre-filling forms +
+ with descriptions generated by a large language model +
+ (LLM).{" "} + + (Default: Off) + + + } + helpText="If enabled, only policy queries (SQL) are sent to the LLM. Fleet doesn’t use this data to train models." + > + Disable generative AI features +
+ + <> + Disabling query reports will decrease database usage,
+ but will prevent you from accessing query results in +
+ Fleet and will delete existing reports. This can also be{" "} +
+ disabled on a per-query basis by enabling "Discard{" "} +
+ data".{" "} + + (Default: Off) + + + + } + helpText="Enabling this setting will delete all existing query reports in Fleet." + > + Disable query reports +
+
+ ( + + )} + />
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Agents/Agents.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Agents/Agents.tsx index 27843a822d..0b758d91b8 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/Agents/Agents.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/Agents/Agents.tsx @@ -12,6 +12,7 @@ import InfoBanner from "components/InfoBanner/InfoBanner"; import YamlAce from "components/YamlAce"; import CustomLink from "components/CustomLink"; import SectionHeader from "components/SectionHeader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IAppConfigFormProps } from "../constants"; @@ -31,6 +32,8 @@ const Agents = ({ isPremiumTier, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; + const { ADMIN_TEAMS } = paths; const [formData, setFormData] = useState({ @@ -114,16 +117,22 @@ const Agents = ({ parseTarget error={formErrors.agent_options} label="YAML" + disabled={gitOpsModeEnabled} + /> + ( + + )} /> - diff --git a/frontend/pages/admin/OrgSettingsPage/cards/FleetDesktop/FleetDesktop.tsx b/frontend/pages/admin/OrgSettingsPage/cards/FleetDesktop/FleetDesktop.tsx index ff25207baf..ed19821c13 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/FleetDesktop/FleetDesktop.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/FleetDesktop/FleetDesktop.tsx @@ -5,8 +5,9 @@ import Button from "components/buttons/Button"; import InputField from "components/forms/fields/InputField"; import validUrl from "components/forms/validators/valid_url"; import SectionHeader from "components/SectionHeader"; - +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import CustomLink from "components/CustomLink"; + import { DEFAULT_TRANSPARENCY_URL, IAppConfigFormProps, @@ -27,6 +28,8 @@ const FleetDesktop = ({ isPremiumTier, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; + const [formData, setFormData] = useState({ transparencyUrl: appConfig.fleet_desktop?.transparency_url || DEFAULT_TRANSPARENCY_URL, @@ -92,16 +95,22 @@ const FleetDesktop = ({ onBlur={validateForm} error={formErrors.transparency_url} placeholder="https://fleetdm.com/transparency" + disabled={gitOpsModeEnabled} + /> + ( + + )} /> - diff --git a/frontend/pages/admin/OrgSettingsPage/cards/GlobalHostStatusWebhook/GlobalHostStatusWebhook.tsx b/frontend/pages/admin/OrgSettingsPage/cards/GlobalHostStatusWebhook/GlobalHostStatusWebhook.tsx index 59ac0bd3cf..4cc718162a 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/GlobalHostStatusWebhook/GlobalHostStatusWebhook.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/GlobalHostStatusWebhook/GlobalHostStatusWebhook.tsx @@ -17,6 +17,7 @@ import Dropdown from "components/forms/fields/Dropdown"; import InputField from "components/forms/fields/InputField"; import validUrl from "components/forms/validators/valid_url"; import SectionHeader from "components/SectionHeader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IAppConfigFormProps, IFormField } from "../constants"; @@ -38,6 +39,7 @@ const GlobalHostStatusWebhook = ({ handleSubmit, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; const [ showHostStatusWebhookPreviewModal, setShowHostStatusWebhookPreviewModal, @@ -145,95 +147,106 @@ const GlobalHostStatusWebhook = ({

Send an alert if a portion of your hosts go offline.

- - Enable host status webhook - -

- A request will be sent to your configured Destination URL if - the configured Percentage of hosts have not checked into - Fleet for the configured Number of days. -

- - {enableHostStatusWebhook && ( - <> - - Provide a URL to deliver
- the webhook request to. - - } - /> - - Select the minimum percentage of hosts that -
- must fail to check into Fleet in order to trigger -
- the webhook request. - - } - /> - - Select the minimum number of days that the -
- configured Percentage of hosts must fail to -
- check into Fleet in order to trigger the -
- webhook request. - - } - /> - - )} - + + Enable host status webhook + +

+ A request will be sent to your configured Destination URL{" "} + if the configured Percentage of hosts have not checked into + Fleet for the configured Number of days. +

+ + {enableHostStatusWebhook && ( + <> + + Provide a URL to deliver
+ the webhook request to. + + } + /> + + Select the minimum percentage of hosts that +
+ must fail to check into Fleet in order to trigger +
+ the webhook request. + + } + /> + + Select the minimum number of days that the +
+ configured Percentage of hosts must fail to +
+ check into Fleet in order to trigger the +
+ webhook request. + + } + /> + + )} + + ( + + )} + /> {showHostStatusWebhookPreviewModal && ( diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Info/Info.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Info/Info.tsx index 7b3ae33fdc..20256db23b 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/Info/Info.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/Info/Info.tsx @@ -7,6 +7,7 @@ import InputField from "components/forms/fields/InputField"; import OrgLogoIcon from "components/icons/OrgLogoIcon"; import validUrl from "components/forms/validators/valid_url"; import SectionHeader from "components/SectionHeader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IAppConfigFormProps, IFormField } from "../constants"; @@ -34,6 +35,8 @@ const Info = ({ handleSubmit, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; + const [formData, setFormData] = useState({ orgName: appConfig.org_info.org_name || "", orgLogoURL: appConfig.org_info.org_logo_url || "", @@ -102,77 +105,89 @@ const Info = ({
- - -
- -
- -
-
-
- -
- -
-
- + + +
+ +
+ +
+
+
+ +
+ +
+
+
+ ( + + )} + /> diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx index eaef7914de..790666d4ee 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx @@ -15,6 +15,7 @@ import validEmail from "components/forms/validators/valid_email"; import EmptyTable from "components/EmptyTable"; import CustomLink from "components/CustomLink"; import SectionHeader from "components/SectionHeader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IAppConfigFormProps, @@ -99,6 +100,7 @@ const Smtp = ({ isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { const { isPremiumTier } = useContext(AppContext); + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; const [formData, setFormData] = useState({ enableSMTP: appConfig.smtp_settings?.enable_smtp || false, @@ -237,89 +239,100 @@ const Smtp = ({ const renderSmtpForm = () => { return (
- - Enable SMTP - - -
- + Enable SMTP + + - + + +
+ + Use SSL/TLS to connect (recommended) + + + If your mail server requires authentication, you need to specify + the authentication type here. +
+
+ No Authentication - Select this if your SMTP is + open. +
+
+ Username & Password - Select this if your SMTP + server requires authentication with a username and password. + + } /> + {renderSmtpSection()} - - Use SSL/TLS to connect (recommended) - - - If your mail server requires authentication, you need to specify - the authentication type here. -
-
- No Authentication - Select this if your SMTP is - open. -
-
- Username & Password - Select this if your SMTP - server requires authentication with a username and password. - - } + ( + + )} /> - {renderSmtpSection()} - ); }; diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx index 74e4a61426..ae5a58ae92 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/Sso/Sso.tsx @@ -7,6 +7,7 @@ import CustomLink from "components/CustomLink"; import InputField from "components/forms/fields/InputField"; import validUrl from "components/forms/validators/valid_url"; import SectionHeader from "components/SectionHeader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants"; import { IAppConfigFormProps, IFormField } from "../constants"; @@ -84,6 +85,8 @@ const Sso = ({ isPremiumTier, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; + const [formData, setFormData] = useState({ enableSso: appConfig.sso_settings.enable_sso ?? false, idpName: appConfig.sso_settings.idp_name ?? "", @@ -163,113 +166,125 @@ const Sso = ({
- - Enable single sign-on - - - - - - - If both Metadata URL and Metadata are specified,{" "} - Metadata URL will be used. - - } - onChange={onInputChange} - name="metadataUrl" - value={metadataUrl} - parseTarget - onBlur={onInputBlur} - error={formErrors.metadata_url} - tooltip="Metadata URL provided by the identity provider." - /> - - Allow SSO login initiated by identity provider - - {isPremiumTier && ( + Enable single sign-on + + + + + + - {" "} - about just-in-time (JIT) user provisioning. + If both Metadata URL and Metadata are specified,{" "} + Metadata URL will be used. } + onChange={onInputChange} + name="metadataUrl" + value={metadataUrl} + parseTarget + onBlur={onInputBlur} + error={formErrors.metadata_url} + tooltip="Metadata URL provided by the identity provider." + /> + - Create user and sync permissions on login + Allow SSO login initiated by identity provider - )} - + {isPremiumTier && ( + + {" "} + about just-in-time (JIT) user provisioning. + + } + > + Create user and sync permissions on login + + )} +
+ ( + + )} + /> diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Statistics/Statistics.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Statistics/Statistics.tsx index 47a4127693..7b65b76910 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/Statistics/Statistics.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/Statistics/Statistics.tsx @@ -5,6 +5,8 @@ import Checkbox from "components/forms/fields/Checkbox"; import SectionHeader from "components/SectionHeader"; import CustomLink from "components/CustomLink"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; + import { IAppConfigFormProps, IFormField } from "../constants"; const baseClass = "app-config-form"; @@ -76,14 +78,20 @@ const Statistics = ({ > Enable usage statistics - + ( + + )} + /> diff --git a/frontend/pages/admin/OrgSettingsPage/cards/WebAddress/WebAddress.tsx b/frontend/pages/admin/OrgSettingsPage/cards/WebAddress/WebAddress.tsx index cbdf6c08a3..f18a9b5948 100644 --- a/frontend/pages/admin/OrgSettingsPage/cards/WebAddress/WebAddress.tsx +++ b/frontend/pages/admin/OrgSettingsPage/cards/WebAddress/WebAddress.tsx @@ -5,6 +5,7 @@ import Button from "components/buttons/Button"; import InputField from "components/forms/fields/InputField"; import validUrl from "components/forms/validators/valid_url"; import SectionHeader from "components/SectionHeader"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IAppConfigFormProps, IFormField } from "../constants"; @@ -23,6 +24,8 @@ const WebAddress = ({ handleSubmit, isUpdatingSettings, }: IAppConfigFormProps): JSX.Element => { + const gitOpsModeEnabled = appConfig.gitops.gitops_mode_enabled; + const [formData, setFormData] = useState({ serverURL: appConfig.server_settings.server_url || "", }); @@ -79,16 +82,22 @@ const WebAddress = ({ onBlur={validateForm} error={formErrors.server_url} tooltip="The base URL of this instance for use in Fleet links." + disabled={gitOpsModeEnabled} + /> + ( + + )} /> - diff --git a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/AgentOptionsPage/AgentOptionsPage.tsx b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/AgentOptionsPage/AgentOptionsPage.tsx index 4fb32891ce..db27cb73b9 100644 --- a/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/AgentOptionsPage/AgentOptionsPage.tsx +++ b/frontend/pages/admin/TeamManagementPage/TeamDetailsWrapper/AgentOptionsPage/AgentOptionsPage.tsx @@ -6,6 +6,8 @@ import { constructErrorString, agentOptionsToYaml } from "utilities/yaml"; import { EMPTY_AGENT_OPTIONS } from "utilities/constants"; import { NotificationContext } from "context/notification"; +import { AppContext } from "context/app"; + import useTeamIdParam from "hooks/useTeamIdParam"; import { IApiError } from "interfaces/errors"; import { ITeam } from "interfaces/team"; @@ -18,6 +20,7 @@ import validateYaml from "components/forms/validators/validate_yaml"; import Button from "components/buttons/Button"; import Spinner from "components/Spinner"; import CustomLink from "components/CustomLink"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; // @ts-ignore import YamlAce from "components/YamlAce"; import { ITeamSubnavProps } from "interfaces/team_subnav"; @@ -29,6 +32,8 @@ const AgentOptionsPage = ({ router, }: ITeamSubnavProps): JSX.Element => { const { renderFlash } = useContext(NotificationContext); + const gitOpsModeEnabled = useContext(AppContext).config?.gitops + .gitops_mode_enabled; const { isRouteOk, teamIdForApi } = useTeamIdParam({ location, @@ -170,15 +175,21 @@ const AgentOptionsPage = ({ parseTarget error={formErrors.agent_options} label="YAML" + disabled={gitOpsModeEnabled} + /> + ( + + )} /> - )} diff --git a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx index 77dc28cf41..70ebb653fe 100644 --- a/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/ManagePoliciesPage.tsx @@ -892,7 +892,7 @@ const ManagePolicyPage = ({ config?.integrations.google_calendar.length > 0) ?? false; - const gomEnabled = config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; const isCalEventsEnabled = teamConfig?.integrations.google_calendar?.enable_calendar_events ?? false; @@ -1057,6 +1057,7 @@ const ManagePolicyPage = ({ isUpdating={isUpdatingPolicies} onExit={toggleOtherWorkflowsModal} onSubmit={onUpdateOtherWorkflows} + gitOpsModeEnabled={gitOpsModeEnabled} /> )} {showAddPolicyModal && ( @@ -1081,6 +1082,7 @@ const ManagePolicyPage = ({ onSubmit={onUpdatePolicySoftwareInstall} isUpdating={isUpdatingPolicies} policies={policiesAvailableToAutomate} + gitOpsModeEnabled={gitOpsModeEnabled} // currentTeamId will at this point be present teamId={currentTeamId ?? 0} /> @@ -1091,6 +1093,7 @@ const ManagePolicyPage = ({ onSubmit={onUpdatePolicyRunScript} isUpdating={isUpdatingPolicies} policies={policiesAvailableToAutomate} + gitOpsModeEnabled={gitOpsModeEnabled} // currentTeamId will at this point be present teamId={currentTeamId ?? 0} /> @@ -1104,7 +1107,7 @@ const ManagePolicyPage = ({ url={teamConfig?.integrations.google_calendar?.webhook_url || ""} policies={policiesAvailableToAutomate} isUpdating={isUpdatingPolicies} - gomEnabled={gomEnabled} + gitOpsModeEnabled={gitOpsModeEnabled} /> )} diff --git a/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tests.tsx b/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tests.tsx index 6d55f8febe..94fcd6aaae 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tests.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tests.tsx @@ -112,7 +112,7 @@ describe("CalendarEventsModal - component", () => { enabled url="https://server.com/example" policies={testGlobalPolicies} - gomEnabled + gitOpsModeEnabled /> ); diff --git a/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tsx index 279c40130e..7cbb277a5a 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/CalendarEventsModal/CalendarEventsModal.tsx @@ -40,7 +40,7 @@ interface ICalendarEventsModal { enabled: boolean; url: string; policies: IPolicyStats[]; - gomEnabled?: boolean; + gitOpsModeEnabled?: boolean; } // allows any policy name to be the name of a form field, one of the checkboxes @@ -54,7 +54,7 @@ const CalendarEventsModal = ({ enabled, url, policies, - gomEnabled = false, + gitOpsModeEnabled = false, }: ICalendarEventsModal) => { const { isGlobalAdmin, isTeamAdmin } = useContext(AppContext); @@ -206,7 +206,7 @@ const CalendarEventsModal = ({ onChange={() => { onPolicyEnabledChange({ name, value: !isChecked }); }} - disabled={!formData.enabled || gomEnabled} + disabled={!formData.enabled || gitOpsModeEnabled} > @@ -276,7 +276,7 @@ const CalendarEventsModal = ({ onChange={onFeatureEnabledChange} inactiveText="Disabled" activeText="Enabled" - disabled={gomEnabled} + disabled={gitOpsModeEnabled} /> diff --git a/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx index 66e2faa46e..760d10f206 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/InstallSoftwareModal.tsx @@ -3,6 +3,8 @@ import React, { useCallback, useMemo, useState } from "react"; import { useQuery } from "react-query"; import { omit } from "lodash"; +import classnames from "classnames"; + import { IPolicyStats } from "interfaces/policy"; import { CommaSeparatedPlatformString, @@ -29,6 +31,7 @@ import { InstallableSoftwareSource, ISoftwareTitle, } from "interfaces/software"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; const SOFTWARE_TITLE_LIST_LENGTH = 1000; @@ -63,6 +66,7 @@ interface IInstallSoftwareModal { isUpdating: boolean; policies: IPolicyStats[]; teamId: number; + gitOpsModeEnabled?: boolean; } const generateSoftwareOptionHelpText = (title: IEnhancedSoftwareTitle) => { @@ -91,6 +95,7 @@ const InstallSoftwareModal = ({ isUpdating, policies, teamId, + gitOpsModeEnabled = false, }: IInstallSoftwareModal) => { const [formData, setFormData] = useState( policies.map((policy) => ({ @@ -251,14 +256,18 @@ const InstallSoftwareModal = ({ swIdToInstall, } = policy; + const classes = classnames(`${baseClass}__policy-row`, "policy-row", { + "policy-row--disabled-by-gitops-mode": gitOpsModeEnabled, + }); return (
  • { onChangeEnableInstallSoftware({ @@ -326,16 +335,22 @@ const InstallSoftwareModal = ({
    - + ( + + )} + /> diff --git a/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/_styles.scss index 89b7d4bd15..61296e06fd 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/_styles.scss +++ b/frontend/pages/policies/ManagePoliciesPage/components/InstallSoftwareModal/_styles.scss @@ -24,6 +24,9 @@ height: 40px; padding-top: 4px; padding-bottom: 4px; + &--disabled-by-gitops-mode { + @include disabled; + } } &__no-software { diff --git a/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx index d593493aeb..ce7e707548 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/OtherWorkflowsModal.tsx @@ -27,6 +27,8 @@ import validUrl from "components/forms/validators/valid_url"; import RevealButton from "components/buttons/RevealButton"; import CustomLink from "components/CustomLink"; import TooltipTruncatedText from "components/TooltipTruncatedText"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; + import ExampleTicket from "../ExampleTicket"; import ExamplePayload from "../ExamplePayload"; @@ -40,6 +42,7 @@ interface IOtherWorkflowsModalProps { webhook_settings: Pick; integrations: IGlobalIntegrations | ITeamIntegrations; }) => void; + gitOpsModeEnabled?: boolean; } interface ICheckedPolicy { @@ -100,6 +103,7 @@ const OtherWorkflowsModal = ({ isUpdating, onExit, onSubmit, + gitOpsModeEnabled = false, }: IOtherWorkflowsModalProps): JSX.Element => { const { webhook_settings: { failing_policies_webhook: webhook }, @@ -300,7 +304,7 @@ const OtherWorkflowsModal = ({ helpText='For each policy, Fleet will send a JSON payload to this URL with a list of the hosts that updated their answer to "No."' placeholder="https://server.com/example" tooltip="Provide a URL to deliver a webhook request to." - disabled={!isPolicyAutomationsEnabled} + disabled={!isPolicyAutomationsEnabled || gitOpsModeEnabled} />
    {isWebhookEnabled ? renderWebhook() : renderIntegrations()} @@ -420,7 +426,11 @@ const OtherWorkflowsModal = ({ const { isChecked, name, id } = policyItem; return (
    @@ -432,7 +442,9 @@ const OtherWorkflowsModal = ({ !isChecked && setErrors((errs) => omit(errs, "policyItems")); }} - disabled={!isPolicyAutomationsEnabled} + disabled={ + !isPolicyAutomationsEnabled || gitOpsModeEnabled + } > @@ -459,15 +471,20 @@ const OtherWorkflowsModal = ({

    - + ( + + )} + /> diff --git a/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/_styles.scss index 39a4cea020..0dff3df85e 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/_styles.scss +++ b/frontend/pages/policies/ManagePoliciesPage/components/OtherWorkflowsModal/_styles.scss @@ -16,6 +16,12 @@ } } + .policy-row { + &--disabled-by-gitops-mode { + @include disabled; + } + } + &__no-integrations a { display: block; } diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx index 630d06332a..cb6cf1da05 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx @@ -275,8 +275,12 @@ const generateTableHeaders = ( position="right" tipOffset={8} fixedPositionStrategy - renderChildren={(dC) => ( - + renderChildren={(disableChildren) => ( + )} /> ); @@ -299,8 +303,12 @@ const generateTableHeaders = ( position="right" tipOffset={8} fixedPositionStrategy - renderChildren={(dC) => ( - + renderChildren={(disableChildren) => ( + )} /> ); diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx index 947a30bff9..a14fbfdf7b 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/PolicyRunScriptModal.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useState } from "react"; import { useQuery } from "react-query"; import { omit } from "lodash"; +import classnames from "classnames"; + import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants"; import scriptsAPI, { @@ -22,6 +24,7 @@ import CustomLink from "components/CustomLink"; import Button from "components/buttons/Button"; import Modal from "components/Modal"; import TooltipWrapper from "components/TooltipWrapper"; +import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; const baseClass = "policy-run-script-modal"; @@ -45,6 +48,7 @@ interface IPolicyRunScriptModal { isUpdating: boolean; policies: IPolicyStats[]; teamId: number; + gitOpsModeEnabled?: boolean; } const PolicyRunScriptModal = ({ @@ -53,6 +57,7 @@ const PolicyRunScriptModal = ({ isUpdating, policies, teamId, + gitOpsModeEnabled = false, }: IPolicyRunScriptModal) => { const [formData, setFormData] = useState( policies.map((policy) => ({ @@ -137,15 +142,19 @@ const PolicyRunScriptModal = ({ scriptIdToRun, } = policy; + const classes = classnames(`${baseClass}__policy-row`, "policy-row", { + "policy-row--disabled-by-gitops-mode": gitOpsModeEnabled, + }); return (
  • { onChangeEnableRunScript({ policyId, @@ -228,16 +237,20 @@ const PolicyRunScriptModal = ({
    - + ( + + )} + /> diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/_styles.scss b/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/_styles.scss index 38d2a6579e..68ca1f616c 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/_styles.scss +++ b/frontend/pages/policies/ManagePoliciesPage/components/PolicyRunScriptModal/_styles.scss @@ -24,6 +24,9 @@ height: 40px; padding-top: 4px; padding-bottom: 4px; + &--disabled-by-gitops-mode { + @include disabled; + } } &__no-scripts { @@ -35,7 +38,7 @@ justify-content: center; font-size: $small; - div { + div { color: $ui-fleet-black-75; font-size: $xx-small; a { diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx index b06a37ee7f..4c960bd8e0 100644 --- a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx +++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx @@ -132,7 +132,7 @@ const PolicyForm = ({ const disabledLiveQuery = config?.server_settings.live_query_disabled; const aiFeaturesDisabled = config?.server_settings.ai_features_disabled || false; - const gomEnabled = config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; const debounceSQL = useDebouncedCallback((sql: string) => { const { errors: newErrors } = validateQuerySQL(sql); @@ -149,7 +149,9 @@ const PolicyForm = ({ } = platformCompatibility; const platformSelectorDisabled = - isFetchingAutofillDescription || isFetchingAutofillResolution || gomEnabled; + isFetchingAutofillDescription || + isFetchingAutofillResolution || + gitOpsModeEnabled; const platformSelector = usePlatformSelector( lastEditedQueryPlatform, @@ -364,9 +366,9 @@ const PolicyForm = ({ { + renderChildren={(disableChildren) => { const classes = classnames(policyNameWrapperClasses, { - [`${policyNameWrapperBase}--gitops-mode-disabled`]: dC, + [`${policyNameWrapperBase}--disabled-by-gitops-mode`]: disableChildren, }); return (
    { + renderChildren={(disableChildren) => { const classes = classnames(policyDescriptionWrapperClasses, { - [`${policyDescriptionWrapperBase}--gitops-mode-disabled`]: dC, + [`${policyDescriptionWrapperBase}--disabled-by-gitops-mode`]: disableChildren, }); return (
    { + renderChildren={(disableChildren) => { const classes = classnames(policyResolutionWrapperClasses, { - [`${policyResolutionWrapperBase}--gitops-mode-disabled`]: dC, + [`${policyResolutionWrapperBase}--disabled-by-gitops-mode`]: disableChildren, }); return (
    setLastEditedQueryCritical(value)} value={lastEditedQueryCritical} isLeftLabel - disabled={gomEnabled} + disabled={gitOpsModeEnabled} > {renderPlatformCompatibility()} {isEditMode && platformSelector.render()} @@ -638,7 +640,7 @@ const PolicyForm = ({
    {hasSavePermissions && ( ( + renderChildren={(disableChildren) => ( // TODO - update to use TooltipWrapper <> diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss b/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss index 8068797283..40a1005ec4 100644 --- a/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss +++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss @@ -85,7 +85,7 @@ } } } - &--gitops-mode-disabled { + &--disabled-by-gitops-mode { @include disabled; } } diff --git a/frontend/pages/queries/ManageQueriesPage/components/ManageQueryAutomationsModal/ManageQueryAutomationsModal.tsx b/frontend/pages/queries/ManageQueriesPage/components/ManageQueryAutomationsModal/ManageQueryAutomationsModal.tsx index abc6312126..597cd8b8cd 100644 --- a/frontend/pages/queries/ManageQueriesPage/components/ManageQueryAutomationsModal/ManageQueryAutomationsModal.tsx +++ b/frontend/pages/queries/ManageQueriesPage/components/ManageQueryAutomationsModal/ManageQueryAutomationsModal.tsx @@ -72,7 +72,8 @@ const ManageQueryAutomationsModal = ({ // TODO: Error handling, if any // const [errors, setErrors] = useState<{ [key: string]: string }>({}); - const gomEnabled = useContext(AppContext).config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = useContext(AppContext).config?.gitops + .gitops_mode_enabled; // Client side sort queries alphabetically const sortedAvailableQueries = @@ -141,7 +142,7 @@ const ManageQueryAutomationsModal = ({ // !isChecked && // setErrors((errs) => omit(errs, "queryItems")); }} - disabled={gomEnabled} + disabled={gitOpsModeEnabled} > @@ -192,14 +193,14 @@ const ManageQueryAutomationsModal = ({
    ( + renderChildren={(disableChildren) => ( diff --git a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx index c74f5f86c7..b2208e632d 100644 --- a/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx +++ b/frontend/pages/queries/ManageQueriesPage/components/QueriesTable/QueriesTableConfig.tsx @@ -298,8 +298,12 @@ const generateColumnConfigs = ({ position="right" tipOffset={8} fixedPositionStrategy - renderChildren={(dC) => ( - + renderChildren={(disableChildren) => ( + )} /> ); @@ -323,8 +327,12 @@ const generateColumnConfigs = ({ position="right" tipOffset={8} fixedPositionStrategy - renderChildren={(dC) => ( - + renderChildren={(disableChildren) => ( + )} /> ); diff --git a/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tsx b/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tsx index 9ec239ab28..57f38a93c4 100644 --- a/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tsx +++ b/frontend/pages/queries/edit/components/EditQueryForm/EditQueryForm.tsx @@ -172,7 +172,7 @@ const EditQueryForm = ({ const savedQueryMode = !!queryIdForEdit; const disabledLiveQuery = config?.server_settings.live_query_disabled; - const gomEnabled = config?.gitops.gitops_mode_enabled; + const gitOpsModeEnabled = config?.gitops.gitops_mode_enabled; const [errors, setErrors] = useState<{ [key: string]: any }>({}); // string | null | undefined or boolean | undefined // NOTE: SaveQueryModal is only being used to create a new query in this component. @@ -510,9 +510,9 @@ const EditQueryForm = ({ { + renderChildren={(disableChildren) => { const classes = classnames(queryNameWrapperClasses, { - [`${queryNameWrapperClass}--gitops-mode-disabled`]: dC, + [`${queryNameWrapperClass}--disabled-by-gitops-mode`]: disableChildren, }); return (
    { + renderChildren={(disableChildren) => { const classes = classnames(queryDescriptionWrapperClasses, { - [`${queryDescriptionWrapperClass}--gitops-mode-disabled`]: dC, + [`${queryDescriptionWrapperClass}--disabled-by-gitops-mode`]: disableChildren, }); return (
    {savedQueryMode && ( ( + renderChildren={(disableChildren) => (