mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
Sync current team to url params on manage hosts page (#4010)
This commit is contained in:
parent
6e5aee3cdf
commit
90fe417ce0
3 changed files with 148 additions and 106 deletions
|
|
@ -46,7 +46,7 @@ export default (
|
|||
regex: new RegExp(`^${URL_PREFIX}/hosts/`),
|
||||
pathname: PATHS.MANAGE_HOSTS,
|
||||
},
|
||||
// withContext: true,
|
||||
withContext: true,
|
||||
},
|
||||
{
|
||||
icon: "software",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import moment from "moment";
|
|||
import yaml from "js-yaml";
|
||||
|
||||
import { ILabel } from "interfaces/label";
|
||||
import { ITeam } from "interfaces/team";
|
||||
import { ITeam, ITeamSummary } from "interfaces/team";
|
||||
import { IUser } from "interfaces/user";
|
||||
import { IPackQueryFormData } from "interfaces/scheduled_query";
|
||||
|
||||
|
|
@ -688,12 +688,12 @@ export const getSortedTeamOptions = memoize((teams: ITeam[]) =>
|
|||
);
|
||||
|
||||
export const getValidatedTeamId = (
|
||||
teams: ITeam[],
|
||||
teams: ITeam[] | ITeamSummary[],
|
||||
teamId: number,
|
||||
currentUser: IUser | null,
|
||||
isOnGlobalTeam: boolean
|
||||
): number => {
|
||||
let currentUserTeams: ITeam[] = [];
|
||||
let currentUserTeams: ITeamSummary[] = [];
|
||||
if (isOnGlobalTeam) {
|
||||
currentUserTeams = teams;
|
||||
} else if (currentUser && currentUser.teams) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useContext, useEffect } from "react";
|
||||
import React, { useState, useContext, useEffect, useCallback } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useQuery } from "react-query";
|
||||
import { InjectedRouter, Params } from "react-router/lib/Router";
|
||||
|
|
@ -9,6 +9,7 @@ import ReactTooltip from "react-tooltip";
|
|||
import enrollSecretsAPI from "services/entities/enroll_secret";
|
||||
import labelsAPI from "services/entities/labels";
|
||||
import teamsAPI from "services/entities/teams";
|
||||
import usersAPI, { IGetMeResponse } from "services/entities/users";
|
||||
import globalPoliciesAPI from "services/entities/global_policies";
|
||||
import teamPoliciesAPI from "services/entities/team_policies";
|
||||
import hostsAPI, {
|
||||
|
|
@ -132,8 +133,10 @@ const ManageHostsPage = ({
|
|||
const dispatch = useDispatch();
|
||||
const queryParams = location.query;
|
||||
const {
|
||||
currentUser,
|
||||
availableTeams,
|
||||
config,
|
||||
currentTeam,
|
||||
currentUser,
|
||||
isGlobalAdmin,
|
||||
isGlobalMaintainer,
|
||||
isTeamMaintainer,
|
||||
|
|
@ -142,9 +145,32 @@ const ManageHostsPage = ({
|
|||
isOnlyObserver,
|
||||
isPremiumTier,
|
||||
isFreeTier,
|
||||
currentTeam,
|
||||
setAvailableTeams,
|
||||
setCurrentTeam,
|
||||
setCurrentUser,
|
||||
} = useContext(AppContext);
|
||||
|
||||
useQuery(["me"], () => usersAPI.me(), {
|
||||
onSuccess: ({ user, available_teams }: IGetMeResponse) => {
|
||||
setCurrentUser(user);
|
||||
setAvailableTeams(available_teams);
|
||||
if (queryParams.team_id) {
|
||||
const teamIdParam = parseInt(queryParams.team_id, 10);
|
||||
if (
|
||||
isNaN(teamIdParam) ||
|
||||
(teamIdParam &&
|
||||
available_teams &&
|
||||
!available_teams.find((t) => t.id === teamIdParam))
|
||||
) {
|
||||
router.replace({
|
||||
pathname: location.pathname,
|
||||
query: omit(queryParams, "team_id"),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const { selectedOsqueryTable, setSelectedOsqueryTable } = useContext(
|
||||
QueryContext
|
||||
);
|
||||
|
|
@ -398,7 +424,7 @@ const ManageHostsPage = ({
|
|||
options = {
|
||||
...options,
|
||||
teamId: getValidatedTeamId(
|
||||
teams || [],
|
||||
availableTeams || [],
|
||||
options.teamId as number,
|
||||
currentUser,
|
||||
isOnGlobalTeam as boolean
|
||||
|
|
@ -429,7 +455,7 @@ const ManageHostsPage = ({
|
|||
options = {
|
||||
...options,
|
||||
teamId: getValidatedTeamId(
|
||||
teams || [],
|
||||
availableTeams || [],
|
||||
options.teamId as number,
|
||||
currentUser,
|
||||
isOnGlobalTeam as boolean
|
||||
|
|
@ -459,9 +485,21 @@ const ManageHostsPage = ({
|
|||
retrieveHostCount(options);
|
||||
};
|
||||
|
||||
let teamSync = false;
|
||||
if (currentUser && availableTeams) {
|
||||
const teamIdParam = queryParams.team_id
|
||||
? parseInt(queryParams.team_id, 10) // we don't want to parse undefined so we can differntiate non-numeric strings as NaN
|
||||
: undefined;
|
||||
if (currentTeam?.id && !teamIdParam) {
|
||||
teamSync = true;
|
||||
} else if (teamIdParam === currentTeam?.id) {
|
||||
teamSync = true;
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const teamId = parseInt(queryParams?.team_id, 10) || 0;
|
||||
const selectedTeam = find(teams, ["id", teamId]);
|
||||
const selectedTeam = find(availableTeams, ["id", teamId]);
|
||||
if (selectedTeam) {
|
||||
setCurrentTeam(selectedTeam);
|
||||
}
|
||||
|
|
@ -490,11 +528,12 @@ const ManageHostsPage = ({
|
|||
if (isEqual(options, currentQueryOptions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
retrieveHosts(options);
|
||||
retrieveHostCount(options);
|
||||
setCurrentQueryOptions(options);
|
||||
}, [location, labels]);
|
||||
if (teamSync) {
|
||||
retrieveHosts(options);
|
||||
retrieveHostCount(options);
|
||||
setCurrentQueryOptions(options);
|
||||
}
|
||||
}, [availableTeams, currentTeam, location, labels]);
|
||||
|
||||
const handleLabelChange = ({ slug }: ILabel): boolean => {
|
||||
if (!slug) {
|
||||
|
|
@ -575,24 +614,15 @@ const ManageHostsPage = ({
|
|||
};
|
||||
|
||||
const handleClearSoftwareFilter = () => {
|
||||
// TODO: In current UX, clearing the software filter resets all URL params.
|
||||
// The code below can be reimplemented if other URL params are to be preserved.
|
||||
// router.replace(
|
||||
// getNextLocationPath({
|
||||
// pathPrefix: PATHS.MANAGE_HOSTS,
|
||||
// routeTemplate,
|
||||
// routeParams,
|
||||
// queryParams: omit(queryParams, ["software_id"]),
|
||||
// })
|
||||
// );
|
||||
router.replace(PATHS.MANAGE_HOSTS);
|
||||
setCurrentTeam(undefined);
|
||||
setSoftwareDetails(null);
|
||||
};
|
||||
|
||||
const handleTeamSelect = (teamId: number) => {
|
||||
const { MANAGE_HOSTS } = PATHS;
|
||||
const teamIdParam = getValidatedTeamId(
|
||||
teams || [],
|
||||
availableTeams || [],
|
||||
teamId,
|
||||
currentUser,
|
||||
isOnGlobalTeam as boolean
|
||||
|
|
@ -615,7 +645,7 @@ const ManageHostsPage = ({
|
|||
queryParams: newQueryParams,
|
||||
});
|
||||
router.replace(nextLocation);
|
||||
const selectedTeam = find(teams, ["id", teamId]);
|
||||
const selectedTeam = find(availableTeams, ["id", teamId]);
|
||||
setCurrentTeam(selectedTeam);
|
||||
};
|
||||
|
||||
|
|
@ -663,82 +693,87 @@ const ManageHostsPage = ({
|
|||
};
|
||||
|
||||
// NOTE: this is called once on initial render and every time the query changes
|
||||
const onTableQueryChange = async (newTableQuery: ITableQueryProps) => {
|
||||
if (isEqual(newTableQuery, tableQueryData)) {
|
||||
return;
|
||||
}
|
||||
const onTableQueryChange = useCallback(
|
||||
async (newTableQuery: ITableQueryProps) => {
|
||||
if (isEqual(newTableQuery, tableQueryData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTableQueryData({ ...newTableQuery });
|
||||
setTableQueryData({ ...newTableQuery });
|
||||
|
||||
const {
|
||||
searchQuery: searchText,
|
||||
sortHeader,
|
||||
sortDirection,
|
||||
} = newTableQuery;
|
||||
const {
|
||||
searchQuery: searchText,
|
||||
sortHeader,
|
||||
sortDirection,
|
||||
} = newTableQuery;
|
||||
|
||||
const teamId = getValidatedTeamId(
|
||||
teams || [],
|
||||
currentTeam?.id as number,
|
||||
let sort = sortBy;
|
||||
if (sortHeader) {
|
||||
sort = [
|
||||
{
|
||||
key: sortHeader,
|
||||
direction: sortDirection || DEFAULT_SORT_DIRECTION,
|
||||
},
|
||||
];
|
||||
} else if (!sortBy.length) {
|
||||
sort = [
|
||||
{ key: DEFAULT_SORT_HEADER, direction: DEFAULT_SORT_DIRECTION },
|
||||
];
|
||||
}
|
||||
|
||||
if (!isEqual(sort, sortBy)) {
|
||||
setSortBy([...sort]);
|
||||
}
|
||||
|
||||
if (!isEqual(searchText, searchQuery)) {
|
||||
setSearchQuery(searchText);
|
||||
}
|
||||
|
||||
// Rebuild queryParams to dispatch new browser location to react-router
|
||||
const newQueryParams: { [key: string]: any } = {};
|
||||
if (!isEmpty(searchText)) {
|
||||
newQueryParams.query = searchText;
|
||||
}
|
||||
|
||||
newQueryParams.order_key = sort[0].key || DEFAULT_SORT_HEADER;
|
||||
newQueryParams.order_direction =
|
||||
sort[0].direction || DEFAULT_SORT_DIRECTION;
|
||||
|
||||
if (currentTeam?.id) {
|
||||
newQueryParams.team_id = currentTeam.id;
|
||||
}
|
||||
|
||||
if (policyId) {
|
||||
newQueryParams.policy_id = policyId;
|
||||
}
|
||||
|
||||
if (policyResponse) {
|
||||
newQueryParams.policy_response = policyResponse;
|
||||
}
|
||||
|
||||
if (softwareId && !policyId) {
|
||||
newQueryParams.software_id = softwareId;
|
||||
}
|
||||
|
||||
router.replace(
|
||||
getNextLocationPath({
|
||||
pathPrefix: PATHS.MANAGE_HOSTS,
|
||||
routeTemplate,
|
||||
routeParams,
|
||||
queryParams: newQueryParams,
|
||||
})
|
||||
);
|
||||
},
|
||||
[
|
||||
availableTeams,
|
||||
currentTeam,
|
||||
currentUser,
|
||||
isOnGlobalTeam as boolean
|
||||
);
|
||||
|
||||
let sort = sortBy;
|
||||
if (sortHeader) {
|
||||
sort = [
|
||||
{ key: sortHeader, direction: sortDirection || DEFAULT_SORT_DIRECTION },
|
||||
];
|
||||
} else if (!sortBy.length) {
|
||||
sort = [{ key: DEFAULT_SORT_HEADER, direction: DEFAULT_SORT_DIRECTION }];
|
||||
}
|
||||
|
||||
if (!isEqual(sort, sortBy)) {
|
||||
setSortBy([...sort]);
|
||||
}
|
||||
|
||||
if (!isEqual(searchText, searchQuery)) {
|
||||
setSearchQuery(searchText);
|
||||
}
|
||||
|
||||
// Rebuild queryParams to dispatch new browser location to react-router
|
||||
const newQueryParams: { [key: string]: any } = {};
|
||||
if (!isEmpty(searchText)) {
|
||||
newQueryParams.query = searchText;
|
||||
}
|
||||
|
||||
newQueryParams.order_key = sort[0].key || DEFAULT_SORT_HEADER;
|
||||
newQueryParams.order_direction =
|
||||
sort[0].direction || DEFAULT_SORT_DIRECTION;
|
||||
|
||||
if (teamId) {
|
||||
newQueryParams.team_id = teamId;
|
||||
}
|
||||
|
||||
if (queryParams.team_id) {
|
||||
newQueryParams.team_id = queryParams.team_id;
|
||||
}
|
||||
|
||||
if (policyId) {
|
||||
newQueryParams.policy_id = policyId;
|
||||
}
|
||||
|
||||
if (policyResponse) {
|
||||
newQueryParams.policy_response = policyResponse;
|
||||
}
|
||||
|
||||
if (softwareId && !policyId) {
|
||||
newQueryParams.software_id = softwareId;
|
||||
}
|
||||
|
||||
router.replace(
|
||||
getNextLocationPath({
|
||||
pathPrefix: PATHS.MANAGE_HOSTS,
|
||||
routeTemplate,
|
||||
routeParams,
|
||||
queryParams: newQueryParams,
|
||||
})
|
||||
);
|
||||
};
|
||||
policyId,
|
||||
queryParams,
|
||||
softwareId,
|
||||
sortBy,
|
||||
]
|
||||
);
|
||||
|
||||
const onSaveSecret = async (enrollSecretString: string) => {
|
||||
const { MANAGE_HOSTS } = PATHS;
|
||||
|
|
@ -1045,7 +1080,7 @@ const ManageHostsPage = ({
|
|||
|
||||
const renderTeamsFilterDropdown = () => (
|
||||
<TeamsDropdown
|
||||
currentUserTeams={teams || []}
|
||||
currentUserTeams={availableTeams || []}
|
||||
selectedTeamId={
|
||||
(policyId && policy?.team_id) || (currentTeam?.id as number)
|
||||
}
|
||||
|
|
@ -1309,13 +1344,13 @@ const ManageHostsPage = ({
|
|||
<div className={`${baseClass}__title`}>
|
||||
{isFreeTier && <h1>Hosts</h1>}
|
||||
{isPremiumTier &&
|
||||
teams &&
|
||||
(teams.length > 1 || isOnGlobalTeam) &&
|
||||
availableTeams &&
|
||||
(availableTeams.length > 1 || isOnGlobalTeam) &&
|
||||
renderTeamsFilterDropdown()}
|
||||
{isPremiumTier &&
|
||||
!isOnGlobalTeam &&
|
||||
teams &&
|
||||
teams.length === 1 && <h1>{teams[0].name}</h1>}
|
||||
availableTeams &&
|
||||
availableTeams.length === 1 && <h1>{availableTeams[0].name}</h1>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1430,7 +1465,8 @@ const ManageHostsPage = ({
|
|||
!currentUser ||
|
||||
!hosts ||
|
||||
selectedFilters.length === 0 ||
|
||||
selectedLabel === undefined
|
||||
selectedLabel === undefined ||
|
||||
!teamSync
|
||||
) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
|
@ -1442,9 +1478,11 @@ const ManageHostsPage = ({
|
|||
// There are no hosts for this instance yet
|
||||
if (
|
||||
getStatusSelected() === ALL_HOSTS_LABEL &&
|
||||
!isHostCountLoading &&
|
||||
filteredHostCount === 0 &&
|
||||
searchQuery === "" &&
|
||||
!isHostsLoading
|
||||
!isHostsLoading &&
|
||||
teamSync
|
||||
) {
|
||||
const { software_id, policy_id } = queryParams || {};
|
||||
const includesSoftwareOrPolicyFilter = !!(software_id || policy_id);
|
||||
|
|
@ -1549,6 +1587,10 @@ const ManageHostsPage = ({
|
|||
);
|
||||
};
|
||||
|
||||
if (!teamSync) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="has-sidebar">
|
||||
{renderForm()}
|
||||
|
|
|
|||
Loading…
Reference in a new issue