mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Fleet UI: Undetermined public ip tooltip (#9907)
This commit is contained in:
parent
c3a9a1cd94
commit
7f6a42e4ac
16 changed files with 124 additions and 26 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const AppleBMTermsMessage = () => {
|
|||
text="Go to ABM"
|
||||
className={`${baseClass}__new-tab`}
|
||||
newTab
|
||||
black
|
||||
iconColor="core-fleet-black"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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}`}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -47,6 +47,12 @@ a {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.__react_component_tooltip {
|
||||
a {
|
||||
color: $core-white;
|
||||
}
|
||||
}
|
||||
|
||||
.body-wrap {
|
||||
padding: $pad-xlarge;
|
||||
border-radius: 3px;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue