mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 01:18:42 +00:00
Add last used column to host details software table (#5681)
This commit is contained in:
parent
ba820384f9
commit
a6b2d2413a
6 changed files with 120 additions and 55 deletions
1
changes/issue-3399-last-used-column
Normal file
1
changes/issue-3399-last-used-column
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Add last used column to host details' software table for Mac OS hosts
|
||||
|
|
@ -596,6 +596,7 @@ const HostDetailsPage = ({
|
|||
isLoading={isLoadingHost}
|
||||
software={hostSoftware}
|
||||
softwareInventoryEnabled={hostSettings?.enable_software_inventory}
|
||||
deviceType={host?.platform === "darwin" ? "macos" : ""}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ interface ISoftwareTableProps {
|
|||
isLoading: boolean;
|
||||
software: ISoftware[];
|
||||
deviceUser?: boolean;
|
||||
deviceType?: string;
|
||||
softwareInventoryEnabled?: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ const SoftwareTable = ({
|
|||
isLoading,
|
||||
software,
|
||||
deviceUser,
|
||||
deviceType,
|
||||
softwareInventoryEnabled,
|
||||
}: ISoftwareTableProps): JSX.Element => {
|
||||
const tableSoftware: ITableSoftware[] = software.map((s) => {
|
||||
|
|
@ -104,27 +106,29 @@ const SoftwareTable = ({
|
|||
/>
|
||||
)}
|
||||
{software && (
|
||||
<TableContainer
|
||||
columns={tableHeaders}
|
||||
data={tableSoftware}
|
||||
filters={filters}
|
||||
isLoading={isLoading}
|
||||
defaultSortHeader={"name"}
|
||||
defaultSortDirection={"asc"}
|
||||
inputPlaceHolder={
|
||||
"Search software by name or vulnerabilities (CVEs)"
|
||||
}
|
||||
onQueryChange={onQueryChange}
|
||||
resultsTitle={"software items"}
|
||||
emptyComponent={EmptySoftwareSearch}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
searchable
|
||||
customControl={renderVulnFilterDropdown}
|
||||
isClientSidePagination
|
||||
isClientSideFilter
|
||||
highlightOnHover
|
||||
/>
|
||||
<div className={deviceType || ""}>
|
||||
<TableContainer
|
||||
columns={tableHeaders}
|
||||
data={tableSoftware}
|
||||
filters={filters}
|
||||
isLoading={isLoading}
|
||||
defaultSortHeader={"name"}
|
||||
defaultSortDirection={"asc"}
|
||||
inputPlaceHolder={
|
||||
"Search software by name or vulnerabilities ( CVEs)"
|
||||
}
|
||||
onQueryChange={onQueryChange}
|
||||
resultsTitle={"software items"}
|
||||
emptyComponent={EmptySoftwareSearch}
|
||||
showMarkAllPages={false}
|
||||
isAllPagesSelected={false}
|
||||
searchable
|
||||
customControl={renderVulnFilterDropdown}
|
||||
isClientSidePagination
|
||||
isClientSideFilter
|
||||
highlightOnHover
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import React from "react";
|
|||
import { Link } from "react-router";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
|
||||
// TODO: Enable after backend has been updated to provide last_opened_at
|
||||
// import distanceInWordsToNow from "date-fns/distance_in_words_to_now";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
|
||||
import { ISoftware } from "interfaces/software";
|
||||
|
||||
|
|
@ -25,6 +24,7 @@ interface ICellProps {
|
|||
};
|
||||
row: {
|
||||
original: ISoftware;
|
||||
index: number;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +40,12 @@ interface IVulnCellProps extends ICellProps {
|
|||
};
|
||||
}
|
||||
|
||||
interface ILastUsedCellProps extends ICellProps {
|
||||
cell: {
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface IDataColumn {
|
||||
title: string;
|
||||
Header: ((props: IHeaderProps) => JSX.Element) | string;
|
||||
|
|
@ -207,34 +213,54 @@ const generateSoftwareTableHeaders = (deviceUser = false): IDataColumn[] => {
|
|||
);
|
||||
},
|
||||
},
|
||||
// TODO: Enable after backend has been updated to provide last_opened_at
|
||||
// {
|
||||
// title: "Last used",
|
||||
// Header: (cellProps) => (
|
||||
// <HeaderCell
|
||||
// value={cellProps.column.title}
|
||||
// isSortedDesc={cellProps.column.isSortedDesc}
|
||||
// />
|
||||
// ),
|
||||
// accessor: "last_opened_at",
|
||||
// Cell: (cellProps) => {
|
||||
// const lastUsed = isNaN(Date.parse(cellProps.cell.value))
|
||||
// ? "Unavailable"
|
||||
// : `${distanceInWordsToNow(Date.parse(cellProps.cell.value))} ago`;
|
||||
// return (
|
||||
// <span
|
||||
// className={
|
||||
// lastUsed === "Unavailable"
|
||||
// ? "software-last-used-muted"
|
||||
// : "software-last-used"
|
||||
// }
|
||||
// >
|
||||
// {lastUsed}
|
||||
// </span>
|
||||
// );
|
||||
// },
|
||||
// sortType: "dateStrings",
|
||||
// },
|
||||
{
|
||||
title: "Last used",
|
||||
Header: (cellProps) => (
|
||||
<HeaderCell
|
||||
value={cellProps.column.title}
|
||||
isSortedDesc={cellProps.column.isSortedDesc}
|
||||
/>
|
||||
),
|
||||
accessor: "last_opened_at",
|
||||
Cell: (cellProps: ILastUsedCellProps): JSX.Element => {
|
||||
const lastUsed = cellProps.cell.value
|
||||
? `${formatDistanceToNow(Date.parse(cellProps.cell.value))} ago`
|
||||
: "Unavailable";
|
||||
const hasLastUsed = lastUsed !== "Unavailable";
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className={`last-used ${
|
||||
lastUsed === "Unavailable" ? "text-muted" : ""
|
||||
}`}
|
||||
data-tip
|
||||
data-for={`last_used__${cellProps.row.index}`}
|
||||
data-tip-disable={hasLastUsed}
|
||||
>
|
||||
{lastUsed}
|
||||
</span>
|
||||
<ReactTooltip
|
||||
place="top"
|
||||
type="dark"
|
||||
effect="solid"
|
||||
backgroundColor="#3e4771"
|
||||
id={`last_used__${cellProps.row.index}`}
|
||||
className="last_used_tooltip"
|
||||
data-tip-disable={hasLastUsed}
|
||||
data-html
|
||||
>
|
||||
<span className={`last_used tooltip__tooltip-text`}>
|
||||
Last used information <br />
|
||||
is only available for the <br />
|
||||
Application (macOS) <br />
|
||||
software type.
|
||||
</span>
|
||||
</ReactTooltip>
|
||||
</>
|
||||
);
|
||||
},
|
||||
sortType: "dateStrings",
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
Header: "",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
.section--software {
|
||||
.text-muted {
|
||||
color: $ui-fleet-black-50;
|
||||
}
|
||||
.table-container__header-left {
|
||||
.controls {
|
||||
// vulnerable software dropdown filter
|
||||
|
|
@ -36,13 +39,16 @@
|
|||
}
|
||||
|
||||
.data-table-block {
|
||||
.last_used_tooltip {
|
||||
text-align: center;
|
||||
}
|
||||
.data-table__table {
|
||||
thead {
|
||||
.name__header {
|
||||
width: $col-md;
|
||||
}
|
||||
.version__header {
|
||||
width: 0px;
|
||||
width: $col-sm;
|
||||
}
|
||||
.source__header {
|
||||
display: none;
|
||||
|
|
@ -51,6 +57,9 @@
|
|||
.hosts_count__header {
|
||||
border-right: 0;
|
||||
}
|
||||
.last_opened_at__header {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: $break-990) {
|
||||
.version__header {
|
||||
width: $col-md;
|
||||
|
|
@ -86,6 +95,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.last_opened_at__cell {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: $break-1400) {
|
||||
.source__cell {
|
||||
display: table-cell;
|
||||
|
|
@ -131,7 +143,28 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.text-muted {
|
||||
color: $ui-fleet-black-50;
|
||||
|
||||
// Only show this column to macos users
|
||||
.macos .data-table-block .data-table__table {
|
||||
@media (min-width: $break-990) {
|
||||
thead .version__header {
|
||||
width: $col-sm;
|
||||
}
|
||||
}
|
||||
@media (min-width: $break-1400) {
|
||||
thead {
|
||||
.vulnerabilities__header {
|
||||
width: $col-md;
|
||||
}
|
||||
.last_opened_at__header {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
.last_opened_at__cell {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
$col-sm: 0px; // column size will "hug" its contents
|
||||
$col-sm: 62px; // column size will "hug" its contents
|
||||
$col-md: 202px; // column size not including 24px padding on left and right (total width is 250px)
|
||||
$col-lg: 352px; // column size not including 24px padding on left and right (total width is 250px)
|
||||
|
|
|
|||
Loading…
Reference in a new issue