From 64d23817ada6b866d0927e17afc11c6a060a8775 Mon Sep 17 00:00:00 2001 From: jacobshandling <61553566+jacobshandling@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:51:02 -0700 Subject: [PATCH] UI: Make more specific and move down a level gating of Setup Experience UX to facilitate appropriate granular access to Linux and Android features (#32754) ## For #32683 - Gate Setup experience steps for MDM and ABM being enabled at the individual sidenav level instead of the entire section - Allow Linux software installation even when MDM/ABM not enabled - Improve typing of sidenav ### Setup experience > Install software > Linux can be accessed without MDM/ABM, but not macOS: ![ezgif-1c8bb8d13011ea](https://github.com/user-attachments/assets/56ffdbc5-2b49-4263-9483-0ebfc1b2754f) ### Other setup experience tabs gated without MDM/ABM configured (note specific conditions for End user authentication - Apple MDM OR Android MDM, with informative Tooltips: ![ezgif-1d194f6b298edd](https://github.com/user-attachments/assets/79450034-b278-46e9-9089-330c126336f3) - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually --------- Co-authored-by: Jacob Shandling --- .../SetupExperience/SetupExperience.tsx | 40 +------ .../SetupExperienceNavItems.tsx | 12 +- .../SetupExperience/_styles.scss | 23 +--- .../BootstrapPackage.tests.tsx | 105 +++++++++++++---- .../BootstrapPackage/BootstrapPackage.tsx | 57 +++++---- .../EndUserAuthentication.tsx | 63 ++++++---- .../EndUserAuthForm/EndUserAuthForm.tsx | 20 +++- .../cards/InstallSoftware/InstallSoftware.tsx | 29 +++-- .../cards/InstallSoftware/_styles.scss | 2 + .../cards/RunScript/RunScript.tests.tsx | 56 ++++++++- .../cards/RunScript/RunScript.tsx | 50 +++++--- .../cards/SetupAssistant/SetupAssistant.tsx | 108 +++++++++++------- .../SetupExperience/helpers.ts | 15 +++ .../handlers/setup-experience-handlers.ts | 2 +- 14 files changed, 380 insertions(+), 202 deletions(-) create mode 100644 frontend/pages/ManageControlsPage/SetupExperience/helpers.ts diff --git a/frontend/pages/ManageControlsPage/SetupExperience/SetupExperience.tsx b/frontend/pages/ManageControlsPage/SetupExperience/SetupExperience.tsx index 5adfd2fc83..41e9e6f323 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/SetupExperience.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/SetupExperience.tsx @@ -1,37 +1,15 @@ import React, { useContext } from "react"; -import PATHS from "router/paths"; import { InjectedRouter, Params } from "react-router/lib/Router"; import { AppContext } from "context/app"; import SideNav from "pages/admin/components/SideNav"; -import Button from "components/buttons/Button/Button"; import PremiumFeatureMessage from "components/PremiumFeatureMessage"; -import EmptyTable from "components/EmptyTable"; import SETUP_EXPERIENCE_NAV_ITEMS from "./SetupExperienceNavItems"; -import TurnOnMdmMessage from "../../../components/TurnOnMdmMessage"; const baseClass = "setup-experience"; -interface ISetupEmptyState { - router: InjectedRouter; -} - -const SetupEmptyState = ({ router }: ISetupEmptyState) => { - const onClickEmptyConnect = () => { - router.push(PATHS.ADMIN_INTEGRATIONS_MDM); - }; - - return ( - Connect} - /> - ); -}; - interface ISetupExperienceProps { params: Params; location: { search: string }; @@ -46,7 +24,7 @@ const SetupExperience = ({ teamIdForApi, }: ISetupExperienceProps) => { const { section } = params; - const { isPremiumTier, config } = useContext(AppContext); + const { isPremiumTier } = useContext(AppContext); // Not premium shows premium message if (!isPremiumTier) { @@ -57,22 +35,6 @@ const SetupExperience = ({ ); } - // MDM is not on so show messaging for user to enable it. - if (!config?.mdm.enabled_and_configured) { - return ( - - ); - } - // User has not set up Apple Business Manager. - if (!config?.mdm.apple_bm_enabled_and_configured) { - return ; - } - const DEFAULT_SETTINGS_SECTION = SETUP_EXPERIENCE_NAV_ITEMS[0]; const currentFormSection = diff --git a/frontend/pages/ManageControlsPage/SetupExperience/SetupExperienceNavItems.tsx b/frontend/pages/ManageControlsPage/SetupExperience/SetupExperienceNavItems.tsx index c5a103b738..79047de0d9 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/SetupExperienceNavItems.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/SetupExperienceNavItems.tsx @@ -1,5 +1,7 @@ import PATHS from "router/paths"; +import { InjectedRouter } from "react-router"; + import { ISideNavItem } from "pages/admin/components/SideNav/SideNav"; import EndUserAuthentication from "./cards/EndUserAuthentication/EndUserAuthentication"; @@ -8,14 +10,12 @@ import SetupAssistant from "./cards/SetupAssistant"; import InstallSoftware from "./cards/InstallSoftware"; import RunScript from "./cards/RunScript"; -interface ISetupExperienceCardProps { - currentTeamId?: number; +export interface ISetupExperienceCardProps { + currentTeamId: number; + router: InjectedRouter; } -// TODO: types -const SETUP_EXPERIENCE_NAV_ITEMS: ISideNavItem< - ISetupExperienceCardProps | any ->[] = [ +const SETUP_EXPERIENCE_NAV_ITEMS: ISideNavItem[] = [ { title: "1. End user authentication", urlSection: "end-user-auth", diff --git a/frontend/pages/ManageControlsPage/SetupExperience/_styles.scss b/frontend/pages/ManageControlsPage/SetupExperience/_styles.scss index 6f14c3db7e..e0d38ca10f 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/_styles.scss +++ b/frontend/pages/ManageControlsPage/SetupExperience/_styles.scss @@ -8,23 +8,10 @@ margin-top: 80px; } - &__empty-state { - margin-top: $pad-xxlarge; - - text-align: center; - - h2 { - font-size: $small; - margin: 0 0 $pad-xsmall; - } - - p { - font-size: $x-small; - margin: 0 0 $pad-medium; - } - - button { - margin: 0 auto; - } + .turn-on-mdm-message { + @include tab-empty-state; + max-width: none; + padding: 64px 170px; + margin: 0; } } diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tests.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tests.tsx index 458666b856..1caac11880 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tests.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tests.tsx @@ -1,10 +1,10 @@ import React from "react"; -import { screen } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; -import { createCustomRenderer } from "test/test-utils"; +import { createCustomRenderer, createMockRouter } from "test/test-utils"; import mockServer from "test/mock-server"; import { - createSetupExperienceBootstrapPackageHandler, + createSetupExperienceBootstrapMetadataHandler, createSetupExperienceScriptHandler, createSetupExperienceSoftwareHandler, createSetuUpExperienceBootstrapSummaryHandler, @@ -16,6 +16,7 @@ import { createMockSoftwarePackage, createMockSoftwareTitle, } from "__mocks__/softwareMock"; +import { createMockMdmConfig } from "__mocks__/configMock"; import BootstrapPackage from "./BootstrapPackage"; @@ -23,7 +24,7 @@ import BootstrapPackage from "./BootstrapPackage"; * sets up some default backend mocks for the tests. Override what you need * with mockServer.use() in the test itself. */ -const setuDefaultBackendMocks = () => { +const setupDefaultBackendMocks = () => { mockServer.use(createGetConfigHandler()); // default is no run script or install software already added @@ -32,7 +33,7 @@ const setuDefaultBackendMocks = () => { // default will be a bootstrap package already uploaded mockServer.use( - createSetupExperienceBootstrapPackageHandler({ name: "foo-package.pkg" }) + createSetupExperienceBootstrapMetadataHandler({ name: "foo-package.pkg" }) ); mockServer.use( createSetuUpExperienceBootstrapSummaryHandler({ @@ -44,11 +45,55 @@ const setuDefaultBackendMocks = () => { }; describe("BootstrapPackage", () => { - it("renders the status table and bootstrap package if a package has been uploaded", async () => { - setuDefaultBackendMocks(); + it("renders the 'turn on automatic enrollment' message when MDM isn't configured", async () => { + setupDefaultBackendMocks(); + mockServer.use( + createGetConfigHandler({ + mdm: createMockMdmConfig({ enabled_and_configured: false }), + }) + ); + const render = createCustomRenderer({ + withBackendMock: true, + }); - const render = createCustomRenderer({ withBackendMock: true }); - render(); + render(); + + await waitFor(() => { + expect( + screen.getByText(/turn on automatic enrollment/) + ).toBeInTheDocument(); + }); + }); + it("renders the 'turn on automatic enrollment' message when MDM is configured, but ABM is not", async () => { + setupDefaultBackendMocks(); + mockServer.use( + createGetConfigHandler({ + mdm: createMockMdmConfig({ + enabled_and_configured: true, + apple_bm_enabled_and_configured: false, + }), + }) + ); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + render(); + + await waitFor(() => { + expect( + screen.getByText(/turn on automatic enrollment/) + ).toBeInTheDocument(); + }); + }); + it("renders the status table and bootstrap package if a package has been uploaded", async () => { + setupDefaultBackendMocks(); + + const render = createCustomRenderer({ + withBackendMock: true, + }); + + render(); await screen.findByText(/status/gi); @@ -61,11 +106,14 @@ describe("BootstrapPackage", () => { }); it("render the bootstrap package uploader if a package has not been uploaded", async () => { - setuDefaultBackendMocks(); + setupDefaultBackendMocks(); mockServer.use(errorNoBootstrapPackageMetadataHandler); - const render = createCustomRenderer({ withBackendMock: true }); - render(); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + render(); await screen.findByText(/Upload a bootstrap package/gi); @@ -82,11 +130,16 @@ describe("BootstrapPackage", () => { }); it("renders the advanced options as disabled if there is no bootstrap package uploaded", async () => { - setuDefaultBackendMocks(); + setupDefaultBackendMocks(); mockServer.use(errorNoBootstrapPackageMetadataHandler); - const render = createCustomRenderer({ withBackendMock: true }); - const { user } = render(); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + const { user } = render( + + ); await screen.findByText("Show advanced options"); await user.click(screen.getByText("Show advanced options")); @@ -98,7 +151,7 @@ describe("BootstrapPackage", () => { }); it("renders the advanced options as disabled if there are already added install software", async () => { - setuDefaultBackendMocks(); + setupDefaultBackendMocks(); mockServer.use( createSetupExperienceSoftwareHandler({ software_titles: [ @@ -111,8 +164,13 @@ describe("BootstrapPackage", () => { }) ); - const render = createCustomRenderer({ withBackendMock: true }); - const { user } = render(); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + const { user } = render( + + ); await screen.findByText("Show advanced options"); await user.click(screen.getByText("Show advanced options")); @@ -124,11 +182,16 @@ describe("BootstrapPackage", () => { }); it("renders the advanced options as disabled if there is alreaddy a run script added", async () => { - setuDefaultBackendMocks(); + setupDefaultBackendMocks(); mockServer.use(createSetupExperienceScriptHandler()); - const render = createCustomRenderer({ withBackendMock: true }); - const { user } = render(); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + const { user } = render( + + ); await screen.findByText("Show advanced options"); await user.click(screen.getByText("Show advanced options")); diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tsx index 01d5a3d386..0167f4676b 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/BootstrapPackage/BootstrapPackage.tsx @@ -17,6 +17,7 @@ import { NotificationContext } from "context/notification"; import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants"; import Spinner from "components/Spinner"; +import TurnOnMdmMessage from "components/TurnOnMdmMessage"; import SectionHeader from "components/SectionHeader"; import BootstrapPackagePreview from "./components/BootstrapPackagePreview"; @@ -26,6 +27,8 @@ import DeleteBootstrapPackageModal from "./components/DeleteBootstrapPackageModa import BootstrapAdvancedOptions from "./components/BootstrapAdvancedOptions"; import SetupExperienceContentContainer from "../../components/SetupExperienceContentContainer"; import { getInstallSoftwareDuringSetupCount } from "../InstallSoftware/components/AddInstallSoftware/helpers"; +import { ISetupExperienceCardProps } from "../../SetupExperienceNavItems"; +import getManualAgentInstallSetting from "../../helpers"; const baseClass = "bootstrap-package"; @@ -33,22 +36,10 @@ const baseClass = "bootstrap-package"; // available for install so we can correctly display the selected count. const PER_PAGE_SIZE = 3000; -export const getManualAgentInstallSetting = ( - currentTeamId: number, - globalConfig?: IConfig, - teamConfig?: ITeamConfig -) => { - if (currentTeamId === API_NO_TEAM_ID) { - return globalConfig?.mdm.macos_setup.manual_agent_install || false; - } - return teamConfig?.mdm?.macos_setup.manual_agent_install || false; -}; - -interface IBootstrapPackageProps { - currentTeamId: number; -} - -const BootstrapPackage = ({ currentTeamId }: IBootstrapPackageProps) => { +const BootstrapPackage = ({ + currentTeamId, + router, +}: ISetupExperienceCardProps) => { const { renderFlash } = useContext(NotificationContext); const [ selectedManualAgentInstall, @@ -87,6 +78,7 @@ const BootstrapPackage = ({ currentTeamId }: IBootstrapPackageProps) => { ); const { + data: globalConfig, isLoading: isLoadingGlobalConfig, refetch: refetchGlobalConfig, } = useQuery( @@ -94,11 +86,12 @@ const BootstrapPackage = ({ currentTeamId }: IBootstrapPackageProps) => { () => configAPI.loadAll(), { ...DEFAULT_USE_QUERY_OPTIONS, - enabled: currentTeamId === API_NO_TEAM_ID, onSuccess: (data) => { - setSelectedManualAgentInstall( - getManualAgentInstallSetting(currentTeamId, data) - ); + if (currentTeamId === API_NO_TEAM_ID) { + setSelectedManualAgentInstall( + getManualAgentInstallSetting(currentTeamId, data) + ); + } }, } ); @@ -211,10 +204,32 @@ const BootstrapPackage = ({ currentTeamId }: IBootstrapPackageProps) => { isLoadingScript || isLoadingSoftware; + const renderContent = () => { + if (isLoading) { + return ; + } + if ( + !( + globalConfig?.mdm.enabled_and_configured && + globalConfig?.mdm.apple_bm_enabled_and_configured + ) + ) { + return ( + + ); + } + return renderBootstrapView(); + }; + return (
- {isLoading ? : renderBootstrapView()} + {renderContent()} {showDeleteBootstrapPackageModal && ( { +}: ISetupExperienceCardProps) => { const { data: globalConfig, isLoading: isLoadingGlobalConfig } = useQuery< IConfig, Error @@ -80,27 +76,48 @@ const EndUserAuthentication = ({ ); const onClickConnect = () => { - router.push(PATHS.ADMIN_INTEGRATIONS_MDM); + router.push(PATHS.ADMIN_INTEGRATIONS_IDENTITY_PROVIDER); + }; + + const renderContent = () => { + if (!globalConfig || isLoadingGlobalConfig || isLoadingTeamConfig) { + return ; + } + const mdmConfig = globalConfig.mdm; + if ( + !( + mdmConfig.enabled_and_configured || + mdmConfig.android_enabled_and_configured + ) + ) { + return ( + + ); + } + return ( + + {!isIdPConfigured(mdmConfig) ? ( + + ) : ( + + )} + + + ); }; return (
- {isLoadingGlobalConfig || isLoadingTeamConfig ? ( - - ) : ( - - {!globalConfig || !isIdPConfigured(globalConfig.mdm) ? ( - - ) : ( - - )} - - - )} + {renderContent()}
); }; diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/EndUserAuthentication/components/EndUserAuthForm/EndUserAuthForm.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/EndUserAuthentication/components/EndUserAuthForm/EndUserAuthForm.tsx index 49e2ceada9..c1a402647d 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/EndUserAuthentication/components/EndUserAuthForm/EndUserAuthForm.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/EndUserAuthentication/components/EndUserAuthForm/EndUserAuthForm.tsx @@ -10,9 +10,19 @@ import Checkbox from "components/forms/fields/Checkbox"; import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { NotificationContext } from "context/notification"; import { AppContext } from "context/app"; +import TooltipWrapper from "components/TooltipWrapper"; const baseClass = "end-user-auth-form"; +const getTooltipCopy = (android = false) => { + return ( + <> + {android ? "Android" : "Apple"} MDM must be turned on in Settings{" "} + > Integrations > Mobile Device Management(MDM) to turn + on end user authentication. + + ); +}; interface IEndUserAuthFormProps { currentTeamId: number; defaultIsEndUserAuthEnabled: boolean; @@ -64,7 +74,15 @@ const EndUserAuthForm = ({

Require end users to authenticate with your identity provider (IdP) and agree to an end user license agreement (EULA) when they setup - their new macOS, iOS, iPadOS and Android hosts.{" "} + their new{" "} + macOS,{" "} + iOS,{" "} + iPadOS{" "} + and{" "} + + Android + {" "} + hosts.{" "} View IdP{" "} and EULA.

diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/InstallSoftware.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/InstallSoftware.tsx index 2d8e13eb5d..d5ecf28da5 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/InstallSoftware.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/InstallSoftware/InstallSoftware.tsx @@ -20,12 +20,14 @@ import Spinner from "components/Spinner"; import TabNav from "components/TabNav"; import TabText from "components/TabText"; import CustomLink from "components/CustomLink"; +import TurnOnMdmMessage from "components/TurnOnMdmMessage"; import InstallSoftwarePreview from "./components/InstallSoftwarePreview"; import AddInstallSoftware from "./components/AddInstallSoftware"; import SelectSoftwareModal from "./components/SelectSoftwareModal"; import SetupExperienceContentContainer from "../../components/SetupExperienceContentContainer"; -import { getManualAgentInstallSetting } from "../BootstrapPackage/BootstrapPackage"; +import { ISetupExperienceCardProps } from "../../SetupExperienceNavItems"; +import getManualAgentInstallSetting from "../../helpers"; const baseClass = "install-software"; @@ -41,11 +43,10 @@ export const PLATFORM_BY_INDEX: SetupExperiencePlatform[] = [ "linux", ]; -interface IInstallSoftwareProps { - currentTeamId: number; -} - -const InstallSoftware = ({ currentTeamId }: IInstallSoftwareProps) => { +const InstallSoftware = ({ + currentTeamId, + router, +}: ISetupExperienceCardProps) => { const [showSelectSoftwareModal, setShowSelectSoftwareModal] = useState(false); const [ selectedPlatform, @@ -81,7 +82,6 @@ const InstallSoftware = ({ currentTeamId }: IInstallSoftwareProps) => { Error >(["config", currentTeamId], () => configAPI.loadAll(), { ...DEFAULT_USE_QUERY_OPTIONS, - enabled: currentTeamId === API_NO_TEAM_ID, }); const { data: teamConfig, isLoading: isLoadingTeamConfig } = useQuery< @@ -130,6 +130,21 @@ const InstallSoftware = ({ currentTeamId }: IInstallSoftwareProps) => { } if (softwareTitles || softwareTitles === null) { + const appleMdmAndAbmEnabled = + globalConfig?.mdm.enabled_and_configured && + globalConfig?.mdm.apple_bm_enabled_and_configured; + + // TODO - incorporate Windows MDM condition when implementing Windows software install on setup + if (platform === "macos" && !appleMdmAndAbmEnabled) { + return ( + + ); + } return ( { + it("should render the 'turn on automatic enrollment' message when MDM isn't configured", async () => { + mockServer.use(errorNoSetupExperienceScriptHandler); + mockServer.use( + createGetConfigHandler({ + mdm: createMockMdmConfig({ enabled_and_configured: false }), + }) + ); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + render(); + + expect( + await screen.getByText(/turn on automatic enrollment/) + ).toBeInTheDocument(); + }); + it("should render the 'turn on automatic enrollment' message when MDM is configured but not ABM", async () => { + mockServer.use(errorNoSetupExperienceScriptHandler); + mockServer.use( + createGetConfigHandler({ + mdm: createMockMdmConfig({ + enabled_and_configured: true, + apple_bm_enabled_and_configured: false, + }), + }) + ); + const render = createCustomRenderer({ + withBackendMock: true, + }); + + render(); + + expect( + await screen.getByText(/turn on automatic enrollment/) + ).toBeInTheDocument(); + }); it("should render the script uploader when no script has been uploaded", async () => { mockServer.use(errorNoSetupExperienceScriptHandler); - const render = createCustomRenderer({ withBackendMock: true }); + mockServer.use(createGetConfigHandler()); + const render = createCustomRenderer({ + withBackendMock: true, + }); - render(); + render(); expect(await screen.findByRole("button", { name: "Upload" })).toBeVisible(); }); it("should render the uploaded script uploader when a script has been uploaded", async () => { mockServer.use(createSetupExperienceScriptHandler()); - const render = createCustomRenderer({ withBackendMock: true }); + mockServer.use(createGetConfigHandler()); + const render = createCustomRenderer({ + withBackendMock: true, + }); - render(); + render(); expect( await screen.findByText("Script will run during setup:") diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tsx index 2f4eb5c6da..26f13139bf 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/RunScript/RunScript.tsx @@ -6,6 +6,7 @@ import { DEFAULT_USE_QUERY_OPTIONS, LEARN_MORE_ABOUT_BASE_LINK, } from "utilities/constants"; + import mdmAPI, { IGetSetupExperienceScriptResponse, } from "services/entities/mdm"; @@ -18,28 +19,26 @@ import SectionHeader from "components/SectionHeader"; import DataError from "components/DataError"; import Spinner from "components/Spinner"; import CustomLink from "components/CustomLink"; +import TurnOnMdmMessage from "components/TurnOnMdmMessage"; import SetupExperiencePreview from "./components/SetupExperienceScriptPreview"; import SetupExperienceScriptUploader from "./components/SetupExperienceScriptUploader"; import SetupExperienceScriptCard from "./components/SetupExperienceScriptCard"; import DeleteSetupExperienceScriptModal from "./components/DeleteSetupExperienceScriptModal"; import SetupExperienceContentContainer from "../../components/SetupExperienceContentContainer"; -import { getManualAgentInstallSetting } from "../BootstrapPackage/BootstrapPackage"; +import { ISetupExperienceCardProps } from "../../SetupExperienceNavItems"; +import getManualAgentInstallSetting from "../../helpers"; const baseClass = "run-script"; -interface IRunScriptProps { - currentTeamId: number; -} - -const RunScript = ({ currentTeamId }: IRunScriptProps) => { +const RunScript = ({ currentTeamId, router }: ISetupExperienceCardProps) => { const [showDeleteScriptModal, setShowDeleteScriptModal] = useState(false); const { data: script, error: scriptError, isLoading, - isError, + isError: isScriptError, refetch: refetchScript, remove: removeScriptFromCache, } = useQuery( @@ -53,7 +52,6 @@ const RunScript = ({ currentTeamId }: IRunScriptProps) => { Error >(["config", currentTeamId], () => configAPI.loadAll(), { ...DEFAULT_USE_QUERY_OPTIONS, - enabled: currentTeamId === API_NO_TEAM_ID, }); const { data: teamConfig, isLoading: isLoadingTeamConfig } = useQuery< @@ -87,7 +85,23 @@ const RunScript = ({ currentTeamId }: IRunScriptProps) => { ; } - if (isError && scriptError.status !== 404) { + if ( + !( + globalConfig?.mdm.enabled_and_configured && + globalConfig?.mdm.apple_bm_enabled_and_configured + ) + ) { + return ( + + ); + } + + if (isScriptError && scriptError.status !== 404) { return ; } @@ -123,6 +137,14 @@ const RunScript = ({ currentTeamId }: IRunScriptProps) => { )} + {showDeleteScriptModal && script && ( + setShowDeleteScriptModal(false)} + /> + )} ); }; @@ -130,15 +152,7 @@ const RunScript = ({ currentTeamId }: IRunScriptProps) => { return (
- <>{renderContent()} - {showDeleteScriptModal && script && ( - setShowDeleteScriptModal(false)} - /> - )} + {renderContent()}
); }; diff --git a/frontend/pages/ManageControlsPage/SetupExperience/cards/SetupAssistant/SetupAssistant.tsx b/frontend/pages/ManageControlsPage/SetupExperience/cards/SetupAssistant/SetupAssistant.tsx index 924fe5cb27..109d2ad689 100644 --- a/frontend/pages/ManageControlsPage/SetupExperience/cards/SetupAssistant/SetupAssistant.tsx +++ b/frontend/pages/ManageControlsPage/SetupExperience/cards/SetupAssistant/SetupAssistant.tsx @@ -14,6 +14,7 @@ import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants"; import SectionHeader from "components/SectionHeader"; import Spinner from "components/Spinner"; import CustomLink from "components/CustomLink"; +import TurnOnMdmMessage from "components/TurnOnMdmMessage"; import SetupAssistantPreview from "./components/SetupAssistantPreview"; import SetupAssistantProfileUploader from "./components/SetupAssistantProfileUploader"; @@ -21,14 +22,14 @@ import SetupAssistantProfileCard from "./components/SetupAssistantProfileCard/Se import DeleteAutoEnrollmentProfile from "./components/DeleteAutoEnrollmentProfile"; import AdvancedOptionsForm from "./components/AdvancedOptionsForm"; import SetupExperienceContentContainer from "../../components/SetupExperienceContentContainer"; +import { ISetupExperienceCardProps } from "../../SetupExperienceNavItems"; const baseClass = "setup-assistant"; -interface ISetupAssistantProps { - currentTeamId: number; -} - -const SetupAssistant = ({ currentTeamId }: ISetupAssistantProps) => { +const SetupAssistant = ({ + currentTeamId, + router, +}: ISetupExperienceCardProps) => { const [showDeleteProfileModal, setShowDeleteProfileModal] = useState(false); const { data: globalConfig, isLoading: isLoadingGlobalConfig } = useQuery< @@ -37,7 +38,6 @@ const SetupAssistant = ({ currentTeamId }: ISetupAssistantProps) => { >(["config", currentTeamId], () => configAPI.loadAll(), { ...DEFAULT_USE_QUERY_OPTIONS, retry: false, - enabled: currentTeamId === API_NO_TEAM_ID, }); const { data: teamConfig, isLoading: isLoadingTeamConfig } = useQuery< @@ -90,45 +90,69 @@ const SetupAssistant = ({ currentTeamId }: ISetupAssistantProps) => { isLoadingGlobalConfig || isLoadingTeamConfig || isLoadingEnrollmentProfile; const enrollmentProfileNotFound = enrollmentProfileError?.status === 404; + const renderSetupAssistantView = () => { + return ( + +
+

+ Add an automatic enrollment profile to customize the macOS Setup + Assistant.{" "} + +

+ {enrollmentProfileNotFound || !enrollmentProfileData ? ( + + ) : ( + setShowDeleteProfileModal(true)} + /> + )} + +
+
+ +
+
+ ); + }; + + const renderContent = () => { + if (isLoading) { + return ; + } + if ( + !( + globalConfig?.mdm.enabled_and_configured && + globalConfig?.mdm.apple_bm_enabled_and_configured + ) + ) { + return ( + + ); + } + return renderSetupAssistantView(); + }; + return (
- {isLoading ? ( - - ) : ( - -
-

- Add an automatic enrollment profile to customize the macOS Setup - Assistant.{" "} - -

- {enrollmentProfileNotFound || !enrollmentProfileData ? ( - - ) : ( - setShowDeleteProfileModal(true)} - /> - )} - -
-
- -
-
- )} + {renderContent()} {showDeleteProfileModal && ( { + if (currentTeamId === API_NO_TEAM_ID) { + return globalConfig?.mdm.macos_setup.manual_agent_install || false; + } + return teamConfig?.mdm?.macos_setup.manual_agent_install || false; +}; + +export default getManualAgentInstallSetting; diff --git a/frontend/test/handlers/setup-experience-handlers.ts b/frontend/test/handlers/setup-experience-handlers.ts index ab8d5158af..80e5555239 100644 --- a/frontend/test/handlers/setup-experience-handlers.ts +++ b/frontend/test/handlers/setup-experience-handlers.ts @@ -53,7 +53,7 @@ export const createSetupExperienceSoftwareHandler = ( ); }); -export const createSetupExperienceBootstrapPackageHandler = ( +export const createSetupExperienceBootstrapMetadataHandler = ( overrides?: Partial ) => http.get(setupExperienceBootstrapMetadataUrl, () => {