mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Host Details Page: Resolve policy issues (#2571)
This commit is contained in:
parent
1dfe37accc
commit
34510ed144
7 changed files with 112 additions and 22 deletions
1
changes/issue-2380-host-details-failing-policies
Normal file
1
changes/issue-2380-host-details-failing-policies
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Host details page renders total issues if present and policies modal with query description and resolution
|
||||
|
|
@ -1,15 +1,19 @@
|
|||
import PropTypes from "prop-types";
|
||||
|
||||
export default PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
id: PropTypes.number.isRequired,
|
||||
query_id: PropTypes.number,
|
||||
query_name: PropTypes.string,
|
||||
query_name: PropTypes.string.isRequired,
|
||||
query_description: PropTypes.string,
|
||||
response: PropTypes.string,
|
||||
resolution: PropTypes.string,
|
||||
});
|
||||
|
||||
export interface IHostPolicy {
|
||||
id: number;
|
||||
query_id: number;
|
||||
query_name: string;
|
||||
query_description?: string;
|
||||
response: string;
|
||||
resolution?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ import BackChevron from "../../../../assets/images/icon-chevron-down-9x6@2x.png"
|
|||
import DeleteIcon from "../../../../assets/images/icon-action-delete-14x14@2x.png";
|
||||
import TransferIcon from "../../../../assets/images/icon-action-transfer-16x16@2x.png";
|
||||
import QueryIcon from "../../../../assets/images/icon-action-query-16x16@2x.png";
|
||||
import IssueIcon from "../../../../assets/images/icon-issue-fleet-black-50-16x16@2x.png";
|
||||
|
||||
const baseClass = "host-details";
|
||||
|
||||
|
|
@ -107,11 +108,25 @@ const HostDetailsPage = ({
|
|||
false
|
||||
);
|
||||
const [showQueryHostModal, setShowQueryHostModal] = useState<boolean>(false);
|
||||
const [showPolicyDetailsModal, setPolicyDetailsModal] = useState(false);
|
||||
const [showPolicyDetailsModal, setPolicyDetailsModal] = useState<boolean>(
|
||||
false
|
||||
);
|
||||
const [selectedPolicy, setSelectedPolicy] = useState<IHostPolicy | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const togglePolicyDetailsModal = useCallback(() => {
|
||||
const togglePolicyDetailsModal = useCallback(
|
||||
(policy: IHostPolicy) => {
|
||||
setPolicyDetailsModal(!showPolicyDetailsModal);
|
||||
setSelectedPolicy(policy);
|
||||
},
|
||||
[showPolicyDetailsModal, setPolicyDetailsModal, setSelectedPolicy]
|
||||
);
|
||||
|
||||
const onCancelPolicyDetailsModal = useCallback(() => {
|
||||
setPolicyDetailsModal(!showPolicyDetailsModal);
|
||||
}, [showPolicyDetailsModal, setPolicyDetailsModal]);
|
||||
setSelectedPolicy(null);
|
||||
}, [showPolicyDetailsModal, setPolicyDetailsModal, setSelectedPolicy]);
|
||||
|
||||
const [refetchStartTime, setRefetchStartTime] = useState<number | null>(null);
|
||||
const [
|
||||
|
|
@ -276,6 +291,7 @@ const HostDetailsPage = ({
|
|||
const titleData = normalizeEmptyValues(
|
||||
pick(host, [
|
||||
"status",
|
||||
"issues",
|
||||
"memory",
|
||||
"cpu_type",
|
||||
"os_version",
|
||||
|
|
@ -741,6 +757,37 @@ const HostDetailsPage = ({
|
|||
);
|
||||
};
|
||||
|
||||
const renderIssues = () => (
|
||||
<div className="info-flex__item info-flex__item--title">
|
||||
<span className="info-flex__header">Issues</span>
|
||||
<span className="info-flex__data">
|
||||
<span
|
||||
className="host-issue tooltip__tooltip-icon"
|
||||
data-tip
|
||||
data-for="host-issue-count"
|
||||
data-tip-disable={false}
|
||||
>
|
||||
<img alt="host issue" src={IssueIcon} />
|
||||
</span>
|
||||
<ReactTooltip
|
||||
place="bottom"
|
||||
type="dark"
|
||||
effect="solid"
|
||||
backgroundColor="#3e4771"
|
||||
id="host-issue-count"
|
||||
data-html
|
||||
>
|
||||
<span className={`tooltip__tooltip-text`}>
|
||||
Failing policies ({host?.issues.failing_policies_count})
|
||||
</span>
|
||||
</ReactTooltip>
|
||||
<span className={`total-issues-count`}>
|
||||
{host?.issues.total_issues_count}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderHostTeam = () => (
|
||||
<div className="info-flex__item info-flex__item--title">
|
||||
<span className="info-flex__header">Team</span>
|
||||
|
|
@ -879,6 +926,7 @@ const HostDetailsPage = ({
|
|||
{titleData.status}
|
||||
</span>
|
||||
</div>
|
||||
{titleData.issues.total_issues_count > 0 && renderIssues()}
|
||||
{isPremiumTier && renderHostTeam()}
|
||||
{renderDeviceUser()}
|
||||
<div className="info-flex__item info-flex__item--title">
|
||||
|
|
@ -987,7 +1035,10 @@ const HostDetailsPage = ({
|
|||
/>
|
||||
)}
|
||||
{!!host && showPolicyDetailsModal && (
|
||||
<PolicyDetailsModal onCancel={togglePolicyDetailsModal} />
|
||||
<PolicyDetailsModal
|
||||
onCancel={onCancelPolicyDetailsModal}
|
||||
policy={selectedPolicy}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const getPolicyStatus = (policy: IHostPolicy): string => {
|
|||
// NOTE: cellProps come from react-table
|
||||
// more info here https://react-table.tanstack.com/docs/api/useTable#cell-properties
|
||||
const generatePolicyTableHeaders = (
|
||||
togglePolicyDetails: () => void
|
||||
togglePolicyDetails: (policy: IHostPolicy) => void
|
||||
): IDataColumn[] => {
|
||||
return [
|
||||
{
|
||||
|
|
@ -64,7 +64,12 @@ const generatePolicyTableHeaders = (
|
|||
const { query_name } = cellProps.row.original;
|
||||
return (
|
||||
<>
|
||||
<Button onClick={togglePolicyDetails} variant={"text-icon"}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
togglePolicyDetails(cellProps.row.original);
|
||||
}}
|
||||
variant={"text-icon"}
|
||||
>
|
||||
<>
|
||||
{query_name}
|
||||
<img src={ArrowIcon} alt="View policy details" />
|
||||
|
|
|
|||
|
|
@ -2,32 +2,41 @@ import React from "react";
|
|||
import Button from "components/buttons/Button";
|
||||
import Modal from "components/modals/Modal";
|
||||
|
||||
import { IHostPolicy } from "interfaces/host_policy";
|
||||
|
||||
interface IPolicyDetailsProps {
|
||||
onCancel: () => void;
|
||||
policy: IHostPolicy | null;
|
||||
}
|
||||
|
||||
const baseClass = "policy-details-modal";
|
||||
|
||||
const PolicyDetailsModal = (props: IPolicyDetailsProps): JSX.Element => {
|
||||
const { onCancel } = props;
|
||||
|
||||
const PolicyDetailsModal = ({
|
||||
onCancel,
|
||||
policy,
|
||||
}: IPolicyDetailsProps): JSX.Element => {
|
||||
return (
|
||||
<Modal title={"Policy Name"} onExit={onCancel} className={baseClass}>
|
||||
<Modal
|
||||
title={`${policy?.query_name || "Query name"}`}
|
||||
onExit={onCancel}
|
||||
className={baseClass}
|
||||
>
|
||||
<div className={`${baseClass}__modal-body`}>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas
|
||||
feugiat venenatis quam, nec eleifend nisi aliquet non. Sed feugiat
|
||||
rutrum turpis, ac convallis odio egestas sit amet. Fusce vel sem
|
||||
massa. Quisque porttitor metus id vulputate vehicula. Donec ut nunc
|
||||
tempor, pretium lorem et, tempus est.
|
||||
</p>
|
||||
<p>{policy?.query_description}</p>
|
||||
{policy?.resolution && (
|
||||
<div className={`${baseClass}__resolution`}>
|
||||
<span className={`${baseClass}__resolve-header`}> Resolve:</span>
|
||||
<br />
|
||||
{policy?.resolution}
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__btn-wrap`}>
|
||||
<Button
|
||||
className={`${baseClass}__btn`}
|
||||
onClick={onCancel}
|
||||
variant="brand"
|
||||
>
|
||||
Cancel
|
||||
Done
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,18 @@
|
|||
.policy-details-modal {
|
||||
&__resolution {
|
||||
p {
|
||||
margin-top: $pad-xsmall;
|
||||
}
|
||||
}
|
||||
|
||||
&__resolve-header {
|
||||
font-weight: $bold;
|
||||
}
|
||||
|
||||
&__btn-wrap {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
margin-top: $pad-xxlarge;
|
||||
margin-top: $pad-large;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
|
|
|
|||
|
|
@ -42,10 +42,20 @@
|
|||
&--title {
|
||||
margin-right: $pad-xxlarge;
|
||||
|
||||
.info__data {
|
||||
.info-flex__data {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.total-issues-count {
|
||||
margin-left: $pad-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue