From f21e9f67b22f3698c8f3cefc77b419ba3d64e615 Mon Sep 17 00:00:00 2001 From: RachelElysia <71795832+RachelElysia@users.noreply.github.com> Date: Wed, 8 Oct 2025 13:30:58 -0400 Subject: [PATCH] Fleet UI: Remove install tooltip and add timestamp to InstallDetails modal (#33991) --- .../SoftwareInstallDetailsModal.tests.tsx | 16 +++++--- .../SoftwareInstallDetailsModal.tsx | 41 ++++++++++--------- .../SoftwareUninstallDetailsModal.tests.tsx | 8 ++-- .../SoftwareUninstallDetailsModal.tsx | 8 ++-- .../VppInstallDetailsModal.tests.tsx | 2 +- .../VppInstallDetailsModal.tsx | 18 ++++---- .../InstallStatusCell.tests.tsx | 9 ++-- .../InstallStatusCell/InstallStatusCell.tsx | 16 +------- 8 files changed, 56 insertions(+), 62 deletions(-) diff --git a/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tests.tsx b/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tests.tsx index 2ed8d2f9c6..889ec19ace 100644 --- a/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tests.tsx +++ b/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tests.tsx @@ -19,7 +19,7 @@ import SoftwareInstallDetailsModal, { describe("SoftwareInstallDetailsModal", () => { describe("StatusMessage component", () => { it("renders basic 'is installed' message when not installed by fleet (no installResult provided)", () => { - render(); + render(); expect(screen.getByText(/CoolApp/)).toBeInTheDocument(); expect(screen.getByText(/is installed/)).toBeInTheDocument(); }); @@ -31,7 +31,7 @@ describe("SoftwareInstallDetailsModal", () => { installResult={createMockSoftwareInstallResult({ status: "pending_install", })} - isDUP={false} + isMyDevicePage={false} /> ); @@ -42,6 +42,7 @@ describe("SoftwareInstallDetailsModal", () => { expect(screen.getByText(/\(com\.cool\.app\)/)).toBeInTheDocument(); expect(screen.getByText(/Test Host/)).toBeInTheDocument(); expect(screen.getByText(/when it comes online/)).toBeInTheDocument(); + expect(screen.queryByText(/\d+.*ago/)).not.toBeInTheDocument(); }); it("on device user page, renders failed install with retry option with contact link", () => { @@ -51,7 +52,7 @@ describe("SoftwareInstallDetailsModal", () => { installResult={createMockSoftwareInstallResult({ status: "failed_install", })} - isDUP + isMyDevicePage contactUrl="http://support" /> ); @@ -61,6 +62,7 @@ describe("SoftwareInstallDetailsModal", () => { expect(screen.getByText(/CoolApp/)).toBeInTheDocument(); // Host name should not be rendered for device user page expect(screen.queryByText(/Test Host/)).not.toBeInTheDocument(); + expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument(); expect(screen.getByText(/You can retry/)).toBeInTheDocument(); expect( screen.getByRole("link", { name: /contact your IT admin/ }) @@ -74,7 +76,7 @@ describe("SoftwareInstallDetailsModal", () => { installResult={createMockSoftwareInstallResult({ status: "failed_install", })} - isDUP + isMyDevicePage /> ); @@ -83,6 +85,7 @@ describe("SoftwareInstallDetailsModal", () => { expect(screen.getByText(/CoolApp/)).toBeInTheDocument(); // Host name should not be rendered for device user page expect(screen.queryByText(/Test Host/)).not.toBeInTheDocument(); + expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument(); expect(screen.getByText(/You can retry/)).toBeInTheDocument(); // Don't show link of not provided expect( @@ -97,7 +100,7 @@ describe("SoftwareInstallDetailsModal", () => { installResult={createMockSoftwareInstallResult({ status: "failed_install", })} - isDUP={false} + isMyDevicePage={false} contactUrl="http://support" /> ); @@ -106,6 +109,7 @@ describe("SoftwareInstallDetailsModal", () => { expect(screen.getByText(/failed to install/)).toBeInTheDocument(); expect(screen.getByText(/Test Host/)).toBeInTheDocument(); expect(screen.queryByText(/You can retry/)).not.toBeInTheDocument(); + expect(screen.getByText(/\d+.*ago/)).toBeInTheDocument(); }); it("on host details page/install activity, renders installed message with timestamp", () => { @@ -115,7 +119,7 @@ describe("SoftwareInstallDetailsModal", () => { installResult={createMockSoftwareInstallResult({ status: "installed", })} - isDUP={false} + isMyDevicePage={false} /> ); diff --git a/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tsx b/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tsx index aaee988a0c..d7c490c53c 100644 --- a/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tsx +++ b/frontend/components/ActivityDetails/InstallDetails/SoftwareInstallDetailsModal/SoftwareInstallDetailsModal.tsx @@ -53,7 +53,7 @@ export const renderContactOption = (url?: string) => ( interface IInstallStatusMessage { softwareName: string; installResult?: ISoftwareInstallResult; - isDUP: boolean; + isMyDevicePage: boolean; contactUrl?: string; } @@ -62,7 +62,7 @@ interface IInstallStatusMessage { export const StatusMessage = ({ softwareName, installResult, - isDUP, + isMyDevicePage, contactUrl, }: IInstallStatusMessage) => { // the case when software is installed by the user and not by Fleet @@ -107,22 +107,25 @@ export const StatusMessage = ({ Fleet {getInstallDetailsStatusPredicate(status)} {software_title} ); - let middle = null; - if (isDUP) { - if (status === "failed_install") { - middle = <>. You can retry{renderContactOption(contactUrl)}; - } - } else { - // host details page - middle = ( - <> - {" "} - ({software_package}) on {formattedHost} - {status === "pending_install" ? " when it comes online" : ""} - {displayTimeStamp} - - ); - } + + const middle = isMyDevicePage ? ( + <> + {" "} + {displayTimeStamp} + {status === "failed_install" && ( + <>. You can retry{renderContactOption(contactUrl)} + )} + + ) : ( + <> + {" "} + ({software_package}) on {formattedHost} + {status === "pending_install" + ? " when it comes online" + : displayTimeStamp} + + ); + return ( {prefix} @@ -349,7 +352,7 @@ export const SoftwareInstallDetailsModal = ({ diff --git a/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tests.tsx b/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tests.tsx index dcef508182..e7dea355c5 100644 --- a/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tests.tsx +++ b/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tests.tsx @@ -15,7 +15,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => { status="pending_uninstall" softwareName="CoolApp" softwarePackageName="com.cool.app" - isDUP={false} + isMyDevicePage={false} /> ); @@ -36,7 +36,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => { hostDisplayName="Test Host" status="failed_uninstall" softwareName="CoolApp" - isDUP + isMyDevicePage contactUrl="http://support" /> ); @@ -54,7 +54,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => { hostDisplayName="Test Host" status="failed_uninstall" softwareName="CoolApp" - isDUP={false} + isMyDevicePage={false} contactUrl="http://support" /> ); @@ -74,7 +74,7 @@ describe("SoftwareUninstallDetailsModal - StatusMessage component", () => { softwareName="CoolApp" softwarePackageName="com.cool.app" timestamp="2025-08-10T12:00:00Z" - isDUP={false} + isMyDevicePage={false} /> ); diff --git a/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tsx b/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tsx index ecac88aecf..113c3e2d39 100644 --- a/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tsx +++ b/frontend/components/ActivityDetails/InstallDetails/SoftwareUninstallDetailsModal/SoftwareUninstallDetailsModal.tsx @@ -36,7 +36,7 @@ interface IUninstallStatusMessage { softwareName: string; softwarePackageName?: string; timestamp?: string; - isDUP: boolean; + isMyDevicePage: boolean; contactUrl?: string; } @@ -46,7 +46,7 @@ export const StatusMessage = ({ softwareName, softwarePackageName, timestamp, - isDUP, + isMyDevicePage, contactUrl, }: IUninstallStatusMessage) => { const formattedHost = hostDisplayName ? {hostDisplayName} : "the host"; @@ -67,7 +67,7 @@ export const StatusMessage = ({ ); let suffix = null; - if (isDUP) { + if (isMyDevicePage) { if (status === "failed_uninstall") { suffix = <>. You can retry{renderContactOption(contactUrl)}; } @@ -232,7 +232,7 @@ const SoftwareUninstallDetailsModal = ({ softwareName={softwareName} softwarePackageName={softwarePackageName} timestamp={uninstallResult?.created_at} - isDUP={!!deviceAuthToken} + isMyDevicePage={!!deviceAuthToken} contactUrl={contactUrl} /> {uninstallStatus !== "pending_uninstall" && ( diff --git a/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx b/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx index 95794aa8e1..5913eb8fcf 100644 --- a/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx +++ b/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tests.tsx @@ -158,7 +158,7 @@ describe("getStatusMessage helper function", () => { it("on the device user page, does not show host info", () => { render( getStatusMessage({ - isDUP: true, + isMyDevicePage: true, displayStatus: "installed", isMDMStatusNotNow: false, isMDMStatusAcknowledged: false, diff --git a/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tsx b/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tsx index 07e7da13f0..8644ec313f 100644 --- a/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tsx +++ b/frontend/components/ActivityDetails/InstallDetails/VppInstallDetailsModal/VppInstallDetailsModal.tsx @@ -29,7 +29,7 @@ import { } from "../constants"; interface IGetStatusMessageProps { - isDUP?: boolean; + isMyDevicePage?: boolean; /** "pending" is an edge case here where VPP install activities that were added to the feed prior to v4.57 * (when we split pending into pending_install/pending_uninstall) will list the status as "pending" rather than "pending_install" */ displayStatus: SoftwareInstallStatus | "pending"; @@ -41,7 +41,7 @@ interface IGetStatusMessageProps { } export const getStatusMessage = ({ - isDUP = false, + isMyDevicePage = false, displayStatus, isMDMStatusNotNow, isMDMStatusAcknowledged, @@ -79,7 +79,7 @@ export const getStatusMessage = ({ return ( <> Fleet tried to install {appName} - {!isDUP && ( + {!isMyDevicePage && ( <> {" "} on {formattedHost} but couldn't because the host was locked or @@ -96,9 +96,9 @@ export const getStatusMessage = ({ return ( <> The MDM command (request) to install {appName} - {!isDUP && <> on {formattedHost}} was acknowledged but the + {!isMyDevicePage && <> on {formattedHost}} was acknowledged but the installation has not been verified. To re-check, select Refetch - {!isDUP && " for this host"}. + {!isMyDevicePage && " for this host"}. ); } @@ -108,7 +108,7 @@ export const getStatusMessage = ({ return ( <> The MDM command (request) to install {appName} - {!isDUP && <> on {formattedHost}} was acknowledged but the + {!isMyDevicePage && <> on {formattedHost}} was acknowledged but the installation has not been verified. Please re-attempt this installation. ); @@ -119,7 +119,7 @@ export const getStatusMessage = ({ return ( <> The MDM command (request) to install {appName} - {!isDUP && <> on {formattedHost}} failed + {!isMyDevicePage && <> on {formattedHost}} failed {displayTimeStamp && <> {displayTimeStamp}}. Please re-attempt this installation. @@ -127,7 +127,7 @@ export const getStatusMessage = ({ } const renderSuffix = () => { - if (isDUP) { + if (isMyDevicePage) { return <> {displayTimeStamp && <> {displayTimeStamp}}; } return ( @@ -299,7 +299,7 @@ export const VppInstallDetailsModal = ({ : true; // if no hostSoftware passed in, can assume this is the activity feed, meaning this can only refer to a Fleet-handled install const statusMessage = getStatusMessage({ - isDUP: !!deviceAuthToken, + isMyDevicePage: !!deviceAuthToken, displayStatus, isMDMStatusNotNow, isMDMStatusAcknowledged, diff --git a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx index f7d03adee1..4eada43992 100644 --- a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx +++ b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tests.tsx @@ -19,7 +19,7 @@ jest.mock("lodash", () => ({ const testSoftwarePackage = createMockHostSoftwarePackage(); describe("InstallStatusCell - component", () => { - it("renders 'Installed' status with tooltip", async () => { + it("renders 'Installed' status without tooltip", async () => { const { user } = renderWithSetup( { await user.hover(screen.getByText("Installed")); - await waitFor(() => { - expect(screen.getByText(/Software was installed/i)).toBeInTheDocument(); - }); + // No tooltip on install status + expect( + screen.queryByText(/Software was installed/i) + ).not.toBeInTheDocument(); // There SHOULD be a button with this label expect( diff --git a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx index 66f446ae00..6eac9f7522 100644 --- a/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx +++ b/frontend/pages/hosts/details/cards/Software/InstallStatusCell/InstallStatusCell.tsx @@ -74,21 +74,7 @@ export const INSTALL_STATUS_DISPLAY_OPTIONS: Record< installed: { iconName: "success", displayText: "Installed", - tooltip: ({ isSelfService, isAppStoreApp, lastInstalledAt }) => { - if (!lastInstalledAt) { - return undefined; - } - - return ( - <> - Software was installed{" "} - {!isSelfService && - !isAppStoreApp && - "(install script finished with exit code 0) "} - {dateAgo(lastInstalledAt)}. - - ); - }, + tooltip: () => undefined, // No tooltip for installed state }, recently_updated: { iconName: "success",