Fleet UI: Undetermined public ip tooltip (#9907)

This commit is contained in:
RachelElysia 2023-02-21 09:16:38 -05:00 committed by GitHub
parent c3a9a1cd94
commit 7f6a42e4ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 124 additions and 26 deletions

View file

@ -1 +1,2 @@
* Only set public IPs on the `host.public_ip` field and add documentation on how to properly configure the deployment to ingest correct public IPs from enrolled devices.
- Only set public IPs on the `host.public_ip` field and add documentation on how to properly configure the deployment to ingest correct public IPs from enrolled devices.
- Add tooltip with link to UI when Public IP address cannot be determined

View file

@ -2,6 +2,7 @@ import React from "react";
import Icon from "components/Icon";
import classnames from "classnames";
import { Colors } from "styles/var/colors";
interface ICustomLinkProps {
url: string;
@ -10,7 +11,7 @@ interface ICustomLinkProps {
newTab?: boolean;
/** Icon wraps on new line with last word */
multiline?: boolean;
black?: boolean;
iconColor?: Colors;
}
const baseClass = "custom-link";
@ -21,7 +22,7 @@ const CustomLink = ({
className,
newTab = false,
multiline = false,
black = false,
iconColor = "core-fleet-blue",
}: ICustomLinkProps): JSX.Element => {
const customLinkClass = classnames(baseClass, className);
@ -39,7 +40,7 @@ const CustomLink = ({
<Icon
name="external-link"
className={`${baseClass}__external-icon`}
color={black ? "core-fleet-black" : "core-fleet-blue"}
color={iconColor}
/>
)}
</span>
@ -51,7 +52,7 @@ const CustomLink = ({
<Icon
name="external-link"
className={`${baseClass}__external-icon`}
color={black ? "core-fleet-black" : "core-fleet-blue"}
color={iconColor}
/>
)}
</>

View file

@ -28,7 +28,7 @@ const AppleBMTermsMessage = () => {
text="Go to ABM"
className={`${baseClass}__new-tab`}
newTab
black
iconColor="core-fleet-black"
/>
</div>
);

View file

@ -1,6 +1,7 @@
import React from "react";
import classnames from "classnames";
import ReactTooltip from "react-tooltip";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
interface IStatusIndicatorProps {
value: string;
@ -11,7 +12,7 @@ interface IStatusIndicatorProps {
}
const generateClassTag = (rawValue: string): string => {
if (rawValue === "---") {
if (rawValue === DEFAULT_EMPTY_CELL_VALUE) {
return "indeterminate";
}
return rawValue.replace(" ", "-").toLowerCase();

View file

@ -96,8 +96,7 @@ const PillCell = ({
<span className={pillClassName}>{indicator}</span>
</span>
<ReactTooltip
place="bottom"
// offset={getTooltipOffset(pillText)}
place="top"
effect="solid"
backgroundColor="#3e4771"
id={`${customIdPrefix || "pill"}__${id?.toString() || tooltipId}`}

View file

@ -1,5 +1,6 @@
import React from "react";
import { getByTestId, render, screen, within } from "@testing-library/react";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import PlatformCell from "./PlatformCell";
@ -23,7 +24,7 @@ describe("Platform cell", () => {
render(<PlatformCell value={[]} />);
const icons = screen.queryAllByTestId("icon");
const emptyText = screen.queryByText("---");
const emptyText = screen.queryByText(DEFAULT_EMPTY_CELL_VALUE);
expect(icons).toHaveLength(0);
expect(emptyText).toBeInTheDocument();

View file

@ -6,6 +6,7 @@ import { osqueryTableNames } from "utilities/osquery_tables";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
import FleetMarkdown from "components/FleetMarkdown";
import CustomLink from "components/CustomLink";
import QueryTableColumns from "./QueryTableColumns";
import QueryTablePlatforms from "./QueryTablePlatforms";
@ -15,7 +16,6 @@ import CloseIcon from "../../../../assets/images/icon-close-black-50-8x8@2x.png"
import QueryTableExample from "./QueryTableExample";
import QueryTableNotes from "./QueryTableNotes";
import EventedTableTag from "./EventedTableTag";
import CustomLink from "components/CustomLink";
interface IQuerySidePanel {
selectedOsqueryTable: IOsQueryTable;

View file

@ -7,6 +7,7 @@ import { IInvite } from "interfaces/invite";
import { IUser } from "interfaces/user";
import { IDropdownOption } from "interfaces/dropdownOption";
import { generateRole, generateTeam, greyCell } from "utilities/helpers";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import DropdownCell from "../../../../../components/TableContainer/DataTable/DropdownCell";
interface IHeaderProps {
@ -193,7 +194,7 @@ const enhanceUserData = (
): IUserTableData[] => {
return users.map((user) => {
return {
name: user.name || "---",
name: user.name || DEFAULT_EMPTY_CELL_VALUE,
status: generateStatus("user", user),
email: user.email,
teams: generateTeam(user.teams, user.global_role),
@ -212,7 +213,7 @@ const enhanceUserData = (
const enhanceInviteData = (invites: IInvite[]): IUserTableData[] => {
return invites.map((invite) => {
return {
name: invite.name || "---",
name: invite.name || DEFAULT_EMPTY_CELL_VALUE,
status: generateStatus("invite", invite),
email: invite.email,
teams: generateTeam(invite.teams, invite.global_role),

View file

@ -16,6 +16,7 @@ import TextCell from "components/TableContainer/DataTable/TextCell/TextCell";
import TruncatedTextCell from "components/TableContainer/DataTable/TruncatedTextCell";
import TooltipWrapper from "components/TooltipWrapper";
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
import CustomLink from "components/CustomLink";
import {
humanHostMemory,
humanHostLastRestart,
@ -28,6 +29,7 @@ import { ITeamSummary } from "interfaces/team";
import { IUser } from "interfaces/user";
import PATHS from "router/paths";
import permissionUtils from "utilities/permissions";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import getHostStatusTooltipText from "../helpers";
interface IGetToggleAllRowsSelectedProps {
@ -424,7 +426,40 @@ const allHostTableHeaders: IDataColumn[] = [
/>
),
accessor: "public_ip",
Cell: (cellProps: ICellProps) => <TextCell value={cellProps.cell.value} />,
Cell: (cellProps: ICellProps) => {
if (cellProps.cell.value) {
return <TextCell value={cellProps.cell.value} />;
}
return (
<>
<span
className="text-cell text-muted tooltip"
data-tip
data-for={`public-ip-tooltip__${cellProps.row.original.id}`}
>
{DEFAULT_EMPTY_CELL_VALUE}
</span>
<ReactTooltip
place="top"
effect="solid"
backgroundColor="#3e4771"
id={`public-ip__${cellProps.row.original.id}`}
data-html
clickable
delayHide={200} // need delay set to hover using clickable
>
Public IP address could not be
<br /> determined.{" "}
<CustomLink
url="https://fleetdm.com/docs/deploying/configuration#public-i-ps-of-devices"
text="Learn more"
newTab
iconColor="core-fleet-white"
/>
</ReactTooltip>
</>
);
},
},
{
title: "Last fetched",

View file

@ -193,7 +193,8 @@
.device_mapping__cell,
.mdm_enrollment_status__cell,
.mdm_server_url__cell {
.mdm_server_url__cell,
.public_ip__cell {
.text-cell {
display: inline;
}

View file

@ -3,9 +3,14 @@ import React from "react";
import ReactTooltip from "react-tooltip";
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
import TooltipWrapper from "components/TooltipWrapper";
import CustomLink from "components/CustomLink";
import { IHostMdmData, IMunkiData, IDeviceUser } from "interfaces/host";
import { humanHostLastRestart } from "utilities/helpers";
import { MDM_STATUS_TOOLTIP } from "utilities/constants";
import {
DEFAULT_EMPTY_CELL_VALUE,
MDM_STATUS_TOOLTIP,
} from "utilities/constants";
interface IAboutProps {
aboutData: { [key: string]: any };
@ -21,6 +26,41 @@ const About = ({
munki,
mdm,
}: IAboutProps): JSX.Element => {
const renderPublicIp = () => {
if (aboutData.public_ip !== DEFAULT_EMPTY_CELL_VALUE) {
return aboutData.public_ip;
}
return (
<>
<span
className="text-cell text-muted tooltip"
data-tip
data-for={"public-ip-tooltip"}
>
{aboutData.public_ip}
</span>
<ReactTooltip
place="bottom"
effect="solid"
backgroundColor="#3e4771"
id={"public-ip-tooltip"}
data-html
clickable
delayHide={200} // need delay set to hover using clickable
>
Public IP address could not be
<br /> determined.{" "}
<CustomLink
url="https://fleetdm.com/docs/deploying/configuration#public-i-ps-of-devices"
text="Learn more"
newTab
iconColor="core-fleet-white"
/>
</ReactTooltip>
</>
);
};
const renderSerialAndIPs = () => {
return (
<>
@ -34,7 +74,7 @@ const About = ({
</div>
<div className="info-grid__block">
<span className="info-grid__header">Public IP address</span>
<span className="info-grid__data">{aboutData.public_ip}</span>
<span className="info-grid__data">{renderPublicIp()}</span>
</div>
</>
);
@ -45,7 +85,9 @@ const About = ({
<>
<div className="info-grid__block">
<span className="info-grid__header">Munki version</span>
<span className="info-grid__data">{munki.version || "---"}</span>
<span className="info-grid__data">
{munki.version || DEFAULT_EMPTY_CELL_VALUE}
</span>
</div>
</>
) : null;
@ -70,7 +112,9 @@ const About = ({
</div>
<div className="info-grid__block">
<span className="info-grid__header">MDM server URL</span>
<span className="info-grid__data">{mdm.server_url || "---"}</span>
<span className="info-grid__data">
{mdm.server_url || DEFAULT_EMPTY_CELL_VALUE}
</span>
</div>
</>
);
@ -108,7 +152,7 @@ const About = ({
</ReactTooltip>
</>
) : (
deviceMapping[0].email || "---"
deviceMapping[0].email || DEFAULT_EMPTY_CELL_VALUE
)}
</span>
</div>

View file

@ -7,6 +7,7 @@ import Button from "components/buttons/Button";
import DiskSpaceGraph from "components/DiskSpaceGraph";
import HumanTimeDiffWithDateTip from "components/HumanTimeDiffWithDateTip";
import { humanHostMemory, wrapFleetHelper } from "utilities/helpers";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import getHostStatusTooltipText from "pages/hosts/helpers";
import StatusIndicator from "components/StatusIndicator";
import IssueIcon from "../../../../../../assets/images/icon-issue-fleet-black-50-16x16@2x.png";
@ -212,7 +213,9 @@ const HostSummary = ({
<div className="title__inner">
<div className="display-name-container">
<h1 className="display-name">
{deviceUser ? "My device" : titleData.display_name || "---"}
{deviceUser
? "My device"
: titleData.display_name || DEFAULT_EMPTY_CELL_VALUE}
</h1>
<p className="last-fetched">

View file

@ -17,6 +17,7 @@ import { IOsqueryPlatform } from "interfaces/platform";
import { IQuery, IFleetQueriesResponse } from "interfaces/query";
import fleetQueriesAPI from "services/entities/queries";
import PATHS from "router/paths";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import checkPlatformCompatibility from "utilities/sql_tools";
import Button from "components/buttons/Button";
// @ts-ignore
@ -67,7 +68,7 @@ const PLATFORM_FILTER_OPTIONS = [
const getPlatforms = (queryString: string): Array<IOsqueryPlatform | "---"> => {
const { platforms } = checkPlatformCompatibility(queryString);
return platforms || ["---"];
return platforms || [DEFAULT_EMPTY_CELL_VALUE];
};
const enhanceQuery = (q: IQuery) => {

View file

@ -12,6 +12,7 @@ import {
import softwareAPI from "services/entities/software";
import hostCountAPI from "services/entities/host_count";
import { DEFAULT_EMPTY_CELL_VALUE } from "utilities/constants";
import Spinner from "components/Spinner";
import BackLink from "components/BackLink";
import MainContent from "components/MainContent";
@ -96,7 +97,9 @@ const SoftwareDetailsPage = ({
</div>
<div className="info-flex__item info-flex__item--title">
<span className="info-flex__header">Hosts</span>
<span className={`info-flex__data`}>{hostCount || "---"}</span>
<span className={`info-flex__data`}>
{hostCount || DEFAULT_EMPTY_CELL_VALUE}
</span>
</div>
</div>
</div>

View file

@ -47,6 +47,12 @@ a {
text-decoration: none;
}
.__react_component_tooltip {
a {
color: $core-white;
}
}
.body-wrap {
padding: $pad-xlarge;
border-radius: 3px;

View file

@ -38,6 +38,7 @@ import { IUser } from "interfaces/user";
import stringUtils from "utilities/strings";
import sortUtils from "utilities/sort";
import {
DEFAULT_EMPTY_CELL_VALUE,
DEFAULT_GRAVATAR_LINK,
DEFAULT_GRAVATAR_LINK_DARK,
PLATFORM_LABEL_DISPLAY_TYPES,
@ -575,7 +576,7 @@ export const humanHostLastRestart = (
if (
!detailUpdatedAt ||
!uptime ||
detailUpdatedAt === "---" ||
detailUpdatedAt === DEFAULT_EMPTY_CELL_VALUE ||
detailUpdatedAt < "2016-07-28T00:00:00Z" ||
typeof uptime !== "number"
) {
@ -825,7 +826,7 @@ export const normalizeEmptyValues = (
if ((Number.isFinite(value) && value !== 0) || !isEmpty(value)) {
Object.assign(result, { [key]: value });
} else {
Object.assign(result, { [key]: "---" });
Object.assign(result, { [key]: DEFAULT_EMPTY_CELL_VALUE });
}
return result;
},
@ -837,7 +838,7 @@ export const wrapFleetHelper = (
helperFn: (value: any) => string, // TODO: replace any with unknown and improve type narrowing by callers
value: string
): string => {
return value === "---" ? value : helperFn(value);
return value === DEFAULT_EMPTY_CELL_VALUE ? value : helperFn(value);
};
export default {