From 3c7c4025e9e635d871e2dde4d401c7d45c41b594 Mon Sep 17 00:00:00 2001 From: Joe Grant Date: Fri, 7 Aug 2026 15:43:58 -0500 Subject: [PATCH] Refactor script help text --- .../cards/ScriptLibrary/ScriptLibrary.tsx | 46 +++++++++++++-- .../EditScriptModal/EditScriptModal.tsx | 12 ---- .../ScriptDetailsModal/RunScriptHelpText.tsx | 56 ------------------- .../ScriptDetailsModal/ScriptDetailsModal.tsx | 40 ++----------- .../HostDetailsPage/HostDetailsPage.tsx | 41 +++++++------- .../ScriptModalGroup/ScriptModalGroup.tsx | 3 - 6 files changed, 65 insertions(+), 133 deletions(-) delete mode 100644 frontend/pages/hosts/components/ScriptDetailsModal/RunScriptHelpText.tsx diff --git a/frontend/pages/ManageControlsPage/Scripts/cards/ScriptLibrary/ScriptLibrary.tsx b/frontend/pages/ManageControlsPage/Scripts/cards/ScriptLibrary/ScriptLibrary.tsx index a899bc5d840..8672cabc6a3 100644 --- a/frontend/pages/ManageControlsPage/Scripts/cards/ScriptLibrary/ScriptLibrary.tsx +++ b/frontend/pages/ManageControlsPage/Scripts/cards/ScriptLibrary/ScriptLibrary.tsx @@ -9,6 +9,7 @@ import { AxiosError } from "axios"; import { useQuery } from "react-query"; import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants"; +import { getPathWithQueryParams } from "utilities/url"; import PATHS from "router/paths"; import { AppContext } from "context/app"; @@ -28,6 +29,7 @@ import PageDescription from "components/PageDescription"; import EmptyState from "components/EmptyState"; import Button from "components/buttons/Button"; import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; +import CustomLink from "components/CustomLink"; import UploadList from "../../../../../components/UploadList"; import DeleteScriptModal from "../../components/DeleteScriptModal"; @@ -49,11 +51,23 @@ const ScriptLibrary = ({ router, teamId, location }: IScriptLibraryProps) => { ? parseInt(location.query.page, 10) : DEFAULT_PAGE; - const { isPremiumTier, isGlobalTechnician, isTeamTechnician } = useContext( - AppContext - ); + const { + isPremiumTier, + isGlobalTechnician, + isTeamTechnician, + isGlobalAdmin, + isAnyTeamAdmin, + isGlobalMaintainer, + isAnyTeamMaintainer, + } = useContext(AppContext); - const isTechnician = isGlobalTechnician || isTeamTechnician; + const isTechnician = !!isGlobalTechnician || !!isTeamTechnician; + const canRunScripts = !!( + isGlobalAdmin || + isAnyTeamAdmin || + isGlobalMaintainer || + isAnyTeamMaintainer + ); const [showDeleteScriptModal, setShowDeleteScriptModal] = useState(false); const [showEditScriptModal, setShowEditScriptModal] = useState(false); @@ -196,6 +210,12 @@ const ScriptLibrary = ({ router, teamId, location }: IScriptLibraryProps) => { ); const canUploadScripts = !isTechnician; + const hostsUrl = getPathWithQueryParams(PATHS.MANAGE_HOSTS, { + fleet_id: teamId, + }); + const policiesUrl = getPathWithQueryParams(PATHS.MANAGE_POLICIES, { + fleet_id: teamId, + }); return (
@@ -203,7 +223,23 @@ const ScriptLibrary = ({ router, teamId, location }: IScriptLibraryProps) => {
+ A collection of scripts for configuring and remediating hosts. +
+ To manually run a script, go to the{" "} + page, select one or + more hosts, and click Run script. + {!isTechnician && canRunScripts && ( + <> +
+ To automatically run a script across hosts that match a policy + condition, create a policy automation on the{" "} + page. + + )} + + } /> {canUploadScripts && ( { const { - currentTeam, isGlobalAdmin, isAnyTeamAdmin, isGlobalMaintainer, isAnyTeamMaintainer, - isTeamTechnician, - isGlobalTechnician, } = useContext(AppContext); const { gitOpsModeEnabled } = useGitOpsMode(); - const isTechnician = !!isTeamTechnician || !!isGlobalTechnician; - const canRunScripts = !!( isGlobalAdmin || isAnyTeamAdmin || @@ -198,12 +192,6 @@ const EditScriptModal = ({ value={scriptFormData} readOnly={gitOpsModeEnabled} /> - {canRunScripts && ( { - const hostsUrl = getPathWithQueryParams(paths.MANAGE_HOSTS, { - fleet_id: teamId, - }); - - if (isTechnician) { - return ( -
- To run this script on a host, go to the{" "} - page and select a host. Then, - click Actions > Run script. -
- ); - } - - return ( -
- To run this script on a host, go to the{" "} - page and select a host. - {canRunScripts && ( - <> -
- To run the script across multiple hosts, add a policy automation on - the{" "} - {" "} - page. - - )} -
- ); -}; - -export default RunScriptHelpText; diff --git a/frontend/pages/hosts/components/ScriptDetailsModal/ScriptDetailsModal.tsx b/frontend/pages/hosts/components/ScriptDetailsModal/ScriptDetailsModal.tsx index 8f3d283b826..abb7618e685 100644 --- a/frontend/pages/hosts/components/ScriptDetailsModal/ScriptDetailsModal.tsx +++ b/frontend/pages/hosts/components/ScriptDetailsModal/ScriptDetailsModal.tsx @@ -25,8 +25,6 @@ import { generateActionDropdownOptions } from "pages/hosts/details/HostDetailsPa import GitOpsModeTooltipWrapper from "components/GitOpsModeTooltipWrapper"; import { IPaginatedListScript } from "pages/hosts/ManageHostsPage/components/RunScriptBatchPaginatedList/RunScriptBatchPaginatedList"; -import RunScriptHelpText from "./RunScriptHelpText"; - const baseClass = "script-details-modal"; type PartialOrFullHostScript = @@ -38,7 +36,6 @@ interface IScriptDetailsModalProps { /** optional onClose to allow both "go back" behavior and "close" behavior depending on context */ onClose?: () => void; onDelete?: () => void; - runScriptHelpText?: boolean; showHostScriptActions?: boolean; onClickRun?: (script: IHostScript) => void; hostTeamId?: number | null; @@ -49,7 +46,6 @@ interface IScriptDetailsModalProps { isScriptContentError?: Error | null; isHidden?: boolean; onClickRunDetails?: (scriptExecutionId: string) => void; - teamIdForApi?: number; suppressSecondaryActions?: boolean; customPrimaryButtons?: React.ReactNode; } @@ -59,7 +55,6 @@ const ScriptDetailsModal = ({ onClose, onDelete, onClickRun, - runScriptHelpText = false, showHostScriptActions = false, hostTeamId, selectedScriptId, @@ -69,7 +64,6 @@ const ScriptDetailsModal = ({ isScriptContentError, isHidden = false, onClickRunDetails, - teamIdForApi, suppressSecondaryActions = false, customPrimaryButtons, }: IScriptDetailsModalProps) => { @@ -84,24 +78,7 @@ const ScriptDetailsModal = ({ } }; - const { - currentUser, - isGlobalAdmin, - isAnyTeamAdmin, - isGlobalMaintainer, - isAnyTeamMaintainer, - isTeamTechnician, - isGlobalTechnician, - } = useContext(AppContext); - - const isTechnician = !!isTeamTechnician || !!isGlobalTechnician; - - const canRunScripts = !!( - isGlobalAdmin || - isAnyTeamAdmin || - isGlobalMaintainer || - isAnyTeamMaintainer - ); + const { currentUser } = useContext(AppContext); // handle multiple possibilities for `selectedScriptDetails` let scriptId: number | null = null; @@ -124,7 +101,7 @@ const ScriptDetailsModal = ({ () => scriptId ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - scriptAPI.downloadScript(scriptId) + scriptAPI.downloadScript(scriptId) : Promise.resolve(null), { refetchOnWindowFocus: false, @@ -143,9 +120,8 @@ const ScriptDetailsModal = ({ try { const content = selectedScriptContent || scriptContent || ""; const formatDate = format(new Date(), "yyyy-MM-dd"); - const filename = `${formatDate} ${ - selectedScriptDetails?.name || "Script details" - }`; + const filename = `${formatDate} ${selectedScriptDetails?.name || "Script details" + }`; const file = new File([content], filename); FileSaver.saveAs(file); } catch { @@ -268,14 +244,6 @@ const ScriptDetailsModal = ({ - {runScriptHelpText && ( - - )}
); }; diff --git a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx index e11d759560d..7f3667bf46a 100644 --- a/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx +++ b/frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx @@ -1094,7 +1094,7 @@ const HostDetailsPage = ({ isManagedLocalAccountEnabled={ host.platform === "windows" ? mdmConfig?.windows_settings?.managed_local_account_settings - ?.enabled ?? false + ?.enabled ?? false : mdmConfig?.macos_setup?.enable_managed_local_account ?? false } managedAccountStatus={ @@ -1212,22 +1212,22 @@ const HostDetailsPage = ({ // Only include Reports for supported platforms ...(showReportsTab ? [ - { - name: "Reports", - title: "reports", - pathname: PATHS.HOST_REPORTS(hostIdFromURL), - }, - ] + { + name: "Reports", + title: "reports", + pathname: PATHS.HOST_REPORTS(hostIdFromURL), + }, + ] : []), ...(showPoliciesTab ? [ - { - name: "Policies", - title: "policies", - pathname: PATHS.HOST_POLICIES(hostIdFromURL), - count: failingPoliciesCount, - }, - ] + { + name: "Policies", + title: "policies", + pathname: PATHS.HOST_POLICIES(hostIdFromURL), + count: failingPoliciesCount, + }, + ] : []), ]; @@ -1560,7 +1560,7 @@ const HostDetailsPage = ({ activeActivityTab === "past" ? pastActivitiesIsFetching || pastMDMCommandsIsFetching : upcomingActivitiesIsFetching || - upcomingMDMCommandsIsFetching + upcomingMDMCommandsIsFetching } isError={ activeActivityTab === "past" @@ -1741,7 +1741,6 @@ const HostDetailsPage = ({ host={host} currentUser={currentUser} onCloseScriptModalGroup={onCloseScriptModalGroup} - teamIdForApi={currentTeam?.id} /> )} {!!host && showTransferHostModal && ( @@ -1772,9 +1771,9 @@ const HostDetailsPage = ({ if (host.mdm.enrollment_status === "Pending") { router.push( filteredHostsPath || - getPathWithQueryParams(PATHS.MANAGE_HOSTS, { - fleet_id: location.query.fleet_id, - }) + getPathWithQueryParams(PATHS.MANAGE_HOSTS, { + fleet_id: location.query.fleet_id, + }) ); return; } @@ -1934,8 +1933,8 @@ const HostDetailsPage = ({ ); const timeAgoText = result.updated_at ? ` (${timeAgo(new Date(result.updated_at), { - addSuffix: true, - })})` + addSuffix: true, + })})` : ""; return ( void; - teamIdForApi?: number; } enum ModalGroupOption { @@ -38,7 +37,6 @@ const ScriptModalGroup = ({ currentUser, host, onCloseScriptModalGroup, - teamIdForApi, }: IScriptsProps) => { const [previousModal, setPreviousModal] = useState( null @@ -222,7 +220,6 @@ const ScriptModalGroup = ({ isScriptContentError={isSelectedScriptContentError} isHidden={currentModal !== ModalGroupOption.ViewScriptDetails} showHostScriptActions - teamIdForApi={teamIdForApi} />