fleet/frontend/components/LiveQuery/TargetsInput/TargetsInput.tsx
Jacob Shandling 333674b051
UI – Host query report page (#15511)
## Addresses second major part of #15011  (item 3) – Host query report

_Note for reviewers: The most important files here are:_
- HostQueryReport.tsx
- HQRTable.tsx
- HQRTableConfig.tsx

_The rest are associated API services, interfaces, helpers, routes,
styles, and miscellanious code improvements I made along the way._

____________

### See linked issue for enumeration of feature-related tasks

<img width="1230" alt="Screenshot 2023-12-08 at 4 23 50 PM"
src="https://github.com/fleetdm/fleet/assets/61553566/4ae4b41b-9209-4afa-ae50-8844d01ff8fd">

<img width="1230" alt="collecting"
src="https://github.com/fleetdm/fleet/assets/61553566/061ac2bc-899f-4b29-91ba-36ebecf5ce58">

<img width="1230" alt="Screenshot 2023-12-08 at 4 24 39 PM"
src="https://github.com/fleetdm/fleet/assets/61553566/f8b25e01-fe3b-47e6-b980-eba9538b1a01">

<img width="1230" alt="Screenshot 2023-12-08 at 4 25 01 PM"
src="https://github.com/fleetdm/fleet/assets/61553566/46360274-8500-494c-8fb7-3a1d45347ce0">

Re-routes to host details > queries if:
- query reports are globally disabled:

https://github.com/fleetdm/fleet/assets/61553566/ac67da8c-57bc-4d9b-96be-daf3b198e704

- query has `Discard data` enabled:

https://github.com/fleetdm/fleet/assets/61553566/b797dd24-9893-4360-bf40-b80298848864

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

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
2023-12-08 16:54:24 -08:00

113 lines
3.5 KiB
TypeScript

import React from "react";
import { Row } from "react-table";
import { isEmpty, pullAllBy } from "lodash";
import { IHost } from "interfaces/host";
import { HOSTS_SEARCH_BOX_PLACEHOLDER } from "utilities/constants";
import DataError from "components/DataError";
// @ts-ignore
import Input from "components/forms/fields/InputFieldWithIcon";
import TableContainer from "components/TableContainer";
import { generateTableHeaders } from "./TargetsInputHostsTableConfig";
interface ITargetsInputProps {
tabIndex: number;
searchText: string;
searchResults: IHost[];
isTargetsLoading: boolean;
hasFetchError: boolean;
targetedHosts: IHost[];
setSearchText: (value: string) => void;
handleRowSelect: (value: Row) => void;
handleRowRemove: (value: Row) => void;
}
const baseClass = "targets-input";
const TargetsInput = ({
tabIndex,
searchText,
searchResults,
isTargetsLoading,
hasFetchError,
targetedHosts,
handleRowSelect,
handleRowRemove,
setSearchText,
}: ITargetsInputProps): JSX.Element => {
const resultsDropdownTableHeaders = generateTableHeaders();
const selectedTableHeaders = generateTableHeaders(handleRowRemove);
const dropdownHosts =
searchResults && pullAllBy(searchResults, targetedHosts, "display_name");
// const finalSelectedHostTargets =
// targetedHosts && filter(targetedHosts, "display_name");
const isActiveSearch =
!isEmpty(searchText) && (!hasFetchError || isTargetsLoading);
const isSearchError = !isEmpty(searchText) && hasFetchError;
return (
<div>
<div className={baseClass}>
<Input
autofocus
type="search"
iconSvg="search"
value={searchText}
tabIndex={tabIndex}
iconPosition="start"
label="Target specific hosts"
placeholder={HOSTS_SEARCH_BOX_PLACEHOLDER}
onChange={setSearchText}
/>
{isActiveSearch && (
<div className={`${baseClass}__hosts-search-dropdown`}>
<TableContainer
columnConfigs={resultsDropdownTableHeaders}
data={dropdownHosts}
isLoading={isTargetsLoading}
resultsTitle=""
emptyComponent={() => (
<div className="empty-search">
<div className="empty-search__inner">
<h4>No hosts match the current search criteria.</h4>
<p>
Expecting to see hosts? Try again in a few seconds as the
system catches up.
</p>
</div>
</div>
)}
showMarkAllPages={false}
isAllPagesSelected={false}
disableCount
disablePagination
disableMultiRowSelect
onSelectSingleRow={handleRowSelect}
/>
</div>
)}
{isSearchError && (
<div className={`${baseClass}__hosts-search-dropdown`}>
<DataError />
</div>
)}
<div className={`${baseClass}__hosts-selected-table`}>
<TableContainer
columnConfigs={selectedTableHeaders}
data={targetedHosts}
isLoading={false}
resultsTitle=""
showMarkAllPages={false}
isAllPagesSelected={false}
disableCount
disablePagination
emptyComponent={() => <></>}
/>
</div>
</div>
</div>
);
};
export default TargetsInput;