From d87f69563ff276ebef71d5bea7f401d51bba9130 Mon Sep 17 00:00:00 2001
From: jacobshandling <61553566+jacobshandling@users.noreply.github.com>
Date: Mon, 1 Dec 2025 13:17:42 -0800
Subject: [PATCH] Fix and improve handling of "installed" software scenarios
(#36516)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**Related issue:** Resolves #31973
Addresses a few closely related issues around determining the
appropriate UI to display for installed software
- Pass the inherited host's `softwareUpdatedAt` to the handler
responsible for determining the software's "UI display status" so it can
calculate whether it is "recently installed", as intended.
- In the scenario where the above is _not_ passed in for some reason,
default to "installed" ui display status any time the software's status
is "installed"
- Add a check that ensures the above default "installed" ui status is
captured even when `installed_versions` is `null`. This scenario
previously returned an "uninstalled" ui display status, which caused
this bug initially
- [x] Changes file added for user-visible changes in `changes/
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
---
changes/31973-improve-installed-ui-status | 2 ++
.../HostSoftwareLibrary.tsx | 4 ++--
.../details/cards/Software/helpers.tests.ts | 7 +++++++
.../hosts/details/cards/Software/helpers.tsx | 19 +++++++++----------
4 files changed, 20 insertions(+), 12 deletions(-)
create mode 100644 changes/31973-improve-installed-ui-status
diff --git a/changes/31973-improve-installed-ui-status b/changes/31973-improve-installed-ui-status
new file mode 100644
index 0000000000..1b78189fa3
--- /dev/null
+++ b/changes/31973-improve-installed-ui-status
@@ -0,0 +1,2 @@
+- Improved handling of softawre install statuses in the UI, fixing a bug where "installed" software
+ sometimes showed up as "uninstalled" when certain other pieces of data were not also present
diff --git a/frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibrary.tsx b/frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibrary.tsx
index fcfb37d60c..64d269184b 100644
--- a/frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibrary.tsx
+++ b/frontend/pages/hosts/details/cards/HostSoftwareLibrary/HostSoftwareLibrary.tsx
@@ -174,9 +174,9 @@ const HostSoftwareLibrary = ({
if (!hostSoftwareLibraryRes) return [];
return hostSoftwareLibraryRes.software.map((software) => ({
...software,
- ui_status: getUiStatus(software, isHostOnline),
+ ui_status: getUiStatus(software, isHostOnline, softwareUpdatedAt),
}));
- }, [hostSoftwareLibraryRes, isHostOnline]);
+ }, [hostSoftwareLibraryRes, isHostOnline, softwareUpdatedAt]);
const pendingSoftwareSetRef = useRef>(new Set()); // Track for polling
const pollingTimeoutIdRef = useRef(null);
diff --git a/frontend/pages/hosts/details/cards/Software/helpers.tests.ts b/frontend/pages/hosts/details/cards/Software/helpers.tests.ts
index 585a9f2c57..a1cf60010b 100644
--- a/frontend/pages/hosts/details/cards/Software/helpers.tests.ts
+++ b/frontend/pages/hosts/details/cards/Software/helpers.tests.ts
@@ -309,6 +309,13 @@ describe("getUiStatus", () => {
});
expect(getUiStatus(sw, true)).toBe("installed");
});
+ it("returns 'installed' for regular package, no installed versions present", () => {
+ const sw = createMockHostSoftware({
+ status: "installed",
+ installed_versions: null,
+ });
+ expect(getUiStatus(sw, true)).toBe("installed");
+ });
it("returns 'installed' for regular package, installed version higher than library version", () => {
const sw = createMockHostSoftware({
diff --git a/frontend/pages/hosts/details/cards/Software/helpers.tsx b/frontend/pages/hosts/details/cards/Software/helpers.tsx
index e53e39ed1a..a780219b48 100644
--- a/frontend/pages/hosts/details/cards/Software/helpers.tsx
+++ b/frontend/pages/hosts/details/cards/Software/helpers.tsx
@@ -249,7 +249,7 @@ export const getUiStatus = (
}
// **Recently_uninstalled check comes BEFORE update_available**
- if (software.status === null && lastUninstallDate && hostSoftwareUpdatedAt) {
+ if (status === null && lastUninstallDate && hostSoftwareUpdatedAt) {
const newerDate = getNewerDate(hostSoftwareUpdatedAt, lastUninstallDate);
if (newerDate === lastUninstallDate || recentUserActionDetected) {
return "recently_uninstalled";
@@ -276,19 +276,18 @@ export const getUiStatus = (
}
// 6. Recently installed (not an update)
- if (
- software.status === "installed" &&
- lastInstallDate &&
- hostSoftwareUpdatedAt
- ) {
- const newerDate = getNewerDate(hostSoftwareUpdatedAt, lastInstallDate);
- if (newerDate === lastInstallDate || recentUserActionDetected) {
- return "recently_installed";
+ if (status === "installed") {
+ if (lastInstallDate && hostSoftwareUpdatedAt) {
+ const newerDate = getNewerDate(hostSoftwareUpdatedAt, lastInstallDate);
+ if (newerDate === lastInstallDate || recentUserActionDetected) {
+ return "recently_installed";
+ }
}
+ return "installed";
}
// 7. Tarballs edge case
- if (software.source === "tgz_packages" && software.status === "installed") {
+ if (source === "tgz_packages" && status === "installed") {
return "installed";
}