diff --git a/frontend/pages/ManageControlsPage/ManageControlsPage.tsx b/frontend/pages/ManageControlsPage/ManageControlsPage.tsx index 5d95de538c..32d9613b11 100644 --- a/frontend/pages/ManageControlsPage/ManageControlsPage.tsx +++ b/frontend/pages/ManageControlsPage/ManageControlsPage.tsx @@ -9,6 +9,7 @@ import useTeamIdParam from "hooks/useTeamIdParam"; import TabsWrapper from "components/TabsWrapper"; import MainContent from "components/MainContent"; import TeamsDropdown from "components/TeamsDropdown"; +import { parseOSUpdatesCurrentVersionsQueryParams } from "./OSUpdates/components/CurrentVersionSection/CurrentVersionSection"; interface IControlsSubNavItem { name: string; @@ -43,6 +44,8 @@ interface IManageControlsPageProps { query: { team_id?: string; page?: string; + order_key?: string; + order_direction?: "asc" | "desc"; }; }; router: InjectedRouter; // v3 @@ -121,7 +124,11 @@ const ManageControlsPage = ({ - {React.cloneElement(children, { teamIdForApi, currentPage: page })} + {React.cloneElement(children, { + teamIdForApi, + currentPage: page, + queryParams: parseOSUpdatesCurrentVersionsQueryParams(location.query), + })} ); }; diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/_styles.scss b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/_styles.scss index cb9b50f1f0..81c563065c 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/_styles.scss +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/_styles.scss @@ -101,10 +101,6 @@ gap: $pad-small; } - &__profile-graphic--message { - text-align: center; - } - &__button-wrap { display: flex; justify-content: flex-end; diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/_styles.scss b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/_styles.scss index 261799fe73..222268e7db 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/_styles.scss +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/CustomSettings/components/ProfileUploader/components/AddProfileModal/_styles.scss @@ -59,6 +59,10 @@ gap: $pad-small; } + &__profile-graphic--message { + text-align: center; + } + &__button-wrap { display: flex; justify-content: flex-end; diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/components/DiskEncryptionTable/_styles.scss b/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/components/DiskEncryptionTable/_styles.scss index 4b9fe65d32..74a24f3ac2 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/components/DiskEncryptionTable/_styles.scss +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/DiskEncryption/components/DiskEncryptionTable/_styles.scss @@ -9,7 +9,7 @@ border-right: none; } - @media (max-width: $break-md) { + @media (max-width: 1120px) { .view-hosts-link { span { display: none; diff --git a/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx b/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx index e18a4afca2..e56450775a 100644 --- a/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx +++ b/frontend/pages/ManageControlsPage/OSUpdates/OSUpdates.tsx @@ -17,6 +17,7 @@ import NudgePreview from "./components/NudgePreview"; import TurnOnMdmMessage from "../components/TurnOnMdmMessage/TurnOnMdmMessage"; import CurrentVersionSection from "./components/CurrentVersionSection"; import TargetSection from "./components/TargetSection"; +import { parseOSUpdatesCurrentVersionsQueryParams } from "./components/CurrentVersionSection/CurrentVersionSection"; export type OSUpdatesSupportedPlatform = | "darwin" @@ -42,9 +43,10 @@ const getSelectedPlatform = ( interface IOSUpdates { router: InjectedRouter; teamIdForApi: number; + queryParams: ReturnType; } -const OSUpdates = ({ router, teamIdForApi }: IOSUpdates) => { +const OSUpdates = ({ router, teamIdForApi, queryParams }: IOSUpdates) => { const { isPremiumTier, config, setConfig } = useContext(AppContext); const [ @@ -113,7 +115,11 @@ const OSUpdates = ({ router, teamIdForApi }: IOSUpdates) => {

- +
; } +const DEFAULT_SORT_DIRECTION = "desc"; +const DEFAULT_SORT_HEADER = "hosts_count"; +const DEFAULT_PAGE = 0; +const DEFAULT_PAGE_SIZE = 8; + +export const parseOSUpdatesCurrentVersionsQueryParams = (queryParams: { + page?: string; + order_key?: string; + order_direction?: "asc" | "desc"; +}) => { + const sortHeader = queryParams?.order_key ?? DEFAULT_SORT_HEADER; + const sortDirection = queryParams?.order_direction ?? DEFAULT_SORT_DIRECTION; + const page = queryParams?.page + ? parseInt(queryParams.page, 10) + : DEFAULT_PAGE; + const pageSize = DEFAULT_PAGE_SIZE; + + return { + page, + order_key: sortHeader, + order_direction: sortDirection, + per_page: pageSize, + }; +}; + const CurrentVersionSection = ({ + router, currentTeamId, + queryParams, }: ICurrentVersionSectionProps) => { const { data, isError, isLoading: isLoadingOsVersions } = useQuery< IOSVersionsResponse, AxiosError >( - ["os_versions", currentTeamId], - () => getOSVersions({ teamId: currentTeamId }), + ["os_versions", currentTeamId, queryParams], + () => getOSVersions({ teamId: currentTeamId, ...queryParams }), { retry: false, refetchOnWindowFocus: false, @@ -91,9 +121,11 @@ const CurrentVersionSection = ({ return ( ); }; diff --git a/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx b/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx index 372430e0c0..b71e546fd0 100644 --- a/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx +++ b/frontend/pages/ManageControlsPage/OSUpdates/components/OSVersionTable/OSVersionTable.tsx @@ -1,30 +1,92 @@ -import React from "react"; +import React, { useCallback } from "react"; +import { InjectedRouter } from "react-router"; +import PATHS from "router/paths"; import { IOperatingSystemVersion } from "interfaces/operating_system"; +import { getNextLocationPath } from "utilities/helpers"; +import { ITableQueryData } from "components/TableContainer/TableContainer"; import TableContainer from "components/TableContainer"; import { generateTableHeaders } from "./OSVersionTableConfig"; import OSVersionsEmptyState from "../OSVersionsEmptyState"; +import { parseOSUpdatesCurrentVersionsQueryParams } from "../CurrentVersionSection/CurrentVersionSection"; const baseClass = "os-version-table"; interface IOSVersionTableProps { + router: InjectedRouter; osVersionData: IOperatingSystemVersion[]; currentTeamId: number; isLoading: boolean; + queryParams: ReturnType; } -const DEFAULT_SORT_HEADER = "hosts_count"; -const DEFAULT_SORT_DIRECTION = "desc"; - const OSVersionTable = ({ + router, osVersionData, currentTeamId, isLoading, + queryParams, }: IOSVersionTableProps) => { const columns = generateTableHeaders(currentTeamId); + const determineQueryParamChange = useCallback( + (newTableQuery: ITableQueryData) => { + const changedEntry = Object.entries(newTableQuery).find(([key, val]) => { + switch (key) { + case "sortDirection": + return val !== queryParams.order_direction; + case "sortHeader": + return val !== queryParams.order_key; + case "pageIndex": + return val !== queryParams.page; + default: + return false; + } + }); + return changedEntry?.[0] ?? ""; + }, + [queryParams.order_direction, queryParams.order_key, queryParams.page] + ); + + const generateNewQueryParams = useCallback( + (newTableQuery: ITableQueryData, changedParam: string) => { + const newQueryParam: Record = { + team_id: currentTeamId, + order_direction: newTableQuery.sortDirection, + order_key: newTableQuery.sortHeader, + page: changedParam === "pageIndex" ? newTableQuery.pageIndex : 0, + }; + + return newQueryParam; + }, + [currentTeamId] + ); + // NOTE: this is called once on initial render and every time the query changes + const onQueryChange = useCallback( + (newTableQuery: ITableQueryData) => { + // we want to determine which query param has changed in order to + // reset the page index to 0 if any other param has changed. + const changedParam = determineQueryParamChange(newTableQuery); + + // if nothing has changed, don't update the route. this can happen when + // this handler is called on the inital render. Can also happen when + // the filter dropdown is changed. That is handled on the onChange handler + // for the dropdown. + if (changedParam === "") return; + + const newRoute = getNextLocationPath({ + pathPrefix: PATHS.CONTROLS_OS_UPDATES, + routeTemplate: "", + queryParams: generateNewQueryParams(newTableQuery, changedParam), + }); + + router.replace(newRoute); + }, + [determineQueryParamChange, generateNewQueryParams, router] + ); + return (
);