UI – Render "vulns not supported" empty state for iphone/ipad host software filtered by vuln (#21029)

## #21027 

<img width="1791" alt="Screenshot 2024-08-02 at 4 12 52 PM"
src="https://github.com/user-attachments/assets/bda1af83-4676-484f-b10a-2ca7e2b14ae1">

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

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
jacobshandling 2024-08-05 12:57:53 -07:00 committed by GitHub
parent a6a9a2e1c2
commit 5730c1c3c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 7 deletions

View file

@ -53,6 +53,8 @@ import {
HOST_OSQUERY_DATA,
} from "utilities/constants";
import { Platform } from "interfaces/platform";
import Spinner from "components/Spinner";
import TabsWrapper from "components/TabsWrapper";
import MainContent from "components/MainContent";
@ -921,6 +923,7 @@ const HostDetailsPage = ({
<TabPanel>
<SoftwareCard
id={host.id}
platform={host.platform as Platform} // TODO - typing
softwareUpdatedAt={host.software_updated_at}
hostCanInstallSoftware={
!!host.orbit_version || isIosOrIpadosHost

View file

@ -12,6 +12,7 @@ import deviceAPI, {
IGetDeviceSoftwareResponse,
} from "services/entities/device_user";
import { IHostSoftware, ISoftware } from "interfaces/software";
import { Platform } from "interfaces/platform";
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
import { NotificationContext } from "context/notification";
import { AppContext } from "context/app";
@ -34,6 +35,8 @@ export interface ITableSoftware extends Omit<ISoftware, "vulnerabilities"> {
interface IHostSoftwareProps {
/** This is the host id or the device token */
id: number | string;
/** The host's platform. Only used for the host details page, so can be omited on the Device User Page. */
platform?: Platform;
softwareUpdatedAt?: string;
hostCanInstallSoftware: boolean;
router: InjectedRouter;
@ -82,6 +85,7 @@ export const parseHostSoftwareQueryParams = (queryParams: {
const HostSoftware = ({
id,
platform,
softwareUpdatedAt,
hostCanInstallSoftware,
router,
@ -93,6 +97,8 @@ const HostSoftware = ({
isMyDevicePage = false,
}: IHostSoftwareProps) => {
const { renderFlash } = useContext(NotificationContext);
const vulnFilterAndNotSupported =
["ios", "ipados"].includes(platform ?? "") && queryParams.vulnerable;
const {
isGlobalAdmin,
isGlobalMaintainer,
@ -129,7 +135,8 @@ const HostSoftware = ({
},
{
...DEFAULT_USE_QUERY_OPTIONS,
enabled: isSoftwareEnabled && !isMyDevicePage, // if disabled, we'll always show a generic "No software detected" message
enabled:
isSoftwareEnabled && !isMyDevicePage && !vulnFilterAndNotSupported,
keepPreviousData: true,
staleTime: 7000,
}
@ -158,7 +165,7 @@ const HostSoftware = ({
({ queryKey }) => deviceAPI.getDeviceSoftware(queryKey[0]),
{
...DEFAULT_USE_QUERY_OPTIONS,
enabled: isSoftwareEnabled && isMyDevicePage, // if disabled, we'll always show a generic "No software detected" message
enabled: isSoftwareEnabled && isMyDevicePage, // if disabled, we'll always show a generic "No software detected" message. No DUP for iPad/iPhone
keepPreviousData: true,
staleTime: 7000,
}
@ -251,7 +258,10 @@ const HostSoftware = ({
if (isLoading) {
return <Spinner />;
}
// will never be the case - to handle `platform` typing discrepancy with DeviceUserPage
if (!platform) {
return null;
}
return (
<>
{isError && <DataError />}
@ -260,7 +270,20 @@ const HostSoftware = ({
isLoading={
isMyDevicePage ? deviceSoftwareFetching : hostSoftwareFetching
}
data={data}
// this could be cleaner, however, we are going to revert this commit anyway once vulns are
// supported for iPad/iPhone, by the end of next sprint
data={
vulnFilterAndNotSupported
? ({
count: 0,
meta: {
has_next_results: false,
has_previous_results: false,
},
} as IGetHostSoftwareResponse)
: data
} // eshould be mpty for iPad/iPhone since API call is disabled, but to be sure to trigger empty state
platform={platform}
router={router}
tableConfig={tableConfig}
sortHeader={queryParams.order_key}

View file

@ -8,6 +8,12 @@ import { QueryParams } from "utilities/url";
import { ISoftwareDropdownFilterVal } from "pages/SoftwarePage/SoftwareTitles/SoftwareTable/helpers";
import {
ApplePlatform,
APPLE_PLATFORM_DISPLAY_NAMES,
Platform,
} from "interfaces/platform";
import TableContainer from "components/TableContainer";
import { ITableQueryData } from "components/TableContainer/TableContainer";
// @ts-ignore
@ -15,6 +21,7 @@ import Dropdown from "components/forms/fields/Dropdown";
import EmptySoftwareTable from "pages/SoftwarePage/components/EmptySoftwareTable";
import TableCount from "components/TableContainer/TableCount";
import { VulnsNotSupported } from "pages/SoftwarePage/components/SoftwareVulnerabilitiesTable/SoftwareVulnerabilitiesTable";
const DEFAULT_PAGE_SIZE = 20;
@ -45,6 +52,7 @@ export const DROPDOWN_OPTIONS = [
interface IHostSoftwareTableProps {
tableConfig: any; // TODO: type
data?: IGetHostSoftwareResponse | IGetDeviceSoftwareResponse;
platform: Platform;
isLoading: boolean;
router: InjectedRouter;
sortHeader: string;
@ -60,6 +68,7 @@ interface IHostSoftwareTableProps {
const HostSoftwareTable = ({
tableConfig,
data,
platform,
isLoading,
router,
sortHeader,
@ -167,7 +176,7 @@ const HostSoftwareTable = ({
[determineQueryParamChange, pagePath, generateNewQueryParams, router]
);
const count = data?.count || data?.software.length || 0;
const count = data?.count || data?.software?.length || 0;
const isSoftwareNotDetected = count === 0 && searchQuery === "";
const memoizedSoftwareCount = useCallback(() => {
@ -179,8 +188,17 @@ const HostSoftwareTable = ({
}, [count, isSoftwareNotDetected]);
const memoizedEmptyComponent = useCallback(() => {
return <EmptySoftwareTable isNotDetectingSoftware={searchQuery === ""} />;
}, [searchQuery]);
const vulnFilterAndNotSupported =
["ios", "ipados"].includes(platform) &&
hostSoftwareFilter === "vulnerableSoftware";
return vulnFilterAndNotSupported ? (
<VulnsNotSupported
platformText={APPLE_PLATFORM_DISPLAY_NAMES[platform as ApplePlatform]}
/>
) : (
<EmptySoftwareTable isNotDetectingSoftware={searchQuery === ""} />
);
}, [hostSoftwareFilter, platform, searchQuery]);
return (
<div className={baseClass}>