fleet/frontend/pages/SoftwarePage/SoftwareOSDetailsPage/SoftwareOSDetailsPage.tsx
Jacob Shandling aa1845a06b
UI – Fix sw detail error handling (#17478)
## Addresses:
-  unreleased bug with error handling on the vulnerability details page
- miscellaneous code and style improvements

Bug (404 and 403s not being omitted from throwing and error as
intended):

![image](https://github.com/fleetdm/fleet/assets/61553566/4d5e556e-c812-497c-85b8-bdc3af0bc805)

Fixed:
<img width="1277" alt="Screenshot 2024-03-07 at 3 37 22 PM"
src="https://github.com/fleetdm/fleet/assets/61553566/55c28bda-7d2f-49e7-ad69-094df8d66b46">

- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
Co-authored-by: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
2024-03-08 08:21:48 -08:00

206 lines
5.1 KiB
TypeScript

/** software/os/:id */
import React, { useCallback, useContext } from "react";
import { useQuery } from "react-query";
import { useErrorHandler } from "react-error-boundary";
import { RouteComponentProps } from "react-router";
import { AxiosError } from "axios";
import useTeamIdParam from "hooks/useTeamIdParam";
import { AppContext } from "context/app";
import { ignoreAxiosError } from "interfaces/errors";
import osVersionsAPI, {
IOSVersionResponse,
IGetOsVersionQueryKey,
} from "services/entities/operating_systems";
import { IOperatingSystemVersion } from "interfaces/operating_system";
import { DEFAULT_USE_QUERY_OPTIONS, SUPPORT_LINK } from "utilities/constants";
import Spinner from "components/Spinner";
import MainContent from "components/MainContent";
import EmptyTable from "components/EmptyTable";
import CustomLink from "components/CustomLink";
import TeamsHeader from "components/TeamsHeader";
import Card from "components/Card";
import SoftwareDetailsSummary from "../components/SoftwareDetailsSummary";
import SoftwareVulnerabilitiesTable from "../components/SoftwareVulnerabilitiesTable";
import DetailsNoHosts from "../components/DetailsNoHosts";
const baseClass = "software-os-details-page";
interface INotSupportedVulnProps {
platform: string;
}
const NotSupportedVuln = ({ platform }: INotSupportedVulnProps) => {
return (
<EmptyTable
header="Vulnerabilities are not supported for this type of host"
info={
<>
Interested in vulnerability management for{" "}
{platform === "chrome" ? "Chromebooks" : "Linux hosts"}?{" "}
<CustomLink url={SUPPORT_LINK} text="Let us know" newTab />
</>
}
/>
);
};
interface ISoftwareOSDetailsRouteParams {
id: string;
team_id?: string;
}
type ISoftwareOSDetailsPageProps = RouteComponentProps<
undefined,
ISoftwareOSDetailsRouteParams
>;
const SoftwareOSDetailsPage = ({
routeParams,
router,
location,
}: ISoftwareOSDetailsPageProps) => {
const { isPremiumTier, isOnGlobalTeam } = useContext(AppContext);
const handlePageError = useErrorHandler();
const osVersionIdFromURL = parseInt(routeParams.id, 10);
const {
currentTeamId,
teamIdForApi,
userTeams,
handleTeamChange,
} = useTeamIdParam({
location,
router,
includeAllTeams: true,
includeNoTeam: false,
});
const {
data: osVersionDetails,
isLoading,
isError: isOsVersionError,
} = useQuery<
IOSVersionResponse,
AxiosError,
IOperatingSystemVersion,
IGetOsVersionQueryKey[]
>(
[
{
scope: "osVersionDetails",
os_version_id: osVersionIdFromURL,
teamId: teamIdForApi,
},
],
({ queryKey }) => osVersionsAPI.getOSVersion(queryKey[0]),
{
...DEFAULT_USE_QUERY_OPTIONS,
retry: false,
enabled: !!osVersionIdFromURL,
select: (data) => data.os_version,
onError: (error) => {
if (!ignoreAxiosError(error, [403, 404])) {
handlePageError(error);
}
},
}
);
const onTeamChange = useCallback(
(teamId: number) => {
handleTeamChange(teamId);
},
[handleTeamChange]
);
const renderTable = () => {
if (!osVersionDetails) {
return null;
}
if (
osVersionDetails.platform !== "darwin" &&
osVersionDetails.platform !== "windows"
) {
return <NotSupportedVuln platform={osVersionDetails.platform} />;
}
return (
<SoftwareVulnerabilitiesTable
data={osVersionDetails.vulnerabilities}
itemName="version"
isLoading={isLoading}
router={router}
teamIdForApi={teamIdForApi}
/>
);
};
const renderContent = () => {
if (isLoading) {
return <Spinner />;
}
if (!osVersionDetails && !isOsVersionError) {
return null;
}
return (
<>
{isPremiumTier && (
<TeamsHeader
isOnGlobalTeam={isOnGlobalTeam}
currentTeamId={currentTeamId}
userTeams={userTeams}
onTeamChange={onTeamChange}
/>
)}
{isOsVersionError ? (
<DetailsNoHosts
header="OS not detected"
details={`No hosts ${
teamIdForApi ? "on this team " : ""
}have this OS installed.`}
/>
) : (
<>
<SoftwareDetailsSummary
title={osVersionDetails.name}
hosts={osVersionDetails.hosts_count}
queryParams={{
os_name: osVersionDetails.name_only,
os_version: osVersionDetails.version,
team_id: teamIdForApi,
}}
name={osVersionDetails.platform}
/>
<Card
borderRadiusSize="large"
includeShadow
className={`${baseClass}__vulnerabilities-section`}
>
<h2>Vulnerabilities</h2>
{renderTable()}
</Card>
</>
)}
</>
);
};
return (
<MainContent className={baseClass}>
<>{renderContent()}</>
</MainContent>
);
};
export default SoftwareOSDetailsPage;