fleet/frontend/components/TableContainer/DataTable/SoftwareNameCell/SoftwareNameCell.tsx
Carlo 715d963f82
My device page (self-service) for iOS/iPadOS (#35238)
Implements #32247. This is the complete feature branch, consolidating:

- https://github.com/fleetdm/fleet/pull/35018
- https://github.com/fleetdm/fleet/pull/34758
- https://github.com/fleetdm/fleet/pull/35009
- https://github.com/fleetdm/fleet/pull/35181
- https://github.com/fleetdm/fleet/pull/35342

---------

Co-authored-by: Jonathan Katz <44128041+jkatz01@users.noreply.github.com>
Co-authored-by: RachelElysia <71795832+RachelElysia@users.noreply.github.com>
Co-authored-by: Martin Angers <martin.n.angers@gmail.com>
Co-authored-by: jkatz01 <yehonatankatz@gmail.com>
2025-11-07 17:30:51 -05:00

213 lines
5.4 KiB
TypeScript

import React from "react";
import { InjectedRouter } from "react-router";
import { getSelfServiceTooltip } from "pages/SoftwarePage/helpers";
import TooltipWrapper from "components/TooltipWrapper";
import Icon from "components/Icon";
import { IconNames } from "components/icons";
import SoftwareIcon from "pages/SoftwarePage/components/icons/SoftwareIcon";
import LinkCell from "../LinkCell";
import TooltipTruncatedTextCell from "../TooltipTruncatedTextCell";
const baseClass = "software-name-cell";
type InstallType =
| "manual"
| "selfService"
| "automatic"
| "automaticSelfService";
export type PageContext = "deviceUser" | "hostDetails" | "hostDetailsLibrary";
interface InstallIconTooltip {
automaticInstallPoliciesCount?: number;
pageContext?: PageContext;
isIosOrIpados?: boolean;
}
interface InstallIconConfig {
iconName: IconNames;
tooltip: ({
automaticInstallPoliciesCount,
pageContext,
}: InstallIconTooltip) => JSX.Element;
}
const getPolicyTooltip = (count = 0) =>
count === 1
? "A policy triggers install."
: `${count} policies trigger install.`;
const installIconMap: Record<InstallType, InstallIconConfig> = {
manual: {
iconName: "install",
tooltip: ({ pageContext }) => (
<>
Software can be installed on the{" "}
{pageContext === "hostDetails" ? "Library tab" : "Host details page"}.
</>
),
},
selfService: {
iconName: "user",
tooltip: ({ isIosOrIpados = false }) =>
getSelfServiceTooltip(isIosOrIpados),
},
automatic: {
iconName: "refresh",
tooltip: ({ automaticInstallPoliciesCount = 0 }) => (
<>{getPolicyTooltip(automaticInstallPoliciesCount)}</>
),
},
automaticSelfService: {
iconName: "automatic-self-service",
tooltip: ({ automaticInstallPoliciesCount = 0 }) => (
<>
{getPolicyTooltip(automaticInstallPoliciesCount)}
<br />
End users can reinstall from
<br />
<b>Fleet Desktop {">"} Self-service</b>.
</>
),
},
};
interface IInstallIconWithTooltipProps {
isSelfService: boolean;
automaticInstallPoliciesCount?: number;
pageContext?: PageContext;
isIosOrIpados?: boolean;
}
const getInstallIconType = (
isSelfService: boolean,
automaticInstallPoliciesCount = 0
): InstallType => {
if (automaticInstallPoliciesCount > 0) {
return isSelfService ? "automaticSelfService" : "automatic";
}
return isSelfService ? "selfService" : "manual";
};
const InstallIconWithTooltip = ({
isSelfService,
automaticInstallPoliciesCount,
pageContext,
isIosOrIpados = false,
}: IInstallIconWithTooltipProps) => {
const iconType = getInstallIconType(
isSelfService,
automaticInstallPoliciesCount
);
// Don't show installer icon on host software library page
if (iconType === "manual" && pageContext === "hostDetailsLibrary") {
return null;
}
const { iconName, tooltip } = installIconMap[iconType];
const tipContent = tooltip({
automaticInstallPoliciesCount,
pageContext,
isIosOrIpados,
});
return (
<div className={`${baseClass}__install-icon-with-tooltip`}>
<TooltipWrapper
tipContent={tipContent}
showArrow
underline={false}
position="top"
tipOffset={12}
>
<Icon
name={iconName}
className={`${baseClass}__install-icon`}
color="ui-fleet-black-50"
/>
</TooltipWrapper>
</div>
);
};
interface ISoftwareNameCellProps {
/** Used to key default software icon and name displayed if no display_name */
name: string;
/** Overrides name for display */
display_name?: string;
source?: string;
/** pass in a `path` that this cell will link to */
path?: string;
router?: InjectedRouter;
pageContext?: PageContext;
hasInstaller?: boolean;
isSelfService?: boolean;
automaticInstallPoliciesCount?: number;
/** e.g. custom icons & app_store_app's override default icons with URLs */
iconUrl?: string | null;
}
const SoftwareNameCell = ({
name,
display_name,
source,
path,
router,
pageContext,
hasInstaller = false,
isSelfService = false,
automaticInstallPoliciesCount,
iconUrl,
}: ISoftwareNameCellProps) => {
const icon = <SoftwareIcon name={name} source={source} url={iconUrl} />;
// My device page > Software fake link as entire row opens a modal
if (pageContext === "deviceUser" && !isSelfService) {
return (
<LinkCell tooltipTruncate prefix={icon} value={display_name || name} />
);
}
// Non-clickable cell if no router/path (e.g. My device page > SelfService)
if (!router || !path) {
return (
<div className={baseClass}>
<TooltipTruncatedTextCell
prefix={icon}
value={display_name || name}
className="software-name"
/>
</div>
);
}
const onClickSoftware = (e: React.MouseEvent) => {
// Allows for button to be clickable in a clickable row
e.stopPropagation();
router.push(path);
};
return (
<LinkCell
className={baseClass}
path={path}
tooltipTruncate
customOnClick={onClickSoftware}
prefix={icon}
value={display_name || name}
suffix={
hasInstaller ? (
<InstallIconWithTooltip
isSelfService={isSelfService}
automaticInstallPoliciesCount={automaticInstallPoliciesCount}
pageContext={pageContext}
/>
) : undefined
}
/>
);
};
export default SoftwareNameCell;