From 7e8a729d9468f3fe2be29584984aabcda1ae1eb9 Mon Sep 17 00:00:00 2001 From: Rachel Perkins Date: Thu, 9 Apr 2026 09:41:34 -0400 Subject: [PATCH 1/8] Update to use fleet_id --- .../components/PoliciesTable/PoliciesTableConfig.tsx | 2 +- .../policies/PolicyDetailsPage/PolicyDetailsPage.tsx | 8 ++++---- .../pages/policies/PolicyPage/screens/QueryEditor.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx index 05a9ee54e6b..e7723d6c78f 100644 --- a/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx +++ b/frontend/pages/policies/ManagePoliciesPage/components/PoliciesTable/PoliciesTableConfig.tsx @@ -132,7 +132,7 @@ const generateTableHeaders = ( } path={getPathWithQueryParams(PATHS.POLICY_DETAILS(id), { - team_id, + fleet_id: team_id, })} /> ); diff --git a/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx b/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx index 89955b19120..9bb5f7b41c1 100644 --- a/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx +++ b/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx @@ -35,7 +35,7 @@ interface IPolicyDetailsPageProps { location: { pathname: string; search: string; - query: { team_id?: string }; + query: { fleet_id?: string }; }; } @@ -187,7 +187,7 @@ const PolicyDetailsPage = ({ const disabledLiveQuery = config?.server_settings.live_query_disabled; const backToPoliciesPath = getPathWithQueryParams(PATHS.MANAGE_POLICIES, { - team_id: teamIdForApi, + fleet_id: teamIdForApi, }); const renderAuthor = (): JSX.Element | null => { @@ -332,7 +332,7 @@ const PolicyDetailsPage = ({ `${getPathWithQueryParams( PATHS.EDIT_POLICY(policyId), { - team_id: teamIdForApi, + fleet_id: teamIdForApi, } )}#targets` ); @@ -348,7 +348,7 @@ const PolicyDetailsPage = ({ policyId && router.push( getPathWithQueryParams(PATHS.EDIT_POLICY(policyId), { - team_id: teamIdForApi, + fleet_id: teamIdForApi, }) ); }} diff --git a/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx b/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx index 7fce523513c..41390649e05 100644 --- a/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx +++ b/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx @@ -261,7 +261,7 @@ const QueryEditor = ({ const backPath = policyIdForEdit ? getPathWithQueryParams(PATHS.POLICY_DETAILS(policyIdForEdit), { - team_id: teamIdForApi, + fleet_id: teamIdForApi, }) : backToPoliciesPath(); From afc5b6ec826e686b0060b387b29ff3938abcc26b Mon Sep 17 00:00:00 2001 From: Rachel Perkins Date: Thu, 9 Apr 2026 10:10:47 -0400 Subject: [PATCH 2/8] Update API call to include team policies, fix dependency array --- .../PolicyDetailsPage/PolicyDetailsPage.tsx | 64 +++++++++++-------- .../components/PolicyForm/PolicyForm.tsx | 14 +++- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx b/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx index 9bb5f7b41c1..e9c281766d3 100644 --- a/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx +++ b/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx @@ -11,6 +11,7 @@ import { ILabelPolicy } from "interfaces/label"; import { API_ALL_TEAMS_ID, APP_CONTEXT_ALL_TEAMS_ID } from "interfaces/team"; import { PLATFORM_DISPLAY_NAMES, Platform } from "interfaces/platform"; import globalPoliciesAPI from "services/entities/global_policies"; +import teamPoliciesAPI from "services/entities/team_policies"; import teamsAPI, { ILoadTeamResponse } from "services/entities/teams"; import { addGravatarUrlToResource } from "utilities/helpers"; import { DOCUMENT_TITLE_SUFFIX } from "utilities/constants"; @@ -108,34 +109,41 @@ const PolicyDetailsPage = ({ IStoredPolicyResponse, Error, IPolicy - >(["policy", policyId], () => globalPoliciesAPI.load(policyId as number), { - enabled: isRouteOk && !!policyId, - refetchOnWindowFocus: false, - retry: false, - select: (data: IStoredPolicyResponse) => data.policy, - onSuccess: (returnedPolicy) => { - setLastEditedQueryId(returnedPolicy.id); - setLastEditedQueryName(returnedPolicy.name); - setLastEditedQueryDescription(returnedPolicy.description); - setLastEditedQueryBody(returnedPolicy.query); - setLastEditedQueryResolution(returnedPolicy.resolution); - setLastEditedQueryCritical(returnedPolicy.critical); - setLastEditedQueryPlatform(returnedPolicy.platform); - setLastEditedQueryLabelsIncludeAny( - returnedPolicy.labels_include_any || [] - ); - setLastEditedQueryLabelsExcludeAny( - returnedPolicy.labels_exclude_any || [] - ); - const deNulledTeamId = returnedPolicy.team_id ?? undefined; - setPolicyTeamId( - deNulledTeamId === API_ALL_TEAMS_ID - ? APP_CONTEXT_ALL_TEAMS_ID - : deNulledTeamId - ); - }, - onError: (error) => handlePageError(error), - }); + >( + ["policy", policyId, teamIdForApi], + () => + teamIdForApi && teamIdForApi > 0 + ? teamPoliciesAPI.load(teamIdForApi, policyId as number) + : globalPoliciesAPI.load(policyId as number), + { + enabled: isRouteOk && !!policyId, + refetchOnWindowFocus: false, + retry: false, + select: (data: IStoredPolicyResponse) => data.policy, + onSuccess: (returnedPolicy) => { + setLastEditedQueryId(returnedPolicy.id); + setLastEditedQueryName(returnedPolicy.name); + setLastEditedQueryDescription(returnedPolicy.description); + setLastEditedQueryBody(returnedPolicy.query); + setLastEditedQueryResolution(returnedPolicy.resolution); + setLastEditedQueryCritical(returnedPolicy.critical); + setLastEditedQueryPlatform(returnedPolicy.platform); + setLastEditedQueryLabelsIncludeAny( + returnedPolicy.labels_include_any || [] + ); + setLastEditedQueryLabelsExcludeAny( + returnedPolicy.labels_exclude_any || [] + ); + const deNulledTeamId = returnedPolicy.team_id ?? undefined; + setPolicyTeamId( + deNulledTeamId === API_ALL_TEAMS_ID + ? APP_CONTEXT_ALL_TEAMS_ID + : deNulledTeamId + ); + }, + onError: (error) => handlePageError(error), + } + ); const { data: teamData } = useQuery( ["team", teamIdForApi], diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx index c3e334119bc..4cf202a43e9 100644 --- a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx +++ b/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx @@ -271,7 +271,19 @@ const PolicyForm = ({ }) ); } - }, [policyIdForEdit, isTeamMaintainerOrTeamAdmin, isStoredPolicyLoading]); + }, [ + policyIdForEdit, + isEditMode, + isStoredPolicyLoading, + isTeamObserver, + isGlobalObserver, + isTeamTechnician, + isGlobalTechnician, + isOnGlobalTeam, + storedPolicy?.team_id, + router, + teamIdForApi, + ]); useEffect(() => { setSelectedTargetType( From 59697f5ffa2bae724d3875a2ffd38c68849e044b Mon Sep 17 00:00:00 2001 From: Rachel Perkins Date: Thu, 9 Apr 2026 10:38:29 -0400 Subject: [PATCH 3/8] Rearrange files to match queries directory, update /live url to match queries url sub --- frontend/pages/policies/PolicyPage/index.ts | 1 - .../PolicyDetailsPage/PolicyDetailsPage.tsx | 8 +- .../PolicyDetailsPage/_styles.scss | 0 .../{ => details}/PolicyDetailsPage/index.ts | 0 .../EditPolicyPage.tsx} | 87 ++------ frontend/pages/policies/edit/_styles.scss | 39 ++++ .../PolicyAutomations/PolicyAutomations.tsx | 0 .../components/PolicyAutomations/_styles.scss | 0 .../components/PolicyAutomations/index.ts | 0 .../PolicyErrorsTable/PolicyErrorsTable.tsx | 0 .../PolicyErrorsTableConfig.tsx | 0 .../components/PolicyErrorsTable/_styles.scss | 0 .../components/PolicyErrorsTable/index.ts | 0 .../PolicyForm/PolicyForm.tests.tsx | 0 .../components/PolicyForm/PolicyForm.tsx | 0 .../components/PolicyForm/_styles.scss | 2 +- .../components/PolicyForm/index.ts | 0 .../PolicyResults/PolicyResults.tsx | 0 .../components/PolicyResults/_styles.scss | 0 .../components/PolicyResults/helpers.tsx | 0 .../components/PolicyResults/index.ts | 0 .../PolicyResultsTable/PolicyResultsTable.tsx | 0 .../PolicyResultsTableConfig.tsx | 0 .../PolicyResultsTable/_styles.scss | 0 .../components/PolicyResultsTable/index.ts | 0 .../SaveNewPolicyModal.tests.tsx | 0 .../SaveNewPolicyModal/SaveNewPolicyModal.tsx | 0 .../SaveNewPolicyModal/_styles.scss | 0 .../components/SaveNewPolicyModal/index.ts | 0 frontend/pages/policies/edit/index.ts | 1 + .../screens/QueryEditor.tsx | 2 +- .../live/LivePolicyPage/LivePolicyPage.tsx | 205 ++++++++++++++++++ .../LivePolicyPage}/_styles.scss | 36 +-- .../policies/live/LivePolicyPage/index.ts | 1 + .../{PolicyPage => live}/screens/RunQuery.tsx | 2 +- frontend/router/index.tsx | 13 +- frontend/router/paths.ts | 2 + 37 files changed, 280 insertions(+), 119 deletions(-) delete mode 100644 frontend/pages/policies/PolicyPage/index.ts rename frontend/pages/policies/{ => details}/PolicyDetailsPage/PolicyDetailsPage.tsx (98%) rename frontend/pages/policies/{ => details}/PolicyDetailsPage/_styles.scss (100%) rename frontend/pages/policies/{ => details}/PolicyDetailsPage/index.ts (100%) rename frontend/pages/policies/{PolicyPage/PolicyPage.tsx => edit/EditPolicyPage.tsx} (79%) create mode 100644 frontend/pages/policies/edit/_styles.scss rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyAutomations/PolicyAutomations.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyAutomations/_styles.scss (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyAutomations/index.ts (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyErrorsTable/PolicyErrorsTable.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyErrorsTable/PolicyErrorsTableConfig.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyErrorsTable/_styles.scss (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyErrorsTable/index.ts (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyForm/PolicyForm.tests.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyForm/PolicyForm.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyForm/_styles.scss (98%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyForm/index.ts (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResults/PolicyResults.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResults/_styles.scss (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResults/helpers.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResults/index.ts (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResultsTable/PolicyResultsTable.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResultsTable/PolicyResultsTableConfig.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResultsTable/_styles.scss (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/PolicyResultsTable/index.ts (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/SaveNewPolicyModal/SaveNewPolicyModal.tests.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/SaveNewPolicyModal/_styles.scss (100%) rename frontend/pages/policies/{PolicyPage => edit}/components/SaveNewPolicyModal/index.ts (100%) create mode 100644 frontend/pages/policies/edit/index.ts rename frontend/pages/policies/{PolicyPage => edit}/screens/QueryEditor.tsx (99%) create mode 100644 frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx rename frontend/pages/policies/{PolicyPage => live/LivePolicyPage}/_styles.scss (73%) create mode 100644 frontend/pages/policies/live/LivePolicyPage/index.ts rename frontend/pages/policies/{PolicyPage => live}/screens/RunQuery.tsx (98%) diff --git a/frontend/pages/policies/PolicyPage/index.ts b/frontend/pages/policies/PolicyPage/index.ts deleted file mode 100644 index f1da6f2c9b9..00000000000 --- a/frontend/pages/policies/PolicyPage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./PolicyPage"; diff --git a/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx b/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx similarity index 98% rename from frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx rename to frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx index e9c281766d3..be9c59184b1 100644 --- a/frontend/pages/policies/PolicyDetailsPage/PolicyDetailsPage.tsx +++ b/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx @@ -28,7 +28,7 @@ import Spinner from "components/Spinner"; import TooltipWrapper from "components/TooltipWrapper"; import Avatar from "components/Avatar"; import ShowQueryModal from "components/modals/ShowQueryModal"; -import PolicyAutomations from "pages/policies/PolicyPage/components/PolicyAutomations"; +import PolicyAutomations from "pages/policies/edit/components/PolicyAutomations"; interface IPolicyDetailsPageProps { router: InjectedRouter; @@ -337,12 +337,12 @@ const PolicyDetailsPage = ({ onClick={() => { policyId && router.push( - `${getPathWithQueryParams( - PATHS.EDIT_POLICY(policyId), + getPathWithQueryParams( + PATHS.LIVE_POLICY(policyId), { fleet_id: teamIdForApi, } - )}#targets` + ) ); }} disabled={!!disabledLiveQuery} diff --git a/frontend/pages/policies/PolicyDetailsPage/_styles.scss b/frontend/pages/policies/details/PolicyDetailsPage/_styles.scss similarity index 100% rename from frontend/pages/policies/PolicyDetailsPage/_styles.scss rename to frontend/pages/policies/details/PolicyDetailsPage/_styles.scss diff --git a/frontend/pages/policies/PolicyDetailsPage/index.ts b/frontend/pages/policies/details/PolicyDetailsPage/index.ts similarity index 100% rename from frontend/pages/policies/PolicyDetailsPage/index.ts rename to frontend/pages/policies/details/PolicyDetailsPage/index.ts diff --git a/frontend/pages/policies/PolicyPage/PolicyPage.tsx b/frontend/pages/policies/edit/EditPolicyPage.tsx similarity index 79% rename from frontend/pages/policies/PolicyPage/PolicyPage.tsx rename to frontend/pages/policies/edit/EditPolicyPage.tsx index 16b1f1db5a0..61c6c008bd3 100644 --- a/frontend/pages/policies/PolicyPage/PolicyPage.tsx +++ b/frontend/pages/policies/edit/EditPolicyPage.tsx @@ -6,36 +6,30 @@ import { useErrorHandler } from "react-error-boundary"; import { AppContext } from "context/app"; import { PolicyContext } from "context/policy"; import useTeamIdParam from "hooks/useTeamIdParam"; -import { IHost, IHostResponse } from "interfaces/host"; -import { ILabel } from "interfaces/label"; import { IPolicyFormData, IPolicy, IStoredPolicyResponse, } from "interfaces/policy"; -import { ITarget } from "interfaces/target"; import { API_ALL_TEAMS_ID, APP_CONTEXT_ALL_TEAMS_ID, - ITeam, } from "interfaces/team"; import globalPoliciesAPI from "services/entities/global_policies"; import teamPoliciesAPI from "services/entities/team_policies"; import teamsAPI, { ILoadTeamResponse } from "services/entities/teams"; -import hostAPI from "services/entities/hosts"; import statusAPI from "services/entities/status"; -import { DOCUMENT_TITLE_SUFFIX, LIVE_POLICY_STEPS } from "utilities/constants"; +import PATHS from "router/paths"; +import { DOCUMENT_TITLE_SUFFIX } from "utilities/constants"; import { getPathWithQueryParams } from "utilities/url"; import SidePanelPage from "components/SidePanelPage"; import QuerySidePanel from "components/side_panels/QuerySidePanel"; -import QueryEditor from "pages/policies/PolicyPage/screens/QueryEditor"; -import SelectTargets from "components/LiveQuery/SelectTargets"; +import QueryEditor from "pages/policies/edit/screens/QueryEditor"; import MainContent from "components/MainContent"; import SidePanelContent from "components/SidePanelContent"; import Spinner from "components/Spinner/Spinner"; import CustomLink from "components/CustomLink"; -import RunQuery from "pages/policies/PolicyPage/screens/RunQuery"; import { DEFAULT_POLICY } from "pages/policies/constants"; interface IPolicyPageProps { @@ -44,12 +38,11 @@ interface IPolicyPageProps { location: { pathname: string; search: string; - query: { host_ids: string; fleet_id: string }; - hash?: string; + query: { fleet_id: string }; }; } -const baseClass = "policy-page"; +const baseClass = "edit-policy-page"; const PolicyPage = ({ router, @@ -144,14 +137,6 @@ const PolicyPage = ({ }; }, []); - const [step, setStep] = useState( - location.hash === "#targets" ? LIVE_POLICY_STEPS[2] : LIVE_POLICY_STEPS[1] - ); - const [selectedTargets, setSelectedTargets] = useState([]); - const [targetedHosts, setTargetedHosts] = useState([]); - const [targetedLabels, setTargetedLabels] = useState([]); - const [targetedTeams, setTargetedTeams] = useState([]); - const [targetsTotalCount, setTargetsTotalCount] = useState(0); const [isLiveQueryRunnable, setIsLiveQueryRunnable] = useState(true); const [isSidebarOpen, setIsSidebarOpen] = useState(true); const [showOpenSchemaActionText, setShowOpenSchemaActionText] = useState( @@ -203,23 +188,6 @@ const PolicyPage = ({ } ); - useQuery( - "hostFromURL", - () => - hostAPI.loadHostDetails(parseInt(location.query.host_ids as string, 10)), // TODO(sarah): What should happen if this doesn't parse (e.g. the string is "foo")? Also, note that "1,2,3" parses as 1. - { - enabled: isRouteOk && !!location.query.host_ids, - retry: false, - select: (data: IHostResponse) => data.host, - onSuccess: (host) => { - const targets = selectedTargets; - host.target_type = "hosts"; - targets.push(host); - setSelectedTargets([...targets]); - }, - } - ); - /** Pesky bug affecting team level users: - Navigating to policies/:id immediately defaults the user to the first team they're on with the most permissions, in the URL bar because of useTeamIdParam @@ -338,7 +306,7 @@ const PolicyPage = ({ }; const renderScreen = () => { - const step1Opts = { + const queryEditorOpts = { router, baseClass, policyIdForEdit: policyId, @@ -352,51 +320,22 @@ const PolicyPage = ({ storedPolicyError, createPolicy, onOsqueryTableSelect, - goToSelectTargets: () => setStep(LIVE_POLICY_STEPS[2]), + goToSelectTargets: () => + router.push( + getPathWithQueryParams(PATHS.LIVE_POLICY(policyId), { + fleet_id: teamIdForApi, + }) + ), onOpenSchemaSidebar, renderLiveQueryWarning, teamIdForApi, currentAutomatedPolicies, }; - const step2Opts = { - baseClass, - selectedTargets, - targetedHosts, - targetedLabels, - targetedTeams, - targetsTotalCount, - goToQueryEditor: () => setStep(LIVE_POLICY_STEPS[1]), - goToRunQuery: () => setStep(LIVE_POLICY_STEPS[3]), - setSelectedTargets, - setTargetedHosts, - setTargetedLabels, - setTargetedTeams, - setTargetsTotalCount, - isLivePolicy: true, - }; - - const step3Opts = { - selectedTargets, - storedPolicy, - setSelectedTargets, - goToQueryEditor: () => setStep(LIVE_POLICY_STEPS[1]), - targetsTotalCount, - }; - - switch (step) { - case LIVE_POLICY_STEPS[2]: - return ; - case LIVE_POLICY_STEPS[3]: - return ; - default: - return ; - } + return ; }; - const isFirstStep = step === LIVE_POLICY_STEPS[1]; const showSidebar = - isFirstStep && isSidebarOpen && (isGlobalAdmin || isGlobalMaintainer || isAnyTeamMaintainerOrTeamAdmin); diff --git a/frontend/pages/policies/edit/_styles.scss b/frontend/pages/policies/edit/_styles.scss new file mode 100644 index 00000000000..d193b6ab3f1 --- /dev/null +++ b/frontend/pages/policies/edit/_styles.scss @@ -0,0 +1,39 @@ +.edit-policy-page { + @include vertical-page-layout; + + &__form { + @include vertical-form-layout; + } + + &__warning { + padding: $pad-medium; + font-size: $x-small; + color: $core-fleet-black; + background-color: #fff0b9; + border: 1px solid #f2c94c; + border-radius: $border-radius; + + p { + margin: 0; + line-height: 20px; + } + } + + &__observer-query-view { + width: 90%; + max-width: 1060px; + margin: 0 auto; + color: $core-fleet-black; + + h1 { + font-size: $medium; + } + p { + font-size: $x-small; + } + } + + .ace_content { + min-height: 500px !important; + } +} diff --git a/frontend/pages/policies/PolicyPage/components/PolicyAutomations/PolicyAutomations.tsx b/frontend/pages/policies/edit/components/PolicyAutomations/PolicyAutomations.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyAutomations/PolicyAutomations.tsx rename to frontend/pages/policies/edit/components/PolicyAutomations/PolicyAutomations.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyAutomations/_styles.scss b/frontend/pages/policies/edit/components/PolicyAutomations/_styles.scss similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyAutomations/_styles.scss rename to frontend/pages/policies/edit/components/PolicyAutomations/_styles.scss diff --git a/frontend/pages/policies/PolicyPage/components/PolicyAutomations/index.ts b/frontend/pages/policies/edit/components/PolicyAutomations/index.ts similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyAutomations/index.ts rename to frontend/pages/policies/edit/components/PolicyAutomations/index.ts diff --git a/frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/PolicyErrorsTable.tsx b/frontend/pages/policies/edit/components/PolicyErrorsTable/PolicyErrorsTable.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/PolicyErrorsTable.tsx rename to frontend/pages/policies/edit/components/PolicyErrorsTable/PolicyErrorsTable.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/PolicyErrorsTableConfig.tsx b/frontend/pages/policies/edit/components/PolicyErrorsTable/PolicyErrorsTableConfig.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/PolicyErrorsTableConfig.tsx rename to frontend/pages/policies/edit/components/PolicyErrorsTable/PolicyErrorsTableConfig.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/_styles.scss b/frontend/pages/policies/edit/components/PolicyErrorsTable/_styles.scss similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/_styles.scss rename to frontend/pages/policies/edit/components/PolicyErrorsTable/_styles.scss diff --git a/frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/index.ts b/frontend/pages/policies/edit/components/PolicyErrorsTable/index.ts similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyErrorsTable/index.ts rename to frontend/pages/policies/edit/components/PolicyErrorsTable/index.ts diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tests.tsx b/frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tests.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tests.tsx rename to frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tests.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx b/frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyForm/PolicyForm.tsx rename to frontend/pages/policies/edit/components/PolicyForm/PolicyForm.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss b/frontend/pages/policies/edit/components/PolicyForm/_styles.scss similarity index 98% rename from frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss rename to frontend/pages/policies/edit/components/PolicyForm/_styles.scss index 7f29d1a73f6..92a76d8d414 100644 --- a/frontend/pages/policies/PolicyPage/components/PolicyForm/_styles.scss +++ b/frontend/pages/policies/edit/components/PolicyForm/_styles.scss @@ -7,7 +7,7 @@ position: relative; font-size: $x-small; - .policy-page__warning { + .edit-policy-page__warning { margin: 0; margin-bottom: $pad-large; } diff --git a/frontend/pages/policies/PolicyPage/components/PolicyForm/index.ts b/frontend/pages/policies/edit/components/PolicyForm/index.ts similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyForm/index.ts rename to frontend/pages/policies/edit/components/PolicyForm/index.ts diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResults/PolicyResults.tsx b/frontend/pages/policies/edit/components/PolicyResults/PolicyResults.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResults/PolicyResults.tsx rename to frontend/pages/policies/edit/components/PolicyResults/PolicyResults.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResults/_styles.scss b/frontend/pages/policies/edit/components/PolicyResults/_styles.scss similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResults/_styles.scss rename to frontend/pages/policies/edit/components/PolicyResults/_styles.scss diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResults/helpers.tsx b/frontend/pages/policies/edit/components/PolicyResults/helpers.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResults/helpers.tsx rename to frontend/pages/policies/edit/components/PolicyResults/helpers.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResults/index.ts b/frontend/pages/policies/edit/components/PolicyResults/index.ts similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResults/index.ts rename to frontend/pages/policies/edit/components/PolicyResults/index.ts diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResultsTable/PolicyResultsTable.tsx b/frontend/pages/policies/edit/components/PolicyResultsTable/PolicyResultsTable.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResultsTable/PolicyResultsTable.tsx rename to frontend/pages/policies/edit/components/PolicyResultsTable/PolicyResultsTable.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResultsTable/PolicyResultsTableConfig.tsx b/frontend/pages/policies/edit/components/PolicyResultsTable/PolicyResultsTableConfig.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResultsTable/PolicyResultsTableConfig.tsx rename to frontend/pages/policies/edit/components/PolicyResultsTable/PolicyResultsTableConfig.tsx diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResultsTable/_styles.scss b/frontend/pages/policies/edit/components/PolicyResultsTable/_styles.scss similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResultsTable/_styles.scss rename to frontend/pages/policies/edit/components/PolicyResultsTable/_styles.scss diff --git a/frontend/pages/policies/PolicyPage/components/PolicyResultsTable/index.ts b/frontend/pages/policies/edit/components/PolicyResultsTable/index.ts similarity index 100% rename from frontend/pages/policies/PolicyPage/components/PolicyResultsTable/index.ts rename to frontend/pages/policies/edit/components/PolicyResultsTable/index.ts diff --git a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tests.tsx b/frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tests.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tests.tsx rename to frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tests.tsx diff --git a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx b/frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx similarity index 100% rename from frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx rename to frontend/pages/policies/edit/components/SaveNewPolicyModal/SaveNewPolicyModal.tsx diff --git a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/_styles.scss b/frontend/pages/policies/edit/components/SaveNewPolicyModal/_styles.scss similarity index 100% rename from frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/_styles.scss rename to frontend/pages/policies/edit/components/SaveNewPolicyModal/_styles.scss diff --git a/frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/index.ts b/frontend/pages/policies/edit/components/SaveNewPolicyModal/index.ts similarity index 100% rename from frontend/pages/policies/PolicyPage/components/SaveNewPolicyModal/index.ts rename to frontend/pages/policies/edit/components/SaveNewPolicyModal/index.ts diff --git a/frontend/pages/policies/edit/index.ts b/frontend/pages/policies/edit/index.ts new file mode 100644 index 00000000000..b1dc948c5b2 --- /dev/null +++ b/frontend/pages/policies/edit/index.ts @@ -0,0 +1 @@ +export { default } from "./EditPolicyPage"; diff --git a/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx b/frontend/pages/policies/edit/screens/QueryEditor.tsx similarity index 99% rename from frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx rename to frontend/pages/policies/edit/screens/QueryEditor.tsx index 41390649e05..100c3dcfa4d 100644 --- a/frontend/pages/policies/PolicyPage/screens/QueryEditor.tsx +++ b/frontend/pages/policies/edit/screens/QueryEditor.tsx @@ -14,7 +14,7 @@ import { getPathWithQueryParams } from "utilities/url"; import { IPolicyFormData, IPolicy } from "interfaces/policy"; import BackButton from "components/BackButton"; -import PolicyForm from "pages/policies/PolicyPage/components/PolicyForm"; +import PolicyForm from "pages/policies/edit/components/PolicyForm"; import { APP_CONTEXT_ALL_TEAMS_ID } from "interfaces/team"; interface IQueryEditorProps { diff --git a/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx b/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx new file mode 100644 index 00000000000..79123dd8e2f --- /dev/null +++ b/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx @@ -0,0 +1,205 @@ +import React, { useState, useEffect, useContext, useCallback } from "react"; +import { useQuery } from "react-query"; +import { useErrorHandler } from "react-error-boundary"; +import { InjectedRouter, Params } from "react-router/lib/Router"; +import PATHS from "router/paths"; +import useTeamIdParam from "hooks/useTeamIdParam"; + +import { AppContext } from "context/app"; +import { PolicyContext } from "context/policy"; +import { + LIVE_QUERY_STEPS, + DOCUMENT_TITLE_SUFFIX, +} from "utilities/constants"; +import { getPathWithQueryParams } from "utilities/url"; +import globalPoliciesAPI from "services/entities/global_policies"; +import teamPoliciesAPI from "services/entities/team_policies"; +import hostAPI from "services/entities/hosts"; +import { IHost, IHostResponse } from "interfaces/host"; +import { ILabel } from "interfaces/label"; +import { ITeam } from "interfaces/team"; +import { IPolicy, IStoredPolicyResponse } from "interfaces/policy"; +import { ITarget } from "interfaces/target"; + +import MainContent from "components/MainContent"; +import SelectTargets from "components/LiveQuery/SelectTargets"; + +import RunQuery from "pages/policies/live/screens/RunQuery"; + +interface ILivePolicyPageProps { + router: InjectedRouter; + params: Params; + location: { + pathname: string; + query: { host_ids?: string; fleet_id?: string }; + search: string; + }; +} + +const baseClass = "live-policy-page"; + +const LivePolicyPage = ({ + router, + params: { id: paramsPolicyId }, + location, +}: ILivePolicyPageProps): JSX.Element => { + const policyId = paramsPolicyId ? parseInt(paramsPolicyId, 10) : null; + const handlePageError = useErrorHandler(); + + const { currentTeamId } = useTeamIdParam({ + location, + router, + includeAllTeams: true, + includeNoTeam: true, + }); + + const { config } = useContext(AppContext); + const { + setLastEditedQueryId, + setLastEditedQueryName, + setLastEditedQueryDescription, + setLastEditedQueryBody, + setLastEditedQueryResolution, + setLastEditedQueryCritical, + setLastEditedQueryPlatform, + setLastEditedQueryLabelsIncludeAny, + setLastEditedQueryLabelsExcludeAny, + } = useContext(PolicyContext); + + const [queryParamHostsAdded, setQueryParamHostsAdded] = useState(false); + const [step, setStep] = useState(LIVE_QUERY_STEPS[1]); + const [selectedTargets, setSelectedTargets] = useState([]); + const [targetedHosts, setTargetedHosts] = useState([]); + const [targetedLabels, setTargetedLabels] = useState([]); + const [targetedTeams, setTargetedTeams] = useState([]); + const [targetsTotalCount, setTargetsTotalCount] = useState(0); + + const disabledLiveQuery = config?.server_settings.live_query_disabled; + const teamIdForApi = currentTeamId === -1 ? undefined : currentTeamId; + + // Reroute users out of live flow when live queries are globally disabled + if (disabledLiveQuery) { + const path = policyId + ? PATHS.POLICY_DETAILS(policyId) + : PATHS.MANAGE_POLICIES; + + router.push(getPathWithQueryParams(path, { fleet_id: teamIdForApi })); + } + + const { data: storedPolicy } = useQuery< + IStoredPolicyResponse, + Error, + IPolicy + >( + ["policy", policyId, teamIdForApi], + () => + teamIdForApi && teamIdForApi > 0 + ? teamPoliciesAPI.load(teamIdForApi, policyId as number) + : globalPoliciesAPI.load(policyId as number), + { + enabled: !!policyId, + refetchOnWindowFocus: false, + select: (data: IStoredPolicyResponse) => data.policy, + onSuccess: (returnedPolicy) => { + setLastEditedQueryId(returnedPolicy.id); + setLastEditedQueryName(returnedPolicy.name); + setLastEditedQueryDescription(returnedPolicy.description); + setLastEditedQueryBody(returnedPolicy.query); + setLastEditedQueryResolution(returnedPolicy.resolution); + setLastEditedQueryCritical(returnedPolicy.critical); + setLastEditedQueryPlatform(returnedPolicy.platform); + setLastEditedQueryLabelsIncludeAny( + returnedPolicy.labels_include_any || [] + ); + setLastEditedQueryLabelsExcludeAny( + returnedPolicy.labels_exclude_any || [] + ); + }, + onError: (error) => handlePageError(error), + } + ); + + useQuery( + "hostFromURL", + () => + hostAPI.loadHostDetails( + parseInt(location.query.host_ids as string, 10) + ), + { + enabled: !!location.query.host_ids && !queryParamHostsAdded, + select: (data: IHostResponse) => data.host, + onSuccess: (host) => { + setTargetedHosts((prevHosts) => + prevHosts.filter((h) => h.id !== host.id).concat(host) + ); + const targets = selectedTargets; + host.target_type = "hosts"; + targets.push(host); + setSelectedTargets([...targets]); + if (!queryParamHostsAdded) { + setQueryParamHostsAdded(true); + } + router.replace(location.pathname); + }, + } + ); + + // Updates title that shows up on browser tabs + useEffect(() => { + if (storedPolicy?.name) { + document.title = `Run ${storedPolicy.name} | Policies | ${DOCUMENT_TITLE_SUFFIX}`; + } else { + document.title = `Policies | ${DOCUMENT_TITLE_SUFFIX}`; + } + }, [location.pathname, storedPolicy?.name]); + + const goToQueryEditor = useCallback(() => { + const path = policyId + ? PATHS.EDIT_POLICY(policyId) + : PATHS.NEW_POLICY; + + router.push(getPathWithQueryParams(path, { fleet_id: teamIdForApi })); + }, [policyId, router, teamIdForApi]); + + const renderScreen = () => { + const step1Props = { + baseClass, + selectedTargets, + targetedHosts, + targetedLabels, + targetedTeams, + targetsTotalCount, + goToQueryEditor, + goToRunQuery: () => setStep(LIVE_QUERY_STEPS[2]), + setSelectedTargets, + setTargetedHosts, + setTargetedLabels, + setTargetedTeams, + setTargetsTotalCount, + isLivePolicy: true, + }; + + const step2Props = { + selectedTargets, + storedPolicy, + setSelectedTargets, + goToQueryEditor, + targetsTotalCount, + }; + + switch (step) { + case LIVE_QUERY_STEPS[2]: + return ; + default: + return ; + } + }; + + return ( + +
{renderScreen()}
+
+ ); +}; + +export default LivePolicyPage; diff --git a/frontend/pages/policies/PolicyPage/_styles.scss b/frontend/pages/policies/live/LivePolicyPage/_styles.scss similarity index 73% rename from frontend/pages/policies/PolicyPage/_styles.scss rename to frontend/pages/policies/live/LivePolicyPage/_styles.scss index e4f6727743c..964d37bca0a 100644 --- a/frontend/pages/policies/PolicyPage/_styles.scss +++ b/frontend/pages/policies/live/LivePolicyPage/_styles.scss @@ -1,8 +1,6 @@ -.policy-page { - @include vertical-page-layout; - - &__form { - @include vertical-form-layout; +.live-policy-page { + &_wrapper { + width: 100%; } &__results { @@ -12,34 +10,6 @@ min-height: 400px; } - &__warning { - padding: $pad-medium; - font-size: $x-small; - color: $core-fleet-black; - background-color: #fff0b9; - border: 1px solid #f2c94c; - border-radius: $border-radius; - - p { - margin: 0; - line-height: 20px; - } - } - - &__observer-query-view { - width: 90%; - max-width: 1060px; - margin: 0 auto; - color: $core-fleet-black; - - h1 { - font-size: $medium; - } - p { - font-size: $x-small; - } - } - .ace_content { min-height: 500px !important; } diff --git a/frontend/pages/policies/live/LivePolicyPage/index.ts b/frontend/pages/policies/live/LivePolicyPage/index.ts new file mode 100644 index 00000000000..971f6899788 --- /dev/null +++ b/frontend/pages/policies/live/LivePolicyPage/index.ts @@ -0,0 +1 @@ +export { default } from "./LivePolicyPage"; diff --git a/frontend/pages/policies/PolicyPage/screens/RunQuery.tsx b/frontend/pages/policies/live/screens/RunQuery.tsx similarity index 98% rename from frontend/pages/policies/PolicyPage/screens/RunQuery.tsx rename to frontend/pages/policies/live/screens/RunQuery.tsx index 5a9a9a99626..2c4c9dc5fcd 100644 --- a/frontend/pages/policies/PolicyPage/screens/RunQuery.tsx +++ b/frontend/pages/policies/live/screens/RunQuery.tsx @@ -15,7 +15,7 @@ import { ICampaign, ICampaignState } from "interfaces/campaign"; import { IPolicy } from "interfaces/policy"; import { ITarget } from "interfaces/target"; -import PolicyResults from "../components/PolicyResults"; +import PolicyResults from "pages/policies/edit/components/PolicyResults"; interface IRunQueryProps { storedPolicy: IPolicy | undefined; diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx index 9c18ff0e038..d340535395f 100644 --- a/frontend/router/index.tsx +++ b/frontend/router/index.tsx @@ -42,10 +42,11 @@ import ManagePacksPage from "pages/packs/ManagePacksPage"; import ManagePoliciesPage from "pages/policies/ManagePoliciesPage"; import NoAccessPage from "pages/NoAccessPage"; import PackComposerPage from "pages/packs/PackComposerPage"; -import PolicyDetailsPage from "pages/policies/PolicyDetailsPage"; -import PolicyPage from "pages/policies/PolicyPage"; +import PolicyDetailsPage from "pages/policies/details/PolicyDetailsPage"; +import EditPolicyPage from "pages/policies/edit"; import QueryDetailsPage from "pages/queries/details/QueryDetailsPage"; import LiveQueryPage from "pages/queries/live/LiveQueryPage"; +import LivePolicyPage from "pages/policies/live/LivePolicyPage"; import EditQueryPage from "pages/queries/edit/EditQueryPage"; import RegistrationPage from "pages/RegistrationPage"; import ResetPasswordPage from "pages/ResetPasswordPage"; @@ -410,11 +411,15 @@ const routes = ( - + + + + - + + {/* deprecated URL */} diff --git a/frontend/router/paths.ts b/frontend/router/paths.ts index 637be790da8..db74d50fd1c 100644 --- a/frontend/router/paths.ts +++ b/frontend/router/paths.ts @@ -128,6 +128,8 @@ export default { `${URL_PREFIX}/policies/${policyId}`, EDIT_POLICY: (policyId: number): string => `${URL_PREFIX}/policies/${policyId}/edit`, + LIVE_POLICY: (policyId: number | null): string => + `${URL_PREFIX}/policies/${policyId || "new"}/live`, FORGOT_PASSWORD: `${URL_PREFIX}/login/forgot`, MFA: `${URL_PREFIX}/login/mfa`, NO_ACCESS: `${URL_PREFIX}/login/denied`, From 0b21e3682726cb81a8b89540954502a92516f307 Mon Sep 17 00:00:00 2001 From: Rachel Perkins Date: Thu, 9 Apr 2026 10:43:55 -0400 Subject: [PATCH 4/8] Add changelog --- changes/41753-policy-details-page | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changes/41753-policy-details-page diff --git a/changes/41753-policy-details-page b/changes/41753-policy-details-page new file mode 100644 index 00000000000..4cc329316b7 --- /dev/null +++ b/changes/41753-policy-details-page @@ -0,0 +1,3 @@ +- Fleet UI: Added new policy details page with read-only view of policy information +- Fleet UI: Updated edit policy page to redirect users with read-only access to policy details page. +- Fleet UI: Added dedicated `/policies/:id/live` route for running policies From 6822842e8d484655e343c5efe07851af7569a9c4 Mon Sep 17 00:00:00 2001 From: RachelElysia Date: Thu, 9 Apr 2026 11:19:04 -0400 Subject: [PATCH 5/8] Fix prettier --- .../details/PolicyDetailsPage/PolicyDetailsPage.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx b/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx index be9c59184b1..7c5e56f5661 100644 --- a/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx +++ b/frontend/pages/policies/details/PolicyDetailsPage/PolicyDetailsPage.tsx @@ -337,12 +337,9 @@ const PolicyDetailsPage = ({ onClick={() => { policyId && router.push( - getPathWithQueryParams( - PATHS.LIVE_POLICY(policyId), - { - fleet_id: teamIdForApi, - } - ) + getPathWithQueryParams(PATHS.LIVE_POLICY(policyId), { + fleet_id: teamIdForApi, + }) ); }} disabled={!!disabledLiveQuery} From 6fa39c2251e7d04c76947a011b89a354c70f97aa Mon Sep 17 00:00:00 2001 From: RachelElysia Date: Thu, 9 Apr 2026 11:27:23 -0400 Subject: [PATCH 6/8] Fix lint on livepolicypage --- .../policies/live/LivePolicyPage/LivePolicyPage.tsx | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx b/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx index 79123dd8e2f..477e55b9e67 100644 --- a/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx +++ b/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx @@ -7,10 +7,7 @@ import useTeamIdParam from "hooks/useTeamIdParam"; import { AppContext } from "context/app"; import { PolicyContext } from "context/policy"; -import { - LIVE_QUERY_STEPS, - DOCUMENT_TITLE_SUFFIX, -} from "utilities/constants"; +import { LIVE_QUERY_STEPS, DOCUMENT_TITLE_SUFFIX } from "utilities/constants"; import { getPathWithQueryParams } from "utilities/url"; import globalPoliciesAPI from "services/entities/global_policies"; import teamPoliciesAPI from "services/entities/team_policies"; @@ -122,9 +119,7 @@ const LivePolicyPage = ({ useQuery( "hostFromURL", () => - hostAPI.loadHostDetails( - parseInt(location.query.host_ids as string, 10) - ), + hostAPI.loadHostDetails(parseInt(location.query.host_ids as string, 10)), { enabled: !!location.query.host_ids && !queryParamHostsAdded, select: (data: IHostResponse) => data.host, @@ -154,9 +149,7 @@ const LivePolicyPage = ({ }, [location.pathname, storedPolicy?.name]); const goToQueryEditor = useCallback(() => { - const path = policyId - ? PATHS.EDIT_POLICY(policyId) - : PATHS.NEW_POLICY; + const path = policyId ? PATHS.EDIT_POLICY(policyId) : PATHS.NEW_POLICY; router.push(getPathWithQueryParams(path, { fleet_id: teamIdForApi })); }, [policyId, router, teamIdForApi]); From 41f3fb99d5c3c21a5e677747970ef9f4d8781106 Mon Sep 17 00:00:00 2001 From: RachelElysia Date: Thu, 9 Apr 2026 11:33:55 -0400 Subject: [PATCH 7/8] lint on editpolicy page --- frontend/pages/policies/edit/EditPolicyPage.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/pages/policies/edit/EditPolicyPage.tsx b/frontend/pages/policies/edit/EditPolicyPage.tsx index 61c6c008bd3..9c6e74455fc 100644 --- a/frontend/pages/policies/edit/EditPolicyPage.tsx +++ b/frontend/pages/policies/edit/EditPolicyPage.tsx @@ -11,10 +11,7 @@ import { IPolicy, IStoredPolicyResponse, } from "interfaces/policy"; -import { - API_ALL_TEAMS_ID, - APP_CONTEXT_ALL_TEAMS_ID, -} from "interfaces/team"; +import { API_ALL_TEAMS_ID, APP_CONTEXT_ALL_TEAMS_ID } from "interfaces/team"; import globalPoliciesAPI from "services/entities/global_policies"; import teamPoliciesAPI from "services/entities/team_policies"; import teamsAPI, { ILoadTeamResponse } from "services/entities/teams"; From ceaa959dd1d90bfd6bdf3b44d2165be8a7c27d3f Mon Sep 17 00:00:00 2001 From: Rachel Perkins Date: Thu, 9 Apr 2026 13:56:27 -0400 Subject: [PATCH 8/8] useEffect for re-route + parsed host id so react query caches properly --- .../live/LivePolicyPage/LivePolicyPage.tsx | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx b/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx index 477e55b9e67..12531cfa8e1 100644 --- a/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx +++ b/frontend/pages/policies/live/LivePolicyPage/LivePolicyPage.tsx @@ -75,13 +75,16 @@ const LivePolicyPage = ({ const teamIdForApi = currentTeamId === -1 ? undefined : currentTeamId; // Reroute users out of live flow when live queries are globally disabled - if (disabledLiveQuery) { - const path = policyId - ? PATHS.POLICY_DETAILS(policyId) - : PATHS.MANAGE_POLICIES; + // Reroute users out of live flow when live queries are globally disabled + useEffect(() => { + if (disabledLiveQuery) { + const path = policyId + ? PATHS.POLICY_DETAILS(policyId) + : PATHS.MANAGE_POLICIES; - router.push(getPathWithQueryParams(path, { fleet_id: teamIdForApi })); - } + router.push(getPathWithQueryParams(path, { fleet_id: teamIdForApi })); + } + }, [disabledLiveQuery, policyId, router, teamIdForApi]); const { data: storedPolicy } = useQuery< IStoredPolicyResponse, @@ -116,12 +119,15 @@ const LivePolicyPage = ({ } ); + const hostIdFromURL = location.query.host_ids + ? parseInt(location.query.host_ids as string, 10) + : null; + useQuery( - "hostFromURL", - () => - hostAPI.loadHostDetails(parseInt(location.query.host_ids as string, 10)), + ["hostFromURL", hostIdFromURL, teamIdForApi], + () => hostAPI.loadHostDetails(hostIdFromURL as number), { - enabled: !!location.query.host_ids && !queryParamHostsAdded, + enabled: !!hostIdFromURL && !queryParamHostsAdded, select: (data: IHostResponse) => data.host, onSuccess: (host) => { setTargetedHosts((prevHosts) =>