mirror of
https://github.com/fleetdm/fleet
synced 2026-05-21 07:58:31 +00:00
for #31054 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [ ] Added/updated automated tests - [ ] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [X] QA'd all new/changed functionality manually
213 lines
5.4 KiB
TypeScript
213 lines
5.4 KiB
TypeScript
import React, { useCallback, useContext, useMemo } from "react";
|
|
import { Tab, Tabs, TabList } from "react-tabs";
|
|
import { InjectedRouter } from "react-router";
|
|
|
|
import PATHS from "router/paths";
|
|
import { AppContext } from "context/app";
|
|
import useTeamIdParam from "hooks/useTeamIdParam";
|
|
|
|
import TabNav from "components/TabNav";
|
|
import TabText from "components/TabText";
|
|
import MainContent from "components/MainContent";
|
|
import TeamsDropdown from "components/TeamsDropdown";
|
|
import { parseOSUpdatesCurrentVersionsQueryParams } from "./OSUpdates/components/CurrentVersionSection/CurrentVersionSection";
|
|
|
|
interface IControlsSubNavItem {
|
|
name: string;
|
|
pathname: string;
|
|
}
|
|
|
|
const controlsSubNav: IControlsSubNavItem[] = [
|
|
{
|
|
name: "OS updates",
|
|
pathname: PATHS.CONTROLS_OS_UPDATES,
|
|
},
|
|
{
|
|
name: "OS settings",
|
|
pathname: PATHS.CONTROLS_OS_SETTINGS,
|
|
},
|
|
{
|
|
name: "Setup experience",
|
|
pathname: PATHS.CONTROLS_SETUP_EXPERIENCE,
|
|
},
|
|
{
|
|
name: "Scripts",
|
|
pathname: PATHS.CONTROLS_SCRIPTS,
|
|
},
|
|
{
|
|
name: "Variables",
|
|
pathname: PATHS.CONTROLS_VARIABLES,
|
|
},
|
|
];
|
|
|
|
// params to strip when navigating between tabs
|
|
const subNavQueryParams = [
|
|
"page",
|
|
"order_key",
|
|
"order_direction",
|
|
"status",
|
|
] as const;
|
|
|
|
interface IManageControlsPageProps {
|
|
children: JSX.Element;
|
|
location: {
|
|
pathname: string;
|
|
search: string;
|
|
hash?: string;
|
|
query: {
|
|
team_id?: string;
|
|
page?: string;
|
|
order_key?: string;
|
|
order_direction?: "asc" | "desc";
|
|
};
|
|
};
|
|
router: InjectedRouter; // v3
|
|
}
|
|
|
|
const getTabIndex = (
|
|
permittedControlsSubNav: IControlsSubNavItem[],
|
|
path: string
|
|
): number => {
|
|
return permittedControlsSubNav.findIndex((navItem) => {
|
|
// tab stays highlighted for paths that start with same pathname
|
|
return path.startsWith(navItem.pathname);
|
|
});
|
|
};
|
|
|
|
const baseClass = "manage-controls-page";
|
|
|
|
const ManageControlsPage = ({
|
|
// TODO(sarah): decide on pattern to pass team id to subcomponents.
|
|
// using children makes it difficult to centralize page-level control
|
|
// over team id param
|
|
children,
|
|
location,
|
|
router,
|
|
}: IManageControlsPageProps): JSX.Element => {
|
|
const page = parseInt(location?.query?.page || "", 10) || 0;
|
|
|
|
const {
|
|
config,
|
|
isOnGlobalTeam,
|
|
isPremiumTier,
|
|
isGlobalAdmin,
|
|
isTeamAdmin,
|
|
} = useContext(AppContext);
|
|
|
|
const {
|
|
currentTeamId,
|
|
userTeams,
|
|
teamIdForApi,
|
|
handleTeamChange,
|
|
} = useTeamIdParam({
|
|
location,
|
|
router,
|
|
includeAllTeams: false,
|
|
includeNoTeam: true,
|
|
permittedAccessByTeamRole: {
|
|
admin: true,
|
|
maintainer: true,
|
|
observer: false,
|
|
observer_plus: false,
|
|
},
|
|
});
|
|
|
|
const permittedControlsSubNav = useMemo(() => {
|
|
let renderedSubNav = controlsSubNav;
|
|
if (!isGlobalAdmin && !isTeamAdmin) {
|
|
renderedSubNav = controlsSubNav.filter((navItem) => {
|
|
return navItem.name !== "OS updates";
|
|
});
|
|
}
|
|
return renderedSubNav;
|
|
}, [isGlobalAdmin, isTeamAdmin]);
|
|
|
|
const navigateToNav = useCallback(
|
|
(i: number): void => {
|
|
const navPath = permittedControlsSubNav[i].pathname;
|
|
// remove query params related to the prior tab
|
|
const newParams = new URLSearchParams(location?.search);
|
|
subNavQueryParams.forEach((p) => newParams.delete(p));
|
|
const newQuery = newParams.toString();
|
|
|
|
router.replace(
|
|
navPath
|
|
.concat(newQuery ? `?${newQuery}` : "")
|
|
.concat(location?.hash || "")
|
|
);
|
|
},
|
|
[location, router, permittedControlsSubNav]
|
|
);
|
|
|
|
const renderBody = () => {
|
|
return (
|
|
<div>
|
|
<TabNav>
|
|
<Tabs
|
|
selectedIndex={getTabIndex(
|
|
permittedControlsSubNav,
|
|
location?.pathname || ""
|
|
)}
|
|
onSelect={navigateToNav}
|
|
>
|
|
<TabList>
|
|
{permittedControlsSubNav.map((navItem) => {
|
|
return (
|
|
<Tab key={navItem.name} data-text={navItem.name}>
|
|
<TabText>{navItem.name}</TabText>
|
|
</Tab>
|
|
);
|
|
})}
|
|
</TabList>
|
|
</Tabs>
|
|
</TabNav>
|
|
{React.cloneElement(children, {
|
|
teamIdForApi,
|
|
currentPage: page,
|
|
queryParams: parseOSUpdatesCurrentVersionsQueryParams(location.query),
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const renderHeaderContent = () => {
|
|
if (isPremiumTier && !config?.partnerships?.enable_primo && userTeams) {
|
|
if (userTeams.length > 1 || isOnGlobalTeam) {
|
|
return (
|
|
<TeamsDropdown
|
|
currentUserTeams={userTeams}
|
|
selectedTeamId={currentTeamId}
|
|
onChange={handleTeamChange}
|
|
includeAllTeams={false}
|
|
includeNoTeams
|
|
/>
|
|
);
|
|
}
|
|
if (!isOnGlobalTeam && userTeams.length === 1) {
|
|
return <h1>{userTeams[0].name}</h1>;
|
|
}
|
|
}
|
|
return <h1>Controls</h1>;
|
|
};
|
|
|
|
const renderHeader = () => (
|
|
<div className={`${baseClass}__header`}>
|
|
<div className={`${baseClass}__text`}>
|
|
<div className={`${baseClass}__title`}>{renderHeaderContent()}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<MainContent>
|
|
<div className={`${baseClass}__wrapper`}>
|
|
<div className={`${baseClass}__header-wrap`}>
|
|
<div className={`${baseClass}__header-wrap`}>{renderHeader()}</div>
|
|
</div>
|
|
{renderBody()}
|
|
</div>
|
|
</MainContent>
|
|
);
|
|
};
|
|
|
|
export default ManageControlsPage;
|