Update software details modal on host details page to include VPP installs (#20638)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
+82
-52
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user