Update software details modal on host details page to include VPP installs (#20638)

This commit is contained in:
Sarah Gillespie
2024-07-22 16:07:31 -05:00
committed by GitHub
parent 746c442d01
commit 06cbdeba36
5 changed files with 106 additions and 55 deletions
@@ -7,6 +7,10 @@
align-items: center;
gap: $pad-small;
margin: 0;
.icon {
padding-top: 3px;
align-self: flex-start;
}
}
&__script-output {
padding-top: $pad-xlarge;
+10
View File
@@ -291,3 +291,13 @@ export const INSTALL_STATUS_ICONS: Record<SoftwareInstallStatus, IconNames> = {
installed: "success-outline",
failed: "error-outline",
} as const;
export type IHostSoftwareWithLastInstall = IHostSoftware & {
last_install: ISoftwareLastInstall;
};
export const hasLastInstall = (
software: IHostSoftware
): software is IHostSoftwareWithLastInstall => {
return !!software.last_install;
};
@@ -1046,6 +1046,7 @@ const HostDetailsPage = ({
)}
{selectedSoftwareDetails && (
<SoftwareDetailsModal
hostDisplayName={host.display_name}
software={selectedSoftwareDetails}
onExit={() => setSelectedSoftwareDetails(null)}
/>
@@ -4,7 +4,9 @@ import { CellProps, Column } from "react-table";
import { cloneDeep } from "lodash";
import {
IHostAppStoreApp,
IHostSoftware,
IHostSoftwarePackage,
SoftwareInstallStatus,
formatSoftwareType,
} from "interfaces/software";
@@ -53,14 +55,16 @@ const generateActions = ({
isFleetdHost,
softwareId,
status,
hasSoftwareToInstall,
software_package,
app_store_app,
}: {
canInstall: boolean;
installingSoftwareId: number | null;
isFleetdHost: boolean;
softwareId: number;
status: SoftwareInstallStatus | null;
hasSoftwareToInstall?: boolean;
software_package: IHostSoftwarePackage | null;
app_store_app: IHostAppStoreApp | null;
}) => {
// this gives us a clean slate of the default actions so we can modify
// the options.
@@ -73,6 +77,7 @@ const generateActions = ({
throw new Error("Install action not found in default actions");
}
const hasSoftwareToInstall = !!software_package || !!app_store_app;
// remove install if there is no package to install
if (!hasSoftwareToInstall || !canInstall) {
actions.splice(indexInstallAction, 1);
@@ -202,7 +207,8 @@ export const generateSoftwareTableHeaders = ({
installingSoftwareId,
softwareId,
status,
hasSoftwareToInstall: !!software_package || !!app_store_app,
software_package,
app_store_app,
})}
onChange={(action) => onSelectAction(original, action)}
/>
@@ -3,8 +3,10 @@ import { Tab, TabList, TabPanel, Tabs } from "react-tabs";
import {
IHostSoftware,
IHostSoftwareWithLastInstall,
ISoftwareInstallVersion,
formatSoftwareType,
hasLastInstall,
} from "interfaces/software";
import Modal from "components/Modal";
@@ -13,6 +15,7 @@ import Button from "components/buttons/Button";
import DataSet from "components/DataSet";
import { dateAgo } from "utilities/date_format";
import { AppInstallDetails } from "components/ActivityDetails/InstallDetails/AppInstallDetails";
import { SoftwareInstallDetails } from "components/ActivityDetails/InstallDetails/SoftwareInstallDetails";
import TooltipTruncatedText from "components/TooltipTruncatedText";
@@ -88,69 +91,96 @@ const SoftwareDetailsInfo = ({
};
interface ISoftwareDetailsModalProps {
hostDisplayName: string;
software: IHostSoftware;
onExit: () => void;
}
const SoftwareDetailsContent = ({
software,
}: Pick<ISoftwareDetailsModalProps, "software">) => {
const { installed_versions } = software;
// special case when we dont have installed versions. We can only show the
// software type atm.
if (!installed_versions || installed_versions.length === 0) {
return (
<div className={`${baseClass}__software-details`}>
<DataSet
title="Type"
value={formatSoftwareType({ source: software.source })}
/>
</div>
);
}
return (
<div className={`${baseClass}__software-details`}>
{installed_versions?.map((installedVersion) => {
return (
<SoftwareDetailsInfo
key={installedVersion.version}
installedVersion={installedVersion}
source={software.source}
bundleIdentifier={software.bundle_identifier}
/>
);
})}
</div>
);
};
const TabsContent = ({
hostDisplayName,
software,
}: {
hostDisplayName: string;
software: IHostSoftwareWithLastInstall;
}) => {
return (
<TabsWrapper>
<Tabs>
<TabList>
<Tab>Software details</Tab>
<Tab>Install details</Tab>
</TabList>
<TabPanel>
<SoftwareDetailsContent software={software} />
</TabPanel>
<TabPanel>
{software.app_store_app ? (
<AppInstallDetails
command_uuid={software.last_install.install_uuid}
host_display_name={hostDisplayName}
software_title={software.name}
status={
software.status || undefined // FIXME: we have a type mismatch here; as a workaroud this will coerce null to undefined, which in turn defaults to "pending"
}
/>
) : (
<SoftwareInstallDetails
installUuid={software.last_install.install_uuid || ""}
/>
)}
</TabPanel>
</Tabs>
</TabsWrapper>
);
};
const SoftwareDetailsModal = ({
hostDisplayName,
software,
onExit,
}: ISoftwareDetailsModalProps) => {
const installUuid = software.last_install?.install_uuid || "";
const renderSoftwareDetails = () => {
const { installed_versions } = software;
// special case when we dont have installed versions. We can only show the
// software type atm.
if (!installed_versions || installed_versions.length === 0) {
return (
<div className={`${baseClass}__software-details`}>
<DataSet
title="Type"
value={formatSoftwareType({ source: software.source })}
/>
</div>
);
}
return (
<div className={`${baseClass}__software-details`}>
{installed_versions?.map((installedVersion) => {
return (
<SoftwareDetailsInfo
key={installedVersion.version}
installedVersion={installedVersion}
source={software.source}
bundleIdentifier={software.bundle_identifier}
/>
);
})}
</div>
);
};
const renderTabs = () => {
return (
<TabsWrapper>
<Tabs>
<TabList>
<Tab>Software details</Tab>
<Tab>Install Details</Tab>
</TabList>
<TabPanel>{renderSoftwareDetails()}</TabPanel>
<TabPanel>
<SoftwareInstallDetails installUuid={installUuid} />
</TabPanel>
</Tabs>
</TabsWrapper>
);
};
return (
<Modal title={software.name} className={baseClass} onExit={onExit}>
<>
{software.last_install ? renderTabs() : renderSoftwareDetails()}
{!hasLastInstall(software) ? (
<SoftwareDetailsContent software={software} />
) : (
<TabsContent hostDisplayName={hostDisplayName} software={software} />
)}
<div className="modal-cta-wrap">
<Button type="submit" variant="brand" onClick={onExit}>
Done